From 30f6dc3a6e8b794d17678491bda904c858522f59 Mon Sep 17 00:00:00 2001 From: "Weblate (bot)" Date: Tue, 22 Jul 2025 11:27:32 +0200 Subject: [PATCH 001/748] chore(web): update translations (#19228) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 1024mb <1024mb@users.noreply.hosted.weblate.org> Co-authored-by: Abel Márquez Mora Co-authored-by: Adupa Vasista Co-authored-by: AgentTricky Co-authored-by: Ahmed Khaleel Shihab Co-authored-by: Ahmed Shihab Co-authored-by: Alberto Serluca Co-authored-by: Alex Co-authored-by: Andreas Johansen Co-authored-by: Antonio Vazquez Co-authored-by: Apurbo Islam Co-authored-by: Bahadır Hamza Öztürk Co-authored-by: Barend van der Walt Co-authored-by: Bernardo Co-authored-by: Bezruchenko Simon Co-authored-by: Bora Atıcı Co-authored-by: Carina Chenot Co-authored-by: Christian Glockner Co-authored-by: Claudiu Hanza Co-authored-by: Cristality_ Co-authored-by: DarkWolf DarkINFINITE Co-authored-by: Davide Ciaccia Co-authored-by: Denis Pacquier Co-authored-by: DevServs Co-authored-by: Etienne de Villiers Co-authored-by: Filip Polak Co-authored-by: Fjuro Co-authored-by: Fjuro Co-authored-by: Florian Ostertag Co-authored-by: G M Co-authored-by: Gerry Co-authored-by: Girom Kenji Respicio Pacho Co-authored-by: Gyubin Lee Co-authored-by: Hazret Co-authored-by: Hoi Co-authored-by: Hurricane-32 Co-authored-by: Indrek Haav Co-authored-by: Ivan Dimitrov Co-authored-by: JPar99 Co-authored-by: Janat Taerakul Co-authored-by: Janat Taerakul Co-authored-by: Jozef Gaal Co-authored-by: JustRensio Co-authored-by: Lenny Angst Co-authored-by: Leo Bottaro Co-authored-by: Lucas Correia Co-authored-by: Luis Peregrina Co-authored-by: MSDNicrosoft Co-authored-by: MaBeniu Co-authored-by: Marc Portabella Navarro Co-authored-by: Martin Co-authored-by: Mateo Varela Co-authored-by: Matjaž T Co-authored-by: Matteo Marchi Co-authored-by: Mehedi Hasan Co-authored-by: Mohammed Al Otaibi Co-authored-by: Molnar Eduard Co-authored-by: Máté Molnár Co-authored-by: Mārtiņš Bruņenieks Co-authored-by: Niko Savola Co-authored-by: OffsetMonkey538 Co-authored-by: P Co-authored-by: Pavel Miniutka Co-authored-by: Petri Hämäläinen Co-authored-by: Revc Nix Co-authored-by: Robert Co-authored-by: Runskrift Co-authored-by: SGT Co-authored-by: Sachin Kekarjawalekar Co-authored-by: Sai varun Co-authored-by: Santiago Co-authored-by: Sergey Katsubo Co-authored-by: Sergi Font Co-authored-by: Shawn Co-authored-by: Sylvain Pichon Co-authored-by: Taiki M Co-authored-by: Theodor Onarheim Co-authored-by: Tijs-B Co-authored-by: Tomas Veselis Co-authored-by: Tony Ronaldo Matute Co-authored-by: Topaz Barziv Co-authored-by: User 123456789 Co-authored-by: Valter Vicente Co-authored-by: Vegard Fladby Co-authored-by: Xo Co-authored-by: Yago Raña Gayoso Co-authored-by: adri1m64 Co-authored-by: adriadam10 Co-authored-by: amirulashraf3861 Co-authored-by: anton garcias Co-authored-by: ardtas Co-authored-by: bacardicoke Co-authored-by: catelixor Co-authored-by: dark&white Co-authored-by: eSascha Co-authored-by: eav5jhl0 Co-authored-by: evensure Co-authored-by: jicetus Co-authored-by: juanCoder64 Co-authored-by: lumppu Co-authored-by: mastoduy Co-authored-by: miiyuh Co-authored-by: no Co-authored-by: sarga Co-authored-by: slick-daddy Co-authored-by: st7105 Co-authored-by: waclaw66 Co-authored-by: wickdj Co-authored-by: Вячеслав Лукьяненко Co-authored-by: Оргил Пүрэвдорж --- i18n/af.json | 118 +++++++++- i18n/ar.json | 470 ++++++++++++++++++++++++++++++------- i18n/be.json | 276 +++++++++++++++++++++- i18n/bg.json | 42 +++- i18n/bn.json | 82 ++++++- i18n/ca.json | 24 +- i18n/cs.json | 69 +++++- i18n/da.json | 4 +- i18n/de.json | 54 ++++- i18n/el.json | 2 - i18n/es.json | 90 ++++--- i18n/et.json | 75 ++++-- i18n/fi.json | 73 +++++- i18n/fil.json | 3 + i18n/fr.json | 59 ++++- i18n/gl.json | 1 - i18n/he.json | 37 ++- i18n/hi.json | 2 + i18n/hr.json | 1 - i18n/hu.json | 163 ++++++++++--- i18n/id.json | 23 +- i18n/it.json | 115 +++++---- i18n/ja.json | 12 +- i18n/ko.json | 39 ++- i18n/lt.json | 99 ++++++-- i18n/lv.json | 84 ++++++- i18n/mn.json | 8 + i18n/mr.json | 19 +- i18n/ms.json | 117 +++++++-- i18n/nb_NO.json | 127 ++++++---- i18n/nl.json | 55 ++++- i18n/nn.json | 59 ++++- i18n/pl.json | 42 +++- i18n/pt.json | 43 +++- i18n/pt_BR.json | 136 ++++++----- i18n/ro.json | 134 +++++++++-- i18n/ru.json | 77 ++++-- i18n/sk.json | 508 +++++++++++++++++++++++++++------------- i18n/sl.json | 49 +++- i18n/sr_Cyrl.json | 1 - i18n/sr_Latn.json | 1 - i18n/sv.json | 24 +- i18n/ta.json | 1 - i18n/te.json | 15 +- i18n/th.json | 219 ++++++++++++++--- i18n/tr.json | 202 +++++++++++++--- i18n/uk.json | 3 +- i18n/vi.json | 20 +- i18n/zh_Hant.json | 2 - i18n/zh_SIMPLIFIED.json | 49 +++- 50 files changed, 3168 insertions(+), 760 deletions(-) diff --git a/i18n/af.json b/i18n/af.json index 55555c8398..b3729903ec 100644 --- a/i18n/af.json +++ b/i18n/af.json @@ -4,6 +4,7 @@ "account_settings": "Rekeninginstellings", "acknowledge": "Erken", "action": "Aksie", + "action_common_update": "Opdateur", "actions": "Aksies", "active": "Aktief", "activity": "Aktiwiteite", @@ -13,6 +14,7 @@ "add_a_location": "Voeg 'n ligging by", "add_a_name": "Voeg 'n naam by", "add_a_title": "Voeg 'n titel by", + "add_endpoint": "Voeg Koppelvlakpunt by", "add_exclusion_pattern": "Voeg uitsgluitingspatrone by", "add_import_path": "Voeg invoerpad by", "add_location": "Voeg ligging by", @@ -20,26 +22,30 @@ "add_partner": "Voeg vennoot by", "add_path": "Voeg pad by", "add_photos": "Voeg foto's by", + "add_tag": "Voeg tag by", "add_to": "Voeg by…", "add_to_album": "Voeg na album", - "add_to_shared_album": "Voeg na gedeelde album", + "add_to_album_bottom_sheet_added": "By {album} bygevoeg", + "add_to_album_bottom_sheet_already_exists": "Reeds in {album}", + "add_to_shared_album": "Voeg toe aan gedeelde album", "add_url": "Voeg URL by", - "added_to_archive": "By argief gevoeg", - "added_to_favorites": "By gunstelinge gevoeg", - "added_to_favorites_count": "Het {count, number} by gunstelinge gevoeg", + "added_to_archive": "By argief toegevoegd", + "added_to_favorites": "By gunstelinge toegevoegd", + "added_to_favorites_count": "Het {count, number} by gunstelinge toegevoegd", "admin": { "add_exclusion_pattern_description": "Voeg uitsluitingspatrone by. Globbing met *, ** en ? word ondersteun. Om alle lêers in enige lêergids genaamd \"Raw\" te ignoreer, gebruik \"**/Raw/**\". Om alle lêers wat op \".tif\" eindig, te ignoreer, gebruik \"**/*.tif\". Om 'n absolute pad te ignoreer, gebruik \"/path/to/ignore/**\".", + "admin_user": "Admin gebruiker", "asset_offline_description": "Hierdie eksterne biblioteekbate word nie meer op skyf gevind nie en is na die asblik geskuif. As die lêer binne die biblioteek geskuif is, gaan jou tydlyn na vir die nuwe ooreenstemmende bate. Om hierdie bate te herstel, maak asseblief seker dat die lêerpad hieronder deur Immich verkry kan word en skandeer die biblioteek.", "authentication_settings": "Verifikasie instellings", "authentication_settings_description": "Bestuur wagwoord, OAuth en ander verifikasie instellings", "authentication_settings_disable_all": "Is jy seker jy wil alle aanmeldmetodes deaktiveer? Aanmelding sal heeltemal gedeaktiveer word.", "authentication_settings_reenable": "Om te heraktiveer, gebruik 'n Server Command.", "background_task_job": "Agtergrondtake", - "backup_database": "Rugsteun databasis", + "backup_database": "Skep Datastortlêer", "backup_database_enable_description": "Aktiveer databasisrugsteun", "backup_keep_last_amount": "Aantal vorige rugsteune om te hou", "backup_settings": "Rugsteun instellings", - "backup_settings_description": "Bestuur databasis rugsteun instellings", + "backup_settings_description": "Bestuur databasis rugsteun instellings.", "cleared_jobs": "Poste gevee vir: {job}", "config_set_by_file": "Config word tans deur 'n konfigurasielêer gestel", "confirm_delete_library": "Is jy seker jy wil {library}-biblioteek uitvee?", @@ -47,6 +53,7 @@ "confirm_email_below": "Om te bevestig, tik \"{email}\" hieronder", "confirm_reprocess_all_faces": "Is jy seker jy wil alle gesigte herverwerk? Dit sal ook genoemde mense skoonmaak.", "confirm_user_password_reset": "Is jy seker jy wil {user} se wagwoord terugstel?", + "confirm_user_pin_code_reset": "Is jy seker jy wil {user} se PIN kode herstel?", "create_job": "Skep werk", "cron_expression": "Cron uitdrukking", "cron_expression_description": "Stel die skanderingsinterval in met die cron-formaat. Vir meer inligting verwys asseblief na bv. Crontab Guru", @@ -56,10 +63,14 @@ "exclusion_pattern_description": "Met uitsluitingspatrone kan jy lêers en vouers ignoreer wanneer jy jou biblioteek skandeer. Dit is nuttig as jy vouers het wat lêers bevat wat jy nie wil invoer nie, soos RAW-lêers.", "external_library_management": "Eksterne Biblioteekbestuur", "face_detection": "Gesig deteksie", + "face_detection_description": "Detecteer die gesigte in media deur middel van masjienleer. Vir videos word slegs die duimnaelskets oorweeg. “Herlaai” (ver)werk al die media weer. “Stel terug” verwyder boonop alle huidige gesigdata. “Onverwerk” plaas bates in die tou wat nog nie verwerk is nie. Gedekte gesigte sal ná voltooiing van Gesigdetectie vir Gesigherkenning in die tou geplaas word, om hulle in bestaande of nuwe persone te groepeer.", + "facial_recognition_job_description": "Groepeer gesigte in mense in. Die stap is vinniger nadat Gesig Deteksie klaar is. \"Herstel\" (her-)groepeer alle gesigte. \"Vermiste\" plaas gesigte in ry wat nie 'n persoon gekoppel het nie.", "failed_job_command": "Opdrag {command} het misluk vir werk: {job}", "force_delete_user_warning": "WAARSKUWING: Dit sal onmiddellik die gebruiker en alle bates verwyder. Dit kan nie ontdoen word nie en die lêers kan nie herstel word nie.", "image_format": "Formaat", "image_format_description": "WebP produseer kleiner lêers as JPEG, maar is stadiger om te enkodeer.", + "image_fullsize_description": "Vol grote prent met geen metadata, gebruik wanner ingezoem", + "image_fullsize_enabled": "Skakel aan vol grote prent generasie", "image_prefer_embedded_preview": "Verkies ingebedde voorskou", "image_prefer_wide_gamut": "Verkies wide gamut", "image_prefer_wide_gamut_setting_description": "Gebruik Display P3 vir kleinkiekies. Dit behou die lewendheid van beelde met wye kleurruimtes beter, maar beelde kan anders verskyn op ou apparate met 'n ou blaaierweergawe. sRGB-beelde gebruik steeds sRGB om kleurverskuiwings te voorkom.", @@ -77,8 +88,99 @@ "job_concurrency": "{job} gelyktydigheid", "job_created": "Taak gemaak", "job_not_concurrency_safe": "Hierdie taak kan nie gelyktydig uitgevoer word nie.", - "job_settings": "Agtergrondtaakinstellings" + "job_settings": "Agtergrondtaakinstellings", + "job_settings_description": "Bestuur werkgelyktydigheid", + "job_status": "Werkstatus", + "library_created": "Biblioteek geskep: {library}", + "library_deleted": "Biblioteek verwyder", + "library_import_path_description": "Spesifiseer 'n leer om in te neem. Hierdie leer, en al die sub leers, gaan geskandeer for vir prente en videos.", + "library_scanning": "Periodieke Skandering", + "library_scanning_description": "Stel periodieke skandering van biblioteek in", + "library_scanning_enable_description": "Aktiveer periodieke biblioteekskandering", + "library_settings": "Eksterne Biblioteek", + "map_settings": "Kaart", + "migration_job": "Migrasie", + "oauth_settings": "OAuth", + "transcoding_acceleration_vaapi": "VAAPI" }, + "administration": "Administrasie", + "advanced": "Gevorderde", + "albums": "Albums", + "all": "Alle", + "anti_clockwise": "Anti-kloksgewys", + "archive": "Argief", + "asset_skipped": "Oorgeslaan", + "asset_uploaded": "Opgelaai", + "asset_uploading": "Oplaai…", + "assets": "Bates", + "back": "Terug", + "backward": "Agteruit", + "build": "Bou", + "camera": "Kamera", + "cancel": "Kanselleer", + "city": "Stad", + "clockwise": "Kloksgewys", + "close": "Maak toe", + "color": "Kleur", + "confirm": "Bevestig", + "contain": "Bevat", + "context": "Konteks", + "continue": "Gaan voort", + "country": "Land", + "cover": "Bedek", + "create": "Skep", + "created": "Geskep", + "dark": "Donker", + "day": "Dag", + "delete": "Verwyder", + "description": "Beskrywing", + "details": "Besonderhede", + "direction": "Rigting", + "discover": "Ontdek", + "documentation": "Dokumentasie", + "done": "Klaar", + "download": "Aflaai", + "download_settings": "Aflaai", + "duplicates": "Duplikate", + "duration": "Duur", + "edit": "Wysig", + "edited": "Gewysigd", "search_by_description": "Soek by beskrywing", - "search_by_description_example": "Stapdag in Sapa" + "search_by_description_example": "Stapdag in Sapa", + "version": "Weergawe", + "version_announcement_closing": "Jou friend, Alex", + "version_history": "Weergawegeskiedenis", + "version_history_item": "{version} geinstaleerd op {date}", + "video": "Video", + "videos": "Video's", + "view": "Bekyk", + "view_album": "Bekyk Album", + "view_all": "Bekyk alle", + "view_all_users": "Bekyk alle gebruikers", + "view_in_timeline": "Bekyk in tydlyn", + "view_link": "Bekyk skakel", + "view_links": "Bekyk skakels", + "view_name": "Bekyk", + "view_next_asset": "Bekyk volgende bate", + "view_previous_asset": "Bekyk vorige bate", + "view_qr_code": "Bekyk QR-kode", + "view_stack": "Bekyk stapel", + "view_user": "Bekyk gebruiker", + "viewer_remove_from_stack": "Verwyder van stapel", + "viewer_stack_use_as_main_asset": "Gebruik as hoofbate", + "viewer_unstack": "Ontstapel", + "visibility_changed": "Sigbaarheid verander voor {count, plural, one {# person} other {# people}}", + "waiting": "Wag", + "warning": "Waaskuwing", + "week": "Week", + "welcome": "Welkom", + "welcome_to_immich": "Welkom by Immich", + "wifi_name": "Wi-Fi Naam", + "wrong_pin_code": "Verkeerde PIN-kode", + "year": "Jaar", + "years_ago": "{years, plural, one {# year} other {# years}} gelede", + "yes": "Ja", + "you_dont_have_any_shared_links": "Jy het geen gedeelde skakels", + "your_wifi_name": "Jou Wi-Fi naam", + "zoom_image": "Vergroot Prent" } diff --git a/i18n/ar.json b/i18n/ar.json index 67f7752ae1..6042c8f82f 100644 --- a/i18n/ar.json +++ b/i18n/ar.json @@ -1,19 +1,20 @@ { - "about": "من نحن", - "account": "الحساب", + "about": "عن", + "account": "حساب", "account_settings": "إعدادات الحساب", "acknowledge": "أُدرك ذلك", - "action": "التحكم", + "action": "عملية", "action_common_update": "تحديث", - "actions": "العمليات", + "actions": "عمليات", "active": "نشط", - "activity": "النشاط", + "activity": "نشاط", "activity_changed": "النشاط {enabled, select, true {مُفْعل} other {معطّل}}", "add": "إضافة", "add_a_description": "إضافة وصف", "add_a_location": "إضافة موقع", "add_a_name": "إضافة إسم", "add_a_title": "إضافة عنوان", + "add_endpoint": "اضف نقطة نهاية", "add_exclusion_pattern": "إضافة نمط إستثناء", "add_import_path": "إضافة مسار الإستيراد", "add_location": "إضافة موقع", @@ -21,28 +22,30 @@ "add_partner": "أضف شريكًا", "add_path": "إضافة مسار", "add_photos": "إضافة صور", + "add_tag": "اضف علامة", "add_to": "إضافة إلى…", "add_to_album": "إضافة إلى ألبوم", "add_to_album_bottom_sheet_added": "تمت الاضافة{album}", "add_to_album_bottom_sheet_already_exists": "موجودة مسبقا {album}", - "add_to_shared_album": "إضافة إلى ألبوم مشترك", + "add_to_shared_album": "إضافة إلى ألبوم مشارك", "add_url": "إضافة رابط", "added_to_archive": "أُضيفت للأرشيف", "added_to_favorites": "أُضيفت للمفضلات", "added_to_favorites_count": "تم إضافة {count, number} إلى المفضلات", "admin": { "add_exclusion_pattern_description": "إضافة أنماط الاستبعاد. يدعم التمويه باستخدام *، **، و؟. لتجاهل جميع الملفات في أي دليل يسمى \"Raw\"، استخدم \"**/Raw/**\". لتجاهل جميع الملفات التي تنتهي بـ \".tif\"، استخدم \"**/*.tif\". لتجاهل مسار مطلق، استخدم \"/path/to/ignore/**\".", + "admin_user": "مستخدم مدير", "asset_offline_description": "لم يعد هذا الأصل الخاص بالمكتبة الخارجية موجودًا على القرص وتم نقله إلى سلة المهملات. إذا تم نقل الملف داخل المكتبة، فتحقق من الجدول الزمني الخاص بك لمعرفة الأصل الجديد المقابل. لاستعادة هذا الأصل، يرجى التأكد من إمكانية الوصول إلى مسار الملف أدناه بواسطة Immich ومن ثم قم بمسح المكتبة.", "authentication_settings": "إعدادات المصادقة", "authentication_settings_description": "إدارة كلمة المرور وOAuth وإعدادات المصادقة الأُخرى", "authentication_settings_disable_all": "هل أنت متأكد أنك تريد تعطيل جميع وسائل تسجيل الدخول؟ سيتم تعطيل تسجيل الدخول بالكامل.", "authentication_settings_reenable": "لإعادة التفعيل، استخدم أمر الخادم.", "background_task_job": "المهام الخلفية", - "backup_database": "قاعدة البيانات الاحتياطية", - "backup_database_enable_description": "تمكين النسخ الاحتياطي لقاعدة البيانات", - "backup_keep_last_amount": "مقدار النسخ الاحتياطية السابقة للاحتفاظ بها", - "backup_settings": "إعدادات النسخ الاحتياطي", - "backup_settings_description": "إدارة إعدادات النسخ الاحتياطي لقاعدة البيانات", + "backup_database": "انشاء تفريغ قاعدة البيانات", + "backup_database_enable_description": "تمكين تفريغ قاعدة البيانات", + "backup_keep_last_amount": "مقدار التفريغات السابقة للاحتفاظ بها", + "backup_settings": "إعدادات تفريغ قاعدة البيانات", + "backup_settings_description": "إدارة إعدادات تفريغ قاعدة البيانات.", "cleared_jobs": "تم إخلاء مهام: {job}", "config_set_by_file": "الإعدادات حاليًا معينة عن طريق ملف الاعدادات", "confirm_delete_library": "هل أنت متأكد أنك تريد حذف مكتبة {library}؟", @@ -50,6 +53,7 @@ "confirm_email_below": "للتأكيد، اكتب \"{email}\" بالأسفل", "confirm_reprocess_all_faces": "هل أنت متأكد أنك تريد إعادة معالجة جميع الوجوه؟ سيخلي هذا كل الأشخاص الذين سَميتَهم.", "confirm_user_password_reset": "هل أنت متأكد أنك تريد إعادة تعيين كلمة مرور {user}؟", + "confirm_user_pin_code_reset": "هل انت متاكد من اعادة ضبط رمز PIN الخاص ب {user}؟", "create_job": "إنشاء وظيفة", "cron_expression": "تعبير Cron", "cron_expression_description": "اضبط الفاصل الزمني للفحص باستخدام تنسيق cron. لمزيد من المعلومات يُرجى الرجوع إلى Crontab Guru على سبيل المثال", @@ -65,10 +69,15 @@ "force_delete_user_warning": "تحذير: سيؤدي ذلك إلى إزالة المستخدم وجميع محتوياته على الفور. لا يمكن التراجع عن هذا الإجراء ولا يمكن استرداد الملفات.", "image_format": "التنسيق", "image_format_description": "يُنتج WebP ملفات أصغر حجمًا من ملفات JPEG، ولكنه أبطأ في عملية الترميز.", + "image_fullsize_description": "صورة بحجم كامل مع ازالة البيانات الوصفية، تستخدم عند التكبير", + "image_fullsize_enabled": "تمكين توليد الصور بحجم كامل", + "image_fullsize_enabled_description": "توليد صور بحجم كامل للصيغ الغير صديقة للويب. عند تفعيل \"تفضيل العرض المدمج\" ، العروض المدمجه تستخدم بشكل مباشر بدون تحويل. لا يؤثر على الصيغ الصديقة للويب مثل JPEG.", + "image_fullsize_quality_description": "صور بدقة كاملة من ١-١٠٠. الاعلى افضل ولكن ينتج ملفات بحجم اكبر.", + "image_fullsize_title": "اعدادات الصور بحجم كامل", "image_prefer_embedded_preview": "تفضيل المعاينة المدمجة", - "image_prefer_embedded_preview_setting_description": "استخدم المعاينات المضمنة في صور RAW كمدخل لمعالجة الصور عندما تكون متاحة. يؤدي لإنتاج ألوان أكثر دقة لبعض الصور، لكن جودة المعاينة تعتمد على الكاميرا وقد تحتوي الصورة على شوائب ضغطٍ أكثر.", - "image_prefer_wide_gamut": "تفضيل نطاق الألوان الواسع", - "image_prefer_wide_gamut_setting_description": "استخدم Display P3 للصور المصغرة. يحافظ هذا على حيوية الصور ذات مساحات الألوان الواسعة بشكل أفضل، ولكن قد تظهر الصور بشكل مختلف على الأجهزة القديمة ذات إصدار متصفح قديم. يتم الاحتفاظ بصور sRGB بتنسيق sRGB لتجنب تغيرات اللون.", + "image_prefer_embedded_preview_setting_description": "استخدم المعاينات المضمنة في صور RAW كمدخل لمعالجة الصور عندما تكون متاحة. ينتج عنه انتاج ألوان أكثر دقة لبعض الصور، لكن جودة المعاينة تعتمد على الكاميرا وقد تحتوي الصورة على شوائب ضغطٍ أكثر.", + "image_prefer_wide_gamut": "تفضيل تدرج الألوان الواسع", + "image_prefer_wide_gamut_setting_description": "استخدم عرض P3 للصور المصغرة. يحافظ هذا على حيوية الصور ذات مساحات الألوان الواسعة بشكل أفضل، ولكن قد تظهر الصور بشكل مختلف على الأجهزة القديمة ذات إصدار متصفح قديم. يتم الاحتفاظ بصور sRGB بتنسيق sRGB لتجنب تغيرات اللون.", "image_preview_description": "صورة متوسطة الحجم مع بيانات وصفية مجردة، تُستخدم عند عرض أصل واحد وللتعلم الآلي", "image_preview_quality_description": "جودة المعاينة من 1 إلى 100. كلما كانت القيمة أعلى كان ذلك أفضل، ولكنها تنتج ملفات أكبر وقد تقلل من استجابة التطبيق. قد يؤثر ضبط قيمة منخفضة على جودة التعلم الآلي.", "image_preview_title": "إعدادات المعاينة", @@ -91,18 +100,18 @@ "library_created": "تم إنشاء المكتبة: {library}", "library_deleted": "تم حذف المكتبة", "library_import_path_description": "حدد مجلدًا للاستيراد. سيتم فحص هذا المجلد، بما في ذلك المجلدات الفرعية، بحثًا عن الصور ومقاطع الفيديو.", - "library_scanning": "الفحص الدوري", - "library_scanning_description": "إعداد فحص المكتبة الدوري", - "library_scanning_enable_description": "تفعيل فحص المكتبة الدوري", + "library_scanning": "المسح الدوري", + "library_scanning_description": "إعداد مسح المكتبة الدوري", + "library_scanning_enable_description": "تفعيل مسح المكتبة الدوري", "library_settings": "المكتبة الخارجية", "library_settings_description": "إدارة إعدادات المكتبة الخارجية", "library_tasks_description": "مسح المكتبات الخارجية للعثور على الأصول الجديدة و/أو المتغيرة", - "library_watching_enable_description": "راقب المكتبات الخارجية لتتبع تغييرات الملفات", + "library_watching_enable_description": "راقب المكتبات الخارجية لتغييرات الملفات", "library_watching_settings": "مراقبة المكتبات (تجريبي)", "library_watching_settings_description": "راقب تلقائيًا التغييرات في الملفات", "logging_enable_description": "تفعيل تسجيل الأحداث", "logging_level_description": "عند التفعيل، أي مستوى تسجيل سيستخدم.", - "logging_settings": "تسجيل الأحداث", + "logging_settings": "تسجيل الاحداث", "machine_learning_clip_model": "نموذج CLIP", "machine_learning_clip_model_description": "اسم نموذج CLIP مدرجٌ هنا. يرجى ملاحظة أنه يجب إعادة تشغيل وظيفة \"البحث الذكي\" لجميع الصور بعد تغيير النموذج.", "machine_learning_duplicate_detection": "كشف التكرار", @@ -142,10 +151,10 @@ "map_light_style": "النمط الفاتح", "map_manage_reverse_geocoding_settings": "إدارة إعدادات التكوين الجغرافي المعكوس", "map_reverse_geocoding": "عكس الترميز الجغرافي", - "map_reverse_geocoding_enable_description": "تفعيل عكس الترميز الجغرافي", - "map_reverse_geocoding_settings": "إعدادات عكس الترميز الجغرافي", - "map_settings": "الخريطة", - "map_settings_description": "إدارة إعدادات الخريطة", + "map_reverse_geocoding_enable_description": "تفعيل الترميز الجغرافي العكسي", + "map_reverse_geocoding_settings": "إعدادات الترميز الجغرافي العكسي", + "map_settings": "الخارطة", + "map_settings_description": "إدارة إعدادات الخارطة", "map_style_description": "عنوان URL لسمة الخريطة style.json", "memory_cleanup_job": "تنظيف الذاكرة", "memory_generate_job": "توليد الذاكرة", @@ -157,12 +166,26 @@ "metadata_settings_description": "إدارة إعدادات البيانات الوصفية", "migration_job": "ترحيل", "migration_job_description": "ترحيل الصور المصغرة للمحتويات والوجوه إلى أحدث هيكل مجلدات", + "nightly_tasks_cluster_faces_setting_description": "قم بتشغيل التعرف على الوجه على الوجوه المكتشفة حديثا", + "nightly_tasks_cluster_new_faces_setting": "مجموعة الوجوه الجديدة", + "nightly_tasks_database_cleanup_setting": "مهام تنظيف قاعدة البيانات", + "nightly_tasks_database_cleanup_setting_description": "قم بتنظيف البيانات القديمة منتهية الصلاحية من قاعدة البيانات", + "nightly_tasks_generate_memories_setting": "إنشاء الذكريات", + "nightly_tasks_generate_memories_setting_description": "إنشاء ذكريات جديدة من الأصول", + "nightly_tasks_missing_thumbnails_setting": "إنشاء صور مصغرة مفقودة", + "nightly_tasks_missing_thumbnails_setting_description": "أصول قائمة الانتظار بدون صور مصغرة لإنشاء الصور المصغرة", + "nightly_tasks_settings": "إعدادات المهام الليلية", + "nightly_tasks_settings_description": "إدارة المهام الليلية", + "nightly_tasks_start_time_setting": "وقت البدء", + "nightly_tasks_start_time_setting_description": "الوقت الذي يبدأ فيه الخادم في تشغيل المهام الليلية", + "nightly_tasks_sync_quota_usage_setting": "مزامنة حصة الاستخدام", + "nightly_tasks_sync_quota_usage_setting_description": "تحديث حصة تخزين المستخدم، بناء على الاستخدام الحالي", "no_paths_added": "لم يتم إضافة أي مسارات", "no_pattern_added": "لم يتم إضافة أي أنماط", - "note_apply_storage_label_previous_assets": "ملاحظة: لتطبيق تسمية التخزين على المحتويات التي تم رفعها سابقًا، قم بتشغيل", + "note_apply_storage_label_previous_assets": "ملاحظة: لتطبيق سمية التخزين على المحتويات التي تم رفعها سابقًا، قم بتشغيل", "note_cannot_be_changed_later": "ملاحظة: لا يمكن تغيير هذا لاحقًا!", "notification_email_from_address": "عنوان المرسل", - "notification_email_from_address_description": "عنوان البريد الإلكتروني للمرسل، على سبيل المثال: \"Immich Photo Server noreply@example.com\"", + "notification_email_from_address_description": "عنوان البريد الإلكتروني للمرسل، على سبيل المثال: \"Immich Photo Server noreply@example.com\". تاكد من استخدام عنوان بريد الكتروني يسمح لك بارسال البريد الالكتروني منه.", "notification_email_host_description": "مضيف خادم البريد الإلكتروني (مثلًا: smtp.immich.app)", "notification_email_ignore_certificate_errors": "تجاهل أخطاء الشهادة", "notification_email_ignore_certificate_errors_description": "تجاهل أخطاء التحقق من صحة شهادة TLS (غير مستحسن)", @@ -182,6 +205,7 @@ "oauth_auto_register": "التسجيل التلقائي", "oauth_auto_register_description": "التسجيل التلقائي للمستخدمين الجدد بعد تسجيل الدخول باستخدام OAuth", "oauth_button_text": "نص الزر", + "oauth_client_secret_description": "مطلوب اذاPKCE(مفتاح الاثبات لتبادل الكود) لم يتم توفيره من مزود OAuth", "oauth_enable_description": "تسجيل الدخول باستخدام OAuth", "oauth_mobile_redirect_uri": "عنوان URI لإعادة التوجيه على الهاتف", "oauth_mobile_redirect_uri_override": "تجاوز عنوان URI لإعادة التوجيه على الهاتف", @@ -189,12 +213,14 @@ "oauth_settings": "OAuth", "oauth_settings_description": "إدارة إعدادات تسجيل الدخول OAuth", "oauth_settings_more_details": "لمزيد من التفاصيل حول هذه الميزة، يرجى الرجوع إلى الوثائق.", - "oauth_storage_label_claim": "المطالبة بتصنيف التخزين", - "oauth_storage_label_claim_description": "قم تلقائيًا بتعيين تصنيف التخزين الخاص بالمستخدم على قيمة هذه المطالبة.", + "oauth_storage_label_claim": "المطالبة بسمة التخزين", + "oauth_storage_label_claim_description": "قم تلقائيًا بتعيين سمة التخزين الخاص بالمستخدم على قيمة هذه المطالبة.", "oauth_storage_quota_claim": "المطالبة بحصة التخزين", "oauth_storage_quota_claim_description": "قم تلقائيًا بتعيين حصة التخزين للمستخدم على قيمة هذه المطالبة.", "oauth_storage_quota_default": "حصة التخزين الافتراضية (جيجابايت)", - "oauth_storage_quota_default_description": "الحصة بالجيجابايت التي سيتم استخدامها عندما لا يتم توفير مطالبة (أدخل 0 لحصة غير محدودة).", + "oauth_storage_quota_default_description": "الحصة بالجيجابايت التي سيتم استخدامها عندما لا يتم توفير مطالبة.", + "oauth_timeout": "نفاذ وقت الطلب", + "oauth_timeout_description": "نفاذ وقت الطلب بالميلي ثانية", "password_enable_description": "تسجيل الدخول باستخدام البريد الكتروني وكلمة المرور", "password_settings": "تسجيل الدخول بكلمة المرور", "password_settings_description": "إدارة تسجيل الدخول بكلمة المرور", @@ -229,13 +255,14 @@ "storage_template_hash_verification_enabled_description": "تفعيل التحقق من الهاش، لا تعطل هذا إلا إذا كنت متأكدًا من تأثيراته", "storage_template_migration": "تهجير قالب التخزين", "storage_template_migration_description": "قم بتطبيق القالب الحالي {template} على المحتويات التي تم رفعها سابقًا", - "storage_template_migration_info": "تغييرات القالب ستنطبق فقط على المحتويات الجديدة. لتطبيق القالب على المحتويات التي تم رفعها سابقًا، قم بتشغيل {job}.", + "storage_template_migration_info": "تغييرات النموذج الخزني ستغير جميع الصيغ الى احرف صغيرة. تغييرات النموذج ستنطبق فقط على المحتويات الجديدة. لتطبيق النموذج على المحتويات التي تم رفعها سابقًا، قم بتشغيل {job}.", "storage_template_migration_job": "وظيفة تهجير قالب التخزين", "storage_template_more_details": "لمزيد من التفاصيل حول هذه الميزة، يرجى الرجوع إلى Storage Template وimplications", + "storage_template_onboarding_description_v2": "عند التفعيل. هذه الخاصية ستقوم بالترتيب التلقائي للملفات بناء على نموذج معرف من قبل المستخدم. رجاء اطلع على التوثيق.", "storage_template_path_length": "الحد التقريبي لطول المسار: {length, number}/{limit, number}", "storage_template_settings": "قالب التخزين", "storage_template_settings_description": "إدارة هيكل المجلد واسم الملف للأصول المرفوعة", - "storage_template_user_label": "{label} هو تسمية التخزين الخاصة بالمستخدم", + "storage_template_user_label": "{label} هو سمة التخزين الخاصة بالمستخدم", "system_settings": "إعدادات النظام", "tag_cleanup_job": "تنظيف العلامة", "template_email_available_tags": "يمكنك استخدام المتغيرات التالية في القالب الخاص بك: {tags}", @@ -246,15 +273,15 @@ "template_email_update_album": "تحديث قالب الألبوم", "template_email_welcome": "قالب البريد الإلكتروني الترحيبي", "template_settings": "قوالب الإشعارات", - "template_settings_description": "إدارة القوالب المخصصة للإشعارات.", + "template_settings_description": "إدارة القوالب المخصصة للإشعارات", "theme_custom_css_settings": "CSS مخصص", "theme_custom_css_settings_description": "أوراق الأنماط المتتالية تسمح بتخصيص تصميم Immich.", "theme_settings": "إعدادات السمة", "theme_settings_description": "إدارة تخصيص واجهة ويب Immich", "thumbnail_generation_job": "إنشاء الصور المصغرة", "thumbnail_generation_job_description": "إنشاء صور مصغرة كبيرة وصغيرة وغير واضحة لكل أصل، بالإضافة إلى صور مصغرة لكل شخص", - "transcoding_acceleration_api": "واجهة برمجة التطبيقات للتسريع", - "transcoding_acceleration_api_description": "الواجهة البرمجية التي ستتفاعل مع جهازك لتسريع التحويل. هذا الإعداد هو \"أفضل محاولة\": سيعود إلى التحويل البرمجي في حالة الفشل. قد لا يعمل VP9 اعتمادًا على عتادك.", + "transcoding_acceleration_api": "تسريع API", + "transcoding_acceleration_api_description": "API التي ستتفاعل مع جهازك لتسريع التحويل. هذا الإعداد هو \"أفضل محاولة\": سيعود إلى التحويل البرمجي في حالة الفشل. قد لا يعمل VP9 اعتمادًا على عتادك.", "transcoding_acceleration_nvenc": "NVENC (يتطلب GPU من NVIDIA)", "transcoding_acceleration_qsv": "Quick Sync (يتطلب معالج Intel من الجيل السابع أو أحدث)", "transcoding_acceleration_rkmpp": "RKMPP (فقط على شرائح Rockchip SOC)", @@ -278,13 +305,13 @@ "transcoding_encoding_options": "خيارات الترميز", "transcoding_encoding_options_description": "اضبط برامج الترميز والدقة والجودة والخيارات الأخرى لمقاطع الفيديو المشفرة", "transcoding_hardware_acceleration": "التسريع العتادي", - "transcoding_hardware_acceleration_description": "تجريبي؛ أسرع بكثير، ولكن ستكون جودتها أقل عند نفس معدل البت", + "transcoding_hardware_acceleration_description": "تجريبي: ترميز اسرع لكن قد يقلل من الجودة مع معدل بت اقل", "transcoding_hardware_decoding": "فك تشفير الأجهزة", "transcoding_hardware_decoding_setting_description": "ينطبق ذلك فقط على NVENC، QSV، و RKMPP. يمكن التسريع من طرف لطرف بدلاً من تسريع الترميز فقط. قد لا يعمل على جميع مقاطع الفيديو.", "transcoding_max_b_frames": "أقصى عدد من الإطارات B", "transcoding_max_b_frames_description": "القيم الأعلى تعزز كفاءة الضغط، ولكنها تبطئ عملية الترميز. قد لا تكون متوافقة مع التسريع العتادي على الأجهزة القديمة. قيمة 0 تعطل إطارات B، بينما تضبط القيمة -1 هذا القيمة تلقائيًا.", "transcoding_max_bitrate": "الحد الأقصى لمعدل البت", - "transcoding_max_bitrate_description": "يمكن أن يؤدي تعيين الحد الأقصى لمعدل البت إلى جعل أحجام الملفات أكثر قابلية للتنبؤ بها بتكلفة بسيطة بالنسبة للجودة. عند دقة 720 بكسل، تكون القيم النموذجية 2600 كيلو بايت لـ VP9 أو HEVC، أو 4500 كيلو بايت لـ H.264. معطل إذا تم ضبطه على 0.", + "transcoding_max_bitrate_description": "يمكن أن يؤدي تعيين الحد الأقصى لمعدل البت إلى جعل أحجام الملفات أكثر قابلية للتنبؤ بها بتكلفة بسيطة بالنسبة للجودة. عند دقة 720 بكسل، تكون القيم النموذجية 2600 كيلو بت لـ VP9 أو HEVC، أو 4500 كيلو بت لـ H.264. معطل إذا تم ضبطه على 0.", "transcoding_max_keyframe_interval": "الحد الأقصى للفاصل الزمني للإطار الرئيسي", "transcoding_max_keyframe_interval_description": "يضبط الحد الأقصى لمسافة الإطار بين الإطارات الرئيسية. تؤدي القيم المنخفضة إلى زيادة سوء كفاءة الضغط، ولكنها تعمل على تحسين أوقات البحث وقد تعمل على تحسين الجودة في المشاهد ذات الحركة السريعة. 0 يضبط هذه القيمة تلقائيًا.", "transcoding_optimal_description": "مقاطع الفيديو ذات الدقة الأعلى من الدقة المستهدفة أو بتنسيق غير مقبول", @@ -324,6 +351,7 @@ "user_delete_delay_settings_description": "عدد الأيام بعد الإزالة لحذف حساب المستخدم ومحتوياته بشكل دائم. تقوم وظيفة حذف المستخدم بالتشغيل في منتصف الليل للتحقق من المستخدمين الجاهزين للحذف. سيتم تقييم التغييرات على هذا الإعداد في التنفيذ القادم.", "user_delete_immediately": "سيتم وضع حساب {user} ومحتوياته في قائمة الانتظار للحذف الدائم على الفور.", "user_delete_immediately_checkbox": "قائمة انتظار المستخدم والمحتويات للحذف الفوري", + "user_details": "تفاصيل المستخدم", "user_management": "إدارة المستخدم", "user_password_has_been_reset": "تمت إعادة تعيين كلمة المرور الخاصة بالمستخدم:", "user_password_reset_description": "يرجى تزويد المستخدم بكلمة المرور المؤقتة وإبلاغه بأنه سيحتاج إلى تغيير كلمة المرور عند تسجيل الدخول التالي.", @@ -343,9 +371,17 @@ "admin_password": "كلمة سر المشرف", "administration": "الإدارة", "advanced": "متقدم", + "advanced_settings_enable_alternate_media_filter_subtitle": "استخدم هذا الخيار لتصفية الوسائط اثناء المزامنه بناء على معايير بديلة. جرب هذا الخيار فقط كان لديك مشاكل مع التطبيق بالكشف عن جميع الالبومات.", + "advanced_settings_enable_alternate_media_filter_title": "[تجريبي] استخدم جهاز تصفية مزامنه البومات بديل", + "advanced_settings_log_level_title": "مستوى السجل: {level}", "advanced_settings_prefer_remote_subtitle": "تكون بعض الأجهزة بطيئة للغاية في تحميل الصور المصغرة من الأصول الموجودة على الجهاز. قم بتنشيط هذا الإعداد لتحميل الصور البعيدة بدلاً من ذلك.", "advanced_settings_prefer_remote_title": "تفضل الصور البعيدة", + "advanced_settings_proxy_headers_subtitle": "عرف عناوين الوكيل التي يستخدمها Immich لارسال كل طلب شبكي", + "advanced_settings_proxy_headers_title": "عناوين الوكيل", + "advanced_settings_self_signed_ssl_subtitle": "تخطي التحقق من شهادة SSL لخادم النقطة النهائي. مكلوب للشهادات الموقعة ذاتيا.", "advanced_settings_self_signed_ssl_title": "السماح بشهادات SSL الموقعة ذاتيًا", + "advanced_settings_sync_remote_deletions_subtitle": "حذف او استعادة تلقائي للاصول على هذا الجهاز عند تنفيذ العملية على الويب", + "advanced_settings_sync_remote_deletions_title": "مزامنة عمليات الحذف عن بعد [تجريبي]", "advanced_settings_tile_subtitle": "إعدادات المستخدم المتقدمة", "advanced_settings_troubleshooting_subtitle": "تمكين الميزات الإضافية لاستكشاف الأخطاء وإصلاحها", "advanced_settings_troubleshooting_title": "استكشاف الأخطاء وإصلاحها", @@ -382,6 +418,9 @@ "album_with_link_access": "السماح لأي شخص لديه الرابط برؤية الصور والأشخاص الموجودين في هذا الألبوم.", "albums": "الألبومات", "albums_count": "{count, plural, one {{count, number} ألبوم} other {{count, number} ألبومات}}", + "albums_default_sort_order": "ترتيب الألبوم الافتراضي", + "albums_default_sort_order_description": "ترتيب فرز الأصول الأولي عند إنشاء ألبومات جديدة.", + "albums_feature_description": "مجموعة من الأصول التي يمكن مشاركتها مع مستخدمين آخرين.", "all": "الكل", "all_albums": "جميع الألبومات", "all_people": "جميع الأشخاص", @@ -392,20 +431,23 @@ "allow_public_user_to_upload": "السماح للمستخدم العام بالرفع", "alt_text_qr_code": "صورة رمز الاستجابة السريعة (QR)", "anti_clockwise": "عكس اتجاه عقارب الساعة", - "api_key": "مفتاح واجهة برمجة التطبيقات", + "api_key": "مفتاح API", "api_key_description": "سيتم عرض هذه القيمة مرة واحدة فقط. يرجى التأكد من نسخها قبل إغلاق النافذة.", "api_key_empty": "يجب ألا يكون اسم مفتاح API فارغًا", - "api_keys": "مفاتيح واجهة برمجة التطبيقات", + "api_keys": "مفاتيح API", "app_bar_signout_dialog_content": "هل أنت متأكد أنك تريد الخروج", "app_bar_signout_dialog_ok": "نعم", "app_bar_signout_dialog_title": "خروج", "app_settings": "إعدادات التطبيق", "appears_in": "يظهر في", "archive": "الأرشيف", + "archive_action_prompt": "{count} اضيف إلى الارشيف", "archive_or_unarchive_photo": "أرشفة الصورة أو إلغاء أرشفتها", "archive_page_no_archived_assets": "لم يتم العثور على الأصول المؤرشفة", + "archive_page_title": "ارشيف ({count})", "archive_size": "حجم الأرشيف", "archive_size_description": "تكوين حجم الأرشيف للتنزيلات (بالجيجابايت)", + "archived": "مؤرشفة", "archived_count": "{count, plural, other {الأرشيف #}}", "are_these_the_same_person": "هل هؤلاء هم نفس الشخص؟", "are_you_sure_to_do_this": "هل انت متأكد من أنك تريد أن تفعل هذا؟", @@ -427,37 +469,55 @@ "asset_list_settings_title": "شبكة الصور", "asset_offline": "المحتوى غير اتصال", "asset_offline_description": "لم يعد هذا الأصل الخارجي موجودًا على القرص. يرجى الاتصال بمسؤول Immich للحصول على المساعدة.", + "asset_restored_successfully": "تم استعادة الاصل بنجاح", "asset_skipped": "تم تخطيه", "asset_skipped_in_trash": "في سلة المهملات", "asset_uploaded": "تم الرفع", "asset_uploading": "جارٍ الرفع…", + "asset_viewer_settings_subtitle": "إدارة إعدادات عارض المعرض الخاص بك", "asset_viewer_settings_title": "عارض الأصول", "assets": "المحتويات", "assets_added_count": "تمت إضافة {count, plural, one {# محتوى} other {# محتويات}}", "assets_added_to_album_count": "تمت إضافة {count, plural, one {# الأصل} other {# الأصول}} إلى الألبوم", - "assets_added_to_name_count": "تم إضافة {count, plural, one {# محتوى} other {# محتويات }} إلى {hasName, select, true {{name}} other {ألبوم جديد}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} لايمكن اضافته الى الالبوم", "assets_count": "{count, plural, one {# محتوى} other {# محتويات}}", + "assets_deleted_permanently": "{count} الاص(و)ل المحذوف(ه) بشكل دائم", + "assets_deleted_permanently_from_server": "{count} الاص(و)ل المحذوف(ه) بشكل دائمي من خادم Immich", + "assets_downloaded_failed": "{count, plural, one {تم التحميل # ملف - {error} ملف فشل} other {تم التحميل # ملفات - {error} ملفات فشلت}}", + "assets_downloaded_successfully": "{count, plural, one {تم التحميل # ملف بنجاح} other {تم التحميل # ملفات بنجاح}}", "assets_moved_to_trash_count": "تم نقل {count, plural, one {# محتوى} other {# محتويات}} إلى سلة المهملات", "assets_permanently_deleted_count": "تم حذف {count, plural, one {# هذا المحتوى} other {# هذه المحتويات}} بشكل دائم", "assets_removed_count": "تمت إزالة {count, plural, one {# محتوى} other {# محتويات}}", + "assets_removed_permanently_from_device": "{count} الاص(و)ل محذوف(ه) من الجهاز", "assets_restore_confirmation": "هل أنت متأكد من أنك تريد استعادة جميع الأصول المحذوفة؟ لا يمكنك التراجع عن هذا الإجراء! لاحظ أنه لا يمكن استعادة أي أصول غير متصلة بهذه الطريقة.", "assets_restored_count": "تمت استعادة {count, plural, one {# محتوى} other {# محتويات}}", + "assets_restored_successfully": "{count} الاص(و)ل المستعاد(ه) بنجاح", + "assets_trashed": "{count} الاصل(و) ل المنقوله الى سلة المهملات", "assets_trashed_count": "تم إرسال {count, plural, one {# محتوى} other {# محتويات}} إلى سلة المهملات", + "assets_trashed_from_server": "{count} الاص(و)ل المنقولة الى سلة المهملات من خادم Immich", "assets_were_part_of_album_count": "{count, plural, one {هذا المحتوى} other {هذه المحتويات}} في الألبوم بالفعل", "authorized_devices": "الأجهزه المخولة", + "automatic_endpoint_switching_subtitle": "اتصل محليا من خلال شبكه Wi-Fi عند توفرها و استخدم اتصالات بديله في الاماكن الاخرى", + "automatic_endpoint_switching_title": "تبديل URL تلقائي", + "autoplay_slideshow": "تشغيل تلقائي لعرض الشرائح", "back": "خلف", "back_close_deselect": "الرجوع أو الإغلاق أو إلغاء التحديد", + "background_location_permission": "اذن الوصول للموقع في الخلفية", + "background_location_permission_content": "للتمكن من تبديل الشبكه بالخلفية، Immich يحتاج*دائما* للحصول على موقع دقيق ليتمكن التطبيق من قرائة اسم شبكة الWi-Fi", + "backup_album_selection_page_albums_device": "الالبومات على الجهاز ({count})", "backup_album_selection_page_albums_tap": "انقر للتضمين، وانقر نقرًا مزدوجًا للاستثناء", "backup_album_selection_page_assets_scatter": "يمكن أن تنتشر الأصول عبر ألبومات متعددة. وبالتالي، يمكن تضمين الألبومات أو استبعادها أثناء عملية النسخ الاحتياطي.", "backup_album_selection_page_select_albums": "حدد الألبومات", "backup_album_selection_page_selection_info": "معلومات الاختيار", "backup_album_selection_page_total_assets": "إجمالي الأصول الفريدة", "backup_all": "الجميع", - "backup_background_service_backup_failed_message": "فشل في النسخ الاحتياطي للأصول. جارٍ إعادة المحاولة...", - "backup_background_service_connection_failed_message": "فشل في الاتصال بالخادم. جارٍ إعادة المحاولة...", - "backup_background_service_default_notification": "التحقق من الأصول الجديدة ...", + "backup_background_service_backup_failed_message": "فشل في النسخ الاحتياطي للأصول. جارٍ إعادة المحاولة…", + "backup_background_service_connection_failed_message": "فشل في الاتصال بالخادم. جارٍ إعادة المحاولة…", + "backup_background_service_current_upload_notification": "تحميل {filename}", + "backup_background_service_default_notification": "التحقق من الأصول الجديدة…", "backup_background_service_error_title": "خطأ في النسخ الاحتياطي", - "backup_background_service_in_progress_notification": "النسخ الاحتياطي للأصول الخاصة بك...", + "backup_background_service_in_progress_notification": "النسخ الاحتياطي للأصول الخاصة بك…", + "backup_background_service_upload_failure_notification": "فشل تحميل {filename}", "backup_controller_page_albums": "ألبومات احتياطية", "backup_controller_page_background_app_refresh_disabled_content": "قم بتمكين تحديث تطبيق الخلفية في الإعدادات > عام > تحديث تطبيق الخلفية لاستخدام النسخ الاحتياطي في الخلفية.", "backup_controller_page_background_app_refresh_disabled_title": "تم تعطيل تحديث التطبيق في الخلفية", @@ -468,17 +528,22 @@ "backup_controller_page_background_battery_info_title": "تحسين البطارية", "backup_controller_page_background_charging": "فقط أثناء الشحن", "backup_controller_page_background_configure_error": "فشل في تكوين خدمة الخلفية", + "backup_controller_page_background_delay": "تاخير الخزن التلقائي للاصول: {duration}", "backup_controller_page_background_description": "قم بتشغيل خدمة الخلفية لإجراء نسخ احتياطي لأي أصول جديدة تلقائيًا دون الحاجة إلى فتح التطبيق", "backup_controller_page_background_is_off": "تم إيقاف النسخ الاحتياطي التلقائي للخلفية", "backup_controller_page_background_is_on": "النسخ الاحتياطي التلقائي للخلفية قيد التشغيل", "backup_controller_page_background_turn_off": "قم بإيقاف تشغيل خدمة الخلفية", "backup_controller_page_background_turn_on": "قم بتشغيل خدمة الخلفية", - "backup_controller_page_background_wifi": "فقط على واي فاي", + "backup_controller_page_background_wifi": "فقط على Wi-Fi", "backup_controller_page_backup": "دعم", "backup_controller_page_backup_selected": "المحدد: ", "backup_controller_page_backup_sub": "النسخ الاحتياطي للصور ومقاطع الفيديو", + "backup_controller_page_created": "انشئ في :{date}", "backup_controller_page_desc_backup": "قم بتشغيل النسخ الاحتياطي الأمامي لتحميل الأصول الجديدة تلقائيًا إلى الخادم عند فتح التطبيق.", "backup_controller_page_excluded": "مستبعد: ", + "backup_controller_page_failed": "فشل ({count})", + "backup_controller_page_filename": "اسم الملف : {filename} [{size}]", + "backup_controller_page_id": "هوية: {id}", "backup_controller_page_info": "معلومات النسخ الاحتياطي", "backup_controller_page_none_selected": "لم يتم التحديد", "backup_controller_page_remainder": "بقية", @@ -487,7 +552,8 @@ "backup_controller_page_start_backup": "بدء النسخ الاحتياطي", "backup_controller_page_status_off": "النسخة الاحتياطية التلقائية غير فعالة", "backup_controller_page_status_on": "النسخة الاحتياطية التلقائية فعالة", - "backup_controller_page_to_backup": "الألبومات الاحتياطية", + "backup_controller_page_storage_format": "{used} من {total} مستخدم", + "backup_controller_page_to_backup": "الألبومات التي سيتم نسخها احتياطيا", "backup_controller_page_total_sub": "جميع الصور ومقاطع الفيديو الفريدة من ألبومات مختارة", "backup_controller_page_turn_off": "قم بإيقاف تشغيل النسخ الاحتياطي المقدمة", "backup_controller_page_turn_on": "قم بتشغيل النسخ الاحتياطي المقدمة", @@ -499,7 +565,12 @@ "backup_manual_success": "نجاح", "backup_manual_title": "حالة التحميل", "backup_options_page_title": "خيارات النسخ الاحتياطي", + "backup_setting_subtitle": "ادارة اعدادات التحميل في الخلفية والمقدمة", "backward": "الى الوراء", + "biometric_auth_enabled": "المصادقة البايومترية مفعله", + "biometric_locked_out": "لقد قفلت عنك المصادقة البيومترية", + "biometric_no_options": "لا توجد خيارات بايومترية متوفرة", + "biometric_not_available": "االمصادقة البيومترية غير متاحة على هذا الجهاز", "birthdate_saved": "تم حفظ تاريخ الميلاد بنجاح", "birthdate_set_description": "يتم استخدام تاريخ الميلاد لحساب عمر هذا الشخص وقت التقاط الصورة.", "blurred_background": "خلفية مشوشة", @@ -514,12 +585,13 @@ "cache_settings_clear_cache_button_title": "يقوم بمسح ذاكرة التخزين المؤقت للتطبيق.سيؤثر هذا بشكل كبير على أداء التطبيق حتى إعادة بناء ذاكرة التخزين المؤقت.", "cache_settings_duplicated_assets_clear_button": "واضح", "cache_settings_duplicated_assets_subtitle": "الصور ومقاطع الفيديو اللتي تم تجاهلها المدرجة في التطبيق", + "cache_settings_duplicated_assets_title": "الاصول المكررة ({count})", "cache_settings_statistics_album": "مكتبه الصور المصغره", "cache_settings_statistics_full": "صور كاملة", "cache_settings_statistics_shared": "صورة ألبوم مشتركة", "cache_settings_statistics_thumbnail": "الصورة المصغرة", "cache_settings_statistics_title": "استخدام ذاكرة التخزين المؤقت", - "cache_settings_subtitle": "تحكم في سلوك التخزين المؤقت لتطبيق الجوال.", + "cache_settings_subtitle": "تحكم في سلوك التخزين المؤقت لتطبيق Immich الجوال", "cache_settings_tile_subtitle": "التحكم في سلوك التخزين المحلي", "cache_settings_tile_title": "التخزين المحلي", "cache_settings_title": "إعدادات التخزين المؤقت", @@ -527,11 +599,16 @@ "camera_brand": "علامة الكاميرا التجارية", "camera_model": "طراز الكاميرا", "cancel": "إلغاء", - "cancel_search": "الغي البحث", + "cancel_search": "الغاء البحث", + "canceled": "تم الالغاء", "cannot_merge_people": "لا يمكن دمج الأشخاص", "cannot_undo_this_action": "لا يمكنك التراجع عن هذا الإجراء!", "cannot_update_the_description": "لا يمكن تحديث الوصف", + "cast": "بث", + "cast_description": "ضبط وجهات البث المتوفرة", "change_date": "غيّر التاريخ", + "change_description": "تغيير الوصف", + "change_display_order": "تغيير ترتيب العرض", "change_expiration_time": "تغيير وقت انتهاء الصلاحية", "change_location": "غيّر الموقع", "change_name": "تغيير الإسم", @@ -543,9 +620,12 @@ "change_password_form_new_password": "كلمة المرور الجديدة", "change_password_form_password_mismatch": "كلمة المرور غير مطابقة", "change_password_form_reenter_new_password": "أعد إدخال كلمة مرور جديدة", - "change_pin_code": "تغيير الرقم السري", + "change_pin_code": "تغيير رمز PIN", "change_your_password": "غير كلمة المرور الخاصة بك", "changed_visibility_successfully": "تم تغيير الرؤية بنجاح", + "check_corrupt_asset_backup": "التحقق من وجود نسخ احتياطية فاسدة للاصول", + "check_corrupt_asset_backup_button": "اجراء فحص", + "check_corrupt_asset_backup_description": "قم بإجراء هذا الفحص فقط عبر شبكة Wi-Fi وبعد نسخ جميع الأصول احتياطيًا. قد يستغرق الإجراء بضع دقائق.", "check_logs": "تحقق من السجلات", "choose_matching_people_to_merge": "اختر الأشخاص المتطابقين لدمجهم", "city": "المدينة", @@ -554,6 +634,14 @@ "clear_all_recent_searches": "مسح جميع عمليات البحث الأخيرة", "clear_message": "إخلاء الرسالة", "clear_value": "إخلاء القيمة", + "client_cert_dialog_msg_confirm": "حسنا", + "client_cert_enter_password": "ادخل كلمة سر", + "client_cert_import": "استيراد", + "client_cert_import_success_msg": "تم استيراد شهادة العميل", + "client_cert_invalid_msg": "ملف شهادة عميل غير صالحة او كلمة سر غير صحيحة", + "client_cert_remove_msg": "تم ازالة شهادة العميل", + "client_cert_subtitle": "يدعم صيغ PKCS12 (.p12, .pfx)فقط. استيراد/ازالة الشهادات متاح فقط قبل تسجيل الدخول", + "client_cert_title": "شهادة مستخدم SSL", "clockwise": "باتجاه عقارب الساعة", "close": "إغلاق", "collapse": "طي", @@ -566,21 +654,27 @@ "comments_are_disabled": "التعليقات معطلة", "common_create_new_album": "إنشاء ألبوم جديد", "common_server_error": "يرجى التحقق من اتصال الشبكة الخاص بك ، والتأكد من أن الجهاز قابل للوصول وإصدارات التطبيق/الجهاز متوافقة.", + "completed": "اكتمل", "confirm": "تأكيد", "confirm_admin_password": "تأكيد كلمة مرور المسؤول", "confirm_delete_face": "هل أنت متأكد من حذف وجه {name} من الأصول؟", "confirm_delete_shared_link": "هل أنت متأكد أنك تريد حذف هذا الرابط المشترك؟", "confirm_keep_this_delete_others": "سيتم حذف جميع الأصول الأخرى في المجموعة باستثناء هذا الأصل. هل أنت متأكد من أنك تريد المتابعة؟", - "confirm_new_pin_code": "ثبت الرقم السري الجديد", + "confirm_new_pin_code": "ثبت رمز PIN الجديد", "confirm_password": "تأكيد كلمة المرور", + "confirm_tag_face": "هل تريد وضع علامة على هذا الوجه {name}؟", + "confirm_tag_face_unnamed": "هل تريد وضع علامة على هذا الوجه؟", + "connected_device": "جهاز متصل", + "connected_to": "متصل ب", "contain": "محتواة", "context": "السياق", "continue": "متابعة", "control_bottom_app_bar_create_new_album": "إنشاء ألبوم جديد", - "control_bottom_app_bar_delete_from_immich": " حذف منال تطبيق", + "control_bottom_app_bar_delete_from_immich": "حذف من Immich", "control_bottom_app_bar_delete_from_local": "حذف من الجهاز", "control_bottom_app_bar_edit_location": "تحديد الوجهة", "control_bottom_app_bar_edit_time": "تحرير التاريخ والوقت", + "control_bottom_app_bar_share_link": "مشاركة رابط", "control_bottom_app_bar_share_to": "مشاركة إلى", "control_bottom_app_bar_trash_from_immich": "حذفه ونقله في سله المهملات", "copied_image_to_clipboard": "تم نسخ الصورة إلى الحافظة.", @@ -602,6 +696,7 @@ "create_link": "إنشاء رابط", "create_link_to_share": "إنشاء رابط للمشاركة", "create_link_to_share_description": "السماح لأي شخص لديه الرابط بمشاهدة الصورة (الصور) المحددة", + "create_new": "انشاء جديد", "create_new_person": "إنشاء شخص جديد", "create_new_person_hint": "تعيين المحتويات المحددة لشخص جديد", "create_new_user": "إنشاء مستخدم جديد", @@ -611,14 +706,18 @@ "create_tag_description": "أنشئ علامة جديدة. بالنسبة للعلامات المتداخلة، يرجى إدخال المسار الكامل للعلامة بما في ذلك الخطوط المائلة للأمام.", "create_user": "إنشاء مستخدم", "created": "تم الإنشاء", + "created_at": "مخلوق", + "crop": "قص", "curated_object_page_title": "أشياء", "current_device": "الجهاز الحالي", - "current_pin_code": "الرقم السري الحالي", + "current_pin_code": "رمز PIN الحالي", + "current_server_address": "عنوان الخادم الحالي", "custom_locale": "لغة مخصصة", "custom_locale_description": "تنسيق التواريخ والأرقام بناءً على اللغة والمنطقة", "daily_title_text_date": "E ، MMM DD", "daily_title_text_date_year": "E ، MMM DD ، yyyy", "dark": "معتم", + "dark_theme": "تبديل المظهر الداكن", "date_after": "التارخ بعد", "date_and_time": "التاريخ و الوقت", "date_before": "التاريخ قبل", @@ -634,11 +733,12 @@ "default_locale": "اللغة الافتراضية", "default_locale_description": "تنسيق التواريخ والأرقام بناءً على لغة المتصفح الخاص بك", "delete": "حذف", + "delete_action_prompt": "{count} حذف بشكل نهائي", "delete_album": "حذف الألبوم", "delete_api_key_prompt": "هل أنت متأكد أنك تريد حذف مفتاح API هذا؟", - "delete_dialog_alert": " هذه العناصر سيتم حذفها بشكل دائم من جهازك ومن تطبيق", - "delete_dialog_alert_local": " العناصر التي تم حذفها من جهازك ولكنها موجوده في تطبيق", - "delete_dialog_alert_local_non_backed_up": "بعض العناصر التي سيتم حذفها بشكل دائم ولا يوجد لها نسخه احتياطيه في تطبيق ", + "delete_dialog_alert": "هذه العناصر سيتم حذفها بشكل دائم من Immich و من جهازك", + "delete_dialog_alert_local": "العناصر التي سيتم حذفها من جهازك ولكن تبقى موجوده في خادم Immich", + "delete_dialog_alert_local_non_backed_up": "بعض العناصر غير مدعومة بنسخة احتياطية على Immich وسيتم إزالتها نهائيًا من جهازك", "delete_dialog_alert_remote": "العناصر التي سيتم حذفها بشكل دائم من تطبيق", "delete_dialog_ok_force": "احذف على أي حال", "delete_dialog_title": "الحذف بشكل نهائي", @@ -664,7 +764,9 @@ "direction": "الإتجاه", "disabled": "معطل", "disallow_edits": "منع التعديلات", + "discord": "دسكورد", "discover": "اكتشف", + "discovered_devices": "اجهزة مكتشفة", "dismiss_all_errors": "تجاهل كافة الأخطاء", "dismiss_error": "تجاهل الخطأ", "display_options": "عرض الخيارات", @@ -675,12 +777,25 @@ "documentation": "الوثائق", "done": "تم", "download": "تنزيل", + "download_canceled": "الغي التنزيل", + "download_complete": "اكتمل التنزيل", + "download_enqueue": "تنزيل في قائمة الانتظار", + "download_error": "خطا في التنزيل", + "download_failed": "فشل التنزيل", + "download_finished": "انتهى التنزيل", "download_include_embedded_motion_videos": "مقاطع الفيديو المدمجة", "download_include_embedded_motion_videos_description": "تضمين مقاطع الفيديو المضمنة في الصور المتحركة كملف منفصل", + "download_notfound": "لم يعثر على التنزيل", + "download_paused": "اوقف التنزيل", "download_settings": "التنزيلات", "download_settings_description": "إدارة الإعدادات المتعلقة بتنزيل المحتويات", + "download_started": "بدا التنزيل", + "download_sucess": "نجح التنزيل", + "download_sucess_android": "تم تحميل الوسائط الى DCIM/Immich", + "download_waiting_to_retry": "الانتظار للمحاولة", "downloading": "جارٍ التنزيل", "downloading_asset_filename": "{filename} قيد التنزيل", + "downloading_media": "تحميل الوسائط", "drop_files_to_upload": "قم بإسقاط الملفات في أي مكان لرفعها", "duplicates": "التكرارات", "duplicates_description": "قم بحل كل مجموعة من خلال الإشارة إلى التكرارات، إن وجدت", @@ -690,6 +805,8 @@ "edit_avatar": "تعديل الصورة الشخصية", "edit_date": "تعديل التاريخ", "edit_date_and_time": "تعديل التاريخ والوقت", + "edit_description": "تعديل الوصف", + "edit_description_prompt": "الرجاء اختيار وصف جديد:", "edit_exclusion_pattern": "تعديل نمط الاستبعاد", "edit_faces": "تعديل الوجوه", "edit_import_path": "تعديل مسار الاستيراد", @@ -697,6 +814,7 @@ "edit_key": "تعديل المفتاح", "edit_link": "تغيير الرابط", "edit_location": "تعديل الموقع", + "edit_location_action_prompt": "{count} موقع تم تعديله", "edit_location_dialog_title": "موقع", "edit_name": "تعديل الاسم", "edit_people": "تعديل الأشخاص", @@ -710,15 +828,24 @@ "editor_crop_tool_h2_aspect_ratios": "نسب العرض إلى الارتفاع", "editor_crop_tool_h2_rotation": "التدوير", "email": "البريد الإلكتروني", + "email_notifications": "تنبيهات البريد الالكتروني", + "empty_folder": "هذا المجلد فارغ", "empty_trash": "أفرغ سلة المهملات", "empty_trash_confirmation": "هل أنت متأكد أنك تريد إفراغ سلة المهملات؟ سيؤدي هذا إلى إزالة جميع المحتويات الموجودة في سلة المهملات بشكل نهائي من Immich.\nلا يمكنك التراجع عن هذا الإجراء!", "enable": "تفعيل", + "enable_biometric_auth_description": "أدخل رمز PIN الخاص بك لتمكين المصادقة البيومترية", "enabled": "مفعل", "end_date": "تاريخ الإنتهاء", - "enter_wifi_name": "Enter WiFi name", + "enqueued": "مُدرج في الطابور", + "enter_wifi_name": "ادخل اسم Wi-Fi", + "enter_your_pin_code": "أدخل رمز PIN الخاص بك", + "enter_your_pin_code_subtitle": "أدخل رمز PIN الخاص بك للوصول إلى المجلد المقفل", "error": "خطأ", + "error_change_sort_album": "فشل في تغيير ترتيب الألبوم", "error_delete_face": "حدث خطأ في حذف الوجه من الأصول", "error_loading_image": "حدث خطأ أثناء تحميل الصورة", + "error_saving_image": "خطأ: {error}", + "error_tag_face_bounding_box": "خطأ في وضع علامة على الوجه - لا يمكن الحصول على إحداثيات المربع المحيط", "error_title": "خطأ - حدث خللٌ ما", "errors": { "cannot_navigate_next_asset": "لا يمكن الانتقال إلى المحتوى التالي", @@ -746,10 +873,12 @@ "failed_to_keep_this_delete_others": "فشل في الاحتفاظ بهذا الأصل وحذف الأصول الأخرى", "failed_to_load_asset": "فشل تحميل المحتوى", "failed_to_load_assets": "فشل تحميل المحتويات", + "failed_to_load_notifications": "فشل تحميل الإشعارات", "failed_to_load_people": "فشل تحميل الأشخاص", "failed_to_remove_product_key": "تعذر إزالة مفتاح المنتج", "failed_to_stack_assets": "فشل في تكديس المحتويات", "failed_to_unstack_assets": "فشل في فصل المحتويات", + "failed_to_update_notification_status": "فشل في تحديث حالة الإشعار", "import_path_already_exists": "مسار الاستيراد هذا موجود مسبقًا.", "incorrect_email_or_password": "بريد أو كلمة مرور غير صحيحة", "paths_validation_failed": "فشل في التحقق من {paths, plural, one {# مسار} other {# مسارات}}", @@ -766,6 +895,7 @@ "unable_to_archive_unarchive": "تعذر {archived, select, true {الأرشفة} other {الإخراج من الأرشيف}}", "unable_to_change_album_user_role": "غير قادر على تغيير دور مستخدم الألبوم", "unable_to_change_date": "غير قادر على تغيير التاريخ", + "unable_to_change_description": "غير قادر على تغيير الوصف", "unable_to_change_favorite": "غير قادر على تغيير المفضلة لمحتوى", "unable_to_change_location": "غير قادر على تغيير الموقع", "unable_to_change_password": "غير قادر على تغيير كلمة المرور", @@ -809,6 +939,7 @@ "unable_to_remove_partner": "غير قادر على إزالة الشريك", "unable_to_remove_reaction": "غير قادر على إزالة رد الفعل", "unable_to_reset_password": "غير قادر على إعادة تعيين كلمة المرور", + "unable_to_reset_pin_code": "غير قادر على إعادة تعيين رمز PIN", "unable_to_resolve_duplicate": "غير قادر على حل التكرارات", "unable_to_restore_assets": "غير قادر على استعادة المحتويات", "unable_to_restore_trash": "غير قادر على استعادة سلة المهملات", @@ -842,6 +973,9 @@ "exif_bottom_sheet_location": "موقع", "exif_bottom_sheet_people": "الناس", "exif_bottom_sheet_person_add_person": "اضف اسما", + "exif_bottom_sheet_person_age_months": "العمر {months} اشهر", + "exif_bottom_sheet_person_age_year_months": "العمر ١ سنة،{months} اشهر", + "exif_bottom_sheet_person_age_years": "العمر {years}", "exit_slideshow": "خروج من العرض التقديمي", "expand_all": "توسيع الكل", "experimental_settings_new_asset_list_subtitle": "أعمال جارية", @@ -858,10 +992,15 @@ "extension": "الإمتداد", "external": "خارجي", "external_libraries": "المكتبات الخارجية", - "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", + "external_network": "شبكة خارجية", + "external_network_sheet_info": "عندما لا يتواجد على شبكة Wi-Fi المفضلة، فإنه سيتصل بالخادم من خلال أول عناوين URL أدناه التي يمكنه الوصول إليها، بدءًا من الأعلى إلى الأسفل", "face_unassigned": "غير معين", + "failed": "فشل", + "failed_to_authenticate": "فشل في المصادقة", "failed_to_load_assets": "فشل تحميل الأصول", + "failed_to_load_folder": "فشل تحميل المجلد", "favorite": "مفضل", + "favorite_action_prompt": "{count} اضيف إلى المفضلات", "favorite_or_unfavorite_photo": "تفضيل أو إلغاء تفضيل الصورة", "favorites": "المفضلة", "favorites_page_no_favorites": "لم يتم العثور على الأصول المفضلة", @@ -872,18 +1011,26 @@ "file_name_or_extension": "اسم الملف أو امتداده", "filename": "اسم الملف", "filetype": "نوع الملف", + "filter": "تصفية", "filter_people": "تصفية الاشخاص", + "filter_places": "تصفية الاماكن", "find_them_fast": "يمكنك العثور عليها بسرعة بالاسم من خلال البحث", "fix_incorrect_match": "إصلاح المطابقة غير الصحيحة", + "folder": "مجلد", + "folder_not_found": "لم يتم العثور على المجلد", "folders": "المجلدات", "folders_feature_description": "تصفح عرض المجلد للصور ومقاطع الفيديو الموجودة على نظام الملفات", "forward": "إلى الأمام", + "gcast_enabled": "كوكل كاست", + "gcast_enabled_description": "تقوم هذه الميزة بتحميل الموارد الخارجية من Google حتى تعمل.", "general": "عام", "get_help": "الحصول على المساعدة", + "get_wifiname_error": "تعذر الحصول على اسم شبكة Wi-Fi. تأكد من منح الأذونات اللازمة واتصالك بشبكة Wi-Fi", "getting_started": "البدء", "go_back": "الرجوع للخلف", "go_to_folder": "اذهب إلى المجلد", "go_to_search": "اذهب إلى البحث", + "grant_permission": "منح الاذن", "group_albums_by": "تجميع الألبومات حسب...", "group_country": "مجموعة البلد", "group_no": "بدون تجميع", @@ -893,6 +1040,12 @@ "haptic_feedback_switch": "تمكين ردود الفعل اللمسية", "haptic_feedback_title": "ردود فعل لمسية", "has_quota": "محدد بحصة", + "header_settings_add_header_tip": "اضاف راس", + "header_settings_field_validator_msg": "القيمة لا يمكن ان تكون فارغة", + "header_settings_header_name_input": "اسم الرأس", + "header_settings_header_value_input": "قيمة الرأس", + "headers_settings_tile_subtitle": "قم بتعريف رؤوس الوكيل التي يجب أن يرسلها التطبيق مع كل طلب شبكة", + "headers_settings_tile_title": "رؤوس وكيل مخصصة", "hi_user": "مرحبا {name} ({email})", "hide_all_people": "إخفاء جميع الأشخاص", "hide_gallery": "اخفاء المعرض", @@ -911,11 +1064,16 @@ "home_page_delete_remote_err_local": "الأصول المحلية في التحديد البعيد المحذوف، سوف يتخطى", "home_page_favorite_err_local": "لا يمكن تفضيل الأصول المحلية بعد، سوف يتخطى", "home_page_favorite_err_partner": "لا يمكن الأصول الشريكة المفضلة بعد ، سوف يتخطى", - "home_page_first_time_notice": "إذا كانت هذه هي المرة الأولى التي تستخدم فيها التطبيق، فيرجى التأكد من اختيار ألبوم (ألبومات) احتياطية حتى يتمكن المخطط الزمني من ملء الصور ومقاطع الفيديو في الألبوم (الألبومات).", + "home_page_first_time_notice": "إذا كانت هذه هي المرة الأولى التي تستخدم فيها التطبيق، فيرجى التأكد من اختيار ألبوم (ألبومات) احتياطية حتى يتمكن المخطط الزمني من ملء الصور ومقاطع الفيديو فيه", + "home_page_locked_error_local": "لا يمكن نقل الأصول المحلية إلى المجلد المقفل، يتم التخطي", + "home_page_locked_error_partner": "لا يمكن نقل أصول الشريك إلى المجلد المقفل، يتم التخطي", "home_page_share_err_local": "لا يمكن مشاركة الأصول المحلية عبر الرابط ، سوف يتخطى", "home_page_upload_err_limit": "لا يمكن إلا تحميل 30 أحد الأصول في وقت واحد ، سوف يتخطى", "host": "المضيف", "hour": "ساعة", + "id": "المعرف", + "ignore_icloud_photos": "تجاهل صور iCloud", + "ignore_icloud_photos_description": "الصور المخزنة في Cloud لن يتم تحميلها إلى خادم Immich", "image": "صورة", "image_alt_text_date": "{isVideo, select, true {Video} other {Image}} تم التقاطها في {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Image}} تم التقاطها مع {person1} في {date}", @@ -927,6 +1085,7 @@ "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Image}} تم التقاطها في {city}، {country} مع {person1} و{person2} في {date}", "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Image}} تم التقاطها في {city}، {country} مع {person1}، {person2}، و{person3} في {date}", "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} تم التقاطها في {city}, {country} with {person1}, {person2}, مع {additionalCount, number} آخرين في {date}", + "image_saved_successfully": "الصور حُفظت", "image_viewer_page_state_provider_download_started": "بدأ التنزيل", "image_viewer_page_state_provider_download_success": "تم التنزيل بنجاح", "image_viewer_page_state_provider_share_error": "خطأ في المشاركة", @@ -948,8 +1107,16 @@ "night_at_midnight": "كل ليلة عند منتصف الليل", "night_at_twoam": "كل ليلة الساعة 2 صباحا" }, + "invalid_date": "تاريخ غير صالح", + "invalid_date_format": "صيغة تاريخ غير صالحة", "invite_people": "دعوة الأشخاص", "invite_to_album": "دعوة إلى الألبوم", + "ios_debug_info_fetch_ran_at": "جرت عملية الجلب في {dateTime}", + "ios_debug_info_last_sync_at": "اخر مزامنة {dateTime}", + "ios_debug_info_no_processes_queued": "لا توجد عمليات خلفية في قائمة الانتظار", + "ios_debug_info_no_sync_yet": "لم يتم تشغيل أي مهمة مزامنة في الخلفية حتى الآن", + "ios_debug_info_processes_queued": "{count, plural, one {{count} عملية خلفية ادخلتةفي طابور} other {{count} عمليات خلفية ادخلت في طابور}}", + "ios_debug_info_processing_ran_at": "المعالجة جرت في {dateTime}", "items_count": "{count, plural, one {# عنصر} other {# عناصر}}", "jobs": "الوظائف", "keep": "احتفظ", @@ -958,6 +1125,9 @@ "kept_this_deleted_others": "تم الاحتفاظ بهذا الأصل وحذف {count, plural, one {# asset} other {# assets}}", "keyboard_shortcuts": "اختصارات لوحة المفاتيح", "language": "اللغة", + "language_no_results_subtitle": "حاول تعديل مصطلح البحث", + "language_no_results_title": "لم يتم العثور على لغات", + "language_search_hint": "البحث عن لغات...", "language_setting_description": "اختر لغتك المفضلة", "last_seen": "اخر ظهور", "latest_version": "احدث اصدار", @@ -983,21 +1153,29 @@ "list": "قائمة", "loading": "تحميل", "loading_search_results_failed": "فشل تحميل نتائج البحث", - "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", + "local_asset_cast_failed": "غير قادر على بث أصل لم يتم تحميله إلى الخادم", + "local_network": "شبكة محلية", + "local_network_sheet_info": "سيتصل التطبيق بالخادم من خلال عنوان URL هذا عند استخدام شبكة Wi-Fi المحددة", + "location_permission": "اذن الموقع", + "location_permission_content": "من أجل استخدام ميزة التبديل التلقائي، يحتاج Immich إلى إذن موقع دقيق حتى يتمكن من قراءة اسم شبكة Wi-Fi الحالية", "location_picker_choose_on_map": "اختر على الخريطة", "location_picker_latitude_error": "أدخل خط عرض صالح", "location_picker_latitude_hint": "أدخل خط العرض الخاص بك هنا", "location_picker_longitude_error": "أدخل خط الطول الصحيح", "location_picker_longitude_hint": "أدخل خط الطول هنا", + "lock": "قفل", + "locked_folder": "مجلد مقفول", "log_out": "تسجيل خروج", "log_out_all_devices": "تسجيل الخروج من كافة الأجهزة", + "logged_in_as": "تم تسجيل الدخول باسم {user}", "logged_out_all_devices": "تم تسجيل الخروج من جميع الأجهزة", "logged_out_device": "تم تسجيل الخروج من الجهاز", "login": "تسجيل الدخول", "login_disabled": "تم تعطيل تسجيل الدخول", - "login_form_api_exception": " استثناء برمجة التطبيقات. يرجى التحقق من عنوان الخادم والمحاولة مرة أخرى ", + "login_form_api_exception": "استثناء API. يرجى التحقق من عنوان URL الخادم والمحاولة مرة أخرى.", "login_form_back_button_text": "الرجوع للخلف", "login_form_email_hint": "yoursemail@email.com", + "login_form_endpoint_hint": "http://المنفذ:عنوان‫-ip-الخادم", "login_form_endpoint_url": "url نقطة نهاية الخادم", "login_form_err_http": "يرجى تحديد http:// أو https://", "login_form_err_invalid_email": "بريد إلكتروني خاطئ", @@ -1021,7 +1199,8 @@ "look": "الشكل", "loop_videos": "تكرار مقاطع الفيديو", "loop_videos_description": "فَعْل لتكرار مقطع فيديو تلقائيًا في عارض التفاصيل.", - "main_branch_warning": "أنت تستخدم إصداراً تطويرياً؛ ونحن نوصي بشدة باستخدام إصدار النشر!", + "main_branch_warning": "أنت تستخدم إصداراً قيد التطوير؛ ونحن نوصي بشدة باستخدام إصدار النشر!", + "main_menu": "القائمة الرئيسية", "make": "صنع", "manage_shared_links": "إدارة الروابط المشتركة", "manage_sharing_with_partners": "إدارة المشاركة مع الشركاء", @@ -1031,6 +1210,8 @@ "manage_your_devices": "إدارة الأجهزة التي تم تسجيل الدخول إليها", "manage_your_oauth_connection": "إدارة اتصال OAuth الخاص بك", "map": "الخريطة", + "map_assets_in_bound": "{count} صوره", + "map_assets_in_bounds": "{count} صور", "map_cannot_get_user_location": "لا يمكن الحصول على موقع المستخدم", "map_location_dialog_yes": "نعم", "map_location_picker_page_use_location": "استخدم هذا الموقع", @@ -1044,13 +1225,18 @@ "map_settings": "إعدادات الخريطة", "map_settings_dark_mode": "الوضع المظلم", "map_settings_date_range_option_day": "24 ساعة الماضية", + "map_settings_date_range_option_days": "الايام {days} الماضية", "map_settings_date_range_option_year": "السنة الفائتة", + "map_settings_date_range_option_years": "السنوات {years} الماضية", "map_settings_dialog_title": "إعدادات الخريطة", "map_settings_include_show_archived": "تشمل الأرشفة", "map_settings_include_show_partners": "تضمين الشركاء", "map_settings_only_show_favorites": "اظهار المفضلة فقط", "map_settings_theme_settings": "مظهر الخريطة", "map_zoom_to_see_photos": "قم بتصغيرها لرؤية الصور", + "mark_all_as_read": "تحديد الكل كمقروء", + "mark_as_read": "تحديد كمقروء", + "marked_all_as_read": "تم تحديد الكل كمقروء", "matches": "تطابقات", "media_type": "نوع الوسائط", "memories": "الذكريات", @@ -1075,6 +1261,13 @@ "month": "شهر", "monthly_title_text_date_format": "ط ط ط", "more": "المزيد", + "move": "تحريك", + "move_off_locked_folder": "تحريك خارج المجلد المقفل", + "move_to_lock_folder_action_prompt": "{count} اضيف إلى المجلد المقفل", + "move_to_locked_folder": "النقل الى مجلد مغلق", + "move_to_locked_folder_confirmation": "هذه الصور والفديوات ستتم ازالتها من جميع الالبومات، ويمكنان تتم مشاهدتها فقط من خلال المجلد المقفل", + "moved_to_archive": "تم نقل {count, plural, one {# اصل} other {# اصول}} الى الارشيف", + "moved_to_library": "تم نقل {count, plural, one {# اصل} other {# اصول}} الى المكتبة", "moved_to_trash": "تم النقل إلى سلة المهملات", "multiselect_grid_edit_date_time_err_read_only": "لا يمكن تعديل تاريخ الأصول (المواد) للقراءة فقط، سوف يتخطى", "multiselect_grid_edit_gps_err_read_only": "لا يمكن تعديل موقع الأصول (المواد) للقراءة فقط، سوف يتخطى", @@ -1082,12 +1275,15 @@ "my_albums": "ألبوماتي", "name": "الاسم", "name_or_nickname": "الاسم أو اللقب", + "networking_settings": "الشبكات", + "networking_subtitle": "إدارة إعدادات نقطة الخادم النهائية", "never": "أبداً", "new_album": "البوم جديد", "new_api_key": "مفتاح API جديد", "new_password": "كلمة المرور الجديدة", "new_person": "شخص جديد", - "new_pin_code": "الرقم السري الجديد", + "new_pin_code": "رمز PIN الجديد", + "new_pin_code_subtitle": "هذه أول مرة تدخل فيها إلى المجلد المقفل. أنشئ رمزًا PIN للوصول بامان إلى هذه الصفحة", "new_user_created": "تم إنشاء مستخدم جديد", "new_version_available": "إصدار جديد متاح", "newest_first": "الأحدث أولاً", @@ -1100,19 +1296,25 @@ "no_archived_assets_message": "أرشفة الصور ومقاطع الفيديو لإخفائها من عرض الصور لديك", "no_assets_message": "انقر لتحميل صورتك الأولى", "no_assets_to_show": "لا توجد أصول لعرضها", + "no_cast_devices_found": "لم يتم ايجاد جهاز بث", "no_duplicates_found": "لم يتم العثور على أي تكرارات.", "no_exif_info_available": "لا تتوفر معلومات exif", "no_explore_results_message": "قم برفع المزيد من الصور لاستكشاف مجموعتك.", "no_favorites_message": "أضف المفضلة للعثور بسرعة على أفضل الصور ومقاطع الفيديو", "no_libraries_message": "إنشاء مكتبة خارجية لعرض الصور ومقاطع الفيديو الخاصة بك", + "no_locked_photos_message": "الصور والفديوهات في المجلد المقفل مخفية ولن تصهر في التصفح او البحث في مكتبتك.", "no_name": "لا اسم", + "no_notifications": "لا توجد تنبيهات", + "no_people_found": "لم يتم العثور على اشخاص مطابقين", "no_places": "لا أماكن", "no_results": "لا يوجد نتائج", "no_results_description": "جرب كلمة رئيسية مرادفة أو أكثر عمومية", "no_shared_albums_message": "قم بإنشاء ألبوم لمشاركة الصور ومقاطع الفيديو مع الأشخاص في شبكتك", "not_in_any_album": "ليست في أي ألبوم", - "note_apply_storage_label_to_previously_uploaded assets": "ملاحظة: لتطبيق تسمية التخزين على المحتويات التي تم رفعها مسبقًا، قم بتشغيل", + "not_selected": "لم يختار", + "note_apply_storage_label_to_previously_uploaded assets": "ملاحظة: لتطبيق سمة التخزين على المحتويات التي تم رفعها مسبقًا، قم بتشغيل", "notes": "ملاحظات", + "nothing_here_yet": "لا يوجد شيء هنا بعد", "notification_permission_dialog_content": "لتمكين الإخطارات ، انتقل إلى الإعدادات و اختار السماح.", "notification_permission_list_tile_content": "منح إذن لتمكين الإخطارات.", "notification_permission_list_tile_enable_button": "تمكين الإخطارات", @@ -1120,16 +1322,22 @@ "notification_toggle_setting_description": "تفعيل إشعارات البريد الإلكتروني", "notifications": "إشعارات", "notifications_setting_description": "إدارة الإشعارات", + "oauth": "OAuth", "official_immich_resources": "الموارد الرسمية لشركة Immich", "offline": "غير متصل", "ok": "نعم", "oldest_first": "الأقدم أولا", + "on_this_device": "على هذا الجهاز", "onboarding": "الإعداد الأولي", - "onboarding_privacy_description": "تعتمد الميزات التالية (اختياري) على خدمات خارجية، ويمكن تعطيلها في أي وقت في إعدادات الإدارة.", + "onboarding_locale_description": "اختر لغتك المفضلة. يمكنك تغييرها فيما بعد في الاعدادات.", + "onboarding_privacy_description": "تعتمد الميزات التالية (اختياري) على خدمات خارجية، ويمكن تعطيلها في أي وقت في الاعدادات.", + "onboarding_server_welcome_description": "لنقم باعداد نسختك من البرنامج مع بعض الاعدادات الشائعة.", "onboarding_theme_description": "اختر نسق الألوان للنسخة الخاصة بك. يمكنك تغيير ذلك لاحقًا في إعداداتك.", + "onboarding_user_welcome_description": "لنساعدك على البدء!", "onboarding_welcome_user": "مرحبا، {user}", "online": "متصل", "only_favorites": "المفضلة فقط", + "open": "فتح", "open_in_map_view": "فتح في عرض الخريطة", "open_in_openstreetmap": "فتح في OpenStreetMap", "open_the_search_filters": "افتح مرشحات البحث", @@ -1146,12 +1354,14 @@ "partner_can_access": "يستطيع {partner} الوصول", "partner_can_access_assets": "جميع الصور ومقاطع الفيديو الخاصة بك باستثناء تلك الموجودة في المؤرشفة والمحذوفة", "partner_can_access_location": "الموقع الذي تم التقاط صورك فيه", + "partner_list_user_photos": "صور {user}", "partner_list_view_all": "عرض الكل", "partner_page_empty_message": "لم يتم مشاركة صورك بعد مع أي شريك.", "partner_page_no_more_users": "لا مزيد من المستخدمين لإضافة", "partner_page_partner_add_failed": "فشل في إضافة شريك", "partner_page_select_partner": "حدد شريكًا", "partner_page_shared_to_title": "مشترك ل", + "partner_page_stop_sharing_content": "{partner} لن يعود قادرا على الوصوف الى صورك.", "partner_sharing": "مشاركة الشركاء", "partners": "الشركاء", "password": "كلمة المرور", @@ -1180,14 +1390,16 @@ "permanently_delete_assets_prompt": "هل أنت متأكد أنك تريد حذف {count, plural, one {هذا العنصر؟} other {هذه العناصر #؟}} سيتم أيضًا إزالته {count, plural, one {من ألبومه} other {من ألبوماتهم}}.", "permanently_deleted_asset": "تم حذف الأصل بشكل نهائي", "permanently_deleted_assets_count": "تم حذف {count, plural, one {# محتوى} other {# المحتويات}} نهائيًا", + "permission": "اذن", + "permission_empty": "الاذن الخاص بك يجب ان لا يكون فارغا", "permission_onboarding_back": "خلف", "permission_onboarding_continue_anyway": "تواصل على أي حال", "permission_onboarding_get_started": "البدء", "permission_onboarding_go_to_settings": "اذهب للاعدادات", - "permission_onboarding_permission_denied": "تم رفض الإذن. لاستخدام التطبيق، قم بمنح أذونات الصور والفيديو في الإعدادات ", + "permission_onboarding_permission_denied": "تم رفض الإذن. لاستخدام التطبيق، قم بمنح أذونات الصور والفيديو في الإعدادات.", "permission_onboarding_permission_granted": "تم تأمين التصريح! وضعك تمام.", "permission_onboarding_permission_limited": "إذن محدود. للسماح بالنسخ الاحتياطي للتطبيق وإدارة مجموعة المعرض بالكامل، امنح أذونات الصور والفيديو في الإعدادات.", - "permission_onboarding_request": "يتطلب التطبيق إذنًا لعرض الصور ومقاطع الفيديو الخاصة بك", + "permission_onboarding_request": "يتطلب التطبيق إذنًا لعرض الصور ومقاطع الفيديو الخاصة بك.", "person": "شخص", "person_birthdate": "تاريخ الميلاد {التاريخ}", "person_hidden": "{name}{hidden, select, true { (مخفي)} other {}}", @@ -1197,9 +1409,10 @@ "photos_count": "{count, plural, one {{count, number} صورة} other {{count, number} صور}}", "photos_from_previous_years": "صور من السنوات السابقة", "pick_a_location": "اختر موقعًا", - "pin_code_changed_successfully": "تم تغير الرقم السري", - "pin_code_reset_successfully": "تم اعادة تعيين الرقم السري", - "pin_code_setup_successfully": "تم انشاء رقم سري", + "pin_code_changed_successfully": "تم تغير رمز PIN بنجاح", + "pin_code_reset_successfully": "تم اعادة تعيين رمز PIN بنجاح", + "pin_code_setup_successfully": "تم انشاء رمز PIN بنجاح", + "pin_verification": "التحقق برمز PIN", "place": "مكان", "places": "الأماكن", "places_count": "{count, plural, one {{count, number} مكان} other {{count, number} أماكن}}", @@ -1207,15 +1420,21 @@ "play_memories": "تشغيل الذكريات", "play_motion_photo": "تشغيل الصور المتحركة", "play_or_pause_video": "تشغيل الفيديو أو إيقافه مؤقتًا", + "please_auth_to_access": "الرجاء القيام بالمصادقة للوصول", "port": "المنفذ", + "preferences_settings_subtitle": "ادارة تفضيلات التطبيق", "preferences_settings_title": "التفضيلات", "preset": "الإعداد المسبق", "preview": "معاينة", "previous": "السابق", "previous_memory": "الذكرى السابقة", - "previous_or_next_photo": "الصورة السابقة أو التالية", + "previous_or_next_day": "يوم تالي/سابق", + "previous_or_next_month": "شهر تالي/سابق", + "previous_or_next_photo": "صورة تالية/سابقة", + "previous_or_next_year": "سنة تالية/سابقة", "primary": "أساسي", "privacy": "الخصوصية", + "profile": "حساب تعريفي", "profile_drawer_app_logs": "السجلات", "profile_drawer_client_out_of_date_major": "تطبيق الهاتف المحمول قديم.يرجى التحديث إلى أحدث إصدار رئيسي.", "profile_drawer_client_out_of_date_minor": "تطبيق الهاتف المحمول قديم.يرجى التحديث إلى أحدث إصدار صغير.", @@ -1247,7 +1466,7 @@ "purchase_lifetime_description": "الشراء لمدى الحياة", "purchase_option_title": "خيارات الشراء", "purchase_panel_info_1": "يتطلب بناء Immich الكثير من الوقت والجهد، ولدينا مهندسون يعملون بدوام كامل لجعله أفضل ما يمكن. مهمتنا هي أن تصبح البرمجيات مفتوحة المصدر وممارسات العمل الأخلاقية مصدر دخل مستدام للمطورين وإنشاء نظام بيئي يحترم الخصوصية مع بدائل حقيقية للخدمات السحابية الاستغلالية.", - "purchase_panel_info_2": "نظرًا لأننا ملتزمون بعدم إضافة نظام حظر الاشتراك غير المدفوع، فإن هذا الشراء لن يمنحك أي ميزات إضافية في Immich. نحن نعتمد على المستخدمين مثلك لدعم التطوير المستمر لـ Immich.", + "purchase_panel_info_2": "نظرًا لأننا ملتزمون بعدم إضافة حاجز دفع، فإن هذا الشراء لن يمنحك أي ميزات إضافية في Immich. نحن نعتمد على المستخدمين مثلك لدعم التطوير المستمر لـ Immich.", "purchase_panel_title": "ادعم المشروع", "purchase_per_server": "لكل خادم", "purchase_per_user": "لكل مستخدم", @@ -1272,13 +1491,16 @@ "recent": "حديث", "recent-albums": "ألبومات الحديثة", "recent_searches": "عمليات البحث الأخيرة", + "recently_added": "اضيف مؤخرا", "recently_added_page_title": "أضيف مؤخرا", + "recently_taken": "تم التقاطها مؤخرًا", + "recently_taken_page_title": "تم التقاطها مؤخرًا", "refresh": "تحديث", "refresh_encoded_videos": "تحديث مقاطع الفيديو المشفرة", "refresh_faces": "تحديث الوجوه", "refresh_metadata": "تحديث البيانات الوصفية", "refresh_thumbnails": "تحديث الصور المصغرة", - "refreshed": "تم التحديث", + "refreshed": "اعادة تحميل", "refreshes_every_file": "إعادة قراءة كافة الملفات الموجودة والجديدة", "refreshing_encoded_video": "جارٍ تحديث الفيديو المرمز", "refreshing_faces": "جاري تحديث الوجوه", @@ -1292,12 +1514,16 @@ "remove_deleted_assets": "إزالة الملفات الغير متصلة", "remove_from_album": "إزالة من الألبوم", "remove_from_favorites": "إزالة من المفضلة", + "remove_from_lock_folder_action_prompt": "{count} أويل من المجلد المقفل", + "remove_from_locked_folder": "ازالة من المجلد المقفل", + "remove_from_locked_folder_confirmation": "هل انت متأكد من ازالة هذه الصور والفيديوهات من المجلد المقفل؟ سيكونون مرئيين في المكتبة الخاصة بك.", "remove_from_shared_link": "إزالة من الرابط المشترك", "remove_memory": "إزالة الذاكرة", "remove_photo_from_memory": "إزالة الصورة من هذه الذكرى", + "remove_tag": "ازالة علامة", "remove_url": "إزالة عنوان URL", "remove_user": "إزالة المستخدم", - "removed_api_key": "تم إزالة مفتاح API: {name}", + "removed_api_key": "تم إزاة مفتاح API: ‪‫{name}", "removed_from_archive": "تمت إزالتها من الأرشيف", "removed_from_favorites": "تمت الإزالة من المفضلة", "removed_from_favorites_count": "{count, plural, other {أُزيلت #}} من التفضيلات", @@ -1315,6 +1541,7 @@ "reset": "إعادة ضبط", "reset_password": "إعادة تعيين كلمة المرور", "reset_people_visibility": "إعادة ضبط ظهور الأشخاص", + "reset_pin_code": "اعادة تعيين رمز PIN", "reset_to_default": "إعادة التعيين إلى الافتراضي", "resolve_duplicates": "معالجة النسخ المكررة", "resolved_all_duplicates": "تم حل جميع التكرارات", @@ -1329,6 +1556,7 @@ "role_editor": "المحرر", "role_viewer": "العارض", "save": "حفظ", + "save_to_gallery": "حفظ الى المعرض", "saved_api_key": "تم حفظ مفتاح الـ API", "saved_profile": "تم حفظ الملف", "saved_settings": "تم حفظ الإعدادات", @@ -1349,19 +1577,33 @@ "search_camera_model": "البحث حسب موديل الكاميرا...", "search_city": "البحث حسب المدينة...", "search_country": "البحث حسب الدولة...", - "search_filter_apply": "اختار الفلتر ", + "search_filter_apply": "اختار الفلتر", + "search_filter_camera_title": "اختر نوع الكاميرا", + "search_filter_date": "تاريخ", + "search_filter_date_interval": "{start} الى {end}", + "search_filter_date_title": "حدد نطاق التاريخ", "search_filter_display_option_not_in_album": "ليس في الألبوم", + "search_filter_display_options": "خيارات العرض", + "search_filter_filename": "بحث باستخدام الاسم", + "search_filter_location": "الموقع", + "search_filter_location_title": "اختر الموقع", + "search_filter_media_type": "نوع الوسائط", + "search_filter_media_type_title": "اختر نوع الوسائط", + "search_filter_people_title": "اختر الاشخاص", "search_for": "البحث عن", "search_for_existing_person": "البحث عن شخص موجود", + "search_no_more_result": "لا توجد نتائج اضافية", "search_no_people": "لا يوجد أشخاص", "search_no_people_named": "لا يوجد أشخاص بالاسم \"{name}\"", + "search_no_result": "لا توجد نتائج، حاول استخدام مصطلح بحث مختلف او تركيبة", "search_options": "خيارات البحث", "search_page_categories": "فئات", "search_page_motion_photos": "الصور المتحركه", "search_page_no_objects": "لا توجد معلومات عن أشياء متاحة", "search_page_no_places": "لا توجد معلومات متوفرة للأماكن", "search_page_screenshots": "لقطات الشاشة", - "search_page_selfies": " صور ذاتيه", + "search_page_search_photos_videos": "ابحث عن صورك او فديوهاتك", + "search_page_selfies": "صور ذاتيه", "search_page_things": "أشياء", "search_page_view_all_button": "عرض الكل", "search_page_your_activity": "نشاطك", @@ -1372,7 +1614,7 @@ "search_result_page_new_search_hint": "بحث جديد", "search_settings": "إعدادات البحث", "search_state": "البحث حسب الولاية...", - "search_suggestion_list_smart_search_hint_1": "يتم تمكين البحث الذكي افتراضيًا ، للبحث عن البيانات الوصفية ، استخدم بناء الجملة", + "search_suggestion_list_smart_search_hint_1": "يتم تمكين البحث الذكي افتراضيًا ، للبحث عن البيانات الوصفية ، استخدم بناء الجملة. ", "search_suggestion_list_smart_search_hint_2": "م: البحث الخاص بك", "search_tags": "البحث عن العلامات...", "search_timezone": "البحث حسب المنطقة الزمنية...", @@ -1385,6 +1627,7 @@ "select_album_cover": "تحديد غلاف الألبوم", "select_all": "تحديد الكل", "select_all_duplicates": "تحديد جميع النسخ المكررة", + "select_all_in": "اختر الكل في {group}", "select_avatar_color": "تحديد لون الصورة الشخصية", "select_face": "تحديد وجه", "select_featured_photo": "تحديد الصورة المميزة", @@ -1392,6 +1635,7 @@ "select_keep_all": "تحديد الأحتفاظ بالكل", "select_library_owner": "تحديد مالِك المكتبة", "select_new_face": "تحديد وجه جديد", + "select_person_to_tag": "اختر شخص لوضع علامة", "select_photos": "تحديد الصور", "select_trash_all": "تحديد حذف الكلِ", "select_user_for_sharing_page_err_album": "فشل في إنشاء ألبوم", @@ -1399,10 +1643,12 @@ "selected_count": "{count, plural, other {# محددة }}", "send_message": "‏إرسال رسالة", "send_welcome_email": "إرسال بريدًا إلكترونيًا ترحيبيًا", + "server_endpoint": "نقطة نهاية الخادم", "server_info_box_app_version": "نسخة التطبيق", "server_info_box_server_url": "عنوان URL الخادم", "server_offline": "الخادم غير متصل", "server_online": "الخادم متصل", + "server_privacy": "خصوصية الخادم", "server_stats": "إحصائيات الخادم", "server_version": "إصدار الخادم", "set": "‏تحديد", @@ -1412,6 +1658,7 @@ "set_date_of_birth": "تحديد تاريخ الميلاد", "set_profile_picture": "تحديد صورة الملف الشخصي", "set_slideshow_to_fullscreen": "تحديد عرض الشرائح على وضع ملء الشاشة", + "set_stack_primary_asset": "تعيين كأصل اساسي", "setting_image_viewer_help": "يقوم عارض التفاصيل بتحميل الصورة المصغرة الصغيرة أولاً ، ثم يقوم بتحميل المعاينة متوسطة الحجم (إذا تم تمكينها) ، ويقوم أخيرًا بتحميل الأصل (إذا تم تمكينه).", "setting_image_viewer_original_subtitle": "تمكين تحميل الصورة الكاملة الدقة الأصلية (كبيرة!).تعطيل لتقليل استخدام البيانات (كل من الشبكة وعلى ذاكرة التخزين المؤقت للجهاز).", "setting_image_viewer_original_title": "تحميل الصورة الأصلية", @@ -1419,21 +1666,30 @@ "setting_image_viewer_preview_title": "تحميل صورة معاينة", "setting_image_viewer_title": "الصور", "setting_languages_apply": "تغيير الإعدادات", + "setting_languages_subtitle": "تغيير لغة التطبيق", + "setting_notifications_notify_failures_grace_period": "التنبيه بفشل النسخ الاحتياطي في الخلفية: {duration}", + "setting_notifications_notify_hours": "{count} ساعات", "setting_notifications_notify_immediately": "في الحال", + "setting_notifications_notify_minutes": "{count} دقائق", "setting_notifications_notify_never": "أبداً", + "setting_notifications_notify_seconds": "{count} ثواني", "setting_notifications_single_progress_subtitle": "معلومات التقدم التفصيلية تحميل لكل أصل", "setting_notifications_single_progress_title": "إظهار تقدم التفاصيل الاحتياطية الخلفية", "setting_notifications_subtitle": "اضبط تفضيلات الإخطار", "setting_notifications_total_progress_subtitle": "التقدم التحميل العام (تم القيام به/إجمالي الأصول)", "setting_notifications_total_progress_title": "إظهار النسخ الاحتياطي الخلفية التقدم المحرز", "setting_video_viewer_looping_title": "تكرار مقطع فيديو تلقائيًا", + "setting_video_viewer_original_video_subtitle": "عند بث فيديو من الخادم، شغّل النسخة الأصلية حتى مع توفر ترميز بديل. قد يؤدي ذلك إلى تقطيع اثناء العرض . تُشغّل الفيديوهات المتوفرة محليًا بجودة أصلية بغض النظر عن هذا الإعداد.", + "setting_video_viewer_original_video_title": "اجبار عرض الفديو الاصلي", "settings": "الإعدادات", "settings_require_restart": "يرجى إعادة تشغيل لتطبيق هذا الإعداد", "settings_saved": "تم حفظ الإعدادات", - "setup_pin_code": "تحديد رقم سري", + "setup_pin_code": "تحديد رمز PIN", "share": "مشاركة", "share_add_photos": "إضافة الصور", + "share_assets_selected": "اختيار {count}", "share_dialog_preparing": "تحضير...", + "share_link": "مشاركة رابط", "shared": "مُشتَرك", "shared_album_activities_input_disable": "التعليق معطل", "shared_album_activity_remove_content": "هل تريد حذف هذا النشاط؟", @@ -1446,22 +1702,40 @@ "shared_by_user": "تمت المشاركة بواسطة {user}", "shared_by_you": "تمت مشاركته من قِبلك", "shared_from_partner": "صور من {partner}", + "shared_intent_upload_button_progress_text": "{current} / {total} تم رفع", "shared_link_app_bar_title": "روابط مشتركة", "shared_link_clipboard_copied_massage": "نسخ إلى الحافظة", + "shared_link_clipboard_text": "رابط: {link}\nكلمة المرور: {password}", "shared_link_create_error": "خطأ أثناء إنشاء رابط مشترك", "shared_link_edit_description_hint": "أدخل وصف المشاركة", "shared_link_edit_expire_after_option_day": "يوم 1", + "shared_link_edit_expire_after_option_days": "{count} ايام", "shared_link_edit_expire_after_option_hour": "1 ساعة", + "shared_link_edit_expire_after_option_hours": "{count} ساعات", "shared_link_edit_expire_after_option_minute": "1 دقيقة", + "shared_link_edit_expire_after_option_minutes": "{count} دقائق", + "shared_link_edit_expire_after_option_months": "{count} اشهر", + "shared_link_edit_expire_after_option_year": "{count} سنة", "shared_link_edit_password_hint": "أدخل كلمة مرور المشاركة", "shared_link_edit_submit_button": "تحديث الرابط", "shared_link_error_server_url_fetch": "لا يمكن جلب عنوان الخادم", + "shared_link_expires_day": "تنتهي صلاحيته في {count} يوم", + "shared_link_expires_days": "تنتهي صلاحيته في {count} ايام", + "shared_link_expires_hour": "تنتهي صلاحية في {count} ساعة", + "shared_link_expires_hours": "تنتهي صلاحيته في {count} ساعات", + "shared_link_expires_minute": "تنتهي صلاحيته في {count} دقيقة", + "shared_link_expires_minutes": "تنتهي صلاحيته في {count} دقائق", "shared_link_expires_never": "تنتهي ∞", + "shared_link_expires_second": "تنتهي صلاحيته في {count} ثانية", + "shared_link_expires_seconds": "تنتهي صلاحيته في {count} ثواني", + "shared_link_individual_shared": "مشاركة فردية", + "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "إدارة الروابط المشتركة", "shared_link_options": "خيارات الرابط المشترك", "shared_links": "روابط مشتركة", "shared_links_description": "وصف الروابط المشتركة", "shared_photos_and_videos_count": "{assetCount, plural, other {# الصور ومقاطع الفيديو المُشارَكة.}}", + "shared_with_me": "تمت مشاركتها معي", "shared_with_partner": "تمت المشاركة مع {partner}", "sharing": "مشاركة", "sharing_enter_password": "الرجاء إدخال كلمة المرور لعرض هذه الصفحة.", @@ -1522,12 +1796,14 @@ "start_date": "تاريخ البدء", "state": "الولاية", "status": "الحالة", + "stop_casting": "ايقاف البث", "stop_motion_photo": "إيقاف حركة الصورة", "stop_photo_sharing": "توقف عن مشاركة صورك؟", "stop_photo_sharing_description": "لن يتمكن {partner} من الوصول إلى صورك بعد الآن.", "stop_sharing_photos_with_user": "توقف عن مشاركة صورك مع هذا المستخدم", "storage": "مساحة التخزين", - "storage_label": "تسمية التخزين", + "storage_label": "سمة التخزين", + "storage_quota": "حصة الخزن", "storage_usage": "{used} من {available} مُستخْدم", "submit": "إرسال", "suggestions": "اقتراحات", @@ -1537,6 +1813,9 @@ "support_third_party_description": "تم حزم تثبيت immich الخاص بك بواسطة جهة خارجية. قد تكون المشكلات التي تواجهها ناجمة عن هذه الحزمة، لذا يرجى طرح المشكلات معهم في المقام الأول باستخدام الروابط أدناه.", "swap_merge_direction": "تبديل اتجاه الدمج", "sync": "مزامنة", + "sync_albums": "مزامنة الالبومات", + "sync_albums_manual_subtitle": "مزامنة جميع الفديوهات والصور المرفوعة الى البومات الخزن الاحتياطي المختارة", + "sync_upload_album_setting_subtitle": "انشئ و ارفع صورك و فديوهاتك الالبومات المختارة في Immich", "tag": "العلامة", "tag_assets": "أصول العلامة", "tag_created": "تم إنشاء العلامة: {tag}", @@ -1551,8 +1830,14 @@ "theme_selection": "اختيار السمة", "theme_selection_description": "قم بتعيين السمة تلقائيًا على اللون الفاتح أو الداكن بناءً على تفضيلات نظام المتصفح الخاص بك", "theme_setting_asset_list_storage_indicator_title": "عرض مؤشر التخزين على بلاط الأصول", + "theme_setting_asset_list_tiles_per_row_title": "عدد الاصول في كل صف({count})", + "theme_setting_colorful_interface_subtitle": "تطبيق اللون الأساسي على الأسطح في الخلفية.", + "theme_setting_colorful_interface_title": "واجهه ملونة", "theme_setting_image_viewer_quality_subtitle": "اضبط جودة عارض الصورة التفصيلية", "theme_setting_image_viewer_quality_title": "جودة عارض الصورة", + "theme_setting_primary_color_subtitle": "اختر لون للعمليات الاساسية والمكمله.", + "theme_setting_primary_color_title": "اللون الاساسي", + "theme_setting_system_primary_color_title": "استخدم لون النظام", "theme_setting_system_theme_switch": "تلقائي (اتبع إعداد النظام)", "theme_setting_theme_subtitle": "اختر إعدادات مظهر التطبيق", "theme_setting_three_stage_loading_subtitle": "قد يزيد التحميل من ثلاث مراحل من أداء التحميل ولكنه يسبب تحميل شبكة أعلى بكثير", @@ -1572,22 +1857,29 @@ "total": "الإجمالي", "total_usage": "الاستخدام الإجمالي", "trash": "المهملات", + "trash_action_prompt": "{count} نقل الى سلة المهملات", "trash_all": "نقل الكل إلى سلة المهملات", "trash_count": "سلة المحملات {count, number}", "trash_delete_asset": "حذف/نقل المحتوى إلى سلة المهملات", + "trash_emptied": "سبة مهملا مفرغة", "trash_no_results_message": "ستظهر هنا الصور ومقاطع الفيديو المحذوفة.", "trash_page_delete_all": "حذف الكل", "trash_page_empty_trash_dialog_content": "هل تريد تفريغ أصولك المهملة؟ ستتم إزالة هذه العناصر نهائيًا من التطبيق", + "trash_page_info": "العناصر المنقولة الى سلة المهملات سيتم حذفها بشكل نهائي بعد {days} ايام", "trash_page_no_assets": "لا توجد اصول في سله المهملات", "trash_page_restore_all": "استعادة الكل", - "trash_page_select_assets_btn": "اختر الأصول ", + "trash_page_select_assets_btn": "اختر الأصول", + "trash_page_title": "سلة المهملات ({count})", "trashed_items_will_be_permanently_deleted_after": "سيتم حذفُ العناصر المحذوفة نِهائيًا بعد {days, plural, one {# يوم} other {# أيام }}.", "type": "النوع", - "unable_to_change_pin_code": "تفيير الرقم السري غير ممكن", - "unable_to_setup_pin_code": "انشاء الرقم السري غير ممكن", + "unable_to_change_pin_code": "تفيير رمز PIN غير ممكن", + "unable_to_setup_pin_code": "انشاء رمز PIN غير ممكن", "unarchive": "أخرج من الأرشيف", + "unarchive_action_prompt": "{count} ازيل من الارشيف", "unarchived_count": "{count, plural, other {غير مؤرشفة #}}", + "undo": "تراجع", "unfavorite": "أزل التفضيل", + "unfavorite_action_prompt": "{count} ازيل من المفضلات", "unhide_person": "أظهر الشخص", "unknown": "غير معروف", "unknown_country": "بلد غير معروف", @@ -1603,9 +1895,11 @@ "unsaved_change": "تغيير غير محفوظ", "unselect_all": "إلغاء تحديد الكل", "unselect_all_duplicates": "إلغاء تحديد كافة النسخ المكررة", + "unselect_all_in": "إلغاء تحديد الكل في {group}", "unstack": "فك الكومه", "unstacked_assets_count": "تم إخراج {count, plural, one {# الأصل} other {# الأصول}} من التكديس", "up_next": "التالي", + "updated_at": "تم التحديث", "updated_password": "تم تحديث كلمة المرور", "upload": "رفع", "upload_concurrency": "الرفع المتزامن", @@ -1618,14 +1912,20 @@ "upload_status_errors": "الأخطاء", "upload_status_uploaded": "تم الرفع", "upload_success": "تم الرفع بنجاح، قم بتحديث الصفحة لرؤية المحتويات المرفوعة الجديدة.", + "upload_to_immich": "الرفع الىImmich ‎ ‏ ({count})", + "uploading": "جاري الرفع", "url": "عنوان URL", "usage": "الاستخدام", + "use_biometric": "استخدم البايومتري", + "use_current_connection": "استخدم الاتصال الحالي", "use_custom_date_range": "استخدم النطاق الزمني المخصص بدلاً من ذلك", "user": "مستخدم", + "user_has_been_deleted": "هذا المستخدم تم حذفه.", "user_id": "معرف المستخدم", "user_liked": "قام {user} بالإعجاب {type, select, photo {بهذه الصورة} video {بهذا الفيديو} asset {بهذا المحتوى} other {بها}}", - "user_pin_code_settings": "الرقم السري", - "user_pin_code_settings_description": "تغير الرقم السري", + "user_pin_code_settings": "رمز PIN", + "user_pin_code_settings_description": "تغير رمز PIN", + "user_privacy": "خصوصية المستخدم", "user_purchase_settings": "الشراء", "user_purchase_settings_description": "إدارة عملية الشراء الخاصة بك", "user_role_set": "قم بتعيين {user} كـ {role}", @@ -1636,6 +1936,7 @@ "users": "المستخدمين", "utilities": "أدوات", "validate": "تحقْق", + "validate_endpoint_error": "الرجاء ادخال عنوان URL صالح", "variables": "المتغيرات", "version": "الإصدار", "version_announcement_closing": "صديقك، أليكس", @@ -1657,7 +1958,9 @@ "view_name": "عرض", "view_next_asset": "عرض المحتوى التالي", "view_previous_asset": "عرض المحتوى السابق", + "view_qr_code": "­عرض رمز الاستجابة السريعة", "view_stack": "عرض التكديس", + "view_user": "عرض المستخدم", "viewer_remove_from_stack": "حذف من الكومه أو المجموعة", "viewer_stack_use_as_main_asset": "استخدم كأصل رئيسي", "viewer_unstack": "فك الكومه", @@ -1667,11 +1970,12 @@ "week": "أسبوع", "welcome": "مرحباً", "welcome_to_immich": "مرحباً بك في Immich", - "wifi_name": "WiFi Name", + "wifi_name": "اسم شبكة Wi-Fi", + "wrong_pin_code": "رمز PIN خاطئ", "year": "سنة", "years_ago": "منذ {years, plural, one {# سنة} other {# سنوات}}", "yes": "نعم", "you_dont_have_any_shared_links": "ليس لديك أي روابط مشتركة", - "your_wifi_name": "Your WiFi name", + "your_wifi_name": "اسم شبكة Wi-Fi الخاص بك", "zoom_image": "تكبير الصورة" } diff --git a/i18n/be.json b/i18n/be.json index 470030b6f6..d3f59b32a5 100644 --- a/i18n/be.json +++ b/i18n/be.json @@ -22,6 +22,7 @@ "add_partner": "Дадаць партнёра", "add_path": "Дадаць шлях", "add_photos": "Дадаць фота", + "add_tag": "Дадаць тэг", "add_to": "Дадаць у…", "add_to_album": "Дадаць у альбом", "add_to_album_bottom_sheet_added": "Дададзена да {album}", @@ -33,28 +34,30 @@ "added_to_favorites_count": "Дададзена {count, number} да абранага", "admin": { "add_exclusion_pattern_description": "Дадайце шаблоны выключэнняў. Падтрымліваецца выкарыстанне сімвалаў * , ** і ?. Каб ігнараваць усе файлы ў любой дырэкторыі з назвай \"Raw\", выкарыстоўвайце \"**/Raw/**\". Каб ігнараваць усе файлы, якія заканчваюцца на \".tif\", выкарыстоўвайце \"**/.tif\". Каб ігнараваць абсолютны шлях, выкарыстоўвайце \"/path/to/ignore/**\".", + "admin_user": "Адміністратар", "asset_offline_description": "Гэты знешні бібліятэчны актыў больш не знойдзены на дыску і быў перамешчаны ў сметніцу. Калі файл быў перамешчаны ў межах бібліятэкі, праверце вашу хроніку для новага адпаведнага актыва. Каб аднавіць гэты актыў, пераканайцеся, што шлях да файла ніжэй даступны для Immich і адскануйце бібліятэку.", "authentication_settings": "Налады праверкі сапраўднасці", "authentication_settings_description": "Кіраванне паролямі, OAuth, і іншыя налады праверкі сапраўднасці", "authentication_settings_disable_all": "Вы ўпэўнены, што жадаеце адключыць усе спосабы логіну? Логін будзе цалкам адключаны.", "authentication_settings_reenable": "Каб зноў уключыць, выкарыстайце Каманду сервера.", "background_task_job": "Фонавыя заданні", - "backup_database": "Рэзервовая копія базы даных", + "backup_database": "Стварыць рэзервовую копію базы даных", "backup_database_enable_description": "Уключыць рэзерваванне базы даных", "backup_keep_last_amount": "Колькасць папярэдніх рэзервовых копій для захавання", "backup_settings": "Налады рэзервовага капіявання", - "backup_settings_description": "Кіраванне наладамі дампа базы дадзеных. Заўвага: гэтыя задачы не кантралююцца, і ў выпадку няўдачы паведамленне адпраўлена не будзе.", + "backup_settings_description": "Кіраванне наладамі рэзервавання базы даных.", "cleared_jobs": "Ачышчаны заданні для: {job}", - "config_set_by_file": "Канфігурацыя ў зараз усталявана праз файл канфігурацыі", - "confirm_delete_library": "Вы ўпэўнены што жадаеце выдаліць {library} бібліятэку?", + "config_set_by_file": "Канфігурацыя зараз усталявана праз файл канфігурацыі", + "confirm_delete_library": "Вы ўпэўнены што жадаеце выдаліць бібліятэку {library}?", "confirm_delete_library_assets": "Вы ўпэўнены, што хочаце выдаліць гэтую бібліятэку? Гэта прывядзе да выдалення {count, plural, one {# актыву} other {усіх # актываў}}, якія змяшчаюцца ў Immich, і гэта дзеянне немагчыма будзе адмяніць. Файлы застануцца на дыску.", "confirm_email_below": "Каб пацвердзіць, увядзіце \"{email}\" ніжэй", "confirm_reprocess_all_faces": "Вы ўпэўнены, што хочаце пераапрацаваць усе твары? Гэта таксама прывядзе да выдалення імя людзей.", "confirm_user_password_reset": "Вы ўпэўнены ў тым, што жадаеце скінуць пароль {user}?", + "confirm_user_pin_code_reset": "Вы ўпэўнены ў тым, што жадаеце скінуць PIN-код {user}?", "create_job": "Стварыць заданне", "cron_expression": "Выраз Cron", "cron_expression_description": "Усталюйце інтэрвал сканавання, выкарыстоўваючы фармат cron. Для атрымання дадатковай інфармацыі, калі ласка, звярніцеся, напрыклад, да Crontab Guru", - "cron_expression_presets": "Прадустановкі выразаў Cron", + "cron_expression_presets": "Прадустаноўкі выразаў Cron", "disable_login": "Адключыць уваход", "duplicate_detection_job_description": "Запусціць машыннае навучанне на актывах для выяўлення падобных выяў. Залежыць ад Smart Search", "exclusion_pattern_description": "Шаблоны выключэння дазваляюць ігнараваць файлы і папкі пры сканаванні вашай бібліятэкі. Гэта карысна, калі ў вас ёсць папкі, якія змяшчаюць файлы, якія вы не хочаце імпартаваць, напрыклад, файлы RAW.", @@ -71,15 +74,272 @@ "image_fullsize_enabled_description": "Ствараць выяву ў поўным памеры для фарматаў, што не прыдатныя для вэб. Калі ўключана опцыя \"Аддаваць перавагу ўбудаванай праяве\", прагляды выкарыстоўваюцца непасрэдна без канвертацыі. Не ўплывае на вэб-прыдатныя фарматы, такія як JPEG.", "image_fullsize_quality_description": "Якасць выявы ў поўным памеры ад 1 да 100. Больш высокае значэнне лепшае, але прыводзіць да павелічэння памеру файла.", "image_fullsize_title": "Налады выявы ў поўным памеры", + "image_prefer_embedded_preview_setting_description": "Выкарыстоўваць убудаваныя праявы ў RAW-фотаздымках ў якасці ўваходных дадзеных для апрацоўкі малюнкаў, калі магчыма. Гэта дазваляе атрымаць больш дакладныя колеры для некаторых відарысаў, але ж якасць праяў залежыць ад камеры, і на відарысе можа быць больш артэфактаў сціску.", + "image_prefer_wide_gamut": "Аддаць перавагу шырокай гаме", "image_preview_title": "Налады папярэдняга прагляду", "image_quality": "Якасць", "image_resolution": "Раздзяляльнасць", "image_settings": "Налады відарыса", - "image_settings_description": "Кіруйце якасцю і раздзяляльнасцю сгенерыраваных відарысаў" + "image_settings_description": "Кіруйце якасцю і раздзяляльнасцю сгенерыраваных відарысаў", + "library_created": "Створана бібліятэка: {library}", + "library_deleted": "Бібліятэка выдалена", + "map_dark_style": "Цёмны стыль", + "map_enable_description": "Уключыць функцыі карты", + "map_gps_settings": "Налады карты і GPS", + "map_light_style": "Светлы стыль", + "map_settings": "Карта", + "map_settings_description": "Кіраванне наладамі карты", + "map_style_description": "URL-адрас style.json тэмы карты", + "metadata_settings": "Налады метаданых", + "oauth_button_text": "Тэкст кнопкі", + "oauth_settings": "OAuth", + "system_settings": "Сістэмныя налады", + "theme_settings": "Налады тэмы", + "transcoding_acceleration_vaapi": "VAAPI", + "transcoding_audio_codec": "Аудыякодэк", + "transcoding_video_codec": "Відэакодэк", + "trash_settings": "Налады сметніцы", + "trash_settings_description": "Кіраванне наладамі сметніцы", + "version_check_settings": "Праверка версіі", + "version_check_settings_description": "Уключыць/адключыць апавяшчэнні аб новай версіі" }, + "advanced_settings_troubleshooting_title": "Выпраўленне непаладак", + "album_added": "Альбом дададзены", + "album_name": "Назва альбома", + "album_remove_user": "Выдаліць карыстальніка?", + "album_updated": "Альбом абноўлены", + "albums": "Альбомы", + "all": "Усе", + "all_albums": "Усе альбомы", + "all_people": "Усе людзі", + "all_videos": "Усе відэа", + "app_bar_signout_dialog_ok": "Так", + "app_bar_signout_dialog_title": "Выйсці", + "app_settings": "Налады праграмы", + "archive": "Архіў", + "archive_size": "Памер архіва", + "asset_uploading": "Запампоўванне…", + "back": "Назад", + "backup_all": "Усе", + "backup_controller_page_background_wifi": "Толькі праз Wi-Fi", + "buy": "Купіць Immich", + "cache_settings_clear_cache_button": "Ачысціць кэш", + "cache_settings_tile_title": "Лакальнае сховішча", + "cancel": "Скасаваць", + "cancel_search": "Скасаваць пошук", + "canceled": "Скасавана", + "city": "Горад", + "clear": "Ачысціць", + "clear_all": "Ачысціць усё", + "client_cert_dialog_msg_confirm": "ОК", + "client_cert_enter_password": "Увядзіце пароль", + "client_cert_import": "Імпарт", + "close": "Закрыць", + "collapse": "Згарнуць", + "collapse_all": "Згарнуць усё", + "color": "Колер", + "color_theme": "Колеравая тэма", + "continue": "Працягнуць", + "control_bottom_app_bar_create_new_album": "Стварыць новы альбом", + "control_bottom_app_bar_delete_from_immich": "Выдаліць з Immich", + "control_bottom_app_bar_delete_from_local": "Выдаліць з прылады", + "control_bottom_app_bar_edit_location": "Рэдагаваць месцазнаходжанне", + "country": "Краіна", + "cover": "Вокладка", + "covers": "Вокладкі", + "create": "Стварыць", + "create_album": "Стварыць альбом", + "create_album_page_untitled": "Без назвы", + "create_library": "Стварыць бібліятэку", + "create_link": "Стварыць спасылку", + "create_new_user": "Стварыць новага карыстальніка", + "create_tag": "Стварыць тэг", + "create_user": "Стварыць карыстальніка", + "dark": "Цёмная", + "day": "Дзень", + "delete": "Выдаліць", + "delete_album": "Выдаліць альбом", + "delete_dialog_ok_force": "Усё адно выдаліць", + "delete_dialog_title": "Выдаліць назаўжды", + "delete_face": "Выдаліць твар", + "delete_key": "Выдаліць ключ", + "delete_library": "Выдаліць бібліятэку", + "delete_link": "Выдаліць спасылку", + "delete_local_dialog_ok_force": "Усё адно выдаліць", + "delete_others": "Выдаліць іншыя", + "delete_tag": "Выдаліць тэг", + "delete_user": "Выдаліць карыстальніка", + "discord": "Discord", + "documentation": "Дакументацыя", + "done": "Гатова", + "download": "Спампаваць", + "download_canceled": "Спампоўванне скасавана", + "download_complete": "Спампоўванне завершана", + "download_enqueue": "Спампоўванне дададзена ў чаргу", + "downloading": "Спампоўванне", + "edit": "Рэдагаваць", + "edit_album": "Рэдагаваць альбом", + "edit_avatar": "Рэдагаваць аватар", + "edit_date": "Рэдагаваць дату", + "edit_date_and_time": "Рэдагаваь дату і час", + "edit_description": "Рэдагаваць апісанне", + "edit_description_prompt": "Выберыце новае апісанне:", + "edit_faces": "Рэдагаваць твары", + "edit_import_path": "Рэдагаваць шлях імпарту", + "edit_import_paths": "Рэдагаваць шляхі імпарту", + "edit_key": "Рэдагаваць ключ", + "edit_link": "Рэдагаваць спасылку", + "edit_location": "Рэдагаваць месцазнаходжанне", + "edit_location_dialog_title": "Месцазнаходжанне", + "edit_name": "Рэдагаваць назву", + "edit_people": "Рэдагаваць людзей", + "edit_tag": "Рэдагаваць тэг", + "edit_title": "Рэдагаваць загаловак", + "edit_user": "Рэдагаваць карыстальніка", + "edited": "Адрэдагавана", + "editor": "Рэдактар", + "editor_close_without_save_prompt": "Змены не будуць захаваны", + "editor_close_without_save_title": "Закрыць рэдактар?", + "editor_crop_tool_h2_aspect_ratios": "Суадносіны бакоў", + "editor_crop_tool_h2_rotation": "Паварот", + "error": "Памылка", + "error_saving_image": "Памылка: {error}", + "exif": "Exif", + "exif_bottom_sheet_description": "Дадаць апісанне...", + "favorite": "У абраным", + "favorite_or_unfavorite_photo": "Дадаць або выдаліць фота з абранага", + "favorites": "Абраныя", + "file_name": "Назва файла", + "filename": "Назва файла", + "filetype": "Тып файла", + "filter": "Фільтр", + "forward": "Наперад", + "gcast_enabled": "Google Cast", + "general": "Агульныя", + "go_back": "Назад", + "go_to_folder": "Перайсці да папкі", + "hi_user": "Вітаем, {name} ({email})", + "hide_all_people": "Схаваць усіх людзей", + "hide_gallery": "Схаваць галерэю", + "hide_named_person": "Схаваць {name}", + "hide_password": "Схаваць пароль", + "hide_person": "Схаваць чалавека", + "image_viewer_page_state_provider_download_started": "Спампоўванне пачалося", + "immich_logo": "Лагатып Immich", + "interval": { + "day_at_onepm": "Кожны дзень а 13-й гадзіне", + "hours": "{hours, plural, one {Кожную гадзіну} few {Кожныя {hours, number} гадзіны} many {Кожныя {hours, number} гадзін} other {Кожныя {hours, number} гадзін}}", + "night_at_midnight": "Кожную ноч апоўначы", + "night_at_twoam": "Кожную ноч а 2-й гадзіне" + }, + "language": "Мова", + "library": "Бібліятэка", + "light": "Светлая", + "login_form_back_button_text": "Назад", + "login_form_email_hint": "youremail@email.com", + "login_form_endpoint_hint": "http://your-server-ip:port", + "login_form_password_hint": "пароль", + "login_form_save_login": "Заставацца ў сістэме", + "main_menu": "Галоўнае меню", + "map_location_dialog_yes": "Так", + "map_settings_dark_mode": "Цёмны рэжым", + "map_settings_date_range_option_day": "Апошнія 24 гадзіны", + "map_settings_date_range_option_days": "Апошніх дзён: {days}", + "map_settings_date_range_option_year": "Апошні год", + "map_settings_date_range_option_years": "Апошніх год: {years}", + "map_settings_dialog_title": "Налады карты", + "map_settings_theme_settings": "Тэма карты", + "menu": "Меню", + "minute": "Хвіліна", + "month": "Месяц", + "monthly_title_text_date_format": "MMMM y", + "my_albums": "Мае альбомы", + "name": "Імя", + "name_or_nickname": "Імя або псеўданім", + "next": "Далей", + "no": "Не", + "offline": "Па-за сеткай", + "ok": "ОК", + "online": "У сетцы", + "open": "Адкрыць", + "or": "або", + "partner_list_user_photos": "Фота карыстальніка {user}", + "pause": "Прыпыніць", + "people": "Людзі", + "permission_onboarding_back": "Назад", + "permission_onboarding_continue_anyway": "Усё адно працягнуць", + "photos": "Фота", + "photos_and_videos": "Фота і відэа", + "place": "Месца", + "places": "Месцы", + "port": "Порт", + "previous": "Папярэдняе", + "profile": "Профіль", + "profile_drawer_app_logs": "Журналы", + "profile_drawer_github": "GitHub", + "purchase_button_buy": "Купіць", + "purchase_button_buy_immich": "Купіць Immich", + "purchase_button_select": "Выбраць", + "remove": "Выдаліць", + "remove_from_album": "Выдаліць з альбома", + "remove_from_favorites": "Выдаліць з абраных", + "remove_tag": "Выдаліць тэг", + "remove_url": "Выдаліць URL-адрас", + "remove_user": "Выдаліць карыстальніка", + "rename": "Перайменаваць", + "repository": "Рэпазіторый", + "reset": "Скінуць", + "reset_password": "Скінуць пароль", + "restore": "Аднавіць", + "restore_all": "Аднавіць усё", + "restore_user": "Аднавіць карыстальніка", + "resume": "Узнавіць", + "role": "Роля", + "role_editor": "Рэдактар", + "role_viewer": "Глядач", + "save": "Захаваць", + "save_to_gallery": "Захаваць у галерэю", + "search_filter_date": "Дата", + "search_filter_location": "Месцазнаходжанне", + "search_filter_location_title": "Выберыце месцазнаходжанне", + "search_filter_media_type": "Тып медыя", + "search_filter_media_type_title": "Выберыце тып медыя", + "search_page_screenshots": "Здымкі экрана", + "search_page_selfies": "Сэлфі", + "search_page_things": "Рэчы", + "search_page_your_map": "Ваша карта", + "second": "Секунда", + "send_message": "Адправіць паведамленне", + "setting_languages_apply": "Ужыць", + "setting_notifications_notify_never": "ніколі", + "settings": "Налады", + "share_add_photos": "Дадаць фота", + "shared_album_section_people_title": "ЛЮДЗІ", + "shared_link_info_chip_metadata": "EXIF", + "sharing_page_empty_list": "ПУСТЫ СПІС", + "sign_out": "Выйсці", + "sign_up": "Зарэгістравацца", + "size": "Памер", + "sort_title": "Загаловак", + "source": "Крыніца", + "tag": "Тэг", + "tags": "Тэгі", + "theme": "Тэма", + "theme_selection": "Выбар тэмы", "timeline": "Хроніка", "total": "Усяго", + "trash": "Сметніца", + "trash_page_delete_all": "Выдаліць усе", + "trash_page_restore_all": "Аднавіць усе", + "trash_page_title": "Сметніца ({count})", + "type": "Тып", + "undo": "Адрабіць", + "upload": "Запампаваць", + "upload_status_errors": "Памылкі", + "uploading": "Запампоўванне", + "url": "URL-адрас", "user": "Карыстальнік", + "user_has_been_deleted": "Гэты карыстальнік быў выдалены.", "user_id": "ID карыстальніка", "user_purchase_settings": "Купля", "user_purchase_settings_description": "Кіруйце пакупкамі", @@ -112,14 +372,14 @@ "view_next_asset": "Паказаць наступны аб'ект", "view_previous_asset": "Праглядзець папярэдні аб'ект", "view_stack": "Прагляд стэка", - "visibility_changed": "Відзімасць змянілася для {count, plural, one {# чалавек(-аў)} астатніх {# чалавек}}", + "visibility_changed": "Бачнасць змянілася для {count, plural, one {# чалавека} other {# чалавек}}", "waiting": "Чакаюць", "warning": "Папярэджанне", "week": "Тыдзень", "welcome": "Вітаем", "welcome_to_immich": "Вітаем у Immich", "year": "Год", - "years_ago": "{years, plural, one {# год} other {# гадоў}} таму", + "years_ago": "{years, plural, one {# год} few {# гады} many {# гадоў} other {# гадоў}} таму", "yes": "Так", "you_dont_have_any_shared_links": "У вас няма абагуленых спасылак", "zoom_image": "Павялічыць відарыс" diff --git a/i18n/bg.json b/i18n/bg.json index 2df3dd927d..327fd7c7df 100644 --- a/i18n/bg.json +++ b/i18n/bg.json @@ -166,6 +166,20 @@ "metadata_settings_description": "Управление на настройките за метаданни", "migration_job": "Миграция", "migration_job_description": "Мигриране на миниатюрите за елементи и лица към най-новата структура на папките", + "nightly_tasks_cluster_faces_setting_description": "Изпълни разпознаване на лице за открити нови лица", + "nightly_tasks_cluster_new_faces_setting": "Разпознаване на нови лица", + "nightly_tasks_database_cleanup_setting": "Задачи по почистване на базата данни", + "nightly_tasks_database_cleanup_setting_description": "Премахни стари, ненужни записи от базата данни", + "nightly_tasks_generate_memories_setting": "Създаване на спомени", + "nightly_tasks_generate_memories_setting_description": "Създаване на нови спомени от съществуващи обекти", + "nightly_tasks_missing_thumbnails_setting": "Генериране на липсващи миниатюри", + "nightly_tasks_missing_thumbnails_setting_description": "Добавяне на обекти без миниатюра в опашката за създаване на миниатюра", + "nightly_tasks_settings": "Настройка на задачи за през нощта", + "nightly_tasks_settings_description": "Управление на задачите, изпълнявани през нощта", + "nightly_tasks_start_time_setting": "Време за начало", + "nightly_tasks_start_time_setting_description": "Време, когато сървъра ще започне изпълнение на нощни задачи", + "nightly_tasks_sync_quota_usage_setting": "Квота за синхронизация", + "nightly_tasks_sync_quota_usage_setting_description": "Обновяване на квотата според текущото потребление", "no_paths_added": "Няма добавени пътища", "no_pattern_added": "Няма добавен модел", "note_apply_storage_label_previous_assets": "Забележка: За да приложите етикета за съхранение към предварително качени файлове, стартирайте", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "URI за мобилно пренасочване", "oauth_mobile_redirect_uri_override": "URI пренасочване за мобилни устройства", "oauth_mobile_redirect_uri_override_description": "Разреши когато доставчика за OAuth удостоверяване не позволява за мобилни URI идентификатори, като ''{callback}''", + "oauth_role_claim": "Потвърждение на роля", + "oauth_role_claim_description": "Автоматично предоставяне на административни права при наличие на това потвържение. Потвърждението може да има стойност 'user' или 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Управление на настройките за вход с OAuth", "oauth_settings_more_details": "За повече информация за функционалността, се потърсете в docs.", @@ -357,6 +373,8 @@ "admin_password": "Администраторска парола", "administration": "Администрация", "advanced": "Разширено", + "advanced_settings_beta_timeline_subtitle": "Опитайте новите функции на приложението", + "advanced_settings_beta_timeline_title": "Бета версия на времевата линия", "advanced_settings_enable_alternate_media_filter_subtitle": "При синхронизация, използвайте тази опция като филтър, основан на промяна на даден критерии. Опитайте само в случай, че приложението има проблем с откриване на всички албуми.", "advanced_settings_enable_alternate_media_filter_title": "[ЕКСПЕРИМЕНТАЛНО] Използвай филтъра на алтернативното устройство за синхронизация на албуми", "advanced_settings_log_level_title": "Ниво на запис в дневника: {level}", @@ -427,6 +445,7 @@ "app_settings": "Настройки ма приложението", "appears_in": "Излиза в", "archive": "Архив", + "archive_action_prompt": "{count} са добавени в Архива", "archive_or_unarchive_photo": "Архивиране или деархивиране на снимка", "archive_page_no_archived_assets": "Не са намерени обекти в архива", "archive_page_title": "Архив ({count})", @@ -464,7 +483,6 @@ "assets": "Елементи", "assets_added_count": "Добавено {count, plural, one {# asset} other {# assets}}", "assets_added_to_album_count": "Добавен(и) са {count, plural, one {# актив} other {# актива}} в албума", - "assets_added_to_name_count": "Добавен(и) са {count, plural, one {# актив} other {# актива}} към {hasName, select, true {{name}} other {нов албум}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Обекта не може да се добави} other {Обектите не може да се добавят}} в албума", "assets_count": "{count, plural, one {# актив} other {# актива}}", "assets_deleted_permanently": "{count} обекта са изтрити завинаги", @@ -703,7 +721,7 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM yyyy", "dark": "Тъмен", - "darkTheme": "Превключи на тъмна тема", + "dark_theme": "Тъмна тема", "date_after": "Дата след", "date_and_time": "Дата и час", "date_before": "Дата преди", @@ -719,6 +737,7 @@ "default_locale": "Локализация по подразбиране", "default_locale_description": "Форматиране на дати и числа в зависимост от езиковата настройка на браузъра", "delete": "Изтрий", + "delete_action_prompt": "{count} са изтрити завинаги", "delete_album": "Изтрий албум", "delete_api_key_prompt": "Сигурни ли сте, че искате да изтриете този API ключ?", "delete_dialog_alert": "Тези обекти ще бъдат изтрити завинаги и от Immich сървъра и от устройството", @@ -732,19 +751,20 @@ "delete_key": "Изтрий ключ", "delete_library": "Изтрий библиотека", "delete_link": "Изтрий линк", + "delete_local_action_prompt": "{count} са изтрити локално", "delete_local_dialog_ok_backed_up_only": "Изтрий локално само архивираните", "delete_local_dialog_ok_force": "Въпреки това изтрий", "delete_others": "Изтрий останалите", "delete_shared_link": "Изтриване на споделен линк", "delete_shared_link_dialog_title": "Изтрий споделената връзка", "delete_tag": "Изтрий таг", - "delete_tag_confirmation_prompt": "Сигурни ли сте, че искате да изтриете таг {tagName}?", + "delete_tag_confirmation_prompt": "Сигурни ли сте, че искате да изтриете тага {tagName}?", "delete_user": "Изтрий потребител", "deleted_shared_link": "Изтрит споделен линк", "deletes_missing_assets": "Изтрива файлове, които липсват на диска", "description": "Описание", "description_input_hint_text": "Добави описание...", - "description_input_submit_error": "Неуспешно обновяване на описанието. За подробности виж в дневника", + "description_input_submit_error": "Неуспешно обновяване на описанието. За подробности вижте в дневника", "details": "Детайли", "direction": "Посока", "disabled": "Изключено", @@ -762,6 +782,7 @@ "documentation": "Документация", "done": "Готово", "download": "Изтегли", + "download_action_prompt": "Зареждане на {count} обекта", "download_canceled": "Изтеглянето е отменено", "download_complete": "Изтеглянето завърши", "download_enqueue": "Изтеглянето е добавено в опашката", @@ -799,6 +820,7 @@ "edit_key": "Редактиране на ключ", "edit_link": "Редактиране на линк", "edit_location": "Редактиране на местоположението", + "edit_location_action_prompt": "{count} локации са редактирани", "edit_location_dialog_title": "Местоположение", "edit_name": "Редактиране на име", "edit_people": "Редактиране на хора", @@ -984,6 +1006,7 @@ "failed_to_load_assets": "Неуспешно зареждане на елементи", "failed_to_load_folder": "Неуспешно зареждане на папка", "favorite": "Любим", + "favorite_action_prompt": "{count} са добавени в Любими", "favorite_or_unfavorite_photo": "Добави или премахни снимка от Любими", "favorites": "Любими", "favorites_page_no_favorites": "Не са намерени любими обекти", @@ -1127,6 +1150,7 @@ "library_page_sort_created": "Дата на създаване", "library_page_sort_last_modified": "Последна промяна", "library_page_sort_title": "Заглавие на албума", + "licenses": "Лицензи", "light": "Светло", "like_deleted": "Като изтрит", "link_motion_video": "Линк към видео", @@ -1246,6 +1270,7 @@ "more": "Още", "move": "Премести", "move_off_locked_folder": "Извади от заключената папка", + "move_to_lock_folder_action_prompt": "{count} са добавени в заключената папка", "move_to_locked_folder": "Премести в заключена папка", "move_to_locked_folder_confirmation": "Тези снимки и видеа ще бъдат изтрити от всички албуми и ще са достъпни само в заключената папка", "moved_to_archive": "{count, plural, one {# обект е преместен} many {# обекта са преместени} other {# обекта са преместени}} в архива", @@ -1495,7 +1520,9 @@ "remove_custom_date_range": "Премахни зададения диапазон от дати", "remove_deleted_assets": "Премахни Изтритите Елементи", "remove_from_album": "Премахни от албума", + "remove_from_album_action_prompt": "{count} са премахнати от албума", "remove_from_favorites": "Премахни от Любими", + "remove_from_lock_folder_action_prompt": "{count} са премахнати от заключената папка", "remove_from_locked_folder": "Махни от заключената папка", "remove_from_locked_folder_confirmation": "Сигурни ли си, че искате тези снимки и видеа да бъдат извадени от заключената папка? Те ще бъдат видими в библиотеката.", "remove_from_shared_link": "Премахни от споделения линк", @@ -1667,6 +1694,7 @@ "settings_saved": "Настройките са запазени", "setup_pin_code": "Задай PIN код", "share": "Споделяне", + "share_action_prompt": "{count} споделени обекта", "share_add_photos": "Добави снимки", "share_assets_selected": "{count} избрани", "share_dialog_preparing": "Подготовка...", @@ -1768,6 +1796,7 @@ "sort_title": "Заглавие", "source": "Код", "stack": "Събери", + "stack_action_prompt": "{count} са групирани", "stack_duplicates": "Подреждане на дубликати", "stack_select_one_photo": "Избери една главна снимка за събраните снимки", "stack_selected_photos": "Подреждане на избрани снимки", @@ -1838,6 +1867,7 @@ "total": "Общо", "total_usage": "Общо използвано", "trash": "Кошче", + "trash_action_prompt": "{count} са преместени в коша", "trash_all": "Изхвърли всички", "trash_count": "В Кошчето {count, number}", "trash_delete_asset": "Вкарай в Кошчето/Изтрий елемент", @@ -1855,9 +1885,11 @@ "unable_to_change_pin_code": "Невъзможна промяна на PIN кода", "unable_to_setup_pin_code": "Неуспешно задаване на PIN кода", "unarchive": "Разархивирай", + "unarchive_action_prompt": "{count} са премахнати от Архива", "unarchived_count": "{count, plural, other {Неархивирани #}}", "undo": "Отмени", "unfavorite": "Премахване от любимите", + "unfavorite_action_prompt": "{count} са премахнати от Любими", "unhide_person": "Покажи отново човека", "unknown": "Неизвестно", "unknown_country": "Непозната Държава", @@ -1875,7 +1907,9 @@ "unselect_all_duplicates": "От маркирай всички дубликати", "unselect_all_in": "Премахни избора на всички от групата {group}", "unstack": "Разкачи", + "unstack_action_prompt": "{count} са разгрупирани", "unstacked_assets_count": "Разкачени {count, plural, one {# елемент} other {# елементи}}", + "untagged": "Немаркирани", "up_next": "Следващ", "updated_at": "Обновено", "updated_password": "Паролата е актуализирана", diff --git a/i18n/bn.json b/i18n/bn.json index ed108652b7..d71e4e25ae 100644 --- a/i18n/bn.json +++ b/i18n/bn.json @@ -8,6 +8,7 @@ "actions": "কর্ম", "active": "সচল", "activity": "কার্যকলাপ", + "activity_changed": "একটিভিটি এখন {enabled, select, true {চালু} other {বন্ধ}} আছে", "add": "যোগ করুন", "add_a_description": "একটি বিবরণ যোগ করুন", "add_a_location": "একটি অবস্থান যোগ করুন", @@ -15,5 +16,84 @@ "add_a_title": "একটি শিরোনাম যোগ করুন", "add_endpoint": "এন্ডপয়েন্ট যোগ করুন", "add_exclusion_pattern": "বহির্ভূতকরণ নমুনা", - "add_url": "লিঙ্ক যোগ করুন" + "add_import_path": "ইমপোর্ট করার পাথ যুক্ত করুন", + "add_location": "অবস্থান যুক্ত করুন", + "add_more_users": "আরো ব্যবহারকারী যুক্ত করুন", + "add_partner": "অংশীদার যোগ করুন", + "add_path": "পাথ যুক্ত করুন", + "add_photos": "ছবি যুক্ত করুন", + "add_tag": "ট্যাগ যুক্ত করুন", + "add_to": "যুক্ত করুন…", + "add_to_album": "এলবাম এ যোগ করুন", + "add_to_album_bottom_sheet_added": "{album} এ যোগ করা হয়েছে", + "add_to_album_bottom_sheet_already_exists": "{album} এ আগে থেকেই আছে", + "add_to_shared_album": "শেয়ার করা অ্যালবামে যোগ করুন", + "add_url": "লিঙ্ক যোগ করুন", + "added_to_archive": "আর্কাইভ এ যোগ করা হয়েছে", + "added_to_favorites": "ফেভারিটে যোগ করা হয়েছে", + "added_to_favorites_count": "পছন্দের তালিকায় {count, number} যোগ করা হয়েছে", + "admin": { + "add_exclusion_pattern_description": "এক্সক্লুশন প্যাটার্ন যোগ করুন। *, **, এবং ? ব্যবহার করে গ্লোবিং করা সম্ভব। \"Raw\" নামের যেকোনো ডিরেক্টরিতে থাকা সমস্ত ফাইল বাদ দিতে \"**/Raw/**\" ব্যবহার করুন। \".tif\" দিয়ে শেষ হওয়া সমস্ত ফাইল বাদ দিতে \"**/*.tif\" ব্যবহার করুন। একটি সম্পূর্ণ পাথ বাদ দিতে, \"/path/to/ignore/**\" ব্যবহার করুন।", + "admin_user": "এডমিন ইউজার", + "asset_offline_description": "এই বহিরাগত লাইব্রেরি সম্পদটি আর ডিস্কে পাওয়া যাচ্ছে না এবং ট্র্যাশে সরানো হয়েছে। যদি ফাইলটি লাইব্রেরির মধ্যে সরানো হয়ে থাকে, তাহলে নতুন সংশ্লিষ্ট সম্পদের জন্য আপনার টাইমলাইন পরীক্ষা করুন। এই সম্পদটি পুনরুদ্ধার করতে, দয়া করে নিশ্চিত করুন যে নীচের ফাইল পাথটি Immich দ্বারা অ্যাক্সেস করা যেতে পারে এবং লাইব্রেরিটি স্ক্যান করুন।", + "authentication_settings": "প্রমাণীকরণ সেটিংস", + "authentication_settings_description": "পাসওয়ার্ড, OAuth এবং অন্যান্য প্রমাণীকরণ সেটিংস পরিচালনা করুন", + "authentication_settings_disable_all": "আপনি কি নিশ্চিত যে আপনি সমস্ত লগইন পদ্ধতি অক্ষম করতে চান? লগইন সম্পূর্ণরূপে অক্ষম করা হবে।", + "authentication_settings_reenable": "পুনরায় সক্ষম করতে, একটি সার্ভার কমান্ড ব্যবহার করুন।", + "background_task_job": "ব্যাকগ্রাউন্ড টাস্ক", + "backup_database": "ডাটাবেস ডাম্প তৈরি করুন", + "backup_database_enable_description": "ডাটাবেস ডাম্প সক্রিয় করুন", + "backup_keep_last_amount": "আগের ডাম্পের পরিমাণ রাখা হবে", + "backup_settings": "ডাটাবেস ডাম্প সেটিংস", + "backup_settings_description": "ডাটাবেস ডাম্প সেটিংস পরিচালনা করুন।", + "cleared_jobs": "{job} এর জন্য jobs খালি করা হয়েছে", + "config_set_by_file": "কনফিগ বর্তমানে একটি কনফিগ ফাইল দ্বারা সেট করা আছে", + "confirm_delete_library": "আপনি কি নিশ্চিত যে আপনি {library} লাইব্রেরি মুছে ফেলতে চান?", + "confirm_delete_library_assets": "আপনি কি নিশ্চিত যে আপনি এই লাইব্রেরিটি মুছে ফেলতে চান? এটি Immich থেকে {count, plural, one {# contained asset} other {all # contained asset}} মুছে ফেলবে এবং পূর্বাবস্থায় ফেরানো যাবে না। ফাইলগুলি ডিস্কে থাকবে।", + "confirm_email_below": "নিশ্চিত করতে, নিচে \"{email}\" টাইপ করুন", + "confirm_reprocess_all_faces": "আপনি কি নিশ্চিত যে আপনি সমস্ত মুখ পুনরায় প্রক্রিয়া করতে চান? এটি নামযুক্ত ব্যক্তিদেরও মুছে ফেলবে।", + "confirm_user_password_reset": "আপনি কি নিশ্চিত যে আপনি {user} এর পাসওয়ার্ড রিসেট করতে চান?", + "confirm_user_pin_code_reset": "আপনি কি নিশ্চিত যে আপনি {user} এর পিন কোড রিসেট করতে চান?", + "create_job": "job তৈরি করুন", + "cron_expression": "ক্রোন এক্সপ্রেশন", + "cron_expression_description": "ক্রোন ফর্ম্যাট ব্যবহার করে স্ক্যানিং ব্যবধান সেট করুন। আরও তথ্যের জন্য দয়া করে দেখুন যেমন Crontab Guru", + "cron_expression_presets": "ক্রোন এক্সপ্রেশন প্রিসেট", + "disable_login": "লগইন অক্ষম করুন", + "duplicate_detection_job_description": "অনুরূপ ছবি সনাক্ত করতে সম্পদগুলিতে মেশিন লার্নিং চালান। স্মার্ট অনুসন্ধানের উপর নির্ভর করে", + "exclusion_pattern_description": "এক্সক্লুশন প্যাটার্ন ব্যবহার করে আপনি আপনার লাইব্রেরি স্ক্যান করার সময় ফাইল এবং ফোল্ডারগুলিকে উপেক্ষা করতে পারবেন। যদি আপনার এমন ফোল্ডার থাকে যেখানে এমন ফাইল থাকে যা আপনি আমদানি করতে চান না, যেমন RAW ফাইল।", + "external_library_management": "বহিরাগত গ্রন্থাগার ব্যবস্থাপনা", + "face_detection": "মুখ সনাক্তকরণ", + "face_detection_description": "মেশিন লার্নিং ব্যবহার করে অ্যাসেটে থাকা মুখগুলি সনাক্ত করুন। ভিডিওগুলির জন্য, শুধুমাত্র থাম্বনেইল বিবেচনা করা হয়। \"রিফ্রেশ\" (পুনরায়) সমস্ত অ্যাসেট প্রক্রিয়া করে। \"রিসেট\" অতিরিক্তভাবে সমস্ত বর্তমান মুখের ডেটা সাফ করে। \"অনুপস্থিত\" অ্যাসেটগুলিকে সারিবদ্ধ করে যা এখনও প্রক্রিয়া করা হয়নি। সনাক্ত করা মুখগুলিকে ফেসিয়াল রিকগনিশনের জন্য সারিবদ্ধ করা হবে, ফেসিয়াল ডিটেকশন সম্পূর্ণ হওয়ার পরে, বিদ্যমান বা নতুন ব্যক্তিদের মধ্যে গোষ্ঠীবদ্ধ করে।", + "facial_recognition_job_description": "শনাক্ত করা মুখগুলিকে মানুষের মধ্যে গোষ্ঠীভুক্ত করুন। মুখ সনাক্তকরণ সম্পূর্ণ হওয়ার পরে এই ধাপটি চলে। \"রিসেট\" (পুনরায়) সমস্ত মুখকে ক্লাস্টার করে। \"অনুপস্থিত\" মুখগুলিকে সারিতে রাখে যেখানে কোনও ব্যক্তিকে বরাদ্দ করা হয়নি।", + "failed_job_command": "কমান্ড {command} কাজের জন্য ব্যর্থ হয়েছে: {job}", + "force_delete_user_warning": "সতর্কতা: এটি ব্যবহারকারী এবং সমস্ত সম্পদ অবিলম্বে সরিয়ে ফেলবে। এটি পূর্বাবস্থায় ফেরানো যাবে না এবং ফাইলগুলি পুনরুদ্ধার করা যাবে না।", + "image_format": "ফরম্যাট", + "image_format_description": "WebP JPEG এর তুলনায় ছোট ফাইল তৈরি করে, কিন্তু এনকোড করতে ধীর।", + "image_fullsize_description": "জুম ইন করার সময় ব্যবহৃত স্ট্রিপড মেটাডেটা সহ পূর্ণ আকারের ছবি", + "image_fullsize_enabled": "পূর্ণ-আকারের ছবি তৈরি সক্ষম করুন", + "image_fullsize_enabled_description": "ওয়েব-বান্ধব নয় এমন ফর্ম্যাটের জন্য পূর্ণ-আকারের ছবি তৈরি করুন। \"এমবেডেড প্রিভিউ পছন্দ করুন\" সক্ষম করা থাকলে, রূপান্তর ছাড়াই এমবেডেড প্রিভিউ সরাসরি ব্যবহার করা হয়। JPEG-এর মতো ওয়েব-বান্ধব ফর্ম্যাটগুলিকে প্রভাবিত করে না।", + "image_fullsize_quality_description": "পূর্ণ-আকারের ছবির মান ১-১০০। উচ্চতর হলে ভালো, কিন্তু আরও বড় ফাইল তৈরি হয়।", + "image_fullsize_title": "পূর্ণ-আকারের চিত্র সেটিংস", + "image_prefer_embedded_preview": "এম্বেড করা প্রিভিউ পছন্দ করুন", + "image_prefer_embedded_preview_setting_description": "ছবি প্রক্রিয়াকরণের জন্য এবং যখনই উপলব্ধ থাকবে তখন RAW ফটোতে এমবেডেড প্রিভিউ ব্যবহার করুন। এটি কিছু ছবির জন্য আরও সঠিক রঙ তৈরি করতে পারে, তবে প্রিভিউয়ের মান ক্যামেরা-নির্ভর এবং ছবিতে আরও কম্প্রেশন আর্টিফ্যাক্ট থাকতে পারে।", + "image_prefer_wide_gamut": "প্রশস্ত পরিসর পছন্দ করুন", + "image_prefer_wide_gamut_setting_description": "থাম্বনেইলের জন্য ডিসপ্লে P3 ব্যবহার করুন। এটি প্রশস্ত রঙের স্থান সহ ছবির প্রাণবন্ততা আরও ভালভাবে সংরক্ষণ করে, তবে পুরানো ব্রাউজার সংস্করণ সহ পুরানো ডিভাইসগুলিতে ছবিগুলি ভিন্নভাবে প্রদর্শিত হতে পারে। রঙের পরিবর্তন এড়াতে sRGB ছবিগুলিকে sRGB হিসাবে রাখা হয়।", + "image_preview_description": "স্ট্রিপড মেটাডেটা সহ মাঝারি আকারের ছবি, একটি একক সম্পদ দেখার সময় এবং মেশিন লার্নিংয়ের জন্য ব্যবহৃত হয়", + "image_preview_quality_description": "১-১০০ এর মধ্যে প্রিভিউ কোয়ালিটি। বেশি হলে ভালো, কিন্তু বড় ফাইল তৈরি হয় এবং অ্যাপের প্রতিক্রিয়াশীলতা কমাতে পারে। কম মান সেট করলে মেশিন লার্নিং কোয়ালিটির উপর প্রভাব পড়তে পারে।", + "image_preview_title": "প্রিভিউ সেটিংস", + "image_quality": "গুণমান", + "image_resolution": "রেজোলিউশন", + "image_resolution_description": "উচ্চ রেজোলিউশনের ক্ষেত্রে আরও বিস্তারিত তথ্য সংরক্ষণ করা সম্ভব কিন্তু এনকোড করতে বেশি সময় লাগে, ফাইলের আকার বড় হয় এবং অ্যাপের প্রতিক্রিয়াশীলতা কমাতে পারে।", + "image_settings": "চিত্র সেটিংস", + "image_settings_description": "তৈরি করা ছবির মান এবং রেজোলিউশন পরিচালনা করুন", + "image_thumbnail_description": "মেটাডেটা বাদ দেওয়া ছোট থাম্বনেইল, মূল টাইমলাইনের মতো ছবির গ্রুপ দেখার সময় ব্যবহৃত হয়", + "image_thumbnail_quality_description": "থাম্বনেইলের মান ১-১০০। বেশি হলে ভালো, কিন্তু বড় ফাইল তৈরি হয় এবং অ্যাপের প্রতিক্রিয়াশীলতা কমাতে পারে।", + "image_thumbnail_title": "থাম্বনেল সেটিংস", + "job_concurrency": "{job} কনকারেন্সি", + "job_created": "Job তৈরি হয়েছে", + "job_not_concurrency_safe": "এই কাজটি সমকালীন-নিরাপদ নয়।", + "job_settings": "কাজের সেটিংস", + "job_settings_description": "কাজের সমান্তরালতা পরিচালনা করুন", + "job_status": "চাকরির অবস্থা" + } } diff --git a/i18n/ca.json b/i18n/ca.json index 5c53311a02..39c249c3a3 100644 --- a/i18n/ca.json +++ b/i18n/ca.json @@ -166,6 +166,10 @@ "metadata_settings_description": "Administrar la configuració de les metadades", "migration_job": "Migració", "migration_job_description": "Migra les miniatures d'elements i cares cap a la nova estructura de carpetes", + "nightly_tasks_cluster_new_faces_setting": "Agrupa cares noves", + "nightly_tasks_database_cleanup_setting": "Tasques de neteja de la base de dades", + "nightly_tasks_database_cleanup_setting_description": "Netegeu les dades antigues i caducades de la base de dades", + "nightly_tasks_missing_thumbnails_setting": "Generar les miniatures restants", "no_paths_added": "No s'ha afegit cap ruta", "no_pattern_added": "Cap patró aplicat", "note_apply_storage_label_previous_assets": "Nota: Per aplicar l'etiquetatge d'emmagatzematge a elements pujats prèviament, executeu la", @@ -196,6 +200,8 @@ "oauth_mobile_redirect_uri": "URI de redirecció mòbil", "oauth_mobile_redirect_uri_override": "Sobreescriu l'URI de redirecció mòbil", "oauth_mobile_redirect_uri_override_description": "Habilita quan el proveïdor d'OAuth no permet una URI mòbil, com ara ''{callback}''", + "oauth_role_claim": "Concessió de rol", + "oauth_role_claim_description": "Atorgar accés d'administrador automàticament segons la presència d'aquesta concessió. La concessió pot ser 'usuari' o 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Gestiona la configuració de l'inici de sessió OAuth", "oauth_settings_more_details": "Per a més detalls sobre aquesta funcionalitat, consulteu la documentació.", @@ -244,6 +250,7 @@ "storage_template_migration_info": "Les extensions es convertiran a minúscules. Els canvis de plantilla només s'aplicaran a nous elements. Per aplicar la plantilla rectroactivament a elements pujats prèviament, executeu la {job}.", "storage_template_migration_job": "Tasca de migració de la plantilla d'emmagatzematge", "storage_template_more_details": "Per obtenir més detalls sobre aquesta funció, consulteu la Storage Template i les seves implications", + "storage_template_onboarding_description_v2": "Un cop habilitada, aquesta funció organitzarà automàticament els fitxers a partir d'una plantilla definida per l'usuari. Per a més informació, podeu consultar la documentació.", "storage_template_path_length": "Límit aproximat de longitud de la ruta: {length, number}/{limit, number}", "storage_template_settings": "Plantilla d'emmagatzematge", "storage_template_settings_description": "Gestiona l'estructura de les carpetes i el nom del fitxers dels elements pujats", @@ -359,7 +366,7 @@ "advanced_settings_enable_alternate_media_filter_subtitle": "Feu servir aquesta opció per filtrar els continguts multimèdia durant la sincronització segons criteris alternatius. Només proveu-ho si teniu problemes amb l'aplicació per detectar tots els àlbums.", "advanced_settings_enable_alternate_media_filter_title": "Utilitza el filtre de sincronització d'àlbums de dispositius alternatius", "advanced_settings_log_level_title": "Nivell de registre: {level}", - "advanced_settings_prefer_remote_subtitle": "Alguns dispositius són molt lents en carregar miniatures dels elements del dispositiu. Activeu aquest paràmetre per carregar imatges remotes en el seu lloc.", + "advanced_settings_prefer_remote_subtitle": "Alguns dispositius són molt lents en carregar miniatures dels elements locals. Activeu aquest paràmetre per carregar imatges remotes en el seu lloc.", "advanced_settings_prefer_remote_title": "Prefereix imatges remotes", "advanced_settings_proxy_headers_subtitle": "Definiu les capçaleres de proxy que Immich per enviar amb cada sol·licitud de xarxa", "advanced_settings_proxy_headers_title": "Capçaleres de proxy", @@ -426,6 +433,7 @@ "app_settings": "Configuració de l'app", "appears_in": "Apareix a", "archive": "Arxiu", + "archive_action_prompt": "{count} afegit a Arxiu", "archive_or_unarchive_photo": "Arxivar o desarxivar fotografia", "archive_page_no_archived_assets": "No s'ha trobat res arxivat", "archive_page_title": "Arxiu({count})", @@ -463,7 +471,6 @@ "assets": "Elements", "assets_added_count": "{count, plural, one {Afegit un element} other {Afegits # elements}}", "assets_added_to_album_count": "{count, plural, one {Afegit un element} other {Afegits # elements}} a l'àlbum", - "assets_added_to_name_count": "{count, plural, one {S'ha afegit # recurs} other {S'han afegit # recursos}} a {hasName, select, true {{name}} other {new album}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} no es pot afegir a l'àlbum", "assets_count": "{count, plural, one {# recurs} other {# recursos}}", "assets_deleted_permanently": "{count} element(s) esborrats permanentment", @@ -702,7 +709,7 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Fosc", - "darkTheme": "Activa/desactiva el tema fosc", + "dark_theme": "Canviar a tema fosc", "date_after": "Data posterior a", "date_and_time": "Data i hora", "date_before": "Data anterior a", @@ -718,6 +725,7 @@ "default_locale": "Localització predeterminada", "default_locale_description": "Format de dates i números segons la configuració del navegador", "delete": "Esborra", + "delete_action_prompt": "{count} eliminats permanentment", "delete_album": "Esborra l'àlbum", "delete_api_key_prompt": "Esteu segurs que voleu eliminar aquesta clau API?", "delete_dialog_alert": "Aquests elements seran eliminats de manera permanent d'Immich i del vostre dispositiu", @@ -798,6 +806,7 @@ "edit_key": "Edita clau", "edit_link": "Edita enllaç", "edit_location": "Edita ubicació", + "edit_location_action_prompt": "{count} ubicacions editades", "edit_location_dialog_title": "Ubicació", "edit_name": "Edita el nom", "edit_people": "Edita la gent", @@ -983,6 +992,7 @@ "failed_to_load_assets": "Error carregant recursos", "failed_to_load_folder": "No s'ha pogut carregar la carpeta", "favorite": "Preferit", + "favorite_action_prompt": "{count} afegit a Favorits", "favorite_or_unfavorite_photo": "Foto preferida o no preferida", "favorites": "Preferits", "favorites_page_no_favorites": "No s'han trobat preferits", @@ -1149,6 +1159,7 @@ "locked_folder": "Carpeta bloquejada", "log_out": "Tanca la sessió", "log_out_all_devices": "Tanqueu la sessió de tots els dispositius", + "logged_in_as": "Sessió iniciada com a {user}", "logged_out_all_devices": "S'ha tancat la sessió de tots els dispositius", "logged_out_device": "Dispositiu tancat", "login": "Iniciar sessió", @@ -1244,6 +1255,7 @@ "more": "Més", "move": "Moure", "move_off_locked_folder": "Moure fora de la carpeta bloquejada", + "move_to_lock_folder_action_prompt": "{count} afegides a la carpeta protegida", "move_to_locked_folder": "Moure a la carpeta bloquejada", "move_to_locked_folder_confirmation": "Aquestes fotos i vídeos seran eliminades de tots els àlbums, i només podran ser vistes des de la carpeta bloquejada", "moved_to_archive": "S'han mogut {count, plural, one {# asset} other {# assets}} a l'arxiu", @@ -1494,6 +1506,7 @@ "remove_deleted_assets": "Suprimeix fitxers fora de línia", "remove_from_album": "Treu de l'àlbum", "remove_from_favorites": "Eliminar dels preferits", + "remove_from_lock_folder_action_prompt": "{count} eliminades de la carpeta protegida", "remove_from_locked_folder": "Elimina de la carpeta bloquejada", "remove_from_locked_folder_confirmation": "Segur que vols moure aquestes fotos i vídeos fora de la carpeta bloquejada? Seran visibles a la teva biblioteca.", "remove_from_shared_link": "Eliminar de l'enllaç compartit", @@ -1606,6 +1619,7 @@ "select_album_cover": "Seleccionar la portada de l'àlbum", "select_all": "Selecciona-ho tot", "select_all_duplicates": "Seleccioneu tots els duplicats", + "select_all_in": "Selecciona tot en {group}", "select_avatar_color": "Tria color de l'avatar", "select_face": "Selecciona cara", "select_featured_photo": "Selecciona foto principal", @@ -1835,6 +1849,7 @@ "total": "Total", "total_usage": "Ús total", "trash": "Paperera", + "trash_action_prompt": "{count} mogudes a la brossa", "trash_all": "Envia-ho tot a la paperera", "trash_count": "Paperera {count, number}", "trash_delete_asset": "Esborra/Elimina element", @@ -1852,9 +1867,11 @@ "unable_to_change_pin_code": "No es pot canviar el codi PIN", "unable_to_setup_pin_code": "No s'ha pogut configurar el codi PIN", "unarchive": "Desarxivar", + "unarchive_action_prompt": "{count} eliminades de l'arxiu", "unarchived_count": "{count, plural, other {# elements desarxivats}}", "undo": "Desfer", "unfavorite": "Reverteix preferit", + "unfavorite_action_prompt": "{count} eliminades de preferits", "unhide_person": "Mostra persona", "unknown": "Desconegut", "unknown_country": "País Desconegut", @@ -1870,6 +1887,7 @@ "unsaved_change": "Canvi no desat", "unselect_all": "Deselecciona-ho tot", "unselect_all_duplicates": "Desmarqueu tots els duplicats", + "unselect_all_in": "Desseleccionar tots els elements de {group}", "unstack": "Desapila", "unstacked_assets_count": "No apilat {count, plural, one {# recurs} other {# recursos}}", "up_next": "Pròxim", diff --git a/i18n/cs.json b/i18n/cs.json index 82e72cab46..c14a4b6d0c 100644 --- a/i18n/cs.json +++ b/i18n/cs.json @@ -44,7 +44,7 @@ "backup_database": "Vytvořit výpis databáze", "backup_database_enable_description": "Povolit výpisy z databáze", "backup_keep_last_amount": "Počet předchozích výpisů, které se mají ponechat", - "backup_settings": "Nastavení výpisu databáze", + "backup_settings": "Zálohování databáze", "backup_settings_description": "Správa nastavení výpisu databáze.", "cleared_jobs": "Hotové úlohy pro: {job}", "config_set_by_file": "Konfigurace je aktuálně prováděna konfiguračním souborem", @@ -166,6 +166,20 @@ "metadata_settings_description": "Správa nastavení metadat", "migration_job": "Migrace", "migration_job_description": "Migrace miniatur snímků a obličejů do nejnovější struktury složek", + "nightly_tasks_cluster_faces_setting_description": "Spustit rozpoznávání obličeje na nově nalezených obličejích", + "nightly_tasks_cluster_new_faces_setting": "Seskupit nové tváře", + "nightly_tasks_database_cleanup_setting": "Úlohy čištění databáze", + "nightly_tasks_database_cleanup_setting_description": "Vyčistit databázi od starých dat, jejichž platnost vypršela", + "nightly_tasks_generate_memories_setting": "Vytváření vzpomínek", + "nightly_tasks_generate_memories_setting_description": "Vytváření nových vzpomínek z položek", + "nightly_tasks_missing_thumbnails_setting": "Generovat chybějící miniatury", + "nightly_tasks_missing_thumbnails_setting_description": "Řadit položky bez miniatur do fronty pro generování miniatur", + "nightly_tasks_settings": "Noční úlohy", + "nightly_tasks_settings_description": "Správa nočních úkolů", + "nightly_tasks_start_time_setting": "Čas zahájení", + "nightly_tasks_start_time_setting_description": "Čas, kdy server spustí noční úlohy", + "nightly_tasks_sync_quota_usage_setting": "Synchronizace využití kvóty", + "nightly_tasks_sync_quota_usage_setting_description": "Aktualizovat kvótu úložiště uživatele na základě aktuálního využití", "no_paths_added": "Nebyly přidány žádné cesty", "no_pattern_added": "Nebyl přidán žádný vzor", "note_apply_storage_label_previous_assets": "Upozornění: Pro uplatnění Štítku úložiště na dříve nahrané položky spusťte", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "Mobilní přesměrování URI", "oauth_mobile_redirect_uri_override": "Přepsat mobilní přesměrování URI", "oauth_mobile_redirect_uri_override_description": "Povolit, pokud poskytovatel OAuth nepovoluje mobilní URI, například ''{callback}''", + "oauth_role_claim": "Deklarace Role", + "oauth_role_claim_description": "Automaticky udělit přístup správce na základě přítomnosti této deklarace. Deklarace může mít hodnotu 'user' nebo 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Správa nastavení OAuth přihlášení", "oauth_settings_more_details": "Další podrobnosti o této funkci naleznete v dokumentaci.", @@ -357,10 +373,12 @@ "admin_password": "Heslo správce", "administration": "Administrace", "advanced": "Pokročilé", + "advanced_settings_beta_timeline_subtitle": "Vyzkoušejte nové prostředí aplikace", + "advanced_settings_beta_timeline_title": "Časová osa beta verze", "advanced_settings_enable_alternate_media_filter_subtitle": "Tuto možnost použijte k filtrování médií během synchronizace na základě alternativních kritérií. Tuto možnost vyzkoušejte pouze v případě, že máte problémy s detekcí všech alb v aplikaci.", "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTÁLNÍ] Použít alternativní filtr pro synchronizaci alb zařízení", "advanced_settings_log_level_title": "Úroveň protokolování: {level}", - "advanced_settings_prefer_remote_subtitle": "U některých zařízení je načítání miniatur z prostředků v zařízení velmi pomalé. Aktivujte toto nastavení, aby se místo toho načítaly vzdálené obrázky.", + "advanced_settings_prefer_remote_subtitle": "U některých zařízení je načítání miniatur z lokálních prostředků velmi pomalé. Aktivujte toto nastavení, aby se místo toho načítaly vzdálené obrázky.", "advanced_settings_prefer_remote_title": "Preferovat vzdálené obrázky", "advanced_settings_proxy_headers_subtitle": "Definice hlaviček proxy serveru, které by měl Immich odesílat s každým síťovým požadavkem", "advanced_settings_proxy_headers_title": "Proxy hlavičky", @@ -388,6 +406,7 @@ "album_options": "Možnosti alba", "album_remove_user": "Odebrat uživatele?", "album_remove_user_confirmation": "Opravdu chcete odebrat uživatele {user}?", + "album_search_not_found": "Nebyla nalezena žádná alba odpovídající vašemu hledání", "album_share_no_users": "Zřejmě jste toto album sdíleli se všemi uživateli, nebo nemáte žádného uživatele, se kterým byste ho mohli sdílet.", "album_updated": "Album aktualizováno", "album_updated_setting_description": "Dostávat e-mailová oznámení o nových položkách sdíleného alba", @@ -407,6 +426,7 @@ "albums_default_sort_order": "Výchozí řazení alb", "albums_default_sort_order_description": "Výchozí řazení položek při vytváření nových alb.", "albums_feature_description": "Sbírky položek, které lze sdílet s ostatními uživateli.", + "albums_on_device_count": "Alba v zařízení ({count})", "all": "Vše", "all_albums": "Všechna alba", "all_people": "Všichni lidé", @@ -427,7 +447,8 @@ "app_settings": "Aplikace", "appears_in": "Vyskytuje se v", "archive": "Archiv", - "archive_or_unarchive_photo": "Archivovat nebo odarchivovat fotku", + "archive_action_prompt": "{count} přidaných do archivu", + "archive_or_unarchive_photo": "Přidat nebo odebrat fotku z archivu", "archive_page_no_archived_assets": "Nebyla nalezena žádná archivovaná média", "archive_page_title": "Archiv ({count})", "archive_size": "Velikost archivu", @@ -464,14 +485,13 @@ "assets": "Položky", "assets_added_count": "{count, plural, one {Přidána # položka} few {Přidány # položky} other {Přidáno # položek}}", "assets_added_to_album_count": "Do alba {count, plural, one {byla přidána # položka} few {byly přidány # položky} other {bylo přidáno # položek}}", - "assets_added_to_name_count": "{count, plural, one {Přidána # položka} few {Přidány # položky} other {Přidáno # položek}} do {hasName, select, true {alba {name}} other {nového alba}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Položku} other {Položky}} nelze přidat do alba", "assets_count": "{count, plural, one {# položka} few {# položky} other {# položek}}", "assets_deleted_permanently": "{count} položek trvale odstraněno", "assets_deleted_permanently_from_server": "{count} položek trvale odstraněno z Immich serveru", "assets_downloaded_failed": "{count, plural, one {Stažen # soubor - {error} souborů selhalo} few {Staženy # soubory - {error} souborů selhalo} other {Staženo # souborů - {error} souborů selhalo}}", "assets_downloaded_successfully": "{count, plural, one {Úspěšně stažen # soubor} few {Úspěšně staženy # soubory} other {Úspěšně staženo # souborů}}", - "assets_moved_to_trash_count": "Do koše {count, plural, one {přesunuta # položka} few {přesunuty # položky} other {přesunuto # položek}}", + "assets_moved_to_trash_count": "{count, plural, one {# položka přesunuta} few {# položky přesunuty} other {# položek přesunuto}} do koše", "assets_permanently_deleted_count": "Trvale {count, plural, one {smazána # položka} few {smazány # položky} other {smazáno # položek}}", "assets_removed_count": "{count, plural, one {Odstraněna # položka} few {Odstraněny # položky} other {Odstraněno # položek}}", "assets_removed_permanently_from_device": "{count} položek trvale odstraněno z vašeho zařízení", @@ -587,6 +607,7 @@ "cancel": "Zrušit", "cancel_search": "Zrušit vyhledávání", "canceled": "Zrušeno", + "canceling": "Rušení", "cannot_merge_people": "Nelze sloučit osoby", "cannot_undo_this_action": "Tuto akci nelze vrátit zpět!", "cannot_update_the_description": "Nelze aktualizovat popis", @@ -703,7 +724,7 @@ "daily_title_text_date": "EEEE, d. MMMM", "daily_title_text_date_year": "EEEE, d. MMMM y", "dark": "Tmavý", - "darkTheme": "Přepnout tmavý motiv", + "dark_theme": "Přepnout tmavý motiv", "date_after": "Datum po", "date_and_time": "Datum a čas", "date_before": "Datum před", @@ -719,6 +740,7 @@ "default_locale": "Výchozí jazyk", "default_locale_description": "Formátovat datumy a čísla podle místního prostředí prohlížeče", "delete": "Smazat", + "delete_action_prompt": "{count} trvale smazaných", "delete_album": "Smazat album", "delete_api_key_prompt": "Opravdu chcete tento API klíč odstranit?", "delete_dialog_alert": "Tyto položky budou trvale smazány z aplikace Immich i z vašeho zařízení", @@ -732,6 +754,7 @@ "delete_key": "Smazat klíč", "delete_library": "Smazat knihovnu", "delete_link": "Smazat odkaz", + "delete_local_action_prompt": "{count} smazáno lokálně", "delete_local_dialog_ok_backed_up_only": "Smazat pouze zálohované", "delete_local_dialog_ok_force": "Přesto smazat", "delete_others": "Odstranit ostatní", @@ -745,6 +768,7 @@ "description": "Popis", "description_input_hint_text": "Přidat popis...", "description_input_submit_error": "Chyba aktualizace popisu, další podrobnosti najdete v logu", + "deselect_all": "Zrušit výběr všech", "details": "Podrobnosti", "direction": "Směr", "disabled": "Zakázáno", @@ -762,6 +786,7 @@ "documentation": "Dokumentace", "done": "Hotovo", "download": "Stáhnout", + "download_action_prompt": "Stahování {count} položek", "download_canceled": "Stahování zrušeno", "download_complete": "Stahování kompletní", "download_enqueue": "Stahování ve frontě", @@ -799,6 +824,7 @@ "edit_key": "Upravit klíč", "edit_link": "Upravit odkaz", "edit_location": "Upravit polohu", + "edit_location_action_prompt": "{count} upravených poloh", "edit_location_dialog_title": "Poloha", "edit_name": "Upravit jméno", "edit_people": "Upravit lidi", @@ -817,6 +843,7 @@ "empty_trash": "Vyprázdnit koš", "empty_trash_confirmation": "Opravdu chcete vysypat koš? Tím se z Immiche trvale odstraní všechny položky v koši.\nTuto akci nelze vrátit zpět!", "enable": "Povolit", + "enable_backup": "Povolit zálohování", "enable_biometric_auth_description": "Zadejte váš PIN kód pro povolení biometrického ověřování", "enabled": "Povoleno", "end_date": "Konečné datum", @@ -861,7 +888,7 @@ "failed_to_load_people": "Chyba načítání osob", "failed_to_remove_product_key": "Nepodařilo se odebrat klíč produktu", "failed_to_stack_assets": "Nepodařilo se seskupit položky", - "failed_to_unstack_assets": "Nepodařilo se rozložit položky", + "failed_to_unstack_assets": "Nepodařilo se zrušit seskupení položek", "failed_to_update_notification_status": "Nepodařilo se aktualizovat stav oznámení", "import_path_already_exists": "Tato cesta importu již existuje.", "incorrect_email_or_password": "Nesprávný e-mail nebo heslo", @@ -876,7 +903,7 @@ "unable_to_add_partners": "Nelze přidat partnery", "unable_to_add_remove_archive": "Nelze {archived, select, true {odstranit položku z} other {přidat položku do}} archivu", "unable_to_add_remove_favorites": "Nelze {favorite, select, true {oblíbit položku} other {zrušit oblíbení položky}}", - "unable_to_archive_unarchive": "Nelze {archived, select, true {archivovat} other {odarchivovat}}", + "unable_to_archive_unarchive": "Nelze {archived, select, true {archivovat} other {odebrat z archivu}}", "unable_to_change_album_user_role": "Nelze změnit roli uživatele alba", "unable_to_change_date": "Nelze změnit datum", "unable_to_change_description": "Nelze změnit popis", @@ -984,6 +1011,7 @@ "failed_to_load_assets": "Nepodařilo se načíst položky", "failed_to_load_folder": "Nepodařilo se načíst složku", "favorite": "Oblíbit", + "favorite_action_prompt": "{count} přidáno do Oblíbených", "favorite_or_unfavorite_photo": "Oblíbit nebo zrušit oblíbení fotky", "favorites": "Oblíbené", "favorites_page_no_favorites": "Nebyla nalezena žádná oblíbená média", @@ -1127,6 +1155,7 @@ "library_page_sort_created": "Naposledy vytvořené", "library_page_sort_last_modified": "Naposledy upraveno", "library_page_sort_title": "Podle názvu alba", + "licenses": "Licence", "light": "Světlý", "like_deleted": "Lajk smazán", "link_motion_video": "Připojit pohyblivé video", @@ -1246,10 +1275,11 @@ "more": "Více", "move": "Přesunout", "move_off_locked_folder": "Přesunout z uzamčené složky", + "move_to_lock_folder_action_prompt": "{count} přidaných do uzamčené složky", "move_to_locked_folder": "Přesunout do uzamčené složky", "move_to_locked_folder_confirmation": "Tyto fotky a videa budou odstraněny ze všech alb a bude je možné zobrazit pouze v uzamčené složce", - "moved_to_archive": "{count, plural, one {Přesunuta # položka} few {Přesunuty # položky} other {Přesunuto # položek}} do archivu", - "moved_to_library": "{count, plural, one {Přesunuta # položka} few {Přesunuty # položky} other {Přesunuto # položek}} do knihovny", + "moved_to_archive": "{count, plural, one {# položka přesunuta} few {# položky přesunuty} other {# položek přesunuto}} do archivu", + "moved_to_library": "{count, plural, one {# položka přesunuta} few {# položky přesunuty} other {# položek přesunuto}} do knihovny", "moved_to_trash": "Přesunuto do koše", "multiselect_grid_edit_date_time_err_read_only": "Nelze upravit datum položek pouze pro čtení, přeskakuji", "multiselect_grid_edit_gps_err_read_only": "Nelze upravit polohu položek pouze pro čtení, přeskakuji", @@ -1460,6 +1490,7 @@ "purchase_server_description_2": "Stav podporovatele", "purchase_server_title": "Server", "purchase_settings_server_activated": "Produktový klíč serveru spravuje správce", + "queue_status": "Ve frontě {count}/{total}", "rating": "Hodnocení hvězdičkami", "rating_clear": "Vyčistit hodnocení", "rating_count": "{count, plural, one {# hvězdička} few {# hvězdičky} other {# hvězdček}}", @@ -1495,7 +1526,9 @@ "remove_custom_date_range": "Odstranit vlastní rozsah datumů", "remove_deleted_assets": "Odstranit offline soubory", "remove_from_album": "Odstranit z alba", + "remove_from_album_action_prompt": "{count} odstraněných z alba", "remove_from_favorites": "Odstranit z oblíbených", + "remove_from_lock_folder_action_prompt": "{count} odebraných z uzamčené složky", "remove_from_locked_folder": "Odstranit z uzamčené složky", "remove_from_locked_folder_confirmation": "Opravdu chcete tyto fotky a videa přesunout z uzamčené složky? Budou viditelné ve vaší knihovně.", "remove_from_shared_link": "Odstranit ze sdíleného odkazu", @@ -1667,6 +1700,7 @@ "settings_saved": "Nastavení uloženo", "setup_pin_code": "Nastavení PIN kódu", "share": "Sdílet", + "share_action_prompt": "Sdíleno {count} položek", "share_add_photos": "Přidat fotografie", "share_assets_selected": "{count} vybráno", "share_dialog_preparing": "Připravuji...", @@ -1768,6 +1802,7 @@ "sort_title": "Název alba", "source": "Zdroj", "stack": "Seskupit", + "stack_action_prompt": "{count} seskupeno", "stack_duplicates": "Seskupit duplicity", "stack_select_one_photo": "Vyberte jednu hlavní fotografii pro seskupení", "stack_selected_photos": "Seskupení vybraných fotografií", @@ -1838,6 +1873,7 @@ "total": "Celkem", "total_usage": "Celkové využití", "trash": "Koš", + "trash_action_prompt": "{count} přesunutých do koše", "trash_all": "Vyhodit vše", "trash_count": "Vyhodit {count, number}", "trash_delete_asset": "Vyhodit/Smazat položku", @@ -1854,10 +1890,12 @@ "type": "Typ", "unable_to_change_pin_code": "Nelze změnit PIN kód", "unable_to_setup_pin_code": "Nelze nastavit PIN kód", - "unarchive": "Odarchivovat", + "unarchive": "Odebrat z archivu", + "unarchive_action_prompt": "{count} odstraněných z archivu", "unarchived_count": "{count, plural, one {Odarchivována #} few {Odarchivovány #} other {Odarchivováno #}}", "undo": "Vrátit zpět", "unfavorite": "Zrušit oblíbení", + "unfavorite_action_prompt": "{count} odstraněných z oblíbených", "unhide_person": "Zrušit skrytí osoby", "unknown": "Neznámý", "unknown_country": "Neznámá země", @@ -1875,12 +1913,15 @@ "unselect_all_duplicates": "Zrušit výběr všech duplicit", "unselect_all_in": "Zrušit výběr ve skupině {group}", "unstack": "Zrušit seskupení", - "unstacked_assets_count": "{count, plural, one {Rozložená # položka} few {Rozložené # položky} other {Rozložených # položiek}}", + "unstack_action_prompt": "{count} seskupených zrušeno", + "unstacked_assets_count": "{count, plural, one {Rozložená # položka} few {Rozložené # položky} other {Rozložených # položek}}", + "untagged": "Neoznačeno", "up_next": "To je prozatím vše", "updated_at": "Aktualizováno", "updated_password": "Heslo aktualizováno", "upload": "Nahrát", "upload_concurrency": "Souběžnost nahrávání", + "upload_details": "Detaily nahrávání", "upload_dialog_info": "Chcete zálohovat vybrané položky na server?", "upload_dialog_title": "Nahrát položku", "upload_errors": "Nahrávání bylo dokončeno s {count, plural, one {# chybou} other {# chybami}}, obnovte stránku pro zobrazení nových položek.", @@ -1912,6 +1953,7 @@ "user_usage_stats_description": "Zobrazit statistiky používání účtu", "username": "Uživateleské jméno", "users": "Uživatelé", + "users_added_to_album_count": "{count, plural, one {Přidán # uživatel} few {Přidány # uživatelé} other {Přidáno # uživatelů}} do alba", "utilities": "Nástroje", "validate": "Ověřit", "validate_endpoint_error": "Zadejte platné URL", @@ -1930,6 +1972,7 @@ "view_album": "Zobrazit album", "view_all": "Zobrazit vše", "view_all_users": "Zobrazit všechny uživatele", + "view_details": "Zobrazit podrobnosti", "view_in_timeline": "Zobrazit na časové ose", "view_link": "Zobrazit odkaz", "view_links": "Zobrazit odkazy", @@ -1941,7 +1984,7 @@ "view_user": "Zobrazit uživatele", "viewer_remove_from_stack": "Odstranit ze zásobníku", "viewer_stack_use_as_main_asset": "Použít jako hlavní položku", - "viewer_unstack": "Rozbalit zásobník", + "viewer_unstack": "Zrušit zásobník", "visibility_changed": "Viditelnost změněna u {count, plural, one {# osoby} few {# osob} other {# lidí}}", "waiting": "Čekající", "warning": "Upozornění", diff --git a/i18n/da.json b/i18n/da.json index 3273a2d553..e9f9534927 100644 --- a/i18n/da.json +++ b/i18n/da.json @@ -34,6 +34,7 @@ "added_to_favorites_count": "Tilføjet {count, number} til favoritter", "admin": { "add_exclusion_pattern_description": "Tilføj udelukkelsesmønstre. Globbing ved hjælp af *, ** og ? understøttes. For at ignorere alle filer i enhver mappe med navnet \"Raw\", brug \"**/Raw/**\". For at ignorere alle filer, der slutter på \".tif\", brug \"**/*.tif\". For at ignorere en absolut sti, brug \"/sti/til/ignoreret/**\".", + "admin_user": "Administrator bruger", "asset_offline_description": "Denne eksterne biblioteksressource findes ikke længere på disken og er blevet flyttet til papirkurven. Hvis filen blev flyttet inde i biblioteket, skal du tjekke din tidslinje for den nye tilsvarende ressource. For at gendanne denne ressource skal du sikre, at filstien nedenfor kan tilgås af Immich og scanne biblioteket.", "authentication_settings": "Godkendelsesindstillinger", "authentication_settings_description": "Administrer adgangskode, OAuth og andre godkendelsesindstillinger", @@ -165,6 +166,7 @@ "metadata_settings_description": "Håndtér metadataindstillinger", "migration_job": "Migrering", "migration_job_description": "Migrér miniaturebilleder for aktiver og ansigter til den seneste mappestruktur", + "nightly_tasks_cluster_faces_setting_description": "Kør ansigtsgenkendelse på nye ansigter", "no_paths_added": "Ingen stier tilføjet", "no_pattern_added": "Intet mønster tilføjet", "note_apply_storage_label_previous_assets": "Bemærk: For at anvende Lagringsmærkatet på tidligere uploadede mediefiler, kør", @@ -462,7 +464,6 @@ "assets": "elementer", "assets_added_count": "Tilføjet {count, plural, one {# mediefil} other {# mediefiler}}", "assets_added_to_album_count": "{count, plural, one {# mediefil} other {# mediefiler}} tilføjet til albummet", - "assets_added_to_name_count": "Tilføjet {count, plural, one {# mediefil} other {# mediefiler}} til {hasName, select, true {{name}} other {nyt album}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Billed} other {Billeder}} kan ikke blive tilføjet til album", "assets_count": "{count, plural, one {# mediefil} other {# mediefiler}}", "assets_deleted_permanently": "{count} element(er) blev fjernet permanent", @@ -701,7 +702,6 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Mørk", - "darkTheme": "Skift til mørkt tema", "date_after": "Dato efter", "date_and_time": "Dato og klokkeslæt", "date_before": "Dato før", diff --git a/i18n/de.json b/i18n/de.json index bbcd9c569c..79ba1251d5 100644 --- a/i18n/de.json +++ b/i18n/de.json @@ -56,9 +56,9 @@ "confirm_user_pin_code_reset": "Bist du sicher, dass du den PIN Code von {user} zurücksetzen möchtest?", "create_job": "Aufgabe erstellen", "cron_expression": "Cron Zeitangabe", - "cron_expression_description": "Setze ein Intervall für die Sicherung mittels cron. Hilfe mit dem Format bietet dir dabei z.B der Crontab Guru", + "cron_expression_description": "Setze ein Intervall für die Sicherung mittels cron. Hilfe mit dem Format bietet dir dabei z. B. der Crontab Guru", "cron_expression_presets": "Nützliche Zeitangaben für Cron", - "disable_login": "Login deaktvieren", + "disable_login": "Login deaktivieren", "duplicate_detection_job_description": "Diese Aufgabe führt das maschinelle Lernen für jede Datei aus, um Duplikate zu finden. Diese Aufgabe beruht auf der intelligenten Suche", "exclusion_pattern_description": "Mit Ausschlussmustern können Dateien und Ordner beim Scannen Ihrer Bibliothek ignoriert werden. Dies ist nützlich, wenn du Ordner hast, die Dateien enthalten, die du nicht importieren möchtest, wie z. B. RAW-Dateien.", "external_library_management": "Verwaltung externer Bibliotheken", @@ -166,6 +166,20 @@ "metadata_settings_description": "Metadaten-Einstellungen verwalten", "migration_job": "Migration", "migration_job_description": "Diese Aufgabe migriert Miniaturansichten für Dateien und Gesichter in die neueste Ordnerstruktur", + "nightly_tasks_cluster_faces_setting_description": "Gesichtsidentifikation auf neu erkannten Gesichtern ausführen", + "nightly_tasks_cluster_new_faces_setting": "Neue Gesichter gruppieren", + "nightly_tasks_database_cleanup_setting": "Datenbankbereinigungs-Aufgaben", + "nightly_tasks_database_cleanup_setting_description": "Alte, abgelaufene Daten aus der Datenbank bereinigen", + "nightly_tasks_generate_memories_setting": "Erinnerungen generieren", + "nightly_tasks_generate_memories_setting_description": "Neue Erinnerungen aus Dateien erstellen", + "nightly_tasks_missing_thumbnails_setting": "Fehlende Miniaturansichten generieren", + "nightly_tasks_missing_thumbnails_setting_description": "Dateien ohne Miniaturansicht in die Warteschlange zur Miniaturansicht-Generierung hinzufügen", + "nightly_tasks_settings": "Einstellungen für nächtliche Aufgaben", + "nightly_tasks_settings_description": "Nächtliche Aufgaben verwalten", + "nightly_tasks_start_time_setting": "Startzeit", + "nightly_tasks_start_time_setting_description": "Die Zeit, zu der der Server mit der Ausführung der nächtlichen Aufgaben beginnt", + "nightly_tasks_sync_quota_usage_setting": "Kontingentnutzung synchronisieren", + "nightly_tasks_sync_quota_usage_setting_description": "Benutzerspeicherkontingent basierend auf der aktuellen Nutzung aktualisieren", "no_paths_added": "Keine Pfade hinzugefügt", "no_pattern_added": "Kein Ausschlussmuster hinzugefügt", "note_apply_storage_label_previous_assets": "Hinweis: Um den Speicherpfad auf die vorher hochgeladenen Dateien anzuwenden, starte den", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "Mobile Umleitungs-URI", "oauth_mobile_redirect_uri_override": "Mobile Umleitungs-URI überschreiben", "oauth_mobile_redirect_uri_override_description": "Einschalten, wenn der OAuth-Anbieter keine mobile URI wie ''{callback}'' erlaubt", + "oauth_role_claim": "Rollen-Claim", + "oauth_role_claim_description": "Gewähre automatisch Admin-Zugriff basierend auf dem Vorhandensein dieses Claims. Der Claim kann entweder 'user' oder 'admin' sein.", "oauth_settings": "OAuth", "oauth_settings_description": "OAuth-Anmeldeeinstellungen verwalten", "oauth_settings_more_details": "Weitere Informationen zu dieser Funktion findest du in der Dokumentation.", @@ -244,6 +260,7 @@ "storage_template_migration_info": "Die Speichervorlage wird alle Dateierweiterungen in Kleinbuchstaben umwandeln. Vorlagenänderungen gelten nur für neue Dateien. Um die Vorlage rückwirkend auf bereits hochgeladene Assets anzuwenden, führe den {job} aus.", "storage_template_migration_job": "Speichervorlagenmigrations-Aufgabe", "storage_template_more_details": "Weitere Details zu dieser Funktion findest du unter Speichervorlage und dessen Implikationen", + "storage_template_onboarding_description_v2": "Wenn aktiviert, werden Dateien automatisch nach einer benutzerdefinierten Vorlage organisiert. Für mehr Informationen siehe die Dokumentation.", "storage_template_path_length": "Ungefähres Pfadlängen-Limit: {length, number}/{limit, number}", "storage_template_settings": "Speichervorlage", "storage_template_settings_description": "Die Ordnerstruktur und den Dateinamen der hochgeladenen Datei verwalten", @@ -356,10 +373,12 @@ "admin_password": "Administrator Passwort", "administration": "Verwaltung", "advanced": "Erweitert", + "advanced_settings_beta_timeline_subtitle": "Probier die neue App-Erfahrung aus", + "advanced_settings_beta_timeline_title": "Beta-Timeline", "advanced_settings_enable_alternate_media_filter_subtitle": "Verwende diese Option, um Medien während der Synchronisierung nach anderen Kriterien zu filtern. Versuchen dies nur, wenn Probleme mit der Erkennung aller Alben durch die App auftreten.", "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTELL] Benutze alternativen Filter für Synchronisierung der Gerätealben", "advanced_settings_log_level_title": "Log-Level: {level}", - "advanced_settings_prefer_remote_subtitle": "Einige Geräte sind sehr langsam beim Laden von Miniaturbildern direkt aus dem Gerät. Aktivieren Sie diese Einstellung, um stattdessen die Server-Bilder zu laden.", + "advanced_settings_prefer_remote_subtitle": "Einige Geräte sind sehr langsam beim Laden von lokalen Vorschaubildern. Aktivieren Sie diese Einstellung, um stattdessen die Server-Bilder zu laden.", "advanced_settings_prefer_remote_title": "Server-Bilder bevorzugen", "advanced_settings_proxy_headers_subtitle": "Definiere einen Proxy-Header, den Immich bei jeder Netzwerkanfrage mitschicken soll", "advanced_settings_proxy_headers_title": "Proxy-Headers", @@ -387,6 +406,7 @@ "album_options": "Albumoptionen", "album_remove_user": "Nutzer entfernen?", "album_remove_user_confirmation": "Bist du sicher, dass du {user} entfernen willst?", + "album_search_not_found": "Keine Alben gefunden, die zur Suche passen", "album_share_no_users": "Es sieht so aus, als hättest du dieses Album mit allen Benutzern geteilt oder du hast keine Benutzer, mit denen du teilen kannst.", "album_updated": "Album aktualisiert", "album_updated_setting_description": "Erhalte eine E-Mail-Benachrichtigung, wenn ein freigegebenes Album neue Dateien enthält", @@ -406,6 +426,7 @@ "albums_default_sort_order": "Standard Album Sortierung", "albums_default_sort_order_description": "Sortierreihenfolge der Dateien bei der Erstellung neuer Alben.", "albums_feature_description": "Sammlung an Alben die mit anderen Benutzern geteilt werden können.", + "albums_on_device_count": "Alben auf dem Gerät ({count})", "all": "Alle", "all_albums": "Alle Alben", "all_people": "Alle Personen", @@ -426,6 +447,7 @@ "app_settings": "App-Einstellungen", "appears_in": "Erscheint in", "archive": "Archiv", + "archive_action_prompt": "{count} zum Archiv hinzugefügt", "archive_or_unarchive_photo": "Foto archivieren bzw. Archivierung aufheben", "archive_page_no_archived_assets": "Keine archivierten Inhalte gefunden", "archive_page_title": "Archiv ({count})", @@ -463,7 +485,6 @@ "assets": "Dateien", "assets_added_count": "{count, plural, one {# Datei} other {# Dateien}} hinzugefügt", "assets_added_to_album_count": "{count, plural, one {# Datei} other {# Dateien}} zum Album hinzugefügt", - "assets_added_to_name_count": "{count, plural, one {# Element} other {# Elemente}} zu {hasName, select, true {{name}} other {neuem Album}} hinzugefügt", "assets_cannot_be_added_to_album_count": "{count, plural, one {Datei kann}other {Dateien können}} nicht zum Album hinzugefügt werden", "assets_count": "{count, plural, one {# Datei} other {# Dateien}}", "assets_deleted_permanently": "{count} Element(e) permanent gelöscht", @@ -586,6 +607,7 @@ "cancel": "Abbrechen", "cancel_search": "Suche abbrechen", "canceled": "Abgebrochen", + "canceling": "Abbrechen", "cannot_merge_people": "Personen können nicht zusammengeführt werden", "cannot_undo_this_action": "Diese Aktion kann nicht rückgängig gemacht werden!", "cannot_update_the_description": "Beschreibung kann nicht aktualisiert werden", @@ -702,7 +724,7 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Dunkel", - "darkTheme": "Dunkles Theme umschalten", + "dark_theme": "Dunkle Ansicht umschalten", "date_after": "Datum nach", "date_and_time": "Datum und Zeit", "date_before": "Datum vor", @@ -718,6 +740,7 @@ "default_locale": "Standard-Sprache", "default_locale_description": "Datumsangaben und Zahlen basierend auf dem Gebietsschema des Browsers formatieren", "delete": "Löschen", + "delete_action_prompt": "{count} endgültig gelöscht", "delete_album": "Album löschen", "delete_api_key_prompt": "Bist du sicher, dass du diesen API-Schlüssel löschen willst?", "delete_dialog_alert": "Diese Elemente werden unwiderruflich von Immich und dem Gerät entfernt", @@ -731,6 +754,7 @@ "delete_key": "Schlüssel löschen", "delete_library": "Bibliothek löschen", "delete_link": "Link löschen", + "delete_local_action_prompt": "{count} lokal gelöscht", "delete_local_dialog_ok_backed_up_only": "Nur gesicherte Inhalte löschen", "delete_local_dialog_ok_force": "Trotzdem löschen", "delete_others": "Andere löschen", @@ -744,6 +768,7 @@ "description": "Beschreibung", "description_input_hint_text": "Beschreibung hinzufügen...", "description_input_submit_error": "Beschreibung konnte nicht geändert werden, bitte im Log für mehr Details nachsehen", + "deselect_all": "Alle abwählen", "details": "Details", "direction": "Richtung", "disabled": "Deaktiviert", @@ -761,6 +786,7 @@ "documentation": "Dokumentation", "done": "Fertig", "download": "Herunterladen", + "download_action_prompt": "Herunterladen von {count} Dateien", "download_canceled": "Download abgebrochen", "download_complete": "Download vollständig", "download_enqueue": "Download in die Warteschlange gesetzt", @@ -798,6 +824,7 @@ "edit_key": "Schlüssel bearbeiten", "edit_link": "Link bearbeiten", "edit_location": "Standort bearbeiten", + "edit_location_action_prompt": "{count} Geolokationen angepasst", "edit_location_dialog_title": "Ort bearbeiten", "edit_name": "Name bearbeiten", "edit_people": "Personen bearbeiten", @@ -816,6 +843,7 @@ "empty_trash": "Papierkorb leeren", "empty_trash_confirmation": "Bist du sicher, dass du den Papierkorb leeren willst?\nDies entfernt alle Dateien im Papierkorb endgültig aus Immich und kann nicht rückgängig gemacht werden!", "enable": "Aktivieren", + "enable_backup": "Sicherung aktivieren", "enable_biometric_auth_description": "Gib deinen PIN Code ein, um die biometrische Authentifizierung zu aktivieren", "enabled": "Aktiviert", "end_date": "Enddatum", @@ -983,6 +1011,7 @@ "failed_to_load_assets": "Laden der Assets fehlgeschlagen", "failed_to_load_folder": "Fehler beim Laden des Ordners", "favorite": "Favorit", + "favorite_action_prompt": "{count} zu den Favoriten hinzugefügt", "favorite_or_unfavorite_photo": "Favorisiertes oder nicht favorisiertes Foto", "favorites": "Favoriten", "favorites_page_no_favorites": "Keine favorisierten Inhalte gefunden", @@ -1126,6 +1155,7 @@ "library_page_sort_created": "Zuletzt erstellt", "library_page_sort_last_modified": "Zuletzt bearbeitet", "library_page_sort_title": "Titel des Albums", + "licenses": "Lizenzen", "light": "Hell", "like_deleted": "Like gelöscht", "link_motion_video": "Bewegungsvideo verknüpfen", @@ -1245,6 +1275,7 @@ "more": "Mehr", "move": "Verschieben", "move_off_locked_folder": "Aus dem gesperrten Ordner verschieben", + "move_to_lock_folder_action_prompt": "{count} zum gesperrten Ordner hinzugefügt", "move_to_locked_folder": "In den gesperrten Ordner verschieben", "move_to_locked_folder_confirmation": "Diese Fotos und Videos werden aus allen Alben entfernt und können nur noch im gesperrten Ordner angezeigt werden", "moved_to_archive": "{count, plural, one {# Datei} other {# Dateien}} archiviert", @@ -1459,6 +1490,7 @@ "purchase_server_description_2": "Unterstützerstatus", "purchase_server_title": "Server", "purchase_settings_server_activated": "Der Server-Produktschlüssel wird durch den Administrator verwaltet", + "queue_status": "Warteschlange {count}/{total}", "rating": "Bewertung", "rating_clear": "Bewertung löschen", "rating_count": "{count, plural, one {# Stern} other {# Sterne}}", @@ -1494,7 +1526,9 @@ "remove_custom_date_range": "Benutzerdefinierten Datumsbereich entfernen", "remove_deleted_assets": "Offline-Dateien entfernen", "remove_from_album": "Aus Album entfernen", + "remove_from_album_action_prompt": "{count} vom Album entfernt", "remove_from_favorites": "Aus Favoriten entfernen", + "remove_from_lock_folder_action_prompt": "{count} aus dem gesperrten Ordner entfernt", "remove_from_locked_folder": "Aus gesperrtem Ordner entfernen", "remove_from_locked_folder_confirmation": "Bist du sicher, dass du diese Fotos und Videos aus dem gesperrten Ordner entfernen möchtest? Sie werden wieder in deiner Bibliothek sichtbar sein.", "remove_from_shared_link": "Aus geteiltem Link entfernen", @@ -1666,6 +1700,7 @@ "settings_saved": "Einstellungen gespeichert", "setup_pin_code": "Einen PIN Code festlegen", "share": "Teilen", + "share_action_prompt": "{count} Dateien geteilt", "share_add_photos": "Fotos hinzufügen", "share_assets_selected": "{count} ausgewählt", "share_dialog_preparing": "Vorbereiten...", @@ -1767,6 +1802,7 @@ "sort_title": "Titel", "source": "Quellcode", "stack": "Stapel", + "stack_action_prompt": "{count} gestapelt", "stack_duplicates": "Duplikate stapeln", "stack_select_one_photo": "Hauptfoto für den Stapel auswählen", "stack_selected_photos": "Ausgewählte Fotos stapeln", @@ -1837,6 +1873,7 @@ "total": "Gesamt", "total_usage": "Gesamtnutzung", "trash": "Papierkorb", + "trash_action_prompt": "{count} in den Papierkorb verschoben", "trash_all": "Alle löschen", "trash_count": "Papierkorb {count, number}", "trash_delete_asset": "Datei löschen/in den Papierkorb verschieben", @@ -1854,9 +1891,11 @@ "unable_to_change_pin_code": "PIN Code konnte nicht geändert werden", "unable_to_setup_pin_code": "PIN Code konnte nicht festgelegt werden", "unarchive": "Entarchivieren", + "unarchive_action_prompt": "{count} aus dem Archiv entfernt", "unarchived_count": "{count, plural, other {# entarchiviert}}", "undo": "Rückgängig", "unfavorite": "Entfavorisieren", + "unfavorite_action_prompt": "{count} aus den Favoriten entfernt", "unhide_person": "Person einblenden", "unknown": "Unbekannt", "unknown_country": "Unbekanntes Land", @@ -1874,12 +1913,15 @@ "unselect_all_duplicates": "Alle Duplikate abwählen", "unselect_all_in": "Alle in {group} abwählen", "unstack": "Entstapeln", + "unstack_action_prompt": "{count} entstapelt", "unstacked_assets_count": "{count, plural, one {# Datei} other {# Dateien}} entstapelt", + "untagged": "Ohne Tag", "up_next": "Weiter", "updated_at": "Aktualisiert", "updated_password": "Passwort aktualisiert", "upload": "Hochladen", "upload_concurrency": "Parallelität beim Hochladen", + "upload_details": "Upload Details", "upload_dialog_info": "Willst du die ausgewählten Elemente auf dem Server sichern?", "upload_dialog_title": "Element hochladen", "upload_errors": "Hochladen mit {count, plural, one {# Fehler} other {# Fehlern}} abgeschlossen, aktualisiere die Seite, um neu hochgeladene Dateien zu sehen.", @@ -1911,6 +1953,7 @@ "user_usage_stats_description": "Statistiken zur Kontonutzung anzeigen", "username": "Nutzername", "users": "Benutzer", + "users_added_to_album_count": "{count, plural, one {# Benutzer} other {# Benutzer}} zum Album hinzugefügt", "utilities": "Hilfsmittel", "validate": "Validieren", "validate_endpoint_error": "Bitte gib eine gültige URL ein", @@ -1929,6 +1972,7 @@ "view_album": "Album anzeigen", "view_all": "Alles anzeigen", "view_all_users": "Alle Nutzer anzeigen", + "view_details": "Details ansehen", "view_in_timeline": "In Zeitleiste anzeigen", "view_link": "Link anzeigen", "view_links": "Links anzeigen", diff --git a/i18n/el.json b/i18n/el.json index 62d0481ec6..015bf80c0a 100644 --- a/i18n/el.json +++ b/i18n/el.json @@ -464,7 +464,6 @@ "assets": "Αντικείμενα", "assets_added_count": "Προστέθηκε {count, plural, one {# αρχείο} other {# αρχεία}}", "assets_added_to_album_count": "Προστέθηκε {count, plural, one {# αρχείο} other {# αρχεία}} στο άλμπουμ", - "assets_added_to_name_count": "Προστέθηκε {count, plural, one {# αρχείο} other {# αρχεία}} στο {hasName, select, true {{name}} other {νέο άλμπουμ}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Στοιχείο} other {Στοιχεία}} δεν μπορούν να προστεθούν στο άλμπουμ", "assets_count": "{count, plural, one {# αρχείο} other {# αρχεία}}", "assets_deleted_permanently": "{count} τα στοιχεία διαγράφηκαν οριστικά", @@ -703,7 +702,6 @@ "daily_title_text_date": "Ε, MMM dd", "daily_title_text_date_year": "Ε, MMM dd, yyyy", "dark": "Σκούρο", - "darkTheme": "Εναλλαγή σκούρου θέματος", "date_after": "Ημερομηνία μετά", "date_and_time": "Ημερομηνία και ώρα", "date_before": "Ημερομηνία πριν", diff --git a/i18n/es.json b/i18n/es.json index 7bfdc678df..83d1c9d355 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -2,7 +2,7 @@ "about": "Acerca de", "account": "Cuenta", "account_settings": "Ajustes de la cuenta", - "acknowledge": "De acuerdo", + "acknowledge": "Aceptar", "action": "Acción", "action_common_update": "Actualizar", "actions": "Acciones", @@ -14,13 +14,13 @@ "add_a_location": "Agregar ubicación", "add_a_name": "Agregar nombre", "add_a_title": "Agregar título", - "add_endpoint": "Añadir endpoint", + "add_endpoint": "Agregar endpoint", "add_exclusion_pattern": "Agregar patrón de exclusión", "add_import_path": "Agregar ruta de importación", "add_location": "Agregar ubicación", "add_more_users": "Agregar más usuarios", "add_partner": "Agregar compañero", - "add_path": "Agregar carpeta", + "add_path": "Agregar ruta", "add_photos": "Agregar fotos", "add_tag": "Agregar etiqueta", "add_to": "Agregar a…", @@ -63,8 +63,8 @@ "exclusion_pattern_description": "Los patrones de exclusión te permiten ignorar archivos y carpetas al escanear tu biblioteca. Es útil si tienes carpetas que contienen archivos que no deseas importar, por ejemplo archivos RAW.", "external_library_management": "Gestión de bibliotecas externas", "face_detection": "Detección de caras", - "face_detection_description": "Detecta las caras en los activos mediante aprendizaje automático. En el caso de los vídeos, solo se tiene en cuenta la miniatura. \"Actualizar\" (re)procesará todos los elementos. \"Restablecer\" borra además todos los datos de caras actuales. \"Falta\" pone en cola los elementos que aún no se han procesado. Las caras detectadas se pondrán en cola para el reconocimiento facial una vez finalizada la detección, agrupándolos en personas existentes o nuevas.", - "facial_recognition_job_description": "Agrupa las caras detectadas en personas. Este paso se ejecuta una vez finalizada la detección de caras. \"Restablecer\" (re)agrupa todas las caras. \"Falta\" pone en cola las caras que no tienen asignada una persona.", + "face_detection_description": "Detecta las caras en los elementos mediante aprendizaje automático. En el caso de los vídeos, solo se tiene en cuenta la miniatura. \"Actualizar\" (re)procesará todos los elementos. \"Restablecer\" borra además todos los datos de caras actuales. \"Faltante\" pone en cola los elementos que aún no se han procesado. Las caras detectadas se pondrán en cola para el reconocimiento facial una vez finalizada la detección, agrupándolos en personas existentes o nuevas.", + "facial_recognition_job_description": "Agrupa las caras detectadas en personas. Este paso se realiza después de completar la detección de caras. \"Restablecer\" (re)agrupa todas las caras. \"Faltante\" pone en cola las caras que no tienen una persona asignada.", "failed_job_command": "El comando {command} ha fallado para la tarea: {job}", "force_delete_user_warning": "CUIDADO: Esta acción eliminará inmediatamente el usuario y todos los elementos. Esta accion no se puede deshacer y los archivos no pueden ser recuperados.", "image_format": "Formato", @@ -166,6 +166,20 @@ "metadata_settings_description": "Administrar la configuración de metadatos", "migration_job": "Migración", "migration_job_description": "Migrar miniaturas de archivos y caras a la estructura de carpetas más reciente", + "nightly_tasks_cluster_faces_setting_description": "Ejecutar reconocimiento facial en caras detectadas recientemente", + "nightly_tasks_cluster_new_faces_setting": "Agrupar caras nuevas", + "nightly_tasks_database_cleanup_setting": "Tareas de limpieza de base de datos", + "nightly_tasks_database_cleanup_setting_description": "Limpiar datos antiguos y caducados de la base de datos", + "nightly_tasks_generate_memories_setting": "Generar recuerdos", + "nightly_tasks_generate_memories_setting_description": "Crear nuevos recuerdos a partir de activos", + "nightly_tasks_missing_thumbnails_setting": "Generar miniaturas faltantes", + "nightly_tasks_missing_thumbnails_setting_description": "Poner en cola a activos sin miniaturas para la generación de miniaturas", + "nightly_tasks_settings": "Configuración de Tareas Nocturnas", + "nightly_tasks_settings_description": "Gestionar Tareas Nocturnas", + "nightly_tasks_start_time_setting": "Tiempo de inicio", + "nightly_tasks_start_time_setting_description": "El tiempo cuando el servidor comienza a ejecutar las tareas nocturnas", + "nightly_tasks_sync_quota_usage_setting": "Uso de la cuota de sincronización", + "nightly_tasks_sync_quota_usage_setting_description": "Actualizar la cuota de almacenamiento del usuario, según el uso actual", "no_paths_added": "No se han añadido carpetas", "no_pattern_added": "No se han añadido patrones", "note_apply_storage_label_previous_assets": "Nota: para aplicar una Etiqueta de Almacenamiento a un elemento anteriormente cargado, lanza el", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "URI de redireccionamiento móvil", "oauth_mobile_redirect_uri_override": "Sobreescribir URI de redirección móvil", "oauth_mobile_redirect_uri_override_description": "Habilitar cuando el proveedor de OAuth no permite una URI móvil, como ''{callback}''", + "oauth_role_claim": "Concesión de rol", + "oauth_role_claim_description": "Otorgar acceso de administrador automáticamente según la presencia de esta concesión. La concesión puede tener \"usuario\" o \"admin\".", "oauth_settings": "OAuth", "oauth_settings_description": "Administrar la configuración de inicio de sesión de OAuth", "oauth_settings_more_details": "Para más detalles acerca de esta característica, consulte la documentación.", @@ -205,7 +221,7 @@ "oauth_storage_quota_claim_description": "Establezca automáticamente la cuota de almacenamiento del usuario al valor de esta solicitud.", "oauth_storage_quota_default": "Cuota de almacenamiento predeterminada (GiB)", "oauth_storage_quota_default_description": "Cuota en GiB que se utilizará cuando no se proporcione ninguna por defecto.", - "oauth_timeout": "Expiración de solicitud", + "oauth_timeout": "Límite de tiempo para la solicitud", "oauth_timeout_description": "Tiempo de espera de solicitudes en milisegundos", "password_enable_description": "Iniciar sesión con correo electrónico y contraseña", "password_settings": "Contraseña de Acceso", @@ -357,10 +373,12 @@ "admin_password": "Contraseña del Administrador", "administration": "Administración", "advanced": "Avanzada", + "advanced_settings_beta_timeline_subtitle": "Prueba la nueva experiencia de la aplicación", + "advanced_settings_beta_timeline_title": "Cronología beta", "advanced_settings_enable_alternate_media_filter_subtitle": "Usa esta opción para filtrar medios durante la sincronización según criterios alternativos. Intenta esto solo si tienes problemas con que la aplicación detecte todos los álbumes.", "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTAL] Usar filtro alternativo de sincronización de álbumes del dispositivo", "advanced_settings_log_level_title": "Nivel de registro: {level}", - "advanced_settings_prefer_remote_subtitle": "Algunos dispositivos tardan mucho en cargar las miniaturas de los elementos encontrados en el dispositivo. Activa esta opción para cargar imágenes remotas en su lugar.", + "advanced_settings_prefer_remote_subtitle": "Algunos dispositivos tardan mucho en cargar las miniaturas desde los archivos locales. Activa esta opción para cargar imágenes remotas en su lugar.", "advanced_settings_prefer_remote_title": "Preferir imágenes remotas", "advanced_settings_proxy_headers_subtitle": "Configura headers HTTP que Immich incluirá en cada petición de red", "advanced_settings_proxy_headers_title": "Cabeceras Proxy", @@ -405,8 +423,8 @@ "albums": "Álbumes", "albums_count": "{count, plural, one {{count, number} Álbum} other {{count, number} Álbumes}}", "albums_default_sort_order": "Ordenación por defecto de los álbumes", - "albums_default_sort_order_description": "Orden de clasificación inicial de los activos al crear nuevos álbumes.", - "albums_feature_description": "Colecciones de activos que pueden compartirse con otros usuarios.", + "albums_default_sort_order_description": "Orden de clasificación inicial de los recursos al crear nuevos álbumes.", + "albums_feature_description": "Colecciones de recursos que pueden ser compartidos con otros usuarios.", "all": "Todos", "all_albums": "Todos los albums", "all_people": "Todas las personas", @@ -427,6 +445,7 @@ "app_settings": "Ajustes de Aplicacion", "appears_in": "Aparece en", "archive": "Archivo", + "archive_action_prompt": "{count} añadidos al Archivo", "archive_or_unarchive_photo": "Archivar o restaurar foto", "archive_page_no_archived_assets": "No se encontraron elementos archivados", "archive_page_title": "Archivo ({count})", @@ -464,13 +483,12 @@ "assets": "elementos", "assets_added_count": "Añadido {count, plural, one {# asset} other {# assets}}", "assets_added_to_album_count": "Añadido {count, plural, one {# asset} other {# assets}} al álbum", - "assets_added_to_name_count": "Añadido {count, plural, one {# asset} other {# assets}} a {hasName, select, true {{name}} other {new album}}", - "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} no pueden ser añadidos al album", + "assets_cannot_be_added_to_album_count": "{count, plural, one {El recurso no puede ser añadido al álbum} other {Los recursos no pueden ser añadidos al álbum}}", "assets_count": "{count, plural, one {# activo} other {# activos}}", "assets_deleted_permanently": "{count} elemento(s) eliminado(s) permanentemente", "assets_deleted_permanently_from_server": "{count} recurso(s) eliminado(s) de forma permanente del servidor de Immich", - "assets_downloaded_failed": "{count, plural, one {Descargado archivo # - {error} archivo fallido} other {Descargados # archivos - {error} archivos fallidos}}", - "assets_downloaded_successfully": "{count, plural, one {Archivo # descargado correctamente} other {Archivos # descargados correctamente}}", + "assets_downloaded_failed": "{count, plural, one {# archivo descargado - {error} archivo fallido} other {# archivos descargados - {error} archivos fallidos}}", + "assets_downloaded_successfully": "{count, plural, one {# archivo descargado exitosamente} other {# archivos descargados exitosamente}}", "assets_moved_to_trash_count": "{count, plural, one {# elemento movido} other {# elementos movidos}} a la papelera", "assets_permanently_deleted_count": "Eliminado permanentemente {count, plural, one {# elemento} other {# elementos}}", "assets_removed_count": "Eliminado {count, plural, one {# elemento} other {# elementos}}", @@ -703,7 +721,7 @@ "daily_title_text_date": "E dd, MMM", "daily_title_text_date_year": "E dd de MMM, yyyy", "dark": "Oscuro", - "darkTheme": "Activar tema oscuro", + "dark_theme": "Alternar tema oscuro", "date_after": "Fecha posterior", "date_and_time": "Fecha y Hora", "date_before": "Fecha anterior", @@ -719,6 +737,7 @@ "default_locale": "Configuración regional predeterminada", "default_locale_description": "Formatee fechas y números según la configuración regional de su navegador", "delete": "Eliminar", + "delete_action_prompt": "{count} eliminados permanentemente", "delete_album": "Eliminar álbum", "delete_api_key_prompt": "¿Está seguro de que desea eliminar esta clave API?", "delete_dialog_alert": "Estos elementos serán eliminados permanentemente de Immich y de tu dispositivo", @@ -732,6 +751,7 @@ "delete_key": "Eliminar clave", "delete_library": "Eliminar biblioteca", "delete_link": "Eliminar enlace", + "delete_local_action_prompt": "{count} eliminados localmente", "delete_local_dialog_ok_backed_up_only": "Borrar solo las que tengan copia de seguridad", "delete_local_dialog_ok_force": "Borrar de todos modos", "delete_others": "Eliminar otros", @@ -749,6 +769,7 @@ "direction": "Dirección", "disabled": "Deshabilitado", "disallow_edits": "Bloquear edición", + "discord": "Discord", "discover": "Descubrir", "discovered_devices": "Dispositivos descubiertos", "dismiss_all_errors": "Descartar todos los errores", @@ -761,6 +782,7 @@ "documentation": "Documentación", "done": "Hecho", "download": "Descargar", + "download_action_prompt": "Descargando {count} archivos", "download_canceled": "Descarga cancelada", "download_complete": "Descarga completada", "download_enqueue": "Descarga en cola", @@ -798,6 +820,7 @@ "edit_key": "Editar clave", "edit_link": "Editar enlace", "edit_location": "Editar ubicación", + "edit_location_action_prompt": "{count} ubicaciones actualizadas", "edit_location_dialog_title": "Ubicación", "edit_name": "Cambiar nombre", "edit_people": "Editar persona", @@ -983,6 +1006,7 @@ "failed_to_load_assets": "Error al cargar los activos", "failed_to_load_folder": "No se pudo cargar la carpeta", "favorite": "Favorito", + "favorite_action_prompt": "{count} añadidos a Favoritos", "favorite_or_unfavorite_photo": "Foto favorita o no favorita", "favorites": "Favoritos", "favorites_page_no_favorites": "No se encontraron elementos marcados como favoritos", @@ -1126,6 +1150,7 @@ "library_page_sort_created": "Creado más recientemente", "library_page_sort_last_modified": "Última modificación", "library_page_sort_title": "Título del álbum", + "licenses": "Licencias", "light": "Claro", "like_deleted": "Me gusta eliminado", "link_motion_video": "Enlazar vídeo en movimiento", @@ -1135,7 +1160,7 @@ "list": "Listar", "loading": "Cargando", "loading_search_results_failed": "Error al cargar los resultados de la búsqueda", - "local_asset_cast_failed": "No se puede emitir un activo que no está cargado en el servidor", + "local_asset_cast_failed": "No es posible transmitir un recurso que no está subido al servidor", "local_network": "Red local", "local_network_sheet_info": "La aplicación se conectará al servidor a través de esta URL cuando utilice la red Wi-Fi especificada", "location_permission": "Permiso de ubicación", @@ -1238,13 +1263,14 @@ "merged_people_count": "Fusionada {count, plural, one {# persona} other {# personas}}", "minimize": "Minimizar", "minute": "Minuto", - "missing": "Perdido", + "missing": "Faltante", "model": "Modelo", "month": "Mes", - "monthly_title_text_date_format": "MMMM y", + "monthly_title_text_date_format": "MMMM a", "more": "Mas", "move": "Mover", "move_off_locked_folder": "Mover fuera de la carpeta protegida", + "move_to_lock_folder_action_prompt": "{count} añadidos a la carpeta protegida", "move_to_locked_folder": "Mover a la carpeta protegida", "move_to_locked_folder_confirmation": "Estas fotos y vídeos serán eliminados de todos los álbumes y sólo podrán ser vistos desde la carpeta protegida", "moved_to_archive": "Movido(s) {count, plural, one {# recurso} other {# recursos}} a archivo", @@ -1277,7 +1303,7 @@ "no_archived_assets_message": "Archive fotos y videos para ocultarlos de su vista de Fotos", "no_assets_message": "HAZ CLIC PARA SUBIR TU PRIMERA FOTO", "no_assets_to_show": "No hay elementos a mostrar", - "no_cast_devices_found": "Dispositivos de difusión no encontrados", + "no_cast_devices_found": "No se encontraron dispositivos de transmisión", "no_duplicates_found": "No se encontraron duplicados.", "no_exif_info_available": "No hay información exif disponible", "no_explore_results_message": "Sube más fotos para explorar tu colección.", @@ -1494,7 +1520,9 @@ "remove_custom_date_range": "Eliminar intervalo de fechas personalizado", "remove_deleted_assets": "Eliminar archivos sin conexión", "remove_from_album": "Eliminar del álbum", + "remove_from_album_action_prompt": "{count} eliminado del álbum", "remove_from_favorites": "Quitar de favoritos", + "remove_from_lock_folder_action_prompt": "{count} eliminado de la carpeta protegida", "remove_from_locked_folder": "Eliminar de la carpeta protegida", "remove_from_locked_folder_confirmation": "¿Estás seguro de que deseas mover estas fotos y vídeos fuera de la carpeta protegida? Serán visibles en tu biblioteca.", "remove_from_shared_link": "Eliminar desde enlace compartido", @@ -1558,17 +1586,17 @@ "search_city": "Buscar ciudad...", "search_country": "Buscar país...", "search_filter_apply": "Aplicar filtros", - "search_filter_camera_title": "Elige tipo de cámara", + "search_filter_camera_title": "Elegir tipo de cámara", "search_filter_date": "Fecha", "search_filter_date_interval": "{start} al {end}", - "search_filter_date_title": "Selecciona un intervalo de fechas", + "search_filter_date_title": "Seleccionar un intervalo de fechas", "search_filter_display_option_not_in_album": "No en álbum", "search_filter_display_options": "Opciones de visualización", "search_filter_filename": "Buscar por nombre de archivo", "search_filter_location": "Ubicación", "search_filter_location_title": "Seleccionar una ubicación", "search_filter_media_type": "Tipo de archivo", - "search_filter_media_type_title": "Selecciona el tipo de archivo", + "search_filter_media_type_title": "Seleccionar el tipo de archivo", "search_filter_people_title": "Seleccionar personas", "search_for": "Buscar", "search_for_existing_person": "Buscar persona existente", @@ -1591,23 +1619,23 @@ "search_people": "Buscar personas", "search_places": "Buscar lugar", "search_rating": "Buscar por calificación...", - "search_result_page_new_search_hint": "Nueva Busqueda", + "search_result_page_new_search_hint": "Nueva Búsqueda", "search_settings": "Ajustes de la búsqueda", "search_state": "Buscar región/estado...", "search_suggestion_list_smart_search_hint_1": "La búsqueda inteligente está habilitada por defecto, para buscar metadatos utiliza esta sintaxis ", "search_suggestion_list_smart_search_hint_2": "m:tu-término-de-búsqueda", - "search_tags": "Buscando etiquetas...", + "search_tags": "Buscar etiquetas...", "search_timezone": "Buscar zona horaria...", "search_type": "Tipo de búsqueda", "search_your_photos": "Busca tus fotos", "searching_locales": "Buscando sitios...", "second": "Segundo", "see_all_people": "Ver todas las personas", - "select": "Selecciona", + "select": "Seleccionar", "select_album_cover": "Seleccionar portada del álbum", "select_all": "Seleccionar todo", "select_all_duplicates": "Seleccionar todos los duplicados", - "select_all_in": "Selecciona todos en {group}", + "select_all_in": "Seleccionar todos en {group}", "select_avatar_color": "Seleccionar color del avatar", "select_face": "Seleccionar cara", "select_featured_photo": "Seleccionar foto principal", @@ -1638,7 +1666,7 @@ "set_date_of_birth": "Establecer fecha de nacimiento", "set_profile_picture": "Establecer foto de perfil", "set_slideshow_to_fullscreen": "Mostrar diapositivas en pantalla completa", - "set_stack_primary_asset": "Establecer como activo principal", + "set_stack_primary_asset": "Establecer como recurso principal", "setting_image_viewer_help": "El visor de detalles carga primero la miniatura pequeña, luego carga la vista previa de tamaño mediano (si está habilitada), finalmente carga la original (si está habilitada).", "setting_image_viewer_original_subtitle": "Activar para cargar la imagen en resolución original (¡muy grande!). Deshabilitar para reducir el consumo de datos (de red y caché).", "setting_image_viewer_original_title": "Cargar imagen original", @@ -1666,6 +1694,7 @@ "settings_saved": "Ajustes guardados", "setup_pin_code": "Establecer un PIN", "share": "Compartir", + "share_action_prompt": "{count} recursos compartidos", "share_add_photos": "Agregar fotos", "share_assets_selected": "{count} seleccionado(s)", "share_dialog_preparing": "Preparando...", @@ -1767,6 +1796,7 @@ "sort_title": "Título", "source": "Origen", "stack": "Apilar", + "stack_action_prompt": "{count} apilados", "stack_duplicates": "Apilar duplicados", "stack_select_one_photo": "Selecciona una imagen principal para la pila", "stack_selected_photos": "Apilar fotos seleccionadas", @@ -1776,7 +1806,7 @@ "start_date": "Fecha de inicio", "state": "Estado", "status": "Estado", - "stop_casting": "Parar difusión", + "stop_casting": "Detener transmisión", "stop_motion_photo": "Parar foto en movimiento", "stop_photo_sharing": "¿Dejar de compartir tus fotos?", "stop_photo_sharing_description": "{partner} ya no podrá acceder a tus fotos.", @@ -1837,6 +1867,7 @@ "total": "Total", "total_usage": "Uso total", "trash": "Papelera", + "trash_action_prompt": "{count} movidos a la papelera", "trash_all": "Descartar todo", "trash_count": "Descartar {count, number}", "trash_delete_asset": "Borrar/Eliminar archivo", @@ -1854,9 +1885,11 @@ "unable_to_change_pin_code": "No se ha podido cambiar el PIN", "unable_to_setup_pin_code": "No se ha podido establecer el PIN", "unarchive": "Desarchivar", + "unarchive_action_prompt": "{count} eliminados del archivo", "unarchived_count": "{count, plural, one {# No archivado} other {# No archivados}}", "undo": "Deshacer", "unfavorite": "Retirar favorito", + "unfavorite_action_prompt": "{count} eliminados de favoritos", "unhide_person": "Mostrar persona", "unknown": "Desconocido", "unknown_country": "País desconocido", @@ -1874,7 +1907,9 @@ "unselect_all_duplicates": "Deseleccionar todos los duplicados", "unselect_all_in": "Deselecciona todos en {group}", "unstack": "Desapilar", + "unstack_action_prompt": "{count} desapilado(s)", "unstacked_assets_count": "Desapilado(s) {count, plural, one {# elemento} other {# elementos}}", + "untagged": "Sin etiqueta", "up_next": "A continuación", "updated_at": "Actualizado", "updated_password": "Contraseña actualizada", @@ -1911,6 +1946,7 @@ "user_usage_stats_description": "Ver estadísticas de uso de la cuenta", "username": "Nombre de usuario", "users": "Usuarios", + "users_added_to_album_count": "{count, plural, one {# usuario agregado} other {# usuarios agregados}} al álbum", "utilities": "Utilidades", "validate": "Validar", "validate_endpoint_error": "Por favor, introduce una URL válida", diff --git a/i18n/et.json b/i18n/et.json index a2b17c6138..804d0c6c46 100644 --- a/i18n/et.json +++ b/i18n/et.json @@ -33,11 +33,11 @@ "added_to_favorites": "Lisatud lemmikutesse", "added_to_favorites_count": "{count, number} pilti lisatud lemmikutesse", "admin": { - "add_exclusion_pattern_description": "Lisa välistamismustreid. Toetatud on metamärgid *, ** ja ?. Kõikide kataloogis nimega \"Raw\" olevate failide ignoreerimiseks kasuta \"**/Raw/**\". Kõikide .tif failide ignoreerimiseks kasuta \"**/*.tif\". Absouutse tee ignoreerimiseks kasuta \"/path/to/ignore/**\".", + "add_exclusion_pattern_description": "Lisa välistamismustreid. Toetatud on metamärgid *, ** ja ?. Kõikide kataloogis nimega \"Raw\" olevate failide ignoreerimiseks kasuta \"**/Raw/**\". Kõikide \".tif\" lõpuga failide ignoreerimiseks kasuta \"**/*.tif\". Absouutse tee ignoreerimiseks kasuta \"/tee/mida/ignoreerida/**\".", "admin_user": "Administraator", - "asset_offline_description": "Seda välise kogu üksust ei leitud kettalt ning see liigutati prügikasti. Kui faili asukoht kogu siseselt muutus, leiad vastava uue üksuse oma ajajoonelt. Üksuse taastamiseks veendu, et allpool toodud failitee on Immich'ile kättesaadav ning skaneeri kogu uuesti.", + "asset_offline_description": "Seda välise kogu üksust ei leitud kettalt ning see liigutati prügikasti. Kui faili asukoht muutus kogu siseselt, leiad vastava uue üksuse oma ajajoonelt. Üksuse taastamiseks veendu, et allpool toodud failitee on Immich'ile kättesaadav ning skaneeri kogu uuesti.", "authentication_settings": "Autentimise seaded", - "authentication_settings_description": "Halda parooli, OAuth ja muid autentimise seadeid", + "authentication_settings_description": "Halda parooli, OAuth'i ja muid autentimise seadeid", "authentication_settings_disable_all": "Kas oled kindel, et soovid kõik sisselogimismeetodid välja lülitada? Sisselogimine lülitatakse täielikult välja.", "authentication_settings_reenable": "Et taas lubada, kasuta serveri käsku.", "background_task_job": "Tausttegumid", @@ -47,26 +47,26 @@ "backup_settings": "Andmebaasi tõmmiste seaded", "backup_settings_description": "Halda andmebaasi tõmmiste seadeid.", "cleared_jobs": "Tööted eemaldatud: {job}", - "config_set_by_file": "Konfiguratsioon on määratud konfifaili abil", + "config_set_by_file": "Konfiguratsioon on määratud konfiguratsioonifaili abil", "confirm_delete_library": "Kas oled kindel, et soovid kustutada {library} kogu?", - "confirm_delete_library_assets": "Kas oled kindel, et soovid selle kogu kustutada? Sellega kustutatakse {count, plural, one {# sisalduv üksus} other {kõik # sisalduvat üksust}} Immich'ist ning seda ei saa tagasi võtta. Failid jäävad kettale alles.", + "confirm_delete_library_assets": "Kas oled kindel, et soovid selle kogu kustutada? Sellega kustutatakse {count, plural, one {# sisalduv üksus} other {kõik # sisalduvat üksust}} Immich'ist ning seda toimingut ei saa tagasi võtta. Failid jäävad kettale alles.", "confirm_email_below": "Kinnitamiseks sisesta allpool \"{email}\"", "confirm_reprocess_all_faces": "Kas oled kindel, et soovid kõik näod uuesti töödelda? See eemaldab kõik nimega isikud.", "confirm_user_password_reset": "Kas oled kindel, et soovid kasutaja {user} parooli lähtestada?", "confirm_user_pin_code_reset": "Kas oled kindel, et soovid kasutaja {user} PIN-koodi lähtestada?", "create_job": "Lisa tööde", "cron_expression": "Cron avaldis", - "cron_expression_description": "Sea skaneerimise intervall cron formaadis. Rohkema info jaoks vaata nt. Crontab Guru", + "cron_expression_description": "Määra skaneerimise intervall cron formaadis. Rohkema info jaoks vaata nt. Crontab Guru", "cron_expression_presets": "Eelseadistatud cron avaldised", "disable_login": "Keela sisselogimine", "duplicate_detection_job_description": "Rakenda üksustele masinõpet, et leida sarnaseid pilte. Kasutab nutiotsingut", - "exclusion_pattern_description": "Välistamismustrid võimaldavad ignoreerida faile ja kaustu kogu skaneerimisel. See on kasulik, kui sul on kaustu, mis sisaldavad faile, mida sa ei soovi importida, nagu RAW failid.", + "exclusion_pattern_description": "Välistamismustrid võimaldavad ignoreerida faile ja kaustu selle kogu skaneerimisel. See on kasulik, kui sul on kaustu, mis sisaldavad faile, mida sa ei soovi importida, nagu RAW failid.", "external_library_management": "Väliste kogude haldus", "face_detection": "Näoavastus", - "face_detection_description": "Avasta üksustest nägusid masinõppe abil. Videote puhul kasutatakse ainult pisipilti. \"Värskenda\" töötleb kõik üksused uuesti. \"Lähtesta\" kustutab lisaks kõik seni leitud näed. \"Puuduvad\" võtab ette üksused, mida pole veel töödeldud. Avastatud näod suunatakse näotuvastusse, et grupeerida nad olemasolevateks või uuteks isikuteks.", + "face_detection_description": "Avasta üksustest nägusid masinõppe abil. Videote puhul kasutatakse ainult pisipilti. \"Värskenda\" töötleb kõik üksused uuesti. \"Lähtesta\" kustutab lisaks kõik seni leitud näod. \"Puuduvad\" võtab ette üksused, mida pole veel töödeldud. Avastatud näod suunatakse näotuvastusse, et grupeerida nad olemasolevateks või uuteks isikuteks.", "facial_recognition_job_description": "Grupeeri avastatud näod inimesteks. See samm käivitub siis, kui näoavastus on lõppenud. \"Lähtesta\" grupeerib kõik näod uuesti. \"Puuduvad\" võtab ette näod, mida pole isikuga seostatud.", "failed_job_command": "Käsk {command} ebaõnnestus töötes: {job}", - "force_delete_user_warning": "HOIATUS: See kustutab koheselt kasutaja ja kõik üksused. Seda ei saa tagasi võtta ja faile ei saa taastada.", + "force_delete_user_warning": "HOIATUS: See kustutab koheselt kasutaja ja kõik tema üksused. Toimingut ei saa tagasi võtta ja faile ei saa taastada.", "image_format": "Formaat", "image_format_description": "WebP failid on väiksemad kui JPEG, aga kodeerimine on aeglasem.", "image_fullsize_description": "Täismõõdus pilt ilma metaandmeteta, kasutatakse sisse suumimisel", @@ -77,9 +77,9 @@ "image_prefer_embedded_preview": "Eelista manustatud eelvaadet", "image_prefer_embedded_preview_setting_description": "Kasuta pilditöötluse sisendina võimalusel RAW fotodesse manustatud eelvaateid. See võib mõnede piltide puhul anda tulemuseks täpsemad värvid, aga eelvaate kvaliteet sõltub konkreetsest kaamerast ning pildis võib olla rohkem tihendusmüra.", "image_prefer_wide_gamut": "Eelista laia värvigammat", - "image_prefer_wide_gamut_setting_description": "Kasuta pisipiltide jaoks Display P3. See säilitab paremini laia värviruumiga piltide erksuse, aga vanematel seadmetel ja vanemate brauseritega võivad pildid teistsugused välja näha. sRGB pildid säilitatakse värvinihete vältimiseks.", + "image_prefer_wide_gamut_setting_description": "Kasuta pisipiltide jaoks Display P3. See säilitab paremini laia värviruumiga piltide erksuse, kuid vanematel seadmetel ja vanemate brauseritega võivad pildid teistsugused välja näha. sRGB pildid säilitatakse värvinihete vältimiseks.", "image_preview_description": "Keskmise suurusega pilt ilma metaandmeteta, kasutusel üksiku üksuse vaatamise ja masinõppe jaoks", - "image_preview_quality_description": "Eelvaate kvaliteet vahemikus 1-100. Kõrgem väärtus on parem, aga tekitab suuremaid faile ning võib mõjutada rakenduse töökiirust. Madala väärtuse seadmine võib mõjutada masinõppe kvaliteeti.", + "image_preview_quality_description": "Eelvaate kvaliteet vahemikus 1-100. Kõrgem väärtus on parem, aga tekitab suuremaid faile ning võib mõjutada rakenduse töökiirust. Madal väärtus võib mõjutada masinõppe kvaliteeti.", "image_preview_title": "Eelvaate seaded", "image_quality": "Kvaliteet", "image_resolution": "Resolutsioon", @@ -92,7 +92,7 @@ "job_concurrency": "{job} samaaegsus", "job_created": "Tööde lisatud", "job_not_concurrency_safe": "Seda töödet pole ohutu samaaegselt käivitada.", - "job_settings": "Tööte seaded", + "job_settings": "Töödete seaded", "job_settings_description": "Halda töödete samaaegsust", "job_status": "Tööte seisund", "jobs_delayed": "{jobCount, plural, other {# edasi lükatud}}", @@ -166,6 +166,20 @@ "metadata_settings_description": "Halda metaandmete seadeid", "migration_job": "Migratsioon", "migration_job_description": "Migreeri üksuste ja nägude pisipildid uusimale kaustastruktuurile", + "nightly_tasks_cluster_faces_setting_description": "Käivita värskelt avastatud nägudel näotuvastus", + "nightly_tasks_cluster_new_faces_setting": "Grupeeri uued näod", + "nightly_tasks_database_cleanup_setting": "Andmebaasi puhastuse tegumid", + "nightly_tasks_database_cleanup_setting_description": "Eemalda andmebaasist vanad, aegunud andmed", + "nightly_tasks_generate_memories_setting": "Genereeri mälestused", + "nightly_tasks_generate_memories_setting_description": "Loo üksustest uued mälestused", + "nightly_tasks_missing_thumbnails_setting": "Genereeri puuduvad pisipildid", + "nightly_tasks_missing_thumbnails_setting_description": "Suuna ilma pisipiltideta üksused pisipiltide genereerimisele", + "nightly_tasks_settings": "Öiste tegumite seaded", + "nightly_tasks_settings_description": "Halda öiseid tegumeid", + "nightly_tasks_start_time_setting": "Algusaeg", + "nightly_tasks_start_time_setting_description": "Aeg, millal server alustab öiste tegumite käivitamist", + "nightly_tasks_sync_quota_usage_setting": "Sünkrooni kvoodikasutus", + "nightly_tasks_sync_quota_usage_setting_description": "Uuenda kasutaja talletuskvoot jooksva kasutuse alusel", "no_paths_added": "Ühtegi teed pole", "no_pattern_added": "Mustreid ei ole", "note_apply_storage_label_previous_assets": "Märkus: Et rakendada talletussilt varem üleslaaditud üksustele, käivita", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "Mobiilne ümbersuunamise URI", "oauth_mobile_redirect_uri_override": "Mobiilse ümbersuunamise URI ülekirjutamine", "oauth_mobile_redirect_uri_override_description": "Lülita sisse, kui OAuth pakkuja ei luba mobiilset URI-d, näiteks ''{callback}''", + "oauth_role_claim": "Rolli väide", + "oauth_role_claim_description": "Anna selle väite olemasolul automaatselt administraatori ligipääs. Väite väärtus võib olla 'user' või 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Halda OAuth sisselogimise seadeid", "oauth_settings_more_details": "Selle funktsiooni kohta rohkem teada saamiseks loe dokumentatsiooni.", @@ -357,10 +373,12 @@ "admin_password": "Administraatori parool", "administration": "Administratsioon", "advanced": "Täpsemad valikud", + "advanced_settings_beta_timeline_subtitle": "Koge uut rakendust", + "advanced_settings_beta_timeline_title": "Beeta ajajoon", "advanced_settings_enable_alternate_media_filter_subtitle": "Kasuta seda valikut, et filtreerida sünkroonimise ajal üksuseid alternatiivsete kriteeriumite alusel. Proovi seda ainult siis, kui rakendusel on probleeme kõigi albumite tuvastamisega.", "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTAALNE] Kasuta alternatiivset seadme albumi sünkroonimise filtrit", "advanced_settings_log_level_title": "Logimistase: {level}", - "advanced_settings_prefer_remote_subtitle": "Mõned seadmed laadivad seadmes olevate üksuste pisipilte piinavalt aeglaselt. Aktiveeri see seadistus, et laadida selle asemel kaugpilte.", + "advanced_settings_prefer_remote_subtitle": "Mõned seadmed laadivad lokaalsete üksuste pisipilte piinavalt aeglaselt. Aktiveeri see seadistus, et laadida selle asemel kaugpilte.", "advanced_settings_prefer_remote_title": "Eelista kaugpilte", "advanced_settings_proxy_headers_subtitle": "Määra vaheserveri päised, mida Immich peaks iga päringuga saatma", "advanced_settings_proxy_headers_title": "Vaheserveri päised", @@ -388,6 +406,7 @@ "album_options": "Albumi valikud", "album_remove_user": "Eemalda kasutaja?", "album_remove_user_confirmation": "Kas oled kindel, et soovid kasutaja {user} eemaldada?", + "album_search_not_found": "Otsingule vastavaid albumeid ei leitud", "album_share_no_users": "Paistab, et oled seda albumit kõikide kasutajatega jaganud, või pole ühtegi kasutajat, kellega jagada.", "album_updated": "Album muudetud", "album_updated_setting_description": "Saa teavitus e-posti teel, kui jagatud albumis on uusi üksuseid", @@ -407,6 +426,7 @@ "albums_default_sort_order": "Vaikimisi albumi järjestus", "albums_default_sort_order_description": "Uute albumite lisamisel üksuste esialgne järjekord.", "albums_feature_description": "Üksuste kollektsioonid, mida saab teiste kasutajatega jagada.", + "albums_on_device_count": "Albumid seadmel ({count})", "all": "Kõik", "all_albums": "Kõik albumid", "all_people": "Kõik isikud", @@ -427,6 +447,7 @@ "app_settings": "Rakenduse seaded", "appears_in": "Albumid", "archive": "Arhiiv", + "archive_action_prompt": "{count} lisatud arhiivi", "archive_or_unarchive_photo": "Arhiveeri või taasta foto", "archive_page_no_archived_assets": "Arhiveeritud üksuseid ei leitud", "archive_page_title": "Arhiveeri ({count})", @@ -464,7 +485,6 @@ "assets": "Üksused", "assets_added_count": "{count, plural, one {# üksus} other {# üksust}} lisatud", "assets_added_to_album_count": "{count, plural, one {# üksus} other {# üksust}} albumisse lisatud", - "assets_added_to_name_count": "{count, plural, one {# üksus} other {# üksust}} lisatud {hasName, select, true {albumisse {name}} other {uude albumisse}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Üksust} other {Üksuseid}} ei saa albumisse lisada", "assets_count": "{count, plural, one {# üksus} other {# üksust}}", "assets_deleted_permanently": "{count} üksus(t) jäädavalt kustutatud", @@ -587,6 +607,7 @@ "cancel": "Katkesta", "cancel_search": "Katkesta otsing", "canceled": "Tühistatud", + "canceling": "Tühistamine", "cannot_merge_people": "Ei saa isikuid ühendada", "cannot_undo_this_action": "Sa ei saa seda tagasi võtta!", "cannot_update_the_description": "Kirjelduse muutmine ebaõnnestus", @@ -703,7 +724,7 @@ "daily_title_text_date": "d. MMMM", "daily_title_text_date_year": "d. MMMM yyyy", "dark": "Tume", - "darkTheme": "Lülita tume teema", + "dark_theme": "Lülita tume teema", "date_after": "Kuupäev pärast", "date_and_time": "Kuupäev ja kellaaeg", "date_before": "Kuupäev enne", @@ -719,6 +740,7 @@ "default_locale": "Vaikimisi lokaat", "default_locale_description": "Vorminda kuupäevad ja numbrid vastavalt brauseri lokaadile", "delete": "Kustuta", + "delete_action_prompt": "{count} jäädavalt kustutatud", "delete_album": "Kustuta album", "delete_api_key_prompt": "Kas oled kindel, et soovid selle API võtme kustutada?", "delete_dialog_alert": "Need üksused kustutatakse jäädavalt Immich'ist ja sinu seadmest", @@ -732,6 +754,7 @@ "delete_key": "Kustuta võti", "delete_library": "Kustuta kogu", "delete_link": "Kustuta link", + "delete_local_action_prompt": "{count} kustutatud lokaalselt", "delete_local_dialog_ok_backed_up_only": "Kustuta ainult varundatud", "delete_local_dialog_ok_force": "Kustuta sellegipoolest", "delete_others": "Kustuta teised", @@ -745,6 +768,7 @@ "description": "Kirjeldus", "description_input_hint_text": "Lisa kirjeldus...", "description_input_submit_error": "Viga kirjelduse muutmisel, rohkem infot leiad logist", + "deselect_all": "Eemalda kõik valikust", "details": "Üksikasjad", "direction": "Suund", "disabled": "Välja lülitatud", @@ -762,6 +786,7 @@ "documentation": "Dokumentatsioon", "done": "Tehtud", "download": "Laadi alla", + "download_action_prompt": "{count} üksust laaditakse alla", "download_canceled": "Allalaadimine katkestatud", "download_complete": "Allalaadimine lõpetatud", "download_enqueue": "Allalaadimine ootel", @@ -799,6 +824,7 @@ "edit_key": "Muuda võtit", "edit_link": "Muuda linki", "edit_location": "Muuda asukohta", + "edit_location_action_prompt": "{count} asukoht muudetud", "edit_location_dialog_title": "Asukoht", "edit_name": "Muuda nime", "edit_people": "Muuda isikuid", @@ -817,6 +843,7 @@ "empty_trash": "Tühjenda prügikast", "empty_trash_confirmation": "Kas oled kindel, et soovid prügikasti tühjendada? See eemaldab kõik seal olevad üksused Immich'ist jäädavalt.\nSeda tegevust ei saa tagasi võtta!", "enable": "Luba", + "enable_backup": "Luba varundus", "enable_biometric_auth_description": "Biomeetrilise autentimise lubamiseks sisesta oma PIN-kood", "enabled": "Lubatud", "end_date": "Lõppkuupäev", @@ -984,6 +1011,7 @@ "failed_to_load_assets": "Üksuste laadimine ebaõnnestus", "failed_to_load_folder": "Kausta laadimine ebaõnnestus", "favorite": "Lemmik", + "favorite_action_prompt": "{count} lisatud lemmikutesse", "favorite_or_unfavorite_photo": "Lisa foto lemmikutesse või eemalda lemmikutest", "favorites": "Lemmikud", "favorites_page_no_favorites": "Lemmikuid üksuseid ei leitud", @@ -1099,7 +1127,7 @@ "ios_debug_info_no_processes_queued": "Taustaprotsesse pole järjekorras", "ios_debug_info_no_sync_yet": "Taustal sünkroonimise tööde pole veel käinud", "ios_debug_info_processes_queued": "{count, plural, one {{count} taustaprotsess järjekorras} other {{count} taustaprotsessi järjekorras}}", - "ios_debug_info_processing_ran_at": "Töötlemine käis {dateTime}", + "ios_debug_info_processing_ran_at": "Töötlemine toimus {dateTime}", "items_count": "{count, plural, one {# üksus} other {# üksust}}", "jobs": "Tööted", "keep": "Jäta alles", @@ -1127,6 +1155,7 @@ "library_page_sort_created": "Loomise aeg", "library_page_sort_last_modified": "Viimase muutmise aeg", "library_page_sort_title": "Albumi pealkiri", + "licenses": "Litsentsid", "light": "Hele", "like_deleted": "Meeldimine kustutatud", "link_motion_video": "Lingi liikuv video", @@ -1246,6 +1275,7 @@ "more": "Rohkem", "move": "Liiguta", "move_off_locked_folder": "Liiguta lukustatud kaustast välja", + "move_to_lock_folder_action_prompt": "{count} lisatud lukustatud kausta", "move_to_locked_folder": "Liiguta lukustatud kausta", "move_to_locked_folder_confirmation": "Need fotod ja videod eemaldatakse kõigist albumitest ning nad on nähtavad ainult lukustatud kaustas", "moved_to_archive": "{count, plural, one {# üksus} other {# üksust}} liigutatud arhiivi", @@ -1460,6 +1490,7 @@ "purchase_server_description_2": "Toetaja staatus", "purchase_server_title": "Server", "purchase_settings_server_activated": "Serveri tootevõtit haldab administraator", + "queue_status": "Järjekorras {count}/{total}", "rating": "Hinnang", "rating_clear": "Tühjenda hinnang", "rating_count": "{count, plural, one {# tärn} other {# tärni}}", @@ -1495,7 +1526,9 @@ "remove_custom_date_range": "Eemalda kohandatud kuupäevavahemik", "remove_deleted_assets": "Eemalda kustutatud üksused", "remove_from_album": "Eemalda albumist", + "remove_from_album_action_prompt": "{count} eemaldatud albumist", "remove_from_favorites": "Eemalda lemmikutest", + "remove_from_lock_folder_action_prompt": "{count} eemaldatud lukustatud kaustast", "remove_from_locked_folder": "Eemalda lukustatud kaustast", "remove_from_locked_folder_confirmation": "Kas oled kindel, et soovid need fotod ja videod lukustatud kaustast välja liigutada? Need muutuvad su kogus nähtavaks.", "remove_from_shared_link": "Eemalda jagatud lingist", @@ -1667,6 +1700,7 @@ "settings_saved": "Seaded salvestatud", "setup_pin_code": "Seadista PIN-kood", "share": "Jaga", + "share_action_prompt": "Jagatud {count} üksust", "share_add_photos": "Lisa fotosid", "share_assets_selected": "{count} valitud", "share_dialog_preparing": "Ettevalmistamine...", @@ -1768,6 +1802,7 @@ "sort_title": "Pealkiri", "source": "Lähtekood", "stack": "Virnasta", + "stack_action_prompt": "{count} virnastatud", "stack_duplicates": "Virnasta duplikaadid", "stack_select_one_photo": "Vali virnale kaanefoto", "stack_selected_photos": "Virnasta valitud fotod", @@ -1838,6 +1873,7 @@ "total": "Kokku", "total_usage": "Kogukasutus", "trash": "Prügikast", + "trash_action_prompt": "{count} liigutatud prügikasti", "trash_all": "Kõik prügikasti", "trash_count": "Liiguta {count, number} prügikasti", "trash_delete_asset": "Kustuta üksus", @@ -1855,9 +1891,11 @@ "unable_to_change_pin_code": "PIN-koodi muutmine ebaõnnestus", "unable_to_setup_pin_code": "PIN-koodi seadistamine ebaõnnestus", "unarchive": "Taasta arhiivist", + "unarchive_action_prompt": "{count} eemaldatud arhiivist", "unarchived_count": "{count, plural, other {# arhiivist taastatud}}", "undo": "Võta tagasi", "unfavorite": "Eemalda lemmikutest", + "unfavorite_action_prompt": "{count} eemaldatud lemmikutest", "unhide_person": "Ära peida isikut", "unknown": "Teadmata", "unknown_country": "Tundmatu riik", @@ -1875,12 +1913,15 @@ "unselect_all_duplicates": "Ära vali duplikaate", "unselect_all_in": "Ära vali ühtegi grupis {group}", "unstack": "Eralda", + "unstack_action_prompt": "{count} eraldatud", "unstacked_assets_count": "{count, plural, one {# üksus} other {# üksust}} eraldatud", + "untagged": "Sildistamata", "up_next": "Järgmine", "updated_at": "Uuendatud", "updated_password": "Parool muudetud", "upload": "Laadi üles", "upload_concurrency": "Üleslaadimise samaaegsus", + "upload_details": "Üleslaadimise üksikasjad", "upload_dialog_info": "Kas soovid valitud üksuse(d) serverisse varundada?", "upload_dialog_title": "Üksuse üleslaadimine", "upload_errors": "Üleslaadimine lõpetatud {count, plural, one {# veaga} other {# veaga}}, uute üksuste nägemiseks värskenda lehte.", @@ -1912,6 +1953,7 @@ "user_usage_stats_description": "Vaata konto kasutuse statistikat", "username": "Kasutajanimi", "users": "Kasutajad", + "users_added_to_album_count": "{count, plural, one {# kasutaja} other {# kasutajat}} lisatud albumisse", "utilities": "Tööriistad", "validate": "Valideeri", "validate_endpoint_error": "Sisesta korrektne URL", @@ -1930,6 +1972,7 @@ "view_album": "Vaata albumit", "view_all": "Vaata kõiki", "view_all_users": "Vaata kõiki kasutajaid", + "view_details": "Vaata üksikasju", "view_in_timeline": "Vaata ajajoonel", "view_link": "Vaata linki", "view_links": "Vaata linke", diff --git a/i18n/fi.json b/i18n/fi.json index f41d0cf631..4e12253cf2 100644 --- a/i18n/fi.json +++ b/i18n/fi.json @@ -166,6 +166,20 @@ "metadata_settings_description": "Hallitse metatietoja", "migration_job": "Migraatio", "migration_job_description": "Migroi aineiston pikkukuvat ja kasvot uusimpaan kansiorakenteeseen", + "nightly_tasks_cluster_faces_setting_description": "Aja kasvojen tunnistus uusiin tunnistettuihin kasvoihin", + "nightly_tasks_cluster_new_faces_setting": "Kokoa uudet kasvot", + "nightly_tasks_database_cleanup_setting": "Tietokannan puhdistuksen tehtävät", + "nightly_tasks_database_cleanup_setting_description": "Siivoa vanhentunut data tietokannasta", + "nightly_tasks_generate_memories_setting": "Luo muistoja", + "nightly_tasks_generate_memories_setting_description": "Luo kohteista uusia muistoja", + "nightly_tasks_missing_thumbnails_setting": "Luo puuttuvat pikkukuvat", + "nightly_tasks_missing_thumbnails_setting_description": "Laita ilman pikkukuvia olevat kohteet jonoon pikkukuvien luontia varten", + "nightly_tasks_settings": "Yöllisten tehtävien asetukset", + "nightly_tasks_settings_description": "Hallitse yöllisiä tehtäviä", + "nightly_tasks_start_time_setting": "Aloitusaika", + "nightly_tasks_start_time_setting_description": "Aika jolloin palvelin aloittaa yöllisten tehtävien ajon", + "nightly_tasks_sync_quota_usage_setting": "Synkronointikiintiön käyttö", + "nightly_tasks_sync_quota_usage_setting_description": "Päivitä käyttäjän tallennustilan kiintiö nykyisen käytön mukaan", "no_paths_added": "Polkuja ei asetettu", "no_pattern_added": "Kaavoja ei lisättynä", "note_apply_storage_label_previous_assets": "Huom: Asettaaksesi nimikkeen aiemmin ladatulle aineistolle, aja", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "Mobiilin uudellenohjaus-URI", "oauth_mobile_redirect_uri_override": "Ohita mobiilin uudelleenohjaus-URI", "oauth_mobile_redirect_uri_override_description": "Ota käyttöön kun OAuth tarjoaja ei salli mobiili URI:a, kuten ''{callback}''", + "oauth_role_claim": "Roolin vaatimus", + "oauth_role_claim_description": "Salli pääkäyttäjän pääsyoikeus automaattisesti tämän vaatimuksen perusteella. Vaatimus voi sisältää, joko 'käyttäjän' tai 'pääkäyttäjän'.", "oauth_settings": "OAuth", "oauth_settings_description": "Hallitse OAuth-kirjautumisen asetuksia", "oauth_settings_more_details": "Saadaksesi lisätietoja tästä toiminnosta, katso dokumentaatio.", @@ -244,6 +260,7 @@ "storage_template_migration_info": "Tallennusmalli muuntaa kaikki tiedostopäätteet pieniksi kirjaimiksi. Mallipohjan muutokset koskevat vain uusia resursseja. Jos haluat käyttää mallipohjaa takautuvasti aiemmin ladattuihin resursseihin, suorita {job}.", "storage_template_migration_job": "Tallennustilan mallin muutostyö", "storage_template_more_details": "Saadaksesi lisätietoa tästä ominaisuudesta, katso Tallennustilan Mallit sekä mihin se vaikuttaa", + "storage_template_onboarding_description_v2": "Päälle kytkettynä, toiminto järjestestelee tiedostot automaattisesti käyttäjän määrittämän mallin mukaisesti. Lisätietoja dokumentaatiosta..", "storage_template_path_length": "Arvioitu tiedostopolun pituusrajoitus: {length, number}/{limit, number}", "storage_template_settings": "Tallennustilan malli", "storage_template_settings_description": "Hallitse palvelimelle ladatun aineiston kansiorakennetta ja tiedostonimiä", @@ -403,6 +420,9 @@ "album_with_link_access": "Anna kenen tahansa nähdä linkin kautta tämän albumin valokuvat ja henkilöt.", "albums": "Albumit", "albums_count": "{count, plural, one {{count, number} albumi} other {{count, number} albumia}}", + "albums_default_sort_order": "Albumin oletuslajittelujärjestys", + "albums_default_sort_order_description": "Kohteiden ensisijainen lajittelujärjestys uusia albumeja luotaessa.", + "albums_feature_description": "Kokoelma kohteita, jotka voidaan jakaa muille käyttäjille.", "all": "Kaikki", "all_albums": "Kaikki albumit", "all_people": "Kaikki henkilöt", @@ -423,6 +443,7 @@ "app_settings": "Sovellusasetukset", "appears_in": "Esiintyy albumeissa", "archive": "Arkisto", + "archive_action_prompt": "{count} lisätty arkistoon", "archive_or_unarchive_photo": "Arkistoi kuva tai palauta arkistosta", "archive_page_no_archived_assets": "Arkistoituja kohteita ei löytynyt", "archive_page_title": "Arkisto ({count})", @@ -460,10 +481,12 @@ "assets": "Kohteet", "assets_added_count": "Lisätty {count, plural, one {# kohde} other {# kohdetta}}", "assets_added_to_album_count": "Albumiin lisätty {count, plural, one {# kohde} other {# kohdetta}}", - "assets_added_to_name_count": "Lisätty {count, plural, one {# kohde} other {# kohdetta}} {hasName, select, true {{name}} other {uuteen albumiin}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Kohdetta} other {Kohdetta}} ei voida lisätä albumiin", "assets_count": "{count, plural, one {# media} other {# mediaa}}", "assets_deleted_permanently": "{count} kohdetta poistettu pysyvästi", "assets_deleted_permanently_from_server": "{count} objektia poistettu pysyvästi Immich-palvelimelta", + "assets_downloaded_failed": "{count, plural, one {Ladattu # tiedosto - {error} tiedosto epäonnistui} other {ladattu # tiedostoa - {error} tiedostot epäonnistuivat}}", + "assets_downloaded_successfully": "{count, plural, one {Ladattu # tiedosto onnistuneesti} other {Ladattu # tiedostoa onnistuneesti}}", "assets_moved_to_trash_count": "Siirretty {count, plural, one {# media} other {# mediaa}} roskakoriin", "assets_permanently_deleted_count": "{count, plural, one {# media} other {# mediaa}} poistettu pysyvästi", "assets_removed_count": "{count, plural, one {# media} other {# mediaa}} poistettu", @@ -478,6 +501,7 @@ "authorized_devices": "Valtuutetut laitteet", "automatic_endpoint_switching_subtitle": "Yhdistä paikallisesti nimetyn Wi-Fi-yhteyden kautta, kun se on saatavilla, ja käytä vaihtoehtoisia yhteyksiä muualla", "automatic_endpoint_switching_title": "Automaattinen URL-osoitteen vaihto", + "autoplay_slideshow": "Toista diaesitys automaattisesti", "back": "Takaisin", "back_close_deselect": "Palaa, sulje tai poista valinnat", "background_location_permission": "Taustasijainnin käyttöoikeus", @@ -582,7 +606,8 @@ "cannot_merge_people": "Ihmisiä ei voitu yhdistää", "cannot_undo_this_action": "Et voi perua tätä toimintoa!", "cannot_update_the_description": "Kuvausta ei voi päivittää", - "cast": "Lähettää", + "cast": "Suoratoisto", + "cast_description": "Määritä saatavilla olevat suoratoistopalvelut", "change_date": "Vaihda päiväys", "change_description": "Muuta kuvausta", "change_display_order": "Muuta näyttöjärjestystä", @@ -641,6 +666,7 @@ "confirm_password": "Vahvista salasana", "confirm_tag_face": "Haluatko merkitä nämä kasvot nimellä {name}?", "confirm_tag_face_unnamed": "Merkitäänkö nämä kasvot?", + "connected_device": "Yhdistetty laite", "connected_to": "Yhdistetty", "contain": "Mahduta", "context": "Konteksti", @@ -693,6 +719,7 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Tumma", + "dark_theme": "Vaihda tumma teema", "date_after": "Päivämäärän jälkeen", "date_and_time": "Päivämäärä ja aika", "date_before": "Päivä ennen", @@ -708,6 +735,7 @@ "default_locale": "Oletuskieliasetus", "default_locale_description": "Muotoile päivämäärät ja numerot selaimesi kielen mukaan", "delete": "Poista", + "delete_action_prompt": "{count} poistettu pysyvästi", "delete_album": "Poista albumi", "delete_api_key_prompt": "Haluatko varmasti poistaa tämän API-avaimen?", "delete_dialog_alert": "Nämä kohteet poistetaan pysyvästi Immich:stä ja laitteeltasi", @@ -740,12 +768,13 @@ "disallow_edits": "Älä salli muokkauksia", "discord": "Discord", "discover": "Tutki", + "discovered_devices": "Löydetyt laitteet", "dismiss_all_errors": "Sivuuta kaikki virheet", "dismiss_error": "Sivuuta virhe", "display_options": "Näyttöasetukset", "display_order": "Näyttöjärjestys", "display_original_photos": "Näytä alkuperäiset kuvat", - "display_original_photos_setting_description": "Näytä mieluiten alkuperäinen kuva peukalokuvan sijasta kun alkuperäinen aineisto on web-yhteensopiva. Tämä voi aiheuttaa kuvien näyttämisen hitautta.", + "display_original_photos_setting_description": "Näytä mieluiten alkuperäinen kuva esikatselukuvan sijasta, kun alkuperäinen kuva on web-yhteensopiva. Tämä voi aiheuttaa kuvien näyttämisen hitautta.", "do_not_show_again": "Älä näytä tätä enää", "documentation": "Dokumentaatio", "done": "Valmis", @@ -787,6 +816,7 @@ "edit_key": "Muokkaa avainta", "edit_link": "Muokkaa linkkiä", "edit_location": "Muokkaa sijaintia", + "edit_location_action_prompt": "{count} sijaintia muokattu", "edit_location_dialog_title": "Sijainti", "edit_name": "Muokkaa nimeä", "edit_people": "Muokkaa henkilöitä", @@ -972,6 +1002,7 @@ "failed_to_load_assets": "Kohteiden lataus epäonnistui", "failed_to_load_folder": "Kansion lataaminen epäonnistui", "favorite": "Suosikki", + "favorite_action_prompt": "{count} lisätty suosikkeihin", "favorite_or_unfavorite_photo": "Suosikki- tai ei-suosikkikuva", "favorites": "Suosikit", "favorites_page_no_favorites": "Suosikkikohteita ei löytynyt", @@ -1086,6 +1117,8 @@ "ios_debug_info_last_sync_at": "Viimeisin synkronisointi {dateTime}", "ios_debug_info_no_processes_queued": "Ei taustaprosesseja jonossa", "ios_debug_info_no_sync_yet": "Taustasynkronisointia ei ole suoritettu vielä", + "ios_debug_info_processes_queued": "{count, plural, one {{count} taustaprosessi jonossa} other {{count} taustaprosessia jonossa}}", + "ios_debug_info_processing_ran_at": "Prosessi valmistui {dateTime}", "items_count": "{count, plural, one {# kpl} other {# kpl}}", "jobs": "Taustatehtävät", "keep": "Säilytä", @@ -1094,6 +1127,9 @@ "kept_this_deleted_others": "Tämä kohde säilytettiin. {count, plural, one {# asset} other {# assets}} poistettiin", "keyboard_shortcuts": "Pikanäppäimet", "language": "Kieli", + "language_no_results_subtitle": "Yritä säätää hakuehtoja", + "language_no_results_title": "Kieliä ei löydetty", + "language_search_hint": "Etsi kieliä...", "language_setting_description": "Valitse suosimasi kieli", "last_seen": "Viimeksi nähty", "latest_version": "Viimeisin versio", @@ -1119,6 +1155,7 @@ "list": "Lista", "loading": "Ladataan", "loading_search_results_failed": "Hakutulosten lataaminen epäonnistui", + "local_asset_cast_failed": "Kohdetta, joka ei ole ladattuna palvelimelle, ei voida striimata", "local_network": "Lähiverkko", "local_network_sheet_info": "Sovellus muodostaa yhteyden palvelimeen tämän URL-osoitteen kautta, kun käytetään määritettyä Wi-Fi-verkkoa", "location_permission": "Sijainnin käyttöoikeus", @@ -1132,6 +1169,7 @@ "locked_folder": "Lukittu kansio", "log_out": "Kirjaudu ulos", "log_out_all_devices": "Kirjaudu ulos kaikilta laitteilta", + "logged_in_as": "Kirjautunut käyttäjänä {user}", "logged_out_all_devices": "Kaikki laitteet kirjattu ulos", "logged_out_device": "Laite kirjattu ulos", "login": "Kirjaudu", @@ -1220,13 +1258,14 @@ "merged_people_count": "{count, plural, one {# Henkilö} other {# henkilöä}} yhdistetty", "minimize": "PIenennä", "minute": "Minuutti", - "missing": "Puuttuu", + "missing": "Puuttuvat", "model": "Malli", "month": "Kuukauden mukaan", "monthly_title_text_date_format": "MMMM y", "more": "Enemmän", "move": "Siirrä", "move_off_locked_folder": "Siirrä pois lukitusta kansiosta", + "move_to_lock_folder_action_prompt": "{count} lisätty lukittuun kansioon", "move_to_locked_folder": "Siirrä lukittuun kansioon", "move_to_locked_folder_confirmation": "Nämä kuvat ja videot poistetaan kaikista albumeista, ja ne ovat nähtävissä vain lukitussa kansiossa", "moved_to_archive": "Siirretty {count, plural, one {# kohde} other {# kohdetta}} arkistoon", @@ -1259,6 +1298,7 @@ "no_archived_assets_message": "Arkistoi kuvia ja videoita piilottaaksesi ne kuvat näkymästä", "no_assets_message": "NAPAUTA LATAAKSESI ENSIMMÄISEN KUVASI", "no_assets_to_show": "Ei näytettäviä kohteita", + "no_cast_devices_found": "Cast-laitteita ei löytynyt", "no_duplicates_found": "Kaksoiskappaleita ei löytynyt.", "no_exif_info_available": "EXIF-tietoa ei saatavilla", "no_explore_results_message": "Lataa lisää kuvia tutkiaksesi kokoelmaasi.", @@ -1291,8 +1331,11 @@ "oldest_first": "Vanhin ensin", "on_this_device": "Laitteella", "onboarding": "Käyttöönotto", - "onboarding_privacy_description": "Seuraavat (valinnaiset) ominaisuudet perustuvat ulkoisiin palveluihin, ja ne voidaan poistaa käytöstä milloin tahansa hallinta asetuksista.", + "onboarding_locale_description": "Valitse haluamasi kieli. Voit muuttaa kieliasetuksia myöhemmin asetuksista.", + "onboarding_privacy_description": "Seuraavat (valinnaiset) ominaisuudet perustuvat ulkoisiin palveluihin ja ne voidaan milloin tahansa poistaa käytöstä asetuksista.", + "onboarding_server_welcome_description": "Määritellään seuraavaksi järjestelmäsi muutamalla yleisellä asetuksella.", "onboarding_theme_description": "Valitse väriteema istunnollesi. Voit muuttaa tämän myöhemmin asetuksistasi.", + "onboarding_user_welcome_description": "Aloitetaan!", "onboarding_welcome_user": "Tervetuloa {user}", "online": "Online", "only_favorites": "Vain suosikit", @@ -1392,7 +1435,7 @@ "previous_or_next_photo": "Kuva seuraava/edellinen", "previous_or_next_year": "Vuosi seuraava/edellinen", "primary": "Ensisijainen", - "privacy": "Yksityisyys", + "privacy": "Tietosuoja", "profile": "Profiili", "profile_drawer_app_logs": "Lokit", "profile_drawer_client_out_of_date_major": "Sovelluksen mobiiliversio on vanhentunut. Päivitä viimeisimpään merkittävään versioon.", @@ -1472,12 +1515,15 @@ "remove_custom_date_range": "Poista aikaväliltä", "remove_deleted_assets": "Poista Offline-tiedostot", "remove_from_album": "Poista albumista", + "remove_from_album_action_prompt": "{count} poistettu albumista", "remove_from_favorites": "Poista suosikeista", + "remove_from_lock_folder_action_prompt": "{count} poistettu lukitusta albumista", "remove_from_locked_folder": "Poista lukitusta kansiosta", "remove_from_locked_folder_confirmation": "Haluatko varmasti siirtää nämä kuvat ja videot pois lukitusta kansiosta? Ne näkyvät sen jälkeen kirjastossasi.", "remove_from_shared_link": "Poista jakolinkistä", "remove_memory": "Tyhjennä muisti", "remove_photo_from_memory": "Poista kuva muistista", + "remove_tag": "Poista tunniste", "remove_url": "Poista URL", "remove_user": "Poista käyttäjä", "removed_api_key": "API-avain {name} poistettu", @@ -1584,6 +1630,7 @@ "select_album_cover": "Valitse albmin kansi", "select_all": "Valitse kaikki", "select_all_duplicates": "Valitse kaikki kaksoiskappaleet", + "select_all_in": "Valitse kaikki {group}", "select_avatar_color": "Valitse avatarin väri", "select_face": "Valitse kasvo", "select_featured_photo": "Valitse esittelykuva", @@ -1604,6 +1651,7 @@ "server_info_box_server_url": "Palvelimen URL-osoite", "server_offline": "Palvelin Offline-tilassa", "server_online": "Palvelin Online-tilassa", + "server_privacy": "Palvelimen tietosuoja", "server_stats": "Palvelimen tilastot", "server_version": "Palvelimen versio", "set": "Aseta", @@ -1613,6 +1661,7 @@ "set_date_of_birth": "Aseta syntymäaika", "set_profile_picture": "Aseta profiilikuva", "set_slideshow_to_fullscreen": "Näytä diaesitys koko ruudulla", + "set_stack_primary_asset": "Aseta pääkohteeksi", "setting_image_viewer_help": "Kuvaa katseltaessa ensin ladataan pikkukuva, sitten keskilaatuinen pikkukuva (jos käytössä) ja lopuksi alkuperäinen (jos käytössä).", "setting_image_viewer_original_subtitle": "Ota käyttöön ladataksesi alkuperäinen täysitarkkuuksinen kuva (suuri!). Poista käytöstä vähentääksesi datan käyttöä (sekä verkossa että laitteen välimuistissa).", "setting_image_viewer_original_title": "Lataa alkuperäinen kuva", @@ -1750,6 +1799,7 @@ "start_date": "Alkupäivä", "state": "Maakunta", "status": "Tila", + "stop_casting": "Lopeta suoratoisto", "stop_motion_photo": "Pysäytä liikkuva kuva", "stop_photo_sharing": "Lopetetaanko kuvien jakaminen?", "stop_photo_sharing_description": "{partner} ei enää pääse kuviisi.", @@ -1769,7 +1819,7 @@ "sync_albums": "Synkronoi albumit", "sync_albums_manual_subtitle": "Synkronoi kaikki ladatut videot ja valokuvat valittuihin varmuuskopioalbumeihin", "sync_upload_album_setting_subtitle": "Luo ja lataa valokuvasi ja videosi valittuihin albumeihin Immichissä", - "tag": "Lisää tunniste", + "tag": "Tunniste", "tag_assets": "Lisää tunnisteita", "tag_created": "Luotu tunniste: {tag}", "tag_feature_description": "Selaa valokuvia ja videoita, jotka on ryhmitelty loogisten tunnisteotsikoiden mukaan", @@ -1810,6 +1860,7 @@ "total": "Yhteensä", "total_usage": "Käyttö yhteensä", "trash": "Roskakori", + "trash_action_prompt": "{count} siirretty roskakoriin", "trash_all": "Vie kaikki roskakoriin", "trash_count": "Roskakori {count, number}", "trash_delete_asset": "Poista / vie roskakoriin", @@ -1827,8 +1878,11 @@ "unable_to_change_pin_code": "PIN-koodin vaihtaminen epäonnistui", "unable_to_setup_pin_code": "PIN-koodin määrittäminen epäonnistui", "unarchive": "Palauta arkistosta", + "unarchive_action_prompt": "{count} poistettu arkistosta", "unarchived_count": "{count, plural, other {# poistettu arkistosta}}", + "undo": "Kumoa", "unfavorite": "Poista suosikeista", + "unfavorite_action_prompt": "{count} poistettu suosikeista", "unhide_person": "Poista henkilö piilosta", "unknown": "Tuntematon", "unknown_country": "Tuntematon maa", @@ -1844,8 +1898,10 @@ "unsaved_change": "Tallentamaton muutos", "unselect_all": "Poista valinnat", "unselect_all_duplicates": "Poista kaikkien kaksoiskappaleiden valinta", + "unselect_all_in": "Poista kaikki valinnat {group}", "unstack": "Pura pino", "unstacked_assets_count": "Poistettu pinosta {count, plural, one {# kohde} other {# kohdetta}}", + "untagged": "Ilman tunnistetta", "up_next": "Seuraavaksi", "updated_at": "Päivitetty", "updated_password": "Salasana päivitetty", @@ -1861,7 +1917,7 @@ "upload_status_uploaded": "Ladattu", "upload_success": "Lataus onnistui. Päivitä sivu jotta näet latauksesi.", "upload_to_immich": "Lähetä Immichiin ({count})", - "uploading": "Lähettään", + "uploading": "Lähettää", "url": "URL", "usage": "Käyttö", "use_biometric": "Käytä biometriikkaa", @@ -1873,6 +1929,7 @@ "user_liked": "{user} tykkäsi {type, select, photo {kuvasta} video {videosta} asset {mediasta} other {tästä}}", "user_pin_code_settings": "PIN-koodi", "user_pin_code_settings_description": "Hallinnoi PIN-koodiasi", + "user_privacy": "Käyttäjän tietosuoja", "user_purchase_settings": "Osta", "user_purchase_settings_description": "Hallitse ostostasi", "user_role_set": "Tee käyttäjästä {user} {role}", diff --git a/i18n/fil.json b/i18n/fil.json index 12e74f7bad..12e6086064 100644 --- a/i18n/fil.json +++ b/i18n/fil.json @@ -14,10 +14,13 @@ "add_a_location": "Dagdagan ng lugar", "add_a_name": "Dagdagan ng pangalan", "add_a_title": "Dagdagan ng pamagat", + "add_endpoint": "Dagdagan ng dulo", "add_location": "Magdagdag ng lugar", "add_more_users": "Magdagdag ng mga user", "add_partner": "Magdagdag ng kasangga", + "add_path": "Magdagdag ng path", "add_photos": "Magdagdag ng litrato", + "add_tag": "Magdagdag ng tag", "add_to": "Idagdag sa…", "add_to_album": "Idagdag sa album", "add_to_album_bottom_sheet_added": "Naidagdag sa {album}", diff --git a/i18n/fr.json b/i18n/fr.json index bf57469944..cebd4b694f 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -17,7 +17,7 @@ "add_endpoint": "Ajouter une adresse", "add_exclusion_pattern": "Ajouter un schéma d'exclusion", "add_import_path": "Ajouter un chemin à importer", - "add_location": "Ajouter un lieu", + "add_location": "Ajouter une localisation", "add_more_users": "Ajouter plus d'utilisateurs", "add_partner": "Ajouter un partenaire", "add_path": "Ajouter un chemin", @@ -166,6 +166,20 @@ "metadata_settings_description": "Gestion des paramètres de métadonnées", "migration_job": "Migration", "migration_job_description": "Migration des miniatures pour les médias et les visages vers la dernière structure de dossiers", + "nightly_tasks_cluster_faces_setting_description": "Démarrer la reconnaissance faciale sur les visages nouvellement détectés", + "nightly_tasks_cluster_new_faces_setting": "Regrouper les nouveaux visages", + "nightly_tasks_database_cleanup_setting": "Tâches de nettoyage de la base de données", + "nightly_tasks_database_cleanup_setting_description": "Nettoyage ancien, données de la base de données expirées", + "nightly_tasks_generate_memories_setting": "Générer les souvenirs", + "nightly_tasks_generate_memories_setting_description": "Créer des souvenirs à partir des éléments", + "nightly_tasks_missing_thumbnails_setting": "Générer les miniatures manquantes", + "nightly_tasks_missing_thumbnails_setting_description": "Mettre en file d'attente les éléments sans miniature pour la création de miniature", + "nightly_tasks_settings": "Paramètres des tâches de nuit", + "nightly_tasks_settings_description": "Gérer les tâches de nuit", + "nightly_tasks_start_time_setting": "Heure de démarrage", + "nightly_tasks_start_time_setting_description": "Heure à laquelle le serveur commence à exécuter les tâches de nuit", + "nightly_tasks_sync_quota_usage_setting": "Synchroniser les quota d'usage", + "nightly_tasks_sync_quota_usage_setting_description": "Mettre à jour les quota d'usage de l'utilisateur, en se basant sur l'utilisation actuelle", "no_paths_added": "Aucun chemin n'a été ajouté", "no_pattern_added": "Aucun schéma d'exclusion n'a été ajouté", "note_apply_storage_label_previous_assets": "Remarque : pour appliquer l'étiquette de stockage à des médias précédemment envoyés, exécutez", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "URI de redirection mobile", "oauth_mobile_redirect_uri_override": "Remplacer l'URI de redirection mobile", "oauth_mobile_redirect_uri_override_description": "Activer quand le fournisseur d'OAuth ne permet pas un URI mobile, comme ''{callback}''", + "oauth_role_claim": "Attribut de rôle", + "oauth_role_claim_description": "Donne automatiquement un accès en tant qu'admin, en se basant sur la présence de cet attribut. L'attribut peut avoir soit 'user' (utilisateur) soit 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Gérer les paramètres de connexion OAuth", "oauth_settings_more_details": "Pour plus de détails sur cette fonctionnalité, consultez ce lien.", @@ -244,7 +260,7 @@ "storage_template_migration_info": "L'enregistrement des modèles va convertir toutes les extensions en minuscule. Les changements de modèle ne s'appliqueront qu'aux nouveaux médias. Pour appliquer rétroactivement le modèle aux médias précédemment envoyés, exécutez la tâche {job}.", "storage_template_migration_job": "Tâche de migration du modèle de stockage", "storage_template_more_details": "Pour plus de détails sur cette fonctionnalité, reportez-vous au Modèle de stockage et à ses implications", - "storage_template_onboarding_description_v2": "Quand elle est activée, cette fonctionnalité organise automatiquement les fichiers, sur base d'un modèle défini par l'utilisateur. Pour plus d'informations, se répéter à la documentation.", + "storage_template_onboarding_description_v2": "Quand elle est activée, cette fonctionnalité organise automatiquement les fichiers, sur base d'un modèle défini par l'utilisateur. Pour plus d'informations, se référer à la documentation.", "storage_template_path_length": "Limite approximative de la longueur du chemin : {length, number}/{limit, number}", "storage_template_settings": "Modèle de stockage", "storage_template_settings_description": "Gérer la structure des dossiers et le nom des fichiers du média envoyé", @@ -357,10 +373,12 @@ "admin_password": "Mot de passe Admin", "administration": "Administration", "advanced": "Avancé", + "advanced_settings_beta_timeline_subtitle": "Essayer la nouvelle application", + "advanced_settings_beta_timeline_title": "Timeline de la béta", "advanced_settings_enable_alternate_media_filter_subtitle": "Utilisez cette option pour filtrer les média durant la synchronisation avec des critères alternatifs. N'utilisez cela que lorsque l'application n'arrive pas à détecter tout les albums.", "advanced_settings_enable_alternate_media_filter_title": "[EXPÉRIMENTAL] Utiliser le filtre de synchronisation d'album alternatif", "advanced_settings_log_level_title": "Niveau de journalisation : {level}", - "advanced_settings_prefer_remote_subtitle": "Certains appareils sont très lents à charger des miniatures à partir de ressources présentes sur l'appareil. Activez ce paramètre pour charger des images externes à la place.", + "advanced_settings_prefer_remote_subtitle": "Certains appareils sont très lents à charger des miniatures à partir de ressources locales. Activez ce paramètre pour charger des images externes à la place.", "advanced_settings_prefer_remote_title": "Préférer les images externes", "advanced_settings_proxy_headers_subtitle": "Ajoutez des en-têtes personnalisés à chaque requête réseau", "advanced_settings_proxy_headers_title": "En-têtes de proxy", @@ -413,8 +431,8 @@ "all_videos": "Toutes les vidéos", "allow_dark_mode": "Autoriser le mode sombre", "allow_edits": "Autoriser les modifications", - "allow_public_user_to_download": "Permettre aux utilisateurs non connectés de télécharger", - "allow_public_user_to_upload": "Autoriser l'envoi aux utilisateurs non connectés", + "allow_public_user_to_download": "Permettre le téléchargement par des utilisateurs non connectés", + "allow_public_user_to_upload": "Permettre l'envoi par des utilisateurs non connectés", "alt_text_qr_code": "Image du code QR", "anti_clockwise": "Sens anti-horaire", "api_key": "Clé API", @@ -427,6 +445,7 @@ "app_settings": "Paramètres de l'application", "appears_in": "Apparaît dans", "archive": "Archiver", + "archive_action_prompt": "{count} ajouté(s) à l'archive", "archive_or_unarchive_photo": "Archiver ou désarchiver une photo", "archive_page_no_archived_assets": "Aucun élément archivé n'a été trouvé", "archive_page_title": "Archiver ({count})", @@ -464,7 +483,6 @@ "assets": "Médias", "assets_added_count": "{count, plural, one {# média ajouté} other {# médias ajoutés}}", "assets_added_to_album_count": "{count, plural, one {# média ajouté} other {# médias ajoutés}} à l'album", - "assets_added_to_name_count": "{count, plural, one {# média ajouté} other {# médias ajoutés}} à {hasName, select, true {{name}} other {new album}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Le média ne peut pas être ajouté} other {Les médias ne peuvent pas être ajoutés}} à l'album", "assets_count": "{count, plural, one {# média} other {# médias}}", "assets_deleted_permanently": "{count} média(s) supprimé(s) définitivement", @@ -703,7 +721,7 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Sombre", - "darkTheme": "Basculer sur le thème sombre", + "dark_theme": "Activer le thème sombre", "date_after": "Date après", "date_and_time": "Date et heure", "date_before": "Date avant", @@ -719,6 +737,7 @@ "default_locale": "Région par défaut", "default_locale_description": "Afficher les dates et nombres en fonction des paramètres de votre navigateur", "delete": "Supprimer", + "delete_action_prompt": "{count} supprimé(s) définitivement", "delete_album": "Supprimer l'album", "delete_api_key_prompt": "Voulez-vous vraiment supprimer cette clé API ?", "delete_dialog_alert": "Ces médias seront définitivement supprimés de Immich et de votre appareil", @@ -732,6 +751,7 @@ "delete_key": "Supprimer la clé", "delete_library": "Supprimer la bibliothèque", "delete_link": "Supprimer le lien", + "delete_local_action_prompt": "{count} supprimé(s) localement", "delete_local_dialog_ok_backed_up_only": "Suppression des données sauvegardées uniquement", "delete_local_dialog_ok_force": "Supprimer tout de même", "delete_others": "Supprimer les autres", @@ -762,6 +782,7 @@ "documentation": "Documentation", "done": "Terminé", "download": "Télécharger", + "download_action_prompt": "Téléchargement de {count} éléments", "download_canceled": "Téléchargement annulé", "download_complete": "Téléchargement terminé", "download_enqueue": "Téléchargement en attente", @@ -799,6 +820,7 @@ "edit_key": "Modifier la clé", "edit_link": "Modifier le lien", "edit_location": "Modifier la localisation", + "edit_location_action_prompt": "{count} localisation(s) mise(s) à jour", "edit_location_dialog_title": "Localisation", "edit_name": "Modifier le nom", "edit_people": "Modifier les personnes", @@ -984,6 +1006,7 @@ "failed_to_load_assets": "Échec du chargement des ressources", "failed_to_load_folder": "Échec de chargement du dossier", "favorite": "Favori", + "favorite_action_prompt": "{count} ajouté(s) aux Favoris", "favorite_or_unfavorite_photo": "Ajouter ou supprimer des favoris", "favorites": "Favoris", "favorites_page_no_favorites": "Aucun élément favori n'a été trouvé", @@ -1036,7 +1059,7 @@ "hide_password": "Masquer le mot de passe", "hide_person": "Masquer la personne", "hide_unnamed_people": "Cacher les personnes non nommées", - "home_page_add_to_album_conflicts": "{added} éléments ajoutés à l'album {album}. Les éléments {failed} sont déjà dans l'album.", + "home_page_add_to_album_conflicts": "{added} éléments ajoutés à l'album {album}. {failed} éléments sont déjà dans l'album.", "home_page_add_to_album_err_local": "Impossible d'ajouter des médias locaux aux albums, ils sont ignorés", "home_page_add_to_album_success": "{added} éléments ajoutés à l'album {album}.", "home_page_album_err_partner": "Impossible d'ajouter des médias d'un partenaire à un album, ils sont ignorés", @@ -1127,6 +1150,7 @@ "library_page_sort_created": "Créations les plus récentes", "library_page_sort_last_modified": "Dernière modification", "library_page_sort_title": "Titre de l'album", + "licenses": "Licences", "light": "Clair", "like_deleted": "Réaction « j'aime » supprimée", "link_motion_video": "Lier la photo animée", @@ -1246,6 +1270,7 @@ "more": "Plus", "move": "Déplacer", "move_off_locked_folder": "Déplacer en dehors du dossier verrouillé", + "move_to_lock_folder_action_prompt": "{count} ajouté(s) au dossier verrouillé", "move_to_locked_folder": "Déplacer dans le dossier verrouillé", "move_to_locked_folder_confirmation": "Ces photos et vidéos seront retirés de tout les albums et ne seront visibles que dans le dossier verrouillé", "moved_to_archive": "{count, plural, one {# élément déplacé} other {# éléments déplacés}} vers les archives", @@ -1390,7 +1415,7 @@ "photos_and_videos": "Photos et vidéos", "photos_count": "{count, plural, one {{count, number} Photo} other {{count, number} Photos}}", "photos_from_previous_years": "Photos des années précédentes", - "pick_a_location": "Choisissez un lieu", + "pick_a_location": "Choisissez une localisation", "pin_code_changed_successfully": "Code PIN changé avec succès", "pin_code_reset_successfully": "Réinitialisation du code PIN réussie", "pin_code_setup_successfully": "Définition du code PIN réussie", @@ -1495,7 +1520,9 @@ "remove_custom_date_range": "Supprimer la plage de date personnalisée", "remove_deleted_assets": "Supprimer les fichiers hors ligne", "remove_from_album": "Supprimer de l'album", + "remove_from_album_action_prompt": "{count} supprimé(s) de l'album", "remove_from_favorites": "Supprimer des favoris", + "remove_from_lock_folder_action_prompt": "{count} supprimé(s) du dossier verrouillé", "remove_from_locked_folder": "Supprimer du dossier verrouillé", "remove_from_locked_folder_confirmation": "Êtes vous sûr de vouloir déplacer ces photos et vidéos en dehors du dossier verrouillé ? Elles seront visibles dans votre galerie.", "remove_from_shared_link": "Supprimer des liens partagés", @@ -1510,7 +1537,7 @@ "removed_from_favorites_count": "{count, plural, one {# supprimé} other {# supprimés}} des favoris", "removed_memory": "Souvenir supprimé", "removed_photo_from_memory": "Photo supprimée du souvenir", - "removed_tagged_assets": "Tag supprimé de {count, plural, one {# média} other {# médias}}", + "removed_tagged_assets": "Étiquette supprimée de {count, plural, one {# média} other {# médias}}", "rename": "Renommer", "repair": "Réparer", "repair_no_results_message": "Les fichiers non importés ou absents s'afficheront ici", @@ -1566,8 +1593,8 @@ "search_filter_display_option_not_in_album": "Pas dans un album", "search_filter_display_options": "Options d'affichage", "search_filter_filename": "Recherche par nom de fichier", - "search_filter_location": "Lieu", - "search_filter_location_title": "Sélectionner un lieu", + "search_filter_location": "Localisation", + "search_filter_location_title": "Sélectionner une localisation", "search_filter_media_type": "Type de média", "search_filter_media_type_title": "Sélectionner type de média", "search_filter_people_title": "Sélectionner une personne", @@ -1667,6 +1694,7 @@ "settings_saved": "Paramètres sauvegardés", "setup_pin_code": "Définir un code PIN", "share": "Partager", + "share_action_prompt": "{count} éléments partagés", "share_add_photos": "Ajouter des photos", "share_assets_selected": "{count} sélectionné(s)", "share_dialog_preparing": "Préparation...", @@ -1768,6 +1796,7 @@ "sort_title": "Titre", "source": "Source", "stack": "Empiler", + "stack_action_prompt": "{count} groupé(s)", "stack_duplicates": "Empiler les doublons", "stack_select_one_photo": "Sélectionnez une photo principale pour la pile", "stack_selected_photos": "Empiler les photos sélectionnées", @@ -1838,6 +1867,7 @@ "total": "Total", "total_usage": "Utilisation globale", "trash": "Corbeille", + "trash_action_prompt": "{count} mis à la corbeille", "trash_all": "Tout supprimer", "trash_count": "Corbeille {count, number}", "trash_delete_asset": "Mettre à la corbeille/Supprimer un média", @@ -1855,9 +1885,11 @@ "unable_to_change_pin_code": "Impossible de changer le code PIN", "unable_to_setup_pin_code": "Impossible de définir le code PIN", "unarchive": "Désarchiver", + "unarchive_action_prompt": "{count} supprimé(s) de l'archive", "unarchived_count": "{count, plural, one {# supprimé} other {# supprimés}} de l'archive", "undo": "Annuler", "unfavorite": "Enlever des favoris", + "unfavorite_action_prompt": "{count} supprimé(s) des favoris", "unhide_person": "Afficher la personne", "unknown": "Inconnu", "unknown_country": "Pays non connu", @@ -1875,7 +1907,9 @@ "unselect_all_duplicates": "Désélectionner tous les doublons", "unselect_all_in": "Tout désélectionner dans {group}", "unstack": "Désempiler", + "unstack_action_prompt": "{count} non groupés", "unstacked_assets_count": "{count, plural, one {# média dépilé} other {# médias dépilés}}", + "untagged": "Étiquette supprimée", "up_next": "Suite", "updated_at": "Mis à jour à", "updated_password": "Mot de passe mis à jour", @@ -1912,6 +1946,7 @@ "user_usage_stats_description": "Voir les statistiques d'utilisation du compte", "username": "Nom d'utilisateur", "users": "Utilisateurs", + "users_added_to_album_count": "{count, plural, one {# utilisateur ajouté} other {# utilisateurs ajoutés}} à l'album", "utilities": "Utilitaires", "validate": "Valider", "validate_endpoint_error": "Merci d'entrer un lien valide", diff --git a/i18n/gl.json b/i18n/gl.json index 558cc48900..70146abb53 100644 --- a/i18n/gl.json +++ b/i18n/gl.json @@ -455,7 +455,6 @@ "assets": "Activos", "assets_added_count": "Engadido {count, plural, one {# activo} other {# activos}}", "assets_added_to_album_count": "Engadido {count, plural, one {# activo} other {# activos}} ao álbum", - "assets_added_to_name_count": "Engadido {count, plural, one {# activo} other {# activos}} a {hasName, select, true {{name}} other {novo álbum}}", "assets_count": "{count, plural, one {# activo} other {# activos}}", "assets_deleted_permanently": "{count} activo(s) eliminado(s) permanentemente", "assets_deleted_permanently_from_server": "{count} activo(s) eliminado(s) permanentemente do servidor Immich", diff --git a/i18n/he.json b/i18n/he.json index 737c307f31..49973dbc63 100644 --- a/i18n/he.json +++ b/i18n/he.json @@ -34,6 +34,7 @@ "added_to_favorites_count": "{count, number} נוספו למועדפים", "admin": { "add_exclusion_pattern_description": "הוספת דפוסי החרגה. נתמכת התאמת דפוסים באמצעות *, ** ו-?. כדי להתעלם מכל הקבצים בתיקיה כלשהי בשם \"Raw\", יש להשתמש ב \"**/Raw/**\". כדי להתעלם מכל הקבצים המסתיימים ב \"tif.\", יש להשתמש ב \"tif.*/**\". כדי להתעלם מנתיב מוחלט, יש להשתמש ב \"**/נתיב/להתעלמות\".", + "admin_user": "מנהל מערכת", "asset_offline_description": "תמונה מספרייה חיצונית זו לא נמצאת יותר בדיסק והועברה לאשפה. אם הקובץ הועבר מתוך הספרייה, נא לבדוק את ציר הזמן שלך עבור התמונה המקבילה החדש. כדי לשחזר תמונה זו, נא לוודא ש-Immich יכול לגשת אל נתיב הקובץ למטה ולסרוק מחדש את הספרייה.", "authentication_settings": "הגדרות התחברות", "authentication_settings_description": "ניהול סיסמה, OAuth, והגדרות התחברות אחרות", @@ -165,6 +166,20 @@ "metadata_settings_description": "ניהול הגדרות מטא-נתונים", "migration_job": "העברה", "migration_job_description": "העבר תמונות ממוזערות של תמונות ופנים למבנה התיקיות העדכני ביותר", + "nightly_tasks_cluster_faces_setting_description": "בצע זיהוי פנים עבור פרצופים שזוהו לאחרונה", + "nightly_tasks_cluster_new_faces_setting": "קבץ פנים חדשות", + "nightly_tasks_database_cleanup_setting": "משימות תחזוקה וניקוי של מסד הנתונים", + "nightly_tasks_database_cleanup_setting_description": "נקה נתונים ישנים שפג תוקפם ממסד הנתונים", + "nightly_tasks_generate_memories_setting": "יצירת זכרונות", + "nightly_tasks_generate_memories_setting_description": "צור זכרונות חדשים מהתמונות שלך", + "nightly_tasks_missing_thumbnails_setting": "צור תמונות ממוזערות חסרות", + "nightly_tasks_missing_thumbnails_setting_description": "הוסף לתור קבצים ללא תמונות ממוזערות ליצירה של תמונות ממוזערות", + "nightly_tasks_settings": "הגדרות של משימות ליליות", + "nightly_tasks_settings_description": "נהל משימות ליליות", + "nightly_tasks_start_time_setting": "זמן התחלה", + "nightly_tasks_start_time_setting_description": "השעה שבה השרת מתחיל להריץ את המשימות הליליות", + "nightly_tasks_sync_quota_usage_setting": "סנכרן את השימוש באחסון", + "nightly_tasks_sync_quota_usage_setting_description": "עדכן את מכסת האחסון של המשתמש בהתאם לשימוש הנוכחי", "no_paths_added": "לא נוספו נתיבים", "no_pattern_added": "לא נוספה תבנית", "note_apply_storage_label_previous_assets": "הערה: כדי להחיל את תווית האחסון על תמונות שהועלו בעבר, הפעל את", @@ -203,7 +218,7 @@ "oauth_storage_quota_claim": "דרישת מכסת אחסון", "oauth_storage_quota_claim_description": "הגדר אוטומטית את מכסת האחסון של המשתמש לערך של דרישה זו.", "oauth_storage_quota_default": "מכסת אחסון ברירת מחדל (GiB)", - "oauth_storage_quota_default_description": "מכסה ב-GiB לשימוש כאשר לא מסופקת דרישה (הזן 0 עבור מכסה בלתי מוגבלת).", + "oauth_storage_quota_default_description": "מכסה ב-GiB לשימוש כאשר לא מסופקת דרישה.", "oauth_timeout": "הבקשה נכשלה – הזמן הקצוב הסתיים", "oauth_timeout_description": "זמן קצוב לבקשות (במילישניות)", "password_enable_description": "התחבר עם דוא\"ל וסיסמה", @@ -243,6 +258,7 @@ "storage_template_migration_info": "תבנית האחסון תמיר את כל ההרחבות לאותיות קטנות. שינויים בתבנית יחולו רק על תמונות חדשות. כדי להחיל באופן רטרואקטיבי את התבנית על תמונות שהועלו בעבר, הפעל את {job}.", "storage_template_migration_job": "משימת העברת תבנית אחסון", "storage_template_more_details": "לפרטים נוספים אודות תכונה זו, עיין בתבנית האחסון ובהשלכותיה", + "storage_template_onboarding_description_v2": "כאשר פיצ’ר זה מופעל, הקבצים יאורגנו אוטומטית לפי תבנית שהוגדרה על ידי המשתמש. למידע נוסף, עיין ב־תיעוד.", "storage_template_path_length": "מגבלת אורך נתיב משוערת: {length, number}/{limit, number}", "storage_template_settings": "תבנית אחסון", "storage_template_settings_description": "ניהול מבנה התיקיות ואת שם הקובץ של התמונה שהועלתה", @@ -425,6 +441,7 @@ "app_settings": "הגדרות יישום", "appears_in": "מופיע ב", "archive": "ארכיון", + "archive_action_prompt": "{count} נוספו לארכיון", "archive_or_unarchive_photo": "העבר תמונה לארכיון או הוצא אותה משם", "archive_page_no_archived_assets": "לא נמצאו תמונות בארכיון", "archive_page_title": "בארכיון ({count})", @@ -462,7 +479,6 @@ "assets": "תמונות", "assets_added_count": "{count, plural, one {נוספה תומנה #} other {נוספו # תמונות}}", "assets_added_to_album_count": "{count, plural, one {נוספה תמונה #} other {נוספו # תמונות}} לאלבום", - "assets_added_to_name_count": "{count, plural, one {תמונה # נוספה} other {# תמונות נוספו}} אל {hasName, select, true {{name}} other {אלבום חדש}}", "assets_cannot_be_added_to_album_count": "לא ניתן להוסיף את ה{count, plural, one {תמונה} other {תמונות}} לאלבום", "assets_count": "{count, plural, one {תמונה #} other {# תמונות}}", "assets_deleted_permanently": "{count} תמונות נמחקו לצמיתות", @@ -701,7 +717,7 @@ "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "כהה", - "darkTheme": "החלפה למצב חושך", + "dark_theme": "הפעל/כבה מצב כהה", "date_after": "תאריך אחרי", "date_and_time": "תאריך ושעה", "date_before": "תאריך לפני", @@ -711,12 +727,13 @@ "day": "יום", "deduplicate_all": "ביטול כל הכפילויות", "deduplication_criteria_1": "גודל תמונה בבתים", - "deduplication_criteria_2": "ספירת נתוני EXIF", + "deduplication_criteria_2": "כמות נתוני EXIF", "deduplication_info": "מידע על ביטול כפילויות", "deduplication_info_description": "כדי לבחור מראש תמונות באופן אוטומטי ולהסיר כפילויות בכמות גדולה, אנו מסתכלים על:", "default_locale": "שפת ברירת מחדל", "default_locale_description": "פורמט תאריכים ומספרים מבוסס שפת הדפדפן שלך", "delete": "מחק", + "delete_action_prompt": "{count} נמחקו לצמיתות", "delete_album": "מחק אלבום", "delete_api_key_prompt": "האם אתה בטוח שברצונך למחוק מפתח ה-API הזה?", "delete_dialog_alert": "הפריטים האלה ימחקו לצמיתות מהשרת ומהמכשיר שלך", @@ -797,6 +814,7 @@ "edit_key": "ערוך מפתח", "edit_link": "ערוך קישור", "edit_location": "ערוך מיקום", + "edit_location_action_prompt": "{count} מיקומים נערכו", "edit_location_dialog_title": "מיקום", "edit_name": "ערוך שם", "edit_people": "ערוך אנשים", @@ -982,6 +1000,7 @@ "failed_to_load_assets": "טעינת תמונות נכשלה", "failed_to_load_folder": "טעינת תיקיה נכשלה", "favorite": "מועדף", + "favorite_action_prompt": "{count} נוספו למועדפים", "favorite_or_unfavorite_photo": "הוסף או הסר תמונה מהמועדפים", "favorites": "מועדפים", "favorites_page_no_favorites": "לא נמצאו תמונות מועדפים", @@ -1148,6 +1167,7 @@ "locked_folder": "תיקיה נעולה", "log_out": "התנתק", "log_out_all_devices": "התנתק מכל המכשירים", + "logged_in_as": "מחובר כ {user}", "logged_out_all_devices": "מנותק מכל המכשירים", "logged_out_device": "מכשיר מנותק", "login": "כניסה", @@ -1243,6 +1263,7 @@ "more": "עוד", "move": "העבר", "move_off_locked_folder": "הוצאה מהתיקייה הנעולה", + "move_to_lock_folder_action_prompt": "{count} נוספו לתיקייה הנעולה", "move_to_locked_folder": "העבר לתיקיה הנעולה", "move_to_locked_folder_confirmation": "התמונות והסרטונים האלו יוסרו מכל האלבומים, ויהיו מוצגים רק בתיקיה הנעולה", "moved_to_archive": "{count, plural, one {הועברה תמונה # } other {# תמונות הועברו}} לארכיון", @@ -1492,7 +1513,9 @@ "remove_custom_date_range": "הסר טווח תאריכים מותאם", "remove_deleted_assets": "הסר קבצים לא מקוונים", "remove_from_album": "הסר מאלבום", + "remove_from_album_action_prompt": "{count} הוסרו מהאלבום", "remove_from_favorites": "הסר מהמועדפים", + "remove_from_lock_folder_action_prompt": "{count} הוסרו מהתיקייה הנעולה", "remove_from_locked_folder": "הסר מהתיקייה הנעולה", "remove_from_locked_folder_confirmation": "האם אתה בטוח שברצונך להעביר את התמונות והסרטונים האלה מחוץ לתיקייה הנעולה? הם יהיו מוצגים בספרייה שלך.", "remove_from_shared_link": "הסר מקישור משותף", @@ -1605,6 +1628,7 @@ "select_album_cover": "בחר עטיפת אלבום", "select_all": "בחר הכל", "select_all_duplicates": "בחר את כל הכפילויות", + "select_all_in": "בחר הכול בתוך {group}", "select_avatar_color": "בחר צבע תמונת פרופיל", "select_face": "בחר פנים", "select_featured_photo": "בחר תמונה מייצגת", @@ -1834,6 +1858,7 @@ "total": "סה\"כ", "total_usage": "שימוש כולל", "trash": "אשפה", + "trash_action_prompt": "{count} הועברו לאשפה", "trash_all": "העבר הכל לאשפה", "trash_count": "העבר לאשפה {count, number}", "trash_delete_asset": "העבר לאשפה/מחק תמונה", @@ -1851,9 +1876,11 @@ "unable_to_change_pin_code": "לא ניתן לשנות את קוד ה PIN", "unable_to_setup_pin_code": "לא ניתן להגדיר קוד PIN", "unarchive": "הוצא מארכיון", + "unarchive_action_prompt": "{count} הוסרו מהארכיון", "unarchived_count": "{count, plural, other {# הוצאו מהארכיון}}", "undo": "לבטל", "unfavorite": "לא מועדף", + "unfavorite_action_prompt": "{count} הוסרו מהמועדפים", "unhide_person": "בטל הסתרת אדם", "unknown": "לא ידוע", "unknown_country": "מדינה לא ידועה", @@ -1869,8 +1896,10 @@ "unsaved_change": "שינוי לא נשמר", "unselect_all": "בטל בחירה בהכל", "unselect_all_duplicates": "בטל בחירת כל הכפילויות", + "unselect_all_in": "בטל את הבחירה של הכל ב {group}", "unstack": "בטל ערימה", "unstacked_assets_count": "{count, plural, one {תמונה # הוסרה} other {# תמונות הוסרו}} מהערימה", + "untagged": "לא מתיוגים", "up_next": "הבא בתור", "updated_at": "עודכן", "updated_password": "סיסמה עודכנה", diff --git a/i18n/hi.json b/i18n/hi.json index abb1552154..2cb29370b5 100644 --- a/i18n/hi.json +++ b/i18n/hi.json @@ -22,6 +22,7 @@ "add_partner": "जोड़ीदार डालें", "add_path": "पथ डालें", "add_photos": "फ़ोटो डालें", + "add_tag": "चिह्नित करें", "add_to": "इसमें डालें…", "add_to_album": "एल्बम में डालें", "add_to_album_bottom_sheet_added": "{album} में डालें", @@ -33,6 +34,7 @@ "added_to_favorites_count": "पसंदीदा में {count, number} डाला गया", "admin": { "add_exclusion_pattern_description": "बहिष्करण पैटर्न जोड़ें. *, **, और ? का उपयोग करके ग्लोबिंग करना समर्थित है। \"Raw\" नामक किसी भी निर्देशिका की सभी फ़ाइलों को अनदेखा करने के लिए, \"**/Raw/**\" का उपयोग करें। \".tif\" से समाप्त होने वाली सभी फ़ाइलों को अनदेखा करने के लिए, \"**/*.tif\" का उपयोग करें। किसी पूर्ण पथ को अनदेखा करने के लिए, \"/path/to/ignore/**\" का उपयोग करें।", + "admin_user": "व्यवस्थापक उपयोगकर्ता", "asset_offline_description": "यह बाहरी लाइब्रेरी एसेट अब डिस्क पर मौजूद नहीं है और इसे ट्रैश में डाल दिया गया है। यदि फ़ाइल को लाइब्रेरी के भीतर कहीं ले जाया गया था, तो नई संबंधित एसेट के लिए अपनी टाइमलाइन देखें। इस एसेट को वापस पाने के लिए, कृपया सुनिश्चित करें कि नीचे दिए गए फ़ाइल पथ को इम्मिच द्वारा एक्सेस किया जा सकता है और फिर लाइब्रेरी को स्कैन करें।", "authentication_settings": "प्रमाणीकरण सेटिंग्स", "authentication_settings_description": "पासवर्ड, OAuth और अन्य प्रमाणीकरण सेटिंग्स प्रबंधित करें", diff --git a/i18n/hr.json b/i18n/hr.json index d2edfc9a8e..35e7aba7e0 100644 --- a/i18n/hr.json +++ b/i18n/hr.json @@ -462,7 +462,6 @@ "assets": "Sredstva", "assets_added_count": "Dodano {count, plural, one {# asset} other {# assets}}", "assets_added_to_album_count": "Dodano {count, plural, one {# asset} other {# assets}} u album", - "assets_added_to_name_count": "Dodano {count, plural, one {# asset} other {# assets}} u {hasName, select, true {{name}} other {new album}}", "assets_cannot_be_added_to_album_count": "{count, plural,\n one {Nije moguće dodati medij u album}\n few {Nije moguće dodati # medija u album}\n other {Nije moguće dodati # medija u album}\n}", "assets_count": "{count, plural, one {# asset} other {# assets}}", "assets_deleted_permanently": "{count} resurs(i) uspješno uklonjeni", diff --git a/i18n/hu.json b/i18n/hu.json index 22ec6f22a1..c00fb37224 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -22,6 +22,7 @@ "add_partner": "Partner hozzáadása", "add_path": "Elérési útvonal megadása", "add_photos": "Fotók hozzáadása", + "add_tag": "Címke hozzáadása", "add_to": "Hozzáadás ide…", "add_to_album": "Felvétel albumba", "add_to_album_bottom_sheet_added": "Hozzáadva a(z) \"{album}\" albumhoz", @@ -33,6 +34,7 @@ "added_to_favorites_count": "{count, number} hozzáadva a kedvencekhez", "admin": { "add_exclusion_pattern_description": "Kihagyási minták (pattern) megadása. A *, ** és ? helyettesítő karakterek engedélyezettek. Pl. a \"Raw\" könyvtárban tárolt összes fájl kihagyásához használható a \"**/Raw/**\". Minden \".tif\" fájl kihagyása az összes mappában: \"**/*.tif\". Abszolút elérési útvonal kihagyása: \"/kihagyni/kivant/mappa/**\".", + "admin_user": "Admin felhasználó", "asset_offline_description": "Ez a külső képtárban lévő elem már nem található, ezért a lomtárba került. Ha a fájl a képtáron belül lett áthelyezve, akkor ellenőrizd, hogy továbbra is látható az idővonaladon. Az elem visszaállításához győződj meg róla, hogy az alábbi mappa az Immich számára elérhető, majd újra fésüld át a képtárat.", "authentication_settings": "Hitelesítési beállítások", "authentication_settings_description": "Jelszó, OAuth és egyéb hitelesítési beállítások kezelése", @@ -43,7 +45,7 @@ "backup_database_enable_description": "Adatbázis mentések engedélyezése", "backup_keep_last_amount": "Megőrizendő korábbi mentések száma", "backup_settings": "Adatbázis mentés beállításai", - "backup_settings_description": "Adatbázis mentés beállításainak kezelése. Megjegyzés: Ezek a feladatok nincsenek felügyelve, így nem kapsz értesítés meghiúsulás esetén.", + "backup_settings_description": "Adatbázis mentés beállításainak kezelése.", "cleared_jobs": "{job}: feladatai törölve", "config_set_by_file": "A konfigurációt jelenleg egy konfigurációs fájl állítja be", "confirm_delete_library": "Biztosan ki szeretnéd törölni a {library} képtárat?", @@ -155,7 +157,7 @@ "map_settings_description": "Térkép beállítások kezelése", "map_style_description": "Egy style.json térképtémára mutató URL cím", "memory_cleanup_job": "Memória takarítás", - "memory_generate_job": "Emlék generálálsa", + "memory_generate_job": "Emlék generálása", "metadata_extraction_job": "Metaadatok kinyerése", "metadata_extraction_job_description": "Metaadat információk (pl. GPS, arcok és felbontás) kinyerése minden elemből", "metadata_faces_import_setting": "Arc importálás engedélyezése", @@ -169,7 +171,7 @@ "note_apply_storage_label_previous_assets": "Megjegyzés: Ha a korábban feltöltött elemekhez is szeretne Tárhely Címkéket társítani, akkor futtassa ezt", "note_cannot_be_changed_later": "FIGYELEM: ezt később nem lehet megváltoztatni!", "notification_email_from_address": "Feladó cím", - "notification_email_from_address_description": "Küldő email címe, például: \"Immich Fotószerver \"", + "notification_email_from_address_description": "Küldő email címe, például: \"Immich Fotószerver \". Figyelj hogy olyan címet adj meg ahonnan az email küldés engedélyezett.", "notification_email_host_description": "Email szerver kiszolgálója (pl. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Tanúsítvány hibák figyelmen kívül hagyása", "notification_email_ignore_certificate_errors_description": "TLS tanúsítvány érvényességi hibák figyelmen kívül hagyása (nem ajánlott)", @@ -202,7 +204,7 @@ "oauth_storage_quota_claim": "Tárhelykvóta igénylése", "oauth_storage_quota_claim_description": "A felhasználó tárhelykvótájának automatikus beállítása ennek az igényeltre.", "oauth_storage_quota_default": "Alapértelmezett tárhelykvóta (GiB)", - "oauth_storage_quota_default_description": "Alapértelmezett tárhely kvóta GiB-ban, amennyiben a felhasználó nem jelezte az igényét (A korlátlan tárhelyhez 0-t adj meg).", + "oauth_storage_quota_default_description": "Alapértelmezett tárhely kvóta GiB-ban, amennyiben a felhasználó nem jelezte az igényét.", "oauth_timeout": "Kérés időkorlátja", "oauth_timeout_description": "Kérések időkorlátja milliszekundumban", "password_enable_description": "Bejelentkezés emaillel és jelszóval", @@ -242,6 +244,7 @@ "storage_template_migration_info": "A sablon az összes kiterjesztést kisbetűssé alakítja át. A megváltozott sablon csak az újonnan feltöltött elemekre vonatkozik. A korábbi elemek visszamenőleges áthelyezéséhez ezt futtasd: {job}.", "storage_template_migration_job": "Tárhely Sablon Migrációja", "storage_template_more_details": "További részletekért erről a funkcióról lásd a Tárhely Sablon és annak következményeit a dokumentációban", + "storage_template_onboarding_description_v2": "A funkció engedélyezésével automatikusan, a felhasználó által definiált sablon alapján lesznek rendezve a fájlok. Több információért lásd a dokumentációt.", "storage_template_path_length": "Útvonal hozzávetőleges maximális hossza: {length, number}{limit, number}", "storage_template_settings": "Tárhely Sablon", "storage_template_settings_description": "A feltöltött elemek mappaszerkezetének és fájl elnevezésének kezelése", @@ -256,7 +259,7 @@ "template_email_update_album": "Album frissítve sablon", "template_email_welcome": "Üdvözlő email sablon", "template_settings": "Értesítés sablon", - "template_settings_description": "Egyéni sablonok kezelése az értesítésekhez.", + "template_settings_description": "Egyéni sablonok kezelése az értesítésekhez", "theme_custom_css_settings": "Egyedi CSS", "theme_custom_css_settings_description": "CSS Stíluslapokkal az Immich stílusa megváltoztatható.", "theme_settings": "Téma Beállítások", @@ -288,7 +291,7 @@ "transcoding_encoding_options": "Enkódolás beállítások", "transcoding_encoding_options_description": "Beállíthatod az enkódolt videók kódolási algoritmusát, felbontását, minőségét és egyéb beállításait", "transcoding_hardware_acceleration": "Hardveres Gyorsítás", - "transcoding_hardware_acceleration_description": "Kísérleti funkció. Sokkal gyorsabb, viszont azonos bitrátán is alacsonyabb minőséghez vezet", + "transcoding_hardware_acceleration_description": "Kísérleti funkció: gyorsabb transzkódolás, viszont azonos bitrátán alacsonyabb minőséghez vezethet", "transcoding_hardware_decoding": "Hardveres dekódolás", "transcoding_hardware_decoding_setting_description": "Lehetővé teszi az egész folyamat gyorsítását a pusztán kódolás gyorsítása helyett. Nem biztos, hogy minden videó esetén működik.", "transcoding_max_b_frames": "B-képkockák maximum száma", @@ -334,6 +337,7 @@ "user_delete_delay_settings_description": "Hány nappal az eltávolítás után legyen véglegesen törölve a felhasználó fiókja és tárolt elemei. A végleges törlés feladat minden éjfélkor fut le, hogy ellenőrizze, hogy van-e törlendő felhasználó. Ez a beállítás a következő futtatás során lép életbe.", "user_delete_immediately": "{user} felhasználója és összes eleme azonnal sorba állításra kerül a végleges törléshez .", "user_delete_immediately_checkbox": "Felhasználó és tárolt elemeinek sorba állítása azonnali törlésre", + "user_details": "Felhasználói adatok", "user_management": "Felhasználók Kezelése", "user_password_has_been_reset": "A felhasználó jelszava megváltoztatásra került:", "user_password_reset_description": "Juttasd el az átmeneti jelszót a felhasználóhoz és tájékoztasd, hogy a következő belépésnél azt majd meg kell változtatnia.", @@ -349,6 +353,7 @@ "video_conversion_job": "Videók Átkódolása", "video_conversion_job_description": "Videók átkódolása böngészőkkel és eszközökkel való széleskörű kompatibilitás érdekében" }, + "admin_email": "Admin e-mail", "admin_password": "Admin Jelszó", "administration": "Adminisztráció", "advanced": "Haladó", @@ -362,6 +367,7 @@ "advanced_settings_self_signed_ssl_subtitle": "Nem ellenőrzi a szerver SSL tanúsítványát. Önaláírt tanúsítvány esetén szükséges beállítás.", "advanced_settings_self_signed_ssl_title": "Önaláírt SSL tanúsítványok engedélyezése", "advanced_settings_sync_remote_deletions_subtitle": "Automatikusan törölni vagy visszaállítani egy elemet ezen az eszközön, ha az adott műveletet a weben hajtották végre", + "advanced_settings_sync_remote_deletions_title": "Távoli törlések szinkronizálása [KÍSÉRLETI FUNKCIÓ]", "advanced_settings_tile_subtitle": "Haladó felhasználói beállítások", "advanced_settings_troubleshooting_subtitle": "További funkciók engedélyezése hibaelhárítás céljából", "advanced_settings_troubleshooting_title": "Hibaelhárítás", @@ -398,6 +404,9 @@ "album_with_link_access": "A link birtokában bárki láthatja a fotókat és a személyeket ebben az albumban.", "albums": "Albumok", "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Album}}", + "albums_default_sort_order": "Alapértelmezett album rendezés", + "albums_default_sort_order_description": "Alapértelmezett sorrendezés új albumok létrehozásánál.", + "albums_feature_description": "Másokkal megosztható elemek gyűjteménye.", "all": "Mind", "all_albums": "Minden album", "all_people": "Minden személy", @@ -455,10 +464,11 @@ "assets": "Elemek", "assets_added_count": "{count, plural, other {# elem}} hozzáadva", "assets_added_to_album_count": "{count, plural, other {# elem}} hozzáadva az albumhoz", - "assets_added_to_name_count": "{count, plural, other {# elem}} hozzáadva {hasName, select, true {a(z) {name}} other {az új}} albumhoz", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Az elem} other {Az elemek}} nem adhatóak hozzá az albumhoz", "assets_count": "{count, plural, other {# elem}}", "assets_deleted_permanently": "{count} elem véglegesen törölve", "assets_deleted_permanently_from_server": "{count} elem véglegesen törölve az Immich szerverről", + "assets_downloaded_successfully": "{count, plural, one {# fájl sikeresen letöltve} other {# fájl sikeresen letöltve}}", "assets_moved_to_trash_count": "{count, plural, other {# elem}} áthelyezve a lomtárba", "assets_permanently_deleted_count": "{count, plural, other {# elem}} véglegesen törölve", "assets_removed_count": "{count, plural, other {# elem}} eltávolítva", @@ -484,10 +494,10 @@ "backup_album_selection_page_selection_info": "Összegzés", "backup_album_selection_page_total_assets": "Összes egyedi elem", "backup_all": "Összes", - "backup_background_service_backup_failed_message": "Az elemek mentése sikertelen. Újrapróbálkozás...", - "backup_background_service_connection_failed_message": "A szerverhez csatlakozás sikertelen. Újrapróbálkozás...", + "backup_background_service_backup_failed_message": "Az elemek mentése sikertelen. Újrapróbálkozás…", + "backup_background_service_connection_failed_message": "A szerverhez csatlakozás sikertelen. Újrapróbálkozás…", "backup_background_service_current_upload_notification": "Feltöltés {filename}", - "backup_background_service_default_notification": "Új elemek ellenőrzése...", + "backup_background_service_default_notification": "Új elemek ellenőrzése…", "backup_background_service_error_title": "Hiba a mentés közben", "backup_background_service_in_progress_notification": "Elemek mentése folyamatban…", "backup_background_service_upload_failure_notification": "A feltöltés sikertelen {filename}", @@ -497,6 +507,7 @@ "backup_controller_page_background_app_refresh_enable_button_text": "Beállítások megnyitása", "backup_controller_page_background_battery_info_link": "Mutasd meg hogyan", "backup_controller_page_background_battery_info_message": "A sikeres háttérben történő mentéshez kérjük, tiltsd le az Immich akkumulátor optimalizálását.\n\nMivel ezt a különféle eszközökön máshogy kell, ezért kérjük, az eszközöd gyártójától tudd meg, hogyan kell.", + "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "Akkumulátor optimalizálás", "backup_controller_page_background_charging": "Csak töltés közben", "backup_controller_page_background_configure_error": "A háttérszolgáltatás beállítása sikertelen", @@ -539,6 +550,10 @@ "backup_options_page_title": "Biztonági mentés beállításai", "backup_setting_subtitle": "A háttérben és előtérben mentés beállításainak kezelése", "backward": "Visszafele", + "biometric_auth_enabled": "Biometrikus azonosítás engedélyezve", + "biometric_locked_out": "A biometrikus azonosításból kizárva", + "biometric_no_options": "A biometrikus azonosítás nem elérhető", + "biometric_not_available": "Biometrikus azonosítás ezen az eszközön nem elérhető", "birthdate_saved": "Születésnap elmentve", "birthdate_set_description": "A születés napját a rendszer arra használja, hogy kiírja, hogy a fénykép készítésekor a személy hány éves volt.", "blurred_background": "Homályos háttér", @@ -572,6 +587,7 @@ "cannot_undo_this_action": "Ez a művelet nem visszavonható!", "cannot_update_the_description": "A leírás megváltoztatása nem sikerült", "change_date": "Dátum változtatása", + "change_description": "Leírás megváltoztatása", "change_display_order": "Megjelenítési sorrend megváltoztatása", "change_expiration_time": "Lejárati idő megváltoztatása", "change_location": "Helyszín változtatása", @@ -598,6 +614,7 @@ "clear_all_recent_searches": "Legutóbbi keresések törlése", "clear_message": "Üzenet törlése", "clear_value": "Érték törlése", + "client_cert_dialog_msg_confirm": "OK", "client_cert_enter_password": "Jelszó Megadása", "client_cert_import": "Importálás", "client_cert_import_success_msg": "Kliens tanúsítvány importálva", @@ -625,6 +642,10 @@ "confirm_keep_this_delete_others": "Minden más elem a készletben törlésre kerül, kivéve ezt az elemet. Biztosan folytatni szeretnéd?", "confirm_new_pin_code": "Új PIN kód megerősítése", "confirm_password": "Jelszó megerősítése", + "confirm_tag_face": "Szeretnéd ezt az arcot {name}-ként megjelölni?", + "confirm_tag_face_unnamed": "Szeretnéd ezt az arcot megjelölni?", + "connected_device": "Kapcsolt eszköz", + "connected_to": "Kapcsolódva", "contain": "Belül", "context": "Kontextus", "continue": "Folytatás", @@ -633,6 +654,7 @@ "control_bottom_app_bar_delete_from_local": "Törlés az eszközről", "control_bottom_app_bar_edit_location": "Hely Módosítása", "control_bottom_app_bar_edit_time": "Dátum és Idő Módosítása", + "control_bottom_app_bar_share_link": "Link megosztása", "control_bottom_app_bar_share_to": "Megosztás Ide", "control_bottom_app_bar_trash_from_immich": "Lomtárba Helyez", "copied_image_to_clipboard": "Kép a vágólapra másolva.", @@ -664,6 +686,7 @@ "create_tag_description": "Új címke létrehozása. Beágyazott címkék esetén add meg a címke teljes elérési útvonalát, beleértve a perjeleket is.", "create_user": "Felhasználó létrehozása", "created": "Készült", + "created_at": "Létrehozva", "crop": "Kivágás", "curated_object_page_title": "Dolgok", "current_device": "Ez az eszköz", @@ -719,7 +742,9 @@ "direction": "Irány", "disabled": "Letiltott", "disallow_edits": "Módosítások letiltása", + "discord": "Discord", "discover": "Felfedez", + "discovered_devices": "Felfedezett eszközök", "dismiss_all_errors": "Minden hiba elvetése", "dismiss_error": "Hiba elvetése", "display_options": "Megjelenítési beállítások", @@ -744,8 +769,8 @@ "download_settings_description": "Elemek letöltésével kapcsolatos beállítások kezelése", "download_started": "Letöltés megkezdve", "download_sucess": "Sikeres letöltés", - "download_sucess_android": "Média letöltve a DCIM/Immich mappába\n", - "download_waiting_to_retry": "Várakozás", + "download_sucess_android": "Média letöltve a DCIM/Immich mappába", + "download_waiting_to_retry": "Várakozás újrapróbálkozáshoz", "downloading": "Letöltés", "downloading_asset_filename": "{filename} elem letöltése", "downloading_media": "Média letöltése", @@ -758,6 +783,8 @@ "edit_avatar": "Profilkép módosítása", "edit_date": "Dátum módosítása", "edit_date_and_time": "Dátum és idő módosítása", + "edit_description": "Leírás szerkesztése", + "edit_description_prompt": "Kérlek válassz egy új leírást:", "edit_exclusion_pattern": "Kizárási minta (pattern) módosítása", "edit_faces": "Arcok módosítása", "edit_import_path": "Importálási útvonal módosítása", @@ -777,18 +804,25 @@ "editor_close_without_save_title": "Szerkesztő bezárása?", "editor_crop_tool_h2_aspect_ratios": "Oldalarányok", "editor_crop_tool_h2_rotation": "Forgatás", + "email": "E-mail", + "email_notifications": "E-mail értesítések", + "empty_folder": "Ez a mappa üres", "empty_trash": "Lomtár ürítése", "empty_trash_confirmation": "Biztosan kiüríted a lomtárat? Ez az Immich lomtárában lévő összes elemet véglegesen törli.\nEz a művelet nem visszavonható!", "enable": "Engedélyezés", + "enable_biometric_auth_description": "Add meg a jelszavad a biometrikus azonosítás engedélyezéséhez", "enabled": "Engedélyezve", "end_date": "Vég dátum", "enqueued": "Sorba állítva", - "enter_wifi_name": "Add meg a WiFi hálózat nevét", + "enter_wifi_name": "Add meg a Wi-Fi hálózat nevét", + "enter_your_pin_code": "Add meg a jelszavad", + "enter_your_pin_code_subtitle": "Add meg a jelszavad a zárolt mappa megnyitásához", "error": "Hiba", "error_change_sort_album": "Album sorbarendezésének megváltoztatása sikertelen", "error_delete_face": "Hiba az arc törlése során", "error_loading_image": "Hiba a kép betöltése közben", "error_saving_image": "Hiba: {error}", + "error_tag_face_bounding_box": "Hiba az arc megjelölése közben - nem elérhetőek a határoló koordináták", "error_title": "Hiba - valami félresikerült", "errors": { "cannot_navigate_next_asset": "Nem lehet a következő elemhez navigálni", @@ -816,10 +850,12 @@ "failed_to_keep_this_delete_others": "Nem sikerült megtartani ezt az elemet, és a többi elemet törölni", "failed_to_load_asset": "Elem betöltése sikertelen", "failed_to_load_assets": "Elemek betöltése sikertelen", + "failed_to_load_notifications": "Értesítések betöltése sikertelen", "failed_to_load_people": "Személyek betöltése sikertelen", "failed_to_remove_product_key": "Termékkulcs eltávolítása sikertelen", "failed_to_stack_assets": "Elemek csoportosítása sikertelen", "failed_to_unstack_assets": "Csoportosított elemek szétszedése sikertelen", + "failed_to_update_notification_status": "Értesítés státusz frissítése sikertelen", "import_path_already_exists": "Ez az importálási útvonal már létezik.", "incorrect_email_or_password": "Helytelen email vagy jelszó", "paths_validation_failed": "A(z) {paths, plural, one {# elérési útvonal} other {# elérési útvonal}} érvényesítése sikertelen", @@ -836,6 +872,7 @@ "unable_to_archive_unarchive": "Az elem {archived, select, true {archiválása} other {kivétele az archívumból}} sikertelen", "unable_to_change_album_user_role": "Az album felhasználói jogkörének megváltoztatása sikertelen", "unable_to_change_date": "Dátum megváltoztatása sikertelen", + "unable_to_change_description": "Leírás módosítása sikertelen", "unable_to_change_favorite": "Az elem kedvenc állapotának megváltoztatása sikertelen", "unable_to_change_location": "Hely megváltoztatása sikertelen", "unable_to_change_password": "Jelszó megváltoztatása sikertelen", @@ -879,6 +916,7 @@ "unable_to_remove_partner": "Partner eltávolítása sikertelen", "unable_to_remove_reaction": "Reakció eltávolítása sikertelen", "unable_to_reset_password": "Jelszó visszaállítása sikertelen", + "unable_to_reset_pin_code": "Jelszó visszaállítása sikertelen", "unable_to_resolve_duplicate": "Duplikátum feloldása sikertelen", "unable_to_restore_assets": "Elemek visszaállítása sikertelen", "unable_to_restore_trash": "Az összes elem visszaállítása sikertelen", @@ -906,11 +944,14 @@ "unable_to_update_user": "Felhasználó módosítása sikertelen", "unable_to_upload_file": "Fájlfeltöltés sikertelen" }, + "exif": "Exif", "exif_bottom_sheet_description": "Leírás Hozzáadása...", "exif_bottom_sheet_details": "RÉSZLETEK", "exif_bottom_sheet_location": "HELY", "exif_bottom_sheet_people": "EMBEREK", "exif_bottom_sheet_person_add_person": "Elnevez", + "exif_bottom_sheet_person_age_months": "{months} hónap idős", + "exif_bottom_sheet_person_age_year_months": "1 év, {months} hónap idős", "exit_slideshow": "Kilépés a Diavetítésből", "expand_all": "Összes kinyitása", "experimental_settings_new_asset_list_subtitle": "Fejlesztés alatt", @@ -928,10 +969,12 @@ "external": "Külső Képtár", "external_libraries": "Külső Képtárak", "external_network": "Külső hálózat", - "external_network_sheet_info": "Ha nem vagy a megadott WiFi hálózathoz csatlakozva, akkor az alkalmazás az alábbi URL címeken fogja elérni a szervert, fentről lefelé haladva", + "external_network_sheet_info": "Ha nem vagy a megadott Wi-Fi hálózathoz csatlakozva, akkor az alkalmazás az alábbi URL címeken fogja elérni a szervert, fentről lefelé haladva", "face_unassigned": "Nincs hozzárendelve", "failed": "Sikertelen", + "failed_to_authenticate": "Autentikáció sikertelen", "failed_to_load_assets": "Nem sikerült betölteni az elemeket", + "failed_to_load_folder": "Mappa betöltése sikertelen", "favorite": "Kedvenc", "favorite_or_unfavorite_photo": "Fotó kedvencnek jelölése vagy annak visszavonása", "favorites": "Kedvencek", @@ -945,14 +988,19 @@ "filetype": "Fájltípus", "filter": "Szűrő", "filter_people": "Személyek szűrése", + "filter_places": "Helyek szűrése", "find_them_fast": "Név alapján kereséssel gyorsan megtalálhatóak", "fix_incorrect_match": "Hibás találat javítása", + "folder": "Mappa", + "folder_not_found": "Mappa nem található", "folders": "Mappák", "folders_feature_description": "A fájlrendszerben lévő fényképek és videók mappanézetben való böngészése", "forward": "Előre", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Ez a funkció a Google-től tölti be a működéséhez szükséges külső adatokat.", "general": "Általános", "get_help": "Segítségkérés", - "get_wifiname_error": "Nem sikerült lekérni a Wi-Fi nevét. Győződj meg róla, hogy megadtad a szükséges engedélyeket és csatlakoztál egy Wi-Fi hálózathoz.", + "get_wifiname_error": "Nem sikerült lekérni a Wi-Fi nevét. Győződj meg róla, hogy megadtad a szükséges engedélyeket és csatlakoztál egy Wi-Fi hálózathoz", "getting_started": "Kezdő Lépések", "go_back": "Visszalépés", "go_to_folder": "Ugrás a mappához", @@ -981,9 +1029,9 @@ "hide_person": "Személy elrejtése", "hide_unnamed_people": "Név nélküli személyek elrejtése", "home_page_add_to_album_conflicts": "{added} elem hozzáadva a(z) \"{album}\" albumhoz. {failed} elem már eleve az albumban volt.", - "home_page_add_to_album_err_local": "Helyi elemeket még nem lehet albumba tenni. Kihagyjuk.", + "home_page_add_to_album_err_local": "Helyi elemeket még nem lehet albumba tenni, kihagyás", "home_page_add_to_album_success": "{added} elem hozzáadva a(z) \"{album}\" albumhoz.", - "home_page_album_err_partner": "Még nem lehet a partner elemeit albumokhoz adni, úghogy kihagyjuk.", + "home_page_album_err_partner": "Még nem lehet a partner elemeit albumokhoz adni, kihagyás", "home_page_archive_err_local": "Helyi elemek archiválása még nem támogatott, úgyhogy kihagyjuk", "home_page_archive_err_partner": "Partner elemeit nem lehet archiválni, úgyhogy kihagyjuk", "home_page_building_timeline": "Idővonal összeállítása", @@ -991,11 +1039,14 @@ "home_page_delete_remote_err_local": "Helyi elemek vannak távoli törlésre kiválasztva, úgyhogy ezeket kihagyjuk", "home_page_favorite_err_local": "Helyi elemeket még nem lehet a kedvencek közé tenni, úgyhogy ezeket kihagyjuk", "home_page_favorite_err_partner": "Partner elemeit még nem lehet a kedvencek közé tenni, úgyhogy ezeket kihagyjuk", - "home_page_first_time_notice": "Ha most használod először az alkalmazást, akkor ahhoz, hogy megjelenjenek a fotók és a videók az idővonaladon, állítsd be, hogy melyik albumaidról készüljön biztonsági mentés.", + "home_page_first_time_notice": "Ha most használod először az alkalmazást, a fotók és videók megjelenítéséhez az idővonaladon, állítsd be, hogy melyik albumaidról készüljön biztonsági mentés", + "home_page_locked_error_local": "Helyi elemek nem mozgathatóak a zárolt mappába, átugorva", + "home_page_locked_error_partner": "Partner elemek nem mozgathatóak a zárolt mappába, átugorva", "home_page_share_err_local": "Helyi elemekről nem lehet megosztott linket készíteni, úgyhogy kihagyjuk", "home_page_upload_err_limit": "Csak 30 elemet tudsz egyszerre feltölteni, úgyhogy kihagyjuk", "host": "Kiszolgáló", "hour": "Óra", + "id": "Azonosító", "ignore_icloud_photos": "iCloud fotók figyelmen kívül hagyása", "ignore_icloud_photos_description": "Az iCloud-ban tárolt fotók nem lesznek feltöltve az Immich szerverre", "image": "Kép", @@ -1035,6 +1086,11 @@ "invalid_date_format": "Érvénytelen dátumformátum", "invite_people": "Személyek Meghívása", "invite_to_album": "Meghívás az albumba", + "ios_debug_info_last_sync_at": "Utoljára szinkronizálva {dateTime}", + "ios_debug_info_no_processes_queued": "Nincs sorba állított hátterfolyamat", + "ios_debug_info_no_sync_yet": "Még nem futott szinkronizáló háttérfolyamat", + "ios_debug_info_processes_queued": "{count, plural, one {{count} háttérfolyamat előkészítve} other {{count} háttérfolyamat előkészítve}}", + "ios_debug_info_processing_ran_at": "Feldolgozás futott {dateTime}", "items_count": "{count, plural, other {# elem}}", "jobs": "Feladatok", "keep": "Megtart", @@ -1043,6 +1099,8 @@ "kept_this_deleted_others": "Ez az elem és a töröltek meg lettek hagyva {count, plural, one {# asset} other {# assets}}", "keyboard_shortcuts": "Billentyűparancsok", "language": "Nyelv", + "language_no_results_subtitle": "Próbáld a keresésed módosítását", + "language_search_hint": "Nyelvek keresése...", "language_setting_description": "Válaszd ki preferált nyelvet", "last_seen": "Utoljára láttuk", "latest_version": "Legfrissebb Verzió", @@ -1071,14 +1129,17 @@ "local_network": "Helyi hálózat", "local_network_sheet_info": "Az alkalmazés ezen az URL címen fogja elérni a szervert, ha a megadott WiFi hálózathoz van csatlankozva", "location_permission": "Helymeghatározási engedély", - "location_permission_content": "Hálózatok automatikus váltásához az Immich-nek *mindenképpen* hozzá kell férnie a pontos helyzethez, hogy az alkalmazás le tudja kérni a Wi-Fi hálózat nevét", + "location_permission_content": "Hálózatok automatikus váltásához az Immich-nek szüksége van a pontos helymeghatározásra, hogy az alkalmazás le tudja kérni a Wi-Fi hálózat nevét", "location_picker_choose_on_map": "Válassz a térképen", "location_picker_latitude_error": "Érvényes szélességi kört írj be", "location_picker_latitude_hint": "Ide írd a szélességi kört", "location_picker_longitude_error": "Érvényes hosszúsági kört írj be", "location_picker_longitude_hint": "Ide írd a hosszúsági kört", + "lock": "Zárolás", + "locked_folder": "Zárolt mappa", "log_out": "Kijelentkezés", "log_out_all_devices": "Kijelentkezés Minden Eszközön", + "logged_in_as": "{user}-ként belépve", "logged_out_all_devices": "Minden eszköz kijelentkeztetve", "logged_out_device": "Eszköz kijelentkeztetve", "login": "Bejelentkezés", @@ -1093,7 +1154,7 @@ "login_form_err_invalid_url": "Érvénytelen cím", "login_form_err_leading_whitespace": "Az első karakter szóköz", "login_form_err_trailing_whitespace": "Az utolsó karakter szóköz", - "login_form_failed_get_oauth_server_config": "Nem sikerült az OAuth bejelentkezés. Ellenőrizd a szerver címét.", + "login_form_failed_get_oauth_server_config": "Nem sikerült az OAuth bejelentkezés. Ellenőrizd a szerver URL-t", "login_form_failed_get_oauth_server_disable": "OAuth bejelentkezés nem elérhető ezen a szerveren", "login_form_failed_login": "Hiba a bejelentkezés közben, ellenőrizd a szerver címét, az emailt és a jelszót", "login_form_handshake_exception": "SSL Kézfogási Hiba törént. Engedélyezd az önaláírt tanúsítvényokat a beállításokban, hogy ha önaláírt tanúsítványt használsz.", @@ -1145,6 +1206,9 @@ "map_settings_only_show_favorites": "Csak Kedvencek Mutatása", "map_settings_theme_settings": "Térkép Témája", "map_zoom_to_see_photos": "Kicsinyítsd, hogy láss fényképeket", + "mark_all_as_read": "Összes megjelölése olvasottként", + "mark_as_read": "Megjelölés olvasottként", + "marked_all_as_read": "Összes megjelölve olvasottként", "matches": "Azonosak", "media_type": "Médiatípus", "memories": "Emlékek", @@ -1169,6 +1233,12 @@ "month": "Hónap", "monthly_title_text_date_format": "y MMMM", "more": "Továbbiak", + "move": "Áthelyezés", + "move_off_locked_folder": "Zárolt mappából kivonás", + "move_to_locked_folder": "Áthelyezés a zárolt mappába", + "move_to_locked_folder_confirmation": "Ezek a képek és videók az összes albumból kikerülnek, és csak a zárolt mappából lesznek elérhetőek", + "moved_to_archive": "{count, plural, one {# Elem} other {# Elemek}} archiválva", + "moved_to_library": "{count, plural, one {# Elem} other {# Elemek}} másik könyvtárba költöztetve", "moved_to_trash": "Áthelyezve a lomtárba", "multiselect_grid_edit_date_time_err_read_only": "Csak-olvasható elem(ek) dátuma nem módosítható, ezért kihagyjuk", "multiselect_grid_edit_gps_err_read_only": "Csak-olvasható elem(ek) helye nem módosítható, ezért kihagyjuk", @@ -1184,6 +1254,7 @@ "new_password": "Új jelszó", "new_person": "Új személy", "new_pin_code": "Új PIN kód", + "new_pin_code_subtitle": "Ez az első alkalom hogy megnyitod a zárolt mappát. Hozz létre egy jelszót az oldal biztosítására", "new_user_created": "Új felhasználó létrehozva", "new_version_available": "ÚJ VERZIÓ ÉRHETŐ EL", "newest_first": "Legújabb először", @@ -1201,7 +1272,9 @@ "no_explore_results_message": "Tölts fel több képet, hogy böngészhesd a gyűjteményed.", "no_favorites_message": "Add hozzá a kedvencekhez, hogy gyorsan megtaláld a legjobb képeidet és videóidat", "no_libraries_message": "Hozz létre külső képtárat a fényképeid és videóid megtekintéséhez", + "no_locked_photos_message": "A zárolt mappában elhelyezett fotók és videók rejtettek, és nem jelennek meg a könyvtárad böngészése vagy keresése közben sem.", "no_name": "Nincs Név", + "no_notifications": "Nincsenek értesítések", "no_places": "Nincsenek helyek", "no_results": "Nincs találat", "no_results_description": "Próbálkozz szinonimákkal vagy általánosabb kulcsszavakkal", @@ -1210,6 +1283,7 @@ "not_selected": "Nincs kiválasztva", "note_apply_storage_label_to_previously_uploaded assets": "Megjegyzés: a korábban feltöltött elemek Tárhely Címkézéséhez futtasd a(z)", "notes": "Megjegyzések", + "nothing_here_yet": "Itt még nincs semmi", "notification_permission_dialog_content": "Az értesítések bekapcsolásához a Beállítások menüben válaszd ki az Engedélyezés-t.", "notification_permission_list_tile_content": "Értesítések engedélyezése.", "notification_permission_list_tile_enable_button": "Értesítések Bekapcsolása", @@ -1217,15 +1291,21 @@ "notification_toggle_setting_description": "Email értesítések engedélyezése", "notifications": "Értesítések", "notifications_setting_description": "Értesítések kezelése", + "oauth": "OAuth", "official_immich_resources": "Hivatalos Immich Források", + "offline": "Offline", "ok": "Rendben", "oldest_first": "Legrégebbi először", "on_this_device": "Ezen az eszközön", "onboarding": "Első lépések", - "onboarding_privacy_description": "Az alábbi (nem kötelező) funkciók külsős szolgáltatásokon alapulnak és bármikor kikapcsolhatóak az adminisztrációs beállításokban.", + "onboarding_locale_description": "Válaszd ki a preferált nyelved. Ezt később a beállításokban bármikor módosíthatod.", + "onboarding_privacy_description": "Az alábbi (nem kötelező) funkciók külsős szolgáltatásokon alapulnak és bármikor kikapcsolhatóak a beállításokban.", "onboarding_theme_description": "Válassz egy színtémát. Ezt bármikor megváltoztathatod a beállításokban.", + "onboarding_user_welcome_description": "Kezdjünk bele!", "onboarding_welcome_user": "Üdvözöllek {user}", + "online": "Online", "only_favorites": "Csak kedvencek", + "open": "Nyitva", "open_in_map_view": "Megnyitás térkép nézetben", "open_in_openstreetmap": "Megnyitás OpenStreetMap-ben", "open_the_search_filters": "Keresési szűrők megnyitása", @@ -1238,6 +1318,7 @@ "other_variables": "Egyéb változók", "owned": "Tulajdonos", "owner": "Tulajdonos", + "partner": "Partner", "partner_can_access": "{partner} hozzáférhet", "partner_can_access_assets": "Minden fényképed és videód, kivéve az Archiváltak és a Töröltek", "partner_can_access_location": "A helyszín, ahol a fotókat készítették", @@ -1247,7 +1328,7 @@ "partner_page_no_more_users": "Nincs több hozzáadható felhasználó", "partner_page_partner_add_failed": "Partner hozzáadása sikertelen", "partner_page_select_partner": "Partner kiválasztása", - "partner_page_shared_to_title": "Megosztva: ", + "partner_page_shared_to_title": "Megosztva", "partner_page_stop_sharing_content": "{partner} nem fog többé hozzáférni a fotóidhoz.", "partner_sharing": "Partner Megosztás", "partners": "Partnerek", @@ -1277,6 +1358,8 @@ "permanently_delete_assets_prompt": "Biztos, hogy véglegesen törölni {count, plural, one {szeretnéd ezt az elemet} other {szeretnél # elemet}}? Ez el fogja távolítani az {count, plural, one {elemet az albumokból, amikben szerepel} other {elemeket az albumokból, amikben szerepelnek}}.", "permanently_deleted_asset": "Elem véglegesen törölve", "permanently_deleted_assets_count": "{count, plural, other {# elem}} véglegesen törölve", + "permission": "Jogosultság", + "permission_empty": "A jogosultság nem hagyható üresen", "permission_onboarding_back": "Vissza", "permission_onboarding_continue_anyway": "Folytatás mindenképp", "permission_onboarding_get_started": "Vágjunk bele", @@ -1284,7 +1367,7 @@ "permission_onboarding_permission_denied": "Hozzáférés megtagadva. Az Immich használatához engedélyezni kell a fotó és videó hozzáférést a Beállításokban.", "permission_onboarding_permission_granted": "Hozzáférés engedélyezve! Minden készen áll.", "permission_onboarding_permission_limited": "Korlátozott hozzáférés. Ha szeretnéd, hogy az Immich a teljes galéria gyűjteményedet mentse és kezelje, akkor a Beállításokban engedélyezd a fotó és videó jogosultságokat.", - "permission_onboarding_request": "Engedélyezni kell, hogy az Immich hozzáférjen a képeidhez és videóidhoz", + "permission_onboarding_request": "Engedélyezni kell, hogy az Immich hozzáférjen a képeidhez és videóidhoz.", "person": "Személy", "person_birthdate": "Született: {date}", "person_hidden": "{name}{hidden, select, true { (rejtett)} other {}}", @@ -1304,19 +1387,25 @@ "play_memories": "Emlékek lejátszása", "play_motion_photo": "Mozgókép lejátszása", "play_or_pause_video": "Videó elindítása vagy megállítása", + "port": "Port", "preferences_settings_subtitle": "Alkalmazásbeállítások kezelése", "preferences_settings_title": "Beállítások", "preset": "Sablon", "preview": "Előnézet", "previous": "Előző", "previous_memory": "Előző emlék", - "previous_or_next_photo": "Előző vagy következő fotó", + "previous_or_next_day": "Nap előre/hátra", + "previous_or_next_month": "Hónap előre/hátra", + "previous_or_next_photo": "Fotó előre/hátra", + "previous_or_next_year": "Év előre/hátra", "primary": "Elsődleges", "privacy": "Magánszféra", + "profile": "Profil", "profile_drawer_app_logs": "Naplók", "profile_drawer_client_out_of_date_major": "A mobilalkalmazás elavult. Kérjük, frissítsd a legfrisebb főverzióra.", "profile_drawer_client_out_of_date_minor": "A mobilalkalmazás elavult. Kérjük, frissítsd a legfrisebb alverzióra.", "profile_drawer_client_server_up_to_date": "A Kliens és a Szerver is naprakész", + "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "A szerver elavult. Kérjük, frissítsd a legfrisebb főverzióra.", "profile_drawer_server_out_of_date_minor": "A szerver elavult. Kérjük, frissítsd a legfrisebb alverzióra.", "profile_image_of_user": "{user} profilképe", @@ -1370,6 +1459,8 @@ "recent_searches": "Legutóbbi keresések", "recently_added": "Nemrég hozzáadott", "recently_added_page_title": "Nemrég Hozzáadott", + "recently_taken": "Nemrég készített", + "recently_taken_page_title": "Nemrég készített", "refresh": "Frissítés", "refresh_encoded_videos": "Átkódolt videók frissítése", "refresh_faces": "Arcok frissítése", @@ -1389,9 +1480,12 @@ "remove_deleted_assets": "Törölt Elemek Eltávolítása", "remove_from_album": "Eltávolítás az albumból", "remove_from_favorites": "Eltávolítás a kedvencekből", + "remove_from_locked_folder": "Eltávolítás a zárolt mappából", + "remove_from_locked_folder_confirmation": "Biztosan ki szeretnéd venni ezeket a fotókat és videókat a zárolt mappából? Láthatóak lesznek a könyvtáradban.", "remove_from_shared_link": "Eltávolítás a megosztott linkből", "remove_memory": "Emlék eltávolítása", "remove_photo_from_memory": "Kép eltávolítása az emlékből", + "remove_tag": "Címke eltávolítása", "remove_url": "URL eltávolítása", "remove_user": "Felhasználó eltávolítása", "removed_api_key": "API Kulcs eltávolítva: {name}", @@ -1485,7 +1579,7 @@ "search_result_page_new_search_hint": "Új Keresés", "search_settings": "Keresési beállítások", "search_state": "Megye/Állam keresése...", - "search_suggestion_list_smart_search_hint_1": "Az intelligens keresés alapértelmezetten be van kapcsolva, metaadatokat így kereshetsz: ", + "search_suggestion_list_smart_search_hint_1": "Az intelligens keresés alapértelmezetten be van kapcsolva, metaadatokat így kereshetsz ", "search_suggestion_list_smart_search_hint_2": "m:keresési-kifejezés", "search_tags": "Címkék keresése...", "search_timezone": "Időzóna keresése...", @@ -1517,6 +1611,7 @@ "server_info_box_server_url": "Szerver Címe", "server_offline": "Szerver Nem Elérhető", "server_online": "Szerver Elérhető", + "server_privacy": "Szerver biztonság", "server_stats": "Szerver Statisztikák", "server_version": "Szerver Verzió", "set": "Beállít", @@ -1526,6 +1621,7 @@ "set_date_of_birth": "Születési dátum beállítása", "set_profile_picture": "Profilkép beállítása", "set_slideshow_to_fullscreen": "Diavetítés teljes képernyőre állítása", + "set_stack_primary_asset": "Beállítás elsődleges elemként", "setting_image_viewer_help": "Az Elem Megjelenítő először a kis bélyegképet tölti be, aztán a közepes méretű előnézetet (ha elérhető), végül az eredetit (ha elérhető).", "setting_image_viewer_original_subtitle": "Engedélyezi az eredeti teljes felbontású kép betöltését (nagy!). Kikapcsolva csökkenti az adathasználatot (a neten és az eszköz gyorsítótárán is).", "setting_image_viewer_original_title": "Eredeti kép betöltése", @@ -1556,6 +1652,7 @@ "share_add_photos": "Fotók hozzáadása", "share_assets_selected": "{count} kiválasztva", "share_dialog_preparing": "Előkészítés...", + "share_link": "Link megosztása", "shared": "Megosztva", "shared_album_activities_input_disable": "Hozzászólások kikapcsolva", "shared_album_activity_remove_content": "Törölni szeretnéd ezt a tevékenységet?", @@ -1595,6 +1692,7 @@ "shared_link_expires_second": "{count} másodperc múlva lejár", "shared_link_expires_seconds": "{count} másodperc múlva lejár", "shared_link_individual_shared": "Egyénileg megosztva", + "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Megosztott linkek kezelése", "shared_link_options": "Megosztott link beállításai", "shared_links": "Megosztott linkek", @@ -1656,6 +1754,7 @@ "stack_select_one_photo": "Válassz egy fő képet a csoportból", "stack_selected_photos": "Kiválasztott fényképek csoportosítása", "stacked_assets_count": "{count, plural, other {# elem}} csoportosítva", + "stacktrace": "Hibaleírás", "start": "Elindít", "start_date": "Kezdő dátum", "state": "Megye/Állam", @@ -1666,6 +1765,7 @@ "stop_sharing_photos_with_user": "Fényképeid megosztásának megszüntetése ezzel a felhasználóval", "storage": "Tárhely", "storage_label": "Tárhely címke", + "storage_quota": "Tárhely kvóta", "storage_usage": "{used}/{available} használatban", "submit": "Beküldés", "suggestions": "Javaslatok", @@ -1693,11 +1793,11 @@ "theme_selection_description": "A böngésző beállításának megfelelően automatikusan használjon világos vagy sötét témát", "theme_setting_asset_list_storage_indicator_title": "Tárhely ikon mutatása az elemeken", "theme_setting_asset_list_tiles_per_row_title": "Elemek száma soronként ({count})", - "theme_setting_colorful_interface_subtitle": "Alapértelmezett szín használata a háttérben lévő felületekhez", + "theme_setting_colorful_interface_subtitle": "Alapértelmezett szín használata a háttérben lévő felületekhez.", "theme_setting_colorful_interface_title": "Színes felhasználói felület", "theme_setting_image_viewer_quality_subtitle": "Részletes képmegjelenítő minőségének beállítása", "theme_setting_image_viewer_quality_title": "Képmegjelenítő minősége", - "theme_setting_primary_color_subtitle": "Válassz egy színt az alapértelmezett műveletekhez és kiemelésekhez", + "theme_setting_primary_color_subtitle": "Válassz egy színt az alapértelmezett műveletekhez és kiemelésekhez.", "theme_setting_primary_color_title": "Alapértelmezett szín", "theme_setting_system_primary_color_title": "Rendszerszínek használata", "theme_setting_system_theme_switch": "Automatikus (követi a rendszer témáját)", @@ -1737,6 +1837,7 @@ "unable_to_setup_pin_code": "Sikertelen PIN kód beállítás", "unarchive": "Archívumból kivesz", "unarchived_count": "{count, plural, other {# elem kivéve az archívumból}}", + "undo": "Visszavonás", "unfavorite": "Kedvenc közül kivesz", "unhide_person": "Nem rejtett személy", "unknown": "Ismeretlen", @@ -1756,6 +1857,7 @@ "unstack": "Csoport Szétszedése", "unstacked_assets_count": "{count, plural, other {# elemből}} álló csoport szétszedve", "up_next": "Következik", + "updated_at": "Frissített", "updated_password": "Jelszó megváltoztatva", "upload": "Feltöltés", "upload_concurrency": "Párhuzamos feltöltés", @@ -1770,14 +1872,17 @@ "upload_success": "Feltöltés sikeres, frissítsd az oldalt az újonnan feltöltött elemek megtekintéséhez.", "upload_to_immich": "Feltöltés Immich-be ({count})", "uploading": "Feltöltés folyamatban", + "url": "URL", "usage": "Használat", "use_current_connection": "Jelenlegi kapcsolat használata", "use_custom_date_range": "Szabadon megadott időintervallum használata", "user": "Felhasználó", + "user_has_been_deleted": "Ez a felhasználó törlésre került.", "user_id": "Felhasználó azonosítója", "user_liked": "{user} felhasználónak {type, select, photo {ez a fénykép} video {ez a videó} asset {ez az elem} other {ez}} tetszik", "user_pin_code_settings": "PIN kód", "user_pin_code_settings_description": "PIN kód kezelése", + "user_privacy": "Felhasználói biztonság", "user_purchase_settings": "Megvásárlás", "user_purchase_settings_description": "Vásárlás kezelése", "user_role_set": "{user} felhasználónak {role} jogkör biztosítása", @@ -1822,12 +1927,12 @@ "week": "Hét", "welcome": "Üdvözlünk", "welcome_to_immich": "Üdvözöl az Immich", - "wifi_name": "WiFi Neve", + "wifi_name": "Wi-Fi Neve", "wrong_pin_code": "Hibás PIN kód", "year": "Év", "years_ago": "{years, plural, one {# évvel} other {# évvel}} ezelőtt", "yes": "Igen", "you_dont_have_any_shared_links": "Nincsenek megosztott linkjeid", - "your_wifi_name": "A WiFi hálózatod neve", + "your_wifi_name": "A Wi-Fi hálózatod neve", "zoom_image": "Kép Nagyítása" } diff --git a/i18n/id.json b/i18n/id.json index 4d15ef01e9..ff5e2524e2 100644 --- a/i18n/id.json +++ b/i18n/id.json @@ -34,6 +34,7 @@ "added_to_favorites_count": "Ditambahkan {count, number} ke favorit", "admin": { "add_exclusion_pattern_description": "Tambahkan pola pengecualian. Glob menggunakan *, **, dan ? didukung. Untuk mengabaikan semua berkas dalam direktori apa pun bernama \"Raw\", gunakan \"**/Raw/**\". Untuk mengabaikan semua berkas berakhiran dengan \".tif\", gunakan \"**/*.tif\". Untuk mengabaikan jalur absolut, gunakan \"/jalur/untuk/diabaikan/**\".", + "admin_user": "Pengguna Admin", "asset_offline_description": "Aset pustaka eksternal ini tidak ada di diska dan telah dipindahkan ke tempat sampah. Jika berkasnya dipindah dalam pustaka, periksa lini masa Anda untuk aset baru yang cocok. Untuk memulihkan aset ini, pastikan jalur berkas di bawah dapat diakses oleh Immich dan pindai pustaka.", "authentication_settings": "Pengaturan Autentikasi", "authentication_settings_description": "Kelola kata sandi, OAuth, dan pengaturan autentikasi lainnya", @@ -44,7 +45,7 @@ "backup_database_enable_description": "Aktifkan pencadangan basis data", "backup_keep_last_amount": "Jumlah cadangan untuk disimpan", "backup_settings": "Pengaturan Pencadangan Basis Data", - "backup_settings_description": "Kelola pengaturan pencadangan basis data. Catatan: Tugas ini tidak dipantau dan Anda tidak akan diberi tahu jika ada kesalahan.", + "backup_settings_description": "Kelola pengaturan pencadangan basis data.", "cleared_jobs": "Tugas terselesaikan untuk: {job}", "config_set_by_file": "Konfigurasi saat ini ditetapkan oleh berkas konfigurasi", "confirm_delete_library": "Apakah Anda yakin ingin menghapus pustaka {library}?", @@ -165,12 +166,18 @@ "metadata_settings_description": "Kelola pengaturan metadata", "migration_job": "Migrasi", "migration_job_description": "Migrasikan gambar kecil untuk aset dan wajah ke struktur folder terkini", + "nightly_tasks_cluster_faces_setting_description": "Mulai pengenalan wajah pada semua wajah yang baru saja terdeteksi", + "nightly_tasks_cluster_new_faces_setting": "Kelompokkan semua wajah baru", + "nightly_tasks_database_cleanup_setting": "Tugas pembersihan basis data", + "nightly_tasks_generate_memories_setting": "Buat kenang-kenangan", + "nightly_tasks_generate_memories_setting_description": "Buat kenang-kenangan baru dari berbagai aset", + "nightly_tasks_start_time_setting": "Waktu mulai", "no_paths_added": "Tidak ada jalur yang ditambahkan", "no_pattern_added": "Tidak ada pola yang ditambahkan", "note_apply_storage_label_previous_assets": "Catatan: Untuk menerapkan Label Penyimpanan untuk aset yang telah diunggah sebelumnya, jalankan", "note_cannot_be_changed_later": "CATATAN: Ini tidak akan dapat diubah lagi!", "notification_email_from_address": "Dari alamat", - "notification_email_from_address_description": "Alamat surel pengirim, misalnya: \"Server Foto Immich \"", + "notification_email_from_address_description": "Alamat surel pengirim, misalnya: \"Server Foto Immich \". Pastikan untuk menggunakan alamat yang diizinkan untuk mengirim email", "notification_email_host_description": "Hos server surel (mis. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Abaikan eror sertifikat", "notification_email_ignore_certificate_errors_description": "Abaikan eror validasi sertifikat TLS (tidak disarankan)", @@ -195,6 +202,7 @@ "oauth_mobile_redirect_uri": "URI pengalihan ponsel", "oauth_mobile_redirect_uri_override": "Penimpaan URI penerusan ponsel", "oauth_mobile_redirect_uri_override_description": "Aktifkan ketika provider OAuth tidak mengizinkan tautan mobile, seperti ''{callback}''", + "oauth_role_claim_description": "Secara otomatis memberikan akses admin berdasarkan keberadaan klaim ini. Klaim dapat berupa \"user\" atau \"admin\".", "oauth_settings": "OAuth", "oauth_settings_description": "Kelola pengaturan log masuk OAuth", "oauth_settings_more_details": "Untuk detail lanjut tentang fitur ini, lihat docs.", @@ -243,6 +251,7 @@ "storage_template_migration_info": "Templat penyimpanan akan mengubah semua ekstensi ke huruf kecil. Perubahan templat hanya akan diterapkan pada aset baru. Untuk menerapkan templat pada setiap aset yang sebelumnya telah diunggah, jalankan {job}.", "storage_template_migration_job": "Tugas Migrasi Templat Ruang Penyimpanan", "storage_template_more_details": "Untuk detail lebih lanjut tentang fitur ini, pergi ke Templat Penyimpanan dan kekurangannya", + "storage_template_onboarding_description_v2": "Saat diaktifkan, fitur ini akan mengatur file secara otomatis berdasarkan templat yang ditentukan pengguna. Untuk informasi selengkapnya, silakan lihat dokumentasi.", "storage_template_path_length": "Batas panjang jalur: {length, number}{limit, number}", "storage_template_settings": "Templat Penyimpanan", "storage_template_settings_description": "Kelola struktur folder dan nama berkas dari aset yang diunggah", @@ -257,7 +266,7 @@ "template_email_update_album": "Perbarui Templat Album", "template_email_welcome": "Templat surel selamat datang", "template_settings": "Templat Notifikasi", - "template_settings_description": "Kelola templat kustom untuk notifikasi.", + "template_settings_description": "Kelola templat kustom untuk notifikasi", "theme_custom_css_settings": "CSS Kustom", "theme_custom_css_settings_description": "CSS memungkinkan desain Immich untuk diubah.", "theme_settings": "Pengaturan Tema", @@ -355,13 +364,20 @@ "admin_password": "Kata Sandi Admin", "administration": "Administrasi", "advanced": "Tingkat lanjut", + "advanced_settings_beta_timeline_subtitle": "Coba pengalaman aplikasi baru", + "advanced_settings_beta_timeline_title": "Garis waktu Beta", "advanced_settings_enable_alternate_media_filter_subtitle": "Gunakan opsi ini untuk menyaring media saat sinkronisasi berdasarkan kriteria alternatif. Hanya coba ini dengan aplikasi mendeteksi semua album.", "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTAL] Gunakan saringan sinkronisasi album perangkat alternatif", "advanced_settings_log_level_title": "Tingkat log: {level}", "advanced_settings_prefer_remote_subtitle": "Beberapa perangkat tidak dapat memuat gambar kecil dengan cepat. Menyalakan ini akan memuat gambar kecil dari server.", "advanced_settings_prefer_remote_title": "Prioritaskan gambar dari server", + "advanced_settings_proxy_headers_subtitle": "Tentukan header proxy yang harus dikirim Immich dengan setiap permintaan jaringan", + "advanced_settings_self_signed_ssl_subtitle": "Melewati verifikasi sertifikat SSL untuk titik akhir server. Diperlukan untuk sertifikat yang ditandatangani sendiri.", + "advanced_settings_self_signed_ssl_title": "Izinkan sertifikat SSL yang ditandatangani sendiri", "advanced_settings_sync_remote_deletions_subtitle": "Hapus atau pulihkan aset pada perangkat ini secara otomatis ketika tindakan dilakukan di web", "advanced_settings_sync_remote_deletions_title": "Sinkronisasi penghapusan jarak jauh [EKSPERIMENTAL]", + "advanced_settings_tile_subtitle": "Pengaturan pengguna tingkat lanjut", + "advanced_settings_troubleshooting_subtitle": "Aktifkan fitur tambahan untuk pemecahan masalah", "age_months": "Umur {months, plural, one {# bulan} other {# bulan}}", "age_year_months": "Umur 1 tahun, {months, plural, one {# bulan} other {# bulan}}", "age_years": "{years, plural, other {Umur #}}", @@ -446,7 +462,6 @@ "assets": "Aset", "assets_added_count": "{count, plural, one {# aset} other {# aset}} ditambahkan", "assets_added_to_album_count": "Ditambahkan {count, plural, one {# aset} other {# aset}} ke album", - "assets_added_to_name_count": "Ditambahkan {count, plural, one {# aset} other {# aset}} ke {hasName, select, true {{name}} other {album baru}}", "assets_count": "{count, plural, one {# aset} other {# aset}}", "assets_deleted_permanently": "{count} aset dihapus secara permanen", "assets_deleted_permanently_from_server": "{count} aset dihapus secara permanen dari server Immich", diff --git a/i18n/it.json b/i18n/it.json index 07e19736a7..5e12bdfc97 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -87,7 +87,7 @@ "image_settings": "Impostazioni delle immagini", "image_settings_description": "Gestisci qualità e risoluzione delle immagini generate", "image_thumbnail_description": "Miniatura piccola senza metadati, utilizzata durante la visualizzazione di gruppi di foto come la sequenza temporale principale", - "image_thumbnail_quality_description": "Qualità delle miniature da 1 a 100. Un valore più alto è migliore, ma produce file più grandi e può ridurre la reattività dell'app.", + "image_thumbnail_quality_description": "Qualità delle anteprime da 1 a 100. Un valore più alto è migliore ma produce file più grandi e può ridurre la reattività dell'app.", "image_thumbnail_title": "Impostazioni della copertina", "job_concurrency": "Concorrenza {job}", "job_created": "Processo creato", @@ -105,7 +105,7 @@ "library_scanning_enable_description": "Attiva la scansione periodica della libreria", "library_settings": "Libreria Esterna", "library_settings_description": "Gestisci le impostazioni della libreria esterna", - "library_tasks_description": "Scansiona le librerie esterne per i nuovi aggiornamenti", + "library_tasks_description": "Scansiona le librerie esterne per risorse nuove o modificate", "library_watching_enable_description": "Osserva le librerie esterne per cambiamenti", "library_watching_settings": "Osserva librerie (SPERIMENTALE)", "library_watching_settings_description": "Osserva automaticamente i cambiamenti dei file", @@ -121,7 +121,7 @@ "machine_learning_enabled": "Attiva machine learning", "machine_learning_enabled_description": "Se disabilitato, tutte le funzioni di ML saranno disabilitate ignorando le importazioni sottostanti.", "machine_learning_facial_recognition": "Riconoscimento Facciale", - "machine_learning_facial_recognition_description": "Rileva, riconosci, e raggruppa facce nelle immagini", + "machine_learning_facial_recognition_description": "Rileva, riconosci e raggruppa volti nelle immagini", "machine_learning_facial_recognition_model": "Modello di riconoscimento facciale", "machine_learning_facial_recognition_model_description": "I modelli sono mostrati in ordine decrescente in base alla dimensione. I modelli più grandi sono più lenti e utilizzano più memoria, però producono risultati migliori. Nota che devi ri-eseguire il processo di rilevamento facciale per tutte le immagini quando cambi il modello.", "machine_learning_facial_recognition_setting": "Attiva riconoscimento facciale", @@ -156,16 +156,30 @@ "map_settings": "Impostazioni Mappa e Posizione", "map_settings_description": "Gestisci impostazioni mappa", "map_style_description": "URL per un tema della mappa style.json", - "memory_cleanup_job": "pulizia memoria", - "memory_generate_job": "Generazione della memoria", + "memory_cleanup_job": "Pulizia dei vecchi Ricordi", + "memory_generate_job": "Generazione dei Ricordi", "metadata_extraction_job": "Estrazione Metadata", - "metadata_extraction_job_description": "Estrai informazioni dai metadati di ciascun asset, ad esempio coordinate GPS, volti e risoluzione", + "metadata_extraction_job_description": "Estrai informazioni dai metadati di ciascuna risorsa, come coordinate GPS, volti e risoluzione", "metadata_faces_import_setting": "Abilita l'importazione dei volti", "metadata_faces_import_setting_description": "Importa i volti dai dati EXIF dell'immagine e dai file sidecar", "metadata_settings": "Impostazioni Metadati", "metadata_settings_description": "Gestisci le impostazioni dei metadati", "migration_job": "Migrazione", "migration_job_description": "Migra le anteprime per gli asset e volti alla struttura di cartelle più recente", + "nightly_tasks_cluster_faces_setting_description": "Avvia riconoscimento facciale sui volti appena rilevati", + "nightly_tasks_cluster_new_faces_setting": "Raggruppa nuovi volti", + "nightly_tasks_database_cleanup_setting": "Processi di pulizia del database", + "nightly_tasks_database_cleanup_setting_description": "Ripulisci il database da file vecchi e scaduti", + "nightly_tasks_generate_memories_setting": "Genera ricordi", + "nightly_tasks_generate_memories_setting_description": "Genera nuovi ricordi a partire dalle risorse", + "nightly_tasks_missing_thumbnails_setting": "Genera anteprime mancanti", + "nightly_tasks_missing_thumbnails_setting_description": "Metti in coda le risorse senza miniatura per la generazione delle anteprime", + "nightly_tasks_settings": "Impostazioni delle attività notturne", + "nightly_tasks_settings_description": "Gestisci attività notturne", + "nightly_tasks_start_time_setting": "Tempo di avvio", + "nightly_tasks_start_time_setting_description": "Il tempo in cui il server fa partire le attività notturne", + "nightly_tasks_sync_quota_usage_setting": "Sincronizza la quota di utilizzo", + "nightly_tasks_sync_quota_usage_setting_description": "Aggiorna la quota di spazio dell'utente in base all'utilizzo corrente", "no_paths_added": "Nessun percorso aggiunto", "no_pattern_added": "Nessun pattern aggiunto", "note_apply_storage_label_previous_assets": "Nota: Per assegnare l'etichetta storage ad asset precedentemente caricati, esegui", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "URI reindirizzamento mobile", "oauth_mobile_redirect_uri_override": "Sovrascrivi URI reindirizzamento cellulare", "oauth_mobile_redirect_uri_override_description": "Abilita quando il gestore OAuth non consente un URL come ''{callback}''", + "oauth_role_claim": "Claim del ruolo", + "oauth_role_claim_description": "Concedi automaticamente l'accesso come amministratore in base alla presenza di questo claim. Il claim può essere 'user' o 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Gestisci impostazioni di login OAuth", "oauth_settings_more_details": "Per più dettagli riguardo a questa funzionalità, consulta la documentazione.", @@ -264,8 +280,8 @@ "theme_custom_css_settings_description": "I Cascading Style Sheets (CSS) permettono di personalizzare l'interfaccia di Immich.", "theme_settings": "Impostazioni Tema", "theme_settings_description": "Gestisci la personalizzazione dell'interfaccia web di Immich", - "thumbnail_generation_job": "Generazione Miniature", - "thumbnail_generation_job_description": "Genera miniature grandi, piccole e sfocate per ogni asset, oltre a miniature per ogni persona", + "thumbnail_generation_job": "Genera Anteprime", + "thumbnail_generation_job_description": "Genera anteprime grandi, piccole e sfocate per ogni asset, oltre a miniature per ogni persona", "transcoding_acceleration_api": "API di accelerazione", "transcoding_acceleration_api_description": "L'API che interagirà con il tuo dispositivo per accelerare la transcodifica. Questa impostazione è \"best effort\": ripiegherà sulla transcodifica software in caso di fallimento. VP9 potrebbe funzionare o meno a seconda del tuo hardware.", "transcoding_acceleration_nvenc": "NVENC (richiede GPU NVIDIA)", @@ -357,25 +373,27 @@ "admin_password": "Password Amministratore", "administration": "Amministrazione", "advanced": "Avanzate", + "advanced_settings_beta_timeline_subtitle": "Prova la nuova esperienza dell'app", + "advanced_settings_beta_timeline_title": "Timeline beta", "advanced_settings_enable_alternate_media_filter_subtitle": "Usa questa opzione per filtrare i contenuti multimediali durante la sincronizzazione in base a criteri alternativi. Prova questa opzione solo se riscontri problemi con il rilevamento di tutti gli album da parte dell'app.", "advanced_settings_enable_alternate_media_filter_title": "[SPERIMENTALE] Usa un filtro alternativo per la sincronizzazione degli album del dispositivo", "advanced_settings_log_level_title": "Livello log: {level}", - "advanced_settings_prefer_remote_subtitle": "Alcuni dispositivi sono molto lenti a caricare le anteprime delle immagini dal dispositivo. Attivare questa impostazione per caricare invece le immagini remote.", + "advanced_settings_prefer_remote_subtitle": "Alcuni dispositivi sono molto lenti a caricare le anteprime delle immagini locali. Attivare questa impostazione per caricare invece le immagini remote.", "advanced_settings_prefer_remote_title": "Preferisci immagini remote", "advanced_settings_proxy_headers_subtitle": "Definisci gli header per i proxy che Immich dovrebbe inviare con ogni richiesta di rete", "advanced_settings_proxy_headers_title": "Header Proxy", "advanced_settings_self_signed_ssl_subtitle": "Salta la verifica dei certificati SSL del server. Richiesto con l'uso di certificati self-signed.", "advanced_settings_self_signed_ssl_title": "Consenti certificati SSL self-signed", - "advanced_settings_sync_remote_deletions_subtitle": "Rimuovi o ripristina automaticamente un elemento su questo dispositivo se l'azione è stata fatta via web", + "advanced_settings_sync_remote_deletions_subtitle": "Rimuovi o ripristina automaticamente un elemento su questo dispositivo quando l'azione è stata fatta via web", "advanced_settings_sync_remote_deletions_title": "Sincronizza le cancellazioni remote [SPERIMENTALE]", - "advanced_settings_tile_subtitle": "Impostazioni aggiuntive utenti", + "advanced_settings_tile_subtitle": "Impostazioni avanzate dell'utente", "advanced_settings_troubleshooting_subtitle": "Attiva funzioni addizionali per la risoluzione dei problemi", "advanced_settings_troubleshooting_title": "Risoluzione problemi", "age_months": "Età {months, plural, one {# mese} other {# mesi}}", "age_year_months": "Età 1 anno, {months, plural, one {# mese} other {# mesi}}", "age_years": "{years, plural, one {# anno} other {# anni}}", "album_added": "Album aggiunto", - "album_added_notification_setting_description": "Ricevi una notifica email quando sei aggiunto a un album condiviso", + "album_added_notification_setting_description": "Ricevi una notifica email quando sei aggiunto ad un album condiviso", "album_cover_updated": "Copertina dell'album aggiornata", "album_delete_confirmation": "Sei sicuro di voler cancellare l'album {album}?", "album_delete_confirmation_description": "Se l'album è condiviso gli altri utenti perderanno l'accesso.", @@ -394,32 +412,32 @@ "album_user_left": "{album} abbandonato", "album_user_removed": "Utente {user} rimosso", "album_viewer_appbar_delete_confirm": "Sei sicuro di voler rimuovere questo album dal tuo account?", - "album_viewer_appbar_share_err_delete": "Impossibile eliminare l'album", - "album_viewer_appbar_share_err_leave": "Impossibile lasciare l'album", - "album_viewer_appbar_share_err_remove": "Ci sono problemi nel rimuovere oggetti dall'album", - "album_viewer_appbar_share_err_title": "Impossibile cambiare il titolo dell'album", + "album_viewer_appbar_share_err_delete": "Non è stato possibile eliminare l'album", + "album_viewer_appbar_share_err_leave": "Non è stato possibile lasciare l'album", + "album_viewer_appbar_share_err_remove": "Ci sono problemi nel rimuovere elementi dall'album", + "album_viewer_appbar_share_err_title": "Non è stato possibile cambiare il titolo dell'album", "album_viewer_appbar_share_leave": "Lascia album", "album_viewer_appbar_share_to": "Condividi a", "album_viewer_page_share_add_users": "Aggiungi utenti", "album_with_link_access": "Permetti a chiunque possieda il link di visualizzare le foto e le persone dell'album.", "albums": "Album", "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Album}}", - "albums_default_sort_order": "Ordinamento album predefinito", - "albums_default_sort_order_description": "Ordine iniziale degli asset alla creazione di nuovi album.", - "albums_feature_description": "Collezione di asset che possono essere condivisi con altri utenti.", + "albums_default_sort_order": "Ordinamento predefinito degli album", + "albums_default_sort_order_description": "Ordine iniziale degli elementi alla creazione di nuovi album.", + "albums_feature_description": "Raggruppamento di elementi che possono essere condivisi con altri utenti.", "all": "Tutti", "all_albums": "Tutti gli album", "all_people": "Tutte le persone", "all_videos": "Tutti i video", "allow_dark_mode": "Permetti Tema Scuro", - "allow_edits": "Permetti Modifiche", + "allow_edits": "Permetti modifiche", "allow_public_user_to_download": "Permetti agli utenti pubblici di scaricare", "allow_public_user_to_upload": "Permetti agli utenti pubblici di caricare", "alt_text_qr_code": "Immagine QR", "anti_clockwise": "Senso anti-orario", "api_key": "Chiave API", - "api_key_description": "Il valore verrà mostrato solo una volta. Assicurati di copiarlo prima di chiudere la finestra.", - "api_key_empty": "Il nome della chiave API non può essere vuoto", + "api_key_description": "Questo valore verrà mostrato una sola volta. Assicurati di copiarlo prima di chiudere la finestra.", + "api_key_empty": "Il nome della chiave API non dovrebbe essere vuoto", "api_keys": "Chiavi API", "app_bar_signout_dialog_content": "Sei sicuro di volerti disconnettere?", "app_bar_signout_dialog_ok": "Si", @@ -427,8 +445,9 @@ "app_settings": "Impostazioni Applicazione", "appears_in": "Compare in", "archive": "Archivio", + "archive_action_prompt": "Aggiunti {count} elementi all'Archivio", "archive_or_unarchive_photo": "Archivia o ripristina foto", - "archive_page_no_archived_assets": "Nessuna oggetto archiviato", + "archive_page_no_archived_assets": "Non è stato trovato nessun elemento archiviato", "archive_page_title": "Archivio ({count})", "archive_size": "Dimensioni Archivio", "archive_size_description": "Imposta le dimensioni dell'archivio per i download (in GiB)", @@ -440,42 +459,41 @@ "asset_action_share_err_offline": "Non è possibile recuperare le risorse offline, azione ignorata", "asset_added_to_album": "Aggiunto all'album", "asset_adding_to_album": "Aggiungendo all'album…", - "asset_description_updated": "La descrizione del media è stata aggiornata", + "asset_description_updated": "La descrizione dell'elemento è stata aggiornata", "asset_filename_is_offline": "Il media {filename} è offline", "asset_has_unassigned_faces": "Il media ha dei volti non categorizzati", "asset_hashing": "Hashing in corso …", "asset_list_group_by_sub_title": "Raggruppa per", "asset_list_layout_settings_dynamic_layout_title": "Layout dinamico", "asset_list_layout_settings_group_automatically": "Automatico", - "asset_list_layout_settings_group_by": "Raggruppa le risorse per", + "asset_list_layout_settings_group_by": "Raggruppa gli elementi per", "asset_list_layout_settings_group_by_month_day": "Mese + giorno", "asset_list_layout_sub_title": "Layout", - "asset_list_settings_subtitle": "Impostazion del layout della griglia delle foto", + "asset_list_settings_subtitle": "Impostazioni del layout della griglia delle foto", "asset_list_settings_title": "Griglia foto", - "asset_offline": "Risorsa Offline", - "asset_offline_description": "Questo media non è stato trovato nel disco. Contatta il tuo amministratore di Immich per assistenza.", - "asset_restored_successfully": "Asset ripristinato con successo", + "asset_offline": "Elemento Offline", + "asset_offline_description": "Questo elemento esterno non viene più trovato sul disco. Contatta il tuo amministratore di Immich per assistenza.", + "asset_restored_successfully": "Elemento ripristinato con successo", "asset_skipped": "Saltato", "asset_skipped_in_trash": "Nel cestino", "asset_uploaded": "Caricato", "asset_uploading": "Caricamento…", - "asset_viewer_settings_subtitle": "Gestisci le impostazioni del visualizzatore risorse", + "asset_viewer_settings_subtitle": "Gestisci le impostazioni del visualizzatore della galleria", "asset_viewer_settings_title": "Visualizzazione risorse", "assets": "Risorse", "assets_added_count": "{count, plural, one {# asset aggiunto} other {# asset aggiunti}}", "assets_added_to_album_count": "{count, plural, one {# asset aggiunto} other {# asset aggiunti}} all'album", - "assets_added_to_name_count": "Aggiunti {count, plural, one {# asset} other {# assets}} a {hasName, select, true {{name}} other {new album}}", - "assets_cannot_be_added_to_album_count": "{count, plural, one {L'asset} other {Gli asset}} non possono essere aggiunti all'album", - "assets_count": "{count, plural, other {# asset}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {L'elemento} other {Gli elementi}} non possono essere aggiunti all'album", + "assets_count": "{count, plural, one {# elemento} other {# elementi}}", "assets_deleted_permanently": "{count} elementi cancellati definitivamente", "assets_deleted_permanently_from_server": "{count} elementi cancellati definitivamente dal server Immich", - "assets_downloaded_failed": "{count, plural, one {Scaricato # file - {error} file non riusciti} other {Scaricati # file - {error} file non riusciti}}", + "assets_downloaded_failed": "{count, plural, one {Scaricato # file - {error} file non riuscito} other {Scaricati # file - {error} file non riusciti}}", "assets_downloaded_successfully": "{count, plural, one {Scaricato # file con successo} other {Scaricati # file con successo}}", - "assets_moved_to_trash_count": "{count, plural, one {# asset spostato} other {# asset spostati}} nel cestino", + "assets_moved_to_trash_count": "{count, plural, one {# elemento spostato} other {# elementi spostati}} nel cestino", "assets_permanently_deleted_count": "{count, plural, one {# asset cancellato} other {# asset cancellati}} definitivamente", "assets_removed_count": "{count, plural, one {# asset rimosso} other {# asset rimossi}}", "assets_removed_permanently_from_device": "{count} elementi cancellati definitivamente dal tuo dispositivo", - "assets_restore_confirmation": "Sei sicuro di voler ripristinare tutti gli asset cancellati? Non puoi annullare questa azione! Tieni presente che eventuali risorse offline NON possono essere ripristinate in questo modo.", + "assets_restore_confirmation": "Sei sicuro di voler ripristinare tutti gli elementi cancellati? Non puoi annullare questa azione! Tieni presente che eventuali risorse offline NON possono essere ripristinate in questo modo.", "assets_restored_count": "{count, plural, one {# asset ripristinato} other {# asset ripristinati}}", "assets_restored_successfully": "{count} elementi ripristinati", "assets_trashed": "{count} elementi cestinati", @@ -497,7 +515,7 @@ "backup_album_selection_page_selection_info": "Informazioni sulla selezione", "backup_album_selection_page_total_assets": "Numero totale delle risorse", "backup_all": "Tutti", - "backup_background_service_backup_failed_message": "Impossibile caricare i contenuti. Riprovo…", + "backup_background_service_backup_failed_message": "È stato impossibile fare il backup dei contenuti. Riprovo…", "backup_background_service_connection_failed_message": "Impossibile connettersi al server. Riprovo…", "backup_background_service_current_upload_notification": "Caricamento di {filename} in corso", "backup_background_service_default_notification": "Ricerca di nuovi contenuti…", @@ -506,7 +524,7 @@ "backup_background_service_upload_failure_notification": "Impossibile caricare {filename}", "backup_controller_page_albums": "Backup Album", "backup_controller_page_background_app_refresh_disabled_content": "Attiva l'aggiornamento dell'app in background in Impostazioni > Generale > Aggiorna app in background per utilizzare backup in background.", - "backup_controller_page_background_app_refresh_disabled_title": "Backup in background è disattivo", + "backup_controller_page_background_app_refresh_disabled_title": "Aggiornamento dell'app in background disattivo", "backup_controller_page_background_app_refresh_enable_button_text": "Vai alle impostazioni", "backup_controller_page_background_battery_info_link": "Mostrami come", "backup_controller_page_background_battery_info_message": "Per una migliore esperienza di backup, disabilita le ottimizzazioni della batteria per l'app Immich.\n\nDal momento che è una funzionalità specifica del dispositivo, per favore consulta il manuale del produttore.", @@ -515,12 +533,12 @@ "backup_controller_page_background_charging": "Solo durante la ricarica", "backup_controller_page_background_configure_error": "Impossibile configurare i servizi in background", "backup_controller_page_background_delay": "Ritarda il backup di nuovi elementi: {duration}", - "backup_controller_page_background_description": "Abilita i servizi in background per fare il backup di tutti i nuovi contenuti senza la necessità di aprire l'app", - "backup_controller_page_background_is_off": "Backup automatico disattivato", - "backup_controller_page_background_is_on": "Backup automatico attivo", + "backup_controller_page_background_description": "Abilita i servizi in background per fare il backup di nuovi contenuti senza la necessità di aprire l'app", + "backup_controller_page_background_is_off": "Backup automatico in background disattivato", + "backup_controller_page_background_is_on": "Backup automatico in background attivo", "backup_controller_page_background_turn_off": "Disabilita servizi in background", "backup_controller_page_background_turn_on": "Abilita servizi in background", - "backup_controller_page_background_wifi": "Solo Wi-Fi", + "backup_controller_page_background_wifi": "Solo con Wi-Fi", "backup_controller_page_backup": "Backup", "backup_controller_page_backup_selected": "Selezionati: ", "backup_controller_page_backup_sub": "Foto e video caricati", @@ -703,7 +721,7 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Scuro", - "darkTheme": "Attiva/Disattiva tema scuro", + "dark_theme": "Imposta tema scuro", "date_after": "Data dopo", "date_and_time": "Data e ora", "date_before": "Data prima", @@ -719,6 +737,7 @@ "default_locale": "Localizzazione preimpostata", "default_locale_description": "Formatta la data e i numeri in base alle impostazioni del tuo browser", "delete": "Elimina", + "delete_action_prompt": "{count} elementi eliminati definitivamente", "delete_album": "Elimina album", "delete_api_key_prompt": "Sei sicuro di voler eliminare questa chiave API?", "delete_dialog_alert": "Questi oggetti saranno eliminati definitivamente da Immich e dal tuo device", @@ -799,6 +818,7 @@ "edit_key": "Modifica chiave", "edit_link": "Modifica link", "edit_location": "Modifica posizione", + "edit_location_action_prompt": "{count} luoghi modificati", "edit_location_dialog_title": "Posizione", "edit_name": "Modifica nome", "edit_people": "Modifica persone", @@ -984,6 +1004,7 @@ "failed_to_load_assets": "Impossibile caricare gli asset", "failed_to_load_folder": "Impossibile caricare la cartella", "favorite": "Preferito", + "favorite_action_prompt": "{count} elementi aggiunti ai preferiti", "favorite_or_unfavorite_photo": "Aggiungi o rimuovi foto da preferiti", "favorites": "Preferiti", "favorites_page_no_favorites": "Nessun preferito", @@ -1127,6 +1148,7 @@ "library_page_sort_created": "Data di creazione", "library_page_sort_last_modified": "Ultima modifica", "library_page_sort_title": "Titolo album", + "licenses": "Licenze", "light": "Chiaro", "like_deleted": "Mi piace rimosso", "link_motion_video": "Collega video in movimento", @@ -1246,6 +1268,7 @@ "more": "Di più", "move": "Sposta", "move_off_locked_folder": "Sposta al di fuori della cartella privata", + "move_to_lock_folder_action_prompt": "{count} elementi aggiunti alla cartella sicura", "move_to_locked_folder": "Sposta nella cartella privata", "move_to_locked_folder_confirmation": "Queste foto e video verranno rimossi da tutti gli album, e saranno visibili solo dalla cartella privata", "moved_to_archive": "Spostati {count, plural, one {# asset} other {# assets}} nell'archivio", @@ -1479,13 +1502,13 @@ "recently_taken_page_title": "Scattate di Recente", "refresh": "Aggiorna", "refresh_encoded_videos": "Ricarica video codificati", - "refresh_faces": "Aggiorna facce", + "refresh_faces": "Aggiorna volti", "refresh_metadata": "Ricarica metadati", "refresh_thumbnails": "Ricarica anteprime", "refreshed": "Aggiornato", "refreshes_every_file": "Rilegge tutti i file esistenti e nuovi", "refreshing_encoded_video": "Ricaricando il video codificato", - "refreshing_faces": "Aggiorna Facce", + "refreshing_faces": "Aggiornando volti", "refreshing_metadata": "Ricaricando i metadati", "regenerating_thumbnails": "Rigenerando le anteprime", "remove": "Rimuovi", @@ -1496,6 +1519,7 @@ "remove_deleted_assets": "Rimuovi file offline", "remove_from_album": "Rimuovere dall'album", "remove_from_favorites": "Rimuovi dai preferiti", + "remove_from_lock_folder_action_prompt": "{count} elementi rimossi dalla cartella sicura", "remove_from_locked_folder": "Rimuovi dalla cartella privata", "remove_from_locked_folder_confirmation": "Sei sicuro di voler spostare queste foto e questi video dalla cartella privata? Diventeranno visibili nella vostra libreria.", "remove_from_shared_link": "Rimuovi dal link condiviso", @@ -1838,6 +1862,7 @@ "total": "Totale", "total_usage": "Utilizzo totale", "trash": "Cestino", + "trash_action_prompt": "{count} elementi spostati nel cestino", "trash_all": "Cestina Tutto", "trash_count": "Cancella {count, number}", "trash_delete_asset": "Cestina/Cancella Asset", diff --git a/i18n/ja.json b/i18n/ja.json index 1649f978d8..69c0f368fa 100644 --- a/i18n/ja.json +++ b/i18n/ja.json @@ -427,6 +427,7 @@ "app_settings": "アプリ設定", "appears_in": "これらに含まれます", "archive": "アーカイブ", + "archive_action_prompt": "アーカイブに{count}項目追加しました", "archive_or_unarchive_photo": "写真をアーカイブまたはアーカイブ解除", "archive_page_no_archived_assets": "アーカイブした写真またはビデオがありません", "archive_page_title": "アーカイブ ({count})", @@ -464,7 +465,6 @@ "assets": "アセット", "assets_added_count": "{count, plural, one {#個} other {#個}}のアセットを追加しました", "assets_added_to_album_count": "{count, plural, one {#個} other {#個}}のアセットをアルバムに追加しました", - "assets_added_to_name_count": "{count, plural, one {#個} other {#個}}のアセットを{hasName, select, true {{name}} other {新しいアルバム}}に追加しました", "assets_cannot_be_added_to_album_count": "{count, plural, one {アセット} other {アセット}} はアルバムに追加できません", "assets_count": "{count, plural, one {#個} other {#個}}のアセット", "assets_deleted_permanently": "{count}項目を完全に削除しました", @@ -492,7 +492,7 @@ "background_location_permission_content": "正常にWi-Fiの名前(SSID)を獲得するにはアプリが常に詳細な位置情報にアクセスできる必要があります", "backup_album_selection_page_albums_device": "デバイス上のアルバム({count})", "backup_album_selection_page_albums_tap": "タップで選択、ダブルタップで除外", - "backup_album_selection_page_assets_scatter": "アルバムを選択・除外してバックアップする写真を選ぶ (同じ写真が複数のアルバムに登録されていることがあるため)", + "backup_album_selection_page_assets_scatter": "アルバムを選択・除外してバックアップする写真を選ぶ。 (同じ写真が複数のアルバムに登録されていることがあるため)", "backup_album_selection_page_select_albums": "アルバムを選択", "backup_album_selection_page_selection_info": "選択・除外中のアルバム", "backup_album_selection_page_total_assets": "選択されたアルバムの写真と動画の数", @@ -703,7 +703,7 @@ "daily_title_text_date": "MM DD, EE", "daily_title_text_date_year": "yyyy MM DD, EE", "dark": "ダークモード", - "darkTheme": "ダークモードを切り替え", + "dark_theme": "ダークモード切り替え", "date_after": "この日以降", "date_and_time": "日付と時間", "date_before": "この日以前", @@ -719,6 +719,7 @@ "default_locale": "デフォルトのロケール", "default_locale_description": "ブラウザのロケールに基づいて日付と数値をフォーマットします", "delete": "削除", + "delete_action_prompt": "{count}項目を完全に削除しました", "delete_album": "アルバムを削除", "delete_api_key_prompt": "本当にこのAPI キーを削除しますか?", "delete_dialog_alert": "サーバーとデバイスの両方から完全に削除されます", @@ -799,6 +800,7 @@ "edit_key": "キーを編集", "edit_link": "リンクを編集する", "edit_location": "位置情報を編集", + "edit_location_action_prompt": "{count}項目の位置情報を変更しました", "edit_location_dialog_title": "位置情報", "edit_name": "名前を変更", "edit_people": "人物を編集", @@ -984,6 +986,7 @@ "failed_to_load_assets": "アセットのロードに失敗しました", "failed_to_load_folder": "フォルダーの読み込みに失敗", "favorite": "お気に入り", + "favorite_action_prompt": "{count}項目をお気に入りに追加しました", "favorite_or_unfavorite_photo": "写真をお気にいりに登録または解除", "favorites": "お気に入り", "favorites_page_no_favorites": "お気に入り登録された項目がありません", @@ -1246,6 +1249,7 @@ "more": "もっと表示", "move": "移動", "move_off_locked_folder": "鍵付きフォルダーから出す", + "move_to_lock_folder_action_prompt": "{count}項目を鍵付きフォルダーに追加しました", "move_to_locked_folder": "鍵付きフォルダーへ移動", "move_to_locked_folder_confirmation": "これらの写真や動画はすべてのアルバムから外され、鍵付きフォルダー内でのみ閲覧可能になります", "moved_to_archive": "{count, plural, one {#} other {#}}項目をアーカイブしました", @@ -1496,6 +1500,7 @@ "remove_deleted_assets": "オフラインのアセットを削除", "remove_from_album": "アルバムから削除", "remove_from_favorites": "お気に入り解除", + "remove_from_lock_folder_action_prompt": "{count}項目を鍵付きフォルダーから出しました", "remove_from_locked_folder": "鍵付きフォルダーから取り除く", "remove_from_locked_folder_confirmation": "選択した写真・動画を鍵付きフォルダーの外に出してよろしいですか?ライブラリに再び表示されるようになります", "remove_from_shared_link": "共有リンクから削除", @@ -1838,6 +1843,7 @@ "total": "合計", "total_usage": "総使用量", "trash": "ゴミ箱", + "trash_action_prompt": "{count}項目をゴミ箱に移動しました", "trash_all": "全て削除", "trash_count": "{count, number}枚ゴミ箱へ移動", "trash_delete_asset": "アセットをゴミ箱へ移動/削除", diff --git a/i18n/ko.json b/i18n/ko.json index 01b29e6776..1890962e5a 100644 --- a/i18n/ko.json +++ b/i18n/ko.json @@ -22,6 +22,7 @@ "add_partner": "파트너 추가", "add_path": "경로 추가", "add_photos": "사진 추가", + "add_tag": "태그 추가하기", "add_to": "앨범에 추가…", "add_to_album": "앨범에 추가", "add_to_album_bottom_sheet_added": "{album}에 추가되었습니다.", @@ -33,6 +34,7 @@ "added_to_favorites_count": "즐겨찾기에 {count, number}개 추가됨", "admin": { "add_exclusion_pattern_description": "규칙에 *, ** 및 ? 를 사용할 수 있습니다. 이름이 \"Raw\"인 디렉터리의 모든 파일을 제외하려면 \"**/Raw/**\"를, \".tif\"로 끝나는 모든 파일을 제외하려면 \"**/*.tif\"를 사용하고, 절대 경로의 경우 \"/path/to/ignore/**\"와 같은 방식으로 사용합니다.", + "admin_user": "관리자", "asset_offline_description": "외부 라이브러리에 포함된 이 항목을 디스크에서 더이상 찾을 수 없어 휴지통으로 이동되었습니다. 파일이 라이브러리 내에서 이동된 경우 타임라인에서 새로 연결된 항목을 확인하세요. 항목을 복원하려면 아래의 파일 경로에 Immich가 접근할 수 있는지 확인하고 라이브러리 스캔을 진행하세요.", "authentication_settings": "인증 설정", "authentication_settings_description": "비밀번호, OAuth 및 기타 인증 설정 관리", @@ -43,7 +45,7 @@ "backup_database_enable_description": "데이터베이스 덤프 활성화", "backup_keep_last_amount": "보관할 이전 덤프의 수", "backup_settings": "데이터베이스 덤프 설정", - "backup_settings_description": "데이터베이스 덤프 설정을 관리합니다. 참고: 이 작업은 진행 및 실패 여부를 확인할 수 없습니다.", + "backup_settings_description": "데이터베이스 덤프 설정을 관리합니다.", "cleared_jobs": "작업 중단: {job}", "config_set_by_file": "현재 구성은 설정 파일을 통해 지정되어 있습니다.", "confirm_delete_library": "{library} 라이브러리를 삭제하시겠습니까?", @@ -164,12 +166,26 @@ "metadata_settings_description": "메타데이터 설정 관리", "migration_job": "마이그레이션", "migration_job_description": "각 항목의 섬네일 및 인물의 얼굴을 최신 폴더 구조로 마이그레이션", + "nightly_tasks_cluster_faces_setting_description": "새로 감지된 얼굴에 대하여 얼굴 인식을 실행", + "nightly_tasks_cluster_new_faces_setting": "새 얼굴 묶기", + "nightly_tasks_database_cleanup_setting": "데이터베이스 정리 작업", + "nightly_tasks_database_cleanup_setting_description": "데이터베이스에서 오래되거나 만료된 데이터 정리하기", + "nightly_tasks_generate_memories_setting": "메모리 생성하기", + "nightly_tasks_generate_memories_setting_description": "항목에서 새로운 메모리 만들기", + "nightly_tasks_missing_thumbnails_setting": "누락된 섬네일 생성하기", + "nightly_tasks_missing_thumbnails_setting_description": "섬네일이 없는 항목에 대해 섬네일 생성 대기열에 추가하기", + "nightly_tasks_settings": "야간 작업 설정", + "nightly_tasks_settings_description": "야간 작업 관리", + "nightly_tasks_start_time_setting": "시작 시간", + "nightly_tasks_start_time_setting_description": "서버가 야간 작업을 시작할 시간", + "nightly_tasks_sync_quota_usage_setting": "사용량 동기화", + "nightly_tasks_sync_quota_usage_setting_description": "현재 사용량에 따라 사용자의 사용량 갱신하기", "no_paths_added": "추가된 경로 없음", "no_pattern_added": "추가된 규칙 없음", "note_apply_storage_label_previous_assets": "참고: 이전에 업로드한 항목에도 스토리지 레이블을 적용하려면 다음을 실행합니다,", "note_cannot_be_changed_later": "주의: 추후 변경할 수 없습니다!", "notification_email_from_address": "보낸 사람 이메일", - "notification_email_from_address_description": "보낸 사람의 이메일 주소, 예: \"Immich Photo Server \"", + "notification_email_from_address_description": "보낸 사람의 이메일 주소, 예: \"Immich Photo Server \". 이메일을 보낼 수 있도록 허가 받은 주소만을 사용하세요.", "notification_email_host_description": "이메일 서버의 호스트 (예: smtp.immich.app)", "notification_email_ignore_certificate_errors": "인증서 오류 무시", "notification_email_ignore_certificate_errors_description": "TLS 인증서 유효성 검사 오류 무시 (권장되지 않음)", @@ -194,6 +210,7 @@ "oauth_mobile_redirect_uri": "모바일 리다이렉트 URI", "oauth_mobile_redirect_uri_override": "모바일 리다이렉트 URI 재정의", "oauth_mobile_redirect_uri_override_description": "OAuth 공급자가 ''{callback}''과 같은 모바일 URI를 제공하지 않는 경우 활성화하세요.", + "oauth_role_claim": "역할 수령", "oauth_settings": "OAuth", "oauth_settings_description": "OAuth 로그인 설정 관리", "oauth_settings_more_details": "이 기능에 대한 자세한 내용은 문서를 참조하세요.", @@ -202,7 +219,7 @@ "oauth_storage_quota_claim": "스토리지 할당량 선택", "oauth_storage_quota_claim_description": "스토리지 할당량을 사용자가 입력한 값으로 자동 설정합니다.", "oauth_storage_quota_default": "스토리지 할당량 기본값 (GiB)", - "oauth_storage_quota_default_description": "입력하지 않은 경우 사용할 GiB 단위의 기본 할당량 (무제한 할당량의 경우 0 입력)", + "oauth_storage_quota_default_description": "입력하지 않은 경우 사용할 GiB 단위의 기본 할당량", "oauth_timeout": "요청 타임아웃", "oauth_timeout_description": "요청 타임아웃 (밀리초 단위)", "password_enable_description": "이메일과 비밀번호로 로그인", @@ -242,6 +259,7 @@ "storage_template_migration_info": "스토리지 템플릿은 모든 확장자를 소문자로 변환하며, 변경 사항은 새로 업로드한 항목에만 적용됩니다. 기존에 업로드된 항목에 적용하려면 {job}을 실행하세요.", "storage_template_migration_job": "스토리지 템플릿 마이그레이션 작업", "storage_template_more_details": "이 기능에 대한 자세한 내용은 스토리지 템플릿설명을 참조하세요.", + "storage_template_onboarding_description_v2": "활성화 시, 이 기능은 사용자 지정 템플릿에 따라 파일을 자동 분류합니다. 자세한 정보는 이 문서를 확인하세요.", "storage_template_path_length": "대략적인 경로 길이 제한: {length, number}/{limit, number}", "storage_template_settings": "스토리지 템플릿", "storage_template_settings_description": "업로드된 항목의 폴더 구조 및 파일 이름 관리", @@ -288,7 +306,7 @@ "transcoding_encoding_options": "인코딩 옵션", "transcoding_encoding_options_description": "인코딩된 동영상의 코덱, 해상도, 품질 및 기타 옵션 설정", "transcoding_hardware_acceleration": "하드웨어 가속", - "transcoding_hardware_acceleration_description": "실험적인 기능입니다. 속도가 향상되지만 동일 비트레이트에서 품질이 상대적으로 낮을 수 있습니다.", + "transcoding_hardware_acceleration_description": "실험적인 기능입니다. 트랜스코딩이 빨라지지만 동일 비트레이트에서 품질이 상대적으로 낮을 수 있습니다.", "transcoding_hardware_decoding": "하드웨어 디코딩", "transcoding_hardware_decoding_setting_description": "인코딩 가속을 위해 엔드 투 엔드 가속을 사용합니다. 모든 동영상에서 작동하지 않을 수 있습니다.", "transcoding_max_b_frames": "최대 B 프레임", @@ -354,10 +372,12 @@ "admin_password": "관리자 비밀번호", "administration": "관리", "advanced": "고급", + "advanced_settings_beta_timeline_subtitle": "새로운 앱 경험 사용하기", + "advanced_settings_beta_timeline_title": "베타 타임라인", "advanced_settings_enable_alternate_media_filter_subtitle": "이 옵션을 사용하면 동기화 중 미디어를 대체 기준으로 필터링할 수 있습니다. 앱이 모든 앨범을 제대로 감지하지 못할 때만 사용하세요.", "advanced_settings_enable_alternate_media_filter_title": "대체 기기 앨범 동기화 필터 사용 (실험적)", "advanced_settings_log_level_title": "로그 레벨: {level}", - "advanced_settings_prefer_remote_subtitle": "일부 기기의 경우 기기 내의 섬네일을 로드하는 속도가 매우 느립니다. 서버 이미지를 대신 로드하려면 이 설정을 활성화하세요.", + "advanced_settings_prefer_remote_subtitle": "일부 기기의 경우 로컬 항목에서 섬네일을 로드하는 속도가 매우 느립니다. 서버 이미지를 대신 로드하려면 이 설정을 활성화하세요.", "advanced_settings_prefer_remote_title": "서버 이미지 선호", "advanced_settings_proxy_headers_subtitle": "네트워크 요청을 보낼 때 Immich가 사용할 프록시 헤더를 정의합니다.", "advanced_settings_proxy_headers_title": "프록시 헤더", @@ -401,6 +421,9 @@ "album_with_link_access": "링크가 있는 경우 누구나 이 앨범의 사진과 인물을 볼 수 있습니다.", "albums": "앨범", "albums_count": "앨범 {count, plural, one {{count, number}개} other {{count, number}개}}", + "albums_default_sort_order": "기본 앨범 정렬 순서", + "albums_default_sort_order_description": "새 앨범을 생성할 때 기본적으로 항목을 정렬할 순서.", + "albums_feature_description": "다른 사용자와 공유할 수 있는 항목 모음.", "all": "모두", "all_albums": "모든 앨범", "all_people": "모든 인물", @@ -421,6 +444,7 @@ "app_settings": "앱 설정", "appears_in": "다음 앨범에 포함됨", "archive": "보관함", + "archive_action_prompt": "보관함에 {count}개가 추가되었습니다.", "archive_or_unarchive_photo": "보관 처리 또는 해제", "archive_page_no_archived_assets": "보관된 항목 없음", "archive_page_title": "보관함 ({count})", @@ -458,10 +482,12 @@ "assets": "항목", "assets_added_count": "{count, plural, one {#개} other {#개}} 항목 추가됨", "assets_added_to_album_count": "앨범에 항목 {count, plural, one {#개} other {#개}} 추가됨", - "assets_added_to_name_count": "{hasName, select, true {{name}} other {새 앨범}}에 항목 {count, plural, one {#개} other {#개}} 추가됨", + "assets_cannot_be_added_to_album_count": "{count, plural, one {항목} other {항목}]이 앨범에 추가될 수 없습니다.", "assets_count": "{count, plural, one {#개} other {#개}} 항목", "assets_deleted_permanently": "{count}개 항목이 영구적으로 삭제됨", "assets_deleted_permanently_from_server": "서버에서 항목 {count}개가 영구적으로 삭제됨", + "assets_downloaded_failed": "{count, plural, one {파일 #개 다운로드 완료 - {error}개 실패} other {파일 #개 다운로드 완료 - {error}개 실패}}", + "assets_downloaded_successfully": "{count, plural, one {#개 파일 다운로드 완료} other {#개 파일 다운로드 완료}}", "assets_moved_to_trash_count": "휴지통으로 항목 {count, plural, one {#개} other {#개}} 이동됨", "assets_permanently_deleted_count": "항목 {count, plural, one {#개} other {#개}}가 영구적으로 삭제됨", "assets_removed_count": "항목 {count, plural, one {#개} other {#개}}를 제거했습니다.", @@ -476,6 +502,7 @@ "authorized_devices": "인증된 기기", "automatic_endpoint_switching_subtitle": "지정된 Wi-Fi가 사용 가능한 경우 내부망을 통해 연결하고, 그렇지 않으면 다른 연결 방식을 사용합니다.", "automatic_endpoint_switching_title": "자동 URL 전환", + "autoplay_slideshow": "슬라이드 쇼 자동 재생", "back": "뒤로", "back_close_deselect": "뒤로, 닫기, 선택 취소", "background_location_permission": "백그라운드 위치 권한", diff --git a/i18n/lt.json b/i18n/lt.json index d5e6ff20ed..14668921d2 100644 --- a/i18n/lt.json +++ b/i18n/lt.json @@ -14,6 +14,7 @@ "add_a_location": "Pridėti vietovę", "add_a_name": "Pridėti vardą", "add_a_title": "Pridėti pavadinimą", + "add_endpoint": "Pridėti galutinį tašką", "add_exclusion_pattern": "Pridėti išimčių šabloną", "add_import_path": "Pridėti importavimo kelią", "add_location": "Pridėti vietovę", @@ -32,6 +33,8 @@ "added_to_favorites": "Pridėta prie mėgstamiausių", "added_to_favorites_count": "{count, plural, one {# pridėtas} few {# pridėti} other {# pridėta}} prie mėgstamiausių", "admin": { + "add_exclusion_pattern_description": "Pridėti išimčių taisyklęs. Plaikomi simboliai *,**, ir ?. Ignoruoti bet kokius failus bet kuriame aplanke užvadintame \"Raw\", naudokite \"**/RAW/**\". Ignoravimui failų su plėtiniu \".tif\", naudokite \"**/*.tiff\". Aplanko kelio nustatymams, naudokite \"/aplanko/kelias/ignoruoti/**\"", + "admin_user": "Administratorius", "asset_offline_description": "Šis išorinės bibliotekos elementas nebepasiekiamas diske ir buvo perkeltas į šiukšliadėžę. Jei failas buvo perkeltas toje pačioje bibliotekoje, laiko skalėje rasite naują atitinkamą elementą. Jei norite šį elementą atkurti, įsitikinkite, kad Immich gali pasiekti failą žemiau nurodytu adresu, ir suvykdykite bibliotekos skenavimą.", "authentication_settings": "Autentifikavimo nustatymai", "authentication_settings_description": "Tvarkyti slaptažodžių, OAuth ir kitus autentifikavimo nustatymus", @@ -42,8 +45,8 @@ "backup_database_enable_description": "Įgalinti duomenų bazės išklotinės", "backup_keep_last_amount": "Išsaugomų ankstesnių duomenų bazės išklotinių skaičius", "backup_settings": "Duomenų bazės išklotinių nustatymai", - "backup_settings_description": "Tvarkyti duomenų bazės išklotinės nustatymus. Pastaba: Šie darbai nėra stebimi ir jums nebus pranešta apie nesėkmę.", - "cleared_jobs": "Išvalyti darbai: {job}", + "backup_settings_description": "Tvarkyti duomenų bazės išklotinės nustatymus. Pastaba: šie darbai nėra stebimi ir jums nebus pranešta apie nesėkmę.", + "cleared_jobs": "Išvalytos užduotys užduočiai: {job}", "config_set_by_file": "Konfigūracija nustatyta pagal konfigūracinį failą", "confirm_delete_library": "Ar tikrai norite ištrinti {library} biblioteką?", "confirm_delete_library_assets": "Ar tikrai norite ištrinti šią biblioteką? Šis veiksmas ištrins {count, plural, one {# contained asset} other {all # contained assets}} iš Immich ir negali būti grąžintas. Failai liks diske.", @@ -51,7 +54,7 @@ "confirm_reprocess_all_faces": "Ar tikrai norite iš naujo apdoroti visus veidus? Tai taip pat ištrins įvardytus asmenis.", "confirm_user_password_reset": "Ar tikrai norite iš naujo nustatyti {user} slaptažodį?", "confirm_user_pin_code_reset": "Ar tikrai norite iš naujo nustatyti {user} PIN kodą?", - "create_job": "Sukurti darbą", + "create_job": "Sukurti užduotį", "cron_expression": "Cron išraiška", "cron_expression_description": "Nustatyti skenavimo intervalą naudojant cron formatą. Norėdami gauti daugiau informacijos žiūrėkite Crontab Guru", "cron_expression_presets": "Išankstiniai Cron nustatymai", @@ -62,15 +65,19 @@ "face_detection": "Veidų aptikimas", "face_detection_description": "Veidų aptikimas bibliotekos elementuose naudojant mašininį mokymąsi. Vaizdo įrašų atveju naudojama tik miniatiūra. \"Atnaujinti\" iš naujo nuskaito visus bibliotekos elementus. \"Atstatyti\" ne tik atnaujina, bet ir išvalo visus esamus veidų duomenis. \"Trūkstami\" nuskaito tik dar nenuskaitytus bibliotekos elementus. Veidų aptikimo darbui pasibaigus, aptikti veidai patenka į veidų atpažinimo darbų eilę, kur jie priskiriami jau esamiems ar naujai atpažintiems žmonėms.", "facial_recognition_job_description": "Aptiktų veidų atpažinimas ir priskyrimas žmonėms. Šis darbas vykdomas pasibaigus \"veidų aptikimo\" darbui. \"Atstatyti\" (per)grupuoja visus aptiktus veidus. \"Trūkstami\" apdoroja jokiam žmogui dar nepriskirtus aptiktus veidus.", - "failed_job_command": "Darbo {job} komanda {command} nepavyko", + "failed_job_command": "Užduoties {job} komanda {command} nepavyko", "force_delete_user_warning": "ĮSPĖJIMAS: Šis veiksmas iš karto pašalins naudotoją ir visą jo informaciją. Šis žingsnis nesugrąžinamas ir failų nebus galima atkurti.", "image_format": "Formatas", "image_format_description": "WebP sukuria mažesnius failus nei JPEG, tačiau lėčiau juos apdoroja.", + "image_fullsize_description": "Pilno dydžio nuotrauka be meta duomenų naudojama priartinus", "image_fullsize_enabled": "Įgalinti pilno dydžio nuotraukų generavimą", + "image_fullsize_enabled_description": "Generuoti viso dydžio vaizdą neinternetui pritaikytiems formatams. Kai įjungta parinktis „Pirmenybė įterptai peržiūrai“, įterptosios peržiūros naudojamos tiesiogiai be konvertavimo. Tai neturi įtakos internetui pritaikytiems formatams, pvz., JPEG", "image_fullsize_quality_description": "Pilno dydžio nuotraukų kokybė 1-100. Didesnė yra geresnė, tačiau sukuria didesniu failus.", "image_fullsize_title": "Pilno dydžio nuotraukų Nustatymai", "image_prefer_embedded_preview": "Pageidautinai rodyti įterptą peržiūrą", + "image_prefer_embedded_preview_setting_description": "Naudokite įterptąsias peržiūras RAW nuotraukose kaip įvestį vaizdų apdorojimui ir, jei įmanoma, tai gali suteikti tikslesnes kai kurių vaizdų spalvas, tačiau peržiūros kokybė priklauso nuo fotoaparato, todėl vaizde gali būti daugiau glaudinimo artefaktų.", "image_prefer_wide_gamut": "Teikti pirmenybę plačiai gamai", + "image_prefer_wide_gamut_setting_description": "Miniatiūroms naudokite „Display P3“. Taip geriau išsaugomas vaizdų, turinčių plačias spalvų erdves, ryškumas, tačiau senesniuose įrenginiuose su senesne naršyklės versija vaizdai gali atrodyti kitaip. sRGB vaizdai išsaugomi kaip sRGB, kad būtų išvengta spalvų pasikeitimo.", "image_preview_description": "Vidutinio dydžio vaizdas su išvalytais metaduomenimis, naudojamas kai žiūrimas vienas objektas arba mašininiam mokymuisi", "image_preview_quality_description": "Peržiūros kokybė nuo 1-100. Aukštesnės reikšmės yra geriau, bet sukuriami didesni failai gali sumažinti programos reagavimo laiką. Mažos vertės nustatymas gali paveikti mašininio mokymo kokybę.", "image_preview_title": "Peržiūros nustatymai", @@ -83,11 +90,11 @@ "image_thumbnail_quality_description": "Miniatiūros kokybė nuo 1-100. Aukštesnės reikšmės yra geriau, bet pagaminami didesni failai ir gali būti sulėtintas programos reagavimo greitis.", "image_thumbnail_title": "Miniatiūros nustatymai", "job_concurrency": "{job} lygiagretumas", - "job_created": "Darbas sukurtas", - "job_not_concurrency_safe": "Šis darbas nėra saugus apdoroti lygiagrečiai.", - "job_settings": "Darbų nustatymai", - "job_settings_description": "Keisti darbų lygiagretumą", - "job_status": "Darbų būsenos", + "job_created": "Užduotis sukurta", + "job_not_concurrency_safe": "Ši užduotis nėra saugi apdoroti lygiagrečiai.", + "job_settings": "Užduočių nustatymai", + "job_settings_description": "Keisti užduočių lygiagretumą", + "job_status": "Užduočių būsenos", "library_created": "Sukurta biblioteka: {library}", "library_deleted": "Biblioteka ištrinta", "library_import_path_description": "Nurodykite aplanką, kurį norite importuoti. Šiame aplanke, įskaitant poaplankius, bus nuskaityti vaizdai ir vaizdo įrašai.", @@ -104,6 +111,7 @@ "logging_level_description": "Įjungus, kokį žurnalo vedimo lygį naudot.", "logging_settings": "Žurnalo vedimas", "machine_learning_clip_model": "CLIP modelis", + "machine_learning_clip_model_description": "Pavadinimas CLIP modelio įvardintio here. Dėmesio, keičiant modelį jūs privalote iš naujo paleisti 'Išmaniosios Paieškos' užduotį visiems vaizdams.", "machine_learning_duplicate_detection": "Dublikatų aptikimas", "machine_learning_duplicate_detection_enabled": "Įjungti dublikatų aptikimą", "machine_learning_duplicate_detection_enabled_description": "Jei išjungta, visiškai identiški elementai vis tiek bus deduplikuoti.", @@ -113,12 +121,15 @@ "machine_learning_facial_recognition": "Veidų atpažinimas", "machine_learning_facial_recognition_description": "Aptikti, atpažinti ir sugrupuoti veidus nuotraukose", "machine_learning_facial_recognition_model": "Veidų atpažinimo modelis", + "machine_learning_facial_recognition_model_description": "Modeliai išvardinti apimties mažėjančia tvarka. Didieji modeliai yra lėti ir naudoja daugiau atminties, tačiau sukuria geresnius rezultatus. Pastebime kad keičiant modelį jūs turite iš naujo paleisti Veidų Atpažinimo užduotį visiems vaizdams.", "machine_learning_facial_recognition_setting": "Įgalinti veidų atpažinimą", "machine_learning_facial_recognition_setting_description": "Išjungus, vaizdai nebus užšifruoti veidų atpažinimui ir nebus naudojami Žmonių sekcijoje Naršymo puslapyje.", "machine_learning_max_detection_distance": "Maksimalus aptikimo atstumas", "machine_learning_max_detection_distance_description": "Didžiausias atstumas tarp dviejų vaizdų, kad jie būtų laikomi dublikatais, svyruoja nuo 0,001 iki 0,1. Didesnės vertės aptiks daugiau dublikatų, tačiau gali būti klaidingai teigiami.", "machine_learning_max_recognition_distance": "Maksimalus atpažinimo atstumas", + "machine_learning_max_recognition_distance_description": "Didžiausias skirtumas tarp veidų, kad būtų užskaityti kaip vienas ir tas pats asmuo, rėžis nuo 0-2. Mažinant tai gali apsaugoti nuo dviejų žmonių žymėjimo tuo pačiu asmeniu, didinant tai gali apsaugoti nuo to pačio asmens žymėjimo kaip du skirtingus žmones. Pastebime kad yra paprasčiau apjungti kelių žmonių modelius į vieną nei vieną išdalinti į du, taigi kai įmanoma geriau naudoti mažensę ribą.", "machine_learning_min_detection_score": "Minimalus aptikimo balas", + "machine_learning_min_detection_score_description": "Minimalus užtikrintumo balas veido aptikimui nuo 0-1. Mažesnė reikšmė aptiks daugiau veidų tačiau bus ir daugiau klaidingų teigiamų režultatų.", "machine_learning_min_recognized_faces": "Mažiausias atpažintų veidų skaičius", "machine_learning_min_recognized_faces_description": "Mažiausias atpažintų veidų skaičius asmeniui, kurį reikia sukurti. Tai padidinus, veido atpažinimas tampa tikslesnis, bet padidėja tikimybė, kad veidas žmogui nepriskirtas.", "machine_learning_settings": "Mašininio mokymosi nustatymai", @@ -151,10 +162,14 @@ "metadata_faces_import_setting_description": "Importuoti veidus iš vaizdo EXIF duomenų ir papildomų failų", "metadata_settings": "Metaduomenų nustatymai", "metadata_settings_description": "Tvarkyti metaduomenų nustatymus", - "migration_job": "Migracija", + "migration_job": "Tvarkymas", + "migration_job_description": "Pertvarkytį turinio ir veidų miniatiūras pagal naują struktūrą", + "nightly_tasks_cluster_faces_setting_description": "Paleisti veido atpažinimą naujai aptiktiems veidams", + "nightly_tasks_cluster_new_faces_setting": "Sugrupuoti naujus veidus", + "nightly_tasks_database_cleanup_setting": "Duomenų bazės valymo darbai", "no_paths_added": "Keliai nepridėti", "no_pattern_added": "Šablonas nepridėtas", - "note_apply_storage_label_previous_assets": "Pastaba: norėdami pritaikyti saugyklos etiketę seniau įkeltiems ištekliams, paleiskite", + "note_apply_storage_label_previous_assets": "Pastaba: norėdami pritaikyti Saugyklos Žymą seniau įkeltiems ištekliams, paleiskite", "note_cannot_be_changed_later": "PASTABA: Vėliau to pakeisti negalima!", "notification_email_from_address": "Iš adreso", "notification_email_from_address_description": "Siuntėjo elektroninis adresas, pavyzdžiui: \"Immich Photo Server \"", @@ -177,20 +192,29 @@ "oauth_auto_register": "Automatinis registravimas", "oauth_auto_register_description": "Automatiškai užregistruoti naujus naudotojus po prisijungimo per OAuth", "oauth_button_text": "Mygtuko tekstas", + "oauth_client_secret_description": "Privalomas jei PKCE (Proof Key for Code Exchange) nepalaikomas pagal OAuth tiekėją", "oauth_enable_description": "Prisijungti su OAuth", "oauth_mobile_redirect_uri": "Mobiliojo peradresavimo URI", "oauth_mobile_redirect_uri_override": "Mobiliojo peradresavimo URI pakeitimas", "oauth_mobile_redirect_uri_override_description": "Įjunkite, kai OAuth teikėjas nepalaiko mobiliojo URI, tokio kaip ''{callback}''", + "oauth_role_claim": "Rolės Tvirtinimas", + "oauth_role_claim_description": "Suteikti admin teises automatiškai pagal šios rolės tvirtinimo buvimą. Tvirtinimas gali turėti priskirtus arba 'vartotoją' arba 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Tvarkyti OAuth prisijungimo nustatymus", "oauth_settings_more_details": "Detaliau apie šią funkciją galite paskaityti dokumentacijoje.", + "oauth_storage_label_claim": "Saugyklos Žyma pagal tvirtinimą", + "oauth_storage_label_claim_description": "Priskirti Saugyklos Žymą automatiškai pagal reikšmę vartotojo tvirtinime.", + "oauth_storage_quota_claim": "Saugyklos apimties tvirtinimas", + "oauth_storage_quota_claim_description": "Priskirti vartotojo saugyklos apimties kvotą automatiškai pagal šio tvirtinimo reikšmę.", "oauth_storage_quota_default": "Numatyta atminties kvota (GiB)", + "oauth_storage_quota_default_description": "Nustatoma appimties kvota GiB kai nėra nurodyta tvirtinime.", "oauth_timeout": "Užklausa viršijo laiko limitą", "oauth_timeout_description": "Laiko limitas užklausoms milisekundėmis", "password_enable_description": "Prisijungti su el. paštu ir slaptažodžiu", "password_settings": "Prisijungimas slaptažodžiu", "password_settings_description": "Tvarkyti prisijungimo slaptažodžiu nustatymus", "paths_validated_successfully": "Visi keliai patvirtinti sėkmingai", + "person_cleanup_job": "Išvalyti asmenis", "quota_size_gib": "Kvotos dydis (GiB)", "refreshing_all_libraries": "Perkraunamos visos bibliotekos", "registration": "Administratoriaus registracija", @@ -199,7 +223,7 @@ "reset_settings_to_default": "Atstatyti nustatymus į numatytuosius", "reset_settings_to_recent_saved": "Nustatymų atstatymas į neseniai išsaugotus nustatymus", "scanning_library": "Biblioteka skenuojama", - "search_jobs": "Ieškoma darbų…", + "search_jobs": "Ieškoma užduočių…", "send_welcome_email": "Siųsti sveikinimo el. laišką", "server_external_domain_settings": "Išorinis domenas", "server_external_domain_settings_description": "Bendrinimo nuorodų domenas, įskaitant http(s)://", @@ -209,19 +233,36 @@ "server_settings_description": "Tvarkyti serverio nustatymus", "server_welcome_message": "Sveikinimo pranešimas", "server_welcome_message_description": "Žinutė, rodoma prisijungimo puslapyje.", + "sidecar_job": "Sidecar metaduomenys", + "sidecar_job_description": "Aptikti ar sinchronizuoti sidecar metaduomenis iš failų sistemos", "slideshow_duration_description": "Sekundžių skaičius, kiek viena nuotrauka rodoma", "smart_search_job_description": "Vykdykite mašininį mokymąsi bibliotekos elementų išmaniajai paieškai", "storage_template_date_time_description": "Elemento sukūrimo laiko žymė yra naudojama laiko informacijai", "storage_template_date_time_sample": "Pavyzdinis laikas {date}", + "storage_template_enable_description": "Aktyvuoti saugyklos šabloną", + "storage_template_hash_verification_enabled": "Aktyvuoti Hash tikrinimą", + "storage_template_hash_verification_enabled_description": "Aktyvuojamas Hash tikrinimas, neišjungti nebent gerai suprantate galimas pasekmes", + "storage_template_migration": "Saugyklos tvarkymas pagal šabloną", + "storage_template_migration_description": "Taikyti dabartinį {template} anksčiau įkeltiems duomenims", + "storage_template_migration_info": "Saugyklos tvarkyklė konvertuos visus plėtinius mažosiomis raidėmis. Šablonas bus taikomas tik naujiems duomenims. Taikyti šabloną retroaktyviai anksčiau įkeltiems duomenims, paleiskite šią {job}.", + "storage_template_migration_job": "Saugyklos Tvarkymo Pagal Šabloną Užduotis", + "storage_template_more_details": "Daugiau detalių apie šią funkciją, atsižvelkite į Storage Template ir jo galimus implications", + "storage_template_onboarding_description_v2": "Kai aktyvuota, ši funkcija automatiškai sukurs failus pagal vartotojo-nustatytą šabloną. Daugiau informacijos, prašome skaityti documentation.", + "storage_template_path_length": "Preliminarus struktūros kelio ilgis/limitas:{length, number}/{limit, number}", + "storage_template_settings": "Saugyklos Šablonas", + "storage_template_settings_description": "Tvarkyti aplankų struktūrą bei failų pavadinimus įkeliamiems duomenims", + "storage_template_user_label": "{label} yra vartotojo Saugyklos Žymą", "system_settings": "Sistemos nustatymai", "tag_cleanup_job": "Žymų išvalymas", + "template_email_available_tags": "Savo šablone galite naudoti nurodytas kintamas reikšmes:{tags}", + "template_email_if_empty": "Jei šablone tuščia reikšmė, bus naudojamas numatytas pagal nutylėjimą El. pašto adresas.", "template_email_preview": "Peržiūra", "template_email_settings": "El. pašto Šablonai", "template_settings": "Pranešimų šablonai", "template_settings_description": "Tvarkyti pasirinktinius pranešimų šablonus", "theme_custom_css_settings": "Individualizuotas CSS", "theme_settings": "Temos nustatymai", - "thumbnail_generation_job": "Generuoti miniatiūras", + "thumbnail_generation_job": "Generuoti Miniatiūras", "thumbnail_generation_job_description": "Didelių, mažų ir neryškių miniatiūrų generavimas kiekvienam bibliotekos elementui, taip pat miniatiūrų generavimas kiekvienam asmeniui", "transcoding_acceleration_api": "Spartinimo API", "transcoding_acceleration_nvenc": "NVENC (reikalinga NVIDIA GPU)", @@ -233,16 +274,21 @@ "transcoding_audio_codec_description": "Opus yra aukščiausios kokybės variantas, tačiau turi mažesnį suderinamumą su senesniais įrenginiais ar programine įranga.", "transcoding_bitrate_description": "Vaizdo įrašai viršija maksimalią leistiną bitų spartą arba nėra priimtino formato", "transcoding_constant_quality_mode": "Pastovios kokybės režimas", + "transcoding_constant_rate_factor_description": "Video kokybės lygis. Tipinės reikšmės yra 23 jei H.264, 28 jei HVEC, 31 jei VP9, ir 35 jei AV1. Kuo mažesnis tuo kokybiškesnis tačiau didesni failai.", "transcoding_hardware_acceleration": "Techninės įrangos spartinimas", "transcoding_hardware_decoding": "Aparatinis dekodavimas", "transcoding_max_bitrate": "Maksimalus bitų srautas", + "transcoding_max_bitrate_description": "Pasirenkant max bitrate galima pasiekti labiau nuspėjamą failų dydį su minimaliais kokybės praradimais. Prie 720p, tipinės reikšmės yra 2600 kbits/s jei BP9 ar HVEC, arba 4500 kbits/s jei H.264. Neveiksnus jei pasirenkamas 0.", + "transcoding_preset_preset_description": "Kompresijos greitis. Siekiant tam tikro bitrate lėtesnis apdorojimas lems mažesnius failų dydžius ir padidins kokybę. VP9 ignoruos greičius virš \"gretesnis\" lygio.", "transcoding_target_resolution_description": "Didesnės skiriamosios gebos gali išsaugoti daugiau detalių, tačiau jas koduoti užtrunka ilgiau, failų dydžiai yra didesni ir gali sumažėti programos jautrumas.", "transcoding_video_codec": "Video kodekas", "trash_enabled_description": "Įgalinti šiukšliadėžės funkcijas", "trash_number_of_days": "Dienų skaičius", "trash_settings": "Šiukšliadėžės nustatymai", "trash_settings_description": "Tvarkyti šiukšliadėžės nustatymus", + "user_cleanup_job": "Vartotojų išvalymas", "user_delete_delay_settings": "Ištrynimo delsa", + "user_delete_delay_settings_description": "Skaičius dienų po ištrynimo kuomet vartotojo paskyrą ir susiję duomenys bus negražinamai ištrinti. Vartotojo Trynimo užduotis paleidžiama vidurnaktį ir tikrina kurie vartotojai gali būti trinami. Šio nustatymo pakeitimai bus naudojami sekančio užduoties paleidimo metu.", "user_management": "Naudotojų valdymas", "user_password_has_been_reset": "Naudotojo slaptažodis buvo iš naujo nustatytas:", "user_restore_description": "Naudotojo {user} paskyra bus atkurta.", @@ -305,7 +351,6 @@ "assets": "Elementai", "assets_added_count": "{count, plural, one {Pridėtas # elementas} few {Pridėti # elementai} other {Pridėta # elementų}}", "assets_added_to_album_count": "Į albumą {count, plural, one {įtrauktas # elementas} few {įtraukti # elementai} other {įtraukta # elementų}}", - "assets_added_to_name_count": "Į {hasName, select, true {{name}} other {naują}} albumą {count, plural, one {įtrauktas # elementas} few {įtraukti # elementai} other {įtraukta # elementų}}", "assets_count": "{count, plural, one {# elementas} few {# elementai} other {# elementų}}", "assets_moved_to_trash_count": "{count, plural, one {# elementas perkeltas} few {# elementai perkelti} other {# elementų perkelta}} į šiukšliadėžę", "assets_permanently_deleted_count": "{count, plural, one {# elementas ištrintas} few {# elementai ištrinti} other {# elementų ištrinta}} visam laikui", @@ -316,7 +361,11 @@ "authorized_devices": "Autorizuoti įrenginiai", "back": "Atgal", "back_close_deselect": "Atgal, uždaryti arba atžymėti", + "backup_background_service_current_upload_notification": "Įkeliamas {filename}", + "backup_background_service_upload_failure_notification": "Nepavyko įkelti {filename}", "backup_controller_page_background_wifi": "Only on WiFi", + "backup_controller_page_filename": "Failo pavadinimas: {filename}[{size}]", + "backup_controller_page_uploading_file_info": "Įkeliama failo info", "birthdate_saved": "Sėkmingai išsaugota gimimo data", "blurred_background": "Neryškus fonas", "bugs_and_feature_requests": "Klaidų ir funkcijų užklausos", @@ -345,6 +394,7 @@ "clear_all": "Išvalyti viską", "clear_message": "Išvalyti pranešimą", "clear_value": "Išvalyti reikšmę", + "client_cert_invalid_msg": "Netinkamas sertifikato failas arba neteisingas slaptažodis", "close": "Uždaryti", "collapse": "Suskleisti", "collapse_all": "Suskleisti viską", @@ -420,8 +470,11 @@ "do_not_show_again": "Daugiau nerodyti šio pranešimo", "documentation": "Dokumentacija", "download": "Atsisiųsti", + "download_include_embedded_motion_videos_description": "Pridėti prie judesio nuotraukų įterptus video kaip atskirą failą", "download_settings": "Atsisiųsti", "downloading": "Siunčiama", + "downloading_asset_filename": "Parsisiunčiamas resursas {filename}", + "drop_files_to_upload": "Užkelkite failus bet kurioje vietoje kad įkeltumėte", "duplicates": "Dublikatai", "duplicates_description": "Sutvarkykite kiekvieną elementų grupę nurodydami elementus, kurie yra dublikatai (jei tokių yra)", "duration": "Trukmė", @@ -493,6 +546,7 @@ "unable_to_delete_import_path": "Nepavyksta ištrinti importavimo kelio", "unable_to_delete_shared_link": "Nepavyko ištrinti bendrinimo nuorodos", "unable_to_delete_user": "Nepavyksta ištrinti naudotojo", + "unable_to_download_files": "Nepavyksta atsisiųsti failų", "unable_to_edit_exclusion_pattern": "Nepavyksta redaguoti išimčių šablono", "unable_to_edit_import_path": "Nepavyksta redaguoti išimčių kelio", "unable_to_enter_fullscreen": "Nepavyksta pereiti į viso ekrano režimą", @@ -517,6 +571,7 @@ "unable_to_scan_library": "Nepavyksta nuskaityti bibliotekos", "unable_to_set_feature_photo": "Nepavyksta nustatyti mėgstamiausios nuotraukos", "unable_to_set_profile_picture": "Nepavyksta nustatyti profilio nuotraukos", + "unable_to_submit_job": "Napvyko sukurti užduoties", "unable_to_trash_asset": "Nepavyko perkelti į šiukšliadėžę", "unable_to_upload_file": "Nepavyksta įkelti failo" }, @@ -539,6 +594,7 @@ "features_setting_description": "Valdyti aplikacijos funkcijas", "file_name": "Failo pavadinimas", "file_name_or_extension": "Failo pavadinimas arba plėtinys", + "filename": "Failopavadinimas", "filetype": "Failo tipas", "filter_people": "Filtruoti žmones", "folders": "Aplankai", @@ -574,8 +630,9 @@ }, "invite_people": "Kviesti žmones", "invite_to_album": "Pakviesti į albumą", + "ios_debug_info_no_sync_yet": "Jokia background sync užduotis dar nebuvo paleista", "items_count": "{count, plural, one {# elementas} few {# elementai} other {# elementų}}", - "jobs": "Darbai", + "jobs": "Užduotys", "keep": "Palikti", "keep_all": "Palikti visus", "keyboard_shortcuts": "Spartieji klaviatūros klavišai", @@ -660,6 +717,7 @@ "no_results": "Nerasta", "no_results_description": "Pabandykite sinonimą arba bendresnį raktažodį", "not_in_any_album": "Nė viename albume", + "note_apply_storage_label_to_previously_uploaded assets": "Pastaba: Priskirti Saugyklos Žymą prie ankčiau įkeltų ištekliu, paleiskite šį", "notes": "Pastabos", "notification_toggle_setting_description": "Įjungti el. pašto pranešimus", "notifications": "Pranešimai", @@ -707,6 +765,14 @@ "place": "Vieta", "places": "Vietos", "play_memories": "Leisti atsiminimus", + "profile": "Profilis", + "profile_drawer_app_logs": "Logai", + "profile_drawer_client_out_of_date_major": "Mobili aplikacija jau pasenusios versijos. Prašome atsinaujinti į paskutinę didžiąją versiją.", + "profile_drawer_client_out_of_date_minor": "Mobili aplikacija jau pasenusios versijos. Prašome atsinaujinti į paskutinę mažąją versiją.", + "profile_drawer_client_server_up_to_date": "Klientas ir Serveris yra atnaujinti", + "profile_drawer_github": "GitHub", + "profile_drawer_server_out_of_date_major": "Serveris jau yra pasenusios versijos. Prašome atsinaujinti į paskutinę didžiąją versiją.", + "profile_drawer_server_out_of_date_minor": "Serveris jau yra pasenusios versijos. Prašome atsinaujinti į paskutinę mažąją versiją.", "profile_image_of_user": "{user} profilio nuotrauka", "profile_picture_set": "Profilio nuotrauka nustatyta.", "public_album": "Viešas albumas", @@ -893,7 +959,7 @@ "show_albums": "Rodyti albumus", "show_all_people": "Rodyti visus asmenis", "show_and_hide_people": "Rodyti ir paslėpti žmones", - "show_file_location": "Rodyti rinkmenos vietą", + "show_file_location": "Rodyti failo vietą", "show_gallery": "Rodyti galeriją", "show_hidden_people": "Rodyti paslėptus asmenis", "show_in_timeline": "Rodyti laiko skalėje", @@ -936,6 +1002,7 @@ "status": "Statusas", "stop_casting": "Nutraukti transliavimą", "storage": "Saugykla", + "storage_label": "Saugyklos Žyma", "storage_usage": "Naudojama {used} iš {available}", "submit": "Pateikti", "suggestions": "Pasiūlymai", diff --git a/i18n/lv.json b/i18n/lv.json index 3fcf228612..01a506a90a 100644 --- a/i18n/lv.json +++ b/i18n/lv.json @@ -88,11 +88,14 @@ "machine_learning_url_description": "Mašīnmācīšanās servera URL", "manage_concurrency": "Vienlaicīgas darbības pārvaldība", "manage_log_settings": "Žurnāla iestatījumu pārvaldība", + "map_dark_style": "Tumšais stils", "map_gps_settings": "Kartes un GPS iestatījumi", "map_gps_settings_description": "Karšu un GPS (apgrieztās ģeokodēšanas) iestatījumu pārvaldība", + "map_light_style": "Gaišais stils", "map_manage_reverse_geocoding_settings": "Reversās ģeokodēšanas iestatījumu pārvaldība", "map_settings": "Karte", "map_settings_description": "Kartes iestatījumu pārvaldība", + "map_style_description": "URL uz style.json kartes tēmu", "metadata_extraction_job": "Metadatu iegūšana", "metadata_settings": "Metadatu iestatījumi", "metadata_settings_description": "Metadatu iestatījumu pārvaldība", @@ -110,6 +113,10 @@ "notification_email_test_email_sent": "Uz {email} ir nosūtīts testa e-pasts. Lūdzu, pārbaudi savu iesūtni.", "notification_settings": "Paziņojumu iestatījumi", "notification_settings_description": "Paziņojumu iestatījumu, tostarp e-pasta, pārvaldība", + "oauth_auto_launch": "Palaist automātiski", + "oauth_auto_launch_description": "Pie navigācijas uz pieslēgšanās lapu automātiski uzsākt OAuth pieslēgšanās plūsmu", + "oauth_auto_register": "Automātiska reģistrācija", + "oauth_auto_register_description": "Pēc pieslēgšanās ar OAuth automātiski reģistrēt jaunus lietotājus", "oauth_button_text": "Pogas teksts", "oauth_enable_description": "Pieslēgties ar OAuth", "oauth_settings": "OAuth", @@ -124,6 +131,7 @@ "require_password_change_on_login": "Pieprasīt lietotājam mainīt paroli pēc pirmās pieteikšanās", "scanning_library": "Skenē bibliotēku", "search_jobs": "Meklēt uzdevumus…", + "server_public_users": "Publiski lietotāji", "server_settings": "Servera iestatījumi", "server_settings_description": "Servera iestatījumu pārvaldība", "server_welcome_message": "Sveiciena ziņa", @@ -134,7 +142,12 @@ "storage_template_path_length": "Aptuvenais ceļa garuma ierobežojums: {length, number}/{limit, number}", "storage_template_settings": "Krātuves veidne", "system_settings": "Sistēmas iestatījumi", + "template_email_available_tags": "Sagatavē var izmantot šos mainīgos: {tags}", + "template_email_if_empty": "Ja sagatave ir tukša, tiks izmantots noklusējuma e-pasts.", + "template_email_invite_album": "Albuma ielūguma sagatave", "template_email_preview": "Priekšskatījums", + "template_email_settings": "E-pasta sagataves", + "template_email_update_album": "Atjaunināt albuma sagatavi", "template_settings_description": "Pielāgotu paziņojumu veidņu pārvaldība", "theme_custom_css_settings": "Pielāgots CSS", "theme_custom_css_settings_description": "Cascading Style Sheets ļauj pielāgot Immich izskatu.", @@ -219,7 +232,9 @@ "are_these_the_same_person": "Vai šī ir tā pati persona?", "asset_action_delete_err_read_only": "Nevar dzēst read only aktīvu(-s), notiek izlaišana", "asset_action_share_err_offline": "Nevar iegūt bezsaistes aktīvu(-s), notiek izlaišana", + "asset_added_to_album": "Pievienots albumam", "asset_adding_to_album": "Pievieno albumam…", + "asset_description_updated": "Faila apraksts ir atjaunināts", "asset_list_group_by_sub_title": "Grupēt pēc", "asset_list_layout_settings_dynamic_layout_title": "Dinamiskais izkārtojums", "asset_list_layout_settings_group_automatically": "Automātiski", @@ -228,6 +243,7 @@ "asset_list_layout_sub_title": "Izvietojums", "asset_list_settings_subtitle": "Fotorežģa izkārtojuma iestatījumi", "asset_list_settings_title": "Fotorežģis", + "asset_uploaded": "Augšupielādēts", "asset_uploading": "Augšupielādē…", "asset_viewer_settings_title": "Aktīvu Skatītājs", "assets": "aktīvi", @@ -339,6 +355,7 @@ "clear": "Notīrīt", "clear_all": "Notīrīt visu", "clear_value": "Notīrīt vērtību", + "client_cert_subtitle": "Atbalsta tikai PKCS12 (.p12, .pfx) formātu. Sertifikātu importēšana/noņemšana ir pieejama tikai pirms pieslēgšanās", "client_cert_title": "SSL klienta sertifikāts", "clockwise": "Pulksteņrādītāja virzienā", "close": "Aizvērt", @@ -504,7 +521,11 @@ "features_setting_description": "Lietotnes funkciju pārvaldība", "filename": "Faila nosaukums", "filetype": "Faila tips", + "folder": "Mape", + "folder_not_found": "Mape nav atrasta", "folders": "Mapes", + "gcast_enabled": "Google Cast", + "get_help": "Saņemt palīdzību", "haptic_feedback_switch": "Iestatīt haptisku reakciju", "haptic_feedback_title": "Haptiska Reakcija", "has_quota": "Ir kvota", @@ -529,10 +550,12 @@ "hour": "Stunda", "id": "ID", "image": "Attēls", + "image_saved_successfully": "Attēls saglabāts", "image_viewer_page_state_provider_download_started": "Lejupielāde Uzsākta", "image_viewer_page_state_provider_download_success": "Lejupielāde izdevās", "image_viewer_page_state_provider_share_error": "Kopīgošanas Kļūda", "immich_logo": "Immich logo", + "immich_web_interface": "Immich tīmekļa saskarne", "import_from_json": "Importēt no JSON", "import_path": "Importa ceļš", "in_albums": "{count, plural, one {# albumā} other {# albumos}}", @@ -545,18 +568,25 @@ "night_at_midnight": "Katru dienu pusnaktī", "night_at_twoam": "Katru dienu 2.00 naktī" }, + "invalid_date": "Nederīgs datums", + "invalid_date_format": "Nederīgs datuma formāts", "invite_people": "Ielūgt cilvēkus", "invite_to_album": "Uzaicināt albumā", + "ios_debug_info_last_sync_at": "Pēdējā sinhronizācija {dateTime}", + "ios_debug_info_no_processes_queued": "Nav ierindotu fona procesu", "jobs": "Uzdevumi", "keep": "Paturēt", "keep_all": "Paturēt visus", + "keep_this_delete_others": "Paturēt šo, dzēst citus", "keyboard_shortcuts": "Tastatūras saīsnes", "language": "Valoda", + "language_search_hint": "Meklēt valodas...", "language_setting_description": "Izvēlieties vēlamo valodu", "last_seen": "Pēdējo reizi redzēts", "latest_version": "Jaunākā versija", "latitude": "Ģeogrāfiskais platums", "leave": "Paturēt", + "lens_model": "Objektīva modelis", "let_others_respond": "Ļaut citiem atbildēt", "level": "Līmenis", "library": "Bibliotēka", @@ -600,7 +630,7 @@ "longitude": "Ģeogrāfiskais garums", "look": "Izskats", "loop_videos_description": "Iespējot, lai automātiski videoklips tiktu cikliski palaists detaļu skatītājā.", - "make": "Firma", + "make": "Ražotājs", "manage_shared_links": "Kopīgoto saišu pārvaldība", "manage_sharing_with_partners": "Koplietošanas ar partneriem pārvaldība", "manage_the_app_settings": "Lietotnes iestatījumu pārvaldība", @@ -738,48 +768,78 @@ "permission_onboarding_request": "Immich nepieciešama atļauja skatīt jūsu fotoattēlus un videoklipus.", "person": "Persona", "photos": "Fotoattēli", + "photos_and_videos": "Fotogrāfijas un video", "photos_from_previous_years": "Fotogrāfijas no iepriekšējiem gadiem", + "pick_a_location": "Izvēlies atrašanās vietu", "pin_verification": "PIN koda pārbaude", "place": "Atrašanās vieta", "places": "Vietas", + "play": "Atskaņot", + "play_memories": "Atskaņot atmiņas", "please_auth_to_access": "Lai piekļūtu, lūdzu, autentificējieties", "port": "Ports", "preferences_settings_title": "Iestatījumi", "preview": "Priekšskatījums", "previous": "Iepriekšējais", + "previous_memory": "Iepriekšējā atmiņa", "privacy": "Privātums", "profile": "Profils", "profile_drawer_app_logs": "Žurnāli", "profile_drawer_client_out_of_date_major": "Mobilā lietotne ir novecojusi. Lūdzu, atjaunini to uz jaunāko pamatversiju.", "profile_drawer_client_out_of_date_minor": "Mobilā lietotne ir novecojusi. Lūdzu, atjaunini to uz jaunāko papildversiju.", "profile_drawer_client_server_up_to_date": "Klients un serveris ir atjaunināti", + "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "Serveris ir novecojis. Lūdzu, atjaunini to uz jaunāko pamatversiju.", "profile_drawer_server_out_of_date_minor": "Serveris ir novecojis. Lūdzu, atjaunini to uz jaunāko papildversiju.", + "profile_image_of_user": "{user} profila attēls", + "profile_picture_set": "Profila attēls iestatīts.", + "public_album": "Publisks albums", "purchase_account_info": "Atbalstītājs", + "purchase_activated_subtitle": "Paldies, ka atbalstāt Immich un atvērtā koda programmatūru", + "purchase_activated_time": "Aktivizēts {date}", + "purchase_activated_title": "Tava atslēga ir sekmīgi aktivizēta", + "purchase_button_activate": "Aktivizēt", "purchase_button_buy": "Pirkt", + "purchase_button_buy_immich": "Iegādāties Immich", "purchase_button_never_show_again": "Nekad vairs nerādīt", "purchase_button_reminder": "Atgādināt man pēc 30 dienām", "purchase_button_remove_key": "Noņemt atslēgu", "purchase_button_select": "Izvēlēties", + "purchase_failed_activation": "Neizdevās aktivizēt! Lūdzu, pārbaudi savu e-pastu, lai iegūtu pareizo produkta atslēgu!", "purchase_individual_description_2": "Atbalstītāja statuss", "purchase_input_suggestion": "Vai tev ir produkta atslēga? Ievadi atslēgu zemāk", "purchase_license_subtitle": "Nopērc Immich licenci, lai atbalstītu turpmāku pakalpojuma attīstību", "purchase_lifetime_description": "Pirkums uz mūžu", "purchase_option_title": "IEGĀDES IESPĒJAS", + "purchase_panel_info_1": "Immich veidošanai ir nepieciešams daudz laika un pūļu, un pie tā strādā pilna laika inženieri, lai padarītu to pēc iespējas labāku. Mūsu misija ir panākt, lai atvērtā koda programmatūra un ētiska uzņēmējdarbības prakse kļūtu par ilgtspējīgu ienākumu avotu izstrādātājiem un izveidotu privātumu respektējošu ekosistēmu ar reālām alternatīvām ekspluatējošiem mākoņpakalpojumiem.", + "purchase_panel_info_2": "Tā kā mēs esam apņēmušies nepievienot maksas funkcionalitāti, šis pirkums nepiešķirs jums nekādas papildu Immich funkcijas. Mēs paļaujamies uz tādiem lietotājiem kā jūs, lai atbalstītu nepārtrauktu Immich attīstību.", "purchase_panel_title": "Atbalsti projektu", "purchase_remove_product_key": "Noņemt produkta atslēgu", + "purchase_remove_product_key_prompt": "Vai tiešām vēlaties noņemt produkta atslēgu?", "purchase_remove_server_product_key": "Noņemt servera produkta atslēgu", + "purchase_remove_server_product_key_prompt": "Vai tiešām vēlaties noņemt Servera produkta atslēgu?", "purchase_server_description_1": "Visam serverim", "purchase_server_description_2": "Atbalstītāja statuss", "purchase_server_title": "Serveris", "purchase_settings_server_activated": "Servera produkta atslēgu pārvalda administrators", "rating_clear": "Noņemt vērtējumu", + "rating_description": "Rādīt EXIF vērtējumu informācijas panelī", + "reaction_options": "Reakcijas iespējas", "read_changelog": "Lasīt izmaiņu sarakstu", "recently_added_page_title": "Nesen Pievienotais", + "refresh_thumbnails": "Atsvaidzināt sīktēlus", + "refreshed": "Atsvaidzināts", "remove": "Noņemt", + "remove_assets_title": "Izņemt failus?", + "remove_deleted_assets": "Izņemt dzēstos failus", "remove_from_album": "Noņemt no albuma", + "remove_from_album_action_prompt": "No albuma izņemti {count} faili", "remove_from_favorites": "Noņemt no izlases", + "remove_from_lock_folder_action_prompt": "No slēgtās mapes izņemti {count} faili", "remove_from_locked_folder": "Izņemt no slēgtās mapes", + "remove_memory": "Noņemt atmiņu", + "remove_photo_from_memory": "Noņemt fotogrāfiju no šīs atmiņas", + "remove_url": "Noņemt URL", "remove_user": "Noņemt lietotāju", "removed_api_key": "Noņēma API atslēgu: {name}", "removed_from_archive": "Noņēma no arhīva", @@ -790,6 +850,11 @@ "replace_with_upload": "Aizstāt ar augšupielādi", "require_user_to_change_password_on_first_login": "Pieprasīt lietotājam mainīt paroli pēc pirmās pieteikšanās", "rescan": "Pārskenēt atkārtoti", + "reset": "Atiestatīt", + "reset_password": "Atiestatīt paroli", + "reset_people_visibility": "Atiestatīt cilvēku redzamību", + "reset_pin_code": "Atiestatīt PIN kodu", + "reset_to_default": "Atiestatīt noklusējuma iestatījumus", "resolve_duplicates": "Atrisināt dublēšanās gadījumus", "resolved_all_duplicates": "Visi dublikāti ir atrisināti", "restore": "Atjaunot", @@ -802,6 +867,7 @@ "role_editor": "Redaktors", "role_viewer": "Skatītājs", "save": "Saglabāt", + "save_to_gallery": "Saglabāt galerijā", "saved_api_key": "API atslēga saglabāta", "saved_profile": "Profils saglabāts", "saved_settings": "Iestatījumi saglabāti", @@ -813,9 +879,23 @@ "scanning_for_album": "Skenē albumu...", "search": "Meklēt", "search_albums": "Meklēt albumus", + "search_by_context": "Meklēt pēc konteksta", + "search_by_description": "Meklēt pēc apraksta", + "search_by_description_example": "Pārgājiens Līgatnē", + "search_by_filename": "Meklēt pēc faila nosaukuma vai paplašinājuma", "search_by_filename_example": "piemēram, IMG_1234.JPG vai PNG", + "search_camera_make": "Meklēt pēc fotokameras ražotāja...", + "search_camera_model": "Meklēt pēc fotokameras modeļa...", + "search_city": "Meklēt pēc pilsētas...", + "search_country": "Meklēt pēc valsts...", "search_filter_apply": "Lietot filtru", + "search_filter_camera_title": "Izvēlies fotokameras veidu", + "search_filter_date": "Datums", "search_filter_display_option_not_in_album": "Nav albumā", + "search_filter_filename": "Meklēt pēc faila nosaukuma", + "search_filter_location": "Atrašanās vieta", + "search_filter_location_title": "Izvēlies atrašanās vietu", + "search_for_existing_person": "Meklēt esošu personu", "search_no_people": "Nav cilvēku", "search_no_people_named": "Nav cilvēku ar vārdu \"{name}\"", "search_page_categories": "Kategorijas", @@ -836,6 +916,7 @@ "second": "Sekunde", "select_album_cover": "Izvēlieties albuma vāciņu", "select_all_duplicates": "Atlasīt visus dublikātus", + "select_from_computer": "Izvēlēties no datora", "select_photos": "Fotoattēlu Izvēle", "select_user_for_sharing_page_err_album": "Neizdevās izveidot albumu", "server_info_box_app_version": "Aplikācijas Versija", @@ -1031,6 +1112,7 @@ "viewer_stack_use_as_main_asset": "Izmantot kā Galveno Aktīvu", "viewer_unstack": "At-Stekot", "waiting": "Gaida", + "warning": "Brīdinājums", "week": "Nedēļa", "welcome": "Laipni lūgti", "welcome_to_immich": "Laipni lūgti Immich", diff --git a/i18n/mn.json b/i18n/mn.json index 8a18a1d5e5..85092fef0d 100644 --- a/i18n/mn.json +++ b/i18n/mn.json @@ -15,10 +15,13 @@ "add_a_name": "Нэр өгөх", "add_a_title": "Гарчиг оруулах", "add_endpoint": "Endpoint нэмэх", + "add_import_path": "Импортлох зам нэмэх", "add_location": "Байршил оруулах", "add_more_users": "Өөр хэрэглэгчид нэмэх", "add_partner": "Хамтрагч нэмэх", + "add_path": "Зам нэмэх", "add_photos": "Зураг нэмэх", + "add_tag": "Шошго нэмэх", "add_to_album": "Цомогт оруулах", "add_to_album_bottom_sheet_added": "{album}-д нэмлээ", "add_to_album_bottom_sheet_already_exists": "{album}-д аль хэдийн орсон байна", @@ -28,6 +31,7 @@ "added_to_favorites": "Дуртай зурганд нэмэх", "added_to_favorites_count": "Дуртай зурагнуудад {count, number} нэмэгдлээ", "admin": { + "admin_user": "Админ хэрэглэгч", "authentication_settings": "Танин нэвтрэлт тохиргоо", "authentication_settings_description": "Нууц үгийн удирдлага, OAuth болон бусад танин нэвтрэлтийн тохиргоо", "authentication_settings_disable_all": "Бүх нэвтрэх аргуудыг идэвхигүй болгохдоо итгэлтэй байна уу? Нэвтрэх үйлдэл бүрэн идэвхигүй болно.", @@ -35,11 +39,15 @@ "backup_database": "Өгөгдлийн сангийн дамп үүсгэх", "backup_database_enable_description": "Өгөгдлийн сангийн дамп идэвхижүүлэх", "backup_keep_last_amount": "Өмнөх хэдэн дампыг хадгалах вэ", + "backup_settings": "Датабаз дамп тохиргоо", + "backup_settings_description": "Датабазаас дамп хийх тохиргоонууд.", "config_set_by_file": "Тохиргоог одоогоор файлаас авч байна", "confirm_delete_library": "Та {library} гэсэн санг устгахдаа итгэлтэй байна уу?", "confirm_delete_library_assets": "Та энэ санг устгахдаа итгэлтэй байна уу? Энэ үйлдлээр таны {count, plural, one {# contained asset} other {all # contained assets}} серверээс устах бөгөөд буцаах боломжгүй. Гэхдээ файлууд диск дээрээ үлдэнэ.", "confirm_email_below": "Баталгаажуулахын тулд та \"{email}\" гэж бичнэ үү", "confirm_reprocess_all_faces": "Бүх царайг дахин процесс хийх үү? Тэгвэл бүх нэрс арилах болно.", + "confirm_user_password_reset": "{user}-ийн нууц үгийг дахин тохируулах уу?", + "confirm_user_pin_code_reset": "{user} хэрэглэгчийн PIN code дахин тохируулах уу?", "face_detection": "Нүүр илрүүлэх", "image_quality": "Чанар", "job_settings": "Ажлын тохиргоо", diff --git a/i18n/mr.json b/i18n/mr.json index 6c2ab7406c..781174c79e 100644 --- a/i18n/mr.json +++ b/i18n/mr.json @@ -4,6 +4,7 @@ "account_settings": "खाते व्यवस्था", "acknowledge": "मान्यता", "action": "कृती", + "action_common_update": "अद्ययावत", "actions": "कृत्ये", "active": "सक्रिय", "activity": "गतिविधि", @@ -13,6 +14,7 @@ "add_a_location": "एक स्थळ टाका", "add_a_name": "नाव टाका", "add_a_title": "शीर्षक टाका", + "add_endpoint": "एंडपॉइंट जोडा", "add_exclusion_pattern": "अपवाद नमुना जोडा", "add_import_path": "आयात मार्ग टाका", "add_location": "स्थळ टाका", @@ -20,8 +22,11 @@ "add_partner": "भागीदार जोडा", "add_path": "मार्ग टाका", "add_photos": "छायाचित्रे जोडा", + "add_tag": "टॅग जोडा", "add_to": "त्या मध्ये जोडा…", "add_to_album": "संग्रहात टाका", + "add_to_album_bottom_sheet_added": "{album} मध्ये जोडले गेले", + "add_to_album_bottom_sheet_already_exists": "आधीच {album} मध्ये आहे", "add_to_shared_album": "सामायिक संग्रहात टाका", "add_url": "URL जोडा", "added_to_archive": "संग्रहालयात जोडले", @@ -29,6 +34,7 @@ "added_to_favorites_count": "आवडत्यात {count, number} टाकले", "admin": { "add_exclusion_pattern_description": "अपवाद अनुकूलन जोडा. ** आणि ? या उपयोगात ग्लोबिंग समर्थित आहे. कोणत्याही \"Raw\" नावाच्या निर्देशिकेमधील सर्व खतावण्या दुर्लक्षीत करण्यासाठी \"/Raw/\" वापरा. \".tif\" या सामान्य पथावर समाप्त असलेल्या सर्व खतावण्या दुर्लक्षीत करण्यासाठी \"**/.tif\" वापरा. विशिष्ट पथ दुर्लक्ष करण्यासाठी \"/path/to/ignore/**\" वापरा.", + "admin_user": "प्रशासन वापरकर्ता", "asset_offline_description": "ही बाह्य संग्रहालय संसाधने डिस्कवर नाहीत आणि ट्रॅशमध्ये विस्थापित केली गेली आहेत. जर फाइल संग्रहालयामध्ये विस्थापित केली गेली आहे, तर नवीन संगत संसाधन किंव्हा रोजीनिशी मध्ये तपासा. हा संसाधन वापर करण्यासाठी कृपया निम्नलिखित खतावणी पथाला इम्मीच द्वारा वापरू शकतो याची तपासणी करा आणि तो संग्रहालय चाळा.", "authentication_settings": "प्रमाणीकरण साधक", "authentication_settings_description": "परवलीचा शब्द, OAuth आणि अन्य प्रमाणीकरण प्रबंधन करा", @@ -47,6 +53,7 @@ "confirm_email_below": "पुष्टी करण्या साठी, खाली \"{email}\" टंकलिखित करा", "confirm_reprocess_all_faces": "तुम्हाला खात्री आहे का की तुम्हाला सर्व चेहऱ्यांवर पुन्हा प्रक्रिया करायची आहे? यामुळे नाव दिलेले लोकही साफ होतील.", "confirm_user_password_reset": "तुम्हाला नक्की {user} चा परवलीचा शब्द बदलायचा आहे का?", + "confirm_user_pin_code_reset": "तुम्हाला नक्की {user} चा पिन कोड रीसेट करायचा आहे का?", "create_job": "कार्य बनवा", "cron_expression": "वेळापत्रक सूत्र", "cron_expression_description": "चाळन्याचे वेळापत्रक क्रॉन पद्धती ने करा. अधिक माहिती साठी पहा: क्रॉन गुरु", @@ -55,6 +62,16 @@ "duplicate_detection_job_description": "सारख्या छायाचित्रांचा शोध घेण्यासाठी यांत्रिकी प्रशिक्षण द्या. ही कार्यक्षमता चतुर शोधप्रणालीवर अवलंबून आहे", "exclusion_pattern_description": "आपले संग्रहालय चाळताना अपवाद नमुने आपल्याला खतावण्या आणि र्निर्देशिकेला दुर्लक्षीत करू देतात. आपल्याकडे कच्च्या खतावण्या सारख्या आयात करू इच्छित नसलेल्या असंपादित (RAW) खतावण्या असलेल्या निर्देशिका असल्यास हे उपयुक्त आहे.", "external_library_management": "बाह्य संग्रहालय व्यवस्थापन", - "face_detection": "मुख संशोधन" + "face_detection": "मुख संशोधन", + "face_detection_description": "मशीन लर्निंग वापरून मालमत्तांमधील चेहरे शोधा. व्हिडिओंसाठी, फक्त थंबनेलचा विचार केला जातो. \"रिफ्रेश\" (पुन्हा) सर्व मालमत्तांवर प्रक्रिया करते. \"रीसेट\" याव्यतिरिक्त सर्व वर्तमान चेहरा डेटा साफ करते. \"गहाळ\" मालमत्तांवर अद्याप प्रक्रिया न केलेल्या रांगेत ठेवते. शोधलेले चेहरे फेस डिटेक्शन पूर्ण झाल्यानंतर फेशियल रेकग्निशनसाठी रांगेत ठेवले जातील, त्यांना विद्यमान किंवा नवीन लोकांमध्ये गटबद्ध केले जाईल.", + "facial_recognition_job_description": "शोधलेले चेहरे लोकांमध्ये गटबद्ध करा. हे चरण चेहरा शोधणे पूर्ण झाल्यानंतर चालते. \"रीसेट करा\" (पुन्हा) सर्व चेहरे क्लस्टर कर. \"गहाळ\" चेहरे रांगेत समाविष्ट करते ज्यांना नियुक्त केलेली व्यक्ती नाही.", + "failed_job_command": "{command} कमांड जॉबसाठी अयशस्वी झाला: {job}", + "force_delete_user_warning": "सावधान: हे वापरकर्ता आणि सर्व मालमत्ता ताबडतोब काढून टाकेल. हे पूर्ववत करता येणार नाही आणि फायली पुनर्प्राप्त करता येणार नाहीत.", + "image_format": "फॉरमॅट", + "image_format_description": "WebP JPEG पेक्षा लहान फायली तयार करते, परंतु एन्कोड करण्यास हळू असते.", + "image_fullsize_description": "झूम इन केल्यावर वापरल्या जाणाऱ्या स्ट्रिप केलेल्या मेटाडेटासह पूर्ण आकाराची प्रतिमा", + "image_fullsize_enabled": "पूर्ण-आकारातील प्रतिमा निर्मिती", + "image_fullsize_enabled_description": "वेब-फ्रेंडली नसलेल्या फॉरमॅटसाठी पूर्ण-आकाराची प्रतिमा तयार करा. जेव्हा \"embedded preview\" चालुआसेल तेव्हा, \"embedded preview\" थेट रूपांतरणाशिवाय वापरले जातात. JPEG सारख्या वेब-फ्रेंडली फॉरमॅटवर परिणाम होत नाही.", + "image_fullsize_quality_description": "१-१०० पर्यंत पूर्ण-आकारातील प्रतिमा गुणवत्ता. जास्त तेव्हडे चांगले, परंतु मोठ्या फायली तयार करते." } } diff --git a/i18n/ms.json b/i18n/ms.json index cfe935102e..e7e0432a4c 100644 --- a/i18n/ms.json +++ b/i18n/ms.json @@ -14,6 +14,7 @@ "add_a_location": "Tambah lokasi", "add_a_name": "Tambah nama", "add_a_title": "Tambah tajuk", + "add_endpoint": "Tambah titik akhir", "add_exclusion_pattern": "Tambahkan corak pengecualian", "add_import_path": "Tambahkan laluan import", "add_location": "Tambah lokasi", @@ -21,6 +22,7 @@ "add_partner": "Tambah rakan", "add_path": "Tambah laluan", "add_photos": "Tambah gambar", + "add_tag": "Tambah tag", "add_to": "Tambah ke…", "add_to_album": "Tambah ke album", "add_to_album_bottom_sheet_added": "Dimasukkan ke {album}", @@ -32,17 +34,18 @@ "added_to_favorites_count": "Menambahkan {count, number} ke kegemaran", "admin": { "add_exclusion_pattern_description": "Tambahkan corak pengecualian. Globbing menggunakan *, **, dan ? disokong. Untuk mengabaikan semua fail dalam mana-mana direktori bernama \"Raw\", gunakan \"**/Raw/**\". Untuk mengabaikan semua fail yang berakhir dengan \".tif\", gunakan \"**/*.tif\". Untuk mengabaikan laluan mutlak, gunakan \"/path/to/ignore/**\".", + "admin_user": "Pengguna Pentadbir", "asset_offline_description": "Aset pustaka luaran ini tidak lagi ditemui pada cakera dan telah dialihkan ke sampah. Jika fail telah dialihkan dalam pustaka, semak garis masa anda untuk aset baharu yang sepadan. Untuk memulihkan aset ini, sila pastikan bahawa laluan fail di bawah boleh diakses oleh Immich dan mengimbas pustaka.", "authentication_settings": "Tetapan Pengesahan", "authentication_settings_description": "Urus kata laluan, OAuth dan tetapan pengesahan lain", "authentication_settings_disable_all": "Adakah anda pasti mahu melumpuhkan semua kaedah log masuk? Log masuk akan dilumpuhkan sepenuhnya.", "authentication_settings_reenable": "Untuk menghidupkan semula, guna Arahan Pelayan.", "background_task_job": "Tugas Latar Belakang", - "backup_database": "Sandar pangkalan data", - "backup_database_enable_description": "Aktifkan sandaran pangkalan data", - "backup_keep_last_amount": "Jumlah sandaran sebelumnya yang hendak disimpan", - "backup_settings": "Tetapan Sandaran", - "backup_settings_description": "Urus tetapan sandaran pangkalan data", + "backup_database": "Buat Salinan Pangkalan Data", + "backup_database_enable_description": "Dayakan salinan pangkalan data", + "backup_keep_last_amount": "Jumlah salinan pangkalan data sebelumnya untuk disimpan", + "backup_settings": "Tetapan Salinan Pangkalan Data", + "backup_settings_description": "Urus tetapan salinan pangkalan data.", "cleared_jobs": "Kerja telah dibersihkan untuk: {job}", "config_set_by_file": "Konfigurasi kini ditetapkan oleh fail konfigurasi", "confirm_delete_library": "Adakah anda pasti mahu memadamkan {library}?", @@ -72,7 +75,7 @@ "image_fullsize_quality_description": "Kualiti imej bersaiz penuh dari 1-100. Lebih tinggi adalah lebih baik, tetapi menghasilkan fail yang lebih besar.", "image_fullsize_title": "Tetapan Imej bersaiz penuh", "image_prefer_embedded_preview": "Cadangkan pratonton terbenam", - "image_prefer_embedded_preview_setting_description": "Gunakan pratonton terbenam dalam foto RAW sebagai input kepada pemprosesan imej apabila tersedia. Cara ini boleh menghasilkan warna yang lebih tepat untuk sesetengah imej, tetapi kualiti pratonton bergantung pada kamera dan imej mungkin mempunyai lebih banyak artifak mampatan.", + "image_prefer_embedded_preview_setting_description": "Gunakan pratonton terbenam dalam foto RAW sebagai input untuk pemprosesan imej apabila tersedia. Ini boleh menghasilkan warna yang lebih tepat untuk sesetengah imej, tetapi kualiti pratonton bergantung kepada kamera dan imej mungkin mengandungi lebih banyak artifak pemampatan.", "image_prefer_wide_gamut": "Cadangkan warna gamut yang luas", "image_prefer_wide_gamut_setting_description": "Gunakan Paparan P3 untuk lakaran kenit. Ini lebih baik mengekalkan kerancakan imej dengan ruang warna yang luas, tetapi imej mungkin kelihatan berbeza pada peranti lama dengan versi penyemak imbas lama. Imej sRGB disimpan sebagai sRGB untuk mengelakkan peralihan warna.", "image_preview_description": "Imej bersaiz sederhana dengan metadata yang dilucutkan, digunakan semasa melihat aset tunggal dan untuk pembelajaran mesin", @@ -102,7 +105,7 @@ "library_scanning_enable_description": "Dayakan pengimbasan perpustakaan berkala", "library_settings": "Perpustakaan Luaran", "library_settings_description": "Urus tetapan perpustakaan luaran", - "library_tasks_description": "Laksanakan tugas perpustakaan", + "library_tasks_description": "Imbas pustaka luaran untuk aset yang baru dan/atau telah diubah", "library_watching_enable_description": "Perhatikan perpustakaan luaran untuk perubahan fail", "library_watching_settings": "Perhati perpustakaan (EKSPERIMEN)", "library_watching_settings_description": "Perhati fail yang diubah secara automatik", @@ -137,7 +140,7 @@ "machine_learning_smart_search_description": "Cari imej secara semantik menggunakan pembenaman CLIP", "machine_learning_smart_search_enabled": "Dayakan carian pintar", "machine_learning_smart_search_enabled_description": "Jika ditutup, gambar-gambar tidak akan dikodkan untuk carian pintar.", - "machine_learning_url_description": "URL pelayan pembelajaran mesin. Jika lebih daripada satu URL disediakan, setiap pelayan akan dicuba satu demi satu sehingga satu menjawab dengan jayanya, mengikut urutan dari pertama hingga terakhir.", + "machine_learning_url_description": "URL pelayan pembelajaran mesin. Jika lebih daripada satu URL disediakan, setiap pelayan akan dicuba satu demi satu mengikut turutan, dari yang pertama hingga yang terakhir, sehingga salah satu memberi maklum balas yang berjaya. Pelayan yang tidak memberi maklum balas akan diabaikan sementara sehingga ia kembali dalam talian.", "manage_concurrency": "Urus Concurrency", "manage_log_settings": "Urus tetapan log", "map_dark_style": "Tema gelap", @@ -146,13 +149,15 @@ "map_gps_settings_description": "Urus Tetapan Peta & GPS (Geokod Terbalik)", "map_implications": "Ciri peta bergantung pada perkhidmatan jubin luaran (tiles.immich.cloud)", "map_light_style": "Tema terang", - "map_manage_reverse_geocoding_settings": "Urus tetapan Geocoding Songsang", + "map_manage_reverse_geocoding_settings": "Urus tetapan Penentuan Alamat Songsang", "map_reverse_geocoding": "Geokoding Sonsang", "map_reverse_geocoding_enable_description": "Dayakan pengekodan geo terbalik", "map_reverse_geocoding_settings": "Tetapan Pengekodan Geo Terbalik", "map_settings": "Peta", "map_settings_description": "Urus tetapan peta", "map_style_description": "URL ke tema peta style.json", + "memory_cleanup_job": "Pembersihan memori", + "memory_generate_job": "Penjanaan memori", "metadata_extraction_job": "Sari metadata", "metadata_extraction_job_description": "Sari maklumat metadata dari setiap aset, seperti GPS, muka-muka, dan pelaraian", "metadata_faces_import_setting": "Dayakan import muka", @@ -161,12 +166,26 @@ "metadata_settings_description": "Urus tetapan metadata", "migration_job": "Migrasi", "migration_job_description": "Pindahkan imej kecil untuk aset-aset dan muka-muka kepada struktur folder terkini", + "nightly_tasks_cluster_faces_setting_description": "Jalankan pengecaman wajah kepada wajah baharu yang dijumpai", + "nightly_tasks_cluster_new_faces_setting": "Kumpulan wajah baharu", + "nightly_tasks_database_cleanup_setting": "Tugasan membersihkan pangkalan data", + "nightly_tasks_database_cleanup_setting_description": "Membersihkan data lama, luput dari pangkalan data", + "nightly_tasks_generate_memories_setting": "Menjana memori", + "nightly_tasks_generate_memories_setting_description": "Mencipta memori dari aset", + "nightly_tasks_missing_thumbnails_setting": "Menjana lakaran kecil yang hilang", + "nightly_tasks_missing_thumbnails_setting_description": "Aturan aset tanpa lakaran kecil untuk janaan lakaran kecil", + "nightly_tasks_settings": "Tetapan tugasan malam", + "nightly_tasks_settings_description": "Mengurus tugasan malam", + "nightly_tasks_start_time_setting": "Masa mula", + "nightly_tasks_start_time_setting_description": "Masa di mana pelayan mula bekerja pada tugasan malam", + "nightly_tasks_sync_quota_usage_setting": "Penyelarasan penggunaan kuota", + "nightly_tasks_sync_quota_usage_setting_description": "Kemaskini kuota simpanan pengguna, berdasarkan kepada penggunaan terkini", "no_paths_added": "Tiada laluan yang ditambah", "no_pattern_added": "Tiada corak ditambah", "note_apply_storage_label_previous_assets": "Nota: Untuk menggunakan Label Storan pada aset yang dimuat naik sebelum ini, jalankan", "note_cannot_be_changed_later": "NOTA: Ini tidak boleh diubah kemudian!", "notification_email_from_address": "Dari alamat", - "notification_email_from_address_description": "Alamat e-mel penghantar, sebagai contoh: \"Immich Photo Server \"", + "notification_email_from_address_description": "Alamat e-mel penghantar, sebagai contoh: \"Pelayan Gambar Immich \". Pastikan menggunakan alamat yang dibenarkan anda untuk menghantar e-mel.", "notification_email_host_description": "Hos e-mel pelayan (cth. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Abaikan ralat-ralat sijil", "notification_email_ignore_certificate_errors_description": "Abaikan ralat pengesahan sijil TLS (tidak disyorkan)", @@ -186,19 +205,24 @@ "oauth_auto_register": "Daftar secara automatik", "oauth_auto_register_description": "Daftar secara automatik pengguna-pengguna baharu selepas mendaftar masuk dengan OAuth", "oauth_button_text": "Teks butang", + "oauth_client_secret_description": "Diperlukan jika PKCE (Proof Key for Code Exchange) tidak disokong oleh penyedia OAuth", "oauth_enable_description": "Log masuk dengan OAuth", "oauth_mobile_redirect_uri": "URI ubah hala mudah alih", "oauth_mobile_redirect_uri_override": "Penggantian URI ubah hala mudah alih", "oauth_mobile_redirect_uri_override_description": "Aktifkan apabila pembekal OAuth tidak membenarkan URI mudah alih, seperti ''{callback}''", + "oauth_role_claim": "Tebus peranan", + "oauth_role_claim_description": "Automatik memberi kebenaran pentadbir berdasarkan tuntutan ini. Tuntutan ini mungkin mempunyai sama ada 'pengguna' atau 'pentadbir'.", "oauth_settings": "OAuth", - "oauth_settings_description": "Urus tetapan-tetapan log masuk OAuth", + "oauth_settings_description": "Urus tetapan log masuk OAuth", "oauth_settings_more_details": "Untuk maklumat lanjut tentang ciri ini, rujuk ke dokumen.", "oauth_storage_label_claim": "Tuntutan label storan", "oauth_storage_label_claim_description": "Tetapkan label storan pengguna secara automatik kepada nilai tuntutan ini.", "oauth_storage_quota_claim": "Tuntutan kuota storan", "oauth_storage_quota_claim_description": "Tetapkan kuota storan pengguna secara automatik kepada nilai tuntutan ini.", "oauth_storage_quota_default": "Kuota storan lalai (GiB)", - "oauth_storage_quota_default_description": "Kuota dalam GiB untuk digunakan apabila tiada tuntutan disediakan (Masukkan 0 untuk kuota tanpa had).", + "oauth_storage_quota_default_description": "Kuota dalam GiB yang akan digunakan jika tiada tuntutan disediakan.", + "oauth_timeout": "Had Masa Permintaan", + "oauth_timeout_description": "Had masa untuk permintaan dalam milisaat", "password_enable_description": "Log masuk dengan e-mel dan kata laluan", "password_settings": "Kata Laluan Log Masuk", "password_settings_description": "Urus tetapan-tetapan kata laluan log masuk", @@ -207,12 +231,12 @@ "quota_size_gib": "Saiz Kuota (GiB)", "refreshing_all_libraries": "Menyegarkan semua perpustakaan", "registration": "Pendaftaran Pentadbir", - "registration_description": "Memandangkan anda adalah pengguna pertama pada sistem, anda akan ditugaskan sebagai Admin dan bertanggungjawab untuk tugas pentadbiran, serta pengguna tambahan yang akan anda tambah.", + "registration_description": "Memandangkan anda adalah pengguna pertama pada sistem, anda akan ditugaskan sebagai Pentadbir dan bertanggungjawab untuk tugas pentadbiran, serta pengguna tambahan yang akan anda tambah.", "require_password_change_on_login": "Perlukan pengguna menukar kata laluan ketika log masuk pertama", "reset_settings_to_default": "Tetapkan semula tetapan kepada lalai", "reset_settings_to_recent_saved": "Tetapkan semula tetapan kepada tetapan yang disimpan baru-baru ini", "scanning_library": "Mengimbas perpustakaan", - "search_jobs": "Cari kerja…", + "search_jobs": "Cari tugasan…", "send_welcome_email": "Hantar e-mel alu-aluan", "server_external_domain_settings": "Domain luaran", "server_external_domain_settings_description": "Domain untuk pautan kongsi awam, termasuk http(s)://", @@ -222,7 +246,7 @@ "server_settings_description": "Urus tetapan pelayan", "server_welcome_message": "Mesej alu-aluan", "server_welcome_message_description": "Mesej yang dipaparkan pada halaman log masuk.", - "sidecar_job": "Metadata kereta sisi", + "sidecar_job": "Metadata sampingan", "sidecar_job_description": "Temui atau segerakkan metadata sampingan daripada sistem fail", "slideshow_duration_description": "Bilangan saat untuk memaparkan setiap imej", "smart_search_job_description": "Jalankan pembelajaran mesin pada aset-aset untuk menyokong carian pintar", @@ -233,9 +257,10 @@ "storage_template_hash_verification_enabled_description": "Mendayakan pengesahan hac, jangan lumpuhkan melainkan anda pasti akan implikasinya", "storage_template_migration": "Penghijrahan templat storan", "storage_template_migration_description": "Gunakan {template} semasa pada aset-aset yang dimuat naik sebelum ini", - "storage_template_migration_info": "Perubahan templat hanya akan digunakan pada aset baharu. Untuk menggunakan templat secara retroaktif pada aset-aset yang dimuat naik sebelum ini, jalankan {job}.", + "storage_template_migration_info": "Templat storan akan menukar semua sambungan fail kepada huruf kecil. Perubahan templat hanya akan digunakan untuk aset baru. Untuk menggunakan templat ini secara retroaktif pada aset yang telah dimuat naik sebelum ini, jalankan {job}.", "storage_template_migration_job": "Kerja Migrasi Templat Storan", "storage_template_more_details": "Untuk butiran lanjut tentang ciri ini, rujuk kepada Templat Storan dan implikasi", + "storage_template_onboarding_description_v2": "Apabila diaktifkan, ciri ini akan mengatur fail secara automatik berdasarkan templat yang ditetapkan oleh pengguna. Untuk maklumat lanjut, sila rujuk dokumentasi.", "storage_template_path_length": "Anggaran kepanjangan laluan: {length, number}/{limit, number}", "storage_template_settings": "Templat Storan", "storage_template_settings_description": "Urus struktur folder dan nama fail aset dimuat naik", @@ -250,7 +275,7 @@ "template_email_update_album": "Templat Kemas kini Album", "template_email_welcome": "Templat e-mel alu-aluan", "template_settings": "Templat Pemberitahuan", - "template_settings_description": "Urus templat tersuai untuk pemberitahuan.", + "template_settings_description": "Urus templat tersuai untuk notifikasi", "theme_custom_css_settings": "CSS tersuai", "theme_custom_css_settings_description": "Lembaran Gaya Lata membolehkan reka bentuk Immich disuaikan.", "theme_settings": "Tetapan Tema", @@ -282,7 +307,7 @@ "transcoding_encoding_options": "Pilihan Pengekodan", "transcoding_encoding_options_description": "Tetapkan codec, resolusi, kualiti dan pilihan lain untuk video yang dikodkan", "transcoding_hardware_acceleration": "Pecutan Perkakasan", - "transcoding_hardware_acceleration_description": "Eksperimen; lebih pantas, tetapi akan mempunyai kualiti yang lebih rendah pada kadar bit yang sama", + "transcoding_hardware_acceleration_description": "Eksperimen: pengekodan semula yang lebih pantas tetapi mungkin mengurangkan kualiti pada kadar bit yang sama", "transcoding_hardware_decoding": "Penyahkodan perkakasan", "transcoding_hardware_decoding_setting_description": "Mendayakan pecutan hujung ke hujung dan bukannya hanya mempercepatkan pengekodan. Mungkin tidak berfungsi pada semua video.", "transcoding_max_b_frames": "Bingkai-B maksimum", @@ -311,8 +336,61 @@ "transcoding_threads_description": "Nilai yang lebih tinggi membawa kepada pengekodan yang lebih pantas, tetapi meninggalkan lebih sedikit ruang untuk pemproses tugas lain semasa aktif. Nilai ini tidak boleh lebih daripada bilangan teras CPU. Memaksimumkan penggunaan jika ditetapkan kepada 0.", "transcoding_tone_mapping": "Pemetaan nada", "transcoding_tone_mapping_description": "Percubaan untuk mengekalkan penampilan video HDR apabila ditukar kepada SDR. Setiap algoritma membuat pertukaran yang berbeza untuk warna, perincian dan kecerahan. Hable mengekalkan perincian, Mobius mengekalkan warna, dan Reinhard mengekalkan kecerahan.", - "transcoding_transcode_policy": "Dasar transkod" + "transcoding_transcode_policy": "Dasar transkod", + "transcoding_transcode_policy_description": "Dasar untuk bila video perlu ditranskod. Video HDR akan sentiasa ditranskod (kecuali jika pengekodan semula dinyahdayakan).", + "transcoding_two_pass_encoding": "Pengekodan dua lelaran", + "transcoding_two_pass_encoding_setting_description": "Transkod dalam dua lelaran untuk menghasilkan video yang ditranskod dengan kualiti lebih baik. Apabila kadar bit maksimum diaktifkan (diperlukan untuk berfungsi dengan H.264 dan HEVC), mod ini akan menggunakan julat kadar bit berdasarkan kadar bit maksimum dan mengabaikan CRF. Untuk VP9, CRF boleh digunakan jika kadar bit maksimum dinyahdayakan.", + "transcoding_video_codec": "Kodek video", + "transcoding_video_codec_description": "VP9 mempunyai kecekapan tinggi dan keserasian web yang baik, tetapi mengambil masa lebih lama untuk ditranskod. HEVC memberikan prestasi yang serupa, tetapi kurang serasi dengan web. H.264 sangat serasi dan pantas untuk ditranskod, tetapi menghasilkan fail yang jauh lebih besar. AV1 ialah kodek paling cekap tetapi tidak disokong pada peranti lama.", + "trash_enabled_description": "Dayakan ciri Tong Sampah", + "trash_number_of_days": "Bilangan hari", + "trash_number_of_days_description": "Bilangan hari untuk menyimpan aset dalam tong sampah sebelum dipadam secara kekal", + "trash_settings": "Tetapan Tong Sampah", + "trash_settings_description": "Urus tetapan tong sampah", + "user_cleanup_job": "Pembersihan pengguna", + "user_delete_delay": "Akaun dan aset {user} akan dijadualkan untuk dipadam secara kekal dalam {delay, plural, one {# hari} other {# hari}}.", + "user_delete_delay_settings": "Kelewatan pemadaman", + "user_delete_delay_settings_description": "Bilangan hari selepas penghapusan sebelum akaun dan aset pengguna dipadam secara kekal. Tugasan pemadaman pengguna dijalankan pada tengah malam untuk menyemak pengguna yang sedia untuk dipadam. Perubahan pada tetapan ini akan dinilai semasa pelaksanaan seterusnya.", + "user_delete_immediately": "Akaun dan aset {user} akan dimasukkan ke dalam baris gilir untuk dipadam secara kekal serta-merta.", + "user_delete_immediately_checkbox": "Masukkan pengguna dan aset ke dalam baris gilir untuk dipadam serta-merta", + "user_details": "Butiran Pengguna", + "user_management": "Pengurusan Pengguna", + "user_password_has_been_reset": "Katalaluan pengguna telah ditetapkan semula:", + "user_password_reset_description": "Sila berikan katalaluan sementara kepada pengguna dan maklumkan bahawa mereka perlu menukar katalaluan semasa log masuk yang seterusnya.", + "user_restore_description": "Akaun {user} akan dipulihkan.", + "user_restore_scheduled_removal": "Pulihkan pengguna – pemadaman dijadualkan pada {date, date, long}", + "user_settings": "Tetapan Pengguna", + "user_settings_description": "Urus tetapan pengguna", + "user_successfully_removed": "Pengguna {email} telah berjaya dipadam.", + "version_check_enabled_description": "Dayakan semakan versi", + "version_check_implications": "Ciri semakan versi bergantung kepada komunikasi berkala dengan github.com", + "version_check_settings": "Semakan Versi", + "version_check_settings_description": "Dayakan/nyahdayakan notifikasi versi baharu", + "video_conversion_job": "Transkod video", + "video_conversion_job_description": "Transkod video untuk keserasian yang lebih luas dengan pelayar dan peranti" }, + "admin_email": "Emel Pentadbir", + "admin_password": "Kata laluan Pentadbir", + "administration": "Pentadbiran", + "advanced": "Lanjutan", + "advanced_settings_beta_timeline_subtitle": "Cuba pengalaman aplikasi baharu", + "advanced_settings_beta_timeline_title": "Garis masa beta", + "advanced_settings_enable_alternate_media_filter_subtitle": "Gunakan pilihan ini untuk menapis media semasa penyegerakan berdasarkan kriteria alternatif. Hanya cuba jika anda menghadapi masalah dengan aplikasi mengesan semua album.", + "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTAL] Gunakan penapis penyelarasan album peranti alternatif", + "advanced_settings_log_level_title": "Tahap log: {level}", + "advanced_settings_prefer_remote_subtitle": "Sesetengah peranti sangat perlahan untuk memuatkan imej kecil daripada aset lokal. Aktifkan tetapan ini untuk memuatkan imej dari jauh sebagai gantinya.", + "advanced_settings_prefer_remote_title": "Utamakan imej jauh", + "advanced_settings_proxy_headers_subtitle": "Tentukan pengepala proksi yang perlu dihantar oleh Immich dengan setiap permintaan rangkaian", + "advanced_settings_proxy_headers_title": "Pengepala Proksi", + "advanced_settings_self_signed_ssl_subtitle": "Langkau pengesahan sijil SSL untuk titik hujung pelayan. Diperlukan untuk sijil yang ditandatangani sendiri.", + "advanced_settings_self_signed_ssl_title": "Benarkan sijil SSL yang ditandatangani sendiri", + "advanced_settings_sync_remote_deletions_subtitle": "Automatik memadam atau memulihkan satu asset di peranti ini apabila tindakan itu diambil di dalam laman sesawang", + "advanced_settings_sync_remote_deletions_title": "Selaraskan pemadaman kawalan jauh [UJI KAJI]", + "advanced_settings_tile_subtitle": "Tetapan lanjutan pengguna", + "advanced_settings_troubleshooting_subtitle": "Dayakan ciri tambahan untuk menyelesaikan masalah", + "advanced_settings_troubleshooting_title": "Menyelesaikan masalah", + "age_months": "Umur {bulan, plural, satu {# bulan} lain {# bulan}}", + "age_year_months": "Umur 1 tahun, {bulan, plural, satu {# bulan} lain {# bulan}}", "deduplication_criteria_1": "Saiz imej dalam bait", "deduplication_criteria_2": "Kiraan data EXIF", "deduplication_info": "Maklumat Pendeduplikasian", @@ -361,5 +439,6 @@ "year": "Tahun", "yes": "Ya", "you_dont_have_any_shared_links": "Anda tidak mempunyai apa-apa pautan yang dikongsi", + "your_wifi_name": "Nama Wi-Fi anda", "zoom_image": "Zum Gambar" } diff --git a/i18n/nb_NO.json b/i18n/nb_NO.json index 3478262019..89b5e298bc 100644 --- a/i18n/nb_NO.json +++ b/i18n/nb_NO.json @@ -22,8 +22,8 @@ "add_partner": "Legg til partner", "add_path": "Legg til sti", "add_photos": "Legg til bilder", - "add_tag": "Legg til tag", - "add_to": "Legg til…", + "add_tag": "Legg til tagg", + "add_to": "Legg til i…", "add_to_album": "Legg til album", "add_to_album_bottom_sheet_added": "Lagt til i {album}", "add_to_album_bottom_sheet_already_exists": "Allerede i {album}", @@ -35,7 +35,7 @@ "admin": { "add_exclusion_pattern_description": "Legg til ekskluderingsmønstre. Globbing med *, ** og ? støttes. For å ignorere alle filer i en hvilken som helst mappe som heter \"Raw\", bruk \"**/Raw/**\". For å ignorere alle filer som slutter på \".tif\", bruk \"**/*.tif\". For å ignorere en absolutt filplassering, bruk \"/filsti/til/ignorer/**\".", "admin_user": "Administrasjonsbruker", - "asset_offline_description": "Denne eksterne bibliotekressursen finnes ikke lenger på disk og har blitt flyttet til papirkurven. Hvis filen ble flyttet innad i biblioteket, sjekk tidslinjen din for den tilsvarende ressursen. For å gjenopprette ressursen, vennligst sørg for at filstien under er tilgjengelig for Immich og skan biblioteket.", + "asset_offline_description": "Dette eksterne biblioteksobjektet finnes ikke lenger på disk og har blitt flyttet til papirkurven. Hvis filen ble flyttet innad i biblioteket, se etter det tilsvarende objektet i tidslinjen din. For å gjenopprette objektet, vennligst sørg for at filstien under er tilgjengelig for Immich og skann biblioteket.", "authentication_settings": "Godkjenningsinnstillinger", "authentication_settings_description": "Administrer passord, OAuth, og andre innstillinger for autentisering", "authentication_settings_disable_all": "Er du sikker på at du ønsker å deaktivere alle innloggingsmetoder? Innlogging vil bli fullstendig deaktivert.", @@ -114,7 +114,7 @@ "logging_settings": "Logger", "machine_learning_clip_model": "Clip-modell", "machine_learning_clip_model_description": "Navnet på en CLIP-modell finnes her. Merk at du må kjøre 'Smart Søk'-jobben på nytt for alle bilder etter at du har endret modell.", - "machine_learning_duplicate_detection": "Duplikat-deteksjon", + "machine_learning_duplicate_detection": "Duplikatsøk", "machine_learning_duplicate_detection_enabled": "Aktiver duplikatdeteksjon", "machine_learning_duplicate_detection_enabled_description": "Hvis deaktivert: helt identiske filer vil fremdeles de-duplisert.", "machine_learning_duplicate_detection_setting_description": "Bruk CLIP-embeddings for å finne sannsynlige duplikater", @@ -166,6 +166,20 @@ "metadata_settings_description": "Administrer metadatainnstillinger", "migration_job": "Migrering", "migration_job_description": "Migrer miniatyrbilder for filer og ansikter til den nyeste mappestrukturen", + "nightly_tasks_cluster_faces_setting_description": "Kjør ansiktsgjenkjenning på nylige oppdagede ansikter", + "nightly_tasks_cluster_new_faces_setting": "Grupper nye ansikter", + "nightly_tasks_database_cleanup_setting": "Opprydningsjobber for databasen", + "nightly_tasks_database_cleanup_setting_description": "Rydder opp i gamle, utgåtte data fra databasen", + "nightly_tasks_generate_memories_setting": "Genererer minner", + "nightly_tasks_generate_memories_setting_description": "Generer nye minner fra objekter", + "nightly_tasks_missing_thumbnails_setting": "Generer manglende miniatyrbilder", + "nightly_tasks_missing_thumbnails_setting_description": "Legg til objekter i kø som mangler miniatyrbilder for generering", + "nightly_tasks_settings": "Innstillinger for nattjobber", + "nightly_tasks_settings_description": "Endre på nattjobber", + "nightly_tasks_start_time_setting": "Starttid", + "nightly_tasks_start_time_setting_description": "Tiden som serveren starter med nattjobbene", + "nightly_tasks_sync_quota_usage_setting": "Synkroniser kvotebruk", + "nightly_tasks_sync_quota_usage_setting_description": "Oppdater brukerkvote basert på nåværende bruk", "no_paths_added": "Ingen filstier lagt til", "no_pattern_added": "Ingen mønster lagt til", "note_apply_storage_label_previous_assets": "Merk: For å bruke lagringsetiketten på tidligere opplastede filer, kjør", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "Mobil omdirigerings-URI", "oauth_mobile_redirect_uri_override": "Mobil omdirigerings-URI overstyring", "oauth_mobile_redirect_uri_override_description": "Aktiver når OAuth-leverandøren ikke tillater en mobil URI, som ''{callback}''", + "oauth_role_claim": "Krev Rolle", + "oauth_role_claim_description": "Gi automatisk administratortilgang basert på tilstedeværelsen av dette kravet. Kravet kan ha enten «bruker» eller «administrator».", "oauth_settings": "OAuth", "oauth_settings_description": "Administrer innstillinger for OAuth-innlogging", "oauth_settings_more_details": "For mer informasjon om denne funksjonen, se dokumentasjonen.", @@ -212,7 +228,7 @@ "password_settings_description": "Administrer innstillinger for passordinnlogging", "paths_validated_successfully": "Alle filstier validert uten problemer", "person_cleanup_job": "Person opprydding", - "quota_size_gib": "Kvotestørrelse (GiB)", + "quota_size_gib": "Kvote (GiB)", "refreshing_all_libraries": "Oppdaterer alle biblioteker", "registration": "Administrator registrering", "registration_description": "Siden du er den første brukeren på systemet, vil du bli utnevnt til administrator og ha ansvar for administrative oppgaver. Du vil også opprette eventuelle nye brukere.", @@ -236,12 +252,12 @@ "smart_search_job_description": "Kjør maskinlæring på filer for å støtte smart søk", "storage_template_date_time_description": "Elementets opprettelsestidspunkt brukes for datotid-informasjonen", "storage_template_date_time_sample": "Eksempeltid {date}", - "storage_template_enable_description": "Aktiver lagringstemplatmotoren", + "storage_template_enable_description": "Aktiver lagringsmal-motoren", "storage_template_hash_verification_enabled": "Hash verifisering aktivert", "storage_template_hash_verification_enabled_description": "Aktiver hasjverifisering. Ikke deaktiver dette med mindre du er sikker på konsekvensene", - "storage_template_migration": "Lagringsmal migrering", + "storage_template_migration": "Implementer lagringsmal", "storage_template_migration_description": "Bruk gjeldende {template} på tidligere opplastede bilder", - "storage_template_migration_info": "Lagringsmalen vil endre filtypen til små bokstaver. Malendringer vil kun gjelde nye ressurser. For å anvende malen på tidligere opplastede ressurser, kjør {job}.", + "storage_template_migration_info": "Lagringsmalen vil endre filtypen til små bokstaver. Malendringer vil kun gjelde nye objekter. For å anvende malen på tidligere opplastede objekter, kjør {job}.", "storage_template_migration_job": "Migreringsjobb for lagringsmal", "storage_template_more_details": "For mer informasjon om denne funksjonen, se lagringsmalen og dens konsekvenser", "storage_template_onboarding_description_v2": "Når aktivert vil denne funksjonen automatisk organisere filer basert på en brukerdefinert mal. For mer informasjon, se denne linken dokumentasjon.", @@ -250,19 +266,19 @@ "storage_template_settings_description": "Administrer mappestrukturen og filnavnet til opplastede fil", "storage_template_user_label": "{label} er brukerens Lagringsetikett", "system_settings": "Systeminstillinger", - "tag_cleanup_job": "Tag opprydding", + "tag_cleanup_job": "Tagg-opprydding", "template_email_available_tags": "Du kan bruke følgende variabler i din mal: {tags}", "template_email_if_empty": "Hvis malen er tom, vil standard epost bli brut.", "template_email_invite_album": "Inviter Album Mal", "template_email_preview": "Forhåndsvis", - "template_email_settings": "Epost mal", + "template_email_settings": "E-postmaler", "template_email_update_album": "Oppdater Album Mal", - "template_email_welcome": "Mal for velkomst epost", + "template_email_welcome": "Mal for velkomst-e-post", "template_settings": "Varslings Mal", "template_settings_description": "Administrer tilpassede maler for varsling", "theme_custom_css_settings": "Egendefinert CSS", "theme_custom_css_settings_description": "Cascading Style Sheets gjør det mulig å tilpasse designet av Immich.", - "theme_settings": "Tema innstillinger", + "theme_settings": "Tema-innstillinger", "theme_settings_description": "Administrer tilpasning av Immich webgrensesnitt", "thumbnail_generation_job": "Generer miniatyrbilder", "thumbnail_generation_job_description": "Generer store, små og uskarpe miniatyrbilder for hver fil, samt miniatyrbilder for hver person", @@ -357,10 +373,12 @@ "admin_password": "Administrator Passord", "administration": "Administrasjon", "advanced": "Avansert", + "advanced_settings_beta_timeline_subtitle": "Prøv den nye app opplevelsen", + "advanced_settings_beta_timeline_title": "Beta tidslinje", "advanced_settings_enable_alternate_media_filter_subtitle": "Bruk denne innstillingen for å filtrere mediefiler under synkronisering basert på alternative kriterier. Bruk kun denne innstillingen dersom man opplever problemer med at applikasjonen ikke oppdager alle album.", "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTELT] Bruk alternativ enhet album synk filter", "advanced_settings_log_level_title": "Loggnivå: {level}", - "advanced_settings_prefer_remote_subtitle": "Noen enheter er veldige trege til å hente mikrobilder fra enheten. Aktiver denne innstillingen for å hente de eksternt istedenfor.", + "advanced_settings_prefer_remote_subtitle": "Noen enheter er veldige trege til å hente miniatyrbilder fra enheten. Aktiver denne innstillingen for å hente de eksternt istedenfor.", "advanced_settings_prefer_remote_title": "Foretrekk eksterne bilder", "advanced_settings_proxy_headers_subtitle": "Definer proxy headere som Immich skal benytte ved enhver nettverksrequest", "advanced_settings_proxy_headers_title": "Proxy headere", @@ -388,6 +406,7 @@ "album_options": "Albumalternativer", "album_remove_user": "Fjerne bruker?", "album_remove_user_confirmation": "Er du sikker på at du vil fjerne {user}?", + "album_search_not_found": "Ingen album ble funnet som traff ditt søk", "album_share_no_users": "Ser ut til at du har delt dette albumet med alle brukere, eller du ikke har noen brukere å dele det med.", "album_updated": "Album oppdatert", "album_updated_setting_description": "Motta e-postvarsling når et delt album får nye filer", @@ -407,6 +426,7 @@ "albums_default_sort_order": "Standard sorteringsrekkefølge for albumer", "albums_default_sort_order_description": "Standard sorteringsrekkefølge for bilder når man lager et nytt album.", "albums_feature_description": "Samlinger av bilder som kan deles med andre brukere.", + "albums_on_device_count": "Albumer på enheten {count}", "all": "Alle", "all_albums": "Alle album", "all_people": "Alle personer", @@ -426,7 +446,8 @@ "app_bar_signout_dialog_title": "Logg ut", "app_settings": "Appinstillinger", "appears_in": "Vises i", - "archive": "Arkiver", + "archive": "Arkiv", + "archive_action_prompt": "{count} lagt til i arkivet", "archive_or_unarchive_photo": "Arkiver eller ta ut av arkivet", "archive_page_no_archived_assets": "Ingen arkiverte objekter funnet", "archive_page_title": "Arkiv ({count})", @@ -464,13 +485,12 @@ "assets": "Filer", "assets_added_count": "Lagt til {count, plural, one {# element} other {# elementer}}", "assets_added_to_album_count": "Lagt til {count, plural, one {# asset} other {# assets}} i album", - "assets_added_to_name_count": "Lagt til {count, plural, one {# asset} other {# assets}} i {hasName, select, true {{name}} other {new album}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} kan ikke legges til i albumet", "assets_count": "{count, plural, one {# fil} other {# filer}}", "assets_deleted_permanently": "{count} objekt(er) slettet permanent", "assets_deleted_permanently_from_server": "{count} objekt(er) slettet permanent fra Immich-serveren", "assets_downloaded_failed": "{count, plural, one {Nedlasting av # fil - {error} fil feilet} other {Nedlastede # filer - {error} filer feilet}}", - "assets_downloaded_successfully": "{count, plural, one {Downloaded # file successfully} other {Downloaded # files successfully}}", + "assets_downloaded_successfully": "{count, plural, one {Nedlastet # fil vellykket} other {Nedlastede # filer vellykket}}", "assets_moved_to_trash_count": "Flyttet {count, plural, one {# asset} other {# assets}} til søppel", "assets_permanently_deleted_count": "Permanent slettet {count, plural, one {# asset} other {# assets}}", "assets_removed_count": "Slettet {count, plural, one {# asset} other {# assets}}", @@ -587,6 +607,7 @@ "cancel": "Avbryt", "cancel_search": "Avbryt søk", "canceled": "Avbrutt", + "canceling": "Avbryter", "cannot_merge_people": "Kan ikke slå sammen personer", "cannot_undo_this_action": "Du kan ikke gjøre om denne handlingen!", "cannot_update_the_description": "Kan ikke oppdatere beskrivelsen", @@ -703,7 +724,7 @@ "daily_title_text_date": "E MMM. dd", "daily_title_text_date_year": "E MMM. dddd, yyyy", "dark": "Mørk", - "darkTheme": "Aktiver mørkt utsende", + "dark_theme": "Aktiver mørk-modus", "date_after": "Dato etter", "date_and_time": "Dato og tid", "date_before": "Dato før", @@ -719,6 +740,7 @@ "default_locale": "Standard språkinnstilling", "default_locale_description": "Formater datoer og tall basert på nettleserens språkinnstilling", "delete": "Slett", + "delete_action_prompt": "{count} permanen slettet", "delete_album": "Slett album", "delete_api_key_prompt": "Er du sikker på at du vil slette denne API-nøkkelen?", "delete_dialog_alert": "Disse objektene vil bli slettet permanent fra Immich og fra enheten din", @@ -732,6 +754,7 @@ "delete_key": "Slett nøkkel", "delete_library": "Slett bibliotek", "delete_link": "Slett lenke", + "delete_local_action_prompt": "{count} slettet lokalt", "delete_local_dialog_ok_backed_up_only": "Slett kun sikkerhetskopierte objekter", "delete_local_dialog_ok_force": "Slett uansett", "delete_others": "Slett andre", @@ -745,6 +768,7 @@ "description": "Beskrivelse", "description_input_hint_text": "Legg til beskrivelse ...", "description_input_submit_error": "Feil ved oppdatering av beskrivelse, sjekk loggen for flere detaljer", + "deselect_all": "Avmerk alle", "details": "Detaljer", "direction": "Retning", "disabled": "Deaktivert", @@ -762,6 +786,7 @@ "documentation": "Dokumentasjon", "done": "Ferdig", "download": "Last ned", + "download_action_prompt": "Laster ned {count} objekter", "download_canceled": "Nedlasting avbrutt", "download_complete": "Nedlasting fullført", "download_enqueue": "Nedlasting satt i kø", @@ -799,6 +824,7 @@ "edit_key": "Rediger nøkkel", "edit_link": "Endre lenke", "edit_location": "Endre lokasjon", + "edit_location_action_prompt": "{count} lokasjon endret", "edit_location_dialog_title": "Lokasjon", "edit_name": "Redigere navn", "edit_people": "Rediger personer", @@ -817,6 +843,7 @@ "empty_trash": "Tøm papirkurv", "empty_trash_confirmation": "Er du sikker på at du vil tømme søppelbøtta? Dette vil slette alle filene i søppelbøtta permanent fra Immich.\nDu kan ikke angre denne handlingen!", "enable": "Aktivere", + "enable_backup": "Aktiver backup", "enable_biometric_auth_description": "Skriv inn PINkoden for å aktivere biometrisk autentisering", "enabled": "Aktivert", "end_date": "Slutt dato", @@ -867,7 +894,7 @@ "incorrect_email_or_password": "Feil epost eller passord", "paths_validation_failed": "{paths, plural, one {# sti} other {# sti}} mislyktes validering", "profile_picture_transparent_pixels": "Profil bilde kan ikke ha gjennomsiktige piksler. Vennligst zoom inn og/eller flytt bilde.", - "quota_higher_than_disk_size": "Du har satt en kvote høyere enn diskstørrelsen", + "quota_higher_than_disk_size": "Du har satt kvoten større enn diskstørrelsen", "unable_to_add_album_users": "Kan ikke legge til brukere i albumet", "unable_to_add_assets_to_shared_link": "Kan ikke legge til bilder til delt lenke", "unable_to_add_comment": "Kan ikke legge til kommentar", @@ -984,6 +1011,7 @@ "failed_to_load_assets": "Feilet med å laste fil", "failed_to_load_folder": "Kunne ikke laste inn mappe", "favorite": "Favoritt", + "favorite_action_prompt": "{count} lagt til i favoritter", "favorite_or_unfavorite_photo": "Merk som favoritt eller fjern som favoritt", "favorites": "Favoritter", "favorites_page_no_favorites": "Ingen favorittobjekter funnet", @@ -1022,7 +1050,7 @@ "group_year": "Grupper etter år", "haptic_feedback_switch": "Aktivert haptisk tilbakemelding", "haptic_feedback_title": "Haptisk tilbakemelding", - "has_quota": "Har kvote", + "has_quota": "Kvote", "header_settings_add_header_tip": "Legg til header", "header_settings_field_validator_msg": "Verdi kan ikke være null", "header_settings_header_name_input": "Header navn", @@ -1127,6 +1155,7 @@ "library_page_sort_created": "Nylig opplastet", "library_page_sort_last_modified": "Sist endret", "library_page_sort_title": "Albumtittel", + "licenses": "Lisenser", "light": "Lys", "like_deleted": "Som slettede", "link_motion_video": "Koble bevegelsesvideo", @@ -1173,7 +1202,7 @@ "login_form_save_login": "Forbli innlogget", "login_form_server_empty": "Skriv inn en server-URL.", "login_form_server_error": "Kan ikke koble til server.", - "login_has_been_disabled": "Login har blitt deaktivert.", + "login_has_been_disabled": "Innlogging har blitt deaktivert.", "login_password_changed_error": "Det skjedde en feil ved oppdatering av passordet", "login_password_changed_success": "Passord oppdatert", "logout_all_device_confirmation": "Er du sikker på at du vil logge ut av alle enheter?", @@ -1209,7 +1238,7 @@ "map_settings_dark_mode": "Mørk modus", "map_settings_date_range_option_day": "Siste 24 timer", "map_settings_date_range_option_days": "Siste {days} dager", - "map_settings_date_range_option_year": "Sist år", + "map_settings_date_range_option_year": "Siste år", "map_settings_date_range_option_years": "Siste {years} år", "map_settings_dialog_title": "Kartinnstillinger", "map_settings_include_show_archived": "Inkluder arkiverte", @@ -1227,7 +1256,7 @@ "memories_check_back_tomorrow": "Sjekk igjen i morgen for flere minner", "memories_setting_description": "Administrer hva du ser i minnene dine", "memories_start_over": "Start på nytt", - "memories_swipe_to_close": "Swipe opp for å lukke", + "memories_swipe_to_close": "Sveip opp for å lukke", "memory": "Minne", "memory_lane_title": "Minnefelt {title}", "menu": "Meny", @@ -1235,7 +1264,7 @@ "merge_people": "Slå sammen personer", "merge_people_limit": "Du kan bare slå sammen opp til 5 fjes om gangen", "merge_people_prompt": "Vil du slå sammen disse personene? Denne handlingen kan ikke reverseres.", - "merge_people_successfully": "Personene ble vellykket slått sammen", + "merge_people_successfully": "Sammenslåing av personer var vellykket", "merged_people_count": "Sammenslått {count, plural, one {# person} other {# people}}", "minimize": "Minimer", "minute": "Minutt", @@ -1246,6 +1275,7 @@ "more": "Mer", "move": "Flytt", "move_off_locked_folder": "Flytt ut av låst mappe", + "move_to_lock_folder_action_prompt": "{count} lagt til i låst mappe", "move_to_locked_folder": "Flytt til låst mappe", "move_to_locked_folder_confirmation": "Disse bildene og videoene vil bli fjernet fra alle albumer, og kun tilgjengelige via den låste mappen", "moved_to_archive": "Flyttet {count, plural, one {# asset} other {# assets}} til arkivet", @@ -1258,14 +1288,14 @@ "name": "Navn", "name_or_nickname": "Navn eller kallenavn", "networking_settings": "Nettverk", - "networking_subtitle": "Administrer serverendepunktinnstillingene", + "networking_subtitle": "Administrer serverendepunkt-innstillinger", "never": "aldri", "new_album": "Nytt Album", "new_api_key": "Ny API-nøkkel", "new_password": "Nytt passord", "new_person": "Ny person", - "new_pin_code": "Ny PIN kode", - "new_pin_code_subtitle": "Dette er første gang du åpner den låste mappen. Lag en PIN kode for å sikre tilgangen til denne siden", + "new_pin_code": "Ny PIN-kode", + "new_pin_code_subtitle": "Dette er første gang du åpner den låste mappen. Lag en PIN-kode for å sikre tilgangen til denne siden", "new_user_created": "Ny bruker opprettet", "new_version_available": "NY VERSJON TILGJENGELIG", "newest_first": "Nyeste først", @@ -1282,7 +1312,7 @@ "no_duplicates_found": "Ingen duplikater ble funnet.", "no_exif_info_available": "Ingen EXIF-informasjon tilgjengelig", "no_explore_results_message": "Last opp flere bilder for å utforske samlingen din.", - "no_favorites_message": "Legg til favoritter for å raskt finne dine beste bilder og videoer", + "no_favorites_message": "Legg til favoritter for å finne dine beste bilder og videoer raskt", "no_libraries_message": "Opprett et eksternt bibliotek for å se bildene og videoene dine", "no_locked_photos_message": "Bilder og videoer i den låste mappen er skjult og vil ikke vises når du blar i biblioteket.", "no_name": "Ingen navn", @@ -1292,7 +1322,7 @@ "no_results": "Ingen resultater", "no_results_description": "Prøv et synonym eller mer generelt søkeord", "no_shared_albums_message": "Opprett et album for å dele bilder og videoer med personer i nettverket ditt", - "not_in_any_album": "Ikke i noen album", + "not_in_any_album": "Ikke i noe album", "not_selected": "Ikke valgt", "note_apply_storage_label_to_previously_uploaded assets": "Merk: For å bruke lagringsetiketten på tidligere opplastede filer, kjør", "notes": "Notater", @@ -1305,7 +1335,7 @@ "notifications": "Notifikasjoner", "notifications_setting_description": "Administrer varsler", "oauth": "OAuth", - "official_immich_resources": "Offisielle Immich Resurser", + "official_immich_resources": "Offisielle Immich-ressurser", "offline": "Frakoblet", "ok": "Ok", "oldest_first": "Eldste først", @@ -1315,7 +1345,7 @@ "onboarding_privacy_description": "Følgene (valgfrie) funksjoner er avhengige av eksterne tjenester, og kan bli deaktivert når som helst under innstillinger.", "onboarding_server_welcome_description": "La oss sette opp din instans med noen standard innstillinger.", "onboarding_theme_description": "Velg et fargetema for din bruker. Du kan endre denne senere under dine instillinger.", - "onboarding_user_welcome_description": "La oss få deg startet!", + "onboarding_user_welcome_description": "La oss få deg i gang!", "onboarding_welcome_user": "Velkommen, {user}", "online": "Tilkoblet", "only_favorites": "Bare favoritter", @@ -1460,6 +1490,7 @@ "purchase_server_description_2": "Støttespiller status", "purchase_server_title": "Server", "purchase_settings_server_activated": "Produktnøkkel for server er administrert av administratoren", + "queue_status": "Kø {count}/{total}", "rating": "Stjernevurdering", "rating_clear": "Slett vurdering", "rating_count": "{count, plural, one {# sjerne} other {# stjerner}}", @@ -1474,7 +1505,7 @@ "recent-albums": "Nylige album", "recent_searches": "Nylige søk", "recently_added": "Nylig lagt til", - "recently_added_page_title": "Nylig lagt til", + "recently_added_page_title": "Nylig oppført", "recently_taken": "Nylig tatt", "recently_taken_page_title": "Nylig Tatt", "refresh": "Oppdater", @@ -1495,7 +1526,9 @@ "remove_custom_date_range": "Fjern egendefinert datoperiode", "remove_deleted_assets": "Fjern fra frakoblede filer", "remove_from_album": "Fjern fra album", + "remove_from_album_action_prompt": "{count} fjernet fra albumet", "remove_from_favorites": "Fjern fra favoritter", + "remove_from_lock_folder_action_prompt": "{count} fjernet fra låst mappe", "remove_from_locked_folder": "Fjern fra låst mappe", "remove_from_locked_folder_confirmation": "Er du sikker på at du vil flytte disse bildene og videoene ut av den låste mappen? De vil bli synlige i biblioteket.", "remove_from_shared_link": "Fjern fra delt lenke", @@ -1630,7 +1663,7 @@ "server_offline": "Server frakoblet", "server_online": "Server tilkoblet", "server_privacy": "Server personvern", - "server_stats": "Server Statistikk", + "server_stats": "Serverstatistikk", "server_version": "Server Versjon", "set": "Sett", "set_as_album_cover": "Sett som albumomslag", @@ -1667,6 +1700,7 @@ "settings_saved": "Innstillinger lagret", "setup_pin_code": "Sett opp en PINkode", "share": "Del", + "share_action_prompt": "Delte {count} objekter", "share_add_photos": "Legg til bilder", "share_assets_selected": "{count} valgt", "share_dialog_preparing": "Forbereder ...", @@ -1768,6 +1802,7 @@ "sort_title": "Tittel", "source": "Kilde", "stack": "Stable", + "stack_action_prompt": "{count} stakket", "stack_duplicates": "Stable duplikater", "stack_select_one_photo": "Velg hovedbilde for bildestabbel", "stack_selected_photos": "Stable valgte bilder", @@ -1838,9 +1873,10 @@ "total": "Total", "total_usage": "Totalt brukt", "trash": "Papirkurv", + "trash_action_prompt": "{count} flyttet til søppel", "trash_all": "Slett alt", "trash_count": "Slett {count, number}", - "trash_delete_asset": "Slett ressurs", + "trash_delete_asset": "Slett objekt", "trash_emptied": "Søppelbøtte tømt", "trash_no_results_message": "Her vises bilder og videoer som er flyttet til papirkurven.", "trash_page_delete_all": "Slett alt", @@ -1852,16 +1888,18 @@ "trash_page_title": "Søppelbøtte ({count})", "trashed_items_will_be_permanently_deleted_after": "Elementer i papirkurven vil bli permanent slettet etter {days, plural, one {# dag} other {# dager}}.", "type": "Type", - "unable_to_change_pin_code": "Klarte ikke å endre PINkode", + "unable_to_change_pin_code": "Klarte ikke å endre PIN-kode", "unable_to_setup_pin_code": "Klarte ikke å sette opp PINkode", "unarchive": "Fjern fra arkiv", + "unarchive_action_prompt": "{count} slettet fra Arkiv", "unarchived_count": "{count, plural, other {uarkivert #}}", "undo": "Angre", "unfavorite": "Fjern favoritt", + "unfavorite_action_prompt": "{count} slettet fra Favoritter", "unhide_person": "Vis person", "unknown": "Ukjent", "unknown_country": "Ukjent Land", - "unknown_year": "Ukjent År", + "unknown_year": "Ukjent år", "unlimited": "Ubegrenset", "unlink_motion_video": "Koble fra bevegelsesvideo", "unlink_oauth": "Fjern kobling til OAuth", @@ -1873,14 +1911,17 @@ "unsaved_change": "Ulagrede endringer", "unselect_all": "Fjern alle valg", "unselect_all_duplicates": "Fjern markeringen av alle duplikater", - "unselect_all_in": "Fjern alle i {group}", + "unselect_all_in": "Fjern velging av alle i {group}", "unstack": "avstable", + "unstack_action_prompt": "{count} ustakket", "unstacked_assets_count": "Ikke stablet {count, plural, one {# asset} other {# assets}}", + "untagged": "Umerket", "up_next": "Neste", "updated_at": "Oppdatert", "updated_password": "Passord oppdatert", "upload": "Last opp", "upload_concurrency": "Samtidig opplastning", + "upload_details": "Opplastingsdetaljer", "upload_dialog_info": "Vil du utføre backup av valgte objekt(er) til serveren?", "upload_dialog_title": "Last opp objekt", "upload_errors": "Opplasting fullført med {count, plural, one {# error} other {# errors}}, oppdater siden for å se nye opplastingsressurser.", @@ -1912,6 +1953,7 @@ "user_usage_stats_description": "Vis kontobruksstatistikk", "username": "Brukernavn", "users": "Brukere", + "users_added_to_album_count": "Lagt til {count, plural, one {#user} other {#users}} til albumet", "utilities": "Verktøy", "validate": "Valider", "validate_endpoint_error": "Skriv inn en gyldig URL", @@ -1919,7 +1961,7 @@ "version": "Versjon", "version_announcement_closing": "Din venn, Alex", "version_announcement_message": "Hei! En ny versjon av Immich er tilgjengelig. Vennligst ta deg tid til å lese utgivelsesnotatene for å sikre at oppsettet ditt er oppdatert for å forhindre feilkonfigurasjoner, spesielt hvis du bruker WatchTower eller en annen mekanisme som håndterer oppdatering av Immich-forekomsten din automatisk.", - "version_history": "Verson Historie", + "version_history": "Versjonshistorikk", "version_history_item": "Installert {version} den {date}", "video": "Video", "video_hover_setting": "Spill av forhåndsvisining mens du holder over musepekeren", @@ -1927,9 +1969,10 @@ "videos": "Videoer", "videos_count": "{count, plural, one {# Video} other {# Videoer}}", "view": "Vis", - "view_album": "Vis Album", + "view_album": "Vis album", "view_all": "Vis alle", "view_all_users": "Vis alle brukere", + "view_details": "Vis detaljer", "view_in_timeline": "Vis i tidslinje", "view_link": "Vis lenke", "view_links": "Vis lenker", @@ -1937,7 +1980,7 @@ "view_next_asset": "Vis neste fil", "view_previous_asset": "Vis forrige fil", "view_qr_code": "Vis QR-kode", - "view_stack": "Vis Stabbel", + "view_stack": "Vis stabel", "view_user": "Vis bruker", "viewer_remove_from_stack": "Fjern fra stabling", "viewer_stack_use_as_main_asset": "Bruk som hovedobjekt", @@ -1948,12 +1991,12 @@ "week": "Uke", "welcome": "Velkommen", "welcome_to_immich": "Velkommen til Immich", - "wifi_name": "Wi-Fi Navn", - "wrong_pin_code": "Feil PINkode", + "wifi_name": "Wi-Fi-navn", + "wrong_pin_code": "Feil PIN-kode", "year": "År", "years_ago": "{years, plural, one {# år} other {# år}} siden", "yes": "Ja", "you_dont_have_any_shared_links": "Du har ingen delte lenker", - "your_wifi_name": "Ditt Wi-Fi navn", + "your_wifi_name": "Ditt Wi-Fi-navn", "zoom_image": "Zoom Bilde" } diff --git a/i18n/nl.json b/i18n/nl.json index 07699e0b31..ecd8418fa5 100644 --- a/i18n/nl.json +++ b/i18n/nl.json @@ -93,7 +93,7 @@ "job_created": "Taak aangemaakt", "job_not_concurrency_safe": "Deze taak kan niet gelijktijdig worden uitgevoerd.", "job_settings": "Achtergrondtaak-instellingen", - "job_settings_description": "Beheer gelijktijdige taken", + "job_settings_description": "Beheer aantal gelijktijdige taken", "job_status": "Taakstatus", "jobs_delayed": "{jobCount, plural, other {# vertraagd}}", "jobs_failed": "{jobCount, plural, other {# mislukt}}", @@ -166,6 +166,20 @@ "metadata_settings_description": "Beheer metadata instellingen", "migration_job": "Migratie", "migration_job_description": "Migreer thumbnails voor assets en gezichten naar de nieuwste mapstructuur", + "nightly_tasks_cluster_faces_setting_description": "Gezichtsherkenning uitvoeren op nieuw gedetecteerde gezichten", + "nightly_tasks_cluster_new_faces_setting": "Cluster nieuwe gezichten", + "nightly_tasks_database_cleanup_setting": "Database opschoon taken", + "nightly_tasks_database_cleanup_setting_description": "Ruim oude data op van de database", + "nightly_tasks_generate_memories_setting": "Genereer herinneringen", + "nightly_tasks_generate_memories_setting_description": "Maak nieuwe herinneringen van assets", + "nightly_tasks_missing_thumbnails_setting": "Genereer ontbrekende thumbnails", + "nightly_tasks_missing_thumbnails_setting_description": "Assets zonder thumbnail in een wachtrij plaatsen voor het genereren van thumbnails", + "nightly_tasks_settings": "Instellingen voor nacht taken", + "nightly_tasks_settings_description": "Beheer nacht taken", + "nightly_tasks_start_time_setting": "Start tijd", + "nightly_tasks_start_time_setting_description": "De tijd waarop de server begint met het uitvoeren van de nacht taken", + "nightly_tasks_sync_quota_usage_setting": "Synchroniseer quota gebruik", + "nightly_tasks_sync_quota_usage_setting_description": "update gebruiker opslag quota, gebaseerd op huidig gebruik", "no_paths_added": "Geen paden toegevoegd", "no_pattern_added": "Geen patroon toegevoegd", "note_apply_storage_label_previous_assets": "Opmerking: om het opslaglabel toe te passen op eerder geüploade assets, voer de volgende taak uit", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "Omleidings-URI voor mobiel", "oauth_mobile_redirect_uri_override": "Omleidings-URI voor mobiele app overschrijven", "oauth_mobile_redirect_uri_override_description": "Inschakelen wanneer de OAuth-provider geen mobiele URI toestaat, zoals ''{callback}''", + "oauth_role_claim": "Rol claim", + "oauth_role_claim_description": "Automatisch admin toegang geven als deze claim aanwezig is. De claim kan 'user' of 'admin' zijn.", "oauth_settings": "OAuth", "oauth_settings_description": "Beheer OAuth inloginstellingen", "oauth_settings_more_details": "Raadpleeg de documentatie voor meer informatie over deze functie.", @@ -305,7 +321,7 @@ "transcoding_policy_description": "Stel in wanneer een video wordt getranscodeerd", "transcoding_preferred_hardware_device": "Voorkeur hardwareapparaat", "transcoding_preferred_hardware_device_description": "Geldt alleen voor VAAPI en QSV. Stelt de dri node in die wordt gebruikt voor hardwaretranscodering.", - "transcoding_preset_preset": "Preset (-preset)", + "transcoding_preset_preset": "Voorkeuze (-preset)", "transcoding_preset_preset_description": "Compressiesnelheid. Langzamere presets produceren kleinere bestanden en verhogen de kwaliteit bij het targeten van een bepaalde bitrate. VP9 negeert snelheden boven 'faster'.", "transcoding_reference_frames": "Referentie frames", "transcoding_reference_frames_description": "Het aantal frames om naar te verwijzen bij het comprimeren van een bepaald frame. Hogere waarden verbeteren de compressie-efficiëntie, maar vertragen de codering. Bij 0 wordt deze waarde automatisch ingesteld.", @@ -357,10 +373,12 @@ "admin_password": "Beheerder wachtwoord", "administration": "Beheer", "advanced": "Geavanceerd", + "advanced_settings_beta_timeline_subtitle": "Probeer de nieuwe app-ervaring", + "advanced_settings_beta_timeline_title": "Beta tijdlijn", "advanced_settings_enable_alternate_media_filter_subtitle": "Gebruik deze optie om media te filteren tijdens de synchronisatie op basis van alternatieve criteria. Gebruik dit enkel als de app problemen heeft met het detecteren van albums.", "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTEEL] Gebruik een alternatieve album synchronisatie filter", "advanced_settings_log_level_title": "Logniveau: {level}", - "advanced_settings_prefer_remote_subtitle": "Sommige apparaten zijn traag met het laden van afbeeldingen die lokaal zijn opgeslagen op het apparaat. Activeer deze instelling om in plaats daarvan externe afbeeldingen te laden.", + "advanced_settings_prefer_remote_subtitle": "Sommige apparaten zijn traag met het laden van lokale afbeeldingen. Activeer deze instelling om in plaats daarvan externe afbeeldingen te laden.", "advanced_settings_prefer_remote_title": "Externe afbeeldingen laden", "advanced_settings_proxy_headers_subtitle": "Definieer proxy headers die Immich bij elk netwerkverzoek moet verzenden", "advanced_settings_proxy_headers_title": "Proxy headers", @@ -427,6 +445,7 @@ "app_settings": "App instellingen", "appears_in": "Komt voor in", "archive": "Archief", + "archive_action_prompt": "{count}toegevoegd aan Archief", "archive_or_unarchive_photo": "Foto archiveren of uit het archief halen", "archive_page_no_archived_assets": "Geen gearchiveerde assets gevonden", "archive_page_title": "Archief ({count})", @@ -459,12 +478,11 @@ "asset_skipped_in_trash": "In prullenbak", "asset_uploaded": "Geüpload", "asset_uploading": "Uploaden…", - "asset_viewer_settings_subtitle": "Beheer je instellingen voor gallerijweergave", - "asset_viewer_settings_title": "Foto weergave", + "asset_viewer_settings_subtitle": "Beheer je instellingen voor galerijweergave", + "asset_viewer_settings_title": "Fotoweergave", "assets": "Assets", "assets_added_count": "{count, plural, one {# asset} other {# assets}} toegevoegd", "assets_added_to_album_count": "{count, plural, one {# asset} other {# assets}} aan het album toegevoegd", - "assets_added_to_name_count": "{count, plural, one {# asset} other {# assets}} toegevoegd aan {hasName, select, true {{name}} other {nieuw album}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {# asset} other {# assets}} konden niet aan album toegevoegd worden", "assets_count": "{count, plural, one {# asset} other {# assets}}", "assets_deleted_permanently": "{count} asset(s) permanent verwijderd", @@ -697,13 +715,13 @@ "curated_object_page_title": "Dingen", "current_device": "Huidig apparaat", "current_pin_code": "Huidige PIN code", - "current_server_address": "Huidige serveradres", + "current_server_address": "Huidig serveradres", "custom_locale": "Aangepaste landinstelling", "custom_locale_description": "Formatteer datums en getallen op basis van de taal en de regio", "daily_title_text_date": "E dd MMM", "daily_title_text_date_year": "E dd MMM yyyy", "dark": "Donker", - "darkTheme": "Donker thema in-/uitschakelen", + "dark_theme": "Wissel naar donker thema", "date_after": "Datum na", "date_and_time": "Datum en tijd", "date_before": "Datum voor", @@ -719,6 +737,7 @@ "default_locale": "Standaard landinstelling", "default_locale_description": "Formatteer datums en getallen op basis van de landinstellingen van je browser", "delete": "Verwijderen", + "delete_action_prompt": "{count} permanent verwijderd", "delete_album": "Album verwijderen", "delete_api_key_prompt": "Weet je zeker dat je deze API-sleutel wilt verwijderen?", "delete_dialog_alert": "Deze items zullen permanent verwijderd worden van Immich en je apparaat", @@ -732,6 +751,7 @@ "delete_key": "Verwijder key", "delete_library": "Verwijder bibliotheek", "delete_link": "Verwijder link", + "delete_local_action_prompt": "{count} lokaal verwijderd", "delete_local_dialog_ok_backed_up_only": "Verwijder alleen met back-up", "delete_local_dialog_ok_force": "Toch verwijderen", "delete_others": "Andere verwijderen", @@ -762,6 +782,7 @@ "documentation": "Documentatie", "done": "Klaar", "download": "Downloaden", + "download_action_prompt": "{count} assets downloaden", "download_canceled": "Download geannuleerd", "download_complete": "Download voltooid", "download_enqueue": "Download in wachtrij", @@ -799,6 +820,7 @@ "edit_key": "Key bewerken", "edit_link": "Link bewerken", "edit_location": "Locatie bewerken", + "edit_location_action_prompt": "{count} locatie(s) aangepast", "edit_location_dialog_title": "Locatie", "edit_name": "Naam bewerken", "edit_people": "Mensen bewerken", @@ -984,6 +1006,7 @@ "failed_to_load_assets": "Kan assets niet laden", "failed_to_load_folder": "Laden van map mislukt", "favorite": "Favoriet", + "favorite_action_prompt": "{count}toegevoegd aan Favorieten", "favorite_or_unfavorite_photo": "Foto markeren als of verwijderen uit favorieten", "favorites": "Favorieten", "favorites_page_no_favorites": "Geen favoriete assets gevonden", @@ -1031,7 +1054,7 @@ "headers_settings_tile_title": "Aangepaste proxy headers", "hi_user": "Hallo {name} ({email})", "hide_all_people": "Verberg alle mensen", - "hide_gallery": "Gallerij verbergen", + "hide_gallery": "Galerij verbergen", "hide_named_person": "Verberg persoon {name}", "hide_password": "Verberg wachtwoord", "hide_person": "Verberg persoon", @@ -1127,6 +1150,7 @@ "library_page_sort_created": "Meest recent gemaakt", "library_page_sort_last_modified": "Laatst aangepast", "library_page_sort_title": "Albumtitel", + "licenses": "Licenties", "light": "Licht", "like_deleted": "Like verwijderd", "link_motion_video": "Koppel bewegende video", @@ -1246,6 +1270,7 @@ "more": "Meer", "move": "Verplaats", "move_off_locked_folder": "Verplaats uit vergrendelde map", + "move_to_lock_folder_action_prompt": "{count} toegevoegd aan de vergrendelde map", "move_to_locked_folder": "Verplaats naar vergrendelde map", "move_to_locked_folder_confirmation": "Deze foto’s en video’s worden uit alle albums verwijderd en zijn alleen te bekijken in de vergrendelde map", "moved_to_archive": "{count, plural, one {# asset} other {# assets}} verplaatst naar archief", @@ -1495,7 +1520,9 @@ "remove_custom_date_range": "Aangepast datumbereik verwijderen", "remove_deleted_assets": "Verwijder offline bestanden", "remove_from_album": "Verwijder uit album", + "remove_from_album_action_prompt": "{count} verwijderd uit het album", "remove_from_favorites": "Verwijderen uit favorieten", + "remove_from_lock_folder_action_prompt": "{count} verwijderd uit de vergrendelde map", "remove_from_locked_folder": "Verwijder uit de vergrendelde map", "remove_from_locked_folder_confirmation": "Weet je zeker dat je deze foto's en video's uit de vergrendelde map wilt verplaatsen? Ze zijn dan weer zichtbaar in je bibliotheek.", "remove_from_shared_link": "Verwijderen uit gedeelde link", @@ -1667,6 +1694,7 @@ "settings_saved": "Instellingen opgeslagen", "setup_pin_code": "Stel een PIN code in", "share": "Delen", + "share_action_prompt": "{count} assets gedeeld", "share_add_photos": "Foto's toevoegen", "share_assets_selected": "{count} geselecteerd", "share_dialog_preparing": "Voorbereiden...", @@ -1768,6 +1796,7 @@ "sort_title": "Titel", "source": "Bron", "stack": "Stapel", + "stack_action_prompt": "{count} gestapeld", "stack_duplicates": "Stapel duplicaten", "stack_select_one_photo": "Selecteer één primaire foto voor de stapel", "stack_selected_photos": "Geselecteerde foto's stapelen", @@ -1838,6 +1867,7 @@ "total": "Totaal", "total_usage": "Totaal gebruik", "trash": "Prullenbak", + "trash_action_prompt": "{count} verwijderd naar de prullenbak", "trash_all": "Verplaats alle naar prullenbak", "trash_count": "{count, number} naar prullenbak", "trash_delete_asset": "Assets naar prullenbak verplaatsen of verwijderen", @@ -1855,9 +1885,11 @@ "unable_to_change_pin_code": "PIN code kan niet gewijzigd worden", "unable_to_setup_pin_code": "PIN code kan niet ingesteld worden", "unarchive": "Herstellen uit archief", + "unarchive_action_prompt": "{count} verwijderd uit het archief", "unarchived_count": "{count, plural, other {# verwijderd uit archief}}", "undo": "Ongedaan maken", "unfavorite": "Verwijderen uit favorieten", + "unfavorite_action_prompt": "{count} verwijderd uit favorieten", "unhide_person": "Persoon zichtbaar maken", "unknown": "Onbekend", "unknown_country": "Onbekend Land", @@ -1875,12 +1907,14 @@ "unselect_all_duplicates": "Deselecteer alle duplicaten", "unselect_all_in": "Deselecteer alles in {group}", "unstack": "Ontstapelen", + "unstack_action_prompt": "{count} ontstapeld", "unstacked_assets_count": "{count, plural, one {# asset} other {# assets}} ontstapeld", + "untagged": "Ongemarkeerd", "up_next": "Volgende", "updated_at": "Geüpdatet", "updated_password": "Wachtwoord bijgewerkt", "upload": "Uploaden", - "upload_concurrency": "Upload gelijktijdigheid", + "upload_concurrency": "Aantal gelijktijdige uploads", "upload_dialog_info": "Wil je een backup maken van de geselecteerde asset(s) op de server?", "upload_dialog_title": "Asset uploaden", "upload_errors": "Upload voltooid met {count, plural, one {# fout} other {# fouten}}, vernieuw de pagina om de nieuwe assets te zien.", @@ -1912,6 +1946,7 @@ "user_usage_stats_description": "Bekijk statistieken van accountgebruik", "username": "Gebruikersnaam", "users": "Gebruikers", + "users_added_to_album_count": "{count, plural, one {# Gebruiker} other {# Gebruikers}} toegevoegd aan album", "utilities": "Gereedschap", "validate": "Valideren", "validate_endpoint_error": "Vul een geldige URL in", diff --git a/i18n/nn.json b/i18n/nn.json index 7fb5fdef02..8b04b5d4b2 100644 --- a/i18n/nn.json +++ b/i18n/nn.json @@ -22,18 +22,20 @@ "add_partner": "Legg til partnar", "add_path": "Legg til sti", "add_photos": "Legg til bilete", + "add_tag": "Legg til tagg", "add_to": "Legg til…", - "add_to_album": "Legg til album", + "add_to_album": "Legg til i album", "add_to_album_bottom_sheet_added": "Lagt til i {album}", "add_to_album_bottom_sheet_already_exists": "Allereie i {album}", - "add_to_shared_album": "Legg til delt album", + "add_to_shared_album": "Legg til i delt album", "add_url": "Legg til URL", - "added_to_archive": "Lagt til arkiv", - "added_to_favorites": "Lagt til favorittar", - "added_to_favorites_count": "Lagt {count, number} til favorittar", + "added_to_archive": "Lagt til i arkiv", + "added_to_favorites": "Lagt til i favorittar", + "added_to_favorites_count": "La til {count, number} i favorittar", "admin": { "add_exclusion_pattern_description": "Legg til utelatingsmønstre. Du kan bruke jokerteikna *, **, og ? for å finne filer som passar mønsteret. For å ignorere alle filer i ei mappe kalla \"Raw\", bruk \"Raw\", bruk \"**/Raw/**\". For å ignorere alle filer som sluttar på \".tif\", bruk \"**/*.tif\". For å ignorere ein absolutt sti, bruk \"/path/to/ignore/**\".", - "asset_offline_description": "Denne eksterne bibliotekressursen finst ikkje lenger på disk og har blitt flytta til papirkurven. Om fila blei flytta innad i biblioteket, sjekk tidslinja di for den tilsvarande ressursen. For å gjenopprette ressursen, vennligst sørg for at filstien under er tilgjengeleg for Immich og skann biblioteket.", + "admin_user": "Admin-brukar", + "asset_offline_description": "Denne eksterne bibliotekressursen finst ikkje lenger på disk og har blitt flytta til papirkorga. Om fila blei flytta innad i biblioteket, sjekk tidslinja di for den tilsvarande ressursen. For å gjenopprette ressursen, vennligst sørg for at filstien under er tilgjengeleg for Immich og skann biblioteket.", "authentication_settings": "Godkjenningsinnstillingar", "authentication_settings_description": "Handsam passord, OAuth, og godkjenningsinnstillingar", "authentication_settings_disable_all": "Er du sikker at du ynskjer å gjera alle innloggingsmetodar uverksame? Innlogging vil bli heilt uverksam.", @@ -43,7 +45,7 @@ "backup_database_enable_description": "Aktiver tryggingskopiering av database", "backup_keep_last_amount": "Antal tryggingskopiar å behalde", "backup_settings": "Tryggingskopi-innstillingar", - "backup_settings_description": "Handter innstillingar for tryggingskopiering av database. Merk: Desse jobbane vert ikkje overvaka, og du får inga varsling ved feil.", + "backup_settings_description": "Handter innstillingar for tryggingskopiering av database. Merk: Desse jobbane vert ikkje overvaka, og du får inga varsling ved feil.", "cleared_jobs": "Rydda jobbar for: {job}", "config_set_by_file": "Oppsettet blir sett av ei oppsettfil", "confirm_delete_library": "Er du sikker at du vil slette biblioteket {library}?", @@ -51,6 +53,7 @@ "confirm_email_below": "For å bekrefte, skriv \"{email}\" under", "confirm_reprocess_all_faces": "Er du sikker på at du vil behandle alle ansikt på nytt? Det vil òg fjerne namngjevne personar.", "confirm_user_password_reset": "Er du sikker at du vil tilbakestille passordet til {user}?", + "confirm_user_pin_code_reset": "Er du sikker på at du vil tilbakestille {user} sin PIN-kode?", "create_job": "Lag jobb", "cron_expression": "Cron uttrykk", "cron_expression_description": "Set inn skanningsintervall med cron-formatet. For meir informasjon sjå t.d. Crontab Guru", @@ -66,6 +69,10 @@ "force_delete_user_warning": "ÅTVARING: Handlinga fjernar brukaren og all data. Du kan ikkje angre, og filane kan ikkje gjenopprettast.", "image_format": "Format", "image_format_description": "WebP gjev mindre filstorleik enn JPEG, men er treigare å lage.", + "image_fullsize_description": "Bilete i full storleik utan metadata, i bruk når zooma inn", + "image_fullsize_enabled": "Skru på generering av bilete i full storleik", + "image_fullsize_quality_description": "Kvalitet på bilete i full storleik frå 1-100. Høgare er betre, men gjev større filer.", + "image_fullsize_title": "Innstillingar for bilete i full storleik", "image_prefer_embedded_preview": "Bruk helst innebygd førehandsvisning", "image_prefer_embedded_preview_setting_description": "Når mogleg bruk innebygd førehandsvisning av RAW bilete som inndata til biletehandsaming. For noko bilete kan det gje meir nøyaktige farger, men kvaliteten kjem an på kamera og det kan oppstå komprimeringsartefakt i bilete.", "image_prefer_wide_gamut": "Bruk helst breitt fargespektrum", @@ -121,6 +128,7 @@ "machine_learning_max_detection_distance": "Maksimal oppdagingsverdi", "machine_learning_max_detection_distance_description": "Den største skilnaden mellom to bilete for å rekne dei som duplikat, frå 0.001-0.1. Større verdiar finn fleire duplikat, men kan gje falske treff.", "machine_learning_max_recognition_distance": "Maksimal attkjenningsverdi", + "machine_learning_max_recognition_distance_description": "Maksimal forskjell på to ansikt for å bli rekna som same person, på ein skala frå 0-2. Eit lågare tal kan hindre to personar i å bli rekna som den same, medan eit høgare tal kan hindre at same individ vert merka som to forskjellige personar. Merk at det er lettare å slå saman to personar enn å dele éin person i to, så sikt mot den låge sida av skalaen når mogleg.", "machine_learning_min_detection_score": "Minimum deteksjonsresultat", "machine_learning_min_detection_score_description": "Minimum tillitspoeng for at eit ansikt skal bli oppdaga, på ein skala frå 0 til 1. Lågare verdiar vil oppdage fleire ansikt, men kan føre til feilaktige treff.", "machine_learning_min_recognized_faces": "Minimum gjenkjende ansikt", @@ -137,16 +145,38 @@ "map_gps_settings_description": "Administrer innstillingar for kart og GPS (Reversert geokoding)", "map_light_style": "Lys modus", "map_settings": "Kart", + "map_settings_description": "Endre kartinnstillingar", + "map_style_description": "URL til eit style.json-karttema", "metadata_extraction_job": "Hent ut metadata", + "metadata_extraction_job_description": "Hent ut metadata frå kvart bilete, slik som GPS, ansikt og oppløysing", + "metadata_faces_import_setting": "Skru på import av ansikt", + "metadata_faces_import_setting_description": "Importer ansikt frå bilete sine EXIF-data og sidecar-filer", "metadata_settings": "Metadata Innstillinger", + "metadata_settings_description": "Endre metadata-innstillingar", "migration_job": "Migrasjon", "notification_email_from_address": "Frå adresse", + "notification_email_test_email_failed": "Mislukka sending av test-e-post, sjekk konfigurasjonen din", + "notification_email_test_email_sent": "Det vart sendt ei test-melding til {email}. Sjekk e-posten din.", + "notification_email_username_description": "Brukarnamn for autentisering på e-post-serveren", + "notification_enable_email_notifications": "Aktiver e-post-varslingar", "notification_settings": "Varselinnstillingar", + "notification_settings_description": "Endre varslingsinnstillingar, inkludert e-post", "oauth_auto_launch": "Autostart", + "oauth_auto_launch_description": "Start OAuth-innloggingsprosessen automatisk når innloggingssida vert opna", + "oauth_auto_register_description": "Registrer nye brukarar automatisk etter innlogging med OAuth", "oauth_button_text": "Tekst på knapp", + "oauth_client_secret_description": "Krevjast dersom PKCE (Proof Key for Code Exchange) ikkje støttast av OAuth-tilbydaren", + "oauth_enable_description": "Logg inn med OAuth", + "oauth_settings": "OAuth", + "oauth_settings_description": "Innstillingar for innlogging med OAuth", + "oauth_storage_quota_default": "Standard lagringskvote (GiB)", + "oauth_timeout": "Tidsavbrot på førespurnad", + "oauth_timeout_description": "Tidsavbrot for førespurnadar i millisekund", "password_enable_description": "Logg inn med e-post og passord", "password_settings": "Passordinnlogging", + "password_settings_description": "Innstillingar for innlogging med passord", "person_cleanup_job": "Personopprydding", + "quota_size_gib": "Lagringskvote (GiB)", "refreshing_all_libraries": "Laster alle bibliotek opp att", "registration": "Administrator registrering", "registration_description": "Sidan du er den første brukaren på systemet, vil du bli utnevnt til administrator og ha ansvar for administrative oppgåver. Du vil òg opprette eventuelle nye brukarar.", @@ -164,8 +194,17 @@ "server_settings_description": "Administrer serverinnstillingar", "server_welcome_message": "Velkomstmelding", "server_welcome_message_description": "Ei melding som synast på innloggingssida.", + "sidecar_job": "Sidecar-metadata", + "sidecar_job_description": "Oppdag eller synkroniser sidecar-metadata frå filsystemet", + "slideshow_duration_description": "Antal sekund å vise kvart bilete", + "storage_template_date_time_sample": "Døme på tid {date}", + "storage_template_enable_description": "Aktiver lagringsmal-motoren", + "storage_template_migration": "Overgang til ny lagringsmal", + "storage_template_migration_job": "Omorganisering etter ny lagringsmal", + "storage_template_settings": "Lagringsmal", "system_settings": "Systeminnstillingar", "template_email_preview": "Førehandsvisning", + "thumbnail_generation_job": "Generer miniatyrbilete", "transcoding_acceleration_nvenc": "NVENC (Krev NVIDIA GPU)", "transcoding_acceleration_qsv": "Quick Sync (Krev 7. generasjons Intel CPU eller nyare)", "transcoding_acceleration_rkmpp": "RKMPP (Berre på Rockchip SOCer)", @@ -180,7 +219,11 @@ "transcoding_audio_codec": "Lydkodek", "transcoding_audio_codec_description": "Opus er det valet med høgast lydkvalitet, men mindre kompabilitet med gamlare einingar og programvare.", "transcoding_bitrate_description": "Videoar med bitrate over høgste tillatte verdi, eller i eit format som ikkje er tillate", - "transcoding_codecs_learn_more": "For å lære meir om nytta begrep, sjå FFmpeg dokumentasjon for H.264 codec, HEVC codec and VP9 codec." + "transcoding_codecs_learn_more": "For å lære meir om nytta begrep, sjå FFmpeg dokumentasjon for H.264 codec, HEVC codec and VP9 codec.", + "transcoding_constant_rate_factor_description": "Videokvalitet. Vanlege verdiar er 23 for H.264, 28 for HEVC, 31 for VP9, og 35 for AV1. Lågare er betre, men gjev større filer.", + "transcoding_hardware_acceleration": "Maskinvare-akselerasjon", + "transcoding_max_bitrate": "Maksimal bitrate", + "transcoding_optimal_description": "Videoar med for høg oppløysing, eller ikkje i eit godkjend format" }, "admin_email": "Adminisrator E-post", "admin_password": "Administratorpassord", diff --git a/i18n/pl.json b/i18n/pl.json index e84c6f53e6..5adc6df943 100644 --- a/i18n/pl.json +++ b/i18n/pl.json @@ -5,7 +5,7 @@ "acknowledge": "Zrozumiałem/łam", "action": "Akcja", "action_common_update": "Aktualizuj", - "actions": "Akcje/i", + "actions": "Akcje", "active": "Aktywne", "activity": "Aktywność", "activity_changed": "Aktywność jest {enabled, select, true {włączona} other {wyłączona}}", @@ -166,6 +166,20 @@ "metadata_settings_description": "Zarządzaj ustawieniami metadanych", "migration_job": "Migracja", "migration_job_description": "Przenieś miniatury zasobów i twarzy do najnowszej struktury folderów", + "nightly_tasks_cluster_faces_setting_description": "Uruchom rozpoznawanie twarzy dla nowo wykrytych twarzy", + "nightly_tasks_cluster_new_faces_setting": "Zgrupuj nowe twarze", + "nightly_tasks_database_cleanup_setting": "Zadania związane z czyszczeniem bazy danych", + "nightly_tasks_database_cleanup_setting_description": "Wyczyść stare, nieaktualne dane z bazy danych", + "nightly_tasks_generate_memories_setting": "Generuj wspomnienia", + "nightly_tasks_generate_memories_setting_description": "Stwórz nowe wspomnienia z zasobów", + "nightly_tasks_missing_thumbnails_setting": "Wygeneruj brakujące miniatury", + "nightly_tasks_missing_thumbnails_setting_description": "Dodaj zasoby bez miniatur do kolejki generowania miniatur", + "nightly_tasks_settings": "Ustawienia nocnych zadań", + "nightly_tasks_settings_description": "Zarządzaj zadaniami wykonywanymi w nocy", + "nightly_tasks_start_time_setting": "Czas rozpoczęcia", + "nightly_tasks_start_time_setting_description": "Czas, w którym serwer rozpoczyna wykonywanie nocnych zadań", + "nightly_tasks_sync_quota_usage_setting": "Zsynchronizuj wykorzystanie kontyngentu", + "nightly_tasks_sync_quota_usage_setting_description": "Zaktualizuj kontyngent przestrzeni dyskowej użytkownika na podstawie aktualnego zużycia", "no_paths_added": "Nie dodano ścieżki", "no_pattern_added": "Nie dodano wzoru", "note_apply_storage_label_previous_assets": "Uwaga: aby zastosować etykietę magazynu do wcześniej przesłanych zasobów, uruchom", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "Mobilny adres zwrotny", "oauth_mobile_redirect_uri_override": "Zapasowy URI przekierowania mobilnego", "oauth_mobile_redirect_uri_override_description": "Włącz, gdy dostawca OAuth nie pozwala na mobilne identyfikatory URI typu ''{callback}''", + "oauth_role_claim": "Oświadczenie roli", + "oauth_role_claim_description": "Automatycznie przyznaj dostęp administratora na podstawie obecności tego oświadczenia. Oświadczenie może mieć wartość „użytkownik” lub „administrator”.", "oauth_settings": "OAuth", "oauth_settings_description": "Zarządzaj ustawieniami logowania OAuth", "oauth_settings_more_details": "Więcej informacji o tej funkcji znajdziesz w dokumentacji.", @@ -357,10 +373,12 @@ "admin_password": "Hasło Administratora", "administration": "Administracja", "advanced": "Zaawansowane", + "advanced_settings_beta_timeline_subtitle": "Wypróbuj nową funkcjonalność aplikacji", + "advanced_settings_beta_timeline_title": "Beta-Timeline", "advanced_settings_enable_alternate_media_filter_subtitle": "Użyj tej opcji do filtrowania mediów podczas synchronizacji alternatywnych kryteriów. Używaj tylko wtedy gdy aplikacja ma problemy z wykrywaniem wszystkich albumów.", "advanced_settings_enable_alternate_media_filter_title": "[EKSPERYMENTALNE] Użyj alternatywnego filtra synchronizacji albumu", "advanced_settings_log_level_title": "Poziom szczegółowości dziennika: {level}", - "advanced_settings_prefer_remote_subtitle": "Niektóre urządzenia bardzo wolno ładują miniatury z zasobów na urządzeniu. Aktywuj to ustawienie, aby ładować zdalne obrazy.", + "advanced_settings_prefer_remote_subtitle": "Niektóre urządzenia bardzo wolno ładują miniatury z lokalnych zasobów. Aktywuj to ustawienie, aby ładować zdalne obrazy.", "advanced_settings_prefer_remote_title": "Preferuj obrazy zdalne", "advanced_settings_proxy_headers_subtitle": "Zdefiniuj nagłówki proxy, które Immich powinien wysyłać z każdym żądaniem sieciowym", "advanced_settings_proxy_headers_title": "Nagłówki proxy", @@ -427,6 +445,7 @@ "app_settings": "Ustawienia Aplikacji", "appears_in": "W albumach", "archive": "Archiwum", + "archive_action_prompt": "{count} dodanych do Archiwum", "archive_or_unarchive_photo": "Dodaj lub usuń zasób z archiwum", "archive_page_no_archived_assets": "Nie znaleziono zarchiwizowanych zasobów", "archive_page_title": "Archiwum {count}", @@ -464,7 +483,6 @@ "assets": "Zasoby", "assets_added_count": "Dodano {count, plural, one {# zasób} few {# zasoby} other {# zasobów}}", "assets_added_to_album_count": "Dodano {count, plural, one {# zasób} few {# zasoby} other {# zasobów}} do albumu", - "assets_added_to_name_count": "Dodano {count, plural, one {# zasób} few {# zasoby} other {# zasobów}} do {hasName, select, true {{name}} other {new album}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {sztuka Elementu} other {szt. Elementów}} nie może być dodana do albumu", "assets_count": "{count, plural, one {# zasób} few {# zasoby} other {# zasobów}}", "assets_deleted_permanently": "{count} zostało trwale usuniętych", @@ -703,7 +721,7 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Ciemny", - "darkTheme": "Włącz ciemny motyw", + "dark_theme": "Przełącz ciemny motyw", "date_after": "Data po", "date_and_time": "Data i godzina", "date_before": "Data przed", @@ -719,6 +737,7 @@ "default_locale": "Domyślny Region", "default_locale_description": "Formatuj daty i liczby na podstawie ustawień Twojej przeglądarki", "delete": "Usuń", + "delete_action_prompt": "{count} trwale usuniętych", "delete_album": "Usuń album", "delete_api_key_prompt": "Czy na pewno chcesz usunąć ten klucz API?", "delete_dialog_alert": "Te elementy zostaną trwale usunięte z Immich i z Twojego urządzenia", @@ -732,6 +751,7 @@ "delete_key": "Usuń klucz", "delete_library": "Usuń bibliotekę", "delete_link": "Usuń link", + "delete_local_action_prompt": "{count} lokalnie usunięto", "delete_local_dialog_ok_backed_up_only": "Usuń tylko kopię zapasową", "delete_local_dialog_ok_force": "Usuń mimo to", "delete_others": "Usuń inne", @@ -799,6 +819,7 @@ "edit_key": "Edytuj klucz", "edit_link": "Edytuj link", "edit_location": "Edytuj lokalizację", + "edit_location_action_prompt": "{count} edytowana lokalizacja", "edit_location_dialog_title": "Lokalizacja", "edit_name": "Edytuj nazwę", "edit_people": "Edytuj osoby", @@ -984,6 +1005,7 @@ "failed_to_load_assets": "Nie udało się załadować zasobów", "failed_to_load_folder": "Nie udało się załadować folderu", "favorite": "Ulubione", + "favorite_action_prompt": "{count} dodane do ulubionych", "favorite_or_unfavorite_photo": "Dodaj lub usuń z ulubionych", "favorites": "Ulubione", "favorites_page_no_favorites": "Nie znaleziono ulubionych zasobów", @@ -1127,6 +1149,7 @@ "library_page_sort_created": "Ostatnio utworzone", "library_page_sort_last_modified": "Ostatnio zmodyfikowany", "library_page_sort_title": "Tytuł albumu", + "licenses": "Licencje", "light": "Jasny", "like_deleted": "Polubienie usunięte", "link_motion_video": "Podłącz ruchome wideo", @@ -1246,6 +1269,7 @@ "more": "Więcej", "move": "Przenieś", "move_off_locked_folder": "Przenieś z folderu zablokowanego", + "move_to_lock_folder_action_prompt": "{count} dodanych do folderu zablokowanego", "move_to_locked_folder": "Przenieś do folderu zablokowanego", "move_to_locked_folder_confirmation": "Te zdjęcia i filmy zostaną usunięte ze wszystkich albumów i będą widzialne tylko w folderze zablokowanym", "moved_to_archive": "Przeniesiono {count, plural, one {# zasób} few {# zasoby} other {# zasobów}} do archiwum", @@ -1495,7 +1519,9 @@ "remove_custom_date_range": "Usuń niestandardowy zakres dat", "remove_deleted_assets": "Usuń Niedostępne Pliki", "remove_from_album": "Usuń z albumu", + "remove_from_album_action_prompt": "{count} usunięto z albumu", "remove_from_favorites": "Usuń z ulubionych", + "remove_from_lock_folder_action_prompt": "{count} usunięte z folderu zablokowanego", "remove_from_locked_folder": "Usuń z folderu zablokowanego", "remove_from_locked_folder_confirmation": "Czy na pewno chcesz przenieść te zdjęcia i filmy z folderu zablokowanego? Będą one widoczne w bibliotece.", "remove_from_shared_link": "Usuń z udostępnionego linku", @@ -1667,6 +1693,7 @@ "settings_saved": "Ustawienia zapisane", "setup_pin_code": "Ustaw kod PIN", "share": "Udostępnij", + "share_action_prompt": "Udostępniono {count} zasobów", "share_add_photos": "Dodaj zdjęcia", "share_assets_selected": "Wybrano {count}", "share_dialog_preparing": "Przygotowywanie…", @@ -1768,6 +1795,7 @@ "sort_title": "Tytuł", "source": "Źródło", "stack": "Stos", + "stack_action_prompt": "{count} zgrupowano", "stack_duplicates": "Stos duplikatów", "stack_select_one_photo": "Wybierz jedno główne zdjęcie do stosu", "stack_selected_photos": "Układaj wybrane zdjęcia", @@ -1838,6 +1866,7 @@ "total": "Całkowity", "total_usage": "Całkowite wykorzystanie", "trash": "Kosz", + "trash_action_prompt": "{count} przeniesione do kosza", "trash_all": "Usuń wszystkie", "trash_count": "Kosz {count, number}", "trash_delete_asset": "Kosz/Usuń zasób", @@ -1855,9 +1884,11 @@ "unable_to_change_pin_code": "Nie można zmienić kodu PIN", "unable_to_setup_pin_code": "Nie można ustawić kodu PIN", "unarchive": "Cofnij archiwizację", + "unarchive_action_prompt": "{count} usunięto z archiwum", "unarchived_count": "{count, plural, one {# cofnięta archiwizacja} few {# cofnięte archiwizacje} other {# cofniętych archiwizacji}}", "undo": "Cofnij", "unfavorite": "Usuń z ulubionych", + "unfavorite_action_prompt": "{count} usunięto z ulubionych", "unhide_person": "Przywróć osobę", "unknown": "Nieznany", "unknown_country": "Nieznane państwo", @@ -1875,7 +1906,9 @@ "unselect_all_duplicates": "Odznacz wszystkie duplikaty", "unselect_all_in": "Odznacz wszystkie w {group}", "unstack": "Rozłóż stos", + "unstack_action_prompt": "{count} odgrupowano", "unstacked_assets_count": "{count, plural, one {Rozłożony # zasób} few {Rozłożone # zasoby} other {Rozłożonych # zasobów}}", + "untagged": "Nieoznaczone", "up_next": "Do następnego", "updated_at": "Zaktualizowany", "updated_password": "Pomyślnie zaktualizowano hasło", @@ -1912,6 +1945,7 @@ "user_usage_stats_description": "Wyświetl statystyki użytkowania konta", "username": "Nazwa użytkownika", "users": "Użytkownicy", + "users_added_to_album_count": "Dodano {count, plural, one {# użytkownika} other {# użytkowników}} do albumu", "utilities": "Narzędzia", "validate": "Walidacja", "validate_endpoint_error": "Proszę wprowadzić prawidłowy adres URL", diff --git a/i18n/pt.json b/i18n/pt.json index bb88cafbfa..1878c9b9cc 100644 --- a/i18n/pt.json +++ b/i18n/pt.json @@ -166,6 +166,20 @@ "metadata_settings_description": "Gerir definições de metadados", "migration_job": "Migração", "migration_job_description": "Migra miniaturas de ficheiros e rostos para a estrutura de pastas mais recente", + "nightly_tasks_cluster_faces_setting_description": "Executar reconhecimento facial em faces detetadas recentemente", + "nightly_tasks_cluster_new_faces_setting": "Agrupar novas faces", + "nightly_tasks_database_cleanup_setting": "Tarefas de limpeza da base de dados", + "nightly_tasks_database_cleanup_setting_description": "Limpar dados antigos e expirados da base de dados", + "nightly_tasks_generate_memories_setting": "Gerar memórias", + "nightly_tasks_generate_memories_setting_description": "Criar novas memórias a partir de ficheiros", + "nightly_tasks_missing_thumbnails_setting": "Gerar miniaturas em falta", + "nightly_tasks_missing_thumbnails_setting_description": "Colocar em fila ficheiros sem miniaturas para a geração das mesmas", + "nightly_tasks_settings": "Definições de Tarefas Diárias", + "nightly_tasks_settings_description": "Gerir tarefas diárias", + "nightly_tasks_start_time_setting": "Hora de início", + "nightly_tasks_start_time_setting_description": "A hora em qual o servidor começa a executar as tarefas diárias", + "nightly_tasks_sync_quota_usage_setting": "Utilização da quota de sincronização", + "nightly_tasks_sync_quota_usage_setting_description": "Atualizar quotas de armazenamento de utilizadores, com base na utilização atual", "no_paths_added": "Nenhum caminho adicionado", "no_pattern_added": "Nenhum padrão adicionado", "note_apply_storage_label_previous_assets": "Observação: Para aplicar o Rótulo de Armazenamento a ficheiros carregados anteriormente, execute o", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "URI de redirecionamento móvel", "oauth_mobile_redirect_uri_override": "Substituição de URI de redirecionamento móvel", "oauth_mobile_redirect_uri_override_description": "Ative quando o provedor do OAuth não permite um URI móvel, como ''{callback}''", + "oauth_role_claim": "Reivindicação de Funções", + "oauth_role_claim_description": "Automaticamente concede acesso de administrador, com base na presença desta reivindicação. A reivindicação tanto pode ter \"user\" como \"admin\".", "oauth_settings": "OAuth", "oauth_settings_description": "Gerir definições de inicio de sessão do OAuth", "oauth_settings_more_details": "Para mais informações sobre esta funcionalidade, veja a documentação.", @@ -357,10 +373,12 @@ "admin_password": "Palavra-passe do administrador", "administration": "Administração", "advanced": "Avançado", + "advanced_settings_beta_timeline_subtitle": "Experimente as novas funcionalidades da aplicação", + "advanced_settings_beta_timeline_title": "Linha temporal da versão Beta", "advanced_settings_enable_alternate_media_filter_subtitle": "Utilize esta definição para filtrar ficheiros durante a sincronização baseada em critérios alternativos. Utilize apenas se a aplicação estiver com problemas a detetar todos os álbuns.", "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTAL] Utilizar um filtro alternativo de sincronização de álbuns em dispositivos", "advanced_settings_log_level_title": "Nível de registo: {level}", - "advanced_settings_prefer_remote_subtitle": "Alguns dispositivos são extremamente lentos para carregar miniaturas da memória. Ative esta opção para preferir imagens do servidor.", + "advanced_settings_prefer_remote_subtitle": "Alguns dispositivos são extremamente lentos a carregar miniaturas da memória interna. Ative esta opção para preferir imagens do servidor.", "advanced_settings_prefer_remote_title": "Preferir imagens do servidor", "advanced_settings_proxy_headers_subtitle": "Defina os cabeçalhos do proxy que o Immich deve enviar em todas comunicações com a rede", "advanced_settings_proxy_headers_title": "Cabeçalhos do Proxy", @@ -427,6 +445,7 @@ "app_settings": "Definições da Aplicação", "appears_in": "Aparece em", "archive": "Arquivo", + "archive_action_prompt": "{count} adicionados ao Arquivo", "archive_or_unarchive_photo": "Arquivar ou desarquivar foto", "archive_page_no_archived_assets": "Nenhum arquivo encontrado", "archive_page_title": "Arquivo ({count})", @@ -464,7 +483,6 @@ "assets": "Ficheiros", "assets_added_count": "{count, plural, one {# ficheiro adicionado} other {# ficheiros adicionados}}", "assets_added_to_album_count": "{count, plural, one {# ficheiro adicionado} other {# ficheiros adicionados}} ao álbum", - "assets_added_to_name_count": "{count, plural, one {# ficheiro adicionado} other {# ficheiros adicionados}} a {hasName, select, true {{name}} other {novo álbum}}", "assets_cannot_be_added_to_album_count": "Não foi possível adicionar {count, plural, one {ficheiro} other {ficheiros}} ao álbum", "assets_count": "{count, plural, one {# ficheiro} other {# ficheiros}}", "assets_deleted_permanently": "{count} ficheiro(s) eliminado(s) permanentemente", @@ -703,7 +721,7 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Escuro", - "darkTheme": "Alternar tema escuro", + "dark_theme": "Alternar tema escuro", "date_after": "Data após", "date_and_time": "Data e Hora", "date_before": "Data antes", @@ -719,6 +737,7 @@ "default_locale": "Localização Padrão", "default_locale_description": "Formatar datas e números baseados na linguagem do seu navegador", "delete": "Eliminar", + "delete_action_prompt": "{count} eliminados permanentemente", "delete_album": "Eliminar álbum", "delete_api_key_prompt": "Tem a certeza de que deseja eliminar esta chave de API?", "delete_dialog_alert": "Esses arquivos serão permanentemente apagados do Immich e de seu dispositivo", @@ -732,6 +751,7 @@ "delete_key": "Eliminar chave", "delete_library": "Eliminar Biblioteca", "delete_link": "Eliminar link", + "delete_local_action_prompt": "{count} eliminados localmente", "delete_local_dialog_ok_backed_up_only": "Excluir apenas arquivos com backup", "delete_local_dialog_ok_force": "Excluir mesmo assim", "delete_others": "Excluir outros", @@ -762,6 +782,7 @@ "documentation": "Documentação", "done": "Feito", "download": "Transferir", + "download_action_prompt": "A descarregar {count} ficheiros", "download_canceled": "Cancelado", "download_complete": "Sucesso", "download_enqueue": "Na fila", @@ -799,6 +820,7 @@ "edit_key": "Editar chave", "edit_link": "Editar link", "edit_location": "Editar Localização", + "edit_location_action_prompt": "{count} locais alterados", "edit_location_dialog_title": "Localização", "edit_name": "Editar nome", "edit_people": "Editar pessoas", @@ -984,6 +1006,7 @@ "failed_to_load_assets": "Ocorreu um erro ao carregar ficheiros", "failed_to_load_folder": "Ocorreu um erro ao carregar a pasta", "favorite": "Favorito", + "favorite_action_prompt": "{count} adicionados aos favoritos", "favorite_or_unfavorite_photo": "Marcar ou desmarcar a foto como favorita", "favorites": "Favoritos", "favorites_page_no_favorites": "Nenhum favorito encontrado", @@ -1127,6 +1150,7 @@ "library_page_sort_created": "Data de criação", "library_page_sort_last_modified": "Última modificação", "library_page_sort_title": "Título do álbum", + "licenses": "Licenças", "light": "Claro", "like_deleted": "Gosto removido", "link_motion_video": "Relacionar video animado", @@ -1246,6 +1270,7 @@ "more": "Mais", "move": "Mover", "move_off_locked_folder": "Mover para fora da pasta trancada", + "move_to_lock_folder_action_prompt": "{count} adicionados à pasta trancada", "move_to_locked_folder": "Mover para a pasta trancada", "move_to_locked_folder_confirmation": "Estas fotos e vídeos serão removidas de todos os álbuns, e só serão visíveis na pasta trancada", "moved_to_archive": "{count, plural, one {Foi movido # ficheiro} other {Foram movidos # ficheiros}} para o arquivo", @@ -1495,7 +1520,9 @@ "remove_custom_date_range": "Remover intervalo de datas personalizado", "remove_deleted_assets": "Remover ficheiros indisponíveis", "remove_from_album": "Remover do álbum", + "remove_from_album_action_prompt": "{count} removido(s) do álbum", "remove_from_favorites": "Remover dos favoritos", + "remove_from_lock_folder_action_prompt": "{count} removidos da pasta trancada", "remove_from_locked_folder": "Remover da pasta trancada", "remove_from_locked_folder_confirmation": "Tem a certeza de que quer mover estas fotos e vídeos para fora da pasta trancada? Passarão a ser visíveis na biblioteca.", "remove_from_shared_link": "Remover do link partilhado", @@ -1667,6 +1694,7 @@ "settings_saved": "Definições guardadas", "setup_pin_code": "Configurar um código PIN", "share": "Partilhar", + "share_action_prompt": "Partilhados {count} ficheiros", "share_add_photos": "Adicionar fotos", "share_assets_selected": "{count} selecionados", "share_dialog_preparing": "Preparando...", @@ -1768,6 +1796,7 @@ "sort_title": "Título", "source": "Fonte", "stack": "Empilhar", + "stack_action_prompt": "{count} empilhados", "stack_duplicates": "Empilhar itens duplicados", "stack_select_one_photo": "Selecione uma foto principal para a pilha", "stack_selected_photos": "Empilhar fotos selecionadas", @@ -1838,6 +1867,7 @@ "total": "Total", "total_usage": "Total utilizado", "trash": "Reciclagem", + "trash_action_prompt": "{count} movidos para a reciclagem", "trash_all": "Mover todos para a reciclagem", "trash_count": "Reciclar {count, number}", "trash_delete_asset": "Eliminar ficheiro", @@ -1855,9 +1885,11 @@ "unable_to_change_pin_code": "Não foi possível alterar o código PIN", "unable_to_setup_pin_code": "Não foi possível configurar o código PIN", "unarchive": "Desarquivar", + "unarchive_action_prompt": "{count} removidos do Arquivo", "unarchived_count": "{count, plural, other {Não arquivado #}}", "undo": "Anular", "unfavorite": "Remover favorito", + "unfavorite_action_prompt": "{count} removidos dos Favoritos", "unhide_person": "Exibir pessoa", "unknown": "Desconhecido", "unknown_country": "País desconhecido", @@ -1875,7 +1907,9 @@ "unselect_all_duplicates": "Remover seleção de todos os itens duplicados", "unselect_all_in": "Remover seleção de {group}", "unstack": "Desempilhar", + "unstack_action_prompt": "{count} desempilhados", "unstacked_assets_count": "Desempilhados {count, plural, one {# ficheiro} other {# ficheiros}}", + "untagged": "Marcador removido", "up_next": "A seguir", "updated_at": "Atualizado a", "updated_password": "Palavra-passe atualizada", @@ -1912,6 +1946,7 @@ "user_usage_stats_description": "Ver estatísticas de utilização de conta", "username": "Nome de utilizador", "users": "Utilizadores", + "users_added_to_album_count": "{count, plural, one {Foi adicionado # utilizador} other {Foram adicionados # utilizadores}} ao álbum", "utilities": "Ferramentas", "validate": "Validar", "validate_endpoint_error": "Digite uma URL válida", @@ -1951,7 +1986,7 @@ "wifi_name": "Nome da rede Wi-Fi", "wrong_pin_code": "Código PIN errado", "year": "Ano", - "years_ago": "Há {years, plural, one {# ano} other {# anos}} atrás", + "years_ago": "Há {years, plural, one {# ano} other {# anos}}", "yes": "Sim", "you_dont_have_any_shared_links": "Não tem links partilhados", "your_wifi_name": "Nome da sua rede Wi-Fi", diff --git a/i18n/pt_BR.json b/i18n/pt_BR.json index 705180cafd..1af3bda2ac 100644 --- a/i18n/pt_BR.json +++ b/i18n/pt_BR.json @@ -105,7 +105,7 @@ "library_scanning_enable_description": "Habilitar verificação periódica da biblioteca", "library_settings": "Biblioteca Externa", "library_settings_description": "Gerenciar configurações de biblioteca externa", - "library_tasks_description": "Escanear bibliotecas externas para ativos novos ou modificados", + "library_tasks_description": "Verificar se há arquivos novos ou modificados nas bibliotecas externas", "library_watching_enable_description": "Observe bibliotecas externas para alterações de arquivos", "library_watching_settings": "Observação de biblioteca (EXPERIMENTAL)", "library_watching_settings_description": "Observe automaticamente os arquivos alterados", @@ -168,7 +168,7 @@ "migration_job_description": "Migrar miniaturas de arquivos e rostos para a estrutura de pastas mais recente", "no_paths_added": "Nenhum caminho adicionado", "no_pattern_added": "Nenhum padrão adicionado", - "note_apply_storage_label_previous_assets": "Observação: Para aplicar o rótulo de armazenamento a arquivos carregados anteriormente, execute o", + "note_apply_storage_label_previous_assets": "Observação: Para aplicar o rótulo de armazenamento a arquivos enviados anteriormente, execute o", "note_cannot_be_changed_later": "NOTA: Isto não pode ser alterado posteriormente!", "notification_email_from_address": "E-mail de origem", "notification_email_from_address_description": "Endereço de e-mail do remetente, por exemplo: \"Immich Photo Server \". Tenha certeza de ter permissão para enviar e-mails a partir do endereço selecionado.", @@ -196,15 +196,17 @@ "oauth_mobile_redirect_uri": "URI de redirecionamento móvel", "oauth_mobile_redirect_uri_override": "Substituição de URI de redirecionamento móvel", "oauth_mobile_redirect_uri_override_description": "Ative quando o provedor do OAuth não suportar uma URI de aplicativo, por exemplo ''{callback}''", + "oauth_role_claim": "Declaração de função", + "oauth_role_claim_description": "Dá permissões de administrador baseado no valor desta declaração. A declaração pode conter os valores 'user' ou 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Gerenciar configurações de login do OAuth", "oauth_settings_more_details": "Para mais detalhes sobre este recurso, consulte a documentação.", - "oauth_storage_label_claim": "Reivindicação de rótulo de armazenamento", + "oauth_storage_label_claim": "Declaração do rótulo de armazenamento", "oauth_storage_label_claim_description": "Defina automaticamente o rótulo de armazenamento do usuário para o valor desta declaração.", - "oauth_storage_quota_claim": "Cota de armazenamento", + "oauth_storage_quota_claim": "Declaração de cota de armazenamento", "oauth_storage_quota_claim_description": "Defina automaticamente a cota de armazenamento do usuário para o valor desta declaração.", "oauth_storage_quota_default": "Cota de armazenamento padrão (GiB)", - "oauth_storage_quota_default_description": "Cota em GiB a ser usada quando nenhuma outra reivindicação for fornecida.", + "oauth_storage_quota_default_description": "Cota em GiB que será usada caso esta declaração não seja fornecida.", "oauth_timeout": "Tempo Limite de Requisição", "oauth_timeout_description": "Tempo limite para requisições, em milissegundos", "password_enable_description": "Login com e-mail e senha", @@ -234,20 +236,20 @@ "sidecar_job_description": "Descubra ou sincronize metadados secundários do sistema de arquivos", "slideshow_duration_description": "Tempo em segundos para exibir cada imagem", "smart_search_job_description": "Execute aprendizado de máquina em arquivos para oferecer suporte à pesquisa inteligente", - "storage_template_date_time_description": "A data e hora da criação do ativo é usado para a informações de data e hora", + "storage_template_date_time_description": "A data e hora da criação do arquivo é usado para a informações de data e hora", "storage_template_date_time_sample": "Exemplo {date}", "storage_template_enable_description": "Habilitar mecanismo de modelo de armazenamento", "storage_template_hash_verification_enabled": "Verificação de hash ativada", "storage_template_hash_verification_enabled_description": "Ativa a verificação de hash, não desative a menos que você tenha certeza das implicações", "storage_template_migration": "Migração de modelo de armazenamento", - "storage_template_migration_description": "Aplique o {template} atual aos arquivos carregados anteriormente", - "storage_template_migration_info": "O modelo altera todas extensões para minúsculo. As mudanças no modelo serão aplicadas apenas em novos arquivos. Para aplicar retroativamente o modelo aos arquivos carregados anteriormente, execute o {job}.", + "storage_template_migration_description": "Aplicar o {template} atual aos arquivos enviados anteriormente", + "storage_template_migration_info": "O modelo altera todas extensões para minúsculo. As mudanças no modelo serão aplicadas apenas em novos arquivos; para aplicar o modelo aos arquivos enviados anteriormente, execute o {job}.", "storage_template_migration_job": "Tarefa de Migração de Modelo de Armazenamento", "storage_template_more_details": "Para mais detalhes sobre este recurso, consulte o Modelo de Armazenamento e suas implicações", "storage_template_onboarding_description_v2": "Ao ser ativado, este recurso irá organizar automaticamente os arquivos com base em um modelo definido pelo usuário. Para mais informações, consulte a documentação.", "storage_template_path_length": "Limite aproximado de comprimento do caminho: {length, number}/{limit, number}", "storage_template_settings": "Modelo de Armazenamento", - "storage_template_settings_description": "Gerencie a estrutura de pasta e o nome do arquivo carregado", + "storage_template_settings_description": "Gerencie a estrutura de pasta e o nome do arquivo enviado", "storage_template_user_label": "{label} é o Rótulo de Armazenamento do usuário", "system_settings": "Configurações do Sistema", "tag_cleanup_job": "Limpeza de marcadores", @@ -336,7 +338,7 @@ "user_delete_delay_settings": "Remover atraso", "user_delete_delay_settings_description": "Número de dias após a remoção para excluir permanentemente a conta e os arquivos de um usuário. A tarefa de exclusão de usuário é executada à meia-noite para verificar usuários que estão prontos para exclusão. As alterações nesta configuração serão avaliadas na próxima execução.", "user_delete_immediately": "A conta e os arquivos de {user} serão programados para exclusão permanente imediata.", - "user_delete_immediately_checkbox": "Adicionar o usuário e seus ativos na fila para serem deletados imediatamente", + "user_delete_immediately_checkbox": "Adicionar o usuário e seus arquivos na fila para serem deletados imediatamente", "user_details": "Detalhes do Usuário", "user_management": "Gerenciamento de usuários", "user_password_has_been_reset": "A senha do usuário foi redefinida:", @@ -426,7 +428,8 @@ "app_bar_signout_dialog_title": "Sair", "app_settings": "Configurações do Aplicativo", "appears_in": "Aparece em", - "archive": "Arquivados", + "archive": "Arquivar", + "archive_action_prompt": "{count} mídias arquivadas", "archive_or_unarchive_photo": "Arquivar ou desarquivar foto", "archive_page_no_archived_assets": "Nenhum arquivo encontrado", "archive_page_title": "Arquivados ({count})", @@ -440,7 +443,7 @@ "asset_action_share_err_offline": "Não foi possível obter os arquivos indisponíveis, ignorando", "asset_added_to_album": "Adicionado ao álbum", "asset_adding_to_album": "Adicionando ao álbum…", - "asset_description_updated": "A descrição do ativo foi atualizada", + "asset_description_updated": "A descrição do arquivo foi atualizada", "asset_filename_is_offline": "O arquivo {filename} não está disponível", "asset_has_unassigned_faces": "O arquivo tem rostos sem nomes", "asset_hashing": "Processando…", @@ -457,14 +460,13 @@ "asset_restored_successfully": "Arquivo restaurado", "asset_skipped": "Ignorado", "asset_skipped_in_trash": "Na lixeira", - "asset_uploaded": "Carregado", - "asset_uploading": "Carregando…", + "asset_uploaded": "Enviado", + "asset_uploading": "Enviando…", "asset_viewer_settings_subtitle": "Gerenciar as configurações do visualizador da galeria", "asset_viewer_settings_title": "Visualizador de Mídia", "assets": "Arquivos", "assets_added_count": "{count, plural, one {# arquivo adicionado} other {# arquivos adicionados}}", "assets_added_to_album_count": "{count, plural, one {# arquivo adicionado} other {# arquivos adicionados}} ao álbum", - "assets_added_to_name_count": "{count, plural, one {# arquivo adicionado} other {# arquivos adicionados}} {hasName, select, true {ao álbum {name}} other {em um novo álbum}}", "assets_cannot_be_added_to_album_count": "Não foi possível adicionar {count, plural, one {o arquivo} other {os arquivos}} ao álbum", "assets_count": "{count, plural, one {# arquivo} other {# arquivos}}", "assets_deleted_permanently": "{count} arquivo(s) deletado(s) permanentemente", @@ -489,7 +491,7 @@ "back": "Voltar", "back_close_deselect": "Voltar, fechar ou desmarcar", "background_location_permission": "Permissão de localização em segundo plano", - "background_location_permission_content": "Para que seja possível trocar a URL quando estiver executando em segundo plano, o Immich deve *sempre* ter a permissão de localização precisa para que o aplicativo consiga ler o nome da rede Wi-Fi", + "background_location_permission_content": "Para que seja possível trocar o endereço quando estiver executando em segundo plano, o Immich deve *sempre* ter a permissão de localização precisa para que o aplicativo consiga ler o nome da rede Wi-Fi", "backup_album_selection_page_albums_device": "Álbuns no dispositivo ({count})", "backup_album_selection_page_albums_tap": "Toque para incluir, toque duas vezes para excluir", "backup_album_selection_page_assets_scatter": "Os recursos podem se espalhar por vários álbuns. Assim, os álbuns podem ser incluídos ou excluídos durante o processo de backup.", @@ -502,9 +504,9 @@ "backup_background_service_current_upload_notification": "Enviando {filename}", "backup_background_service_default_notification": "Verificando se há novos arquivos…", "backup_background_service_error_title": "Erro no backup", - "backup_background_service_in_progress_notification": "Fazendo backup de seus ativos…", + "backup_background_service_in_progress_notification": "Fazendo backup de seus arquivos…", "backup_background_service_upload_failure_notification": "Falha ao enviar {filename}", - "backup_controller_page_albums": "Álbuns de backup", + "backup_controller_page_albums": "Backup de álbuns", "backup_controller_page_background_app_refresh_disabled_content": "Para utilizar o backup em segundo plano, ative a atualização da aplicação em segundo plano em Configurações > Geral > Atualização em 2º plano.", "backup_controller_page_background_app_refresh_disabled_title": "Atualização em 2º plano desativada", "backup_controller_page_background_app_refresh_enable_button_text": "Ir para as configurações", @@ -512,40 +514,40 @@ "backup_controller_page_background_battery_info_message": "Para uma melhor experiência de backup em segundo plano, desative todas as otimizações de bateria que restrinjam a atividade em segundo plano do Immich.\n\nComo isso é específico por dispositivo, consulte as informações de como fazer isso com o fabricante do seu dispositivo.", "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "Otimizações de bateria", - "backup_controller_page_background_charging": "Apenas durante o carregamento", + "backup_controller_page_background_charging": "Apenas enquanto carrega a bateria", "backup_controller_page_background_configure_error": "Falha ao configurar o serviço em segundo plano", "backup_controller_page_background_delay": "Adiar backup de novos arquivos: {duration}", - "backup_controller_page_background_description": "Ative o serviço em segundo plano para fazer backup automático de novos ativos sem precisar abrir o aplicativo", + "backup_controller_page_background_description": "Ative o serviço em segundo plano para fazer backup automático de novos arquivos sem precisar abrir o aplicativo", "backup_controller_page_background_is_off": "O backup automático em segundo plano está desativado", "backup_controller_page_background_is_on": "O backup automático em segundo plano está ativado", "backup_controller_page_background_turn_off": "Desativar o serviço em segundo plano", "backup_controller_page_background_turn_on": "Ativar o serviço em segundo plano", "backup_controller_page_background_wifi": "Apenas no Wi-Fi", "backup_controller_page_backup": "Backup", - "backup_controller_page_backup_selected": "Selecionado: ", - "backup_controller_page_backup_sub": "Backup de fotos e vídeos", - "backup_controller_page_created": "Criado em: {date}", - "backup_controller_page_desc_backup": "Ative o backup para carregar automaticamente novos ativos no servidor.", - "backup_controller_page_excluded": "Excluído: ", + "backup_controller_page_backup_selected": "Selecionados: ", + "backup_controller_page_backup_sub": "Total de mídias com backup", + "backup_controller_page_created": "Data: {date}", + "backup_controller_page_desc_backup": "Ative para fazer backup automático dos novos arquivos ao abrir este aplicativo.", + "backup_controller_page_excluded": "Ignorados: ", "backup_controller_page_failed": "Falhou ({count})", - "backup_controller_page_filename": "Nome do arquivo: {filename} [{size}]", + "backup_controller_page_filename": "Arquivo: {filename} [{size}]", "backup_controller_page_id": "ID: {id}", - "backup_controller_page_info": "Informações de backup", - "backup_controller_page_none_selected": "Nenhum selecionado", + "backup_controller_page_info": "Informações do backup", + "backup_controller_page_none_selected": "Nenhum álbum selecionado", "backup_controller_page_remainder": "Restante", - "backup_controller_page_remainder_sub": "Fotos e vídeos restantes para fazer backup da seleção", + "backup_controller_page_remainder_sub": "Mídias nos álbuns selecionados que ainda não tem backup", "backup_controller_page_server_storage": "Armazenamento do servidor", - "backup_controller_page_start_backup": "Iniciar backup", - "backup_controller_page_status_off": "O backup está desativado", - "backup_controller_page_status_on": "O backup está ativado", + "backup_controller_page_start_backup": "Iniciar backup manual", + "backup_controller_page_status_off": "O backup automático está desativado", + "backup_controller_page_status_on": "O backup automático está ativado", "backup_controller_page_storage_format": "{used} de {total} usados", - "backup_controller_page_to_backup": "Álbuns para backup", - "backup_controller_page_total_sub": "Todas as fotos e vídeos únicos dos álbuns selecionados", - "backup_controller_page_turn_off": "Desativar o backup", - "backup_controller_page_turn_on": "Ativar Backup", - "backup_controller_page_uploading_file_info": "Carregando informações do arquivo", + "backup_controller_page_to_backup": "Escolha os álbuns para fazer backup", + "backup_controller_page_total_sub": "Total de mídias nos álbuns selecionados", + "backup_controller_page_turn_off": "Desativar backup automático", + "backup_controller_page_turn_on": "Ativar backup automático", + "backup_controller_page_uploading_file_info": "Informações do arquivo", "backup_err_only_album": "Não é possível remover o único álbum", - "backup_info_card_assets": "ativos", + "backup_info_card_assets": "arquivos", "backup_manual_cancelled": "Cancelado", "backup_manual_in_progress": "Envio já está em progresso. Tente novamente mais tarde", "backup_manual_success": "Sucesso", @@ -570,7 +572,7 @@ "cache_settings_clear_cache_button": "Limpar o cache", "cache_settings_clear_cache_button_title": "Limpa o cache do aplicativo. Isso afetará significativamente o desempenho do aplicativo até que o cache seja reconstruído.", "cache_settings_duplicated_assets_clear_button": "LIMPAR", - "cache_settings_duplicated_assets_subtitle": "Fotos e vídeos que são bloqueados pelo app", + "cache_settings_duplicated_assets_subtitle": "Mídias ignoradas pelo app", "cache_settings_duplicated_assets_title": "Arquivos duplicados ({count})", "cache_settings_statistics_album": "Miniaturas da biblioteca", "cache_settings_statistics_full": "Imagens completas", @@ -658,9 +660,9 @@ "control_bottom_app_bar_create_new_album": "Criar novo álbum", "control_bottom_app_bar_delete_from_immich": "Excluir do Immich", "control_bottom_app_bar_delete_from_local": "Excluir do dispositivo", - "control_bottom_app_bar_edit_location": "Editar Localização", + "control_bottom_app_bar_edit_location": "Alterar Local", "control_bottom_app_bar_edit_time": "Editar data e hora", - "control_bottom_app_bar_share_link": "Compartilhar Link", + "control_bottom_app_bar_share_link": "Link", "control_bottom_app_bar_share_to": "Compartilhar", "control_bottom_app_bar_trash_from_immich": "Mover para a Lixeira", "copied_image_to_clipboard": "Imagem copiada para a área de transferência.", @@ -680,7 +682,7 @@ "create_album_page_untitled": "Sem título", "create_library": "Criar biblioteca", "create_link": "Criar link", - "create_link_to_share": "Criar link para partilhar", + "create_link_to_share": "Criar link e compartilhar", "create_link_to_share_description": "Permitir que qualquer pessoa com o link veja a(s) foto(s) selecionada(s)", "create_new": "CRIAR NOVO", "create_new_person": "Criar nova pessoa", @@ -703,7 +705,7 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Escuro", - "darkTheme": "trocar para tema escuro", + "dark_theme": "Usar tema escuro", "date_after": "Data após", "date_and_time": "Data e Hora", "date_before": "Data antes", @@ -719,6 +721,7 @@ "default_locale": "Localização Padrão", "default_locale_description": "Formatar datas e números baseados na linguagem do seu navegador", "delete": "Excluir", + "delete_action_prompt": "{count} deletados permanentemente", "delete_album": "Excluir álbum", "delete_api_key_prompt": "Tem certeza de que deseja excluir esta chave de API?", "delete_dialog_alert": "Esses itens serão excluídos permanentemente do Immich e do seu dispositivo", @@ -781,7 +784,7 @@ "downloading": "Baixando", "downloading_asset_filename": "Baixando arquivo {filename}", "downloading_media": "Baixando mídia", - "drop_files_to_upload": "Solte arquivos em qualquer lugar para carregar", + "drop_files_to_upload": "Solte os arquivos em qualquer lugar para enviar", "duplicates": "Duplicados", "duplicates_description": "Marque cada grupo indicando quais arquivos, se algum, são duplicados", "duration": "Duração", @@ -799,6 +802,7 @@ "edit_key": "Editar chave", "edit_link": "Editar link", "edit_location": "Editar Localização", + "edit_location_action_prompt": "{count} locais alterados", "edit_location_dialog_title": "Localização", "edit_name": "Editar nome", "edit_people": "Editar pessoas", @@ -814,7 +818,7 @@ "email": "E-mail", "email_notifications": "Notificações por e-mail", "empty_folder": "A pasta está vazia", - "empty_trash": "Esvaziar lixo", + "empty_trash": "Esvaziar lixeira", "empty_trash_confirmation": "Tem certeza de que deseja esvaziar a lixeira? Isso removerá permanentemente do Immich todos os arquivos que estão na lixeira.\nVocê não pode desfazer esta ação!", "enable": "Habilitar", "enable_biometric_auth_description": "Insira seu código PIN para ativar a autenticação por biometria", @@ -855,8 +859,8 @@ "failed_to_edit_shared_link": "Falha ao editar o link compartilhado", "failed_to_get_people": "Falha na obtenção de pessoas", "failed_to_keep_this_delete_others": "Falha ao manter este arquivo e excluir os outros", - "failed_to_load_asset": "Não foi possível carregar o ativo", - "failed_to_load_assets": "Não foi possível carregar os ativos", + "failed_to_load_asset": "Não foi possível carregar o arquivo", + "failed_to_load_assets": "Não foi possível carregar os arquivos", "failed_to_load_notifications": "Falha ao carregar notificações", "failed_to_load_people": "Falha ao carregar pessoas", "failed_to_remove_product_key": "Falha ao remover a chave do produto", @@ -949,7 +953,7 @@ "unable_to_update_settings": "Não foi possível atualizar as configurações", "unable_to_update_timeline_display_status": "Não foi possível atualizar o modo de visualização da linha do tempo", "unable_to_update_user": "Não foi possível atualizar o usuário", - "unable_to_upload_file": "Não foi possível carregar o arquivo" + "unable_to_upload_file": "Não foi possível enviar o arquivo" }, "exif": "Exif", "exif_bottom_sheet_description": "Adicionar descrição...", @@ -977,13 +981,14 @@ "external": "Externo", "external_libraries": "Bibliotecas externas", "external_network": "Rede externa", - "external_network_sheet_info": "Quando não estiver na rede Wi-Fi especificada, o aplicativo irá se conectar usando a primeira URL abaixo que obtiver sucesso, começando do topo da lista para baixo", + "external_network_sheet_info": "Quando não estiver na rede Wi-Fi especificada, o aplicativo irá se conectar usando o primeiro endereço abaixo que obtiver sucesso, começando do topo da lista para baixo", "face_unassigned": "Sem nome", "failed": "Falhou", "failed_to_authenticate": "Não foi possível autenticar", "failed_to_load_assets": "Falha ao carregar arquivos", "failed_to_load_folder": "Falha ao carregar a pasta", "favorite": "Favorito", + "favorite_action_prompt": "{count} marcados como favorito", "favorite_or_unfavorite_photo": "Marque ou desmarque a foto como favorita", "favorites": "Favoritos", "favorites_page_no_favorites": "Nenhuma mídia favorita encontrada", @@ -1138,7 +1143,7 @@ "loading_search_results_failed": "Falha ao carregar os resultados da pesquisa", "local_asset_cast_failed": "Não é possível transmitir um arquivo que não foi enviado ao servidor", "local_network": "Rede local", - "local_network_sheet_info": "O aplicativo irá se conectar ao servidor através desta URL quando estiver na rede Wi-Fi especificada", + "local_network_sheet_info": "O aplicativo irá se conectar ao servidor através deste endereço quando estiver na rede Wi-Fi especificada", "location_permission": "Permissão de localização", "location_permission_content": "Para utilizar a função de troca automática de URL é necessário a permissão de localização precisa, para que seja possível ler o nome da rede Wi-Fi", "location_picker_choose_on_map": "Escolha no mapa", @@ -1147,7 +1152,7 @@ "location_picker_longitude_error": "Digite uma longitude válida", "location_picker_longitude_hint": "Digite a longitude", "lock": "Trancar", - "locked_folder": "Pasta Trancada", + "locked_folder": "Pasta com senha", "log_out": "Sair", "log_out_all_devices": "Sair de todos dispositivos", "logged_in_as": "Usuário atual: {user}", @@ -1246,6 +1251,7 @@ "more": "Mais", "move": "Mover", "move_off_locked_folder": "Mover para fora da pasta com senha", + "move_to_lock_folder_action_prompt": "{count} adicionados à pasta com senha", "move_to_locked_folder": "Mover para a pasta com senha", "move_to_locked_folder_confirmation": "Estas fotos e vídeos serão removidos de todos os álbuns e somente poderão ser visualizados de dentro da pasta com senha", "moved_to_archive": "{count, plural, one {# mídia foi arquivada} other {# mídias foram arquivadas}}", @@ -1276,12 +1282,12 @@ "no_albums_with_name_yet": "Parece que você ainda não tem nenhum álbum com esse nome.", "no_albums_yet": "Parece que você ainda não tem nenhum álbum.", "no_archived_assets_message": "Arquive fotos e vídeos para os ocultar da sua visualização de fotos", - "no_assets_message": "CLIQUE PARA CARREGAR SUA PRIMEIRA FOTO", + "no_assets_message": "CLIQUE PARA ENVIAR SUA PRIMEIRA FOTO", "no_assets_to_show": "Não há arquivos para exibir", "no_cast_devices_found": "Nenhum dispositivo encontrado", "no_duplicates_found": "Nenhuma duplicidade foi encontrada.", "no_exif_info_available": "Sem informações exif disponíveis", - "no_explore_results_message": "Carregue mais fotos para explorar sua coleção.", + "no_explore_results_message": "Envie mais fotos para explorar sua coleção.", "no_favorites_message": "Adicione aos favoritos para encontrar suas melhores fotos e vídeos rapidamente", "no_libraries_message": "Crie uma biblioteca externa para ver suas fotos e vídeos", "no_locked_photos_message": "Fotos e vídeos na pasta com senha são ocultos e não serão exibidos enquanto explora ou pesquisa na biblioteca.", @@ -1294,7 +1300,7 @@ "no_shared_albums_message": "Crie um álbum para compartilhar fotos e vídeos com pessoas em sua rede", "not_in_any_album": "Fora de álbum", "not_selected": "Não selecionado", - "note_apply_storage_label_to_previously_uploaded assets": "Nota: Para aplicar o rótulo de armazenamento a arquivos carregados anteriormente, execute o", + "note_apply_storage_label_to_previously_uploaded assets": "Nota: Para aplicar o rótulo de armazenamento a arquivos enviados anteriormente, execute o", "notes": "Notas", "nothing_here_yet": "Ainda não existe nada aqui", "notification_permission_dialog_content": "Para ativar as notificações, vá em Configurações e selecione permitir.", @@ -1369,7 +1375,7 @@ "permanent_deletion_warning_setting_description": "Exibe um aviso ao deletar arquivos de forma permanente", "permanently_delete": "Deletar permanentemente", "permanently_delete_assets_count": "Excluir permanentemente {count, plural, one {asset} other {assets}}", - "permanently_delete_assets_prompt": "Você tem certeza de que deseja excluir permanentemente {count, plural, one {este ativo?} other {estes # ativos?}} Esta ação também removerá {count, plural, one {o ativo} other {os ativos}} de um ou mais álbuns.", + "permanently_delete_assets_prompt": "Você tem certeza de que deseja excluir permanentemente {count, plural, one {este arquivo?} other {estes # arquivos?}} Esta ação também removerá {count, plural, one {o arquivo} other {os arquivos}} de um ou mais álbuns.", "permanently_deleted_asset": "Arquivo deletado permanentemente", "permanently_deleted_assets_count": "{count, plural, one {# arquivo permanentemente excluído} other {# arquivos permanentemente excluídos}}", "permission": "Permissão", @@ -1495,7 +1501,9 @@ "remove_custom_date_range": "Remover intervalo de datas personalizado", "remove_deleted_assets": "Remover arquivos excluídos", "remove_from_album": "Remover do álbum", + "remove_from_album_action_prompt": "{count} removido do álbum", "remove_from_favorites": "Remover dos favoritos", + "remove_from_lock_folder_action_prompt": "{count} removidos da pasta com senha", "remove_from_locked_folder": "Remover da pasta com senha", "remove_from_locked_folder_confirmation": "Tem a certeza de que deseja mover estes arquivos para fora da pasta com senha? Eles ficarão visíveis na biblioteca principal.", "remove_from_shared_link": "Remover do link compartilhado", @@ -1531,7 +1539,7 @@ "restore_user": "Restaurar usuário", "restored_asset": "Arquivo restaurado", "resume": "Continuar", - "retry_upload": "Tentar carregar novamente", + "retry_upload": "Tentar enviar novamente", "review_duplicates": "Revisar duplicidade", "role": "Função", "role_editor": "Editor", @@ -1626,7 +1634,7 @@ "send_welcome_email": "Enviar E-mail de boas vindas", "server_endpoint": "URL do servidor", "server_info_box_app_version": "Versão do aplicativo", - "server_info_box_server_url": "URL do servidor", + "server_info_box_server_url": "Endereço", "server_offline": "Servidor Indisponível", "server_online": "Servidor Disponível", "server_privacy": "Privacidade do servidor", @@ -1713,7 +1721,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Gerenciar links compartilhados", "shared_link_options": "Opções de link compartilhado", - "shared_links": "Links compartilhados", + "shared_links": "Links", "shared_links_description": "Compartilhar fotos e videos com um link", "shared_photos_and_videos_count": "{assetCount, plural, one {# Foto & vídeo compartilhado.} other {# Fotos & vídeos compartilhados.}}", "shared_with_me": "Compartilhado comigo", @@ -1838,6 +1846,7 @@ "total": "Total", "total_usage": "Utilização total", "trash": "Lixeira", + "trash_action_prompt": "{count} enviados à lixeira", "trash_all": "Mover todos para o lixo", "trash_count": "Lixo {count, number}", "trash_delete_asset": "Jogar na lixeira/Excluir Arquivo", @@ -1855,9 +1864,11 @@ "unable_to_change_pin_code": "Não foi possível alterar o código PIN", "unable_to_setup_pin_code": "Não foi possível criar o código PIN", "unarchive": "Desarquivar", + "unarchive_action_prompt": "{count} desarquivado", "unarchived_count": "{count, plural, one {# Desarquivado} other {# Desarquivados}}", "undo": "Desfazer", "unfavorite": "Remover favorito", + "unfavorite_action_prompt": "{count} removido dos favoritos", "unhide_person": "Exibir pessoa", "unknown": "Desconhecido", "unknown_country": "País desconhecido", @@ -1876,20 +1887,21 @@ "unselect_all_in": "Remover seleção de {group}", "unstack": "Retirar do grupo", "unstacked_assets_count": "{count, plural, one {# arquivo retirado} other {# arquivos retirados}} do grupo", + "untagged": "Marcador removido", "up_next": "A seguir", "updated_at": "Atualizado em", "updated_password": "Senha atualizada", - "upload": "Carregar", + "upload": "Enviar", "upload_concurrency": "Envios simultâneos", "upload_dialog_info": "Deseja fazer o backup dos arquivos selecionados no servidor?", "upload_dialog_title": "Enviar arquivo", - "upload_errors": "Envio concluído com {count, plural, one {# erro} other {# erros}}, atualize a página para ver os novos arquivos carregados.", + "upload_errors": "Envio concluído com {count, plural, one {# erro} other {# erros}}, atualize a página para ver os novos arquivos.", "upload_progress": "{remaining, number} restantes - {processed, number}/{total, number} já processados", "upload_skipped_duplicates": "{count, plural, one {# Arquivo duplicado foi ignorado} other {# Arquivos duplicados foram ignorados}}", "upload_status_duplicates": "Duplicados", "upload_status_errors": "Erros", - "upload_status_uploaded": "Carregado", - "upload_success": "Carregado com sucesso, atualize a página para ver os novos arquivos.", + "upload_status_uploaded": "Enviado", + "upload_success": "Enviado com sucesso, atualize a página para ver os novos arquivos.", "upload_to_immich": "Enviar para o Immich ({count})", "uploading": "Enviando", "url": "URL", diff --git a/i18n/ro.json b/i18n/ro.json index 0b2ef61a36..46197e943f 100644 --- a/i18n/ro.json +++ b/i18n/ro.json @@ -14,6 +14,7 @@ "add_a_location": "Adaugă o locație", "add_a_name": "Adaugă un nume", "add_a_title": "Adaugă un titlu", + "add_endpoint": "Adaugă punct final", "add_exclusion_pattern": "Adăugă un model de excludere", "add_import_path": "Adaugă o cale de import", "add_location": "Adaugă locație", @@ -21,6 +22,7 @@ "add_partner": "Adaugă partener", "add_path": "Adaugă o cale", "add_photos": "Adaugă fotografii", + "add_tag": "Adaugă etichetă", "add_to": "Adaugă la…", "add_to_album": "Adaugă în album", "add_to_album_bottom_sheet_added": "Adăugat în {album}", @@ -32,6 +34,7 @@ "added_to_favorites_count": "Adăugat {count, number} la favorite", "admin": { "add_exclusion_pattern_description": "Adăugați modele de excludere. Globing folosind *, ** și ? este suportat. Pentru a ignora toate fișierele din orice director numit „Raw”, utilizați „**/Raw/**”. Pentru a ignora toate fișierele care se termină în „.tif”, utilizați „**/*.tif”. Pentru a ignora o cale absolută, utilizați „/path/to/ignore/**”.", + "admin_user": "Utilizator admin", "asset_offline_description": "Acest material din biblioteca externă nu se mai găsește pe disc și a fost mutat în coșul de gunoi. Dacă fișierul a fost mutat în bibliotecă, verificați cronologia pentru noul material corespunzător. Pentru a restabili acest material, asigurați-vă că calea fișierului de mai jos poate fi accesată de Immich și scanați biblioteca.", "authentication_settings": "Setări de Autentificare", "authentication_settings_description": "Gestionează parola, OAuth și alte setări de autentificare", @@ -39,8 +42,8 @@ "authentication_settings_reenable": "Pentru a reactiva, folosește Comandă Server.", "background_task_job": "Activități de Fundal", "backup_database": "Salvare Bază de Date", - "backup_database_enable_description": "Activare salvare bază de date", - "backup_keep_last_amount": "Cantitatea de copii de rezervă anterioare de păstrat", + "backup_database_enable_description": "Activare salvarea bazei de date", + "backup_keep_last_amount": "Număr de copii de rezervă anterioare de păstrat", "backup_settings": "Setări Copii de Rezervă", "backup_settings_description": "Gestionați setările de salvare a bazei de date", "cleared_jobs": "Activități eliminate pentru: {job}", @@ -50,6 +53,7 @@ "confirm_email_below": "Pentru a confirma, tastați „{email}” mai jos", "confirm_reprocess_all_faces": "Sigur doriți să reprocesați toate fețele? Acest lucru va șterge și persoanele cu nume.", "confirm_user_password_reset": "Sigur doriți să resetați parola utilizatorului {user}?", + "confirm_user_pin_code_reset": "Ești sigur că vrei să resetezi codul PIN al {user}?", "create_job": "Creează sarcină", "cron_expression": "Expresia cron", "cron_expression_description": "Setați intervalul de scanare folosind formatul cron. Pentru mai multe informații, consultați de ex. Crontab Guru", @@ -167,7 +171,7 @@ "note_apply_storage_label_previous_assets": "Notă: Pentru a aplica Eticheta de Stocare la elementele încărcate anterior, executați", "note_cannot_be_changed_later": "NOTĂ: Nu se va mai putea modifica ulterior!", "notification_email_from_address": "De la adresa", - "notification_email_from_address_description": "Adresa expeditorului, spre exemplu: „Immich Photo Server ”", + "notification_email_from_address_description": "Adresa expeditorului, spre exemplu: „Immich Photo Server ”. Asigură-te că folosești o adresă de la care ai permisiunea de a trimite e-mailuri.", "notification_email_host_description": "Adresa serverului de email (ex. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Ingnoră erorile de certificat", "notification_email_ignore_certificate_errors_description": "Ignoră erorile de validare a certificatului TLS (nerecomandat)", @@ -187,6 +191,7 @@ "oauth_auto_register": "Auto înregistrare", "oauth_auto_register_description": "Înregistrează automat utilizatori noi după autentificarea cu OAuth", "oauth_button_text": "Text buton", + "oauth_client_secret_description": "Necesar dacă PKCE (Proof Key for Code Exchange) nu este suportat de furnizorul OAuth", "oauth_enable_description": "Autentifică-te cu OAuth", "oauth_mobile_redirect_uri": "URI de redirecționare mobilă", "oauth_mobile_redirect_uri_override": "Înlocuire URI de redirecționare mobilă", @@ -199,7 +204,9 @@ "oauth_storage_quota_claim": "Revendicare spațiu de stocare", "oauth_storage_quota_claim_description": "Setează automat spațiul de stocare al utilizatorului la valoarea acestei cereri.", "oauth_storage_quota_default": "Cota implicită a spațiului de stocare (GiB)", - "oauth_storage_quota_default_description": "Spațiul în GiB ce urmează a fi utilizat atunci când nu este furnizată nicio solicitare (introduceți 0 pentru spațiu nelimitat).", + "oauth_storage_quota_default_description": "Spațiul în GiB ce urmează a fi utilizat atunci când nu este furnizată nicio solicitare.", + "oauth_timeout": "Solicitarea a expirat", + "oauth_timeout_description": "Timp de expirare pentru solicitări în milisecunde", "password_enable_description": "Autentificare cu email și parolǎ", "password_settings": "Autentificare cu Parolǎ", "password_settings_description": "Gestioneazǎ setǎrile de autentificare cu parola", @@ -237,6 +244,7 @@ "storage_template_migration_info": "Șablonul de stocare va converti extensiile in litere mici. Modificările șablonului se vor aplica doar materialelor noi. Pentru a aplica retroactiv șablonul la materialele încărcate anterior, rulați {job}.", "storage_template_migration_job": "Sarcină Migrare Șablon Stocare", "storage_template_more_details": "Pentru mai multe detalii despre aceasta caracteristică, accesați Șablon stocare si implicațiile", + "storage_template_onboarding_description_v2": "Când este activată, această funcție va organiza automat fișierele pe baza șablonului definit de către utilizator. Pentru mai multe informații, accesează documentația.", "storage_template_path_length": "Limita de lungime pentru calea aproximativă: {length, number}/{limit, number}", "storage_template_settings": "Șablon Stocare", "storage_template_settings_description": "Gestionează structura folderelor și numele fișierelor pentru elementele încărcate", @@ -251,7 +259,7 @@ "template_email_update_album": "Actualizați Șablonul de Album", "template_email_welcome": "Șablon de e-mail de bun venit", "template_settings": "Șabloane de Notificare", - "template_settings_description": "Gestionați șabloanele personalizate pentru notificări.", + "template_settings_description": "Gestionați șabloanele personalizate pentru notificări", "theme_custom_css_settings": "CSS personalizat", "theme_custom_css_settings_description": "Foile de stil în cascadă (CSS) permit personalizarea designului Immich.", "theme_settings": "Setări Temă", @@ -283,7 +291,7 @@ "transcoding_encoding_options": "Opțiuni codificare", "transcoding_encoding_options_description": "Setează codecuri , calitatea, rezoluția și alte opțiuni pentru videoclipuri codificare", "transcoding_hardware_acceleration": "Accelerare Hardware", - "transcoding_hardware_acceleration_description": "Experimental; mult mai rapid, dar va avea o calitate mai scăzută la același bitrate", + "transcoding_hardware_acceleration_description": "Experimental: transcodare mai rapidă, dar poate reduce calitatea la aceeași rată de biți", "transcoding_hardware_decoding": "Decodare hardware", "transcoding_hardware_decoding_setting_description": "Se aplică doar pentru NVENC, QSV și RKMPP. Activează accelerarea completă în loc de doar accelerarea codificării. S-ar putea să nu funcționeze pentru toate videoclipurile.", "transcoding_max_b_frames": "Număr maxim de cadre B", @@ -329,6 +337,7 @@ "user_delete_delay_settings_description": "Numărul de zile după eliminare până la ștergerea permanentă a contului și a resurselor unui utilizator. Procesul de ștergere a utilizatorului rulează la miezul nopții pentru a verifica utilizatorii care sunt pregătiți pentru ștergere. Modificările aduse acestei setări vor fi evaluate la următoarea execuție.", "user_delete_immediately": "Contul și resursele utilizatorului {user} vor fi puse în coadă pentru ștergere permanentă imediat.", "user_delete_immediately_checkbox": "Pune utilizatorul și resursele în coadă pentru ștergere imediată", + "user_details": "Detalii utilizator", "user_management": "Gestionarea Utilizatorilor", "user_password_has_been_reset": "Parola utilizatorului a fost resetată:", "user_password_reset_description": "Vă rugăm să furnizați utilizatorului parola temporară și să îi informați că va trebui să o schimbe la următoarea autentificare.", @@ -353,6 +362,8 @@ "advanced_settings_log_level_title": "Nivel log: {level}", "advanced_settings_prefer_remote_subtitle": "Unele dispozitive întâmpină dificultăți în încărcarea miniaturilor pentru resursele de pe dispozitiv. Activează această setare pentru a încărca imaginile de la distanță în schimb.", "advanced_settings_prefer_remote_title": "Preferă fotografii la distanță", + "advanced_settings_proxy_headers_subtitle": "Definește antetele proxy pe care Immich ar trebui să le trimită cu fiecare solicitare de rețea", + "advanced_settings_proxy_headers_title": "Antete Proxy", "advanced_settings_self_signed_ssl_subtitle": "Omite verificare certificate SSL pentru distinația server-ului, necesar pentru certificate auto-semnate.", "advanced_settings_self_signed_ssl_title": "Permite certificate SSL auto-semnate", "advanced_settings_sync_remote_deletions_subtitle": "Ștergeți sau restaurați automat un element de pe acest dispozitiv atunci când acțiunea este efectuată pe web", @@ -382,6 +393,7 @@ "album_updated_setting_description": "Primiți o notificare prin e-mail când un album partajat are elemente noi", "album_user_left": "A părăsit {album}", "album_user_removed": "{user} eliminat", + "album_viewer_appbar_delete_confirm": "Ești sigur că vrei să ștergi acest album din contul tău?", "album_viewer_appbar_share_err_delete": "Ștergere album eșuată", "album_viewer_appbar_share_err_leave": "Părăsire album eșuată", "album_viewer_appbar_share_err_remove": "Probleme la ștergerea resurselor din album", @@ -392,6 +404,9 @@ "album_with_link_access": "Permite oricui cu link-ul să vadă fotografiile și persoanele din acest album.", "albums": "Albume", "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Albume}}", + "albums_default_sort_order": "Ordinea implicită de sortare a albumelor", + "albums_default_sort_order_description": "Ordinea inițială de sortare a pozelor la crearea de albume noi.", + "albums_feature_description": "Colecții de date care pot fi partajate cu alți utilizatori.", "all": "Toate", "all_albums": "Toate albumele", "all_people": "Toți oamenii", @@ -412,11 +427,13 @@ "app_settings": "Setări Aplicație", "appears_in": "Apare în", "archive": "Arhivă", + "archive_action_prompt": "{count} adăugate la Arhivă", "archive_or_unarchive_photo": "Arhiveazǎ sau dezarhiveazǎ fotografia", "archive_page_no_archived_assets": "Nu au fost găsite resurse favorite", "archive_page_title": "Arhivă ({count})", "archive_size": "Mărime arhivă", "archive_size_description": "Configurează dimensiunea arhivei pentru descărcări (în GiB)", + "archived": "Arhivat", "archived_count": "{count, plural, other {Arhivat/e#}}", "are_these_the_same_person": "Sunt aceștia aceeași persoană?", "are_you_sure_to_do_this": "Sunteți sigur că doriți să faceți acest lucru?", @@ -427,33 +444,52 @@ "asset_description_updated": "Descrierea resursei a fost actualizată", "asset_filename_is_offline": "Resursa {filename} este offline", "asset_has_unassigned_faces": "Resursa are fețe neatribuite", + "asset_hashing": "Calculare amprentă digitală", + "asset_list_group_by_sub_title": "Grupare după", "asset_list_layout_settings_dynamic_layout_title": "Aspect dinamic", "asset_list_layout_settings_group_automatically": "Automat", "asset_list_layout_settings_group_by": "Grupează resurse după", "asset_list_layout_settings_group_by_month_day": "Lună + zi", + "asset_list_layout_sub_title": "Aspect", "asset_list_settings_subtitle": "Setări format grilă fotografii", "asset_list_settings_title": "Grilă fotografii", "asset_offline": "Resursă Offline", "asset_offline_description": "Această resursă externă nu mai este găsită pe disc. Contactează te rog administratorul tău Immich pentru ajutor.", + "asset_restored_successfully": "Date restaurate cu succes", "asset_skipped": "Sărit", "asset_skipped_in_trash": "În coșul de gunoi", "asset_uploaded": "Încărcat", "asset_uploading": "Se incarcă…", + "asset_viewer_settings_subtitle": "Gestionați setările de vizualizare a galeriei", + "asset_viewer_settings_title": "Vizualizator resurse", "assets": "Resurse", "assets_added_count": "Adăugat {count, plural, one {# resursă} other {# resurse}}", "assets_added_to_album_count": "Am adăugat {count, plural, one {# resursă} other {# resurse}} în album", - "assets_added_to_name_count": "Am adăugat {count, plural, one {# resursă} other {# resurse}} în {hasName, select, true {{name}} other {albumul nou}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} nu pot fi adăugate în album", "assets_count": "{count, plural, one {# resursă} other {# resurse}}", + "assets_deleted_permanently": "{count} poză/poze ștearsă/șterse permanent", + "assets_deleted_permanently_from_server": "{count} poză/poze ștearsă/șterse permanent din serverul Immich", + "assets_downloaded_failed": "{count, plural, one {S-a descărcat # fișier – {error} fișier eșuat} other {S-au descărcat # fișiere – {error} fișiere eșuate}}", + "assets_downloaded_successfully": "{count, plural, one {S-a descărcat cu succes # fișier} other {S-au descărcat cu succes # fișiere}}", "assets_moved_to_trash_count": "Am mutat {count, plural, one {# resursă} other {# resurse}} în coșul de gunoi", "assets_permanently_deleted_count": "Șters permanent {count, plural, one {# resursă} other {# resurse}}", "assets_removed_count": "Eliminat {count, plural, one {# resursă} other {# resurse}}", + "assets_removed_permanently_from_device": "{count} resursă(e) eliminate permanent din dispozitivul dvs.", "assets_restore_confirmation": "Ești sigur că vrei să restaurezi toate resursele tale din coșul de gunoi? Nu poți anula această acțiune! Ține minte că resursele offline nu se restaurează astfel.", "assets_restored_count": "Restaurat {count, plural, one {# resursă} other {# resurse}}", + "assets_restored_successfully": "{count} resursă(e) restaurate cu succes", + "assets_trashed": "{count} resursă(e) eliminate", "assets_trashed_count": "Mutat în coșul de gunoi {count, plural, one {# resursă} other {# resurse}}", + "assets_trashed_from_server": "{count} resursă(e) eliminate de pe serverul Immich", "assets_were_part_of_album_count": "{count, plural, one {Resursa era} other {Resursele erau}} deja parte din album", "authorized_devices": "Dispozitive Autorizate", + "automatic_endpoint_switching_subtitle": "Conectează-te local prin rețeaua Wi‐Fi configurată când este valabilă și prin rețele alternative în caz contrar", + "automatic_endpoint_switching_title": "Alternare URL automată", + "autoplay_slideshow": "Derulare slideshow automat", "back": "Înapoi", "back_close_deselect": "Înapoi, închidere sau deselectare", + "background_location_permission": "Permisiune locație în fundal", + "background_location_permission_content": "Pentru a putea schimba rețeaua activă în fundal, Immich are nevoie de acces *permanent* la locația precisă pentru a citi numele rețelei Wi-Fi", "backup_album_selection_page_albums_device": "Albume în dispozitiv ({count})", "backup_album_selection_page_albums_tap": "Apasă odata pentru a include, de două ori pentru a exclude", "backup_album_selection_page_assets_scatter": "Resursele pot fi împrăștiate în mai multe albume. Prin urmare, albumele pot fi incluse sau excluse în timpul procesului de backup.", @@ -474,6 +510,7 @@ "backup_controller_page_background_app_refresh_enable_button_text": "Mergi la setări", "backup_controller_page_background_battery_info_link": "Arată-mi cum", "backup_controller_page_background_battery_info_message": "Pentru cea mai bună experiență a backup-ului în fundal, te rugăm să dezactivezi orice optimizare pentru baterie care restricționează activitatea în fundal pentru Immich.\n\nDeoarece aceasta este specifică fiecărui dispozitiv, te rugăm verifică informațiile necesare tipului tău de dispozitiv.", + "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "Optimizări baterie", "backup_controller_page_background_charging": "Doar în timpul încărcării", "backup_controller_page_background_configure_error": "Configurare serviciu în fundal eșuată", @@ -483,7 +520,8 @@ "backup_controller_page_background_is_on": "Backup-ul automat în fundal este activat", "backup_controller_page_background_turn_off": "Dezactivează serviciul în fundal", "backup_controller_page_background_turn_on": "Activează serviciul în fundal", - "backup_controller_page_background_wifi": "Doar conectat la WiFi", + "backup_controller_page_background_wifi": "Numai prin Wi-Fi", + "backup_controller_page_backup": "Backup", "backup_controller_page_backup_selected": "Selectat(e): ", "backup_controller_page_backup_sub": "S-a făcut backup pentru fotografii și videoclipuri", "backup_controller_page_created": "Creat la: {date}", @@ -491,6 +529,7 @@ "backup_controller_page_excluded": "Exclus(e): ", "backup_controller_page_failed": "Eșuate ({count})", "backup_controller_page_filename": "Nume fișier: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "Informații backup", "backup_controller_page_none_selected": "Nici o selecție", "backup_controller_page_remainder": "Rămas(e)", @@ -504,14 +543,20 @@ "backup_controller_page_total_sub": "Toate fotografiile și videoclipurile unice din albumele selectate", "backup_controller_page_turn_off": "Dezactivează backup-ul în prim-plan", "backup_controller_page_turn_on": "Activează backup-ul în prim-plan", - "backup_controller_page_uploading_file_info": "Încărcare informații fișier", + "backup_controller_page_uploading_file_info": "Informații încărcare fișier", "backup_err_only_album": "Nu poți șterge singurul album", "backup_info_card_assets": "resurse", "backup_manual_cancelled": "Anulat", "backup_manual_in_progress": "Încărcarea este deja în curs. Încearcă din nou mai târziu", "backup_manual_success": "Succes", "backup_manual_title": "Status încărcare", + "backup_options_page_title": "Opțiuni Backup", + "backup_setting_subtitle": "Schimbă opțiuni pentru backup în prim-plan și în fundal", "backward": "În sens invers", + "biometric_auth_enabled": "Autentificare biometrică activată", + "biometric_locked_out": "Sunteți blocați de la autentificare biometrică", + "biometric_no_options": "Nu sunt disponibile opțiuni biometrice", + "biometric_not_available": "Autentificarea biometrică nu este disponibilă pe acest dispozitiv", "birthdate_saved": "Data nașterii salvată cu succes", "birthdate_set_description": "Data nașterii este utilizată pentru a calcula vârsta acestei persoane la momentul realizării fotografiei.", "blurred_background": "Fundal neclar", @@ -541,14 +586,19 @@ "camera_model": "Model cameră", "cancel": "Anulați", "cancel_search": "Anulați căutarea", + "canceled": "Anulat", "cannot_merge_people": "Nu se pot îmbina persoanele", "cannot_undo_this_action": "Nu puteți anula această acțiune!", "cannot_update_the_description": "Nu se poate actualiza descrierea", + "cast": "Partajare", + "cast_description": "Configurați destinațiile de difuzare disponibile", "change_date": "Schimbați data", + "change_description": "Schimbă descrierea", + "change_display_order": "Schimbați ordinea de afișare", "change_expiration_time": "Schimbați data expirare", "change_location": "Schimbați locația", "change_name": "Schimbați nume", - "change_name_successfully": "Schimbare nume cu succes", + "change_name_successfully": "Schimbare a numelui făcută cu succes", "change_password": "Schimbați parolă", "change_password_description": "Aceasta este fie prima dată când te conectezi în sistem, fie s-a făcut o solicitare pentru a schimba parola ta. Te rog să introduci noua parolă mai jos.", "change_password_form_confirm_password": "Confirmă parola", @@ -556,8 +606,12 @@ "change_password_form_new_password": "Parolă nouă", "change_password_form_password_mismatch": "Parolele nu se potrivesc", "change_password_form_reenter_new_password": "Reintrodu noua parolă", + "change_pin_code": "Schimbă codul PIN", "change_your_password": "Schimbă-ți parola", "changed_visibility_successfully": "Schimbare vizibilitate cu succes", + "check_corrupt_asset_backup": "Verifică copii de rezervă a resurselor corupte", + "check_corrupt_asset_backup_button": "Efectuează verificarea", + "check_corrupt_asset_backup_description": "Rulează această verificare doar prin Wi-Fi și doar după ce toate resursele au fost salvate în copia de rezerva. Procedura poate dura câteva minute.", "check_logs": "Verificați Jurnale", "choose_matching_people_to_merge": "Alegeți persoanele care se potrivesc pentru a le fuziona", "city": "Oraș", @@ -566,6 +620,14 @@ "clear_all_recent_searches": "Curățați toate căutările recente", "clear_message": "Ștergeți mesajul", "clear_value": "Ștergeți valoarea", + "client_cert_dialog_msg_confirm": "OK", + "client_cert_enter_password": "Introdu Parola", + "client_cert_import": "Importă", + "client_cert_import_success_msg": "Certificatul de client este importat", + "client_cert_invalid_msg": "Fisier cu certificat invalid sau parola este greșită", + "client_cert_remove_msg": "Certificatul de client este șters", + "client_cert_subtitle": "Acceptă doar formatul PKCS12 (.p12, .pfx). Importul/ștergerea certificatului este disponibil(ă) doar înainte de autentificare", + "client_cert_title": "Certificat SSL pentru client", "clockwise": "În sensul acelor de ceas", "close": "Închideți", "collapse": "Restrângeți", @@ -578,19 +640,27 @@ "comments_are_disabled": "Comentariile sunt dezactivate", "common_create_new_album": "Creează album nou", "common_server_error": "Te rugăm să verifici conexiunea la rețea, asigura-te că server-ul este accesibil și că versiunile aplicației/server-ului sunt compatibile.", + "completed": "Finalizat", "confirm": "Confirmați", "confirm_admin_password": "Confirmați Parola de Administrator", "confirm_delete_face": "Ești sigur ca vrei sa ștergi {name} din activ?", "confirm_delete_shared_link": "Sunteți sigur că doriți să ștergeți acest link partajat?", "confirm_keep_this_delete_others": "Toate celelalte active din stivă vor fi șterse, cu excepția acestui material. Sunteți sigur că doriți să continuați?", + "confirm_new_pin_code": "Confirmă noul cod PIN", "confirm_password": "Confirmați parola", + "confirm_tag_face": "Vrei să etichetezi această față ca {name}?", + "confirm_tag_face_unnamed": "Vrei să etichetezi această față?", + "connected_device": "Dispozitiv conectat", + "connected_to": "Conectat la", "contain": "Încadrează", + "context": "Context", "continue": "Continuați", "control_bottom_app_bar_create_new_album": "Creează album nou", "control_bottom_app_bar_delete_from_immich": "Șterge din Immich", "control_bottom_app_bar_delete_from_local": "Șterge din dispozitiv", "control_bottom_app_bar_edit_location": "Editează locație", "control_bottom_app_bar_edit_time": "Editează Data și Ora", + "control_bottom_app_bar_share_link": "Partajează linkul", "control_bottom_app_bar_share_to": "Distribuire către", "control_bottom_app_bar_trash_from_immich": "Mută în coș", "copied_image_to_clipboard": "Imagine copiată în clipboard.", @@ -612,6 +682,7 @@ "create_link": "Creează link", "create_link_to_share": "Creează link pentru a distribui", "create_link_to_share_description": "Permiteți oricui are link-ul să vadă fotografia (fotografiile) selectată(e)", + "create_new": "CREARE NOUĂ", "create_new_person": "Creați o persoană nouă", "create_new_person_hint": "Atribuiți resursele selectate unei persoane noi", "create_new_user": "Creează utilizator nou", @@ -621,11 +692,16 @@ "create_tag_description": "Creează o etichetă nouă. Pentru etichete imbricate, te rog să introduci calea completă a etichetei, inclusiv bare oblice (/).", "create_user": "Creează utilizator", "created": "Creat", + "created_at": "Creat", + "crop": "Decupează", "curated_object_page_title": "Obiecte", "current_device": "Dispozitiv curent", + "current_pin_code": "Codul PIN actual", + "current_server_address": "Adresa actuală a serverului", "custom_locale": "Setare Regională Personalizată", "custom_locale_description": "Formatați datele și numerele în funcție de limbă și regiune", "dark": "Întunecat", + "dark_theme": "Comută tema întunecată", "date_after": "După data", "date_and_time": "Dată și oră", "date_before": "Anterior datei", @@ -640,6 +716,7 @@ "default_locale": "Setare Regională Implicită", "default_locale_description": "Formatați datele și numerele în funcție de regiunea browserului dvs", "delete": "Ștergere", + "delete_action_prompt": "{count} șterse permanent", "delete_album": "Ștergere album", "delete_api_key_prompt": "Sunteți sigur că doriți să ștergeți această cheie API?", "delete_dialog_alert": "Aceste elemente vor fi șterse permanent de pe server-ul Immich și din dispozitivul tău", @@ -672,6 +749,7 @@ "disallow_edits": "Interzice modificările", "discord": "Server Discord", "discover": "Descoperiți", + "discovered_devices": "Dispozititve descoperite", "dismiss_all_errors": "Ignorați toate erorile", "dismiss_error": "Ignorați eroarea", "display_options": "Opțiuni de afișare", @@ -682,6 +760,12 @@ "documentation": "Documentație", "done": "Gata", "download": "Descărcați", + "download_canceled": "Descărcare anulată", + "download_complete": "Descărcare completă", + "download_enqueue": "Descărcare în coadă", + "download_error": "Eroare de descărcare", + "download_failed": "Descărcare eșuată", + "download_finished": "Descărcare finalizată", "download_include_embedded_motion_videos": "Videoclipuri încorporate", "download_include_embedded_motion_videos_description": "Include videoclipurile încorporate în fotografiile în mișcare ca fișier separat", "download_settings": "Descărcați", @@ -1204,13 +1288,16 @@ "previous": "Anterior", "previous_memory": "Memoria anterioară", "previous_or_next_photo": "Fotografie înainte/înapoi", + "previous_or_next_year": "An înainte/înapoi", "primary": "Primar", "privacy": "Confidențialitate", + "profile": "Profil", "profile_drawer_app_logs": "Log-uri", - "profile_drawer_client_out_of_date_major": "Aplicația nu folosește ultima versiune. Te rugăm să actulizezi la ultima versiune majoră.", - "profile_drawer_client_out_of_date_minor": "Aplicația nu folosește ultima versiune. Te rugăm să actulizezi la ultima versiune minoră.", + "profile_drawer_client_out_of_date_major": "Aplicația nu folosește ultima versiune. Te rugăm să actualizezi la ultima versiune majoră.", + "profile_drawer_client_out_of_date_minor": "Aplicația nu folosește ultima versiune. Te rugăm să actualizezi la ultima versiune minoră.", "profile_drawer_client_server_up_to_date": "Aplicația client și server-ul sunt actualizate", - "profile_drawer_server_out_of_date_major": "Server-ul nu folosește ultima versiune. Te rugăm să actulizezi la ultima versiune majoră.", + "profile_drawer_github": "GitHub", + "profile_drawer_server_out_of_date_major": "Server-ul nu folosește ultima versiune. Te rugăm să actualizezi la ultima versiune majoră.", "profile_drawer_server_out_of_date_minor": "Server-ul nu folosește ultima versiune. Te rugăm să actulizezi la ultima versiune minoră.", "profile_image_of_user": "Imagine de profil a lui {user}", "profile_picture_set": "Poză de profil setată.", @@ -1230,22 +1317,25 @@ "purchase_failed_activation": "Activare eșuată! Vă rugăm să vă verificați e-mailul pentru cheia de produs corectă!", "purchase_individual_description_1": "Pentru un individ", "purchase_individual_description_2": "Statutul de suporter", + "purchase_individual_title": "Individual", "purchase_input_suggestion": "Aveți o cheie de produs? Introduceți cheia mai jos", "purchase_license_subtitle": "Cumpărați Immich pentru a sprijini dezvoltarea continuă a serviciului", "purchase_lifetime_description": "Achiziție pe viață", "purchase_option_title": "OPȚIUNI DE CUMPĂRARE", - "purchase_panel_info_1": "Dezvoltarea Immich necesită mult timp și efort și avem ingineri cu normă întreagă care lucrează la ea pentru a o face cât se poate de bună. Misiunea noastră este ca software-ul open-source și practicile de afaceri etice să devină o sursă de venit durabilă pentru dezvoltatori și să se creeze un ecosistem care să respecte confidențialitatea, cu alternative reale la serviciile cloud care exploatează.", + "purchase_panel_info_1": "Dezvoltarea programului Immich necesită mult timp și efort și avem ingineri cu normă întreagă care lucrează la el pentru a-l face cât se poate de bun. Misiunea noastră este ca software-ul open-source și practicile de afaceri etice să devină o sursă de venit durabilă pentru dezvoltatori și să se creeze un ecosistem care să respecte confidențialitatea utilizatorilor, cu alternative reale la serviciile cloud care exploatează utilizatorii.", "purchase_panel_info_2": "Deoarece ne-am angajat să nu adăugăm planuri de plată, această achiziție nu vă va oferi nicio funcție suplimentară în Immich. Ne bazăm pe utilizatori ca dvs. pentru a sprijini dezvoltarea continuă a lui Immich.", "purchase_panel_title": "Susțineți proiectul", + "purchase_per_user": "Per utilizator", "purchase_remove_product_key": "Eliminați Cheia Produsului", "purchase_remove_product_key_prompt": "Sigur doriți să eliminați cheia de produs?", "purchase_remove_server_product_key": "Eliminați cheia de produs a Serverului", "purchase_remove_server_product_key_prompt": "Sigur doriți să eliminați cheia de produs a Serverului?", "purchase_server_description_1": "Pentru tot serverul", "purchase_server_description_2": "Statutul de suporter", + "purchase_server_title": "Server", "purchase_settings_server_activated": "Cheia de produs a serverului este gestionată de administrator", "rating": "Evaluare cu stele", - "rating_clear": "Anulați evaluare", + "rating_clear": "Anulați evaluarea", "rating_count": "{count, plural, one {# stea} other {# stele}}", "rating_description": "Afișați evaluarea EXIF în panoul de informații", "reaction_options": "Opțiuni de reacție", @@ -1256,15 +1346,16 @@ "reassing_hint": "Atribuiți resursele selectate unei persoane existente", "recent-albums": "Albume recente", "recent_searches": "Căutări recente", + "recently_added": "Adăugate recent", "recently_added_page_title": "Adăugate recent", "refresh": "Reîmprospătare", - "refresh_encoded_videos": "Actualizează videoclipurile codificate", + "refresh_encoded_videos": "Actualizează videoclipurile encodate", "refresh_faces": "Reîmprospătați fețele", "refresh_metadata": "Actualizați metadatele", "refresh_thumbnails": "Reîmprospătați miniaturile", "refreshed": "Reîmprospătat", "refreshes_every_file": "Recitește toate fișierele existente și noi", - "refreshing_encoded_video": "Se reîmprospătează videoclipul codificat", + "refreshing_encoded_video": "Se reîmprospătează videoclipul encodat", "refreshing_faces": "Se reîmprospătează fețele", "refreshing_metadata": "Se reîmprospătează metadatele", "regenerating_thumbnails": "Se regenerează miniaturile", @@ -1276,9 +1367,12 @@ "remove_deleted_assets": "Eliminați Resursele Șterse", "remove_from_album": "Ștergeți din album", "remove_from_favorites": "Eliminați din favorite", + "remove_from_locked_folder": "Eliminați din folderul securizat", + "remove_from_locked_folder_confirmation": "Sunteți sigur că doriți să mutați aceste poze și videoclipuri afară din folderul securizat? Vor deveni vizibile în biblioteca dvs.", "remove_from_shared_link": "Eliminați din linkul partajat", "remove_memory": "Șterge amintirea", "remove_photo_from_memory": "Șterge fotografia din această amintire", + "remove_tag": "Eliminați ticheta", "remove_url": "Eliminați adresa URL", "remove_user": "Eliminați utilizatorul", "removed_api_key": "Cheie API eliminată: {name}", @@ -1647,6 +1741,7 @@ "view_previous_asset": "Vizualizați resursa anterioară", "view_qr_code": "Vezi cod QR", "view_stack": "Vizualizați Stiva", + "view_user": "Vizualizare utilizator", "viewer_remove_from_stack": "Șterge din grup", "viewer_stack_use_as_main_asset": "Folosește ca resursă principală", "viewer_unstack": "Anulează grup", @@ -1656,11 +1751,12 @@ "week": "Sǎptǎmânǎ", "welcome": "Bun venit", "welcome_to_immich": "Bun venit la Immich", - "wifi_name": "WiFi Name", + "wifi_name": "Nume Wi-Fi", + "wrong_pin_code": "Cod PIN greșit", "year": "An", "years_ago": "acum {years, plural, one {# an} other {# ani}} în urmă", "yes": "Da", "you_dont_have_any_shared_links": "Nu aveți linkuri partajate", - "your_wifi_name": "Your WiFi name", + "your_wifi_name": "Numele rețelei tale WiFi", "zoom_image": "Măriți Imaginea" } diff --git a/i18n/ru.json b/i18n/ru.json index 5810a31053..84db991027 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -166,6 +166,20 @@ "metadata_settings_description": "Управление настройками метаданных", "migration_job": "Миграция", "migration_job_description": "Перенос миниатюр объектов и лиц в последнюю структуру папок", + "nightly_tasks_cluster_faces_setting_description": "Запустить распознавание людей по новым обнаруженным лицам", + "nightly_tasks_cluster_new_faces_setting": "Распознавание новых лиц", + "nightly_tasks_database_cleanup_setting": "Задачи очистки базы данных", + "nightly_tasks_database_cleanup_setting_description": "Удаление старых и более ненужных записей из базы данных", + "nightly_tasks_generate_memories_setting": "Создание воспоминаний", + "nightly_tasks_generate_memories_setting_description": "Создание новых воспоминаний из существующих объектов", + "nightly_tasks_missing_thumbnails_setting": "Создание отсутствующих миниатюр", + "nightly_tasks_missing_thumbnails_setting_description": "Добавление объектов без миниатюр в очередь для их создания", + "nightly_tasks_settings": "Настройки ночных задач", + "nightly_tasks_settings_description": "Управление ночными регламентными задачами", + "nightly_tasks_start_time_setting": "Время начала", + "nightly_tasks_start_time_setting_description": "Время, когда сервер начинает выполнять ночные задачи", + "nightly_tasks_sync_quota_usage_setting": "Синхронизация квот хранилища", + "nightly_tasks_sync_quota_usage_setting_description": "Обновление квоты хранилища пользователя на основе актуальных данных", "no_paths_added": "Пути не добавлены", "no_pattern_added": "Шаблон не добавлен", "note_apply_storage_label_previous_assets": "Примечание: Чтобы применить метку хранилища к ранее загруженным объектам, запустите", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "URI редиректа для мобильных", "oauth_mobile_redirect_uri_override": "Перенаправление URI для мобильных устройств", "oauth_mobile_redirect_uri_override_description": "Включите, если поставщик OAuth не разрешает использование мобильного URI, например, ''{callback}''", + "oauth_role_claim": "Утверждение роли", + "oauth_role_claim_description": "Автоматическое предоставление доступа администратора на основе наличия этого утверждения. Утверждение может иметь значение 'user' или 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Настройки входа через OAuth", "oauth_settings_more_details": "Для получения дополнительной информаций об этой функции обратитесь к документации.", @@ -357,10 +373,12 @@ "admin_password": "Пароль администратора", "administration": "Управление сервером", "advanced": "Расширенные", + "advanced_settings_beta_timeline_subtitle": "Попробуйте новый функционал приложения", + "advanced_settings_beta_timeline_title": "Бета-версия временной шкалы", "advanced_settings_enable_alternate_media_filter_subtitle": "Используйте этот параметр для фильтрации медиафайлов во время синхронизации на основе альтернативных критериев. Пробуйте только в том случае, если у вас есть проблемы с обнаружением приложением всех альбомов.", "advanced_settings_enable_alternate_media_filter_title": "[ЭКСПЕРИМЕНТАЛЬНО] Использование фильтра синхронизации альбомов альтернативных устройств", "advanced_settings_log_level_title": "Уровень логирования: {level}", - "advanced_settings_prefer_remote_subtitle": "Некоторые устройства очень медленно загружают локальные изображения. Активируйте эту настройку, чтобы изображения всегда загружались с сервера.", + "advanced_settings_prefer_remote_subtitle": "Некоторые устройства очень медленно загружают локальные миниатюры. Активируйте эту настройку, чтобы изображения всегда загружались с сервера.", "advanced_settings_prefer_remote_title": "Предпочитать фото на сервере", "advanced_settings_proxy_headers_subtitle": "Определите заголовки прокси-сервера, которые Immich должен отправлять с каждым сетевым запросом", "advanced_settings_proxy_headers_title": "Заголовки прокси", @@ -388,6 +406,7 @@ "album_options": "Параметры альбома", "album_remove_user": "Удалить пользователя?", "album_remove_user_confirmation": "Вы уверены, что хотите удалить пользователя {user}?", + "album_search_not_found": "Не найдено альбомов по вашему запросу", "album_share_no_users": "Похоже, вы поделились этим альбомом со всеми пользователями или у вас нет пользователей, с которыми можно поделиться.", "album_updated": "Альбом обновлён", "album_updated_setting_description": "Получать уведомление по электронной почте при добавлении новых ресурсов в общий альбом", @@ -407,6 +426,7 @@ "albums_default_sort_order": "Порядок сортировки в альбомах по умолчанию", "albums_default_sort_order_description": "Первоначальный порядок сортировки, устанавливаемый в новых альбомах.", "albums_feature_description": "Коллекции фото и видео, которыми можно делиться с другими пользователями.", + "albums_on_device_count": "Альбомы на устройстве ({count})", "all": "Все", "all_albums": "Все альбомы", "all_people": "Все люди", @@ -427,6 +447,7 @@ "app_settings": "Параметры приложения", "appears_in": "Добавлено в", "archive": "Архив", + "archive_action_prompt": "{count} добавлено в Архив", "archive_or_unarchive_photo": "Архивировать или разархивировать фото", "archive_page_no_archived_assets": "В архиве сейчас пусто", "archive_page_title": "Архив ({count})", @@ -464,7 +485,6 @@ "assets": "Объекты", "assets_added_count": "{count, plural, one {Добавлен # объект} many {Добавлено # объектов} other {Добавлено # объекта}}", "assets_added_to_album_count": "В альбом {count, plural, one {добавлен # объект} many {добавлено # объектов} other {добавлено # объекта}}", - "assets_added_to_name_count": "{count, plural, one {# объект добавлен} many {# объектов добавлено} other {# объекта добавлено}} в {hasName, select, true {альбом {name}} other {новый альбом}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Объект не может быть добавлен} other {Объекты не могут быть добавлены}} в альбом", "assets_count": "{count, plural, one {# объект} many {# объектов} other {# объекта}}", "assets_deleted_permanently": "{count} объект(ов) удалено навсегда", @@ -587,6 +607,7 @@ "cancel": "Отменить", "cancel_search": "Отменить поиск", "canceled": "Отменено", + "canceling": "Отмена", "cannot_merge_people": "Невозможно объединить людей", "cannot_undo_this_action": "Это действие нельзя отменить!", "cannot_update_the_description": "Невозможно обновить описание", @@ -703,7 +724,7 @@ "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Тёмная", - "darkTheme": "Переключение темной темы", + "dark_theme": "Тёмная тема", "date_after": "Дата после", "date_and_time": "Дата и Время", "date_before": "Дата до", @@ -713,12 +734,13 @@ "day": "День", "deduplicate_all": "Убрать все дубликаты", "deduplication_criteria_1": "Размер изображения в байтах", - "deduplication_criteria_2": "Подсчет данных EXIF", + "deduplication_criteria_2": "Количество EXIF данных", "deduplication_info": "Информация о дедупликации", - "deduplication_info_description": "Для автоматического предварительного выбора объектов и массового удаления дубликатов мы рассмотрим:", + "deduplication_info_description": "Для автоматического выбора лучших объектов среди дубликатов анализируется следующая информация:", "default_locale": "Дата и время по умолчанию", "default_locale_description": "Использовать формат даты и времени в соответствии с языковым стандартом вашего браузера", "delete": "Удалить", + "delete_action_prompt": "{count} удалено навсегда", "delete_album": "Удалить альбом", "delete_api_key_prompt": "Вы действительно хотите удалить этот API ключ?", "delete_dialog_alert": "Эти элементы будут безвозвратно удалены с сервера, а также с вашего устройства", @@ -732,6 +754,7 @@ "delete_key": "Удалить ключ", "delete_library": "Удалить библиотеку", "delete_link": "Удалить ссылку", + "delete_local_action_prompt": "{count} удалено локально", "delete_local_dialog_ok_backed_up_only": "Удалить только резервные копии", "delete_local_dialog_ok_force": "Все равно удалить", "delete_others": "Удалить остальные", @@ -745,6 +768,7 @@ "description": "Описание", "description_input_hint_text": "Добавить описание...", "description_input_submit_error": "Не удалось обновить описание, проверьте логи, чтобы узнать причину", + "deselect_all": "Снять выделение", "details": "Подробности", "direction": "Направление", "disabled": "Отключено", @@ -762,6 +786,7 @@ "documentation": "Документация", "done": "Готово", "download": "Скачать", + "download_action_prompt": "Загружаются {count} объектов", "download_canceled": "Загрузка отменена", "download_complete": "Загрузка окончена", "download_enqueue": "Загрузка в очереди", @@ -783,7 +808,7 @@ "downloading_media": "Загрузка медиа", "drop_files_to_upload": "Перенесите файлы в любое место для загрузки", "duplicates": "Дубликаты", - "duplicates_description": "Разберитесь с каждой группой, указав, какие из них являются дубликатами, если таковые имеются", + "duplicates_description": "Просмотрите найденные дубликаты и в каждой группе укажите, какие объекты оставить, а какие удалить", "duration": "Продолжительность", "edit": "Редактировать", "edit_album": "Редактировать альбом", @@ -799,6 +824,7 @@ "edit_key": "Изменить ключ", "edit_link": "Редактировать ссылку", "edit_location": "Редактировать местоположение", + "edit_location_action_prompt": "{count} мест изменено", "edit_location_dialog_title": "Местоположение", "edit_name": "Редактировать имя", "edit_people": "Редактировать людей", @@ -817,6 +843,7 @@ "empty_trash": "Очистить корзину", "empty_trash_confirmation": "Вы уверены, что хотите очистить корзину? Все объекты в корзине будут навсегда удалены из Immich.\nВы не сможете отменить это действие!", "enable": "Включить", + "enable_backup": "Включить резервное копирование", "enable_biometric_auth_description": "Введите свой PIN-код для включения биометрической аутентификации", "enabled": "Включено", "end_date": "Дата окончания", @@ -984,6 +1011,7 @@ "failed_to_load_assets": "Не удалось загрузить объекты", "failed_to_load_folder": "Ошибка при загрузке папки", "favorite": "Избранное", + "favorite_action_prompt": "{count} добавлено в Избранное", "favorite_or_unfavorite_photo": "Добавить или удалить фотографию из избранного", "favorites": "Избранное", "favorites_page_no_favorites": "В избранном сейчас пусто", @@ -1103,7 +1131,7 @@ "items_count": "{count, plural, one {# элемент} many {# элементов} other {# элемента}}", "jobs": "Задачи", "keep": "Оставить", - "keep_all": "Сохранить всё", + "keep_all": "Сохранить все", "keep_this_delete_others": "Оставить этот, удалить остальные", "kept_this_deleted_others": "Сохранён этот объект и {count, plural, one {# объект удалён} many {# объектов удалено} other {# объекта удалено}}", "keyboard_shortcuts": "Сочетания клавиш", @@ -1127,6 +1155,7 @@ "library_page_sort_created": "Недавно созданные", "library_page_sort_last_modified": "Последнее изменение", "library_page_sort_title": "Название альбома", + "licenses": "Лицензии", "light": "Светлая", "like_deleted": "Лайк удален", "link_motion_video": "Ссылка на движущееся видео", @@ -1246,6 +1275,7 @@ "more": "Больше", "move": "Переместить", "move_off_locked_folder": "Переместить из личной папки", + "move_to_lock_folder_action_prompt": "{count} добавлено в Личную папку", "move_to_locked_folder": "Переместить в личную папку", "move_to_locked_folder_confirmation": "Эти фото и видео будут удалены из всех альбомов и будут доступны только в личной папке", "moved_to_archive": "{count, plural, one {# объект перемещён} many {# объектов перемещено} other {# объекта перемещено}} в архив", @@ -1460,6 +1490,7 @@ "purchase_server_description_2": "Состояние поддержки", "purchase_server_title": "Сервер", "purchase_settings_server_activated": "Ключом продукта управляет администратор сервера", + "queue_status": "В очереди {count}/{total}", "rating": "Рейтинг звёзд", "rating_clear": "Очистить рейтинг", "rating_count": "{count, plural, one {# звезда} many {# звезд} other {# звезды}}", @@ -1495,7 +1526,9 @@ "remove_custom_date_range": "Удалить пользовательский диапазон дат", "remove_deleted_assets": "Удаление автономных файлов", "remove_from_album": "Удалить из альбома", + "remove_from_album_action_prompt": "{count} удалено из альбома", "remove_from_favorites": "Удалить из избранного", + "remove_from_lock_folder_action_prompt": "{count} удалено из Личной папки", "remove_from_locked_folder": "Удалить из личной папки", "remove_from_locked_folder_confirmation": "Вы действительно хотите переместить эти фото и видео из личной папки? Они станут доступны в вашей библиотеке.", "remove_from_shared_link": "Удалить из публичной ссылки", @@ -1532,7 +1565,7 @@ "restored_asset": "Восстановленный объект", "resume": "Продолжить", "retry_upload": "Повторить загрузку", - "review_duplicates": "Посмотреть дубликаты", + "review_duplicates": "Разобрать дубликаты", "role": "Роль", "role_editor": "Редактор", "role_viewer": "Зритель", @@ -1607,18 +1640,18 @@ "select": "Выбрать", "select_album_cover": "Выбрать обложку альбома", "select_all": "Выбрать все", - "select_all_duplicates": "Выбрать все дубликаты", + "select_all_duplicates": "Выбрать все для сохранения", "select_all_in": "Выбрать все в {group}", "select_avatar_color": "Выберите цвет аватара", "select_face": "Выбрать лицо", "select_featured_photo": "Выбрать избранное фото", "select_from_computer": "Выберите с компьютера", - "select_keep_all": "Оставить всё выбранное", + "select_keep_all": "Выбрать все для сохранения", "select_library_owner": "Выберите владельца библиотеки", "select_new_face": "Выбрать другое лицо", "select_person_to_tag": "Выделите лицо человека, которого хотите отметить", "select_photos": "Выберите фотографии", - "select_trash_all": "Удалить всё выбранное", + "select_trash_all": "Выбрать все для удаления", "select_user_for_sharing_page_err_album": "Не удалось создать альбом", "selected": "Выбрано", "selected_count": "{count, plural, one {Выбран # объект} many {Выбрано # объектов} other {Выбрано # объекта}}", @@ -1667,6 +1700,7 @@ "settings_saved": "Настройки сохранены", "setup_pin_code": "Создание PIN-кода", "share": "Поделиться", + "share_action_prompt": "Расшарено {count} объектов", "share_add_photos": "Добавить фото", "share_assets_selected": "{count} выбрано", "share_dialog_preparing": "Подготовка...", @@ -1767,10 +1801,11 @@ "sort_recent": "Недавние фото", "sort_title": "Заголовок", "source": "Исходный код", - "stack": "Группировать", - "stack_duplicates": "Группировать дубликаты", + "stack": "Сгруппировать", + "stack_action_prompt": "{count} сгруппировано", + "stack_duplicates": "Сгруппировать дубликаты", "stack_select_one_photo": "Выберите главную фотографию для группы", - "stack_selected_photos": "Группировать выбранные объекты", + "stack_selected_photos": "Сгруппировать выбранные объекты", "stacked_assets_count": "{count, plural, one {# объект объединен} many {# объектов объединено} other {# объекта объединено}} в группу", "stacktrace": "Трассировка стека", "start": "Старт", @@ -1838,7 +1873,8 @@ "total": "Всего", "total_usage": "Общая статистика", "trash": "Корзина", - "trash_all": "Удалить всё", + "trash_action_prompt": "{count} перемещено в корзину", + "trash_all": "Удалить все", "trash_count": "Удалить {count, number}", "trash_delete_asset": "Переместить в корзину", "trash_emptied": "Корзина очищена", @@ -1855,9 +1891,11 @@ "unable_to_change_pin_code": "Ошибка при изменении PIN-кода", "unable_to_setup_pin_code": "Ошибка при создании PIN-кода", "unarchive": "Восстановить", + "unarchive_action_prompt": "{count} удалено из архива", "unarchived_count": "{count, plural, one {# объект возвращён} many {# объектов возвращено} other {# объекта возвращено}} из архива", "undo": "Отменить", "unfavorite": "Удалить из избранного", + "unfavorite_action_prompt": "{count} удалено из избранного", "unhide_person": "Показать человека", "unknown": "Неизвестно", "unknown_country": "Неизвестная страна", @@ -1872,15 +1910,18 @@ "unnamed_share": "Общий доступ без названия", "unsaved_change": "Несохранённое изменение", "unselect_all": "Отменить выделение", - "unselect_all_duplicates": "Отменить выбор всех дубликатов", + "unselect_all_duplicates": "Выбрать все для удаления", "unselect_all_in": "Отменить выделение в {group}", "unstack": "Разгруппировать", + "unstack_action_prompt": "{count} разгруппировано", "unstacked_assets_count": "{count, plural, one {Разгруппирован # объект} many {Разгруппировано # объектов} other {Разгруппировано # объекта}}", + "untagged": "Без тегов", "up_next": "Следующее", "updated_at": "Обновлён", "updated_password": "Пароль изменён", "upload": "Загрузить", "upload_concurrency": "Параллельность загрузки", + "upload_details": "Подробности загрузки", "upload_dialog_info": "Хотите загрузить выбранные объекты на сервер?", "upload_dialog_title": "Загрузить объект", "upload_errors": "Загрузка завершена с {count, plural, one {# ошибкой} other {# ошибками}}, обновите страницу, чтобы увидеть новые загруженные объекты.", @@ -1912,6 +1953,7 @@ "user_usage_stats_description": "Посмотреть статистику использования аккаунта", "username": "Имя пользователя", "users": "Пользователи", + "users_added_to_album_count": "{count, plural, one {# пользователь добавлен} many {# пользователей добавлено} other {# пользователя добавлено}} к альбому", "utilities": "Утилиты", "validate": "Проверить", "validate_endpoint_error": "Введите корректный URL", @@ -1930,6 +1972,7 @@ "view_album": "Просмотреть альбом", "view_all": "Посмотреть всё", "view_all_users": "Показать всех пользователей", + "view_details": "Посмотреть подробности", "view_in_timeline": "Показать на временной шкале", "view_link": "Показать ссылку", "view_links": "Показать ссылки", @@ -1937,7 +1980,7 @@ "view_next_asset": "Показать следующий объект", "view_previous_asset": "Показать предыдущий объект", "view_qr_code": "Посмотреть QR код", - "view_stack": "Показать стек", + "view_stack": "Показать группу", "view_user": "Просмотреть пользователя", "viewer_remove_from_stack": "Убрать из группы", "viewer_stack_use_as_main_asset": "Использовать в качестве основного объекта", diff --git a/i18n/sk.json b/i18n/sk.json index cd64c52532..c8c1cb5303 100644 --- a/i18n/sk.json +++ b/i18n/sk.json @@ -14,6 +14,7 @@ "add_a_location": "Pridať polohu", "add_a_name": "Pridať meno", "add_a_title": "Pridať názov", + "add_endpoint": "Pridať koncový bod", "add_exclusion_pattern": "Pridať vzor vylúčenia", "add_import_path": "Pridať cestu pre import", "add_location": "Pridať polohu", @@ -33,6 +34,7 @@ "added_to_favorites_count": "Pridané {count, number} do obľúbených", "admin": { "add_exclusion_pattern_description": "Pridávanie vzorov na vylúčenie. Globovanie pomocou *, ** a ? je podporované. Ak chcete ignorovať všetky súbory v akomkoľvek adresári s názvom \"Raw\", použite \"**/Raw/**\". Ak chcete ignorovať všetky súbory končiace na \".tif\", použite \"**/*.tif\". Ak chcete ignorovať absolútnu cestu, použite príkaz \"/cesta/k/ignorovanym/**\".", + "admin_user": "Správca", "asset_offline_description": "Táto položka externej knižnice sa už na disku nenachádza a bola presunutá do koša. Pokiaľ bol súbor presunutý v rámci knižnice, skontrolujte časovú os a vyhľadajte nové odpovedajúce položky. Ak chcete túto položku obnoviť, uistite sa, že je cesta k nižšie uvedenému súboru prístupná pre aplikáciu Immich a prehľadajte knižnicu.", "authentication_settings": "Overovanie a prihlásenie", "authentication_settings_description": "Spravovať heslo, protokol OAuth a ďalšie nastavenia overenia", @@ -47,7 +49,7 @@ "cleared_jobs": "Hotové úlohy pre: {job}", "config_set_by_file": "Konfigurácia je v súčasnosti nastavená konfiguračným súborom", "confirm_delete_library": "Naozaj chcete vymazať knižnicu {library}?", - "confirm_delete_library_assets": "Ste si istí, že chcete vymazať túto knižnicu? Tato operácia nenávratne odstráni {count, plural, one {# contained asset} other {all # contained assets}} súborov z Immich. Súbory budú ponechané na disku.", + "confirm_delete_library_assets": "Ste si istí, že chcete vymazať túto knižnicu? Tato operácia nenávratne odstráni {count, plural, one {# zahrnutú položku} few {# zahrnuté položky} other {všetkých # zahrnutých položiek}} z aplikácie Immich. Súbory budú ponechané na disku.", "confirm_email_below": "Pre potvrdenie zadajte \"{email}\" nižšie", "confirm_reprocess_all_faces": "Naozaj chcete spracovať všetky tváre znova? Tento proces vymaže pomenovaných ľudí.", "confirm_user_password_reset": "Naozaj chcete resetovať heslo pre {user}?", @@ -55,14 +57,14 @@ "create_job": "Vytvoriť úlohu", "cron_expression": "Výraz cron", "cron_expression_description": "Nastavte interval skenovania pomocou formátu cron. Pre viac informácií navštívte Crontab Guru", - "cron_expression_presets": "Presety cron výrazov", + "cron_expression_presets": "Predvoľby výrazov Cron", "disable_login": "Zakázať prihlásenie", - "duplicate_detection_job_description": "Spustiť strojové učenie na položkách pre detekciu podobných obrázkov. Spolieha sa na inteligentné vyhľadávanie", + "duplicate_detection_job_description": "Spustite strojové učenie na položkách pre detekciu podobných obrázkov. Spolieha sa na inteligentné vyhľadávanie", "exclusion_pattern_description": "Vylučovacie vzory Vám umožňujú ignorovať súbory a priečinky pri skenovaní Vašej knižnice. Toto je užitočné, ak máte priečinky obsahujúce súbory, ktoré nechcete importovať, napríklad RAW súbory.", "external_library_management": "Správa Externej Knižnice", "face_detection": "Detekcia tvárí", - "face_detection_description": "Rozpoznajte tváre v položkách pomocou strojového učenia. V prípade videí sa berie do úvahy len náhľad. „Obnoviť“ (znovu) spracuje všetky položky. „Obnoviť“ dodatočne vymaže všetky aktuálne údaje o tvárach. „Chýbajúce“ zaradí do poradia položky aktív, ktoré ešte neboli spracované. Zistené tváre sa po dokončení rozpoznávania tvárí zaradia do poradia na rozpoznávanie tvárí, pričom sa zoskupia do existujúcich alebo nových osôb.", - "facial_recognition_job_description": "Zoskupovať rozpoznané tváre do osôb. Tento krok sa vykoná po dokončení rozpoznávania tvárí. „Obnoviť“ (znovu) zoskupí všetky tváre. „Chýbajúce“ zaradí tváre, ktoré nemajú pridelenú osobu.", + "face_detection_description": "Rozpoznajte tváre v položkách pomocou strojového učenia. V prípade videí sa berie do úvahy len náhľad. „Obnoviť“ (znovu) spracuje všetky položky. „Resetovať“ dodatočne vymaže všetky aktuálne údaje o tvárach. „Chýbajúce“ zaradí do poradia médiá, ktoré ešte neboli spracované. Zistené tváre sa po dokončení rozpoznávania tvárí zaradia do poradia na rozpoznávanie tvárí, pričom sa zoskupia do existujúcich alebo nových osôb.", + "facial_recognition_job_description": "Zoskupte rozpoznané tváre do osôb. Tento krok sa vykoná po dokončení rozpoznávania tvárí. „Resetovať“ (znovu) zoskupí všetky tváre. „Chýbajúce“ zaradí tváre, ktoré nemajú pridelenú osobu.", "failed_job_command": "Príkaz {command} zlyhal pre úlohu: {job}", "force_delete_user_warning": "VAROVANIE: Toto okamžite odstráni používateľa a všetky položky. Tento krok nie je možné vrátiť späť a súbory nebude možné obnoviť.", "image_format": "Formát", @@ -84,7 +86,7 @@ "image_resolution_description": "Vyššie rozlíšenie môže zachovať viac detailov, ale kódovanie trvá dlhšie, súbory sú väčšie a môže to znížiť rýchlosť odozvy aplikácie.", "image_settings": "Obrázky", "image_settings_description": "Spravovať kvalitu a rozlíšenie generovaných obrázkov", - "image_thumbnail_description": "Malá miniatúra s odstránenými metadátami, používané pri zobrazovaní skupín fotiek ako na hlavnej časovej osi", + "image_thumbnail_description": "Malá miniatúra s odstránenými metadátami, ktorá sa používa pri prezeraní skupín fotografií ako na hlavnej časovej osi", "image_thumbnail_quality_description": "Kvalita miniatúry v stupnici od 1 do 100. Vyššia hodnota znamená lepšiu kvalitu, ale produkuje väčšie súbory a môže znížiť odozvu aplikácie.", "image_thumbnail_title": "Miniatúry", "job_concurrency": "Súbežnosť úlohy - {job}", @@ -103,18 +105,18 @@ "library_scanning_enable_description": "Zapnúť pravidelné skenovanie knižnice", "library_settings": "Externá knižnica", "library_settings_description": "Spravovať nastavenia externej knižnice", - "library_tasks_description": "Vyhľadávanie nových alebo zmenených položiek v externých knižniciach", + "library_tasks_description": "Vyhľadajte nové alebo zmenené médiá v externých knižniciach", "library_watching_enable_description": "Sledovať externé knižnice pre zmeny v súboroch", "library_watching_settings": "Sledovanie knižnice (EXPERIMENTÁLNE)", "library_watching_settings_description": "Automaticky sledovať zmenené súbory", - "logging_enable_description": "Povoliť logovanie", - "logging_level_description": "Ak je povolené, akú úroveň logovania použiť.", - "logging_settings": "Logovanie", + "logging_enable_description": "Povoliť ukladanie záznamov", + "logging_level_description": "Ak je povolené, akú úroveň záznamov použiť.", + "logging_settings": "Ukladanie záznamov", "machine_learning_clip_model": "Model CLIP", "machine_learning_clip_model_description": "Názov modelu CLIP je uvedený tu. Pamätajte, že pri zmene modelu je nutné znovu spustiť úlohu 'Inteligentné vyhľadávanie' pre všetky obrázky.", "machine_learning_duplicate_detection": "Detekcia duplikátov", "machine_learning_duplicate_detection_enabled": "Povoliť detekciu duplikátov", - "machine_learning_duplicate_detection_enabled_description": "Ak je vypnuté, presne identické položky budú stále deduplikované.", + "machine_learning_duplicate_detection_enabled_description": "Ak je vypnuté, úplne identické položky budú stále deduplikované.", "machine_learning_duplicate_detection_setting_description": "Použiť CLIP embeddings na identifikáciu pravdepodobných duplikátov", "machine_learning_enabled": "Povoliť strojové učenie", "machine_learning_enabled_description": "Ak je vypnuté, všetky funkcie strojového učenia (ML) budú vypnuté, bez ohľadu na nastavenia nižšie.", @@ -124,10 +126,10 @@ "machine_learning_facial_recognition_model_description": "Modely sú zoradené od najväčšieho po najmenší. Väčšie modely sú pomalšie a vyžadujú viac pamäte, ale poskytujú lepšie výsledky. Pamätajte, že po zmene modelu je potrebné znovu spustiť úlohu detekcie tvárí pre všetky obrázky.", "machine_learning_facial_recognition_setting": "Povoliť rozpoznávanie tvárí", "machine_learning_facial_recognition_setting_description": "Ak je vypnuté, obrázky nebudú spracované pre rozpoznávanie tvárí a nebudú sa zobrazovať v sekcii Ľudia na stránke Preskúmať.", - "machine_learning_max_detection_distance": "Maximálna detekčná odchylka", - "machine_learning_max_detection_distance_description": "Maximálna odchylka medzi dvoma obrázkami, aby boli považované za duplikáty, v rozsahu od 0.001 do 0.1. Vyššie hodnoty odhalia viac duplikátov, ale môžu viesť k falošným pozitívam.", - "machine_learning_max_recognition_distance": "Maximálna rozpoznávacia odchylka", - "machine_learning_max_recognition_distance_description": "Maximálna odchylka medzi dvoma tvárami, aby boli považované za rovnakú osobu, v rozsahu od 0 do 2. Zníženie tejto hodnoty môže zabrániť označeniu dvoch ľudí za tú istú osobu, zatiaľ čo zvýšenie môže zabrániť označeniu jednej osoby za dve rôzne osoby. Pamätajte, že je jednoduchšie spojiť dvoch ľudí ako rozdeliť jednu osobu na dve, takže je lepšie voliť nižší prah, ak je to možné.", + "machine_learning_max_detection_distance": "Maximálna detekčná odchýlka", + "machine_learning_max_detection_distance_description": "Maximálna odchýlka medzi dvoma obrázkami, aby boli považované za duplikáty, v rozsahu od 0.001 do 0.1. Vyššie hodnoty odhalia viac duplikátov, ale môžu viesť k falošným pozitívam.", + "machine_learning_max_recognition_distance": "Maximálna rozpoznávacia odchýlka", + "machine_learning_max_recognition_distance_description": "Maximálna odchýlka medzi dvomi tvárami, aby boli považované za rovnakú osobu, v rozsahu od 0 do 2. Zníženie tejto hodnoty môže zabrániť označeniu dvoch ľudí za tú istú osobu, zatiaľ čo zvýšenie môže zabrániť označeniu jednej osoby za dve rôzne osoby. Pamätajte, že je jednoduchšie spojiť dvoch ľudí ako rozdeliť jednu osobu na dve, takže je lepšie voliť nižší prah, ak je to možné.", "machine_learning_min_detection_score": "Minimálne detekčné skóre", "machine_learning_min_detection_score_description": "Minimálne skóre dôveryhodnosti pre detekciu tváre v rozsahu od 0 do 1. Nižšie hodnoty odhalia viac tvárí, ale môžu viesť k falošným pozitivním výsledkom.", "machine_learning_min_recognized_faces": "Minimum rozpoznaných tvárí", @@ -140,11 +142,11 @@ "machine_learning_smart_search_enabled_description": "Ak je vypnuté, obrázky nebudú spracované pre inteligentné vyhľadávanie.", "machine_learning_url_description": "URL adresa servera strojového učenia. Ak je zadaných viacero adries URL, každý server bude testovaný postupne, kým jeden z nich neodpovie úspešne, v poradí od prvého po posledný. Servery, ktoré neodpovedajú, budú dočasne ignorované, kým nebudú opäť online.", "manage_concurrency": "Správa súbežnosti", - "manage_log_settings": "Spravovať nastavenia logovania", + "manage_log_settings": "Spravovať nastavenia ukladania záznamov", "map_dark_style": "Tmavý štýl", "map_enable_description": "Povoliť funkcie mapy", - "map_gps_settings": "Mapa & GPS", - "map_gps_settings_description": "Správa nastavení máp a GPS reverzného geokódovania", + "map_gps_settings": "Mapa a nastavenia GPS", + "map_gps_settings_description": "Spravujte nastavenia mapy a GPS (reverzné geokódovanie)", "map_implications": "Táto funkčnosť sa spolieha na externý servis spracovania mapových dlaždíc (tiles.immich.cloud)", "map_light_style": "Svetlý štýl", "map_manage_reverse_geocoding_settings": "Správa nastavení Reverzného geokódovania", @@ -157,16 +159,30 @@ "memory_cleanup_job": "Vymazávanie spomienok", "memory_generate_job": "Vytváranie spomienok", "metadata_extraction_job": "Extrahovať metadáta", - "metadata_extraction_job_description": "Vytiahne metadáta z každej položky, ako napríklad GPS, tváre a rozlíšenie", + "metadata_extraction_job_description": "Získajte informácie o metadátach z každého média, ako sú GPS, tváre a rozlíšenie", "metadata_faces_import_setting": "Povoliť import tváre", - "metadata_faces_import_setting_description": "Importuj tváre z EXIF dát obrázkov a sidecar súborov", + "metadata_faces_import_setting_description": "Importovať tváre z EXIF údajov obrázka a pridružených súborov", "metadata_settings": "Metadáta", "metadata_settings_description": "Spravovať nastavenia metadát", "migration_job": "Migrácia", - "migration_job_description": "Migrácia miniatúr položiek a tvárí na najnovšiu štruktúru priečinkov", + "migration_job_description": "Presunúť miniatúry pre médiá a tváre do najnovšej štruktúry priečinkov", + "nightly_tasks_cluster_faces_setting_description": "Spustiť rozpoznávanie tváre na novo-zistených tvárach", + "nightly_tasks_cluster_new_faces_setting": "Zoskupiť nové tváre", + "nightly_tasks_database_cleanup_setting": "Úlohy čistenia databázy", + "nightly_tasks_database_cleanup_setting_description": "Vyčistiť databázu od starých, neplatných údajov", + "nightly_tasks_generate_memories_setting": "Vytvoriť spomienky", + "nightly_tasks_generate_memories_setting_description": "Vytvoriť nové spomienky z položiek", + "nightly_tasks_missing_thumbnails_setting": "Vytvoriť chýbajúce náhľady", + "nightly_tasks_missing_thumbnails_setting_description": "Zaradiť položky bez náhľadov do poradia na vytvorenie náhľadov", + "nightly_tasks_settings": "Nastavenia nočných úloh", + "nightly_tasks_settings_description": "Spravovať nočné úlohy", + "nightly_tasks_start_time_setting": "Čas spustenia", + "nightly_tasks_start_time_setting_description": "Čas, kedy server začne vykonávať nočné úlohy", + "nightly_tasks_sync_quota_usage_setting": "Synchronizovať využitie kvóty", + "nightly_tasks_sync_quota_usage_setting_description": "Aktualizovať kvótu úložiska používateľa na základe aktuálneho využitia", "no_paths_added": "Neboli pridané žiadne cesty", "no_pattern_added": "Nebol pridaný žiadny vzor", - "note_apply_storage_label_previous_assets": "Poznámka: Ak chcete použiť Štítkovanie úložiska na predtým nahrané aktíva, spustite príkaz", + "note_apply_storage_label_previous_assets": "Poznámka: Ak chcete použiť štítok úložiska na predtým nahrané položky, spustite príkaz", "note_cannot_be_changed_later": "POZNÁMKA: Toto nie je možné neskôr zmeniť!", "notification_email_from_address": "Z adresy", "notification_email_from_address_description": "E-mailová adresa odosielateľa, napríklad: \"Immich Foto Server \". Uistite sa, že používate adresu, z ktorej máte povolené odosielať e-maily.", @@ -189,19 +205,24 @@ "oauth_auto_register": "Automatická regristrácia", "oauth_auto_register_description": "Automatické zaregistrovanie nového požívateľa pri prihlásení pomocou OAuth", "oauth_button_text": "Text tlačítka", + "oauth_client_secret_description": "Vyžaduje sa, ak poskytovateľ OAuth nepodporuje PKCE (Proof Key for Code Exchange)", "oauth_enable_description": "Prihlásiť sa pomocou OAuth", "oauth_mobile_redirect_uri": "URI mobilného presmerovania", "oauth_mobile_redirect_uri_override": "Prepísanie URI mobilného presmerovania", "oauth_mobile_redirect_uri_override_description": "Povoľte, keď poskytovateľ protokolu OAuth nepovoľuje identifikátor URI pre mobilné zariadenia, napríklad ''{callback}''", + "oauth_role_claim": "Požiadavka na rolu", + "oauth_role_claim_description": "Automaticky udeliť prístup správcu na základe prítomnosti tejto požiadavky. Požiadavka môže mať príznak „user“ alebo „admin“.", "oauth_settings": "OAuth", "oauth_settings_description": "Spravovať nastavenia prihlásenia OAuth", "oauth_settings_more_details": "Pre viac informácii o tejto funkcii, prejdite na docs.", "oauth_storage_label_claim": "Nárokovať Štítok úložiska", - "oauth_storage_label_claim_description": "Automaticky nastaviť Štítok úložiska používateľa na hodnotu tohto nároku.", + "oauth_storage_label_claim_description": "Automaticky nastaviť štítok úložiska používateľa na hodnotu tohto nároku.", "oauth_storage_quota_claim": "Deklarácia kvóty úložiska", "oauth_storage_quota_claim_description": "Automaticky nastaviť kvótu úložiska používateľa na hodnotu tejto deklarácie.", "oauth_storage_quota_default": "Predvolený limit úložiska (GiB)", "oauth_storage_quota_default_description": "Kvóta v GiB, ktorá sa použije, ak nie je poskytnutá žiadna požiadavka.", + "oauth_timeout": "Časový limit požiadavky", + "oauth_timeout_description": "Časový limit pre požiadavky v milisekundách", "password_enable_description": "Prihlásiť sa pomocou emailu a hesla", "password_settings": "Prihlásenie cez heslo", "password_settings_description": "Spravovať nastavenia prihlásenia cez heslo", @@ -225,8 +246,8 @@ "server_settings_description": "Spravovať nastavenia servera", "server_welcome_message": "Uvítacia správa", "server_welcome_message_description": "Správa, ktorá sa zobrazí na prihlasovacej stránke.", - "sidecar_job": "Sidecar metadáta", - "sidecar_job_description": "Objavte alebo synchronizujte metadáta Sidecar zo súborového systému", + "sidecar_job": "Pridružené metadáta", + "sidecar_job_description": "Objavte alebo synchronizujte pridružené metadáta zo súborového systému", "slideshow_duration_description": "Čas zobrazenia obrázku v sekundách", "smart_search_job_description": "Spustite strojové učenie na médiách na podporu inteligentného vyhľadávania", "storage_template_date_time_description": "Časová pečiatka vytvorenia položky sa používa pre informácie o dátume a čase", @@ -239,13 +260,14 @@ "storage_template_migration_info": "Šablóna úložiska skonvertuje všetky prípony na malé písmená. Zmeny šablón sa budú vzťahovať iba na nové diela. Ak chcete šablónu spätne použiť na predtým nahrané médiá, spustite {job}.", "storage_template_migration_job": "Úloha migrácie šablóny úložiska", "storage_template_more_details": "Ďalšie podrobnosti o tejto funkcii nájdete v Šablóna úložiska a jej dôsledky", + "storage_template_onboarding_description_v2": "Ak je táto funkcia zapnutá, automaticky usporiada súbory na základe šablóny definovanej používateľom. Ďalšie informácie nájdete v dokumentácii.", "storage_template_path_length": "Približný limit dĺžky cesty: {length, number}/{limit, number}", "storage_template_settings": "Šablóna úložiska", "storage_template_settings_description": "Spravujte štruktúru priečinkov a názov súboru odovzdaného média", "storage_template_user_label": "{label} je Štítok úložiska používateľa", "system_settings": "Nastavenia systému", - "tag_cleanup_job": "Premazanie značiek", - "template_email_available_tags": "V šablóne môžeš použiť nasledujúce stítky: {tags}", + "tag_cleanup_job": "Prečistenie štítkov", + "template_email_available_tags": "V šablóne môžete použiť nasledujúce premenné: {tags}", "template_email_if_empty": "Ak nie je zadaná žiadna šablóna, bude použitá predvolená šablóna.", "template_email_invite_album": "Šablóna Pozvánky do albumu", "template_email_preview": "Ukážka", @@ -259,19 +281,19 @@ "theme_settings": "Motívy", "theme_settings_description": "Spravovať prispôsobenie webového rozhrania Immich", "thumbnail_generation_job": "Generovať Miniatúry", - "thumbnail_generation_job_description": "Generuje veľké, malé a rozostrení miniatúry pre každú položku, ako aj miniatúry pre každú osobu", + "thumbnail_generation_job_description": "Generujte veľké, malé a rozmazané miniatúry pre každú položku, ako aj miniatúry pre každú osobu", "transcoding_acceleration_api": "API pre akceleráciu", - "transcoding_acceleration_api_description": "Rozhranie API, ktoré bude interagovať s vaším zariadením s cieľom urýchliť prekódovanie. Toto nastavenie je „najlepšie úsilie“: pri zlyhaní sa vráti k softvérovému prekódovaniu. VP9 môže alebo nemusí fungovať v závislosti od vášho hardvéru.", + "transcoding_acceleration_api_description": "Rozhranie API, ktoré bude spolupracovať s vaším zariadením s cieľom urýchliť prekódovanie. Toto nastavenie je „najlepšie úsilie“: pri zlyhaní sa vráti k softvérovému prekódovaniu. VP9 môže alebo nemusí fungovať v závislosti od vášho hardvéru.", "transcoding_acceleration_nvenc": "NVENC (vyžaduje NVIDIA GPU)", "transcoding_acceleration_qsv": "Quick Sync (vyžaduje 7. generáciu Intel CPU alebo novšiu)", "transcoding_acceleration_rkmpp": "RKMPP (iba na Rockchip SOC)", "transcoding_acceleration_vaapi": "VAAPI", "transcoding_accepted_audio_codecs": "Akceptované zvukové kodeky", - "transcoding_accepted_audio_codecs_description": "Vyberte, ktoré zvukové kodeky nie je potrebné prekódovať. Používa sa len pre určité zásady prekódovania.", + "transcoding_accepted_audio_codecs_description": "Vyberte, ktoré zvukové kodeky nie je potrebné prekódovať. Používa sa len pre určité pravidlá prekódovania.", "transcoding_accepted_containers": "Akceptované kontajnery", - "transcoding_accepted_containers_description": "Vyberte, ktoré formáty kontajnerov nie je potrebné remuxovať na MP4. Používa sa len pre určité zásady prekódovania.", + "transcoding_accepted_containers_description": "Vyberte, ktoré formáty kontajnerov nie je potrebné remuxovať na MP4. Používa sa len pre určité pravidlá prekódovania.", "transcoding_accepted_video_codecs": "Akceptované video kodeky", - "transcoding_accepted_video_codecs_description": "Vyberte, ktoré video kodeky nie je potrebné prekódovať. Používa sa len pre určité zásady prekódovania.", + "transcoding_accepted_video_codecs_description": "Vyberte, ktoré video kodeky nie je potrebné prekódovať. Používa sa len pre určité pravidlá prekódovania.", "transcoding_advanced_options_description": "Možnosti, ktoré by väčšina používateľov nemala meniť", "transcoding_audio_codec": "Zvukový kodek", "transcoding_audio_codec_description": "Opus je najkvalitnejšia možnosť, ale má nižšiu kompatibilitu so starými zariadeniami alebo softvérom.", @@ -281,7 +303,7 @@ "transcoding_constant_quality_mode_description": "ICQ je lepšie ako CQP, ale niektoré zariadenia na hardvérovú akceleráciu tento režim nepodporujú. Nastavenie tejto možnosti uprednostní špecifikovaný režim pri použití kódovania založeného na kvalite. Ignorované spoločnosťou NVENC, pretože nepodporuje ICQ.", "transcoding_constant_rate_factor": "Faktor konštantnej rýchlosti (-crf)", "transcoding_constant_rate_factor_description": "Úroveň kvality videa. Typické hodnoty sú 23 pre H.264, 28 pre HEVC, 31 pre VP9 a 35 pre AV1. Nižšie je lepšie, ale vytvára väčšie súbory.", - "transcoding_disabled_description": "Neprekódujte žiadne videá, na niektorých klientoch môže prerušiť prehrávanie", + "transcoding_disabled_description": "Neprekódovať žiadne videá, na niektorých klientoch môže prerušiť prehrávanie", "transcoding_encoding_options": "Možnosti kódovania", "transcoding_encoding_options_description": "Nastavte kodeky, rozlíšenie, kvalitu a ďalšie možnosti pre kódované videá", "transcoding_hardware_acceleration": "Hardvérová akcelerácia", @@ -295,7 +317,7 @@ "transcoding_max_keyframe_interval": "Maximálny interval medzi kľúčovými snímkami", "transcoding_max_keyframe_interval_description": "Nastavuje maximálnu vzdialenosť medzi kľúčovými snímkami. Nižšie hodnoty zhoršujú účinnosť kompresie, ale zlepšujú časy vyhľadávania a môžu zlepšiť kvalitu v scénach s rýchlym pohybom. Hodnota 0 nastavuje túto hodnotu automaticky.", "transcoding_optimal_description": "Videá s vyšším ako cieľovým rozlíšením alebo videá, ktoré nie sú v prijateľnom formáte", - "transcoding_policy": "Politika prekódovania", + "transcoding_policy": "Pravidlá prekódovania", "transcoding_policy_description": "Nastavte, kedy bude video prekódované", "transcoding_preferred_hardware_device": "Uprednostňované hardvérové zariadenie", "transcoding_preferred_hardware_device_description": "Platí len pre VAAPI a QSV. Nastavuje uzol dri, ktorý sa používa na hardvérové prekódovanie.", @@ -304,7 +326,7 @@ "transcoding_reference_frames": "Referenčné snímky", "transcoding_reference_frames_description": "Počet snímok, na ktoré sa má odkazovať pri kompresii daného snímku. Vyššie hodnoty zvyšujú účinnosť kompresie, ale spomaľujú kódovanie. Hodnota 0 sa nastavuje automaticky.", "transcoding_required_description": "Iba videá, ktoré nie sú v prijatom formáte", - "transcoding_settings": "Transkódovania videa", + "transcoding_settings": "Nastavenia prekódovania videa", "transcoding_settings_description": "Spravujte, ktoré videá sa majú prekódovať a ako ich spracovať", "transcoding_target_resolution": "Cieľové rozlíšenie", "transcoding_target_resolution_description": "Vyššie rozlíšenia môžu zachovať viac detailov, ale ich kódovanie trvá dlhšie, majú väčšiu veľkosť súborov a môžu znížiť odozvu aplikácie.", @@ -314,22 +336,22 @@ "transcoding_threads_description": "Vyššie hodnoty vedú k rýchlejšiemu kódovaniu, ale ponechávajú serveru menej priestoru na spracovanie iných úloh počas aktivity. Táto hodnota by nemala byť väčšia ako počet jadier CPU. Maximalizuje využitie, ak je nastavená na hodnotu 0.", "transcoding_tone_mapping": "Tónové mapovanie", "transcoding_tone_mapping_description": "Snaží sa zachovať vzhľad videí HDR pri konverzii na SDR. Každý algoritmus robí rôzne kompromisy v oblasti farieb, detailov a jasu. Hable zachováva detaily, Mobius zachováva farby a Reinhard zachováva jas.", - "transcoding_transcode_policy": "Politika prekódovania", - "transcoding_transcode_policy_description": "Zásady, kedy sa má video prekódovať. Videá HDR sa vždy prekódujú (okrem prípadov, keď je prekódovanie vypnuté).", + "transcoding_transcode_policy": "Pravidlá prekódovania", + "transcoding_transcode_policy_description": "Pravidlá, kedy sa má video prekódovať. HDR videá sa prekódujú vždy (okrem prípadov, keď je prekódovanie vypnuté).", "transcoding_two_pass_encoding": "Dvojpriechodové kódovanie", - "transcoding_two_pass_encoding_setting_description": "Prekladajte v dvoch priechodoch, aby ste vytvorili lepšie zakódované videá. Keď je povolený maximálny dátový tok (vyžaduje sa na prácu s formátmi H.264 a HEVC), tento režim používa rozsah dátového toku na základe maximálneho dátového toku a ignoruje CRF. V prípade VP9 sa CRF môže použiť, ak je max bitrate vypnutý.", + "transcoding_two_pass_encoding_setting_description": "Prekódovať v dvoch fázach, aby sa vytvorili lepšie kódované videá. Keď je povolený maximálny dátový tok (vyžaduje sa na prácu s formátmi H.264 a HEVC), tento režim používa rozsah dátového toku na základe maximálneho dátového toku a ignoruje CRF. V prípade VP9 sa CRF môže použiť, ak je maximálny bitrate vypnutý.", "transcoding_video_codec": "Video kodek", - "transcoding_video_codec_description": "VP9 má vysokú účinnosť a kompatibilitu s webom, ale prekódovanie trvá dlhšie. HEVC má podobnú výkonnosť, ale nižšiu kompatibilitu s webom. H.264 je široko kompatibilný a rýchlo sa prekódováva, ale vytvára oveľa väčšie súbory. AV1 je najúčinnejší kodek, ale chýba mu podpora v starších zariadeniach.", + "transcoding_video_codec_description": "VP9 má vysokú účinnosť a kompatibilitu s webom, ale prekódovanie trvá dlhšie. HEVC má podobnú výkonnosť, ale nižšiu kompatibilitu s webom. H.264 je široko kompatibilný a rýchlo sa prekóduje, ale vytvára oveľa väčšie súbory. AV1 je najúčinnejší kodek, ale chýba mu podpora v starších zariadeniach.", "trash_enabled_description": "Povoliť funkcie koša", "trash_number_of_days": "Počet dní", - "trash_number_of_days_description": "Počet dní, počas ktorých sa má majetok ponechať v koši pred jeho trvalým odstránením", + "trash_number_of_days_description": "Počet dní, počas ktorých sa majú médiá ponechať v koši pred ich trvalým odstránením", "trash_settings": "Kôš", "trash_settings_description": "Spravovať nastavenia koša", "user_cleanup_job": "Premazanie používateľov", - "user_delete_delay": "Konto {user} a jeho médiá budú podľa plánu natrvalo vymazané za {delay, plural, one {# day} other {# days}}.", + "user_delete_delay": "Konto {user} a jeho médiá budú podľa plánu natrvalo vymazané za {delay, plural, one {# deň} few {# dni} other {# dní}}.", "user_delete_delay_settings": "Odstrániť oneskorenie", - "user_delete_delay_settings_description": "Počet dní po odstránení na trvalé vymazanie účtu a aktív používateľa. Úloha odstraňovania používateľov sa spúšťa o polnoci, aby sa skontrolovali používatelia, ktorí sú pripravení na odstránenie. Zmeny tohto nastavenia sa vyhodnotia pri ďalšom spustení.", - "user_delete_immediately": "Konto a médiá {user} budú zaradené do frontu na trvalé vymazanie okamžite.", + "user_delete_delay_settings_description": "Počet dní po odstránení na trvalé vymazanie účtu a médií používateľa. Úloha odstraňovania používateľov sa spúšťa o polnoci, aby sa skontrolovali používatelia, ktorí sú pripravení na odstránenie. Zmeny tohto nastavenia sa vyhodnotia pri ďalšom spustení.", + "user_delete_immediately": "Konto a médiá používateľa {user} budú zaradené do poradia na trvalé vymazanie okamžite.", "user_delete_immediately_checkbox": "Používateľ a médiá budú zaradení do frontu na okamžité vymazanie", "user_details": "Podrobnosti o používateľovi", "user_management": "Správa používateľov", @@ -351,11 +373,19 @@ "admin_password": "Administrátorské heslo", "administration": "Administrácia", "advanced": "Pokročilé", - "advanced_settings_log_level_title": "Úroveň logovania: {level}", - "advanced_settings_prefer_remote_subtitle": "Niektoré zariadenia sú extrémne pomalé pre načítavanie miniatúr z fotiek na zariadení. Povoľte toto nastavenie aby sa namiesto toho načítavali obrázky zo servera.", - "advanced_settings_prefer_remote_title": "Preferovať vzdialené obrázky", + "advanced_settings_beta_timeline_subtitle": "Vyskúšajte prostredie novej aplikácie", + "advanced_settings_beta_timeline_title": "Beta verzia časovej osi", + "advanced_settings_enable_alternate_media_filter_subtitle": "Túto možnosť použite na filtrovanie médií počas synchronizácie na základe alternatívnych kritérií. Túto možnosť vyskúšajte len vtedy, ak máte problémy s detekciou všetkých albumov v aplikácii.", + "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTÁLNE] Použiť alternatívny filter synchronizácie albumu zariadenia", + "advanced_settings_log_level_title": "Úroveň ukladania záznamov: {level}", + "advanced_settings_prefer_remote_subtitle": "V niektorých zariadeniach sa miniatúry z miestnych položiek načítavajú veľmi pomaly. Aktivovaním tohto nastavenia sa namiesto toho načítajú vzdialené obrázky.", + "advanced_settings_prefer_remote_title": "Uprednostniť vzdialené obrázky", + "advanced_settings_proxy_headers_subtitle": "Určite hlavičky proxy servera, ktoré by mal Immich posielať s každou požiadavkou na sieť", + "advanced_settings_proxy_headers_title": "Proxy hlavičky", "advanced_settings_self_signed_ssl_subtitle": "Preskakuje overovanie SSL certifikátom zo strany servera. Vyžaduje sa pre samo-podpísané certifikáty.", "advanced_settings_self_signed_ssl_title": "Povoliť samo-podpísané SSL certifikáty", + "advanced_settings_sync_remote_deletions_subtitle": "Automaticky vymazať alebo obnoviť položku na tomto zariadení, keď sa táto akcia vykoná na webe", + "advanced_settings_sync_remote_deletions_title": "Synchronizovať vzdialené vymazania [EXPERIMENTÁLNE]", "advanced_settings_tile_subtitle": "Pokročilé nastavenia používateľa", "advanced_settings_troubleshooting_subtitle": "Povoliť ďalšie funkcie pre opravu chýb", "advanced_settings_troubleshooting_title": "Oprava chýb", @@ -376,6 +406,7 @@ "album_options": "Nastavenia albumu", "album_remove_user": "Odstrániť používateľa?", "album_remove_user_confirmation": "Ste si istý, že chcete odstrániť používateľa {user}?", + "album_search_not_found": "Neboli nájdené žiadne albumy zodpovedajúce vášmu hľadaniu", "album_share_no_users": "Vyzerá to, že ste tento album zdieľali so všetkými používateľmi alebo nemáte žiadneho používateľa, s ktorým by ste ho mohli zdieľať.", "album_updated": "Album bol aktualizovaný", "album_updated_setting_description": "Obdržať e-mailové upozornenie, keď v zdieľanom albume pribudnú nové položky", @@ -391,15 +422,19 @@ "album_viewer_page_share_add_users": "Pridať používateľov", "album_with_link_access": "Umožnite komukoľvek s odkazom pozrieť si fotky a ľudí v tomto albume.", "albums": "Albumy", - "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Albumov}}", + "albums_count": "{count, plural, one {{count, number} album} few {{count, number} albumy} other {{count, number} albumov}}", + "albums_default_sort_order": "Predvolené poradie albumov", + "albums_default_sort_order_description": "Počiatočné poradie triedenia položiek pri vytváraní nových albumov.", + "albums_feature_description": "Zbierky médií, ktoré možno zdieľať s ostatnými používateľmi.", + "albums_on_device_count": "Albumy v zariadení ({count})", "all": "Všetko", "all_albums": "Všetky albumy", "all_people": "Všetci ľudia", "all_videos": "Všetky videa", "allow_dark_mode": "Povoliť tmavý režim", "allow_edits": "Povoliť úpravy", - "allow_public_user_to_download": "Povoľte verejnému používateľovi sťahovať", - "allow_public_user_to_upload": "Umožniť verejnému používateľovi nahrávať", + "allow_public_user_to_download": "Povoliť verejnému používateľovi stiahnutie", + "allow_public_user_to_upload": "Umožniť verejnému používateľovi nahrať", "alt_text_qr_code": "Obrázok QR kódu", "anti_clockwise": "Proti smeru hodinových ručičiek", "api_key": "API Klúč", @@ -409,9 +444,10 @@ "app_bar_signout_dialog_content": "Skutočne sa chcete odhlásiť?", "app_bar_signout_dialog_ok": "Áno", "app_bar_signout_dialog_title": "Odhlásiť sa", - "app_settings": "Nastavenia Aplikácie", + "app_settings": "Nastavenia aplikácie", "appears_in": "Vyskytuje sa v", - "archive": "Archivovať", + "archive": "Archív", + "archive_action_prompt": "{count} pridaných do archívu", "archive_or_unarchive_photo": "Archivácia alebo odarchivovanie fotografie", "archive_page_no_archived_assets": "Žiadne archivované médiá", "archive_page_title": "Archív ({count})", @@ -439,31 +475,45 @@ "asset_list_settings_title": "Fotografická mriežka", "asset_offline": "Médium je offline", "asset_offline_description": "Toto externý obsah sa už nenachádza na disku. Požiadajte o pomoc svojho správcu Immich.", + "asset_restored_successfully": "Položky boli úspešne obnovené", "asset_skipped": "Preskočené", "asset_skipped_in_trash": "V koši", "asset_uploaded": "Nahrané", "asset_uploading": "Nahráva sa…", - "asset_viewer_settings_title": "Zobrazovač položiek", + "asset_viewer_settings_subtitle": "Spravujte nastavenia prehliadača galérie", + "asset_viewer_settings_title": "Prehliadač médií", "assets": "Položky", "assets_added_count": "{count, plural, one {Pridaná # položka} few {Pridané # položky} other {Pridaných # položek}}", "assets_added_to_album_count": "Do albumu {count, plural, one {bola pridaná # položka} few {boli pridané # položky} other {bolo pridaných # položiek}}", - "assets_added_to_name_count": "{count, plural, one {Pridaná # položka} few {Pridané # položky} other {Pridaných # položiek}} do {hasName, select, true {alba {name}} other {nového albumu}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {položku} other {položiek}} nie je možné pridať do albumu", "assets_count": "{count, plural, one {# položka} few {# položky} other {# položiek}}", + "assets_deleted_permanently": "{count} položka(iek) natrvalo vymazaná(ých)", + "assets_deleted_permanently_from_server": "{count} položka(iek) natrvalo vymazaná(ých) zo servera Immich", + "assets_downloaded_failed": "{count, plural, one {Stiahnutý # súbor - {error} súbor zlyhal} few {Stiahnuté # súbory - {error} súbory zlyhali} other {Stiahnutých # súborov - {error} súborov zlyhalo}}", + "assets_downloaded_successfully": "{count, plural, one {# súbor bol úspešne stiahnutý} few {# súbory boli úspešne stiahnuté} other {# súborov bolo úspešne stiahnutých}}", "assets_moved_to_trash_count": "Do koša {count, plural, one {bola presunutá # položka} few {boli presunuté # položky} other {bolo presunutých # položiek}}", "assets_permanently_deleted_count": "Trvalo {count, plural, one {vymazaná # položka} few {vymazané # položky} other {vymazaných # položiek}}", "assets_removed_count": "{count, plural, one {Odstránená # položka} few {Odstránené # položky} other {Odstránených # položiek}}", + "assets_removed_permanently_from_device": "{count} položka(iek) natrvalo vymazaná(ých) z vášho zariadenia", "assets_restore_confirmation": "Naozaj chcete obnoviť všetky vyhodené položky? Túto akciu nie je možné vrátiť späť! Upozorňujeme, že týmto spôsobom nie je možné obnoviť žiadne offline položky.", "assets_restored_count": "{count, plural, one {Obnovená # položka} few {Obnovené # položky} other {Obnovených # položiek}}", - "assets_restored_successfully": "{count} medií úspešne obnovených", + "assets_restored_successfully": "{count} médií úspešne obnovených", + "assets_trashed": "{count} položka(iek) vyhodená(ých) do koša", "assets_trashed_count": "{count, plural, one {Odstránená # položka} few {Odstránené # položky} other {Odstránených # položiek}}", - "assets_were_part_of_album_count": "{count, plural, one {Položka bola} other {Položky boli}} súčasťou albumu", + "assets_trashed_from_server": "{count} položka(iek) vyhodená(ých) do koša zo servera Immich", + "assets_were_part_of_album_count": "{count, plural, one {Položka už bola} other {Položky už boli}} súčasťou albumu", "authorized_devices": "Autorizované zariadenia", + "automatic_endpoint_switching_subtitle": "Pripojiť sa lokálne prostredníctvom určeného pripojenia Wi-Fi, ak je k dispozícii, a používať alternatívne pripojenia inde", + "automatic_endpoint_switching_title": "Automatické prepínanie URL adresy", + "autoplay_slideshow": "Automatické prehrávanie prezentácie", "back": "Späť", "back_close_deselect": "Späť, zavrieť alebo zrušiť výber", + "background_location_permission": "Povolenie na určenie polohy na pozadí", + "background_location_permission_content": "Aby bolo možné prepínať siete pri spustení na pozadí, musí mať aplikácia Immich *vždy* presný prístup k polohe, aby mohla prečítať názov siete Wi-Fi", "backup_album_selection_page_albums_device": "Albumy v zariadení ({count})", "backup_album_selection_page_albums_tap": "Ťuknutím na položku ju zahrniete, dvojitým ťuknutím ju vylúčite", "backup_album_selection_page_assets_scatter": "Súbory môžu byť roztrúsené vo viacerých albumoch. To umožňuje zahrnúť alebo vylúčiť albumy počas procesu zálohovania.", - "backup_album_selection_page_select_albums": "Vybrané albumy", + "backup_album_selection_page_select_albums": "Vybrať albumy", "backup_album_selection_page_selection_info": "Informácie o výbere", "backup_album_selection_page_total_assets": "Celkový počet jedinečných súborov", "backup_all": "Všetko", @@ -485,7 +535,7 @@ "backup_controller_page_background_charging": "Len počas nabíjania", "backup_controller_page_background_configure_error": "Nepodarilo sa nakonfigurovať službu na pozadí", "backup_controller_page_background_delay": "Oneskorenie zálohovania nových médií: {duration}", - "backup_controller_page_background_description": "Povoľte službu na pozadí na automatické zálohovanie všetkých nových aktív bez nutnosti otvorenia aplikácie", + "backup_controller_page_background_description": "Povoľte službu na pozadí na automatické zálohovanie všetkých nových položiek bez nutnosti otvorenia aplikácie", "backup_controller_page_background_is_off": "Automatické zálohovanie na pozadí je vypnuté", "backup_controller_page_background_is_on": "Automatické zálohovanie na pozadí je zapnuté", "backup_controller_page_background_turn_off": "Vypnúť zálohovanie na pozadí", @@ -503,7 +553,7 @@ "backup_controller_page_info": "Informácie o zálohovaní", "backup_controller_page_none_selected": "Žiadne vybrané", "backup_controller_page_remainder": "Zostáva", - "backup_controller_page_remainder_sub": "Zostávajúce fotografie a videá, ktoré sa majú zálohovať z výbraných albumov", + "backup_controller_page_remainder_sub": "Zostávajúce fotografie a videá, ktoré sa majú zálohovať z výberu", "backup_controller_page_server_storage": "Serverové úložisko", "backup_controller_page_start_backup": "Spustiť zálohovanie", "backup_controller_page_status_off": "Automatické zálohovanie na popredí je vypnuté", @@ -521,22 +571,27 @@ "backup_manual_success": "Úspech", "backup_manual_title": "Stav nahrávania", "backup_options_page_title": "Možnosti zálohovania", - "backward": "Spätne", + "backup_setting_subtitle": "Spravovať nastavenia odosielania na pozadí a v popredí", + "backward": "Dozadu", + "biometric_auth_enabled": "Biometrické overovanie je povolené", + "biometric_locked_out": "Ste vymknutí z biometrického overovania", + "biometric_no_options": "Nie sú k dispozícii žiadne biometrické možnosti", + "biometric_not_available": "Biometrické overenie nie je v tomto zariadení k dispozícii", "birthdate_saved": "Dátum narodenia bol úspešne uložený", "birthdate_set_description": "Dátum narodenia sa používa na výpočet veku tejto osoby v čase fotografie.", "blurred_background": "Rozmazané pozadie", "bugs_and_feature_requests": "Chyby a požiadavky na funkcie", "build": "Zostava", "build_image": "Obraz zostavy", - "bulk_delete_duplicates_confirmation": "Naozaj chcete hromadne odstrániť {count, plural, one {# duplikátnu položku} few {# duplikáte položky} other {# duplikátnych položiek}}? Týmto sa zachová najväčšia položka z každej skupiny a všetky ostatné duplikáty sa natrvalo odstránia. Túto akciu nie je možné vrátiť späť!", - "bulk_keep_duplicates_confirmation": "Naozaj chceš ponechať {count, plural, one {# duplicitný súbor} other {# duplicitné súbory}}? Týmto sa vysporiadaš so všetkými duplicitnými skupinami bez mazania súborov.", - "bulk_trash_duplicates_confirmation": "Naozaj chcete hromadne vymazať {count, plural, one {# duplicitný súbor} other {# duplicitné súbory}}? Týmto si ponecháš z každej skupiny najväčší súbor a vymažeš všetky ostatné duplicitné súbory v skupine.", + "bulk_delete_duplicates_confirmation": "Naozaj chcete hromadne odstrániť {count, plural, one {# duplikátnu položku} few {# duplikátne položky} other {# duplikátnych položiek}}? Týmto sa zachová najväčšia položka z každej skupiny a všetky ostatné duplikáty sa natrvalo odstránia. Túto akciu nie je možné vrátiť späť!", + "bulk_keep_duplicates_confirmation": "Naozaj chcete ponechať {count, plural, one {# duplicitnú položku} few {# duplicitné položky} other {# duplicitných položiek}}? Týmto sa vyriešia všetky duplicitné skupiny bez toho, aby sa čokoľvek odstránilo.", + "bulk_trash_duplicates_confirmation": "Naozaj chcete hromadne vymazať {count, plural, one {# duplicitnú položku} few {# duplicitné položky} other {# duplicitných položiek}}? Týmto sa zachová najväčšia položka z každej skupiny a všetky ostatné duplicitné položky sa vyhodia.", "buy": "Kúpiť Immich", "cache_settings_clear_cache_button": "Vymazať vyrovnávaciu pamäť", "cache_settings_clear_cache_button_title": "Vymaže vyrovnávaciu pamäť aplikácie. To výrazne ovplyvní výkon aplikácie, kým sa vyrovnávacia pamäť neobnoví.", "cache_settings_duplicated_assets_clear_button": "VYČISTIŤ", "cache_settings_duplicated_assets_subtitle": "Fotky a videá ktoré sú na čiernej listine zvolené aplikáciou", - "cache_settings_duplicated_assets_title": "Duplikáty ({count})", + "cache_settings_duplicated_assets_title": "Duplicitné položky ({count})", "cache_settings_statistics_album": "Knižnica náhľadov", "cache_settings_statistics_full": "Kompletné fotografie", "cache_settings_statistics_shared": "Zdieľané náhľady albumov", @@ -552,9 +607,12 @@ "cancel": "Zrušiť", "cancel_search": "Zrušiť vyhľadávanie", "canceled": "Zrušené", + "canceling": "Ruší sa", "cannot_merge_people": "Nie je možné zlúčiť ľudí", "cannot_undo_this_action": "Túto akciu nemôžete vrátiť späť!", "cannot_update_the_description": "Popis nie je možné aktualizovať", + "cast": "Prenos (cast)", + "cast_description": "Nastavte dostupné ciele prenosu", "change_date": "Upraviť dátum", "change_description": "Zmeniť popis", "change_display_order": "Zmeniť poradie zobrazenia", @@ -572,7 +630,9 @@ "change_pin_code": "Zmeniť PIN kód", "change_your_password": "Zmeňte si heslo", "changed_visibility_successfully": "Viditeľnosť bola úspešne zmenená", + "check_corrupt_asset_backup": "Skontrolovať, či nie sú poškodené zálohy položiek", "check_corrupt_asset_backup_button": "Vykonať kontrolu", + "check_corrupt_asset_backup_description": "Spustiť túto kontrolu len cez Wi-Fi a po zálohovaní všetkých položiek. Tento postup môže trvať niekoľko minút.", "check_logs": "Skontrolovať logy", "choose_matching_people_to_merge": "Vyberte rovnakých ľudí na zlúčenie", "city": "Mesto", @@ -584,6 +644,11 @@ "client_cert_dialog_msg_confirm": "OK", "client_cert_enter_password": "Zadať heslo", "client_cert_import": "Importovať", + "client_cert_import_success_msg": "Certifikát klienta je naimportovaný", + "client_cert_invalid_msg": "Neplatný súbor certifikátu alebo nesprávne heslo", + "client_cert_remove_msg": "Certifikát klienta je odstránený", + "client_cert_subtitle": "Podporuje iba formát PKCS12 (.p12, .pfx). Importovanie/odstránenie certifikátu je k dispozícii len pred prihlásením", + "client_cert_title": "SSL certifikát klienta", "clockwise": "V smere hodinových ručičiek", "close": "Zatvoriť", "collapse": "Zbaliť", @@ -607,7 +672,8 @@ "confirm_tag_face": "Chcete označiť túto tvár ako {name}?", "confirm_tag_face_unnamed": "Chcete označiť túto tvár?", "connected_device": "Pripojené zariadenie", - "contain": "Obsiahnúť", + "connected_to": "Pripojené k", + "contain": "Prispôsobiť", "context": "Kontext", "continue": "Pokračovať", "control_bottom_app_bar_create_new_album": "Vytvoriť nový album", @@ -616,6 +682,7 @@ "control_bottom_app_bar_edit_location": "Upraviť polohu", "control_bottom_app_bar_edit_time": "Upraviť dátum a čas", "control_bottom_app_bar_share_link": "Zdieľať odkaz", + "control_bottom_app_bar_share_to": "Zdieľať cez", "control_bottom_app_bar_trash_from_immich": "Presunúť do koša", "copied_image_to_clipboard": "Obrázok skopírovaný do schránky.", "copied_to_clipboard": "Skopírované do schránky!", @@ -627,7 +694,7 @@ "copy_password": "Skopírovať heslo", "copy_to_clipboard": "Skopírovať do schránky", "country": "Krajina", - "cover": "Titulka", + "cover": "Vyplniť", "covers": "Dlaždice", "create": "Vytvoriť", "create_album": "Vytvoriť album", @@ -635,15 +702,15 @@ "create_library": "Vytvoriť knižnicu", "create_link": "Vytvoriť odkaz", "create_link_to_share": "Vytvoriť odkaz na zdieľanie", - "create_link_to_share_description": "Umožniť každému kto má odkaz zobraziť vybrané fotografie", + "create_link_to_share_description": "Umožniť každému, kto má odkaz, zobraziť vybrané fotografie", "create_new": "VYTVORIŤ NOVÉ", "create_new_person": "Vytvoriť novú osobu", "create_new_person_hint": "Priradiť vybrané položky novej osobe", "create_new_user": "Vytvorenie nového používateľa", "create_shared_album_page_share_add_assets": "Pridať položky", "create_shared_album_page_share_select_photos": "Vybrať fotografie", - "create_tag": "Vytvoriť značku", - "create_tag_description": "Vytvorenie nového štítku. Pre Vnorené štítky, prosím, zadaj celú cestu štítku, vrátane lomítok vpred.", + "create_tag": "Vytvoriť štítok", + "create_tag_description": "Vytvorte nový štítok. V prípade vnorených štítkov zadajte celú cestu k štítku vrátane lomiek.", "create_user": "Vytvoriť používateľa", "created": "Vytvorené", "created_at": "Vytvorené", @@ -657,6 +724,7 @@ "daily_title_text_date": "EEEE, d. MMMM", "daily_title_text_date_year": "EEEE, d. MMMM y", "dark": "Tmavý", + "dark_theme": "Prepnúť tmavú tému", "date_after": "Dátum po", "date_and_time": "Dátum a Čas", "date_before": "Dátum pred", @@ -669,14 +737,15 @@ "deduplication_criteria_2": "Počet EXIF údajov", "deduplication_info": "Info o deduplikácii", "deduplication_info_description": "Na automatický predvýber položiek a hromadné odstránenie duplicít, sa pozeráme do:", - "default_locale": "Predvolená Lokalizácia", - "default_locale_description": "Formátovanie dátumu a čísel podľa lokalizácie vášho prehliadača", + "default_locale": "Predvolené miestne nastavenie", + "default_locale_description": "Formátovanie dátumov a čísel na základe miestneho nastavenia prehliadača", "delete": "Vymazať", + "delete_action_prompt": "{count} natrvalo vymazaných", "delete_album": "Odstrániť album", "delete_api_key_prompt": "Naozaj chcete odstrániť tento API kľúč?", "delete_dialog_alert": "Tieto položky budú natrvalo odstránené z aplikácie Immich a z vášho zariadenia", "delete_dialog_alert_local": "Tieto položky budú permanentne vymazané z vašeho zariadenia, ale budú stále k dispozícií na serveri Immich", - "delete_dialog_alert_local_non_backed_up": "Niektoré položky nie sú zálohované na Immichi a budú permanentne vymazané z vášho zariadenia", + "delete_dialog_alert_local_non_backed_up": "Niektoré položky nie sú zálohované na Immich a budú permanentne odstránené z vášho zariadenia", "delete_dialog_alert_remote": "Tieto položky budú permanentne vymazané zo serveru Immich", "delete_dialog_ok_force": "Napriek tomu vymazať", "delete_dialog_title": "Vymazať natrvalo", @@ -685,12 +754,13 @@ "delete_key": "Odstrániť kľúč", "delete_library": "Vymazať knižnicu", "delete_link": "Odstrániť odkaz", + "delete_local_action_prompt": "{count} vymazané lokálne", "delete_local_dialog_ok_backed_up_only": "Vymazať len zálohované", "delete_local_dialog_ok_force": "Napriek tomu vymazať", "delete_others": "Vymazať ostatné", "delete_shared_link": "Odstrániť zdieľaný odkaz", "delete_shared_link_dialog_title": "Odstrániť zdieľaný odkaz", - "delete_tag": "Odstrániť označenie", + "delete_tag": "Odstrániť štítok", "delete_tag_confirmation_prompt": "Naozaj chcete odstrániť štítok menom {tagName}?", "delete_user": "Vymazať používateľa", "deleted_shared_link": "Vymazaný zdieľaný odkaz", @@ -698,6 +768,7 @@ "description": "Popis", "description_input_hint_text": "Pridať popis...", "description_input_submit_error": "Chyba pri aktualizovaní popisu, zobrazte log pre viac detailov", + "deselect_all": "Zrušiť výber všetkých", "details": "Podrobnosti", "direction": "Smer", "disabled": "Vypnuté", @@ -715,22 +786,27 @@ "documentation": "Dokumentácia", "done": "Hotovo", "download": "Stiahnuť", + "download_action_prompt": "Sťahuje sa {count} položiek", "download_canceled": "Stiahnutie zrušené", "download_complete": "Stiahnutie dokončené", + "download_enqueue": "Stiahnutie v poradí", "download_error": "Chyba sťahovania", "download_failed": "Stiahnutie sa nepodarilo", "download_finished": "Stiahnutie dokončené", "download_include_embedded_motion_videos": "Vložené videá", "download_include_embedded_motion_videos_description": "Zahrnúť videá vložené do pohyblivých fotiek ako samostatné súbory", + "download_notfound": "Stiahnutie nebolo nájdené", "download_paused": "Stiahnutie pozastavené", "download_settings": "Stiahnuť", "download_settings_description": "Spravovať nastavenia súvisiace so sťahovaním položiek", "download_started": "Sťahovanie spustené", + "download_sucess": "Stiahnutie úspešné", "download_sucess_android": "Médiá boli stiahnuté do DCIM/Immich", + "download_waiting_to_retry": "Čaká sa na opakovanie pokusu", "downloading": "Sťahuje sa", "downloading_asset_filename": "Sťahuje sa položka {filename}", "downloading_media": "Sťahovanie médií", - "drop_files_to_upload": "Hoď súbory kdekoľvek, nahrajú sa", + "drop_files_to_upload": "Umiestnite súbory kamkoľvek na nahratie", "duplicates": "Duplikáty", "duplicates_description": "Vysporiadať sa s každou skupinou tak, že sa duplicitné označia ako duplicitné", "duration": "Trvanie", @@ -748,10 +824,11 @@ "edit_key": "Upraviť kľúč", "edit_link": "Upraviť odkaz", "edit_location": "Upraviť polohu", + "edit_location_action_prompt": "{count} poloha upravená", "edit_location_dialog_title": "Poloha", "edit_name": "Upraviť meno", "edit_people": "Upraviť osoby", - "edit_tag": "Upraiť značku", + "edit_tag": "Upraviť štítok", "edit_title": "Upraviť názov", "edit_user": "Upraviť používateľa", "edited": "Upravené", @@ -766,23 +843,28 @@ "empty_trash": "Vyprázdniť kôš", "empty_trash_confirmation": "Naozaj chcete vyprázdniť kôš? Nenávratne sa vymažú všetky položky z Immich.\nTáto akcia sa nedá vrátiť!", "enable": "Aktivovať", + "enable_backup": "Povoliť zálohovanie", + "enable_biometric_auth_description": "Zadajte svoj PIN kód, aby ste povolili biometrické overenie", "enabled": "Aktivovaný", "end_date": "Koncový dátum", + "enqueued": "V poradí", "enter_wifi_name": "Zadajte názov Wi-Fi", "enter_your_pin_code": "Zadajte svoj PIN kód", "enter_your_pin_code_subtitle": "Zadaním kódu PIN získate prístup k zamknutému priečinku", "error": "Chyba", + "error_change_sort_album": "Nepodarilo sa zmeniť poradie albumu", "error_delete_face": "Chyba pri odstraňovaní tváre z položky", "error_loading_image": "Nepodarilo sa načítať obrázok", "error_saving_image": "Chyba: {error}", + "error_tag_face_bounding_box": "Chyba pri označovaní tváre - nemožno získať súradnice ohraničujúceho poľa", "error_title": "Chyba - niečo sa pokazilo", "errors": { "cannot_navigate_next_asset": "Nedokážem prejsť na ďaľšiu položku", "cannot_navigate_previous_asset": "Nedokážem prejsť na predošlú položku", "cant_apply_changes": "Nedokážem aplikovať zmeny", - "cant_change_activity": "Nodokážem {enabled, select, true {zakázať} other {povoliť}} aktivitu", + "cant_change_activity": "Nie je možné {enabled, select, true {zakázať} other {povoliť}} aktivitu", "cant_change_asset_favorite": "Nedokážem zmeniť obľúbenosť pre položku", - "cant_change_metadata_assets_count": "Nedokážem zmeniť metadáta pre {count, plural, one {# túto položku} other {# tieto položky}}", + "cant_change_metadata_assets_count": "Nie je možné zmeniť metadáta pre {count, plural, one {# položku} few {# položky} other {# položiek}}", "cant_get_faces": "Nedokážem získať tváre", "cant_get_number_of_comments": "Nedokážem získať počet komentárov", "cant_search_people": "Nedokážem hľadať osoby", @@ -802,10 +884,12 @@ "failed_to_keep_this_delete_others": "Nepodarilo sa ponechať túto položku a vymazať tie ostatné položky", "failed_to_load_asset": "Nepodarilo sa načítať položku", "failed_to_load_assets": "Nepodarilo sa načítať položky", + "failed_to_load_notifications": "Nepodarilo sa načítať oznámenia", "failed_to_load_people": "Nepodarilo sa načítať ľudí", "failed_to_remove_product_key": "Nepodarilo sa odstrániť produktový kľúč", "failed_to_stack_assets": "Nepodarilo sa zoskupiť položky", "failed_to_unstack_assets": "Nepodarilo sa rozdeliť položky", + "failed_to_update_notification_status": "Nepodarilo sa aktualizovať stav oznámenia", "import_path_already_exists": "Táto cesta importu už existuje.", "incorrect_email_or_password": "Nesprávny e-mail alebo heslo", "paths_validation_failed": "{paths, plural, one {# cesta zlyhala} few {# cesty zlyhali} other {# ciest zlyhalo}} pri validácii", @@ -826,14 +910,14 @@ "unable_to_change_favorite": "Nie je možné zmeniť obľúbené pre položku", "unable_to_change_location": "Nie je možné zmeniť polohu", "unable_to_change_password": "Nie je možné zmeniť heslo", - "unable_to_change_visibility": "Nie je možné zmeniť viditeľnosť pre {count, plural, one {# osobu} other {# ľudí}}", + "unable_to_change_visibility": "Nie je možné zmeniť viditeľnosť pre {count, plural, one {# osobu} few {# osoby} other {# osôb}}", "unable_to_complete_oauth_login": "Nemožno dokončiť prihlásenie cez OAuth", "unable_to_connect": "Nie je možné sa pripojiť", "unable_to_copy_to_clipboard": "Nie je možné kopírovať do schránky, overte si, že stránku navštevujete cez https", - "unable_to_create_admin_account": "Nie je možné vytvoriť admin účet", + "unable_to_create_admin_account": "Nie je možné vytvoriť účet správcu", "unable_to_create_api_key": "Nie je možné vytvoriť nový API Klúč", "unable_to_create_library": "Nie je možné vytvoriť knihovňu", - "unable_to_create_user": "Nie je možné vytvoriť uživateľa", + "unable_to_create_user": "Nie je možné vytvoriť používateľa", "unable_to_delete_album": "Nie je možné vymazať album", "unable_to_delete_asset": "Nie je možné vymazať položku", "unable_to_delete_assets": "Chyba pri odstraňovaní položiek", @@ -842,7 +926,7 @@ "unable_to_delete_shared_link": "Nie je možné vymazať zdieľaný odkaz", "unable_to_delete_user": "Nie je možné vymazať používateľa", "unable_to_download_files": "Nie je možné stiahnuť súbory", - "unable_to_edit_exclusion_pattern": "Nie je možné upravit vzorec vylúčenia", + "unable_to_edit_exclusion_pattern": "Nie je možné upraviť vzorec vylúčenia", "unable_to_edit_import_path": "Nie je možné upraviť cestu importu", "unable_to_empty_trash": "Nie je možné vyprázdniť kôš", "unable_to_enter_fullscreen": "Nie je možné prejsť do režimu celej obrazovky", @@ -900,6 +984,7 @@ "exif_bottom_sheet_location": "POLOHA", "exif_bottom_sheet_people": "ĽUDIA", "exif_bottom_sheet_person_add_person": "Pridať meno", + "exif_bottom_sheet_person_age_months": "Vek {months} mesiacov", "exif_bottom_sheet_person_age_year_months": "Vek 1 rok, {months} mesiacov", "exif_bottom_sheet_person_age_years": "Vek {years}", "exit_slideshow": "Opustiť Slideshow", @@ -908,7 +993,7 @@ "experimental_settings_new_asset_list_title": "Povolenie experimentálnej mriežky fotografií", "experimental_settings_subtitle": "Používajte na vlastné riziko!", "experimental_settings_title": "Experimentálne", - "expire_after": "Expiruje po", + "expire_after": "Platnosť vyprší", "expired": "Vypršalo", "expires_date": "Expiruje {date}", "explore": "Preskúmať", @@ -922,17 +1007,20 @@ "external_network_sheet_info": "Ak nie ste v preferovanej sieti Wi-Fi, aplikácia sa pripojí k serveru prostredníctvom prvej z nižšie uvedených adries URL, na ktorú sa dostane, počnúc zhora nadol", "face_unassigned": "Nepriradená", "failed": "Neúspešné", + "failed_to_authenticate": "Nepodarilo sa overiť", "failed_to_load_assets": "Nepodarilo sa načítať položky", + "failed_to_load_folder": "Nepodarilo sa načítať priečinok", "favorite": "Obľúbené", + "favorite_action_prompt": "{count} pridané do obľúbených", "favorite_or_unfavorite_photo": "Označiť fotku ako obľúbenú alebo neobľúbenú", "favorites": "Obľúbené", "favorites_page_no_favorites": "Žiadne obľúbené médiá", "feature_photo_updated": "Hlavný obrázok bol aktualizovaný", "features": "Funkcie", "features_setting_description": "Spravovať funkcie aplikácie", - "file_name": "Meno súboru", + "file_name": "Názov súboru", "file_name_or_extension": "Názov alebo prípona súboru", - "filename": "Meno súboru", + "filename": "Názov súboru", "filetype": "Typ súboru", "filter": "Filter", "filter_people": "Filtrovať ľudí", @@ -940,11 +1028,15 @@ "find_them_fast": "Nájdite ich rýchlejšie podľa mena", "fix_incorrect_match": "Opraviť nesprávnu zhodu", "folder": "Priečinok", + "folder_not_found": "Priečinok nebol nájdený", "folders": "Priečinky", - "folders_feature_description": "Prehliadanie zobrazenia priečinka s fotografiami a videami na súborovom systéme", + "folders_feature_description": "Prezeranie zobrazenia priečinkov fotografií a videí v systéme súborov", "forward": "Dopredu", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Táto funkcia načítava externé zdroje zo spoločnosti Google, aby mohla fungovať.", "general": "Všeobecné", "get_help": "Získať pomoc", + "get_wifiname_error": "Nepodarilo sa získať názov Wi-Fi siete. Uistite sa, že ste udelili potrebné oprávnenia a ste pripojení k sieti Wi-Fi", "getting_started": "Začíname", "go_back": "Vrátiť sa späť", "go_to_folder": "Prejsť do priečinka", @@ -959,6 +1051,12 @@ "haptic_feedback_switch": "Povoliť hmatovú odozvu", "haptic_feedback_title": "Hmatová odozva", "has_quota": "Má kvótu", + "header_settings_add_header_tip": "Pridať hlavičku", + "header_settings_field_validator_msg": "Hodnota nemôže byť prázdna", + "header_settings_header_name_input": "Názov hlavičky", + "header_settings_header_value_input": "Hodnota hlavičky", + "headers_settings_tile_subtitle": "Určite hlavičky proxy servera, ktoré má aplikácia posielať s každou požiadavkou na sieť", + "headers_settings_tile_title": "Vlastné hlavičky proxy servera", "hi_user": "Ahoj {name} ({email})", "hide_all_people": "Skryť všetky osoby", "hide_gallery": "Skryť galériu", @@ -974,25 +1072,31 @@ "home_page_archive_err_partner": "Nemožno archivovať partnerské položky, preskakuje sa", "home_page_building_timeline": "Vytváranie časovej osi", "home_page_delete_err_partner": "Nie je možné vymazať položky partnera, preskakuje sa", + "home_page_delete_remote_err_local": "Miestne položky vo výbere vzdialeného odstránenia, preskakuje sa", "home_page_favorite_err_local": "Zatiaľ nie je možné zaradiť lokálne média medzi obľúbené, preskakuje sa", "home_page_favorite_err_partner": "Na teraz nemôžete pridať partnerove médiá medzi obľúbené", "home_page_first_time_notice": "Ak aplikáciu používate prvýkrát, uistite sa, že ste si vybrali záložný album, aby sa na časovej osi mohli zobrazovať fotografie a videá", + "home_page_locked_error_local": "Nie je možné presunúť miestne položky do zamknutého priečinka, preskakuje sa", + "home_page_locked_error_partner": "Nie je možné presunúť partnerské položky do zamknutého priečinka, preskakuje sa", "home_page_share_err_local": "Nemožno zdieľať lokálne médiá pomocou odkazu", "home_page_upload_err_limit": "Naraz môžete nahrať len 30 médií, preskakuje sa", "host": "Hostiteľ", "hour": "Hodina", "id": "ID", + "ignore_icloud_photos": "Ignorovať fotky v službe iCloud", + "ignore_icloud_photos_description": "Fotografie uložené v službe iCloud sa nebudú odosielať na server Immich", "image": "Obrázok", "image_alt_text_date": "{isVideo, select, true {Video} other {Image}} nasnímané {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Image}} nasnímané s {person1} dňa {date}", "image_alt_text_date_2_people": "{isVideo, select, true {Video} other {Image}} nasnímané s {person1} a {person2} dňa {date}", "image_alt_text_date_3_people": "{isVideo, select, true {Video} other {Image}} nasnímané s {person1}, {person2} a {person3} dňa {date}", - "image_alt_text_date_4_or_more_people": "{isVideo, select, true {Video} other {Image}} nasnímané s {person1}, {person2} a {additionalCount, number} inými dňa {date}", - "image_alt_text_date_place": "{isVideo, select, true {Video} other {Obrázok}} nasnímané v {city}, {country} dňa {date}", - "image_alt_text_date_place_1_person": "{isVideo, select, true {Video} other {Obrázok}} zo dňa {date} v {city}, {country} s {person1}", - "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Obrázok}} v {city}, {country} s {person1} a {person2} zo dňa {date}", - "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Obrázok}} zo dňa {date} v {city}, {country} s {person1}, {person2} a {person3}", - "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} nasnímaný v {city}, {country} s {person1}, {person2} a {additionalCount, number} inými dňa {date}", + "image_alt_text_date_4_or_more_people": "{isVideo, select, true {Video nasnímané} other {Obrázok odfotený}} s osobami {person1}, {person2} a {additionalCount, number} inými dňa {date}", + "image_alt_text_date_place": "{isVideo, select, true {Video nasnímané} other {Obrázok odfotený}} v {city}, {country} dňa {date}", + "image_alt_text_date_place_1_person": "{isVideo, select, true {Video nasnímané} other {Obrázok odfotený}} dňa {date} v {city}, {country} s {person1}", + "image_alt_text_date_place_2_people": "{isVideo, select, true {Video nasnímané} other {Obrázok odfotený}} v {city}, {country} s {person1} a {person2} dňa {date}", + "image_alt_text_date_place_3_people": "{isVideo, select, true {Video nasnímané} other {Obrázok odfotený}} dňa {date} v {city}, {country} s {person1}, {person2} a {person3}", + "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video nasnímamé} other {Obrázok odfotený}} v {city}, {country} s {person1}, {person2} a {additionalCount, number} inými dňa {date}", + "image_saved_successfully": "Obrázok bol uložený", "image_viewer_page_state_provider_download_started": "Sťahovanie sa začalo", "image_viewer_page_state_provider_download_success": "Sťahovanie bolo úspešné", "image_viewer_page_state_provider_share_error": "Chyba zdieľania", @@ -1015,16 +1119,26 @@ "night_at_twoam": "Každú noc o 2:00" }, "invalid_date": "Neplatný dátum", + "invalid_date_format": "Neplatný formát dátumu", "invite_people": "Pozvať ľudí", "invite_to_album": "Pozvať do albumu", + "ios_debug_info_fetch_ran_at": "Načítanie prebehlo {dateTime}", + "ios_debug_info_last_sync_at": "Posledná synchronizácia {dateTime}", + "ios_debug_info_no_processes_queued": "Žiadne procesy nie sú v poradí na pozadí", + "ios_debug_info_no_sync_yet": "Zatiaľ nebola spustená žiadna úloha synchronizácie na pozadí", + "ios_debug_info_processes_queued": "{count, plural, one {{count} proces na pozadí v poradí} few {{count} procesy na pozadí v poradí} other {{count} procesov na pozadí v poradí}}", + "ios_debug_info_processing_ran_at": "Spracovanie prebehlo {dateTime}", "items_count": "{count, plural, one {# položka} few {# položky} other {# položiek}}", "jobs": "Úlohy", "keep": "Ponechať", "keep_all": "Ponechať všetko", "keep_this_delete_others": "Ponechať toto, odstrániť ostatné", - "kept_this_deleted_others": "Ponechá túto položku a odstráni {count, plural, one {# položku} other {# položiek}}", + "kept_this_deleted_others": "Ponechal túto položku a odstránil {count, plural, one {# položku} few {# položky} other {# položiek}}", "keyboard_shortcuts": "Klávesové skratky", "language": "Jazyk", + "language_no_results_subtitle": "Skúste upraviť hľadaný výraz", + "language_no_results_title": "Neboli nájdené žiadne jazyky", + "language_search_hint": "Vyhľadať jazyky...", "language_setting_description": "Vyberte preferovaný jazyk", "last_seen": "Naposledy videné", "latest_version": "Najnovšia verzia", @@ -1041,6 +1155,7 @@ "library_page_sort_created": "Najnovšie vytvorené", "library_page_sort_last_modified": "Naposledy upravené", "library_page_sort_title": "Podľa názvu albumu", + "licenses": "Licencie", "light": "Svetlý", "like_deleted": "Like odstránený", "link_motion_video": "Pripojiť pohyblivé video", @@ -1050,7 +1165,10 @@ "list": "Zoznam", "loading": "Načítavanie", "loading_search_results_failed": "Načítanie výsledkov hľadania sa nepodarilo", + "local_asset_cast_failed": "Nie je možné preniesť médium, ktoré nie je nahrané na serveri", "local_network": "Miestna sieť", + "local_network_sheet_info": "Pri použití zadanej siete Wi-Fi sa aplikácia pripojí k serveru prostredníctvom tejto URL adresy", + "location_permission": "Povolenie na určenie polohy", "location_permission_content": "Na používanie funkcie automatického prepínania potrebuje aplikácia Immich presné povolenie na určenie polohy, aby mohla prečítať názov aktuálnej Wi-Fi siete", "location_picker_choose_on_map": "Zvoľte na mape", "location_picker_latitude_error": "Zadajte platnú zemepisnú šírku", @@ -1061,6 +1179,7 @@ "locked_folder": "Zamknutý priečinok", "log_out": "Odhlásiť sa", "log_out_all_devices": "Odhlásiť všetky zariadenia", + "logged_in_as": "Prihlásený ako {user}", "logged_out_all_devices": "Všetky zariadenia odhlásené", "logged_out_device": "Zariadenie odhlásené", "login": "Prihlásenie", @@ -1069,7 +1188,7 @@ "login_form_back_button_text": "Späť", "login_form_email_hint": "tvojmail@email.com", "login_form_endpoint_hint": "http://ip-tvojho-servera:port", - "login_form_endpoint_url": "URL adresa servera", + "login_form_endpoint_url": "URL adresa koncového bodu servera", "login_form_err_http": "Prosím, uveďte http:// alebo https://", "login_form_err_invalid_email": "Neplatný e-mail", "login_form_err_invalid_url": "Neplatná URL adresa", @@ -1089,7 +1208,7 @@ "logout_all_device_confirmation": "Ste si istý, že sa chcete odhlásiť zo všetkých zariadení?", "logout_this_device_confirmation": "Ste si istý, že sa chcete odhlásiť z tohoto zariadenia?", "longitude": "Zemepisná dĺžka", - "look": "Zobrazenie", + "look": "Vzhľad", "loop_videos": "Opakovať videá", "loop_videos_description": "Povolí prehrávanie videí v slučke v detailnom zobrazení.", "main_branch_warning": "Používate vývojársku verziu; dôrazne odporúčame používať vydané verzie!", @@ -1127,6 +1246,9 @@ "map_settings_only_show_favorites": "Zobraziť iba obľúbené", "map_settings_theme_settings": "Téma mapy", "map_zoom_to_see_photos": "Oddiaľte priblíženie aby ste videli fotky", + "mark_all_as_read": "Označiť všetko ako prečítané", + "mark_as_read": "Označiť ako prečítané", + "marked_all_as_read": "Označené všetko ako prečítané", "matches": "Zhody", "media_type": "Typ média", "memories": "Spomienky", @@ -1143,7 +1265,7 @@ "merge_people_limit": "Zlúčiť môžete naraz najviac 5 tvárí", "merge_people_prompt": "Chcete zlúčiť týchto ľudí? Táto akcia sa nedá vrátiť.", "merge_people_successfully": "Zlúčenie ľudí sa podarilo", - "merged_people_count": "Zlúčení {count, plural, one {# človek} other {# ľudia}}", + "merged_people_count": "{count, plural, one {Zlúčená # osoba} few {Zlúčené # osoby} other {Zlúčených # osôb}}", "minimize": "Minimalizovať", "minute": "Minúta", "missing": "Chýbajúce", @@ -1153,15 +1275,20 @@ "more": "Viac", "move": "Presunúť", "move_off_locked_folder": "Presunúť zo zamknutého priečinka", + "move_to_lock_folder_action_prompt": "{count} pridaných do zamknutého priečinka", "move_to_locked_folder": "Presunúť do zamknutého priečinka", "move_to_locked_folder_confirmation": "Tieto fotografie a videá budú odstránené zo všetkých albumov a bude ich možné zobraziť len v zamknutom priečinku", + "moved_to_archive": "{count, plural, one {Presunutá # položka} few {Presunuté # položky} other {Presunutých # položiek}} do archívu", + "moved_to_library": "{count, plural, one {Presunutá # položka} few {Presunuté # položky} other {Presunutých # položiek}} do knižnice", "moved_to_trash": "Presunuté do koša", "multiselect_grid_edit_date_time_err_read_only": "Nemožno upraviť dátum položky len na čítanie, preskakujem", - "multiselect_grid_edit_gps_err_read_only": "Nie je možné upraviť polohu položky (položiek) len na čítanie, preskakuje sa", + "multiselect_grid_edit_gps_err_read_only": "Nie je možné upraviť polohu položky (položiek), ktorá je len na čítanie, preskakuje sa", "mute_memories": "Vyblednutie spomienok", "my_albums": "Moje albumy", "name": "Meno", "name_or_nickname": "Meno alebo prezývka", + "networking_settings": "Sieť", + "networking_subtitle": "Spravovať nastavenia koncového bodu servera", "never": "nikdy", "new_album": "Nový album", "new_api_key": "Nový API kľúč", @@ -1178,9 +1305,10 @@ "no_albums_message": "Vytvorí album na organizovanie fotiek a videí", "no_albums_with_name_yet": "Vyzerá, že zatiaľ nemáte album s týmto názvom.", "no_albums_yet": "Vyzerá, že zatiaľ nemáte žiadne albumy.", - "no_archived_assets_message": "Archivovať fotografie a videá, aby sa skryli zo zobrazenia Fotografie", + "no_archived_assets_message": "Archivujte fotografie a videá a skryte ich z vášho zobrazenia fotografií", "no_assets_message": "KLIKNITE A NAHRAJTE SVOJU PRVÚ FOTKU", "no_assets_to_show": "Žiadne položky", + "no_cast_devices_found": "Nenašli sa žiadne zariadenia na prenos", "no_duplicates_found": "Nenašli sa žiadne duplicity.", "no_exif_info_available": "Nie sú dostupné exif údaje", "no_explore_results_message": "Nahrajte viac fotiek na objavovanie vašej zbierky.", @@ -1188,16 +1316,20 @@ "no_libraries_message": "Vytvorí externú knižnicu na prezeranie fotiek a videí", "no_locked_photos_message": "Fotografie a videá v zamknutom priečinku sú skryté a nezobrazujú sa pri prehľadávaní alebo vyhľadávaní v knižnici.", "no_name": "Bez mena", + "no_notifications": "Žiadne oznámenia", + "no_people_found": "Nenašli sa žiadni vyhovujúci ľudia", "no_places": "Bez miesta", "no_results": "Žiadne výsledky", "no_results_description": "Skúste synonymum alebo všeobecnejší výraz", "no_shared_albums_message": "Vytvorí album na zdieľanie fotiek a videí s ľuďmi vo vašej sieti", "not_in_any_album": "Nie je v žiadnom albume", + "not_selected": "Nevybrané", "note_apply_storage_label_to_previously_uploaded assets": "Poznámka: Ak chcete použiť Štítok úložiska na predtým nahrané médiá, spustite príkaz", "notes": "Poznámky", + "nothing_here_yet": "Zatiaľ tu nič nie je", "notification_permission_dialog_content": "Ak chcete povoliť upozornenia, prejdite do Nastavenia a vyberte možnosť Povoliť.", - "notification_permission_list_tile_content": "Udeľte oprávnenie k aktivácii oznámení.", - "notification_permission_list_tile_enable_button": "Povoliť upozornenia", + "notification_permission_list_tile_content": "Udeľte povolenie na zapnutie oznámení.", + "notification_permission_list_tile_enable_button": "Povoliť oznámenia", "notification_permission_list_tile_title": "Povolenie oznámení", "notification_toggle_setting_description": "Povoliť e-mailové upozornenia", "notifications": "Oznámenia", @@ -1209,7 +1341,9 @@ "oldest_first": "Najstaršie prvé", "on_this_device": "Na tomto zariadení", "onboarding": "Na palube", + "onboarding_locale_description": "Vyberte požadovaný jazyk. Neskôr ho môžete zmeniť v nastaveniach.", "onboarding_privacy_description": "Nasledujúce (voliteľné) funkcie závisia na externých službách a kedykoľvek ich môžete vypnúť nastaveniach.", + "onboarding_server_welcome_description": "Nastavme vašu inštanciu pomocou bežných nastavení.", "onboarding_theme_description": "Vyberte farbu témy pre váš server. Môžete to aj neskôr zmeniť vo vašich nastaveniach.", "onboarding_user_welcome_description": "Začnime!", "onboarding_welcome_user": "Vitaj, {user}", @@ -1247,9 +1381,9 @@ "password_required": "Heslo je povinné", "password_reset_success": "Obnovenie hesla úspešné", "past_durations": { - "days": "{days, plural, one {Posledný deň} other {Posledných # dní }}", - "hours": "{hours, plural, one {Posledná hodina} other {Posledných # hodín}}", - "years": "{years, plural, one {Posledný rok} other {Posledné # roky}}" + "days": "{days, plural, one {Posledný deň} few {Posledné # dni} other {Posledných # dní }}", + "hours": "{hours, plural, one {Posledná hodina} few {Posledné # hodiny} other {Posledných # hodín}}", + "years": "{years, plural, one {Posledný rok} few {Posledné # roky} other {Posledných # rokov}}" }, "path": "Cesta", "pattern": "Vzor", @@ -1258,17 +1392,18 @@ "paused": "Pozastavené", "pending": "Čakajúce", "people": "Ľudia", - "people_edits_count": "{count, plural, one {Upravená # osoba} other {Upravených # ľudí}}", + "people_edits_count": "{count, plural, one {Upravená # osoba} few {Upravené # osoby} other {Upravených # osôb}}", "people_feature_description": "Prehliadanie fotiek a videí zoskupených podľa ľudí", - "people_sidebar_description": "Zobrazí odkaz na Ľudí v bočnom paneli", + "people_sidebar_description": "Zobraziť odkaz na Ľudí v bočnom paneli", "permanent_deletion_warning": "Varovanie o trvalom zmazaní", "permanent_deletion_warning_setting_description": "Zobraziť varovanie pri trvalom zmazaní položky", "permanently_delete": "Trvalo zmazať", - "permanently_delete_assets_count": "Navždy zmazať {count, plural, one {položku} other {položky}}", - "permanently_delete_assets_prompt": "Naozaj si prajete navždy zmazať {count, plural, one {túto položku?} other {týchto # položiek?}} Vymažú sa aj {count, plural, one {zo svojho albumu} other {zo svojich albumov}}.", + "permanently_delete_assets_count": "Natrvalo vymazať {count, plural, one {položku} few {položky} other {položiek}}", + "permanently_delete_assets_prompt": "Ste si istí, že chcete natrvalo vymazať {count, plural, one {túto položku?} few {tieto # položky?} other {týchto # položiek?}} Týmto sa odstráni aj {count, plural, one {z jej albumu} other {zo svojich albumov}}.", "permanently_deleted_asset": "Navždy odstránená položka", - "permanently_deleted_assets_count": "Navždy {count, plural, one {odstránená # položka} other {odstránené # položky}}", + "permanently_deleted_assets_count": "Natrvalo {count, plural, one {odstránená # položka} few {odstránené # položky} other {odstránených # položiek}}", "permission": "Povolenie", + "permission_empty": "Vaše povolenie by nemalo byť prázdne", "permission_onboarding_back": "Späť", "permission_onboarding_continue_anyway": "Pokračovať aj tak", "permission_onboarding_get_started": "Začať", @@ -1283,9 +1418,13 @@ "photo_shared_all_users": "Vyzerá, že zdieľate svoje fotky so všetkými používateľmi alebo nemáte žiadnych používateľov.", "photos": "Fotografie", "photos_and_videos": "Fotografie & Videa", - "photos_count": "{count, plural, one {{count, number} Fotka} other {{count, number} Fotiek}}", + "photos_count": "{count, plural, one {{count, number} fotka} few {{count, number} fotky} other {{count, number} fotiek}}", "photos_from_previous_years": "Fotky z minulých rokov", "pick_a_location": "Vyberte polohu", + "pin_code_changed_successfully": "Úspešne ste zmenili PIN kód", + "pin_code_reset_successfully": "Úspešne ste obnovili PIN kód", + "pin_code_setup_successfully": "Úspešne ste nastavili PIN kód", + "pin_verification": "Overenie PIN kódom", "place": "Miesto", "places": "Miesta", "places_count": "{count, plural, one {{count, number} miesto} few {{count, number} miesta} other {{count, number} miest}}", @@ -1293,17 +1432,22 @@ "play_memories": "Prehrať spomienky", "play_motion_photo": "Prehrať pohyblivú fotku", "play_or_pause_video": "Pustí alebo pozastaví video", + "please_auth_to_access": "Prosím, potvrďte overenie pre prístup", "port": "Port", - "preferences_settings_title": "Preferencie", + "preferences_settings_subtitle": "Spravujte predvoľby aplikácie", + "preferences_settings_title": "Predvoľby", "preset": "Prednastavenie", "preview": "Náhľad", "previous": "Predošlé", "previous_memory": "Predošlá spomienka", + "previous_or_next_day": "Deň dopredu/dozadu", + "previous_or_next_month": "Mesiac dopredu/dozadu", "previous_or_next_photo": "Fotka ďalšia/predošlá", + "previous_or_next_year": "Rok dopredu/dozadu", "primary": "Primárne", "privacy": "Súkromie", "profile": "Profil", - "profile_drawer_app_logs": "Logy", + "profile_drawer_app_logs": "Záznamy", "profile_drawer_client_out_of_date_major": "Mobilná aplikácia je zastaralá. Prosím aktualizujte na najnovšiu verziu.", "profile_drawer_client_out_of_date_minor": "Mobilná aplikácia je zastaralá. Prosím aktualizujte na najnovšiu verziu.", "profile_drawer_client_server_up_to_date": "Klient a server sú aktuálne", @@ -1327,7 +1471,7 @@ "purchase_button_select": "Vybrať", "purchase_failed_activation": "Aktivácia sa nepodarila! Prosím skontrolujte email či je správny kľúč produktu!", "purchase_individual_description_1": "Pre jednotlivca", - "purchase_individual_description_2": "Stav podporovateľa", + "purchase_individual_description_2": "Štatút podporovateľa", "purchase_individual_title": "Jednotlivec", "purchase_input_suggestion": "Máte produktový kľúč? Zadajte ho nižšie", "purchase_license_subtitle": "Kúpte si Immich a podporte neustály vývoj tejto služby", @@ -1343,22 +1487,24 @@ "purchase_remove_server_product_key": "Odstrániť produktový kľúč servera", "purchase_remove_server_product_key_prompt": "Naozaj chcete odstrániť produktový kľúč servera?", "purchase_server_description_1": "Pre celý server", - "purchase_server_description_2": "Stav podporovateľa", + "purchase_server_description_2": "Štatút podporovateľa", "purchase_server_title": "Server", "purchase_settings_server_activated": "Produktový kľúč servera spravuje admin", + "queue_status": "V poradí {count}/{total}", "rating": "Hodnotenie hviezdičkami", "rating_clear": "Vyčistiť hodnotenie", - "rating_count": "{count, plural, one {# hviezdička} other {# hviezdičky}}", - "rating_description": "Zobrazí EXIF hodnotenie v info paneli", + "rating_count": "{count, plural, one {# hviezdička} few {# hviezdičky} other {# hviezdičiek}}", + "rating_description": "Zobraziť EXIF hodnotenie v informačnom paneli", "reaction_options": "Možnosti reakcie", "read_changelog": "Prečítať zoznam zmien", "reassign": "Preradiť", - "reassigned_assets_to_existing_person": "Preradené {count, plural, one {# položka} other {# položky}} k {name, select, null {existujúcej osobe} other {{name}}}", - "reassigned_assets_to_new_person": "Preradené {count, plural, one {# položka} other {# položiek}} novej osobe", + "reassigned_assets_to_existing_person": "Opätovne {count, plural, one {priradená # položka} few {priradené # položky} other {priradených # položiek}} k {name, select, null {existujúcej osobe} other {{name}}}", + "reassigned_assets_to_new_person": "Opätovne {count, plural, one {priradená # položka} few {priradené # položky} other {priradených # položiek}} novej osobe", "reassing_hint": "Priradí zvolenú položku k existujúcej osobe", "recent": "Nedávne", "recent-albums": "Posledné albumy", "recent_searches": "Posledné vyhľadávania", + "recently_added": "Nedávno pridané", "recently_added_page_title": "Nedávno pridané", "recently_taken": "Nedávno nasnímané", "recently_taken_page_title": "Nedávno zhotovené", @@ -1367,20 +1513,22 @@ "refresh_faces": "Obnoviť tváre", "refresh_metadata": "Obnoviť metadáta", "refresh_thumbnails": "Obnoviť miniatúry", - "refreshed": "Aktualizované", + "refreshed": "Obnovené", "refreshes_every_file": "Znova prečíta všetky existujúce a nové súbory", "refreshing_encoded_video": "Obnovovanie enkódovaných videí", - "refreshing_faces": "Obnovovnie tvárí", + "refreshing_faces": "Obnovovanie tvárí", "refreshing_metadata": "Obnovovanie metadát", "regenerating_thumbnails": "Pregenerovanie náhľadov", "remove": "Odstrániť", - "remove_assets_album_confirmation": "Naozaj chcete odstrániť {count, plural, one {# položky} other {# položiek}} z albumu?", - "remove_assets_shared_link_confirmation": "Naozaj chcete odstrániť {count, plural, one {# položku} other {# položiek}} z tohoto zdieľaného odkazu?", + "remove_assets_album_confirmation": "Naozaj chcete odstrániť {count, plural, one {# položku} few {# položky} other {# položiek}} z albumu?", + "remove_assets_shared_link_confirmation": "Naozaj chcete odstrániť {count, plural, one {# položku} few {# položky} other {# položiek}} z tohoto zdieľaného odkazu?", "remove_assets_title": "Odstrániť položky?", "remove_custom_date_range": "Odstrániť vlastný rozsah dátumov", "remove_deleted_assets": "Odstrániť vymazané položky", "remove_from_album": "Odstrániť z albumu", + "remove_from_album_action_prompt": "{count} odstránené z albumu", "remove_from_favorites": "Odstrániť z obľúbených", + "remove_from_lock_folder_action_prompt": "{count} odobrané zo zamknutého priečinka", "remove_from_locked_folder": "Odstrániť zo zamknutého priečinka", "remove_from_locked_folder_confirmation": "Ste si istí, že chcete tieto fotografie a videá presunúť zo zamknutého priečinka? Budú viditeľné vo vašej knižnici.", "remove_from_shared_link": "Odstrániť zo zdieľaného odkazu", @@ -1395,7 +1543,7 @@ "removed_from_favorites_count": "{count, plural, other {Odstránených #}} z obľúbených", "removed_memory": "Odstránená pamäť", "removed_photo_from_memory": "Fotografia odstránená z pamäte", - "removed_tagged_assets": "Odstránená značka z {count, plural, one {# položky} other {# položiek}}", + "removed_tagged_assets": "Odstránený štítok z {count, plural, one {# položky} other {# položiek}}", "rename": "Premenovať", "repair": "Opraviť", "repair_no_results_message": "Nesledované a chýbajúce súbory sa zobrazia tu", @@ -1422,6 +1570,7 @@ "role_editor": "Editor", "role_viewer": "Divák", "save": "Uložiť", + "save_to_gallery": "Uložiť do galérie", "saved_api_key": "Uložený API Kľúč", "saved_profile": "Uložený profil", "saved_settings": "Uložené nastavenia", @@ -1445,9 +1594,11 @@ "search_filter_apply": "Použiť filter", "search_filter_camera_title": "Vyberte typ kamery", "search_filter_date": "Dátum", + "search_filter_date_interval": "{start} do {end}", "search_filter_date_title": "Vyberte rozsah dátumov", "search_filter_display_option_not_in_album": "Mimo albumu", "search_filter_display_options": "Možnosti zobrazenia", + "search_filter_filename": "Hľadať podľa názvu súboru", "search_filter_location": "Poloha", "search_filter_location_title": "Vyberte polohu", "search_filter_media_type": "Typ média", @@ -1490,6 +1641,7 @@ "select_album_cover": "Vyberte obal albumu", "select_all": "Vybrať všetko", "select_all_duplicates": "Vybrať všetky duplikáty", + "select_all_in": "Označiť všetky v {group}", "select_avatar_color": "Vyberte farbu avatara", "select_face": "Vyberte tvár", "select_featured_photo": "Vyberte náhľadovú fotku", @@ -1502,15 +1654,17 @@ "select_trash_all": "Vybrať zahodiť všetky", "select_user_for_sharing_page_err_album": "Nepodarilo sa vytvoriť album", "selected": "Vybrané", - "selected_count": "{count, plural, other {# vybrané}}", + "selected_count": "{count, plural, one {# vybraná} few {# vybrané} other {# vybraných}}", "send_message": "Odoslať správu", "send_welcome_email": "Odoslať uvítací e-mail", + "server_endpoint": "Koncový bod servera", "server_info_box_app_version": "Verzia aplikácie", - "server_info_box_server_url": "URL Serveru", + "server_info_box_server_url": "URL adresa servera", "server_offline": "Server je Offline", "server_online": "Server je Online", - "server_stats": "Serverové Štatistiky", - "server_version": "Verzia Servera", + "server_privacy": "Zásady ochrany osobných údajov servera", + "server_stats": "Štatistiky servera", + "server_version": "Verzia servera", "set": "Nastaviť", "set_as_album_cover": "Nastaviť ako obal albumu", "set_as_featured_photo": "Nastaviť ako hlavnú fotku", @@ -1518,14 +1672,16 @@ "set_date_of_birth": "Nastaviť dátum narodenia", "set_profile_picture": "Nastaviť profilový obrázok", "set_slideshow_to_fullscreen": "Nastaviť prezentáciu na celú obrazovku", - "setting_image_viewer_help": "Prehliadač detailov najprv načíta malú miniatúru, potom načíta náhľad strednej veľkosti (ak je povolený) a nakoniec načíta originál (ak je povolený).", + "set_stack_primary_asset": "Nastaviť ako primárnu položku", + "setting_image_viewer_help": "V detailnom prehliadači sa najprv načíta malá miniatúra, potom sa načíta stredne veľký náhľad (ak je povolený) a nakoniec sa načíta originál (ak je povolený).", "setting_image_viewer_original_subtitle": "Povolením umožníte načítať pôvodný obrázok v plnom rozlíšení (veľký!). Zakázaním znížite používania dát (v sieti, aj v dočasnej pamäte zariadenia).", "setting_image_viewer_original_title": "Načítať pôvodný obrázok", "setting_image_viewer_preview_subtitle": "Povolením umožníte načítať obrázok so stredným rozlíšením. Zakážte, ak chcete priamo načítať originál alebo použiť iba miniatúru.", "setting_image_viewer_preview_title": "Načítať náhľad obrázka", "setting_image_viewer_title": "Obrázky", "setting_languages_apply": "Použiť", - "setting_notifications_notify_failures_grace_period": "Oznámenie o zlyhaní zálohovania na pozadí: {duration}", + "setting_languages_subtitle": "Zmeniť jazyk aplikácie", + "setting_notifications_notify_failures_grace_period": "Upozorniť na zlyhanie zálohovania na pozadí: {duration}", "setting_notifications_notify_hours": "{count} hodín", "setting_notifications_notify_immediately": "okamžite", "setting_notifications_notify_minutes": "{count} minút", @@ -1533,14 +1689,18 @@ "setting_notifications_notify_seconds": "{count} sekúnd", "setting_notifications_single_progress_subtitle": "Podrobné informácie o priebehu nahrávania pre položku", "setting_notifications_single_progress_title": "Zobraziť priebeh detailov zálohovania na pozadí", - "setting_notifications_subtitle": "Prispôsobenie predvolieb oznámení", + "setting_notifications_subtitle": "Upravte svoje nastavenia oznámení", "setting_notifications_total_progress_subtitle": "Celkový priebeh nahrávania (nahraných/celkovo)", "setting_notifications_total_progress_title": "Zobraziť celkový priebeh zálohovania na pozadí", "setting_video_viewer_looping_title": "Opakovanie", + "setting_video_viewer_original_video_subtitle": "Pri streamovaní videa zo servera prehrať originál, aj keď je k dispozícii prekódované video. Môže to viesť k prerušovanému prehrávaniu videa. Videá dostupné lokálne sa prehrajú v pôvodnej kvalite bez ohľadu na toto nastavenie.", + "setting_video_viewer_original_video_title": "Vynútiť pôvodné video", "settings": "Nastavenia", "settings_require_restart": "Na použitie tohto nastavenia reštartujte Immich", "settings_saved": "Nastavenia boli uložené", + "setup_pin_code": "Nastavte si PIN kód", "share": "Zdieľať", + "share_action_prompt": "{count} položiek zdieľaných", "share_add_photos": "Pridať fotografie", "share_assets_selected": "{count} označených", "share_dialog_preparing": "Pripravujem...", @@ -1574,14 +1734,14 @@ "shared_link_edit_password_hint": "Zadajte heslo zdieľania", "shared_link_edit_submit_button": "Aktualizovať odkaz", "shared_link_error_server_url_fetch": "Nemožno nájsť URL severa", - "shared_link_expires_day": "Vyprší o {count} dní", + "shared_link_expires_day": "Vyprší o {count} deň", "shared_link_expires_days": "Vyprší o {count} dní", - "shared_link_expires_hour": "Vyprší o {count} hodín", + "shared_link_expires_hour": "Vyprší o {count} hodinu", "shared_link_expires_hours": "Vyprší o {count} hodín", - "shared_link_expires_minute": "Vyprší o {count} minút", + "shared_link_expires_minute": "Vyprší o {count} minútu", "shared_link_expires_minutes": "Vyprší o {count} minút", "shared_link_expires_never": "Nevyprší", - "shared_link_expires_second": "Vyprší o {count} sekúnd", + "shared_link_expires_second": "Vyprší o {count} sekundu", "shared_link_expires_seconds": "Vyprší o {count} sekúnd", "shared_link_individual_shared": "Individuálne zdieľané", "shared_link_info_chip_metadata": "EXIF", @@ -1589,7 +1749,8 @@ "shared_link_options": "Možnosti zdieľaných odkazov", "shared_links": "Zdieľané odkazy", "shared_links_description": "Zdieľanie fotografií a videí pomocou odkazu", - "shared_photos_and_videos_count": "{assetCount, plural, other {# zdieľané fotky a videá.}}", + "shared_photos_and_videos_count": "{assetCount, plural, few {# zdieľané fotky a videá.} other {# zdieľaných fotiek a videí.}}", + "shared_with_me": "Zdieľané so mnou", "shared_with_partner": "Zdieľané s {partner}", "sharing": "Zdieľanie", "sharing_enter_password": "Ak chcete zobraziť túto stránku, prosím, zadajte heslo.", @@ -1599,7 +1760,7 @@ "sharing_sidebar_description": "Zobraziť odkaz na Zdieľanie v bočnom paneli", "sharing_silver_appbar_create_shared_album": "Vytvoriť zdieľaný album", "sharing_silver_appbar_share_partner": "Zdieľať s partnerom", - "shift_to_permanent_delete": "stlačte ⇧ pre nemenné zmazanie pložiek", + "shift_to_permanent_delete": "stlačte ⇧ na trvalé vymazanie položky", "show_album_options": "Zobraziť možnosti albumu", "show_albums": "Zobraziť albumy", "show_all_people": "Zobraziť všetkých ľudí", @@ -1608,21 +1769,21 @@ "show_gallery": "Zobraziť galériu", "show_hidden_people": "Zobraziť skrytých ľudí", "show_in_timeline": "Zobraziť na časovej osi", - "show_in_timeline_setting_description": "Zobrazí fotky a videá tohoto používateľa na časovej osi", + "show_in_timeline_setting_description": "Zobraziť fotky a videá tohoto používateľa na vašej časovej osi", "show_keyboard_shortcuts": "Zobraziť klávesové skratky", "show_metadata": "Zobraziť metadáta", - "show_or_hide_info": "Zobrazí alebo skryje info", + "show_or_hide_info": "Zobraziť alebo skryť informácie", "show_password": "Zobraziť heslo", - "show_person_options": "Zobrazí možnosti osoby", - "show_progress_bar": "Zobrazí ukazovateľ priebehu", + "show_person_options": "Zobraziť možnosti osoby", + "show_progress_bar": "Zobraziť ukazovateľ priebehu", "show_search_options": "Zobraziť možnosti vyhľadávania", "show_shared_links": "Zobraziť zdieľané odkazy", - "show_slideshow_transition": "Zobrazí prechody v prezentácii", + "show_slideshow_transition": "Zobraziť prechody v prezentácii", "show_supporter_badge": "Odznak podporovateľa", "show_supporter_badge_description": "Zobraziť odznak podporovateľa", "shuffle": "Náhodné poradie", "sidebar": "Bočný panel", - "sidebar_display_description": "Zobrazí odkaz na pohľad v bočnom paneli", + "sidebar_display_description": "Zobraziť odkaz na zobrazenie v bočnom paneli", "sign_out": "Odhlásiť sa", "sign_up": "Registrovať", "size": "Veľkosť", @@ -1641,15 +1802,17 @@ "sort_title": "Názov", "source": "Zdroj", "stack": "Zoskupenie", + "stack_action_prompt": "{count} zoskupených", "stack_duplicates": "Zoskupiť duplicity", "stack_select_one_photo": "Vyberte jednu hlavnú fotku pre zoskupenie", "stack_selected_photos": "Zoskupiť vybraté fotky", - "stacked_assets_count": "{count, plural, one {Zoskupená # položka} other {Zoskupených # položiek}}", + "stacked_assets_count": "{count, plural, one {Zoskupená # položka} few {Zoskupené # položky} other {Zoskupených # položiek}}", "stacktrace": "Výpis zásobníku", "start": "Štart", "start_date": "Začiatočný dátum", "state": "Štát", "status": "Stav", + "stop_casting": "Zastaviť prenos", "stop_motion_photo": "Stopmotion fotka", "stop_photo_sharing": "Zastaviť zdieľanie vašich fotiek?", "stop_photo_sharing_description": "{partner} už nebude mať prístup k vašim fotkám.", @@ -1667,24 +1830,31 @@ "swap_merge_direction": "Vymeniť smer zlúčenia", "sync": "Synchronizovať", "sync_albums": "Synchronizovať albumy", - "tag": "Značka", - "tag_assets": "Pridať značku", - "tag_created": "Vytvorená značka: {tag}", - "tag_feature_description": "Prehliadanie fotiek a videá zoskupených podľa tematických značiek", - "tag_not_found_question": "Neviete nájsť značku? Vytvorte novú značku.", + "sync_albums_manual_subtitle": "Synchronizujte všetky nahrané videá a fotografie s vybranými záložnými albumami", + "sync_upload_album_setting_subtitle": "Vytvárajte a nahrávajte svoje fotografie a videá do vybraných albumov na Immich", + "tag": "Štítok", + "tag_assets": "Označiť položky", + "tag_created": "Vytvorený štítok: {tag}", + "tag_feature_description": "Prehliadanie fotiek a videá zoskupených podľa tematických štítkov", + "tag_not_found_question": "Neviete nájsť štítok? Vytvorte nový štítok.", "tag_people": "Označiť ľudí", - "tag_updated": "Upravená značka: {tag}", - "tagged_assets": "Značka priradená {count, plural, one {# položke} other {# položkám}}", + "tag_updated": "Upravený štítok: {tag}", + "tagged_assets": "Štítok priradený {count, plural, one {# položke} other {# položkám}}", "tags": "Štítky", "template": "Šablóna", "theme": "Téma", "theme_selection": "Výber témy", - "theme_selection_description": "Automaticky nastaví tému na svetlú alebo tmavú podľa systémových preferencií v prehliadači", - "theme_setting_asset_list_storage_indicator_title": "Zobraziť indikátor úložiska na dlaždiciach položiek", + "theme_selection_description": "Automaticky nastaví tému na svetlú alebo tmavú podľa systémových predvolieb v prehliadači", + "theme_setting_asset_list_storage_indicator_title": "Zobraziť indikátor úložiska na dlaždiciach médií", "theme_setting_asset_list_tiles_per_row_title": "Počet položiek na riadok ({count})", - "theme_setting_image_viewer_quality_subtitle": "Prispôsobenie kvality prehliadača detailov", + "theme_setting_colorful_interface_subtitle": "Použiť základnú farbu na plochy na pozadí.", + "theme_setting_colorful_interface_title": "Farebné rozhranie", + "theme_setting_image_viewer_quality_subtitle": "Upravte kvalitu detailného prehliadača obrázkov", "theme_setting_image_viewer_quality_title": "Kvalita prehliadača obrázkov", - "theme_setting_system_theme_switch": "Automaticky (podľa systemového nastavenia)", + "theme_setting_primary_color_subtitle": "Vyberte si farbu pre základné akcie a dôrazy.", + "theme_setting_primary_color_title": "Základná farba", + "theme_setting_system_primary_color_title": "Použiť systémovú farbu", + "theme_setting_system_theme_switch": "Automaticky (podľa systémového nastavenia)", "theme_setting_theme_subtitle": "Vyberte nastavenia témy aplikácie", "theme_setting_three_stage_loading_subtitle": "Trojstupňové načítanie môže zvýšiť výkonnosť načítania, ale vedie k výrazne vyššiemu zaťaženiu siete", "theme_setting_three_stage_loading_title": "Povolenie trojstupňového načítavania", @@ -1693,7 +1863,7 @@ "time_based_memories": "Časové spomienky", "timeline": "Časová os", "timezone": "Časové pásmo", - "to_archive": "Archivovať", + "to_archive": "Archív", "to_change_password": "Zmeniť heslo", "to_favorite": "Obľúbiť", "to_login": "Prihlásiť", @@ -1703,23 +1873,29 @@ "total": "Celkom", "total_usage": "Celkové využitie", "trash": "Kôš", + "trash_action_prompt": "{count} presunutých do koša", "trash_all": "Všetko do koša", "trash_count": "{count, number} do koša", "trash_delete_asset": "Položky do koša/odstrániť", + "trash_emptied": "Kôš vyprázdnený", "trash_no_results_message": "Vymazané fotografie a videá sa zobrazia tu.", "trash_page_delete_all": "Vymazať všetky", - "trash_page_empty_trash_dialog_content": "Skutočne chcete vyprázdniť kôš? Tieto položky budú permanentne odstránené z Immichu", + "trash_page_empty_trash_dialog_content": "Skutočne chcete vyprázdniť kôš? Tieto položky budú permanentne odstránené z aplikácie Immich", "trash_page_info": "Médiá v koši sa permanentne odstránia po {days} dňoch", "trash_page_no_assets": "Žiadne médiá v koši", "trash_page_restore_all": "Obnoviť všetky", - "trash_page_select_assets_btn": "Označiť médiá", + "trash_page_select_assets_btn": "Vybrať médiá", "trash_page_title": "Kôš ({count})", "trashed_items_will_be_permanently_deleted_after": "Položky v koši sa natrvalo vymažú po {days, plural, one {# dni} other {# dňoch}}.", "type": "Typ", + "unable_to_change_pin_code": "Nie je možné zmeniť PIN kód", + "unable_to_setup_pin_code": "Nie je možné nastaviť PIN kód", "unarchive": "Odarchivovať", + "unarchive_action_prompt": "{count} odstránené z archívu", "unarchived_count": "{count, plural, other {Odarchivovaných #}}", "undo": "Späť", "unfavorite": "Odznačiť ako obľúbené", + "unfavorite_action_prompt": "{count} odstránené z Obľúbených", "unhide_person": "Odkryť osobu", "unknown": "Neznáme", "unknown_country": "Neznámy štát", @@ -1727,7 +1903,7 @@ "unlimited": "Neobmedzené", "unlink_motion_video": "Odpojiť pohyblivé video", "unlink_oauth": "Odpojiť OAuth", - "unlinked_oauth_account": "Odpojiť OAuth účet", + "unlinked_oauth_account": "Odpojený OAuth účet", "unmute_memories": "Zrušenie stlmenia spomienok", "unnamed_album": "Nepomenovaný album", "unnamed_album_delete_confirmation": "Ste si istý, že chcete zmazať tento album?", @@ -1735,18 +1911,22 @@ "unsaved_change": "Neuložená zmena", "unselect_all": "Zrušiť výber všetkých", "unselect_all_duplicates": "Zrušiť výber všetkých duplicít", + "unselect_all_in": "Zrušiť výber všetkých v {group}", "unstack": "Odskupiť", - "unstacked_assets_count": "{count, plural, one {Rozložená # položka} few {Rozložené # položky} other {Rozložených # položiek}}", + "unstack_action_prompt": "{count} nezoskupených", + "unstacked_assets_count": "Zrušenie zoskupenia pre {count, plural, one {# položku} few {# položky} other {# položiek}}", + "untagged": "Bez štítku", "up_next": "To je všetko", "updated_at": "Aktualizované", "updated_password": "Heslo zmenené", "upload": "Nahrať", "upload_concurrency": "Súbežnosť nahrávania", + "upload_details": "Podrobnosti o nahrávaní", "upload_dialog_info": "Chcete zálohovať zvolené médiá na server?", "upload_dialog_title": "Nahrať médiá", - "upload_errors": "Nahrávanie ukončené s {count, plural, one {# chybou} other {# chybami}}, obnovte stránku aby sa zobrazili nové položky.", + "upload_errors": "Nahrávanie ukončené s {count, plural, one {# chybou} other {# chybami}}, obnovte stránku, aby sa zobrazili nové položky.", "upload_progress": "Ostáva {remaining, number} - Spracovaných {processed, number}/{total, number}", - "upload_skipped_duplicates": "{count, plural, one {Preskočená # duplicita} few {Preskočené # duplicity} other {Preskočených # duplicít}}", + "upload_skipped_duplicates": "{count, plural, one {Preskočená # duplicitná položka} few {Preskočené # duplicitné položky} other {Preskočených # duplicitných položiek}}", "upload_status_duplicates": "Duplikáty", "upload_status_errors": "Chyby", "upload_status_uploaded": "Nahrané", @@ -1755,6 +1935,8 @@ "uploading": "Nahrávanie", "url": "Odkaz URL", "usage": "Použitie", + "use_biometric": "Použiť biometrické údaje", + "use_current_connection": "použiť aktuálne pripojenie", "use_custom_date_range": "Použiť radšej vlastný rozsah dátumov", "user": "Používateľ", "user_has_been_deleted": "Tento používateľ bol vymazaný.", @@ -1762,16 +1944,19 @@ "user_liked": "Používateľovi {user} sa páči {type, select, photo {táto fotka} video {toto video} asset {táto položka} other {toto}}", "user_pin_code_settings": "PIN kód", "user_pin_code_settings_description": "Spravujte svoj PIN kód", + "user_privacy": "Ochrana osobných údajov používateľa", "user_purchase_settings": "Nákup", - "user_purchase_settings_description": "Správa vášho nákupu", + "user_purchase_settings_description": "Spravujte svoj nákup", "user_role_set": "Nastav {user} ako {role}", "user_usage_detail": "Podrobnosti o využívaní používateľmi", "user_usage_stats": "Štatistiky využitia účtu", "user_usage_stats_description": "Zobraziť štatistiky využitia účtu", "username": "Používateľské meno", "users": "Používatelia", + "users_added_to_album_count": "{count, plural, one {Pridaný # používateľ} few {Pridaní # používatelia} other {Pridaných # používateľov}} do albumu", "utilities": "Nástroje", "validate": "Validovať", + "validate_endpoint_error": "Zadajte prosím platnú URL adresu", "variables": "Premenné", "version": "Verzia", "version_announcement_closing": "Tvoj kamarát, Alex", @@ -1783,10 +1968,11 @@ "video_hover_setting_description": "Prehrá video náhľad keď kurzor myši prejde cez položku. Aj keď je vypnuté, prehrávanie sa môže spustiť nabehnutí cez ikonu Prehrať.", "videos": "Videá", "videos_count": "{count, plural, one {# Video} few {# Videá} other {# Videí}}", - "view": "Zobraziť", + "view": "Zobrazenie", "view_album": "Zobraziť Album", "view_all": "Zobraziť všetky", "view_all_users": "Zobraziť všetkých používateľov", + "view_details": "Zobraziť podrobnosti", "view_in_timeline": "Zobraziť v časovej osi", "view_link": "Zobraziť odkaz", "view_links": "Zobraziť odkazy", @@ -1799,7 +1985,7 @@ "viewer_remove_from_stack": "Odstrániť zo zoskupenia", "viewer_stack_use_as_main_asset": "Použiť ako hlavnú fotku", "viewer_unstack": "Odskupiť", - "visibility_changed": "Viditeľnosť zmenená pre {count, plural, one {# osobu} other {# ľudí}}", + "visibility_changed": "Viditeľnosť zmenená pre {count, plural, one {# osobu} few {# osoby} other {# osôb}}", "waiting": "Čaká", "warning": "Varovanie", "week": "Týždeň", diff --git a/i18n/sl.json b/i18n/sl.json index 5132d09fc8..c4409c717e 100644 --- a/i18n/sl.json +++ b/i18n/sl.json @@ -166,6 +166,20 @@ "metadata_settings_description": "Upravljanje nastavitev metapodatkov", "migration_job": "Migracija", "migration_job_description": "Preselite sličice za sredstva in obraze v najnovejšo strukturo map", + "nightly_tasks_cluster_faces_setting_description": "Zaženi prepoznavanje obrazov na novo zaznanih obrazih", + "nightly_tasks_cluster_new_faces_setting": "Združite nove obraze", + "nightly_tasks_database_cleanup_setting": "Naloge čiščenja baze podatkov", + "nightly_tasks_database_cleanup_setting_description": "Očistite stare, potekle podatke iz baze podatkov", + "nightly_tasks_generate_memories_setting": "Ustvarjajte spomine", + "nightly_tasks_generate_memories_setting_description": "Ustvarite nove spomine iz sredstev", + "nightly_tasks_missing_thumbnails_setting": "Ustvari manjkajoče sličice", + "nightly_tasks_missing_thumbnails_setting_description": "Sredstva brez sličic postavite v čakalno vrsto za ustvarjanje sličic", + "nightly_tasks_settings": "Nastavitve nočnih opravil", + "nightly_tasks_settings_description": "Upravljajte nočne naloge", + "nightly_tasks_start_time_setting": "Začetni čas", + "nightly_tasks_start_time_setting_description": "Čas, ko strežnik začne izvajati nočne naloge", + "nightly_tasks_sync_quota_usage_setting": "Poraba kvote za sinhronizacijo", + "nightly_tasks_sync_quota_usage_setting_description": "Posodobi kvoto shrambe uporabnikov glede na trenutno uporabo", "no_paths_added": "Ni dodanih poti", "no_pattern_added": "Brez dodanega vzorca", "note_apply_storage_label_previous_assets": "Opomba: Če želite oznako za shranjevanje uporabiti za predhodno naložena sredstva, zaženite", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "Mobilni preusmeritveni URI", "oauth_mobile_redirect_uri_override": "Preglasitev URI preusmeritve za mobilne naprave", "oauth_mobile_redirect_uri_override_description": "Omogoči, ko ponudnik OAuth ne dovoli mobilnega URI-ja, kot je ''{callback}''", + "oauth_role_claim": "Zahteva vloge", + "oauth_role_claim_description": "Samodejno dodeli skrbniški dostop na podlagi prisotnosti tega zahtevka. Zahtevek ima lahko »uporabnik« ali »skrbnik«.", "oauth_settings": "OAuth", "oauth_settings_description": "Upravljanje nastavitev prijave OAuth", "oauth_settings_more_details": "Za več podrobnosti o tej funkciji glejte dokumentacijo.", @@ -357,10 +373,12 @@ "admin_password": "Skrbniško geslo", "administration": "Administracija", "advanced": "Napredno", + "advanced_settings_beta_timeline_subtitle": "Preizkusite novo izkušnjo aplikacije", + "advanced_settings_beta_timeline_title": "Časovnica beta različice", "advanced_settings_enable_alternate_media_filter_subtitle": "Uporabite to možnost za filtriranje medijev med sinhronizacijo na podlagi alternativnih meril. To poskusite le, če imate težave z aplikacijo, ki zaznava vse albume.", "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTALNO] Uporabite alternativni filter za sinhronizacijo albuma v napravi", "advanced_settings_log_level_title": "Nivo dnevnika: {level}", - "advanced_settings_prefer_remote_subtitle": "Nekatere naprave zelo počasi nalagajo sličice iz sredstev v napravi. Aktivirajte to nastavitev, če želite namesto tega naložiti oddaljene slike.", + "advanced_settings_prefer_remote_subtitle": "Nekatere naprave zelo počasi nalagajo sličice iz lokalnih sredstev. Aktivirajte to nastavitev, če želite namesto tega naložiti oddaljene slike.", "advanced_settings_prefer_remote_title": "Uporabi raje oddaljene slike", "advanced_settings_proxy_headers_subtitle": "Določi proxy glavo, ki jo naj Immich pošlje ob vsaki mrežni zahtevi", "advanced_settings_proxy_headers_title": "Proxy glave", @@ -388,6 +406,7 @@ "album_options": "Možnosti albuma", "album_remove_user": "Odstrani uporabnika?", "album_remove_user_confirmation": "Ali ste prepričani, da želite odstraniti {user}?", + "album_search_not_found": "Ni najdenih albumov, ki bi ustrezali vašemu iskanju", "album_share_no_users": "Videti je, da ste ta album dali v skupno rabo z vsemi uporabniki ali pa nimate nobenega uporabnika, s katerim bi ga lahko delili.", "album_updated": "Album posodobljen", "album_updated_setting_description": "Prejmite e-poštno obvestilo, ko ima album v skupni rabi nova sredstva", @@ -407,6 +426,7 @@ "albums_default_sort_order": "Privzeti vrstni red razvrščanja albumov", "albums_default_sort_order_description": "Začetni vrstni red razvrščanja sredstev pri ustvarjanju novih albumov.", "albums_feature_description": "Zbirke sredstev, ki jih je mogoče deliti z drugimi uporabniki.", + "albums_on_device_count": "Albumi v napravi ({count})", "all": "Vse", "all_albums": "Vsi albumi", "all_people": "Vsi ljudje", @@ -427,6 +447,7 @@ "app_settings": "Nastavitve aplikacije", "appears_in": "Pojavi se v", "archive": "Arhiv", + "archive_action_prompt": "v arhiv je dodanih {count}", "archive_or_unarchive_photo": "Arhivirajte ali odstranite fotografijo iz arhiva", "archive_page_no_archived_assets": "Ni arhiviranih sredstev", "archive_page_title": "Arhiv ({count})", @@ -464,7 +485,6 @@ "assets": "Sredstva", "assets_added_count": "Dodano{count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", "assets_added_to_album_count": "Dodano {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} v album", - "assets_added_to_name_count": "Dodano {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} v {hasName, select, true {{name}} other {new album}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Sredstvo} two {Sredstvi} few {Sredstva} other {Sredstev}} ni mogoče dodati v album", "assets_count": "{count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", "assets_deleted_permanently": "trajno izrisana sredstva {count}", @@ -587,6 +607,7 @@ "cancel": "Prekliči", "cancel_search": "Prekliči iskanje", "canceled": "Preklicano", + "canceling": "Preklic", "cannot_merge_people": "Oseb ni mogoče združiti", "cannot_undo_this_action": "Tega dejanja ne morete razveljaviti!", "cannot_update_the_description": "Opisa ni mogoče posodobiti", @@ -703,7 +724,7 @@ "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Temno", - "darkTheme": "Preklopi na temno temo", + "dark_theme": "Preklopi temno temo", "date_after": "Datum po", "date_and_time": "Datum in ura", "date_before": "Datum pred", @@ -719,6 +740,7 @@ "default_locale": "Privzeti jezik", "default_locale_description": "Oblikujte datume in številke glede na lokalne nastavitve brskalnika", "delete": "Izbriši", + "delete_action_prompt": "trajno izbrisano {count}", "delete_album": "Izbriši album", "delete_api_key_prompt": "Ali ste prepričani, da želite izbrisati ta API ključ?", "delete_dialog_alert": "Ti elementi bodo trajno izbrisani iz Immicha in vaše naprave", @@ -732,6 +754,7 @@ "delete_key": "Izbriši ključ", "delete_library": "Izbriši knjižnico", "delete_link": "Izbriši povezavo", + "delete_local_action_prompt": "{count} izbrisano lokalno", "delete_local_dialog_ok_backed_up_only": "Izbriši samo kar je varnostno kopirano", "delete_local_dialog_ok_force": "Vseeno izbriši", "delete_others": "Izbriši ostale", @@ -745,6 +768,7 @@ "description": "Opis", "description_input_hint_text": "Dodaj opis ...", "description_input_submit_error": "Napaka pri posodabljanju opisa, preverite dnevnik za več podrobnosti", + "deselect_all": "Prekliči vse", "details": "Podrobnosti", "direction": "Usmeritev", "disabled": "Onemogočeno", @@ -762,6 +786,7 @@ "documentation": "Dokumentacija", "done": "Končano", "download": "Prenesi", + "download_action_prompt": "Prenašanje {count} sredstev", "download_canceled": "Prenos preklican", "download_complete": "Prenos končan", "download_enqueue": "Prenos v čakalni vrsti", @@ -799,6 +824,7 @@ "edit_key": "Uredi ključ", "edit_link": "Uredi povezavo", "edit_location": "Uredi lokacijo", + "edit_location_action_prompt": "urejenih {count} lokacij", "edit_location_dialog_title": "Lokacija", "edit_name": "Uredi ime", "edit_people": "Uredi osebe", @@ -817,6 +843,7 @@ "empty_trash": "Izprazni smetnjak", "empty_trash_confirmation": "Ste prepričani, da želite izprazniti smetnjak? S tem boste iz Immicha trajno odstranili vsa sredstva v smetnjaku.\nTega dejanja ne morete razveljaviti!", "enable": "Omogoči", + "enable_backup": "Omogoči varnostno kopiranje", "enable_biometric_auth_description": "Vnesite svojo PIN kodo, da omogočite biometrično preverjanje pristnosti", "enabled": "Omogočeno", "end_date": "Končni datum", @@ -984,6 +1011,7 @@ "failed_to_load_assets": "Sredstev ni bilo mogoče naložiti", "failed_to_load_folder": "Mape ni bilo mogoče naložiti", "favorite": "Priljubljen", + "favorite_action_prompt": "med priljubljene je dodanih {count}", "favorite_or_unfavorite_photo": "Priljubljena ali nepriljubljena fotografija", "favorites": "Priljubljene", "favorites_page_no_favorites": "Ni priljubljenih sredstev", @@ -1127,6 +1155,7 @@ "library_page_sort_created": "Nazadnje ustvarjeno", "library_page_sort_last_modified": "Nazadnje spremenjeno", "library_page_sort_title": "Naslov albuma", + "licenses": "Licence", "light": "Svetlo", "like_deleted": "Všeček izbrisan", "link_motion_video": "Povezava videa gibanja", @@ -1246,6 +1275,7 @@ "more": "Več", "move": "Premakni", "move_off_locked_folder": "Premakni iz zaklenjene mape", + "move_to_lock_folder_action_prompt": "V zaklenjeno mapo je bilo dodanih {count}", "move_to_locked_folder": "Premakni v zaklenjeno mapo", "move_to_locked_folder_confirmation": "Te fotografije in videoposnetki bodo odstranjeni iz vseh albumov in si jih bo mogoče ogledati le v zaklenjeni mapi", "moved_to_archive": "Premaknjeno {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} v arhiv", @@ -1460,6 +1490,7 @@ "purchase_server_description_2": "Status podpornika", "purchase_server_title": "Strežnik", "purchase_settings_server_activated": "Ključ izdelka strežnika upravlja skrbnik", + "queue_status": "Čakalna vrsta {count}/{total}", "rating": "Ocena z zvezdicami", "rating_clear": "Počisti oceno", "rating_count": "{count, plural, one {# zvezdica} two {# zvezdici} few {# zvezdice} other {# zvezdic}}", @@ -1495,7 +1526,9 @@ "remove_custom_date_range": "Odstrani časovno obdobje po meri", "remove_deleted_assets": "Odstrani izbrisana sredstva", "remove_from_album": "Odstrani iz albuma", + "remove_from_album_action_prompt": "{count} odstranjenih iz albuma", "remove_from_favorites": "Odstrani iz priljubljenih", + "remove_from_lock_folder_action_prompt": "iz zaklenjene mape je odstranjenih {count}", "remove_from_locked_folder": "Odstrani iz zaklenjene mape", "remove_from_locked_folder_confirmation": "Ali ste prepričani, da želite premakniti te fotografije in videoposnetke iz zaklenjene mape? Vidni bodo v vaši knjižnici.", "remove_from_shared_link": "Odstrani iz skupne povezave", @@ -1667,6 +1700,7 @@ "settings_saved": "Nastavitve shranjene", "setup_pin_code": "Nastavi PIN kodo", "share": "Deli", + "share_action_prompt": "Deljena sredstva {count}", "share_add_photos": "Dodaj fotografije", "share_assets_selected": "{count} izbrano", "share_dialog_preparing": "Priprava...", @@ -1768,6 +1802,7 @@ "sort_title": "Naslov", "source": "Vir", "stack": "Sklad", + "stack_action_prompt": "{count} naloženih", "stack_duplicates": "Nabor dvojnikov", "stack_select_one_photo": "Izberite eno glavno fotografijo za nabor", "stack_selected_photos": "Nabor izbranih fotografij", @@ -1838,6 +1873,7 @@ "total": "Skupno", "total_usage": "Skupna poraba", "trash": "Smetnjak", + "trash_action_prompt": "premaknjeno v smetnjak {count}", "trash_all": "Vse v smetnjak", "trash_count": "Smetnjak {count, number}", "trash_delete_asset": "V smetnjak/izbriši sredstvo", @@ -1855,9 +1891,11 @@ "unable_to_change_pin_code": "PIN kode ni mogoče spremeniti", "unable_to_setup_pin_code": "PIN kode ni mogoče nastaviti", "unarchive": "Odstrani iz arhiva", + "unarchive_action_prompt": "{count} odstranjenih iz arhiva", "unarchived_count": "{count, plural, other {nearhiviranih #}}", "undo": "Razveljavi", "unfavorite": "Odznači priljubljeno", + "unfavorite_action_prompt": "{count} odstranjenih iz priljubljenih", "unhide_person": "Prikaži osebo", "unknown": "Neznano", "unknown_country": "Neznana država", @@ -1875,12 +1913,15 @@ "unselect_all_duplicates": "Odznači vse dvojnike", "unselect_all_in": "Prekliči izbor vseh v {group}", "unstack": "Razklad", + "unstack_action_prompt": "{count} razloženih", "unstacked_assets_count": "Razloži {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", + "untagged": "Neoznačeno", "up_next": "Naslednja", "updated_at": "Posodobljeno", "updated_password": "Posodobljeno geslo", "upload": "Naloži", "upload_concurrency": "Sočasnost nalaganja", + "upload_details": "Podrobnosti o nalaganju", "upload_dialog_info": "Ali želite varnostno kopirati izbrana sredstva na strežnik?", "upload_dialog_title": "Naloži sredstvo", "upload_errors": "Nalaganje je končano s/z {count, plural, one {# napako} two {# napakama} other {# napakami}}, osvežite stran, da vidite nova sredstva za nalaganje.", @@ -1912,6 +1953,7 @@ "user_usage_stats_description": "Oglejte si statistiko uporabe računa", "username": "Uporabniško ime", "users": "Uporabniki", + "users_added_to_album_count": "V album {count, plural, one {je bil dodan # uporabnik} two {sta bila dodana # uporabnika} few {so bili dodani # uporabniki} other {je bilo dodanih # uporanikov}}", "utilities": "Pripomočki", "validate": "Potrdi", "validate_endpoint_error": "Vnesite veljaven URL", @@ -1930,6 +1972,7 @@ "view_album": "Ogled albuma", "view_all": "Poglej vse", "view_all_users": "Ogled vseh uporabnikov", + "view_details": "Ogled podrobnosti", "view_in_timeline": "Ogled na časovnici", "view_link": "Odpri povezavo", "view_links": "Ogled povezav", diff --git a/i18n/sr_Cyrl.json b/i18n/sr_Cyrl.json index b518bb39af..6216e671a8 100644 --- a/i18n/sr_Cyrl.json +++ b/i18n/sr_Cyrl.json @@ -458,7 +458,6 @@ "assets": "Записи", "assets_added_count": "Додато {count, plural, one {# датотека} other {# датотека}}", "assets_added_to_album_count": "Додато је {count, plural, one {# датотека} other {# датотека}} у албум", - "assets_added_to_name_count": "Додато {count, plural, one {# датотека} other {# датотеке}} у {hasName, select, true {{name}} other {нови албум}}", "assets_count": "{count, plural, one {# датотека} few {# датотеке} other {# датотека}}", "assets_deleted_permanently": "{count} елемената трајно обрисано", "assets_deleted_permanently_from_server": "{count} ресурс(а) трајно обрисан(а) са Immich сервера", diff --git a/i18n/sr_Latn.json b/i18n/sr_Latn.json index aa92e6da49..a757146861 100644 --- a/i18n/sr_Latn.json +++ b/i18n/sr_Latn.json @@ -456,7 +456,6 @@ "assets": "Zapisi", "assets_added_count": "Dodato {count, plural, one {# datoteka} other {# datoteka}}", "assets_added_to_album_count": "Dodato je {count, plural, one {# datoteka} other {# datoteka}} u album", - "assets_added_to_name_count": "Dodato {count, plural, one {# datoteka} other {# datoteke}} u {hasName, select, true {{name}} other {novi album}}", "assets_count": "{count, plural, one {# datoteka} few {# datoteke} other {# datoteka}}", "assets_deleted_permanently": "{count} elemenata trajno obrisano", "assets_deleted_permanently_from_server": "{count} resurs(a) trajno obrisan(a) sa Immich servera", diff --git a/i18n/sv.json b/i18n/sv.json index a4c08f8c22..00f5d95b66 100644 --- a/i18n/sv.json +++ b/i18n/sv.json @@ -22,6 +22,7 @@ "add_partner": "Lägg till partner", "add_path": "Lägg till sökväg", "add_photos": "Lägg till foton", + "add_tag": "Lägg till tagg", "add_to": "Lägg till i…", "add_to_album": "Lägg till i album", "add_to_album_bottom_sheet_added": "Tillagd till {album}", @@ -33,6 +34,7 @@ "added_to_favorites_count": "{count, number} tillagda till favoriter", "admin": { "add_exclusion_pattern_description": "Lägg till exkluderande mönster. Matchning med jokertecken *, ** samt ? stödjs. För att ignorera alla filer i samtliga mappar som heter \"Raw\", använd \"**/Raw/**\". För att ignorera alla filer som slutar med \".tif\", använd \"**/*.tif\". För att ignorera en absolut sökväg, använd \"/sökväg/att/ignorera/**\".", + "admin_user": "Adminanvändare", "asset_offline_description": "Denna externa bibliotekstillgång finns inte längre på disken och har flyttats till papperskorgen. Om filen flyttades inom biblioteket, kontrollera din tidslinje för den nya motsvarande tillgången. För att återställa denna tillgång, se till att filsökvägen nedan kan nås av Immich och skanna biblioteket.", "authentication_settings": "Autentiseringsinställningar", "authentication_settings_description": "Hantera lösenord, OAuth, och andra autentiseringsinställningar", @@ -169,7 +171,7 @@ "note_apply_storage_label_previous_assets": "Obs: Om du vill använda lagringsetiketten på tidigare uppladdade tillgångar kör du", "note_cannot_be_changed_later": "OBS: Detta kan inte ändras i efterhand!", "notification_email_from_address": "Från adress", - "notification_email_from_address_description": "Avsändarens epost, t.ex.: \"Immich Fotoserver \"", + "notification_email_from_address_description": "Avsändarens e-post, till exempel: \"Immich Fotoserver \". Säkerställ att du använder en adress som du har tillåtelse att skicka e-post från.", "notification_email_host_description": "Värd för epostservern (t.ex. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Ignorera certifikatfel", "notification_email_ignore_certificate_errors_description": "Ignorera valideringsfel för TLS-certifikat (rekommenderas ej)", @@ -194,6 +196,8 @@ "oauth_mobile_redirect_uri": "Telefonomdirigernings-URI", "oauth_mobile_redirect_uri_override": "Telefonomdirigerings-URI överrskridning", "oauth_mobile_redirect_uri_override_description": "Aktivera om OAuth-leverantören inte tillåter mobila URI:er, så som ''{callback}''", + "oauth_role_claim": "Rollanspråk", + "oauth_role_claim_description": "Bevilja administratörsåtkomst automatiskt baserat på förekomsten av detta påstående. Påståendet kan innehålla antingen 'user' eller 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Hantera OAuth-logininställningar", "oauth_settings_more_details": "För ytterligare detaljer om denna funktion, se dokumentationen.", @@ -202,7 +206,7 @@ "oauth_storage_quota_claim": "Användaranknuten lagringskvot", "oauth_storage_quota_claim_description": "Sätter automatiskt angiven användares lagringskvot.", "oauth_storage_quota_default": "Standardlagringskvot (GiB)", - "oauth_storage_quota_default_description": "Kvot i GiB som används när ingen fordran angetts (Ange 0 för obegränsad kvot).", + "oauth_storage_quota_default_description": "Kvot i GiB som används när ingen fordran angetts.", "oauth_timeout": "Begäran tog för lång tid", "oauth_timeout_description": "Timeout för förfrågningar i millisekunder", "password_enable_description": "Logga in med epost och lösenord", @@ -242,6 +246,7 @@ "storage_template_migration_info": "Lagringsmallen kommer konvertera alla filändelser till gemena bokstäver. Ändringar gäller endast för nya resurser, för att retoaktivt tillämpa mallen på befintliga resurser kör {job}.", "storage_template_migration_job": "Lagringsmall migreringsjobb", "storage_template_more_details": "För mer information om den här funktionen se Lagringsmall och dess konsekvenser", + "storage_template_onboarding_description_v2": "Denna funktion kommer när den är aktiverad att auto-organisera filer baserat på en användardefinierad mall. För mer information se dokumentationen.", "storage_template_path_length": "Uppskattad längdbegränsning på sökväg: {length, number}/{limit, number}", "storage_template_settings": "Lagringsmall", "storage_template_settings_description": "Hantera mappstruktur och filnamn för uppladdade resurser", @@ -401,6 +406,9 @@ "album_with_link_access": "Låt alla med länken se foton och personer i det här albumet.", "albums": "Album", "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Album}}", + "albums_default_sort_order": "Standard sorteringsordning för album", + "albums_default_sort_order_description": "Standard sorteringsordning för mediefiler vid skapande av nytt album.", + "albums_feature_description": "Samlingar av mediefiler som kan delas med andra användare.", "all": "Allt", "all_albums": "Alla album", "all_people": "Alla personer", @@ -421,6 +429,7 @@ "app_settings": "Appinställningar", "appears_in": "Visas i", "archive": "Arkiv", + "archive_action_prompt": "{count} adderade till Arkiv", "archive_or_unarchive_photo": "Arkivera eller oarkivera fotot", "archive_page_no_archived_assets": "Inga arkiverade objekt hittade", "archive_page_title": "Arkiv ({count})", @@ -458,7 +467,6 @@ "assets": "Objekt", "assets_added_count": "La till {count, plural, one {# asset} other {# assets}}", "assets_added_to_album_count": "Lade till {count, plural, one {# asset} other {# assets}} i albumet", - "assets_added_to_name_count": "Lade till {count, plural, one {# objekt} other {# objekt}} till {hasName, select, true {{name}} other {nytt album}}", "assets_count": "{count, plural, one {# objekt} other {# objekt}}", "assets_deleted_permanently": "{count} objekt har tagits bort permanent", "assets_deleted_permanently_from_server": "{count} objekt har tagits bort permanent från Immich-servern", @@ -639,6 +647,7 @@ "confirm_password": "Bekräfta lösenord", "confirm_tag_face": "Vill du tagga det här ansiktet som {name}?", "confirm_tag_face_unnamed": "Vill du tagga detta ansikte?", + "connected_device": "Ansluten enhet", "connected_to": "Ansluten till", "contain": "Anpassa", "context": "Sammanhang", @@ -691,7 +700,6 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Mörk", - "darkTheme": "Växla till mörkt tema", "date_after": "Datum efter", "date_and_time": "Datum och Tid", "date_before": "Datum före", @@ -707,6 +715,7 @@ "default_locale": "Standardplats", "default_locale_description": "Formatera datum och siffror baserat på din webbläsares språkversion", "delete": "Radera", + "delete_action_prompt": "{count, plural, one {# permanent raderad} other {# permanent raderade}}", "delete_album": "Ta bort album", "delete_api_key_prompt": "Är du säker på att du vill ta bort denna API-nyckel?", "delete_dialog_alert": "Dessa objekt kommer att raderas permanent från Immich och din enhet", @@ -739,6 +748,7 @@ "disallow_edits": "Tillåt inte redigeringar", "discord": "Discord", "discover": "Upptäck", + "discovered_devices": "Funna enheter", "dismiss_all_errors": "Avvisa alla fel", "dismiss_error": "Avvisa fel", "display_options": "Visningsalternativ", @@ -1083,6 +1093,7 @@ "invite_to_album": "Bjuder in till album", "ios_debug_info_last_sync_at": "Senaste synkning {dateTime}", "ios_debug_info_no_processes_queued": "Inga bakgrundsprocesser köade", + "ios_debug_info_processes_queued": "{count, plural, one {{count} bakgrundsprocess köad} other {{count} bakgrundsprocesser köade}}", "items_count": "{count, plural, one {# objekt} other {# objekt}}", "jobs": "Jobb", "keep": "Behåll", @@ -1091,6 +1102,8 @@ "kept_this_deleted_others": "Behåll denna tillgång och borttagna {count, plural, one {# asset} other {# assets}}", "keyboard_shortcuts": "Kortkommandon", "language": "Språk", + "language_no_results_title": "Inga språk funna", + "language_search_hint": "Sök språk…", "language_setting_description": "Välj önskat språk", "last_seen": "Senast sedd", "latest_version": "Senaste versionen", @@ -1129,6 +1142,7 @@ "locked_folder": "Låst Mapp", "log_out": "Logga ut", "log_out_all_devices": "Logga ut alla enheter", + "logged_in_as": "Inloggad som {user}", "logged_out_all_devices": "Loggat ut från alla enheter", "logged_out_device": "Loggat ut enheten", "login": "Logga in", @@ -1224,6 +1238,7 @@ "more": "Mer", "move": "Flytta", "move_off_locked_folder": "Flytta från låst mapp", + "move_to_lock_folder_action_prompt": "{count} adderades till låst mapp", "move_to_locked_folder": "Flytta till låst mapp", "move_to_locked_folder_confirmation": "Dessa foton och videor kommer tas bort från alla album och går endast se i låsta mappen", "moved_to_archive": "Flyttade {count, plural, one {# resurs} other {# assets}} till arkivet", @@ -1825,6 +1840,7 @@ "unable_to_setup_pin_code": "Kunde inte konfigurera pinkod", "unarchive": "Ångra arkivering", "unarchived_count": "{count, plural, one {# borttagen från arkiv} other {# borttagna från arkiv}}", + "undo": "Ångra", "unfavorite": "Avfavorisera", "unhide_person": "Visa person", "unknown": "Okänd", diff --git a/i18n/ta.json b/i18n/ta.json index ce0768982d..a91ea9d045 100644 --- a/i18n/ta.json +++ b/i18n/ta.json @@ -392,7 +392,6 @@ "assets": "சொத்துக்கள்", "assets_added_count": "சேர்க்கப்பட்டது {எண்ணிக்கை, பன்மை, ஒன்று {# சொத்து} மற்ற {# சொத்துக்கள்}}", "assets_added_to_album_count": "ஆல்பத்தில் {எண்ணிக்கை, பன்மை, ஒன்று {# சொத்து} மற்ற {# சொத்துக்கள்}}", - "assets_added_to_name_count": "சேர்க்கப்பட்டது {எண்ணிக்கை, பன்மை, ஒன்று {# சொத்து} மற்ற {# சொத்துக்கள்}} {hasname, தேர்ந்தெடுக்கவும், உண்மை { {name} } பிற {new album}}", "assets_count": "{எண்ணிக்கை, பன்மை, ஒன்று {# சொத்து} மற்ற {# சொத்துக்கள்}}", "assets_moved_to_trash_count": "நகர்த்தப்பட்டது {எண்ணிக்கை, பன்மை, ஒன்று {# சொத்து} மற்ற {# சொத்துக்கள்}}}", "assets_permanently_deleted_count": "நிரந்தரமாக நீக்கப்பட்டது {எண்ணிக்கை, பன்மை, ஒன்று {# சொத்து} பிற {# சொத்துக்கள்}}", diff --git a/i18n/te.json b/i18n/te.json index 8bd57bc3bc..4a2e938c9a 100644 --- a/i18n/te.json +++ b/i18n/te.json @@ -23,6 +23,8 @@ "add_photos": "ఫోటోలను జోడించండి", "add_to": "జోడించండి…", "add_to_album": "ఆల్బమ్‌కు జోడించండి", + "add_to_album_bottom_sheet_added": "ఆల్బమ్కు జోడించబడింది", + "add_to_album_bottom_sheet_already_exists": "ఆల్బమ్‌లో ఇప్పటికే జోడించబడింది", "add_to_shared_album": "భాగస్వామ్య ఆల్బమ్‌కు జోడించండి", "add_url": "URLని జోడించండి", "added_to_archive": "ఆర్కైవ్‌కి జోడించబడింది", @@ -30,17 +32,18 @@ "added_to_favorites_count": "ఇష్టమైన వాటికి {count, number} జోడించబడింది", "admin": { "add_exclusion_pattern_description": "మినహాయింపు నమూనాలను జోడించండి. *, ** మరియు ?ని ఉపయోగించి గ్లోబింగ్‌కు మద్దతు ఉంది. \"Raw\" అనే పేరు గల ఏదైనా డైరెక్టరీలోని అన్ని ఫైల్‌లను విస్మరించడానికి, \"**/Raw/**\"ని ఉపయోగించండి. \".tif\"తో ముగిసే అన్ని ఫైల్‌లను విస్మరించడానికి, \"**/*.tif\"ని ఉపయోగించండి. సంపూర్ణ మార్గాన్ని విస్మరించడానికి, \"/path/to/ignore/**\"ని ఉపయోగించండి.", + "admin_user": "నిర్వాహకుడు", "asset_offline_description": "ఈ బాహ్య లైబ్రరీ ఫైల్ ఇకపై డిస్క్‌లో కనుగొనబడలేదు మరియు ట్రాష్‌కు తరలించబడింది. ఫైల్ లైబ్రరీలోకి తరలించబడితే, కొత్త సంబంధిత ఫైల్ కోసం మీ టైమ్‌లైన్‌ను తనిఖీ చేయండి. ఈ ఫైల్ని పునరుద్ధరించడానికి, దయచేసి దిగువన ఉన్న ఫైల్ పాత్‌ను Immich యాక్సెస్ చేయగలదని నిర్ధారించుకోండి మరియు లైబ్రరీని స్కాన్ చేయండి.", "authentication_settings": "ప్రమాణీకరణ సెట్టింగ్‌లు", "authentication_settings_description": "పాస్‌వర్డ్, OAuth మరియు ఇతర ప్రమాణీకరణ సెట్టింగ్‌లను నిర్వహించండి", "authentication_settings_disable_all": "మీరు ఖచ్చితంగా అన్ని లాగిన్ పద్ధతులను నిలిపివేయాలనుకుంటున్నారా? లాగిన్ పూర్తిగా నిలిపివేయబడుతుంది.", "authentication_settings_reenable": "మళ్లీ ప్రారంబించటానికి, Server Commandని ఉపయోగించండి.", "background_task_job": "నేపథ్య పనులు", - "backup_database": "బ్యాకప్ డేటాబేస్", - "backup_database_enable_description": "డేటాబేస్ బ్యాకప్‌లను ప్రారంభించండి", - "backup_keep_last_amount": "ఉంచుకోవాల్సిన మునుపటి బ్యాకప్‌ల మొత్తం", - "backup_settings": "బ్యాకప్ సెట్టింగ్‌లు", - "backup_settings_description": "డేటాబేస్ బ్యాకప్ సెట్టింగ్‌లను నిర్వహించండి", + "backup_database": "డేటాబేస్ ను సృష్టించు", + "backup_database_enable_description": "డేటాబేస్ పడవెయ్యడాన్నీ ప్రారంభించండి", + "backup_keep_last_amount": "ఉంచుకోవాల్సిన మునుపటి పడవెయ్యడాల్లా మొత్తం", + "backup_settings": "డేటాబేస్ పడవెసే సెట్టింగ్‌లు", + "backup_settings_description": "డేటాబేస్ పడవెసే సెట్టింగ్‌లను నిర్వహించండి", "cleared_jobs": "దీని కోసం ఉద్యోగాలు క్లియర్ చేయబడ్డాయి: {job}", "config_set_by_file": "కాన్ఫిగరేషన్ ప్రస్తుతం కాన్ఫిగరేషన్ ఫైల్ ద్వారా సెట్ చేయబడింది", "confirm_delete_library": "మీరు ఖచ్చితంగా {library} లైబ్రరీని తొలగించాలనుకుంటున్నారా?", @@ -48,6 +51,7 @@ "confirm_email_below": "నిర్ధారించడానికి, క్రింద \"{email}\" టైప్ చేయండి", "confirm_reprocess_all_faces": "మీరు ఖచ్చితంగా అన్ని ముఖాలను రీప్రాసెస్ చేయాలనుకుంటున్నారా? ఇది పేరున్న వ్యక్తులను కూడా క్లియర్ చేస్తుంది.", "confirm_user_password_reset": "మీరు ఖచ్చితంగా {user} పాస్‌వర్డ్‌ని రీసెట్ చేయాలనుకుంటున్నారా?", + "confirm_user_pin_code_reset": "మీరు ఖచ్చితంగా {user} యొక్క పిన్ కోడ్ నీ రీసెట్ చేద్దామనిఅనుకుంటున్నారా?", "create_job": "పనిని సృష్టించండి", "cron_expression": "క్రాన్ వ్యక్తీకరణ", "cron_expression_description": "క్రాన్ ఫార్మాట్ ఉపయోగించి స్కానింగ్ విరామాన్ని సెట్ చేయండి. మరిన్ని వివరాల కోసం దయచేసి ఉదా. క్రోంటాబ్ గురు చూడండి", @@ -402,7 +406,6 @@ "assets": "ఆస్తులు", "assets_added_count": "జోడించబడినవి {count, plural, one {# ఆస్తి} other {# ఆస్తులు}}", "assets_added_to_album_count": "{count, plural, one {# ఆస్తి} other {# ఆస్తులు}} ఆల్బమ్‌కి జోడించబడినవి", - "assets_added_to_name_count": "{count, plural, one {# ఆస్తి} other {# ఆస్తులు}} {hasName, select, true {{name}} other {కొత్త ఆల్బమ్}}కి జోడించబడినవి", "assets_count": "{count, plural, one {# ఆస్తి} other {# ఆస్తులు}}", "assets_moved_to_trash_count": "{count, plural, one {# ఆస్తి} other {# ఆస్తులు}} చెత్తబుట్టలోకి తరలించారు", "assets_permanently_deleted_count": "{count, plural, one {# ఆస్తి} other {# ఆస్తులు}} శాశ్వతంగా తొలగించబడినవి", diff --git a/i18n/th.json b/i18n/th.json index 13d9514d63..3cce9afd1c 100644 --- a/i18n/th.json +++ b/i18n/th.json @@ -14,14 +14,15 @@ "add_a_location": "เพิ่มตำแหน่ง", "add_a_name": "เพิ่มชื่อ", "add_a_title": "เพิ่มหัวข้อ", - "add_endpoint": "เพิ่มอุปกรณ์ปลายทาง", + "add_endpoint": "เพิ่มปลายทาง", "add_exclusion_pattern": "เพิ่มข้อยกเว้น", "add_import_path": "เพิ่มเส้นทางนำเข้า", "add_location": "เพิ่มตำแหน่ง", "add_more_users": "เพิ่มผู้ใช้งาน", - "add_partner": "เพิ่มพันธมิตร", + "add_partner": "เพิ่มคู่หู", "add_path": "เพิ่มพาทที่ตั้ง", "add_photos": "เพิ่มรูปภาพ", + "add_tag": "เพิ่มแท็ก", "add_to": "เพิ่มไปยัง …", "add_to_album": "เพิ่มไปอัลบั้ม", "add_to_album_bottom_sheet_added": "เพิ่มไปยัง {album}", @@ -33,7 +34,8 @@ "added_to_favorites_count": "{count, number} รูปถูกเพิ่มเข้ารายการโปรด", "admin": { "add_exclusion_pattern_description": "เพิ่มรูปแบบข้อยกเว้น รองรับการใช้ *, ** และ ? หากต้องการละเว้นไฟล์ทั้งหมดในไดเร็กทอรีที่ชื่อว่า \"Raw\" ให้ใช้ \"**/Raw/**\" ถ้าต้องการละเว้นไฟล์ทั้งหมดที่ลงท้ายด้วย \".tif\" ให้ใช้ \"**/*.tif\" ถ้าต้องการละเว้นพาธที่เริ่มจากไดเรกทอรีบนสุดให้ใช้ \"/พาธ/ที่ต้องการ/ละเว้น/**\"", - "asset_offline_description": "ไฟล์ Asset ของไลบรารีภายนอกนี้ไม่พบในดิสก์แล้ว และถูกย้ายไปที่ถังขยะ หากไฟล์ถูกย้ายภายในไลบรารี โปรดตรวจสอบไทม์ไลน์ของคุณเพื่อหาแอสเซ็ตที่เกี่ยวข้องใหม่ หากต้องการกู้คืน Asset นี้ โปรดตรวจสอบให้แน่ใจว่า Immich สามารถเข้าถึงเส้นทางไฟล์ด้านล่างได้ และทำการสแกนไลบรารีอีกครั้ง", + "admin_user": "ผู้ดูแล", + "asset_offline_description": "ไม่พบไฟล์สื่อของไลบรารีภายนอกนี้ในดิสก์ และถูกย้ายไปที่ถังขยะแล้ว หากไฟล์ถูกย้ายภายในไลบรารี โปรดตรวจสอบไทม์ไลน์ของคุณเพื่อหาสื่อที่เกี่ยวข้องใหม่ หากต้องการกู้คืนสื่อนี้ โปรดตรวจสอบว่า Immich สามารถเข้าถึงไฟล์ด้านล่างได้ และทำการสแกนไลบรารีอีกครั้ง", "authentication_settings": "การตั้งค่าการเข้าถึง", "authentication_settings_description": "จัดการรหัสผ่าน, OAuth, และตั้งค่าการเข้าถึงอื่นๆ", "authentication_settings_disable_all": "คุณแน่ใจว่าต้องการปิดวิธีการล็อกอินทั้งหมดหรือไม่? ล็อกอินจะถูกปิดทั้งหมด", @@ -43,7 +45,7 @@ "backup_database_enable_description": "เปิดใช้งานสำรองฐานข้อมูล", "backup_keep_last_amount": "จำนวนข้อมูลสำรองก่อนหน้าที่ต้องเก็บไว้", "backup_settings": "ตั้งค่าการสำรองข้อมูล", - "backup_settings_description": "จัดการการตั้งค่าการสำรองฐานข้อมูล งานสำรองนี้จะไม่ถูกตรวจสอบและคุณจะไม่ได้รับการแจ้งเมื่อมันสำรองไม่สำเร็จ", + "backup_settings_description": "จัดการการตั้งค่าการสำรองฐานข้อมูล", "cleared_jobs": "เคลียร์งานสำหรับ: {job}", "config_set_by_file": "การตั้งค่าคอนฟิกกำลังถูกกำหนดโดยไฟล์คอนฟิก", "confirm_delete_library": "คุณแน่ใจว่าอยากลบคลังภาพ {library} หรือไม่?", @@ -169,7 +171,7 @@ "note_apply_storage_label_previous_assets": "หากต้องการใช้ Storage Label กับไฟล์ที่อัปโหลดก่อนหน้านี้ ให้รันคำสั่งนี้", "note_cannot_be_changed_later": "หมายเหตุ: ไม่สามารถเปลี่ยนภายหลังได้!", "notification_email_from_address": "จากที่อยู่", - "notification_email_from_address_description": "อีเมลผู้ส่ง อย่างเช่น \"Immich Photo Server \"", + "notification_email_from_address_description": "ที่อยู่อีเมลผู้ส่ง ตัวอย่าง \"Immich Photo Server \" (กรุณายีนยันว่าสามารถส่งเมลจากที่อยู่นี้ได้)", "notification_email_host_description": "ที่อยู่เซิร์ฟเวอร์อีเมล (เช่น smtp.immich.app)", "notification_email_ignore_certificate_errors": "ไม่สนใจข้อผิดพลาดเกี่ยวกับใบรับรอง", "notification_email_ignore_certificate_errors_description": "ไม่สนใจการยืนยันใบรับรอง TLS ผิดพลาด (ไม่แนะนำ)", @@ -193,7 +195,7 @@ "oauth_enable_description": "ล็อกอินผ่าน OAuth", "oauth_mobile_redirect_uri": "URI เปลี่ยนเส้นทางบนโทรศัพท์", "oauth_mobile_redirect_uri_override": "แทนที่ URI เปลี่ยนเส้นทางบนโทรศัพท์", - "oauth_mobile_redirect_uri_override_description": "เปิดเมื่อ 'app.immich:/' เป็น URI ที่เปลี่ยนเส้นทางไม่ถูกต้อง", + "oauth_mobile_redirect_uri_override_description": "เปิดเมื่อผู้ให้บริการ OAuth ไม่อนุญาต URI เช่น \"{callback}\"", "oauth_settings": "OAuth", "oauth_settings_description": "จัดการการตั้งค่าล็อกอินผ่าน OAuth", "oauth_settings_more_details": "สำหรับรายละเอียดเพิ่มเติม ให้อ้างถึงเอกสาร", @@ -202,7 +204,7 @@ "oauth_storage_quota_claim": "สิทธิ์ที่ใช้อ้างถึงโควต้าพื้นที่จัดเก็บ", "oauth_storage_quota_claim_description": "ตั้งโควต้าพื้นที่จัดเก็บของผู้ใช้งานตามสิทธิ์ที่ใช้อ้างถึงโดยอัตโนมัติ", "oauth_storage_quota_default": "โควต้าพื้นที่เก็บข้อมูลเริ่มต้น (GiB)", - "oauth_storage_quota_default_description": "โควต้าในหน่วย GiB ที่จะใช้เมื่อไม่มีการอ้างสิทธิ์ (ป้อน 0 สำหรับโควต้าไม่จำกัด)", + "oauth_storage_quota_default_description": "โควต้าในหน่วย GiB ที่จะใช้เมื่อไม่มีการอ้างสิทธิ์", "oauth_timeout": "หมดเวลาการร้องขอ", "oauth_timeout_description": "ระยะเวลาหมดเวลาสำหรับการร้องขอ (หน่วยเป็นมิลลิวินาที)", "password_enable_description": "ล็อกอินกับอีเมลและรหัสผ่าน", @@ -242,6 +244,7 @@ "storage_template_migration_info": "เทมเพลตของการจัดเก็บข้อมูลจะเปลี่ยนตัวอักษรเป็นตัวพิมพ์เล็กทั้งหมด การเปลี่ยนแปลงเทมเพลตจะมีผลกับแอสเซ็ตใหม่เท่านั้น หากต้องการนำเทมเพลตไปใช้กับ Asset ที่อัปโหลดก่อนหน้านี้ ให้รัน {job}.", "storage_template_migration_job": "เทมเพลตการ Migration ข้อมูล", "storage_template_more_details": "สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับฟีเจอร์นี้ โปรดดูที่ Storage Template และ ผลกระทบ", + "storage_template_onboarding_description_v2": "เมื่อเปิด ฟีเจอร์จะจัดเก็บข้อมูลตามเทมเพลตที่ผู้ใช้กำหนด อ่านเพิ่มเติม", "storage_template_path_length": "ขีดจำกัดของความยาวพาธโดยประมาณ: {length, number}/{limit, number}", "storage_template_settings": "เทมเพลตการจัดเก็บข้อมูล", "storage_template_settings_description": "จัดการโครงสร้างโฟลเดอร์และชื่อไฟล์ที่อัปโหลด", @@ -256,7 +259,7 @@ "template_email_update_album": "อัปเดตเทมเพลตอัลบั้ม", "template_email_welcome": "เทมเพลตสำหรับอีเมลต้อนรับ", "template_settings": "เทมเพลตการแจ้งเตือน", - "template_settings_description": "ปรับแต่งเทมเพลตแจ้งเตือน", + "template_settings_description": "ปรับแต่งเทมเพลตการแจ้งเตือน", "theme_custom_css_settings": "CSS กําหนดเอง", "theme_custom_css_settings_description": "Cascading Style Sheets ช่วยให้ปรับแต่งเค้าโครง Immich ได้", "theme_settings": "การตั้งค่าธีม", @@ -362,7 +365,7 @@ "advanced_settings_proxy_headers_subtitle": "กำหนด proxy headers ที่ Immich ควรส่งพร้อมกับแต่ละคำขอเครือข่าย", "advanced_settings_proxy_headers_title": "พ็อกซี่ เฮดเดอร์", "advanced_settings_self_signed_ssl_subtitle": "ข้ามการตรวจสอบใบรับรอง SSL จำเป็นสำหรับใบรับรองแบบ self-signed", - "advanced_settings_self_signed_ssl_title": "อนุญาตใบรับรอง SSL แบบ self-signed ", + "advanced_settings_self_signed_ssl_title": "อนุญาตใบรับรอง SSL แบบ self-signed", "advanced_settings_sync_remote_deletions_subtitle": "บหรือกู้คืนไฟล์บนอุปกรณ์นี้โดยอัตโนมัติเมื่อดำเนินการดังกล่าวผ่านเว็บ", "advanced_settings_sync_remote_deletions_title": "ซิงก์การลบจากระยะไกล [คุณสมบัติทดลอง]", "advanced_settings_tile_subtitle": "ตั้งค่าผู้ใช้งานขั้นสูง", @@ -401,6 +404,9 @@ "album_with_link_access": "อนุญาตให้ทุกคนที่มีลิงก์สามารถดูรูปภาพและผู้คนที่อยู่ในอัลบั้มนี้", "albums": "อัลบั้ม", "albums_count": "{count, plural, one {{count, number} อัลบั้ม} other {{count, number} อัลบั้ม}}", + "albums_default_sort_order": "การจัดเรียงอัลบั้มเริ่มต้น", + "albums_default_sort_order_description": "การจัดเรียงแอสเซ็ตเริ่มต้นเมื่อสร้างอัลบั้มใหม่", + "albums_feature_description": "กลุ่มของแอสเซ็ตที่สามารถส่งให้ผู้ใช้อื่นได้", "all": "ทั้งหมด", "all_albums": "อัลบั้มทั้งหมด", "all_people": "ทุกคน", @@ -413,8 +419,8 @@ "anti_clockwise": "ทวนเข็มนาฬิกา", "api_key": "API key", "api_key_description": "ค่านี้จะแสดงเพียงครั้งเดียว โปรดคัดลอกก่อนปิดหน้าต่าง", - "api_key_empty": "ชื่อคีย์ API ของคุณไม่ควรว่างเปล่า", - "api_keys": "API คีย์", + "api_key_empty": "ชื่อ API Key ของคุณไม่ควรว่างเปล่า", + "api_keys": "API Key", "app_bar_signout_dialog_content": "คุณแน่ใจว่าอยากออกจากระบบ", "app_bar_signout_dialog_ok": "ใช่", "app_bar_signout_dialog_title": "ออกจากระบบ", @@ -428,7 +434,7 @@ "archive_size_description": "ตั้งค่าขนาดสูงสุดสำหรับการดาวน์โหลด (GiB)", "archived": "เก็บถาวรแล้ว", "archived_count": "{count, plural, other {เก็บถาวร # รายการ}}", - "are_these_the_same_person": "เป็นคนเดียวกันหรือไม่?", + "are_these_the_same_person": "เป็นบุคคลเดียวกันหรือไม่?", "are_you_sure_to_do_this": "คุณแน่ใจว่าต้องการทำสิ่งนี้หรือไม่?", "asset_action_delete_err_read_only": "ไม่สามารถลบทรัพยากรแบบอ่านอย่างเดียวได้ กำลังข้าม", "asset_action_share_err_offline": "ไม่สามารถดึงข้อมูลทรัพยากรออฟไลน์ กำลังข้าม", @@ -448,24 +454,41 @@ "asset_list_settings_title": "ตารางรูปภาพ", "asset_offline": "สื่อออฟไลน์", "asset_offline_description": "ไม่พบทรัพยากรภายนอกนี้ในดิสก์อีกต่อไป โปรดติดต่อผู้ดูแลระบบ Immich ของคุณเพื่อขอความช่วยเหลือ", + "asset_restored_successfully": "กู้คืนสื่อสำเร็จ", "asset_skipped": "ข้ามแล้ว", "asset_skipped_in_trash": "ในถังขยะ", "asset_uploaded": "อัปโหลดแล้ว", "asset_uploading": "กำลังอัปโหลด…", + "asset_viewer_settings_subtitle": "ตั้งค่าการแสดงแกลเลอรี", "asset_viewer_settings_title": "ตัวดูทรัพยากร", "assets": "สื่อ", + "assets_added_count": "เพิ่ม {count, plural, one{# สื่อ} other {# สื่อ}} แล้ว", "assets_added_to_album_count": "เพิ่ม {count, plural, one {# asset} other {# assets}} ไปยังอัลบั้ม", - "assets_added_to_name_count": "เพิ่ม {count, plural, one {# asset} other {# assets}} ไปยัง {hasName, select, true {{name}} other {new album}}", + "assets_cannot_be_added_to_album_count": "ไม่สามารถเพิ่ม {count, plural, one {สื่อ} other {สื่อ}} ไปยังอัลบั้ม", + "assets_count": "{count, plural, one { สื่อ} other { สื่อ}}", + "assets_deleted_permanently": "{count} สื่อถูกลบอย่างถาวร", + "assets_deleted_permanently_from_server": "ลบ {count} สื่อออกจาก Immich อย่างถาวร", + "assets_downloaded_failed": "ดาวน์โหลด {count, plural, one {ไฟล์} other {ไฟล์}} ไม่สำเร็จ - {error}", + "assets_downloaded_successfully": "ดาวน์โหลด {count, plural, one {ไฟล์} other {ไฟล์}} สำเร็จ", "assets_moved_to_trash_count": "ย้าย {count, plural, one {# asset} other {# assets}} ไปยังถังขยะแล้ว", "assets_permanently_deleted_count": "ลบ {count, plural, one {# asset} other {# assets}} ทิ้งถาวร", "assets_removed_count": "{count, plural, one {# asset} other {# assets}} ถูกลบแล้ว", + "assets_removed_permanently_from_device": "นำ {count} สื่อออกจากอุปกรณ์อย่างถาวร", "assets_restore_confirmation": "คุณแน่ใจหรือไม่ว่าต้องการกู้คืนสื่อที่ทิ้งทั้งหมด? คุณไม่สามารถย้อนกลับการดำเนินการนี้ได้! โปรดทราบว่าสื่อออฟไลน์ใดๆ ไม่สามารถกู้คืนได้ด้วยวิธีนี้", "assets_restored_count": "{count, plural, one {# asset} other {# assets}} คืนค่า", + "assets_restored_successfully": "กู้คืน {count} สื่อสำเร็จ", + "assets_trashed": "ย้าย {count} สื่อไปยังถังขยะ", "assets_trashed_count": "{count, plural, one {# asset} other {# assets}} ถูกลบ", + "assets_trashed_from_server": "ย้าย {count} สื่อจาก Immich ไปยังถังขยะ", "assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Assets were}} อยู่ในอัลบั้มอยู่แล้ว", "authorized_devices": "อุปกรณ์ที่ได้รับอนุญาต", + "automatic_endpoint_switching_subtitle": "เชื่อมต่อด้วย LAN ภายในวง Wi-Fi ที่ระบุไว้ และเชื่อมต่อด้วยวิธีอื่นเมื่ออยู่นอก Wi-Fi ที่ระบุไว้", + "automatic_endpoint_switching_title": "สลับ URL อัตโนมัติ", + "autoplay_slideshow": "เล่นสไลด์โชว์", "back": "กลับ", "back_close_deselect": "ย้อนกลับ, ปิด, หรือยกเลิกการเลือก", + "background_location_permission": "การอนุญาตระบุตำแหน่งพื้นหลัง", + "background_location_permission_content": "เพื่อที่จะสลับการเชื่อมต่อขณะที่รันในพื้นหลัง Immich ต้องรู้ตำแหน่งที่แม่ยำตลอดเวลา เพื่อจะสามารถอ่านชื่อ Wi-Fi", "backup_album_selection_page_albums_device": "อัลบั้มบนเครื่อง ({count})", "backup_album_selection_page_albums_tap": "กดเพื่อรวม กดสองครั้งเพื่อยกเว้น", "backup_album_selection_page_assets_scatter": "ทรัพยาการสามารถกระจายไปในหลายอัลบั้ม ดังนั้นอัลบั้มสามารถถูกรวมหรือยกเว้นในกระบวนการสำรองข้อมูล", @@ -496,15 +519,16 @@ "backup_controller_page_background_is_on": "การสำรองข้อมูลอัตโนมัติเปิดอยู่", "backup_controller_page_background_turn_off": "ปิดบริการเบื้องหลัง", "backup_controller_page_background_turn_on": "เปิดบริการเบื้องหลัง", - "backup_controller_page_background_wifi": "บน WiFi เท่านั้น", + "backup_controller_page_background_wifi": "บน Wi-Fi เท่านั้น", "backup_controller_page_backup": "สำรองข้อมูล", "backup_controller_page_backup_selected": "ที่เลือก: ", "backup_controller_page_backup_sub": "รูปภาพและวิดีโอที่สำรองแล้ว", "backup_controller_page_created": "สร้างเมื่อ: {date}", "backup_controller_page_desc_backup": "เปิดการสำรองข้อมูลในฉากหน้าเพื่อที่จะอัพโหลดทรัพยากรใหม่ไปยังเซิร์ฟเวอร์เมื่อเปิดแอพ", - "backup_controller_page_excluded": "ถูกยกเว้น: ", + "backup_controller_page_excluded": "ยกเว้น: ", "backup_controller_page_failed": "ล้มเหลว ({count})", "backup_controller_page_filename": "ชื่อไฟล์: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "ข้อมูลเกี่ยวกับการสำรองข้อมูล", "backup_controller_page_none_selected": "ไม่มีที่เลือก", "backup_controller_page_remainder": "ที่เหลือ", @@ -526,7 +550,12 @@ "backup_manual_success": "สำเร็จ", "backup_manual_title": "สถานะอัพโหลด", "backup_options_page_title": "ตัวเลือกการสำรองข้อมูล", + "backup_setting_subtitle": "ตั้งค่าการอัพโหลดในฉากหน้า และพื้นหลัง", "backward": "กลับหลัง", + "biometric_auth_enabled": "การพิสูจน์อัตลักษณ์เพื่อยืนยันตัวบุคคลถูกเปิด", + "biometric_locked_out": "การพิสูจน์อัตลักษณ์เพื่อยืนยันตัวบุคคลถูกล็อค", + "biometric_no_options": "ไม่มีตัวเลือกการพิสูจน์อัตลักษณ์เพื่อยืนยันตัวบุคคล", + "biometric_not_available": "ไม่สามารถใช้งานการพิสูจน์อัตลักษณ์เพื่อยืนยันตัวบุคคลได้บนอุปกรณ์นี้", "birthdate_saved": "บันทึกวันเกิดแล้ว", "birthdate_set_description": "วันที่เกิดจะนำมาใช้ในการคำนวณอายุของบุคคลนี้ในขณะที่ถ่ายรูป", "blurred_background": "พื้นหลังแบบเบลอ", @@ -556,14 +585,19 @@ "camera_model": "รุ่นกล้อง", "cancel": "ยกเลิก", "cancel_search": "ยกเลิกการค้นหา", + "canceled": "ยกเลิก", "cannot_merge_people": "ไม่สามารถรวมกลุ่มคนได้", "cannot_undo_this_action": "การกระทำนี้ไม่สามารถย้อนกลับได้!", "cannot_update_the_description": "ไม่สามารถอัพเดทรายละเอียดได้", + "cast": "แคสต์", + "cast_description": "ตั้งค่าปลายทางแคสต์", "change_date": "เปลี่ยนวันที่", + "change_description": "แก้ไขคำอธิบาย", + "change_display_order": "เปลี่ยนลำดับการแสดงผล", "change_expiration_time": "เปลี่ยนเวลาหมดอายุ", "change_location": "เปลี่ยนตําแหน่ง", "change_name": "เปลี่ยนชื่อ", - "change_name_successfully": "เปลี่ยนชื่อเรียบร้อยแล้ว", + "change_name_successfully": "เปลี่ยนชื่อสำเร็จ", "change_password": "เปลี่ยนรหัสผ่าน", "change_password_description": "การเข้าสู่ระบบครั้งแรก จำเป็นจต้องเปลี่ยนรหัสผ่านของคุณเพื่อความปลอดภัย โปรดป้อนรหัสผ่านใหม่ด้านล่าง", "change_password_form_confirm_password": "ยืนยันรหัสผ่าน", @@ -574,6 +608,9 @@ "change_pin_code": "เปลี่ยนรหัสประจำตัว (PIN)", "change_your_password": "เปลี่ยนรหัสผ่านของคุณ", "changed_visibility_successfully": "เปลี่ยนการมองเห็นเรียบร้อยแล้ว", + "check_corrupt_asset_backup": "ตรวจสอบสำรองสื่อที่ผิดปกติ", + "check_corrupt_asset_backup_button": "ตรวจสอบ", + "check_corrupt_asset_backup_description": "ตรวจสอบเมื่อเชื่อมต่อ Wi-Fi และสื่อทั้งหมดถูกสำรองข้อมูลแล้วเท่านั้น การตรวจสอบอาจใช้เวลาหลายนาที", "check_logs": "ตรวจสอบบันทึก", "choose_matching_people_to_merge": "เลือกคนที่ตรงกันเพื่อรวมเข้าด้วยกัน", "city": "เมือง", @@ -582,6 +619,14 @@ "clear_all_recent_searches": "ล้างประวัติการค้นหา", "clear_message": "ล้างข้อความ", "clear_value": "ล้างค่า", + "client_cert_dialog_msg_confirm": "เสร็จ", + "client_cert_enter_password": "ใส่รหัสผ่าน", + "client_cert_import": "นำเข้า", + "client_cert_import_success_msg": "นำเข้าใบรับรองสำเร็จ", + "client_cert_invalid_msg": "ใบรับรอง หรือรหัสผ่านไม่ถูกต้อง", + "client_cert_remove_msg": "ลบใบรับรองสำเร็จ", + "client_cert_subtitle": "รองรับเฉพาะ PKCS12 (.p12, .pfx) เท่านั้น การนำเข้า/ลบใบรับรองสามารถทำได้ก่อนล็อคอินเท่านั้น", + "client_cert_title": "ใบรับรอง SSL ไคลเอนต์", "clockwise": "ตามเข็มนาฬิกา", "close": "ปิด", "collapse": "ย่อ", @@ -602,6 +647,10 @@ "confirm_keep_this_delete_others": "จะลบทั้งหมดในรายการ และยกเว้นสื่อนี้หรือไม่ คุณแน่ใจใช่ไหมที่ต้องการดำเนินการต่อ?", "confirm_new_pin_code": "ยืนยันรหัสประจำตัว (PIN)", "confirm_password": "ยืนยันรหัสผ่าน", + "confirm_tag_face": "คุณต้องการแท็กใบหน้านี้ด้วยชื่อ {name} หรือไม่", + "confirm_tag_face_unnamed": "คุณต้องการแท็กใบหน้านี้หรือไม่", + "connected_device": "อุปกรณ์ที่เชื่อมต่อแล้ว", + "connected_to": "เชื่อมต่อไปยัง", "contain": "มีอยู่", "context": "บริบท", "continue": "ต่อไป", @@ -610,6 +659,7 @@ "control_bottom_app_bar_delete_from_local": "ลบจากเรื่อง", "control_bottom_app_bar_edit_location": "แก้ไขตำแหน่ง", "control_bottom_app_bar_edit_time": "แก้ไขวันและเวลา", + "control_bottom_app_bar_share_link": "แชร์ลิงค์", "control_bottom_app_bar_share_to": "แชร์ให้", "control_bottom_app_bar_trash_from_immich": "ย้ายเข้าถังขยะ", "copied_image_to_clipboard": "คัดลอกภาพไปยังคลิปบอร์ดแล้ว", @@ -631,6 +681,7 @@ "create_link": "สร้างลิงก์", "create_link_to_share": "สร้างลิงก์เพื่อแชร์", "create_link_to_share_description": "ผู้ที่มีลิงก์ สามารถดูรูปที่เลือกได้", + "create_new": "สร้างใหม่", "create_new_person": "สร้างคนใหม่", "create_new_person_hint": "กำหนดสื่อที่เลือกให้กับคนใหม่", "create_new_user": "สร้างผู้ใช้งานใหม่", @@ -640,9 +691,12 @@ "create_tag_description": "สร้างแท็กใหม่ สำหรับแท็กที่ซ้อนกัน โปรดป้อนเส้นทางทั้งหมดของแท็ก รวมถึงเครื่องหมายทับ", "create_user": "สร้างผู้ใช้", "created": "สร้างแล้ว", + "created_at": "สร้างเมื่อ", + "crop": "ครอป", "curated_object_page_title": "สิ่งของ", "current_device": "อุปกรณ์ปัจจุบัน", "current_pin_code": "รหัสประจำตัว (PIN) ปัจจุบัน", + "current_server_address": "ที่อยู่เซิร์ฟเวอร์ปัจจุบัน", "custom_locale": "ปรับภาษาท้องถิ่นเอง", "custom_locale_description": "ใช้รูปแบบวันที่และตัวเลขจากภาษาและขอบเขต", "daily_title_text_date": "E dd MMM", @@ -694,6 +748,7 @@ "disallow_edits": "ไม่อนุญาตให้แก้ไข", "discord": "ดิสคอร์ด", "discover": "ค้นพบ", + "discovered_devices": "ค้นหาอุปกรณ์", "dismiss_all_errors": "ปฏิเสธข้อผิดพลาดทั้งหมด", "dismiss_error": "ปฏิเสธข้อผิดพลาด", "display_options": "ตัวเลือกการแสดง", @@ -704,12 +759,25 @@ "documentation": "เอกสาร", "done": "ดำเนินการสำเร็จ", "download": "ดาวน์โหลด", + "download_canceled": "การดาวน์โหลดยกเลิก", + "download_complete": "การดาวน์โหลดเสร็จสิ้น", + "download_enqueue": "การดาวน์โหลดอยู่ในคิว", + "download_error": "ดาวน์โหลดผิดพลาด", + "download_failed": "ดาวน์โหลดไม่สำเร็จ", + "download_finished": "ดาวน์โหลดเสร็จสิ้น", "download_include_embedded_motion_videos": "รวมวิดีโอที่ฝังอยู่ในภาพเคลื่อนไหว", "download_include_embedded_motion_videos_description": "รวมวิดีโอที่ฝังอยู่ในภาพเคลื่อนไหวเมื่อดาวน์โหลดอัลบั้ม", + "download_notfound": "ไม่พบดาวน์โหลด", + "download_paused": "หยุดการดาวน์โหลดชั่วคราว", "download_settings": "การตั้งค่าการดาวน์โหลด", "download_settings_description": "จัดการการตั้งค่าการดาวน์โหลด", + "download_started": "เริ่มการดาวน์โหลด", + "download_sucess": "ดาวน์โหลดสำเร็จ", + "download_sucess_android": "สื่อถูกดาวน์โหลดไปยัง DCIM/Immich", + "download_waiting_to_retry": "รอลองใหม่", "downloading": "กำลังดาวน์โหลด", "downloading_asset_filename": "กำลังดาวน์โหลด {filename}", + "downloading_media": "กำลังดาวน์โหลดสื่อ", "drop_files_to_upload": "วางไฟล์ในช่องอัปโหลด", "duplicates": "รายการที่ซ้ำกัน", "duplicates_description": "แก้ไขแต่ละกลุ่มโดยระบุว่ากลุ่มใดซ้ำกันหากมี", @@ -719,6 +787,8 @@ "edit_avatar": "แก้ไขตัวละคร", "edit_date": "แก้ไขวันที่", "edit_date_and_time": "แก้ไขวันที่และเวลา", + "edit_description": "แก้ไขคำอธิบาย", + "edit_description_prompt": "โปรดเลื่อกคำอธิบายใหม่", "edit_exclusion_pattern": "แก้ไขข้อยกเว้น", "edit_faces": "แก้ไขหน้า", "edit_import_path": "แก้ไขพาธนําเข้า", @@ -739,15 +809,24 @@ "editor_crop_tool_h2_aspect_ratios": "อัตราส่วนภาพ", "editor_crop_tool_h2_rotation": "การหมุน", "email": "อีเมล", + "email_notifications": "แจ้งเตือนผ่านอีเมล", + "empty_folder": "โฟลเดอร์นี้ว่างเปล่า", "empty_trash": "ทิ้งจากถังขยะ", "empty_trash_confirmation": "คุณแน่ใจหรือไม่ว่าต้องการล้างถังขยะ การดำเนินการนี้จะลบทรัพยากรทั้งหมดในถังขยะออกจาก Immich อย่างถาวร\nคุณไม่สามารถย้อนกลับการดำเนินการนี้ได้!", "enable": "เปิดใช้งาน", + "enable_biometric_auth_description": "ใส่พินเพื่อเปิดการพิสูจน์อัตลักษณ์เพื่อยืนยันตัวบุคคล", "enabled": "เปิดใช้งาน", "end_date": "วันสิ้นสุด", - "enter_wifi_name": "Enter WiFi name", + "enqueued": "รอคิว", + "enter_wifi_name": "ใส่ชื่อ Wi-Fi", + "enter_your_pin_code": "ใส่พินโค้ด", + "enter_your_pin_code_subtitle": "ใส่พินโค้ดเพื่อเข้าถึงโฟลเดอร์ล็อค", "error": "เกิดข้อผิดพลาด", + "error_change_sort_album": "เปลี่ยนการเรียงลำดับอัลบั้มไม่สำเร็จ", "error_delete_face": "เกิดเออเรอร์ ไม่สามารถลบใบหน้าออกได้", "error_loading_image": "เกิดข้อผิดพลาดระหว่างโหลดภาพ", + "error_saving_image": "เกิดข้อผิดพลาดระหว่างเซฟภาพ: {error}", + "error_tag_face_bounding_box": "การแท็กใบหน้าผิดพลาด - ไม่สามารถตีกรอบใบหน้าได้", "error_title": "เกิดข้อผิดพลาด", "errors": { "cannot_navigate_next_asset": "ไม่สามารถเปลี่ยนเส้นทางได้", @@ -755,7 +834,7 @@ "cant_apply_changes": "เกิดข้อผิดพลาดในการเปลี่ยนแปลง", "cant_change_activity": "Can't {enabled, select, true {disable} other {enable}} activity", "cant_change_asset_favorite": "ไม่สามารถเปลี่ยนสื่อที่ชื่นชอบได้", - "cant_change_metadata_assets_count": "Can't change metadata of {count, plural, one {# asset} other {# assets}}", + "cant_change_metadata_assets_count": "ไม่สามารถแก้ไขข้อมูล metadata ของ {count, plural, one {# สื่อ} other {# สื่อ}}", "cant_get_faces": "เกิดข้อผิดพลาดในการเรียกดูใบหน้า", "cant_get_number_of_comments": "ไม่สามารถเรียกดูจำนวนความคิดเห็นได้", "cant_search_people": "ไม่สามารถค้นหาบุคคลคนได้", @@ -775,10 +854,12 @@ "failed_to_keep_this_delete_others": "ไม่สามารถเก็บหรือลบได้", "failed_to_load_asset": "ไม่สามารถโหลดสื่อได้", "failed_to_load_assets": "ไม่สามารถโหลดสื่อได้", + "failed_to_load_notifications": "โหลดการแจ้งเตือนไม่สำเร็จ", "failed_to_load_people": "ไม่สามารถโหลดบุคคลได้", "failed_to_remove_product_key": "ไม่สามารถลบ product key ได้", "failed_to_stack_assets": "Failed to stack assets", "failed_to_unstack_assets": "Failed to un-stack assets", + "failed_to_update_notification_status": "อัพเดทสถานะการแจ้งเตือนไม่สำเร็จ", "import_path_already_exists": "พาธนำเข้านี้มีอยู่แล้ว", "incorrect_email_or_password": "อีเมลหรือรหัสผ่านไม่ถูกต้อง", "paths_validation_failed": "การตรวจสอบ {paths, plural, one {# path} other {# paths}} ล้มเหลว", @@ -795,6 +876,7 @@ "unable_to_archive_unarchive": "ไม่สามารถทำรายการ {archived, select, true {archive} other {unarchive}}", "unable_to_change_album_user_role": "ไม่สามารถเปลี่ยนบทบาทผู้ใช้ในอัลบั้มได้", "unable_to_change_date": "ไม่สามารถเปลี่ยนวันที่ได้", + "unable_to_change_description": "ไม่สามารถเปลี่ยนคำอธิบาย", "unable_to_change_favorite": "ไม่สามารถเปลี่ยนแปลงสื่อรายการโปรดได้", "unable_to_change_location": "ไม่สามารถเปลี่ยนตําแหน่งได้", "unable_to_change_password": "ไม่สามารถเปลี่ยนรหัสผ่านได้", @@ -838,6 +920,7 @@ "unable_to_remove_partner": "ไม่สามารถลบคู่หูได้", "unable_to_remove_reaction": "ไม่สามารถลบ reaction ได้", "unable_to_reset_password": "ไม่สามารถตั้งรหัสผ่านใหม่ได้", + "unable_to_reset_pin_code": "ไม่สามารถรีเซ็ตพินโค้ด", "unable_to_resolve_duplicate": "ไม่สามารถแก้ไขของซ้ำได้", "unable_to_restore_assets": "ไม่สามารถเรียกคืนสื่อได้", "unable_to_restore_trash": "ไม่สามารถเรียกคืนถังขยะได้", @@ -865,11 +948,15 @@ "unable_to_update_user": "ไม่สามารถอัพเดทผู้ใช้ได้", "unable_to_upload_file": "ไม่สามารถอัปโหลดได้" }, + "exif": "Exif", "exif_bottom_sheet_description": "เพิ่มคำอธิบาย", "exif_bottom_sheet_details": "รายละเอียด", "exif_bottom_sheet_location": "ตำแหน่ง", "exif_bottom_sheet_people": "คน", "exif_bottom_sheet_person_add_person": "เพิ่มชื่อ", + "exif_bottom_sheet_person_age_months": "อายุ {months} เดือน", + "exif_bottom_sheet_person_age_year_months": "อายุ 1 ปี {months} เดือน", + "exif_bottom_sheet_person_age_years": "อายุ {years} ปี", "exit_slideshow": "ออกจากการนำเสนอ", "expand_all": "ขยายทั้งหมด", "experimental_settings_new_asset_list_subtitle": "กำลังพัฒนา", @@ -886,9 +973,13 @@ "extension": "ส่วนต่อขยาย", "external": "ภายนอก", "external_libraries": "ภายนอกคลังภาพ", - "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", + "external_network": "การเชื่อมต่อภายนอก", + "external_network_sheet_info": "เมื่อไม่ได้เชื่อมต่อ Wi-Fi ที่เลือกไว้ แอพจะเชื่อมต่อเซิร์ฟเวอร์ผ่าน URL ด้านล่างตามลำดับ", "face_unassigned": "ไม่กำหนดมอบหมาย", + "failed": "ล้มเหลว", + "failed_to_authenticate": "การยืนยันตัวตนไม่สำเร็จ", "failed_to_load_assets": "เกิดข้อผิดพลาดในการโหลดสื่อ", + "failed_to_load_folder": "โหลดโฟลเดอร์ไม่สำเร็จ", "favorite": "รายการโปรด", "favorite_or_unfavorite_photo": "โปรดหรือไม่โปรดภาพ", "favorites": "รายการโปรด", @@ -900,18 +991,26 @@ "file_name_or_extension": "นามสกุลหรือชื่อไฟล์", "filename": "ชื่อไฟล์", "filetype": "ชนิดไฟล์", + "filter": "ตัวกรอง", "filter_people": "กรองผู้คน", + "filter_places": "กรองสถานที่", "find_them_fast": "ค้นหาโดยชื่ออย่างรวดเร็ว", "fix_incorrect_match": "แก้ไขการจับคู่ที่ไม่ถูกต้อง", + "folder": "โฟลเดอร์", + "folder_not_found": "ไม่พบโฟลเดอร์", "folders": "โฟล์เดอร์", "folders_feature_description": "การเรียกดูมุมมองโฟลเดอร์สำหรับภาพถ่ายและวิดีโอในระบบไฟล์", "forward": "ไปข้างหน้า", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "ฟีเจอร์นี้ต้องโหลดทรัพยากรจาก Google เพื่อทำงาน", "general": "ทั่วไป", "get_help": "ขอความช่วยเหลือ", + "get_wifiname_error": "ไม่สามารถรับชื่อ Wi-Fi กรุณายืนยันการให้อนุญาตแอพ และยืนยันว่า Wi-Fi เชื่อมต่ออยู่", "getting_started": "เริ่มต้นใช้งาน", "go_back": "กลับ", "go_to_folder": "ไปที่โฟล์เดอร์", "go_to_search": "กลับไปยังการค้นหา", + "grant_permission": "ให้อนุญาต", "group_albums_by": "จัดกลุ่มอัลบั้มตาม", "group_country": "จัดเรียงกลุ่มตามประเทศ", "group_no": "ไม่จัดกลุ่ม", @@ -921,6 +1020,11 @@ "haptic_feedback_switch": "เปิดการตอบสนองแบบสัมผัส", "haptic_feedback_title": "การตอบสนองแบบสัมผัส", "has_quota": "เหลือพื้นที่", + "header_settings_add_header_tip": "เพิ่ม Header", + "header_settings_field_validator_msg": "ค่าต้องไม่ว่างเปล่า", + "header_settings_header_name_input": "ชื่อ Header", + "header_settings_header_value_input": "ค่า Header", + "headers_settings_tile_title": "ปรับแต่ง proxy headers", "hi_user": "สวัสดีคุณ {name} {email}", "hide_all_people": "ซ่อนบุคคลทั้งหมด", "hide_gallery": "ซ่อนคลังภาพ", @@ -929,22 +1033,28 @@ "hide_person": "ซ่อนบุคคล", "hide_unnamed_people": "ซ่อนบุคคลที่ไม่ได้ระบุชื่อ", "home_page_add_to_album_conflicts": "เพิ่ม {added} ทรัพยากรเข้าอัลบั้ม {album}. {failed} ทรัพยากรอยู่ในอัลบั้มอยู่แล้ว", - "home_page_add_to_album_err_local": " ยังไม่สามารถเพิ่มทรัพยากรบนเครื่องเข้าอัลบั้ม กำลังข้าม", + "home_page_add_to_album_err_local": "ยังไม่สามารถเพิ่มสื่อบนอุปกรณ์เข้าอัลบั้ม ข้าม", "home_page_add_to_album_success": "เพิ่มทรัพยากร {added} เข้าอัลบั้ม {album}", - "home_page_album_err_partner": "ยังไม่สามารถเพิ่มทรัพยากรของพันธมิตรได้ กำลังข้าม", + "home_page_album_err_partner": "ยังไม่สามารถเพิ่มสื่อของคู่หูได้ กำลังข้าม", "home_page_archive_err_local": "ยังไม่สามารถเก็บถาวรได้ กำลังข้าม", - "home_page_archive_err_partner": "ไม่สามารถเก็บทรัพยากรของพันธมิตรได้ กำลังข้าม", + "home_page_archive_err_partner": "ไม่สามารถเก็บสื่อของคู่หูได้ กำลังข้าม", "home_page_building_timeline": "กำลังสร้าง timeline", - "home_page_delete_err_partner": "ไม่สามารถลบทรัพยากรของพันธมิตรได้ กำลังข้าม", + "home_page_delete_err_partner": "ไม่สามารถลบสื่อของคู่ได้ กำลังข้าม", "home_page_delete_remote_err_local": "ทรัพยากรบนเครื่องอยู่ในลบจากรีโมท กำลังข้าม", "home_page_favorite_err_local": "ยังไม่สามารถตั้งทรัพยากรบนเครื่องเป็นรายการโปรด กำลังข้าม", - "home_page_favorite_err_partner": "ยังไม่สามารถเพิ่มทรัพยากรของพันธมิตรในรายการโปรดได้ กำลังข้าม", + "home_page_favorite_err_partner": "ยังไม่สามารถเพิ่มสื่อของคู่หูในรายการโปรดได้ กำลังข้าม", "home_page_first_time_notice": "ถ้าครั้งนี้เป็นครั้งแรกที่ใช้แอปนี้ กรุณาเลือกอัลบั้มที่จะสำรองข้อมูล ไทม์ไลน์จะได้เพิ่มรูปภาพและวิดีโอที่อยู่ในอัลบั้ม", + "home_page_locked_error_local": "ไม่สามารถย้ายสื่อบนอุปกรณ์ไปยังโฟลเดอร์ล็อค ข้าม", + "home_page_locked_error_partner": "ยังไม่สามารถเพิ่มสื่อของคู่หูไปยังโฟลเดอร์ล็อคได้ กำลังข้าม", "home_page_share_err_local": "ไม่สามารถแชร์ผ่านลิงค์ได้ กำลังข้าม", "home_page_upload_err_limit": "สามารถอัพโหลดได้มากสุดครั้งละ 30 ทรัพยากร กำลังข้าม", "host": "โฮสต์", "hour": "ชั่วโมง", + "id": "ไอดี", + "ignore_icloud_photos": "ข้ามภาพบน iCloud", + "ignore_icloud_photos_description": "ภาพที่ถูกเก็บบน iCloud จะไม่ถูกอัพโหลดขึ้น Immich", "image": "รูปภาพ", + "image_alt_text_date": "{isVideo, select, true {วิดีโอ} other {รูปภาพ}}ถูกถ่ายเมื่อ {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Image}} ถ่ายกับ {person1} วันที่ {date}", "image_alt_text_date_2_people": "{isVideo, select, true {Video} other {Image}} ถ่ายกับ {person1} และ {person2} วันที่ {date}", "image_alt_text_date_3_people": "{isVideo, select, true {Video} other {Image}} ถ่ายกับ {person1}, {person2},และ {person3} วันที่ {date}", @@ -954,6 +1064,7 @@ "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Image}} ถ่ายใน {city}, {country} กับ {person1} และ {person2} วันที่ {date}", "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Image}} ถ่ายใน {city}, {country} กับ {person1}, {person2},และ {person3} วันที่ {date}", "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} ถ่ายใน {city}, {country} กับ {person1}, {person2}, และ {additionalCount, number} ในวันที่ {date}", + "image_saved_successfully": "รูปภาพถูกเซฟ", "image_viewer_page_state_provider_download_started": "ดาวน์โหลดเริ่มต้น", "image_viewer_page_state_provider_download_success": "ดาวน์โหลดสำเร็จ", "image_viewer_page_state_provider_share_error": "แชร์ผิดพลาด", @@ -974,8 +1085,16 @@ "night_at_midnight": "ทุกเที่ยงคืน", "night_at_twoam": "ทุกวันเวลาตี 2" }, + "invalid_date": "วันที่ไม่ถูกต้อง", + "invalid_date_format": "รูปแบบวันที่ไม่ถูกต้อง", "invite_people": "เชิญผู้คน", "invite_to_album": "เชิญเข้าอัลบั้ม", + "ios_debug_info_fetch_ran_at": "รับข้อมูลเมื่อ {dateTime}", + "ios_debug_info_last_sync_at": "ซิงค์ล่าสุด {dateTime}", + "ios_debug_info_no_processes_queued": "ไม่มีคิวในพื้นหลัง", + "ios_debug_info_no_sync_yet": "ยังไม่มีงานซิงค์รันในพื้นหลัง", + "ios_debug_info_processes_queued": "{count} โพรเซสรอคิวในพื้นหลัง", + "ios_debug_info_processing_ran_at": "โพรเซสรันเมื่อ {dateTime}", "items_count": "{count, plural, one {# รายการ} other {#รายการ}}", "jobs": "งาน", "keep": "เก็บ", @@ -984,6 +1103,9 @@ "kept_this_deleted_others": "เก็บเนื้อหานี้และลบ {count, plural, one {# Asset} other {# Asset}}", "keyboard_shortcuts": "ปุ่มพิมพ์ลัด", "language": "ภาษา", + "language_no_results_subtitle": "กรุณาปรับเปลี่ยนคำค้นหา", + "language_no_results_title": "ไม่พบภาษา", + "language_search_hint": "ค้นหาภาษา...", "language_setting_description": "เลือกภาษาที่ต้องการ", "last_seen": "เห็นล่าสุด", "latest_version": "เวอร์ชันล่าสุด", @@ -1009,14 +1131,21 @@ "list": "รายการ", "loading": "กำลังโหลด", "loading_search_results_failed": "โหลดผลการค้นหาล้มเหลว", - "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", + "local_asset_cast_failed": "ไม่สามารถแคสสื่อที่ไม่ถูกอัพโหลดไปยังเซิร์ฟเวอร์", + "local_network": "เครือข่ายระยะใกล้", + "local_network_sheet_info": "แอพจะทำการเชื่อมต่อไปยังเซิร์ฟเวอร์ผ่าน URL นี้เมื่อเชื่อต่อกับ Wi-Fi ที่เลือกไว้", + "location_permission": "การอนุญาตตำแหน่ง", + "location_permission_content": "เพื่อใช้ฟีเจอร์การสับโดยอัตโนมัติ Immich ต้องการการอนุญาตเข้าถึงต่ำแหน่งที่แม่นยำเพื่ออ่านชื่อ Wi-Fi ที่เชื่อมต่ออยู่", "location_picker_choose_on_map": "เลือกบนแผนที่", "location_picker_latitude_error": "กรุณาเพิ่มละติจูตที่ถูกต้อง", "location_picker_latitude_hint": "เพิ่มละติจูตตรงนี้", "location_picker_longitude_error": "กรุณาเพิ่มลองจิจูตที่ถูกต้อง", "location_picker_longitude_hint": "เพิ่มลองจิจูตตรงนี้", + "lock": "ล็อค", + "locked_folder": "โฟลเดอร์ล็อค", "log_out": "ออกจากระบบ", "log_out_all_devices": "ให้ทุกอุปกรณ์ออกจากระบบทั้งหมด", + "logged_in_as": "{user} กำลังล็อคอิน", "logged_out_all_devices": "ออกจากระบบทั้งหมดแล้ว", "logged_out_device": "ออกจากระบบแล้ว", "login": "เข้าสู่ระบบ", @@ -1079,7 +1208,7 @@ "map_settings_date_range_option_years": "{years} ปีผ่านมา", "map_settings_dialog_title": "ตั้งค่าแผนที่", "map_settings_include_show_archived": "รวมเก็บถาวร", - "map_settings_include_show_partners": "รามพันธมิตร", + "map_settings_include_show_partners": "รวมคู่หู", "map_settings_only_show_favorites": "แสดงรายการโปรดเท่านั้น", "map_settings_theme_settings": "ธีมแผนที่", "map_zoom_to_see_photos": "ซูมออกเพื่อดูรูป", @@ -1106,12 +1235,17 @@ "model": "โมเดล", "month": "เดือน", "more": "เพิ่มเติม", + "move": "ย้าย", + "move_off_locked_folder": "ย้ายออกจากโฟลเดอร์ล็อค", + "move_to_locked_folder": "ย้ายไปโฟลเดอร์ล็อค", "moved_to_trash": "ทิ้งลงถังขยะแล้ว", "multiselect_grid_edit_date_time_err_read_only": "ไม่สามารถแก้ไขวันที่ทรัพยากรแบบอ่านอย่างเดียว กำลังข้าม", "multiselect_grid_edit_gps_err_read_only": "ไม่สามารถแก้ตำแหน่งของทรัพยากรแบบอ่านอย่างเดียว กำลังข้าม", "my_albums": "อัลบั้มของฉัน", "name": "ชื่อ", "name_or_nickname": "ชื่อหรือชื่อเล่น", + "networking_settings": "การเชื่อมต่อ", + "networking_subtitle": "ตั้งค่าปลายทางเซิร์ฟเวอร์", "never": "ไม่เคย", "new_album": "อัลบั้มใหม่", "new_api_key": "สร้าง API คีย์ใหม่", @@ -1155,7 +1289,7 @@ "ok": "ตกลง", "oldest_first": "เรียงเก่าสุดก่อน", "onboarding": "การเริ่มต้นใช้งาน", - "onboarding_privacy_description": "คุณลักษณะ (ไม่จำเป็น) ต่อไปนี้ต้องอาศัยบริการภายนอก และสามารถปิดใช้งานได้ตลอดเวลาในการตั้งค่าการดูแลระบบ", + "onboarding_privacy_description": "ฟีเจอร์ (ตัวเลือก) ต่อไปนี้ต้องอาศัยบริการภายนอก และสามารถปิดใช้งานได้ตลอดเวลาในการตั้งค่าการ", "onboarding_theme_description": "เลือกธีมสี คุณสามารถเปลี่ยนแปลงได้ในภายหลังในการตั้งค่าของคุณ", "onboarding_welcome_user": "ยินดีต้อนรับคุณ {user}", "online": "ออนไลน์", @@ -1172,20 +1306,20 @@ "other_variables": "ตัวแปรอื่น", "owned": "เป็นเจ้าของ", "owner": "เจ้าของ", - "partner": "พาร์ทเนอร์", + "partner": "คู่หู", "partner_can_access": "{partner} สามารถเข้าถึง", "partner_can_access_assets": "รูปภาพและวิดีโอทั้งหมดยกเว้นที่อยู่ในเก็บถาวรและถูกลบทิ้ง", "partner_can_access_location": "ตำแหน่งที่รูปถูกถ่าย", "partner_list_user_photos": "รูปภาพของ {user}", "partner_list_view_all": "ดูทั้งหมด", - "partner_page_empty_message": "รูปภาพของคุณยังไม่ถูกแชร์กับพันธมิตร", + "partner_page_empty_message": "รูปภาพของคุณยังไม่ถูกแชร์กับคู่หู", "partner_page_no_more_users": "ไม่มีผู้ใช้งานให้เพิ่ม", - "partner_page_partner_add_failed": "การเพิ่มพันธมิตรล้มเหลว", - "partner_page_select_partner": "เลือกพันธมิตร", + "partner_page_partner_add_failed": "การเพิ่มคู่หูล้มเหลว", + "partner_page_select_partner": "เลือกคู่หู", "partner_page_shared_to_title": "แชร์กับ", "partner_page_stop_sharing_content": "{partner} จะไม่สามารถเข้าถึงรูปภาพของคุณ", - "partner_sharing": "แชร์สำหรับพาร์ทเนอร์", - "partners": "พาร์ทเนอร์", + "partner_sharing": "แชร์สำหรับคู่หู", + "partners": "คู่หู", "password": "รหัสผ่าน", "password_does_not_match": "รหัสผ่านไม่ตรงกัน", "password_required": "จำเป็นต้องมีรหัสผ่าน", @@ -1276,7 +1410,7 @@ "purchase_lifetime_description": "ซื้อตลอดชีพ", "purchase_option_title": "ตัวเลือกการซื้อ", "purchase_panel_info_1": "ทางทีม Immich ต้องใช้เวลาและความพยายามอย่างมากในการพัฒนาระบบนี้ขึ้นมา และเรามีวิศวกรที่ทำงานเต็มเวลาเพื่อพัฒนาให้ดีที่สุดเท่าที่จะทำได้ ภารกิจของเราคือการทำให้ซอฟต์แวร์โอเพ่นซอร์สและแนวทางปฏิบัติทางธุรกิจที่ถูกต้องตามจริยธรรมกลายเป็นแหล่งรายได้ที่ยั่งยืนสำหรับนักพัฒนา และสร้างระบบนิเวศที่เคารพความเป็นส่วนตัวพร้อมทางเลือกอื่นที่เป็นรูปธรรมแทนบริการคลาวด์ที่เอารัดเอาเปรียบ", - "purchase_panel_info_2": "เนื่องจากเราให้คำมั่นว่า จะไม่เพิ่มระบบชำระเงินในระบบของเรา ดังนั้นการซื้อครั้งนี้จะไม่ทำให้คุณได้รับฟีเจอร์เพิ่มเติมใน Immich เป็นพิเศษ เราอาศัยผู้คนแบบท่านในการสนับสนุนการพัฒนาอย่างต่อเนื่องของ Immich", + "purchase_panel_info_2": "เนื่องจากเราให้คำมั่นว่า จะไม่เพิ่มระบบชำระเงินในระบบของเรา ดังนั้นการซื้อครั้งนี้จะไม่ทำให้คุณได้รับฟีเจอร์เพิ่มเติมใน Immich เป็นพิเศษ เราอาศัยผู้คนแบบคุณในการสนับสนุนการพัฒนาอย่างต่อเนื่องของ Immich", "purchase_panel_title": "สนับสนุนโครงการนี้", "purchase_per_server": "ต่อเซิร์ฟเวอร์", "purchase_per_user": "ต่อผู้ใช้งาน", @@ -1421,6 +1555,7 @@ "select_keep_all": "เลือกเก็บทั้งหมด", "select_library_owner": "เลือกเจ้าของคลังภาพ", "select_new_face": "เลือกใบหน้าใหม่", + "select_person_to_tag": "เลือกบุคคล", "select_photos": "เลือกรูปภาพ", "select_trash_all": "เลือกในถังขยะทั้งหมด", "select_user_for_sharing_page_err_album": "สร้างอัลบั้มล้มเหลว", @@ -1428,12 +1563,14 @@ "selected_count": "{count, plural, other {# เลือกแล้ว}}", "send_message": "ส่งข้อความ", "send_welcome_email": "ส่งอีเมลต้อนรับ", + "server_endpoint": "ปลายทางเซิร์ฟเวอร์", "server_info_box_app_version": "เวอร์ชันแอพ", "server_info_box_server_url": "URL เซิร์ฟเวอร์", "server_offline": "Server ออฟไลน์", "server_online": "Server ออนไลน์", + "server_privacy": "ความเป็นส่วนตัวเซิร์ฟเวอร์", "server_stats": "สถิติเซิร์ฟเวอร์", - "server_version": "เวอร์ชันของ Server", + "server_version": "เวอร์ชันของเซิร์ฟเวอร์", "set": "ตั้ง", "set_as_album_cover": "ตั้งเป็นภาพปกอัลบั้ม", "set_as_featured_photo": "ตั้งเป็นรูปสำคัญ", @@ -1518,7 +1655,7 @@ "sharing_page_empty_list": "รายการว่างเปล่า", "sharing_sidebar_description": "แสดงลิงก์ที่แชร์ในแถบด้านข้าง", "sharing_silver_appbar_create_shared_album": "อัลบั้มที่แชร์ใหม่", - "sharing_silver_appbar_share_partner": "แชร์กับพันธมิตร", + "sharing_silver_appbar_share_partner": "แชร์กับคู่หู", "shift_to_permanent_delete": "กด ⇧ to สำหรับลบสื่อถาวร", "show_album_options": "แสดงตัวเลือกอัลบั้ม", "show_albums": "แสดงอัลบั้ม", @@ -1570,7 +1707,7 @@ "status": "สถานะ", "stop_motion_photo": "ภาพวัตถุเคลื่อนไหว", "stop_photo_sharing": "หยุดแชร์รูปภาพ?", - "stop_photo_sharing_description": "{partner}จะไม่สามารถเข้าถึงรูปของคุณได้อีก", + "stop_photo_sharing_description": "{partner} จะไม่สามารถเข้าถึงรูปของคุณได้อีก", "stop_sharing_photos_with_user": "หยุดการแชร์รูปภาพของคุณกับผู้ใช้นี้", "storage": "พื้นที่จัดเก็บ", "storage_label": "เนื้อที่จัดเก็บ", @@ -1644,6 +1781,7 @@ "unselect_all": "ยกเลิกการเลือกทั้งหมด", "unstack": "หยุดซ้อน", "up_next": "ต่อไป", + "updated_at": "อัพเดท", "updated_password": "รหัสผ่านเปลี่ยนแล้ว", "upload": "อัปโหลด", "upload_concurrency": "อัปโหลดพร้อมกัน", @@ -1653,7 +1791,9 @@ "upload_status_errors": "ข้อผิดพลาด", "upload_status_uploaded": "อัปโหลดแล้ว", "upload_success": "อัปโหลดสำเร็จ, รีเฟรชหน้านี้ใหม่คุณจะเห็นสื่อที่เพิ่มล่าสุด", + "uploading": "กำลังอัพโหลด", "usage": "การใช้งาน", + "use_biometric": "ใช้การพิสูจน์อัตลักษณ์", "use_custom_date_range": "ใช้การปรับแต่งช่วงเวลา", "user": "ผู้ใช้", "user_id": "ไอดีผู้ใช้", @@ -1686,6 +1826,7 @@ "view_links": "ดูลิงก์", "view_next_asset": "ดูสื่อถัดไป", "view_previous_asset": "ดูสื่อก่อนหน้า", + "view_qr_code": "ดูคิวอาร์โค้ด", "viewer_remove_from_stack": "เอาออกจากที่ซ้อน", "viewer_stack_use_as_main_asset": "ใช้เป็นทรัพยากรหลัก", "viewer_unstack": "หยุดซ้อน", @@ -1695,11 +1836,11 @@ "week": "สัปดาห์", "welcome": "ยินดีต้อนรับ", "welcome_to_immich": "ยินดีต้อนรับสู่ immich", - "wifi_name": "WiFi Name", + "wifi_name": "ชื่อ Wi-Fi", "year": "ปี", "years_ago": "{years, plural, one {# ปี} other {# ปี}} ที่แล้ว", "yes": "ใช่", "you_dont_have_any_shared_links": "คุณไม่ได้มีลิงก์ที่แชร์", - "your_wifi_name": "Your WiFi name", + "your_wifi_name": "ชื่อ Wi-Fi", "zoom_image": "ซูมรูปภาพ" } diff --git a/i18n/tr.json b/i18n/tr.json index ddb99ca019..b26f70acc2 100644 --- a/i18n/tr.json +++ b/i18n/tr.json @@ -34,6 +34,7 @@ "added_to_favorites_count": "{count, number} fotoğraf favorilere eklendi", "admin": { "add_exclusion_pattern_description": "Hariç tutma desenleri ekleyin. *, ** ve ? kullanılarak Globbing (temsili yer doldurucu karakter) desteklenir. Farzedelim \"Raw\" adlı bir dizininiz var, içinde ki tüm dosyaları yoksaymak için \"**/Raw/**\" şeklinde yazabilirsiniz. \".tif\" ile biten tüm dosyaları yoksaymak için \"**/*.tif\" yazabilirsiniz. Mutlak yolu yoksaymak için \"/yoksayılacak/olan/yol/**\" şeklinde yazabilirsiniz.", + "admin_user": "Yönetici kullanıcısı", "asset_offline_description": "Bu harici kütüphane varlığı artık diskte bulunmuyor ve çöp kutusuna taşındı. Dosya kütüphane içinde taşındıysa, yeni karşılık gelen varlık için zaman çizelgenizi kontrol edin. Bu varlığı geri yüklemek için lütfen aşağıdaki dosya yolunun Immich tarafından erişilebilir olduğundan emin olun ve kütüphaneyi tarayın.", "authentication_settings": "Yetkilendirme Ayarları", "authentication_settings_description": "Şifre, OAuth, ve diğer yetkilendirme ayarlarını yönet", @@ -44,7 +45,7 @@ "backup_database_enable_description": "Veritabanı yığınlarını etkinleştir", "backup_keep_last_amount": "Tutulması gereken geçmiş yığını miktarı", "backup_settings": "Veritabanı yığını ayarları", - "backup_settings_description": "Veritabanı Yedekleme Ayarlarını Yönet", + "backup_settings_description": "Veritabanı döküm ayarlarını yönet.", "cleared_jobs": "{job} için işler temizlendi", "config_set_by_file": "Ayarlar şuanda config dosyası tarafından ayarlanmıştır", "confirm_delete_library": "{library} kütüphanesini silmek istediğinize emin misiniz?", @@ -68,7 +69,9 @@ "force_delete_user_warning": "UYARI: Bu işlem kullanıcıyı ve tüm varlıkları anında kaldıracaktır. Bu geri alınamaz ve dosyalar geri getirilemez.", "image_format": "Biçim", "image_format_description": "WebP, JPEG'e göre daha küçük dosya boyutu sunar fakat işlemesi daha uzun sürer.", + "image_fullsize_description": "Yakınlaştırıldığında kullanılan, meta verileri kaldırılmış tam boyutlu görüntü", "image_fullsize_enabled": "Tam boyutlu görüntü üretimini etkinleştir", + "image_fullsize_enabled_description": "Yerleşik önizlemeyi tercih et” seçeneği etkinleştirildiğinde, yerleşik önizlemeler dönüştürme yapılmadan doğrudan kullanılır. JPEG gibi web dostu formatlar bu ayardan etkilenmez.", "image_fullsize_quality_description": "1-100 arasında tam boyutlu görüntü kalitesi. Daha yüksek kalitelidir, ancak daha büyük dosyalar üretir.", "image_fullsize_title": "Tam boyutlu görüntü ayarları", "image_prefer_embedded_preview": "Gömülü önizlemeyi tercih et", @@ -168,7 +171,7 @@ "note_apply_storage_label_previous_assets": "Not: Daha önce yüklenen varlıklara Depolama Etiketi uygulamak için şu komutu çalıştırın", "note_cannot_be_changed_later": "NOT: Bu daha sonra değiştirilemez!", "notification_email_from_address": "Şu adresten", - "notification_email_from_address_description": "Göndericinin email adresi, örnek: \"Immich Fotoğraf Sunucusu \"", + "notification_email_from_address_description": "Gönderen e-posta adresi, örneğin: \"Immich Görsel Sunucusu \". E-posta gönderilmesine izin verdiğiniz bir adres kullandığınızdan emin olun.", "notification_email_host_description": "E-posta sunucusunun ana bilgisayarı (örneğin, smtp.immich.app)", "notification_email_ignore_certificate_errors": "Sertifika hatalarını görmezden gel", "notification_email_ignore_certificate_errors_description": "TLS sertifika doğrulama ayarlarını görmezden gel (Önerilmez)", @@ -188,6 +191,7 @@ "oauth_auto_register": "Otomatik kayıt", "oauth_auto_register_description": "OAuth ile giriş yapan yeni kullanıcıları otomatik kaydet", "oauth_button_text": "Buton yazısı", + "oauth_client_secret_description": "OAuth sağlayıcısı PKCE (Kod Değişimi İçin Kanıt Anahtarı) desteği sunmuyorsa gereklidir", "oauth_enable_description": "OAuth ile giriş yap", "oauth_mobile_redirect_uri": "Mobil yönlendirme URL'si", "oauth_mobile_redirect_uri_override": "Mobilde zorla kullanılacak Yönlendirme Adresi", @@ -200,7 +204,7 @@ "oauth_storage_quota_claim": "Depolama kotası talebi", "oauth_storage_quota_claim_description": "Kullanıcıya depolama kotası koymak için kullanılacak değer (en: OAuth claim).", "oauth_storage_quota_default": "Varsayılan depolama kotası (GiB)", - "oauth_storage_quota_default_description": "Değer (en: OAuth claim) mevcut değilse konulacak kota. GiB cinsinden, sınırsız kota için 0 kullanın.", + "oauth_storage_quota_default_description": "Değer (en: OAuth claim) mevcut değilse GiB cinsinden konulacak kota.", "oauth_timeout": "İstek zaman aşımı", "oauth_timeout_description": "Milisaniye cinsinden istek zaman aşımı", "password_enable_description": "Email ve şifre ile giriş yap", @@ -237,9 +241,10 @@ "storage_template_hash_verification_enabled_description": "Hash doğrulamayı etkinleştirir, eğer ne işe yaradığını bilmiyorsanız bunu devre dışı bırakmayın", "storage_template_migration": "Depolama şablonu birleştirme", "storage_template_migration_description": "Geçerli {template} ayarlarını daha önce yüklenmiş olan varlıklara uygula", - "storage_template_migration_info": "Şablon ayarlarındaki değişiklikler sadece yeni varlıklara uygulanacak. Şablon ayarlarını daha önce yüklenmiş olan varlıklara uygulamak için {job} çalıştırın.", + "storage_template_migration_info": "Depolama şablonu tüm dosya uzantılarını küçük harfe dönüştürecektir. Şablon ayarlarındaki değişiklikler sadece yeni varlıklara uygulanacak. Şablon ayarlarını daha önce yüklenmiş olan varlıklara uygulamak için {job} çalıştırın.", "storage_template_migration_job": "Depolama Adreslerini Değiştirme Görevi", "storage_template_more_details": "Bu özellik hakkında daha fazla bilgi için, Depolama Şablonu ve onun etkileri kısmına bakın", + "storage_template_onboarding_description_v2": "Etkinleştirildiğinde, bu özellik dosyaları kullanıcı tanımlı bir şablona göre otomatik olarak organize eder. Daha fazla bilgi için lütfen belgelere bakın.", "storage_template_path_length": "Tahmini dosya adresi uzunluğu: {length, number}/{limit, number}", "storage_template_settings": "Depolama Şablonu", "storage_template_settings_description": "Yüklenen dosyanın ismini ve klasör yapısını düzenle", @@ -254,7 +259,7 @@ "template_email_update_album": "Albüm Şablonunu Güncelle", "template_email_welcome": "Hoş geldiniz e-posta şablonu", "template_settings": "Bildirim Şablonları", - "template_settings_description": "Bildirim şablonlarını yönet.", + "template_settings_description": "Bildirim şablonlarını yönet", "theme_custom_css_settings": "Özel CSS", "theme_custom_css_settings_description": "CSS (Cascading Style Sheets) kullanılarak Immich'in tasarımı değiştirilebilir.", "theme_settings": "Tema ayarları", @@ -286,13 +291,13 @@ "transcoding_encoding_options": "Kodlama Seçenekleri", "transcoding_encoding_options_description": "Kodlanmış videolar için kodekleri, çözünürlüğü, kaliteyi ve diğer seçenekleri ayarlayın", "transcoding_hardware_acceleration": "Donanım Hızlandırma", - "transcoding_hardware_acceleration_description": "Deneysel; daha hızlı, fakat aynı bitrate ayarlarında daha düşük kaliteye sahip", + "transcoding_hardware_acceleration_description": "Deneysel: daha hızlı dönüştürme, ancak aynı bit hızında kaliteyi düşürebilir", "transcoding_hardware_decoding": "Donanım çözücü", "transcoding_hardware_decoding_setting_description": "Uçtan uca hızlandırmayı, sadece kodlamayı hızlandırmanın yerine etkinleştirir. Tüm videolarda çalışmayabilir.", "transcoding_max_b_frames": "Maksimum B-kareler", "transcoding_max_b_frames_description": "Daha yüksek değerler sıkıştırma verimliliğini artırır, ancak kodlamayı yavaşlatır. Eski cihazlarda donanım hızlandırma ile uyumlu olmayabilir. 0, B-çerçevelerini devre dışı bırakır, -1 ise bu değeri otomatik olarak ayarlar.", "transcoding_max_bitrate": "Maksimum bitrate", - "transcoding_max_bitrate_description": "Maksimum bit hızı ayarlamak, kaliteye küçük bir maliyetle dosya boyutlarını daha öngörülebilir hale getirebilir.", + "transcoding_max_bitrate_description": "Maksimum bit hızı ayarlamak, kaliteyi az bir maliyetle düşürerek dosya boyutlarını daha öngörülebilir hale getirebilir. 720p çözünürlükte, tipik değerler VP9 veya HEVC için 2600 kbit/s, H.264 için ise 4500 kbit/s’dir. 0 olarak ayarlanırsa devre dışı bırakılır.", "transcoding_max_keyframe_interval": "Maksimum ana kare aralığı", "transcoding_max_keyframe_interval_description": "Ana kareler arasındaki maksimum kare mesafesini ayarlar. Düşük değerler sıkıştırma verimliliğini kötüleştirir, ancak arama sürelerini iyileştirir ve hızlı hareket içeren sahnelerde kaliteyi artırabilir. 0 bu değeri otomatik olarak ayarlar.", "transcoding_optimal_description": "Hedef çözünürlükten yüksek veya kabul edilen formatta olmayan videolar", @@ -352,6 +357,7 @@ "admin_password": "Yönetici Şifresi", "administration": "Yönetim", "advanced": "Gelişmiş", + "advanced_settings_enable_alternate_media_filter_subtitle": "Eşleme sırasında medyayı alternatif ölçütlere göre süzgeçten geçirmek için bu seçeneği kullanın. Uygulamanın tüm albümleri algılamasında sorun yaşıyorsanız yalnızca bu durumda deneyin.", "advanced_settings_enable_alternate_media_filter_title": "[DENEYSEL] Alternatif cihaz albüm eşleme süzgeci kullanın", "advanced_settings_log_level_title": "Günlük düzeyi: {level}", "advanced_settings_prefer_remote_subtitle": "Bazı cihazlar, cihazdaki öğelerin küçük resimlerini göstermekte çok yavaştır. Bunun yerine sunucudaki küçük resimleri göstermek için bu ayarı etkinleştirin.", @@ -360,6 +366,7 @@ "advanced_settings_proxy_headers_title": "Proxy Header'lar", "advanced_settings_self_signed_ssl_subtitle": "Sunucu uç noktası için SSL sertifika doğrulamasını atlar. Kendinden imzalı sertifikalar için gereklidir.", "advanced_settings_self_signed_ssl_title": "Kendi kendine imzalanmış SSL sertifikalarına izin ver", + "advanced_settings_sync_remote_deletions_subtitle": "Web üzerinde işlem yapıldığında, bu aygıttaki varlığı otomatik olarak sil veya geri yükle", "advanced_settings_sync_remote_deletions_title": "Uzaktan silinmeleri eşle [DENEYSEL]", "advanced_settings_tile_subtitle": "Gelişmiş kullanıcı ayarları", "advanced_settings_troubleshooting_subtitle": "Sorun giderme için ek özellikleri etkinleştirin", @@ -392,11 +399,14 @@ "album_viewer_appbar_share_err_remove": "Albümden öğeleri kaldırmada sorunlar var", "album_viewer_appbar_share_err_title": "Albüm başlığı değiştirilemedi", "album_viewer_appbar_share_leave": "Albümden çık", - "album_viewer_appbar_share_to": "Paylaş:", + "album_viewer_appbar_share_to": "Paylaşma", "album_viewer_page_share_add_users": "Kullanıcı ekle", "album_with_link_access": "Link'e sahip olan herhangi bir kişinin bu albümdeki fotoğrafları ve kişileri görmesine izin ver.", "albums": "Albümler", "albums_count": "{count, plural, one {{count, number} Albüm} other {{count, number} Albüm}}", + "albums_default_sort_order": "Varsayılan albüm sıralama düzeni", + "albums_default_sort_order_description": "Yeni albüm oluştururken kullanılacak başlangıç varlık sıralama düzeni.", + "albums_feature_description": "Diğer kullanıcılarla paylaşılabilen varlık koleksiyonları.", "all": "Tümü", "all_albums": "Tüm Albümler", "all_people": "Tüm Kişiler", @@ -417,6 +427,7 @@ "app_settings": "Uygulama Ayarları", "appears_in": "Şurada görünür", "archive": "Arşiv", + "archive_action_prompt": "{count} arşive eklendi", "archive_or_unarchive_photo": "Fotoğrafı arşivle/arşivden çıkar", "archive_page_no_archived_assets": "Arşivlenmiş öğe bulunamadı", "archive_page_title": "Arşiv ({count})", @@ -454,10 +465,12 @@ "assets": "Varlıklar", "assets_added_count": "{count, plural, one {# varlık eklendi} other {# varlık eklendi}}", "assets_added_to_album_count": "{count, plural, one {# varlık} other {# varlık}} albüme eklendi", - "assets_added_to_name_count": "{count, plural, one {# varlık} other {# varlık}} {hasName, select, true {{name}} other {yeni albüm}} içine eklendi", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Varlık} other {Varlıklar}} albüme eklenemiyor", "assets_count": "{count, plural, one {# varlık} other {# varlıklar}}", "assets_deleted_permanently": "{count} öğe kalıcı olarak silindi", "assets_deleted_permanently_from_server": "{count} öğe kalıcı olarak Immich sunucusundan silindi", + "assets_downloaded_failed": "{count, plural, one {# dosya indirildi – {error} dosya indirilemedi} other {# dosya indirildi – {error} dosya indirilemedi}}", + "assets_downloaded_successfully": "{count, plural, one {# dosya başarıyla indirildi} other {# dosya başarıyla indirildi}}", "assets_moved_to_trash_count": "{count, plural, one {# varlık} other {# varlık}} çöpe taşındı", "assets_permanently_deleted_count": "Kalıcı olarak silindi {count, plural, one {# varlık} other {# varlıklar}}", "assets_removed_count": "Kaldırıldı {count, plural, one {# varlık} other {# varlıklar}}", @@ -484,12 +497,12 @@ "backup_album_selection_page_selection_info": "Seçim Bilgileri", "backup_album_selection_page_total_assets": "Toplam eşsiz öğeler", "backup_all": "Tümü", - "backup_background_service_backup_failed_message": "Yedekleme başarısız. Tekrar deneniyor...", - "backup_background_service_connection_failed_message": "Sunucuya bağlanılamadı. Tekrar deneniyor...", + "backup_background_service_backup_failed_message": "Yedekleme başarısız. Tekrar deneniyor…", + "backup_background_service_connection_failed_message": "Sunucuya bağlanılamadı. Tekrar deneniyor…", "backup_background_service_current_upload_notification": "{filename} yükleniyor", "backup_background_service_default_notification": "Yeni öğeler kontrol ediliyor…", "backup_background_service_error_title": "Yedekleme hatası", - "backup_background_service_in_progress_notification": "Öğeleriniz yedekleniyor...", + "backup_background_service_in_progress_notification": "Öğeleriniz yedekleniyor…", "backup_background_service_upload_failure_notification": "{filename} yüklemesi başarısız oldu", "backup_controller_page_albums": "Yedekleme Albümleri", "backup_controller_page_background_app_refresh_disabled_content": "Arka planda yedeklemeyi kullanabilmek için Ayarlar > Genel > Arka Planda Uygulama Yenileme bölümünden arka planda uygulama yenilemeyi etkinleştirin.", @@ -577,6 +590,8 @@ "cannot_merge_people": "Kişiler birleştirilemiyor", "cannot_undo_this_action": "Bu işlem geri alınamaz!", "cannot_update_the_description": "Açıklama güncellenemiyor", + "cast": "Yansıt", + "cast_description": "Kullanılabilir yansıtma hedeflerini yapılandır", "change_date": "Tarihi değiştir", "change_description": "Açıklamayı değiştir", "change_display_order": "Görüntüleme sırasını değiştir", @@ -636,6 +651,7 @@ "confirm_tag_face": "Bu yüzü {name} olarak etiketlemek ister misiniz?", "confirm_tag_face_unnamed": "Bu yüzü etiketlemek ister misin?", "connected_device": "Cihaz bağlandı", + "connected_to": "Bağlı", "contain": "İçermek", "context": "Bağlam", "continue": "Devam et", @@ -644,8 +660,8 @@ "control_bottom_app_bar_delete_from_local": "Cihazdan sil", "control_bottom_app_bar_edit_location": "Konumu Düzenle", "control_bottom_app_bar_edit_time": "Tarih ve Saati Düzenle", - "control_bottom_app_bar_share_link": "İlişimi paylaş", - "control_bottom_app_bar_share_to": "Paylaş:", + "control_bottom_app_bar_share_link": "Bağlantıyı Paylaş", + "control_bottom_app_bar_share_to": "Paylaşma", "control_bottom_app_bar_trash_from_immich": "Çöp Kutusuna At", "copied_image_to_clipboard": "Resim, panoya kopyalandı.", "copied_to_clipboard": "Panoya kopyalandı!", @@ -687,6 +703,7 @@ "daily_title_text_date": "dd MMM E", "daily_title_text_date_year": "dd MMM yyyy E", "dark": "Koyu", + "dark_theme": "Karanlık temaya geç", "date_after": "Sonraki tarih", "date_and_time": "Tarih ve Zaman", "date_before": "Önceki tarih", @@ -702,6 +719,7 @@ "default_locale": "Varsayılan Yerel Ayar", "default_locale_description": "Tarihleri ve sayıları tarayıcınızın yerel ayarına göre biçimlendirin", "delete": "Sil", + "delete_action_prompt": "{count} kalıcı olarak silindi", "delete_album": "Albümü sil", "delete_api_key_prompt": "Bu API anahtarını silmek istediğinizden emin misiniz?", "delete_dialog_alert": "Bu öğeler cihazınızdan ve Immich'ten kalıcı olarak silinecektir", @@ -715,6 +733,7 @@ "delete_key": "Anahtarı sil", "delete_library": "Kütüphaneyi sil", "delete_link": "Bağlantıyı sil", + "delete_local_action_prompt": "{count} yerel olarak silindi", "delete_local_dialog_ok_backed_up_only": "Sadece Yedeklenmişleri Sil", "delete_local_dialog_ok_force": "Yine de Sil", "delete_others": "Diğerlerini sil", @@ -734,6 +753,7 @@ "disallow_edits": "Değişikliklere izin verme", "discord": "Discord", "discover": "Keşfet", + "discovered_devices": "Keşfedilen aygıtlar", "dismiss_all_errors": "Tüm hataları yoksay", "dismiss_error": "Hatayı yoksay", "display_options": "Görüntüleme seçenekleri", @@ -802,6 +822,7 @@ "enable_biometric_auth_description": "Biyometrik kimlik doğrulamasını etkinleştirmek için PIN kodu girin", "enabled": "Etkinleştirildi", "end_date": "Bitiş tarihi", + "enqueued": "Kuyruğa alındı", "enter_wifi_name": "Wi-Fi adını girin", "enter_your_pin_code": "Pin kodu girin", "enter_your_pin_code_subtitle": "Kilitli klasöre erişmek için PIN kodunuzu girin", @@ -810,6 +831,7 @@ "error_delete_face": "Yüzü varlıktan silme hatası", "error_loading_image": "Resim yüklenirken hata oluştu", "error_saving_image": "Hata: {error}", + "error_tag_face_bounding_box": "Yüz etiketleme hatası – sınırlayıcı kutu koordinatları alınamadı", "error_title": "Bir Hata Oluştu - Bir şeyler ters gitti", "errors": { "cannot_navigate_next_asset": "Sonraki varlığa geçiş yapılamıyor", @@ -859,6 +881,7 @@ "unable_to_archive_unarchive": "{archived, select, true {Arşivleme} other {Arşivden çıkarma}} işlemi yapılamıyor", "unable_to_change_album_user_role": "Albüm kullanıcı rolü değiştirilemiyor", "unable_to_change_date": "Tarih değiştirilemiyor", + "unable_to_change_description": "Açıklama değiştirilemiyor", "unable_to_change_favorite": "Favori durumu değiştirilemiyor", "unable_to_change_location": "Konum değiştirilemiyor", "unable_to_change_password": "Şifre değiştirilemiyor", @@ -902,6 +925,7 @@ "unable_to_remove_partner": "Ortak kaldırılamıyor", "unable_to_remove_reaction": "Reaksiyon kaldırılamıyor", "unable_to_reset_password": "Şifre sıfırlanamıyor", + "unable_to_reset_pin_code": "Pin kodunu sıfırlanamıyor", "unable_to_resolve_duplicate": "Çiftler çözümlenemiyor", "unable_to_restore_assets": "Varlıklar geri yüklenemiyor", "unable_to_restore_trash": "Çöp geri yüklenemiyor", @@ -935,6 +959,9 @@ "exif_bottom_sheet_location": "KONUM", "exif_bottom_sheet_people": "KİŞİLER", "exif_bottom_sheet_person_add_person": "İsim ekle", + "exif_bottom_sheet_person_age_months": "Yaş: {months} ay", + "exif_bottom_sheet_person_age_year_months": "Yaş: 1 yıl, {months} ay", + "exif_bottom_sheet_person_age_years": "Yaş: {years}", "exit_slideshow": "Slayt gösterisinden çık", "expand_all": "Hepsini genişlet", "experimental_settings_new_asset_list_subtitle": "Çalışmalar devam ediyor", @@ -952,13 +979,17 @@ "external": "Harici", "external_libraries": "Harici kütüphaneler", "external_network": "Harici ağlar", - "external_network_sheet_info": "Belirlenmiş WiFi ağına bağlı olmadığında uygulama, yukarıdan aşağıya doğru ulaşabileceği aşağıdaki URL'lerden ilki aracılığıyla sunucuya bağlanacaktır", + "external_network_sheet_info": "Belirlenmiş Wi-Fi ağına bağlı olmadığında uygulama, yukarıdan aşağıya doğru ulaşabileceği aşağıdaki URL'lerden ilki aracılığıyla sunucuya bağlanacaktır", "face_unassigned": "Yüz atanmadı", + "failed": "Başarısız", + "failed_to_authenticate": "Kimlik doğrulaması yapılamadı", "failed_to_load_assets": "Varlıklar yüklenemedi", - "favorite": "Favori", - "favorite_or_unfavorite_photo": "Favoriye ekle veya çıkar", - "favorites": "Favoriler", - "favorites_page_no_favorites": "Favori öğe bulunamadı", + "failed_to_load_folder": "Klasör yüklenemedi", + "favorite": "Gözde", + "favorite_action_prompt": "{count} gözdelere eklendi", + "favorite_or_unfavorite_photo": "Gözdeye ekle veya çıkar", + "favorites": "Gözdeler", + "favorites_page_no_favorites": "Gözde öge bulunamadı", "feature_photo_updated": "Özellikli fotoğraf güncellendi", "features": "Özellikler", "features_setting_description": "Uygulamanın özelliklerini yönet", @@ -968,11 +999,16 @@ "filetype": "Dosya tipi", "filter": "Filtre", "filter_people": "Kişileri filtrele", + "filter_places": "Yerleri süz", "find_them_fast": "Adlarına göre hızlıca bul", "fix_incorrect_match": "Yanlış eşleştirmeyi düzelt", + "folder": "Klasör", + "folder_not_found": "Klasör bulunamadı", "folders": "Klasörler", "folders_feature_description": "Dosya sistemindeki fotoğraf ve videoları klasör görünümüyle keşfedin", "forward": "İleri", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Bu özellik, çalışabilmek için Google'dan harici kaynaklar yükler.", "general": "Genel", "get_help": "Yardım Al", "get_wifiname_error": "Wi-Fi adı alınamadı. Gerekli izinleri verdiğinizden ve bir Wi-Fi ağına bağlı olduğunuzdan emin olun", @@ -1012,12 +1048,16 @@ "home_page_building_timeline": "Zaman çizelgesi oluşturuluyor", "home_page_delete_err_partner": "Partner öğeleri silinemez, atlanıyor", "home_page_delete_remote_err_local": "Uzaktan silme seçimindeki yerel öğeler atlanıyor", - "home_page_favorite_err_local": "Yerel öğeler henüz favorilere eklenemiyor, atlanıyor", - "home_page_favorite_err_partner": "Partner öğeleri henüz favorilere eklenemiyor, atlanıyor", + "home_page_favorite_err_local": "Yerel ögeler henüz gözdelere eklenemiyor, atlanıyor", + "home_page_favorite_err_partner": "Ortak ögeleri henüz gözdelere eklenemiyor, atlanıyor", "home_page_first_time_notice": "Uygulamayı ilk kez kullanıyorsanız, zaman çizelgesinin albümlerdeki fotoğraf ve videolar ile oluşturulabilmesi için lütfen yedekleme için albüm(ler) seçtiğinizden emin olun.", + "home_page_locked_error_local": "Yerel varlıklar kilitli klasöre taşınamıyor, atlanıyor", + "home_page_locked_error_partner": "Ortak varlıklar kilitli klasöre taşınamıyor, atlanıyor", "home_page_share_err_local": "Yerel öğeler bağlantı ile paylaşılamaz, atlanıyor", "home_page_upload_err_limit": "Aynı anda en fazla 30 öğe yüklenebilir, atlanabilir", + "host": "Ana bilgisayar", "hour": "Saat", + "id": "ID", "ignore_icloud_photos": "iCloud Fotoğraflarını Yok Say", "ignore_icloud_photos_description": "iCloud'a yüklenmiş fotoğraflar Immich sunucusuna yüklenmesin", "image": "Resim", @@ -1057,6 +1097,12 @@ "invalid_date_format": "Geçersiz tarih formatı", "invite_people": "Kişileri Davet Et", "invite_to_album": "Albüme davet et", + "ios_debug_info_fetch_ran_at": "Veri çekme {dateTime} tarihinde çalıştırıldı", + "ios_debug_info_last_sync_at": "Son eşleme: {dateTime}", + "ios_debug_info_no_processes_queued": "Hiçbir arka plan işlemi kuyruğa alınmadı", + "ios_debug_info_no_sync_yet": "Henüz hiçbir arka plan eşleme görevi çalıştırılmadı", + "ios_debug_info_processes_queued": "{count, plural, one {{count} arka plan işlemi kuyruğa alındı} other {{count} arka plan işlemi kuyruğa alındı}}", + "ios_debug_info_processing_ran_at": "İşleme {dateTime} tarihinde çalıştırıldı", "items_count": "{count, plural, one {# Öğe} other {# Öğe}}", "jobs": "Görevler", "keep": "Koru", @@ -1065,6 +1111,9 @@ "kept_this_deleted_others": "Bu varlık tutuldu ve {count, plural, one {# varlık} other {# varlık}} silindi", "keyboard_shortcuts": "Klavye kısayolları", "language": "Dil", + "language_no_results_subtitle": "Arama teriminizi değiştirmeyi deneyin", + "language_no_results_title": "Dil bulunamadı", + "language_search_hint": "Dilleri ara...", "language_setting_description": "Tercih ettiğiniz dili seçiniz", "last_seen": "Son görülme", "latest_version": "En son versiyon", @@ -1081,6 +1130,7 @@ "library_page_sort_created": "Oluşturma tarihi", "library_page_sort_last_modified": "Son düzenleme", "library_page_sort_title": "Albüm başlığı", + "licenses": "Lisanslar", "light": "Açık", "like_deleted": "Beğeni silindi", "link_motion_video": "Hareket videosunu bağla", @@ -1090,17 +1140,21 @@ "list": "Liste", "loading": "Yükleniyor", "loading_search_results_failed": "Arama sonuçları yüklenemedi", + "local_asset_cast_failed": "Sunucuya yüklenmemiş bir varlık yansıtılamaz", "local_network": "Yerel Wi-Fi", "local_network_sheet_info": "Uygulama belirlenmiş Wi-Fi ağını kullanırken bu URL üzerinden sunucuya bağlanacaktır", "location_permission": "Konum izni", - "location_permission_content": "Otomatik geçiş özelliğinin çalışabilmesi için Immich'in mevcut Wi-Fi ağının adını bilmesi, bunu sağlamak için de tam konum iznine ihtiyacı vardır.", + "location_permission_content": "Otomatik geçiş özelliğinin çalışabilmesi için Immich'in mevcut Wi-Fi ağının adını bilmesi, bunu sağlamak için de tam konum iznine ihtiyacı vardır", "location_picker_choose_on_map": "Haritada seç", "location_picker_latitude_error": "Geçerli bir enlem yazın", "location_picker_latitude_hint": "Buraya enlem yazın", "location_picker_longitude_error": "Geçerli bir boylam yazın", "location_picker_longitude_hint": "Buraya boylam yazın", + "lock": "Kilitle", + "locked_folder": "Kilitli Klasör", "log_out": "Oturumu kapat", "log_out_all_devices": "Tüm Cihazlarda Oturumu Kapat", + "logged_in_as": "{user} olarak oturum açıldı", "logged_out_all_devices": "Tüm cihazlarda oturum kapatıldı", "logged_out_device": "Oturum kapatılmış cihaz", "login": "Giriş yap", @@ -1124,7 +1178,7 @@ "login_form_server_empty": "Sunucu URL'si girin", "login_form_server_error": "Sunucuya bağlanılamadı.", "login_has_been_disabled": "Giriş devre dışı bırakıldı.", - "login_password_changed_error": "Parola güncellenirken bir hata oluştu.", + "login_password_changed_error": "Parolanız güncellenirken bir hata oluştu.", "login_password_changed_success": "Parola güncellendi", "logout_all_device_confirmation": "Tüm cihazlarda oturum kapatmak istediğinizden emin misiniz?", "logout_this_device_confirmation": "Bu cihazda oturum kapatmak istediğinizden emin misiniz?", @@ -1133,6 +1187,7 @@ "loop_videos": "Videoları döngüye al", "loop_videos_description": "Ayrıntı görünümünde videoların otomatik döngüye alınmasını etkinleştir.", "main_branch_warning": "Geliştirme sürümü kullanıyorsunuz. Yayınlanan bir sürüm kullanmanızı önemle tavsiye ederiz!", + "main_menu": "Ana menü", "make": "Marka", "manage_shared_links": "Paylaşılan bağlantıları yönet", "manage_sharing_with_partners": "Ortaklarla paylaşımı yönet", @@ -1163,9 +1218,12 @@ "map_settings_dialog_title": "Harita Ayarları", "map_settings_include_show_archived": "Arşivdekileri dahil et", "map_settings_include_show_partners": "Partnerleri Dahil Et", - "map_settings_only_show_favorites": "Sadece Favorileri Göster", + "map_settings_only_show_favorites": "Sadece Gözdeleri Göster", "map_settings_theme_settings": "Harita Teması", "map_zoom_to_see_photos": "Fotoğrafları görmek için uzaklaştırın", + "mark_all_as_read": "Tümünü okundu olarak işaretle", + "mark_as_read": "Okundu olarak işaretle", + "marked_all_as_read": "Tümü okundu olarak işaretlendi", "matches": "Eşleşenler", "media_type": "Medya türü", "memories": "Anılar", @@ -1186,8 +1244,16 @@ "minimize": "Küçült", "minute": "Dakika", "missing": "Eksik", + "model": "Model", "month": "Ay", + "monthly_title_text_date_format": "MMMM y", "more": "Daha fazla", + "move": "Taşı", + "move_off_locked_folder": "Kilitli klasörden taşı", + "move_to_locked_folder": "Kilitli klasöre taşı", + "move_to_locked_folder_confirmation": "Bu fotoğraflar ve videolar tüm albümlerden kaldırılacak ve yalnızca kilitli klasörden görüntülenebilecektir", + "moved_to_archive": "{count, plural, one {# öğe arşive taşındı} other {# öğe arşive taşındı}}", + "moved_to_library": "{count, plural, one {# öğe kitaplığa taşındı} other {# öğe kitaplığa taşındı}}", "moved_to_trash": "Çöp kutusuna taşındı", "multiselect_grid_edit_date_time_err_read_only": "Salt okunur öğelerin tarihi düzenlenemedi, atlanıyor", "multiselect_grid_edit_gps_err_read_only": "Salt okunur öğelerin konumu düzenlenemedi, atlanıyor", @@ -1203,6 +1269,7 @@ "new_password": "Yeni şifre", "new_person": "Yeni kişi", "new_pin_code": "Yeni PIN kodu", + "new_pin_code_subtitle": "Kilitli klasöre ilk kez erişiyorsunuz. Bu sayfaya güvenli erişim için bir PIN kodu oluşturun", "new_user_created": "Yeni kullanıcı oluşturuldu", "new_version_available": "YENİ VERSİYON MEVCUT", "newest_first": "Önce en yeniler", @@ -1215,19 +1282,25 @@ "no_archived_assets_message": "Fotoğraf görünümünüzden kaldırmak için fotoğrafları ve videoları arşivleyin", "no_assets_message": "İLK FOTOĞRAFINIZI YÜKLEMEK İÇİN TIKLAYIN", "no_assets_to_show": "Gösterilecek öğe yok", + "no_cast_devices_found": "Yansıtılacak cihaz bulunamadı", "no_duplicates_found": "Çift bulunamadı.", "no_exif_info_available": "EXIF bilgisi mevcut değil", "no_explore_results_message": "Koleksiyonunuzu keşfetmek için daha fazla fotoğraf yükleyin.", - "no_favorites_message": "En sevdiğiniz fotoğraf ve videoları hızlıca bulmak için favoriler ekleyin", + "no_favorites_message": "En sevdiğiniz fotoğraf ve videoları hızlıca bulmak için gözdelere ekleyin", "no_libraries_message": "Fotoğraf ve videolarınızı görmek için bir harici kütüphane oluşturun", + "no_locked_photos_message": "Kilitli klasördeki fotoğraf ve videolar gizlidir; kitaplığınızda gezinirken veya arama yaparken görünmezler.", "no_name": "İsim yok", + "no_notifications": "Bildirim yok", + "no_people_found": "Eşleşen kişi bulunamadı", "no_places": "Yer yok", "no_results": "Sonuç bulunamadı", "no_results_description": "Eş anlamlı ya da daha genel anlamlı bir kelime deneyin", "no_shared_albums_message": "Fotoğrafları ve videoları ağınızdaki kişilerle paylaşmak için bir albüm oluşturun", "not_in_any_album": "Hiçbir albümde değil", + "not_selected": "Seçilmedi", "note_apply_storage_label_to_previously_uploaded assets": "Not: Daha önce yüklenen varlıklar için bir depolama yolu etiketi uygulamak üzere şunu başlatın", "notes": "Notlar", + "nothing_here_yet": "Burada henüz bir şey yok", "notification_permission_dialog_content": "Bildirimleri etkinleştirmek için cihaz ayarlarına gidin ve izin verin.", "notification_permission_list_tile_content": "Bildirimleri etkinleştirmek için izin verin.", "notification_permission_list_tile_enable_button": "Bildirimleri Etkinleştir", @@ -1235,17 +1308,22 @@ "notification_toggle_setting_description": "E-posta bildirimlerine izin ver", "notifications": "Bildirimler", "notifications_setting_description": "Bildirimleri yönetin", + "oauth": "OAuth", "official_immich_resources": "Resmi Immich Kaynakları", "offline": "Çevrim dışı", "ok": "Tamam", "oldest_first": "Eski olan önce", "on_this_device": "Bu cihazda", "onboarding": "Uyum Süreci", - "onboarding_privacy_description": "Şu (isteğe bağlı) özellikler harici hizmetlere dayanır ve yönetim ayarlarından herhangi bir zamanda devre dışı bırakılabilir.", + "onboarding_locale_description": "Tercih ettiğiniz dili seçin. Bu ayarı daha sonra değiştirebilirsiniz.", + "onboarding_privacy_description": "Şu (isteğe bağlı) özellikler harici hizmetlere dayanır ve ayarlardan herhangi bir zamanda devre dışı bırakılabilir.", + "onboarding_server_welcome_description": "Örneğinizi bazı yaygın ayarlarla ayarlayalım.", "onboarding_theme_description": "İnstance’ınız için bir renk teması seçin. Bunu daha sonra ayarlarınızdan değiştirebilirsiniz.", + "onboarding_user_welcome_description": "Haydi başlayalım!", "onboarding_welcome_user": "Hoş geldin, {user}", "online": "Çevrimiçi", - "only_favorites": "Sadece favoriler", + "only_favorites": "Sadece gözdeler", + "open": "Aç", "open_in_map_view": "Harita görünümünde aç", "open_in_openstreetmap": "OpenStreetMap'te Aç", "open_the_search_filters": "Arama filtrelerini aç", @@ -1268,7 +1346,7 @@ "partner_page_no_more_users": "Eklenecek başka kullanıcı yok", "partner_page_partner_add_failed": "Partner eklenemedi", "partner_page_select_partner": "Partner seç", - "partner_page_shared_to_title": "Paylaşıldı:", + "partner_page_shared_to_title": "Paylaşıldı", "partner_page_stop_sharing_content": "{partner} artık fotoğraflarınıza erişemeyecek.", "partner_sharing": "Ortak paylaşımı", "partners": "Ortaklar", @@ -1298,15 +1376,18 @@ "permanently_delete_assets_prompt": "Bu {count, plural, one {dosyayı} other {# dosyaları}} kalıcı olarak silmek istediğinizden emin misiniz? Bu işlem {count, plural, one {bu dosyayı} other {bu dosyaları}} albümlerinizden de kaldırır.", "permanently_deleted_asset": "Kalıcı olarak silinmiş ögeler", "permanently_deleted_assets_count": "{count, plural, one {# dosya} other {# dosya}} kalıcı olarak silindi", + "permission": "İzin", + "permission_empty": "İzniniz boş olmamalı", "permission_onboarding_back": "Geri", "permission_onboarding_continue_anyway": "Yine de devam et", "permission_onboarding_get_started": "Haydi başlayalım", "permission_onboarding_go_to_settings": "Ayarlara git", "permission_onboarding_permission_denied": "İzin reddedildi. Immich'i kullanmak için Ayarlar'da fotoğraf ve video izinlerini verin.", - "permission_onboarding_permission_granted": "İzin verildi. Artık hazırsınız!", + "permission_onboarding_permission_granted": "İzin verildi! Artık hazırsınız.", "permission_onboarding_permission_limited": "Sınırlı izin. Immich'in tüm fotoğrav ve videolarınızı yedeklemesine ve yönetmesine izin vermek için Ayarlar'da fotoğraf ve video izinlerini verin.", "permission_onboarding_request": "Immich'in fotoğraflarınızı ve videolarınızı görüntüleyebilmesi için izne ihtiyacı var.", "person": "Kişi", + "person_birthdate": "{date} tarihinde doğdu", "person_hidden": "{name}{hidden, select, true { (gizli)} other {}}", "photo_shared_all_users": "Fotoğraflarınızı tüm kullanıcılarla paylaştınız gibi görünüyor veya paylaşacak kullanıcı bulunmuyor.", "photos": "Fotoğraflar", @@ -1317,6 +1398,7 @@ "pin_code_changed_successfully": "PIN kodu başarıyla değiştirildi", "pin_code_reset_successfully": "PIN kodu başarıyla sıfırlandı", "pin_code_setup_successfully": "PIN kodu başarıyla ayarlandı", + "pin_verification": "PIN kodu doğrulama", "place": "Konum", "places": "Konumlar", "places_count": "{count, plural, one {{count, number} yer} other {{count, number} yer}}", @@ -1324,19 +1406,26 @@ "play_memories": "Anıları oynat", "play_motion_photo": "Hareketli fotoğrafı oynat", "play_or_pause_video": "Videoyu oynat ya da durdur", + "please_auth_to_access": "Erişim için lütfen kimliğinizi doğrulayın", + "port": "Port", "preferences_settings_subtitle": "Uygulama tercihlerini düzenle", "preferences_settings_title": "Tercihler", "preset": "Ön ayar", "preview": "Önizleme", "previous": "Önceki", "previous_memory": "Önceki anı", - "previous_or_next_photo": "Önceki ya da sonraki fotoğraf", + "previous_or_next_day": "Gün ileri/geri", + "previous_or_next_month": "Ay ileri/geri", + "previous_or_next_photo": "Fotoğraf ileri/geri", + "previous_or_next_year": "Yıl ileri/geri", "primary": "Birincil", "privacy": "Gizlilik", + "profile": "Profil", "profile_drawer_app_logs": "Günlükler", "profile_drawer_client_out_of_date_major": "Mobil uygulama güncel değil. Lütfen en son ana sürüme güncelleyin.", "profile_drawer_client_out_of_date_minor": "Mobil uygulama güncel değil. Lütfen en son sürüme güncelleyin.", "profile_drawer_client_server_up_to_date": "Uygulama ve sunucu güncel", + "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "Sunucu güncel değil. Lütfen en son ana sürüme güncelleyin.", "profile_drawer_server_out_of_date_minor": "Sunucu güncel değil. Lütfen en son sürüme güncelleyin.", "profile_image_of_user": "{user} kullanıcısının profil resmi", @@ -1363,7 +1452,7 @@ "purchase_lifetime_description": "Ömür boyu geçerli", "purchase_option_title": "SATIN ALMA SEÇENEKLERİ", "purchase_panel_info_1": "Immich'in gelişimi zaman ve çaba gerektiriyor ve tam zamanlı geliştiricilerimiz var. Amacımız, açık kaynak yazılımı sürdürülebilir bir gelir kaynağı haline getirmek.", - "purchase_panel_info_2": "Bu satın alma işlemi Immich'te ek işlevsellik açmayacak. Immich'in gelişimini desteklemek için size güveniyoruz.", + "purchase_panel_info_2": "Ücretli özellikler (paywall) eklememeye kararlı olduğumuz için, bu satın alma işlemi Immich'te ek işlevsellik sağlamaz. Immich'in sürekli gelişimini desteklemek için sizin gibi kullanıcılara güveniyoruz.", "purchase_panel_title": "Projeyi destekleyin", "purchase_per_server": "Sunucu başına", "purchase_per_user": "Kullanıcı başına", @@ -1390,6 +1479,8 @@ "recent_searches": "Son aramalar", "recently_added": "Son eklenenler", "recently_added_page_title": "Son Eklenenler", + "recently_taken": "Son çekilenler", + "recently_taken_page_title": "Son Çekilenler", "refresh": "Yenile", "refresh_encoded_videos": "Kodlanmış videoları yenile", "refresh_faces": "Yüzleri yenile", @@ -1408,14 +1499,21 @@ "remove_custom_date_range": "Özel tarih aralığını kaldır", "remove_deleted_assets": "Çevrimdışı dosyaları kaldır", "remove_from_album": "Albümden çıkar", - "remove_from_favorites": "Favorilerden çıkar", + "remove_from_album_action_prompt": "{count} albümden kaldırıldı", + "remove_from_favorites": "Gözdelerden çıkar", + "remove_from_lock_folder_action_prompt": "{count} kilitli klasörden kaldırıldı", + "remove_from_locked_folder": "Kilitli klasörden kaldır", + "remove_from_locked_folder_confirmation": "Bu fotoğraf ve videoları kilitli klasörden çıkarmak istediğinizden emin misiniz? Çıkarıldıklarında kitaplığınızda görünür olacaklar.", "remove_from_shared_link": "Paylaşılan bağlantıdan çıkar", + "remove_memory": "Anıyı kaldır", + "remove_photo_from_memory": "Bu anıdan fotoğrafı kaldır", + "remove_tag": "Etiketi kaldır", "remove_url": "Bağlantıyı kaldır", "remove_user": "Kullanıcıyı çıkar", "removed_api_key": "API anahtarı {name} kaldırıldı", "removed_from_archive": "Arşivden çıkarıldı", - "removed_from_favorites": "Favorilerden kaldırıldı", - "removed_from_favorites_count": "{count, plural, other {#}} favorilerden çıkarıldı", + "removed_from_favorites": "Gözdelerden kaldırıldı", + "removed_from_favorites_count": "{count, plural, other {#}} gözdelerden çıkarıldı", "removed_memory": "Anı kaldırıldı", "removed_photo_from_memory": "Fotoğraf anıdan kaldırıldı", "removed_tagged_assets": "{count, plural, one {# dosya} other {# dosya}} etiketleri kaldırıldı", @@ -1460,7 +1558,7 @@ "search_by_context": "Bağlama göre ara", "search_by_description": "Açıklamaya göre ara", "search_by_description_example": "Sapa'da yürüyüş günü", - "search_by_filename": "Dosya adına göre ara", + "search_by_filename": "Dosya adına veya uzantısına göre ara", "search_by_filename_example": "Örn. IMG_1234.JPG veya PNG", "search_camera_make": "Kamera markasına göre ara...", "search_camera_model": "Kamera modeline göre ara...", @@ -1473,6 +1571,7 @@ "search_filter_date_title": "Tarih aralığı seç", "search_filter_display_option_not_in_album": "Albümde değil", "search_filter_display_options": "Görüntü Seçenekleri", + "search_filter_filename": "Dosya adına göre ara", "search_filter_location": "Konum", "search_filter_location_title": "Konum seç", "search_filter_media_type": "Medya Türü", @@ -1480,8 +1579,10 @@ "search_filter_people_title": "Kişi seç", "search_for": "Araştır", "search_for_existing_person": "Mevcut bir kişiyi ara", + "search_no_more_result": "Daha fazla sonuç yok", "search_no_people": "Kişi yok", "search_no_people_named": "\"{name}\" isimli bir kişi yok", + "search_no_result": "Sonuç bulunamadı. Farklı bir arama terimi veya kombinasyon deneyin", "search_options": "Arama seçenekleri", "search_page_categories": "Kategoriler", "search_page_motion_photos": "Canlı Fotoğraflar", @@ -1509,9 +1610,11 @@ "searching_locales": "Yerleri arıyor...", "second": "Saniye", "see_all_people": "Tüm kişileri gör", + "select": "Seç", "select_album_cover": "Albüm kapağı seç", "select_all": "Tümünü seç", "select_all_duplicates": "Tüm çiftleri seç", + "select_all_in": "{group} içindekilerin tümünü seç", "select_avatar_color": "Avatar rengini seç", "select_face": "Yüzü seç", "select_featured_photo": "Öne çıkan fotoğrafı seç", @@ -1519,6 +1622,7 @@ "select_keep_all": "Hepsini sakla", "select_library_owner": "Kütüphane sahibini seç", "select_new_face": "Yeni yüz seç", + "select_person_to_tag": "Etiketlemek için bir kişi seçin", "select_photos": "Fotoğrafları seç", "select_trash_all": "Hepsini çöpe at", "select_user_for_sharing_page_err_album": "Albüm oluşturulamadı", @@ -1531,6 +1635,7 @@ "server_info_box_server_url": "Sunucu URL", "server_offline": "Sunucu çevrimdışı", "server_online": "Sunucu çevrimiçi", + "server_privacy": "Sunucu Gizliliği", "server_stats": "Sunucu istatistikleri", "server_version": "Sunucu versiyonu", "set": "Ayarla", @@ -1540,6 +1645,7 @@ "set_date_of_birth": "Doğum tarihini ayarla", "set_profile_picture": "Profil resmini ayarla", "set_slideshow_to_fullscreen": "Slayt gösterisini tam ekran yap", + "set_stack_primary_asset": "Birincil varlık olarak ayarla", "setting_image_viewer_help": "Görüntüleyici önce küçük resmi gösterir, ardından orta boy önizlemeyi (etkinleştirilmişse) ve son olarak orijinali (etkinleştirilmişse) gösterir.", "setting_image_viewer_original_subtitle": "Orijinal tam çözünürlüklü görüntüyü göstermek için etkinleştirin. Veri kullanımını azaltmak için devre dışı bırakın (hem ağ hem de cihaz önbelleği).", "setting_image_viewer_original_title": "Orijinal görüntüyü göster", @@ -1560,6 +1666,8 @@ "setting_notifications_total_progress_subtitle": "Toplam yükleme ilerlemesi (tamamlanan/toplam)", "setting_notifications_total_progress_title": "Arkaplan yedeklemesi toplam ilerlemesini göster", "setting_video_viewer_looping_title": "Döngü", + "setting_video_viewer_original_video_subtitle": "Sunucudan video aktarılırken, transcode (dönüştürülmüş) sürüm mevcut olsa bile orijinal dosya oynatılır. Bu durum, arabelleğe alma (buffering) sorunlarına yol açabilir. Videolar yerel olarak mevcutsa, bu ayardan bağımsız olarak orijinal kalitede oynatılır.", + "setting_video_viewer_original_video_title": "Orijinal videoyu zorla", "settings": "Ayarlar", "settings_require_restart": "Bu ayarı uygulamak için lütfen Immich'i yeniden başlatın", "settings_saved": "Ayarlar kaydedildi", @@ -1568,6 +1676,7 @@ "share_add_photos": "Fotoğraf ekle", "share_assets_selected": "{count} seçili", "share_dialog_preparing": "Hazırlanıyor...", + "share_link": "Bağlantıyı Paylaş", "shared": "Paylaşılan", "shared_album_activities_input_disable": "Yoruma kapalı", "shared_album_activity_remove_content": "Bu etkinliği silmek istiyor musunuz?", @@ -1580,6 +1689,7 @@ "shared_by_user": "{user} tarafından paylaşıldı", "shared_by_you": "Senin tarafından paylaşıldı", "shared_from_partner": "{partner} tarafından paylaşılan fotoğraflar", + "shared_intent_upload_button_progress_text": "{current} / {total} Yüklendi", "shared_link_app_bar_title": "Paylaşılan Bağlantılar", "shared_link_clipboard_copied_massage": "Panoya kopyalandı", "shared_link_clipboard_text": "Bağlantı: {link}\nParola: {password}", @@ -1606,6 +1716,7 @@ "shared_link_expires_second": "Süresi {count} saniye içinde doluyor", "shared_link_expires_seconds": "{count} sanyei içinde süresi doluyor", "shared_link_individual_shared": "Bireysel paylaşımlı", + "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Paylaşılan Bağlantıları Yönet", "shared_link_options": "Paylaşılan bağlantı seçenekleri", "shared_links": "Paylaşılan bağlantılar", @@ -1672,12 +1783,14 @@ "start_date": "Başlangıç tarihi", "state": "Eyalet/İl", "status": "Durum", + "stop_casting": "Yansıtmayı durdur", "stop_motion_photo": "Hareketli fotoğrafı durdur", "stop_photo_sharing": "Fotoğraflarınızı paylaşmayı durdurmak mı istiyorsunuz?", "stop_photo_sharing_description": "{partner} artık fotoğraflarınıza erişemeyecek.", "stop_sharing_photos_with_user": "Bu kullanıcı ile fotoğraflarınızı paylaşmayı durdurun", "storage": "Depolama alanı", "storage_label": "Depolama yolu", + "storage_quota": "Depolama Kotası", "storage_usage": "{used} / {available} kullanıldı", "submit": "Gönder", "suggestions": "Öneriler", @@ -1714,7 +1827,7 @@ "theme_setting_system_primary_color_title": "Sistem rengini kullan", "theme_setting_system_theme_switch": "Otomatik (sistem ayarına göre)", "theme_setting_theme_subtitle": "Uygulama teması seç", - "theme_setting_three_stage_loading_subtitle": "Üç aşamalı yükleme yükleme performansını artırabilir ancak önemli ölçüde daha yüksek ağ yüküne sebep olur.", + "theme_setting_three_stage_loading_subtitle": "Üç aşamalı yükleme, yükleme performansını artırabilir ancak önemli ölçüde daha yüksek ağ yüküne sebep olur.", "theme_setting_three_stage_loading_title": "Üç aşamalı yüklemeyi etkinleştir", "they_will_be_merged_together": "Birlikte birleştirilecekler", "third_party_resources": "Üçüncü taraf kaynaklar", @@ -1723,7 +1836,7 @@ "timezone": "Zaman dilimi", "to_archive": "Arşivle", "to_change_password": "Şifreyi değiştir", - "to_favorite": "Favorilere ekle", + "to_favorite": "Gözdelere ekle", "to_login": "Oturum aç", "to_parent": "Üst öğeye git", "to_trash": "Çöpe taşı", @@ -1749,7 +1862,8 @@ "unable_to_setup_pin_code": "PIN kodu ayarlanamadı", "unarchive": "Arşivden çıkar", "unarchived_count": "{count, plural, other {# arşivden çıkarıldı}}", - "unfavorite": "Favorilerden kaldır", + "undo": "Geri al", + "unfavorite": "Gözdelerden kaldır", "unhide_person": "Kişiyi göster", "unknown": "Bilinmeyen", "unknown_country": "Bilinmeyen ülke", @@ -1765,9 +1879,11 @@ "unsaved_change": "Kaydedilmemiş değişiklik", "unselect_all": "Tümünü seçimini kaldır", "unselect_all_duplicates": "Tüm çiftlerin seçimini kaldır", + "unselect_all_in": "{group} içindeki tüm seçimleri kaldır", "unstack": "Yığını kaldır", "unstacked_assets_count": "{count, plural, one {# dosya} other {# dosya}} yığını kaldırıldı", "up_next": "Sıradaki", + "updated_at": "Güncellenme", "updated_password": "Şifreyi güncelle", "upload": "Yükle", "upload_concurrency": "Yükleme eşzamanlılığı", @@ -1780,14 +1896,20 @@ "upload_status_errors": "Hatalar", "upload_status_uploaded": "Yüklendi", "upload_success": "Yükleme başarılı, yüklenen yeni ögeleri görebilmek için sayfayı yenileyin.", + "upload_to_immich": "Immich'e Yükle ({count})", + "uploading": "Yükleniyor", + "url": "URL", "usage": "Kullanım", + "use_biometric": "Biyometri kullan", "use_current_connection": "mevcut bağlantıyı kullan", "use_custom_date_range": "Bunun yerine özel tarih aralığını kullan", "user": "Kullanıcı", + "user_has_been_deleted": "Bu kullanıcı silindi.", "user_id": "Kullanıcı ID", "user_liked": "{type, select, photo {Bu fotoğraf} video {Bu video} asset {Bu dosya} other {Bu}} {user} tarafından beğenildi", "user_pin_code_settings": "PIN Kodu", "user_pin_code_settings_description": "PIN kodunuzu yönetin", + "user_privacy": "Kullanıcı Gizliliği", "user_purchase_settings": "Satın Alma", "user_purchase_settings_description": "Satın alma işlemlerini yönet", "user_role_set": "{user}, {role} olarak ayarlandı", @@ -1805,6 +1927,7 @@ "version_announcement_message": "Merhaba! Immich'in yeni bir sürümü mevcut. Lütfen yapılandırmanızın güncel olduğundan emin olmak için sürüm notlarını okumak için biraz zaman ayırın, özellikle WatchTower veya Immich kurulumunuzu otomatik olarak güncelleyen bir mekanizma kullanıyorsanız yanlış yapılandırmaların önüne geçmek adına bu önemlidir.", "version_history": "Versiyon geçmişi", "version_history_item": "{version}, {date} tarihinde kuruldu", + "video": "Video", "video_hover_setting": "Üzerinde durulduğunda video önizlemesi oynat", "video_hover_setting_description": "Öğe üzerinde fareyle durulduğunda video küçük resmini oynatır. Bu özellik devre dışıyken, oynatma simgesine fareyle gidilerek oynatma başlatılabilir.", "videos": "Videolar", @@ -1819,7 +1942,9 @@ "view_name": "Göster", "view_next_asset": "Sonraki dosyayı görüntüle", "view_previous_asset": "Önceki dosyayı görüntüle", + "view_qr_code": "QR kodu görüntüle", "view_stack": "Yığını görüntüle", + "view_user": "Kullanıcıyı Görüntüle", "viewer_remove_from_stack": "Yığından Kaldır", "viewer_stack_use_as_main_asset": "Ana fotoğraf olarak kullan", "viewer_unstack": "Yığını Kaldır", @@ -1830,6 +1955,7 @@ "welcome": "Hoş geldiniz", "welcome_to_immich": "Immich'e hoş geldiniz", "wifi_name": "Wi-Fi Adı", + "wrong_pin_code": "Yanlış PIN kodu", "year": "Yıl", "years_ago": "{years, plural, one {bir yıl} other {# yıl}} önce", "yes": "Evet", diff --git a/i18n/uk.json b/i18n/uk.json index 7b843ceff9..8a375e627a 100644 --- a/i18n/uk.json +++ b/i18n/uk.json @@ -244,6 +244,7 @@ "storage_template_migration_info": "Шаблон зберігання конвертуватиме всі розширення у нижній регістр. Зміни шаблону застосовуватимуться лише до нових ресурсів. Щоб застосувати шаблон до раніше завантажених ресурсів, запустіть {job}.", "storage_template_migration_job": "Завдання міграції шаблону зберігання", "storage_template_more_details": "Для отримання детальнішої інформації про цю функцію, звертайтесь до Шаблону зберігання та його наслідків", + "storage_template_onboarding_description_v2": "Якщо цю функцію увімкнено, файли будуть автоматично впорядковуватися за шаблоном, визначеним користувачем. Докладніше дивіться в документації.", "storage_template_path_length": "Приблизна максимальна довжина шляху: {length, number}/{limit, number}", "storage_template_settings": "Шаблон сховища", "storage_template_settings_description": "Керуйте структурою тек та іменем завантаженого файлу", @@ -463,7 +464,6 @@ "assets": "елементи", "assets_added_count": "Додано {count, plural, one {# ресурс} few {# ресурси} other {# ресурсів}}", "assets_added_to_album_count": "Додано {count, plural, one {# ресурс} few {# ресурси} other {# ресурсів}} до альбому", - "assets_added_to_name_count": "Додано {count, plural, one {# елемент} other {# елементів}} до {hasName, select, true {{name}} other {нового альбому}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Ресурс} other {Ресурси}} не можна додати до альбому", "assets_count": "{count, plural, one {# ресурс} few {# ресурси} other {# ресурсів}}", "assets_deleted_permanently": "{count} елемент(и) остаточно видалено", @@ -702,7 +702,6 @@ "daily_title_text_date": "Е, МММ дд", "daily_title_text_date_year": "Е, МММ дд, рррр", "dark": "Темний", - "darkTheme": "Перемкнути темну тему", "date_after": "Дата після", "date_and_time": "Дата і час", "date_before": "Дата до", diff --git a/i18n/vi.json b/i18n/vi.json index 5890ef87db..10c883dc70 100644 --- a/i18n/vi.json +++ b/i18n/vi.json @@ -461,7 +461,6 @@ "assets": "Các tập tin", "assets_added_count": "Đã thêm {count, plural, one {# mục} other {# mục}}", "assets_added_to_album_count": "Đã thêm {count, plural, one {# mục} other {# mục}} vào album", - "assets_added_to_name_count": "Đã thêm {count, plural, one {# mục} other {# mục}} vào {hasName, select, true {{name}} other {album mới}}", "assets_count": "{count, plural, one {# mục} other {# mục}}", "assets_deleted_permanently": "Đã xoá vĩnh viễn {count} mục", "assets_deleted_permanently_from_server": "Đã xoá vĩnh viễn {count} mục khỏi máy chủ Immich", @@ -534,7 +533,7 @@ "backup_controller_page_start_backup": "Bắt đầu sao lưu", "backup_controller_page_status_off": "Sao lưu tự động khi ứng dụng hoạt động đang tắt", "backup_controller_page_status_on": "Sao lưu tự động khi ứng dụng hoạt động đang bật", - "backup_controller_page_storage_format": "Đã sử dụng {used} của {total}", + "backup_controller_page_storage_format": "Đã dùng {used} của {total}", "backup_controller_page_to_backup": "Các album cần được sao lưu", "backup_controller_page_total_sub": "Tất cả ảnh và video không trùng lập từ các album được chọn", "backup_controller_page_turn_off": "Tắt sao lưu khi ứng dụng hoạt động", @@ -698,7 +697,6 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Tối", - "darkTheme": "Chuyển đổi chủ đề tối", "date_after": "Ngày sau", "date_and_time": "Ngày và giờ", "date_before": "Ngày trước", @@ -1016,7 +1014,7 @@ "group_year": "Nhóm theo năm", "haptic_feedback_switch": "Bật phản hồi haptic", "haptic_feedback_title": "Phản hồi Hapic", - "has_quota": "Có hạn mức", + "has_quota": "Hạn mức", "header_settings_add_header_tip": "Thêm Header", "header_settings_field_validator_msg": "Trường này không được để trống", "header_settings_header_name_input": "Tên Header", @@ -1426,7 +1424,7 @@ "purchase_button_activate": "Kích hoạt", "purchase_button_buy": "Mua", "purchase_button_buy_immich": "Mua Immich", - "purchase_button_never_show_again": "Không hiển thị lại", + "purchase_button_never_show_again": "Không hiện lại", "purchase_button_reminder": "Nhắc tôi trong 30 ngày", "purchase_button_remove_key": "Xóa khóa", "purchase_button_select": "Chọn", @@ -1504,7 +1502,7 @@ "rename": "Đổi tên", "repair": "Sửa chữa", "repair_no_results_message": "Các tập tin không được theo dõi và bị mất sẽ xuất hiện ở đây", - "replace_with_upload": "Thay thế bằng tập tin tải lên", + "replace_with_upload": "Thay thế tập tin khác", "repository": "Kho lưu trữ", "require_password": "Yêu cầu mật khẩu", "require_user_to_change_password_on_first_login": "Yêu cầu người dùng thay đổi mật khẩu ở lần đầu đăng nhập", @@ -1522,7 +1520,7 @@ "restored_asset": "Ảnh đã được khôi phục", "resume": "Tiếp tục", "retry_upload": "Thử tải lên lại", - "review_duplicates": "Xem xét các mục trùng lặp", + "review_duplicates": "Xem lại các mục trùng lặp", "role": "Vai trò", "role_editor": "Người chỉnh sửa", "role_viewer": "Người xem", @@ -1617,7 +1615,7 @@ "server_info_box_app_version": "Phiên bản ứng dụng", "server_info_box_server_url": "Địa chỉ máy chủ", "server_offline": "Máy chủ ngoại tuyến", - "server_online": "Máy chủ trực tuyến", + "server_online": "Phiên bản", "server_privacy": "Quyền riêng tư máy chủ", "server_stats": "Thống kê máy chủ", "server_version": "Phiên bản máy chủ", @@ -1772,7 +1770,7 @@ "storage": "Bộ nhớ", "storage_label": "Nhãn lưu trữ", "storage_quota": "Giới hạn Dung lượng", - "storage_usage": "Đã sử dụng {used} của {available}", + "storage_usage": "Đã dùng {used} của {available}", "submit": "Gửi", "suggestions": "Gợi ý", "sunrise_on_the_beach": "Bình minh trên bãi biển", @@ -1819,7 +1817,7 @@ "to_change_password": "Đổi mật khẩu", "to_favorite": "Yêu thích", "to_login": "Đăng nhập", - "to_parent": "Đi tới thư mục cha", + "to_parent": "Về thư mục gốc", "to_trash": "Xóa", "toggle_settings": "Chuyển đổi cài đặt", "total": "Tổng cộng", @@ -1894,7 +1892,7 @@ "user_purchase_settings_description": "Quản lý mục mua của bạn", "user_role_set": "Đặt {user} làm {role}", "user_usage_detail": "Chi tiết sử dụng của người dùng", - "user_usage_stats": "Thống kê sử dụng của tài khoản", + "user_usage_stats": "Thống kê sử dụng tài khoản", "user_usage_stats_description": "Xem thống kê sử dụng của tài khoản", "username": "Tên người dùng", "users": "Người dùng", diff --git a/i18n/zh_Hant.json b/i18n/zh_Hant.json index e5a8a65e25..334da7bfb1 100644 --- a/i18n/zh_Hant.json +++ b/i18n/zh_Hant.json @@ -464,7 +464,6 @@ "assets": "項目", "assets_added_count": "已新增 {count, plural, one {# 個項目} other {# 個項目}}", "assets_added_to_album_count": "已將 {count, plural, other {# 個項目}}加入相簿", - "assets_added_to_name_count": "已將 {count, plural, other {# 個項目}}加入{hasName, select, true {{name}} other {新相簿}}", "assets_cannot_be_added_to_album_count": "{count. plural, one {個} other {個}} 項目未能被添加至相簿", "assets_count": "{count, plural, one {# 個項目} other {# 個項目}}", "assets_deleted_permanently": "{count} 個項目已被永久刪除", @@ -699,7 +698,6 @@ "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "YYYY年M月D日 (E)", "dark": "深色", - "darkTheme": "切換爲深色主題", "date_after": "日期之後", "date_and_time": "日期與時間", "date_before": "日期之前", diff --git a/i18n/zh_SIMPLIFIED.json b/i18n/zh_SIMPLIFIED.json index 17c6954ab7..28d6a5c0d5 100644 --- a/i18n/zh_SIMPLIFIED.json +++ b/i18n/zh_SIMPLIFIED.json @@ -166,6 +166,20 @@ "metadata_settings_description": "管理元数据设置", "migration_job": "迁移", "migration_job_description": "将项目和人脸识别的缩略图迁移到最新的文件夹结构", + "nightly_tasks_cluster_faces_setting_description": "对新检测到的面部进行面部识别", + "nightly_tasks_cluster_new_faces_setting": "群组新面孔", + "nightly_tasks_database_cleanup_setting": "数据库清理任务", + "nightly_tasks_database_cleanup_setting_description": "清理数据库中过期的旧数据", + "nightly_tasks_generate_memories_setting": "生成回忆", + "nightly_tasks_generate_memories_setting_description": "从项目中生成新的回忆", + "nightly_tasks_missing_thumbnails_setting": "生成缺失的缩略图", + "nightly_tasks_missing_thumbnails_setting_description": "为生成缩略图队列无缩略图的项目", + "nightly_tasks_settings": "夜间任务设置", + "nightly_tasks_settings_description": "管理夜间任务", + "nightly_tasks_start_time_setting": "开始时间", + "nightly_tasks_start_time_setting_description": "服务器开始运行夜间任务的时间", + "nightly_tasks_sync_quota_usage_setting": "同步配额使用情况", + "nightly_tasks_sync_quota_usage_setting_description": "根据当前使用情况更新用户存储配额", "no_paths_added": "无已添加路径", "no_pattern_added": "无已添加规则", "note_apply_storage_label_previous_assets": "提示:要将存储标签应用于之前上传的项目,需要运行", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "移动端重定向 URI", "oauth_mobile_redirect_uri_override": "移动端重定向 URI 覆盖", "oauth_mobile_redirect_uri_override_description": "当 OAuth 提供商不允许使用移动 URI 时启用,如“''{callback}''”", + "oauth_role_claim": "角色声明", + "oauth_role_claim_description": "根据此声明的存在自动授予管理员访问权限。声明可以是“user”(用户)或“admin”(管理员)。", "oauth_settings": "OAuth", "oauth_settings_description": "管理 OAuth 登录设置", "oauth_settings_more_details": "关于此功能的更多详细信息,请查看相关文档。", @@ -357,6 +373,8 @@ "admin_password": "管理员密码", "administration": "系统管理", "advanced": "高级", + "advanced_settings_beta_timeline_subtitle": "体验全新的应用程序", + "advanced_settings_beta_timeline_title": "测试版时间线", "advanced_settings_enable_alternate_media_filter_subtitle": "使用此选项可在同步过程中根据备用条件筛选项目。仅当您在应用程序检测所有相册均遇到问题时才尝试此功能。", "advanced_settings_enable_alternate_media_filter_title": "[实验] 使用备用的设备相册同步筛选条件", "advanced_settings_log_level_title": "日志等级: {level}", @@ -388,6 +406,7 @@ "album_options": "相册设置", "album_remove_user": "移除用户?", "album_remove_user_confirmation": "确定要移除“{user}”吗?", + "album_search_not_found": "未找到符合搜索条件的相册", "album_share_no_users": "看起来您已与所有用户共享了此相册,或者您根本没有任何用户可共享。", "album_updated": "相册有更新", "album_updated_setting_description": "当共享相册有新项目时接收邮件通知", @@ -407,6 +426,7 @@ "albums_default_sort_order": "默认相册排序方式", "albums_default_sort_order_description": "创建新相册时的项目初始排序方式。", "albums_feature_description": "可与其他用户共享的项目收藏。", + "albums_on_device_count": "设备上的相册({count} 个)", "all": "全部", "all_albums": "所有相册", "all_people": "全部人物", @@ -427,12 +447,13 @@ "app_settings": "应用设置", "appears_in": "出现于", "archive": "归档", + "archive_action_prompt": "已将 {count} 项添加到归档", "archive_or_unarchive_photo": "归档或取消归档照片", "archive_page_no_archived_assets": "未找到归档项目", "archive_page_title": "归档({count})", "archive_size": "归档大小", "archive_size_description": "配置下载归档大小(GB)", - "archived": "已存档", + "archived": "已归档", "archived_count": "{count, plural, other {已归档 # 项}}", "are_these_the_same_person": "他们是同一位人吗?", "are_you_sure_to_do_this": "确定要这样做吗?", @@ -464,7 +485,6 @@ "assets": "项目", "assets_added_count": "已添加{count, plural, one {#个项目} other {#个项目}}", "assets_added_to_album_count": "已添加{count, plural, one {#个项目} other {#个项目}}到相册", - "assets_added_to_name_count": "已添加{count, plural, one {#个项目} other {#个项目}}到{hasName, select, true {{name}} other {新相册}}", "assets_cannot_be_added_to_album_count": "无法添加 {count, plural, one {个项目} other {个项目}} 到相册中", "assets_count": "{count, plural, one {#个项目} other {#个项目}}", "assets_deleted_permanently": "{count} 个项目已被永久删除", @@ -587,6 +607,7 @@ "cancel": "取消", "cancel_search": "取消搜索", "canceled": "已取消", + "canceling": "取消中", "cannot_merge_people": "无法合并人物", "cannot_undo_this_action": "注意:该操作无法被撤消!", "cannot_update_the_description": "无法更新描述", @@ -703,7 +724,7 @@ "daily_title_text_date": "MMM dd (E)", "daily_title_text_date_year": "YYYY年M月D日 (E)", "dark": "深色", - "darkTheme": "暗色主题", + "dark_theme": "切换深色主题", "date_after": "开始日期", "date_and_time": "日期与时间", "date_before": "结束日期", @@ -719,6 +740,7 @@ "default_locale": "默认地区", "default_locale_description": "根据您的浏览器地区设置日期和数字显示格式", "delete": "删除", + "delete_action_prompt": "已永久删除 {count} 项", "delete_album": "删除相册", "delete_api_key_prompt": "确定删除此 API 密钥吗?", "delete_dialog_alert": "这些项目将从 Immich 和您的设备中永久删除", @@ -732,6 +754,7 @@ "delete_key": "删除密钥", "delete_library": "删除图库", "delete_link": "删除链接", + "delete_local_action_prompt": "已删除本地项目{count}项", "delete_local_dialog_ok_backed_up_only": "仅删除已备份项目", "delete_local_dialog_ok_force": "确认删除", "delete_others": "删除其它", @@ -745,6 +768,7 @@ "description": "描述", "description_input_hint_text": "添加描述...", "description_input_submit_error": "更新描述时出错,请检查日志以获取更多详细信息", + "deselect_all": "取消全选", "details": "详情", "direction": "方向", "disabled": "已禁用", @@ -762,6 +786,7 @@ "documentation": "帮助文档", "done": "完成", "download": "下载", + "download_action_prompt": "正在下载 {count} 个项目", "download_canceled": "下载已取消", "download_complete": "下载完成", "download_enqueue": "已加入下载队列", @@ -799,6 +824,7 @@ "edit_key": "编辑 API 密钥", "edit_link": "编辑链接", "edit_location": "编辑位置", + "edit_location_action_prompt": "{count} 个位置已编辑", "edit_location_dialog_title": "位置", "edit_name": "编辑名称", "edit_people": "编辑人物", @@ -817,6 +843,7 @@ "empty_trash": "清空回收站", "empty_trash_confirmation": "确定要清空回收站?这将永久删除回收站中的所有项目。\n注意:该操作无法撤消!", "enable": "启用", + "enable_backup": "启用备份", "enable_biometric_auth_description": "输入您的PIN码以启用生物识别身份验证", "enabled": "已启用", "end_date": "结束日期", @@ -984,6 +1011,7 @@ "failed_to_load_assets": "加载项目失败", "failed_to_load_folder": "加载文件夹失败", "favorite": "收藏", + "favorite_action_prompt": "已将 {count} 项添加到收藏", "favorite_or_unfavorite_photo": "收藏或取消收藏照片", "favorites": "收藏夹", "favorites_page_no_favorites": "未找到收藏项目", @@ -1127,6 +1155,7 @@ "library_page_sort_created": "创建日期", "library_page_sort_last_modified": "上次修改", "library_page_sort_title": "相册标题", + "licenses": "许可证", "light": "浅色", "like_deleted": "已删除的收藏", "link_motion_video": "链接动态视频", @@ -1246,6 +1275,7 @@ "more": "更多", "move": "移动", "move_off_locked_folder": "移出锁定文件夹", + "move_to_lock_folder_action_prompt": "已将 {count} 项添加到锁定文件夹", "move_to_locked_folder": "移动到锁定文件夹", "move_to_locked_folder_confirmation": "这些照片和视频将从所有相册中移除,只能在锁定文件夹中查看", "moved_to_archive": "已归档 {count, plural, one {# 个项目} other {# 个项目}}", @@ -1460,6 +1490,7 @@ "purchase_server_description_2": "支持者状态", "purchase_server_title": "服务器", "purchase_settings_server_activated": "服务器产品密钥正在由管理员管理", + "queue_status": "排队中 {count}/{total}", "rating": "星级", "rating_clear": "删除星级", "rating_count": "{count, plural, one {#星} other {#星}}", @@ -1495,7 +1526,9 @@ "remove_custom_date_range": "取消自定义日期范围", "remove_deleted_assets": "彻底删除文件", "remove_from_album": "从相册中移除", + "remove_from_album_action_prompt": "从相册中移除了 {count} 项", "remove_from_favorites": "移出收藏", + "remove_from_lock_folder_action_prompt": "已从锁定的文件夹中移除 {count} 项", "remove_from_locked_folder": "从锁定文件夹中移除", "remove_from_locked_folder_confirmation": "您确定要将这些照片和视频移出锁定文件夹吗?移出后它们将会在您的图库中可见。", "remove_from_shared_link": "从共享链接中移除", @@ -1667,6 +1700,7 @@ "settings_saved": "设置已保存", "setup_pin_code": "设置PIN码", "share": "共享", + "share_action_prompt": "已共享 {count} 项目", "share_add_photos": "添加项目", "share_assets_selected": "{count} 已选择", "share_dialog_preparing": "正在准备...", @@ -1768,6 +1802,7 @@ "sort_title": "标题", "source": "GitHub 源代码", "stack": "堆叠", + "stack_action_prompt": "{count} 个已堆叠", "stack_duplicates": "堆叠重复项目", "stack_select_one_photo": "为堆叠选择一张展示图", "stack_selected_photos": "堆叠选定的照片", @@ -1838,6 +1873,7 @@ "total": "总计", "total_usage": "总用量", "trash": "回收站", + "trash_action_prompt": "已将 {count} 项移至回收站", "trash_all": "全部删除", "trash_count": "删除{count, number}项", "trash_delete_asset": "将项目放入回收站/删除", @@ -1855,9 +1891,11 @@ "unable_to_change_pin_code": "无法修改PIN码", "unable_to_setup_pin_code": "无法设置PIN码", "unarchive": "取消归档", + "unarchive_action_prompt": "已从归档中移除 {count} 项", "unarchived_count": "{count, plural, other {取消归档 # 项}}", "undo": "撤销", "unfavorite": "取消收藏", + "unfavorite_action_prompt": "已从收藏中移除 {count} 项", "unhide_person": "显示人物", "unknown": "未知", "unknown_country": "未知的国家", @@ -1875,12 +1913,15 @@ "unselect_all_duplicates": "取消选择所有重复项", "unselect_all_in": "取消选择 {group} 中的所有内容", "unstack": "取消堆叠", + "unstack_action_prompt": "{count} 个未堆叠", "unstacked_assets_count": "{count, plural, one {#个项目} other {#个项目}}已取消堆叠", + "untagged": "无标签", "up_next": "下一个", "updated_at": "已更新", "updated_password": "更新密码", "upload": "上传", "upload_concurrency": "上传并发", + "upload_details": "上传详情", "upload_dialog_info": "是否要将所选项目备份到服务器?", "upload_dialog_title": "上传项目", "upload_errors": "上传完成,出现{count, plural, one {#个错误} other {#个错误}},刷新页面以查看新上传的项目。", @@ -1912,6 +1953,7 @@ "user_usage_stats_description": "查看帐户使用统计信息", "username": "用户名", "users": "用户", + "users_added_to_album_count": "已将 {count, plural, one {# 个用户} other {# 个用户}} 添加到相册", "utilities": "实用工具", "validate": "验证", "validate_endpoint_error": "请输入有效的 URL", @@ -1930,6 +1972,7 @@ "view_album": "查看相册", "view_all": "查看全部", "view_all_users": "查看全部用户", + "view_details": "查看详情", "view_in_timeline": "在时间轴中查看", "view_link": "查看链接", "view_links": "查看链接", From 166452640d6d421d4ff1281134e5cf0a39971895 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:28:15 +0100 Subject: [PATCH 002/748] chore(deps): update dependency @types/node to ^22.16.4 (#20068) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- cli/package-lock.json | 10 +++++----- cli/package.json | 2 +- e2e/package-lock.json | 12 ++++++------ e2e/package.json | 2 +- open-api/typescript-sdk/package-lock.json | 8 ++++---- open-api/typescript-sdk/package.json | 2 +- server/package-lock.json | 8 ++++---- server/package.json | 2 +- 8 files changed, 23 insertions(+), 23 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index 7c87c3be2e..0d3db4cffa 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -27,7 +27,7 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.15.33", + "@types/node": "^22.16.4", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", @@ -61,7 +61,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.15.33", + "@types/node": "^22.16.4", "typescript": "^5.3.3" } }, @@ -1355,9 +1355,9 @@ } }, "node_modules/@types/node": { - "version": "22.15.34", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.34.tgz", - "integrity": "sha512-8Y6E5WUupYy1Dd0II32BsWAx5MWdcnRd8L84Oys3veg1YrYtNtzgO4CFhiBg6MDSjk7Ay36HYOnU7/tuOzIzcw==", + "version": "22.16.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.5.tgz", + "integrity": "sha512-bJFoMATwIGaxxx8VJPeM8TonI8t579oRvgAuT8zFugJsJZgzqv0Fu8Mhp68iecjzG7cnN3mO2dJQ5uUM2EFrgQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/cli/package.json b/cli/package.json index e1f49475c0..3fde7e8b02 100644 --- a/cli/package.json +++ b/cli/package.json @@ -21,7 +21,7 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.15.33", + "@types/node": "^22.16.4", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 1e2f8b47e1..1884b22dd5 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -16,7 +16,7 @@ "@playwright/test": "^1.44.1", "@socket.io/component-emitter": "^3.1.2", "@types/luxon": "^3.4.2", - "@types/node": "^22.15.33", + "@types/node": "^22.16.4", "@types/oidc-provider": "^9.0.0", "@types/pg": "^8.15.1", "@types/pngjs": "^6.0.4", @@ -68,7 +68,7 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.15.33", + "@types/node": "^22.16.4", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", @@ -102,7 +102,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.15.33", + "@types/node": "^22.16.4", "typescript": "^5.3.3" } }, @@ -1996,9 +1996,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.15.34", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.34.tgz", - "integrity": "sha512-8Y6E5WUupYy1Dd0II32BsWAx5MWdcnRd8L84Oys3veg1YrYtNtzgO4CFhiBg6MDSjk7Ay36HYOnU7/tuOzIzcw==", + "version": "22.16.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.5.tgz", + "integrity": "sha512-bJFoMATwIGaxxx8VJPeM8TonI8t579oRvgAuT8zFugJsJZgzqv0Fu8Mhp68iecjzG7cnN3mO2dJQ5uUM2EFrgQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/e2e/package.json b/e2e/package.json index 24b97bb4b7..c780a93a24 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -26,7 +26,7 @@ "@playwright/test": "^1.44.1", "@socket.io/component-emitter": "^3.1.2", "@types/luxon": "^3.4.2", - "@types/node": "^22.15.33", + "@types/node": "^22.16.4", "@types/oidc-provider": "^9.0.0", "@types/pg": "^8.15.1", "@types/pngjs": "^6.0.4", diff --git a/open-api/typescript-sdk/package-lock.json b/open-api/typescript-sdk/package-lock.json index ab9f3b1fd7..01086f3113 100644 --- a/open-api/typescript-sdk/package-lock.json +++ b/open-api/typescript-sdk/package-lock.json @@ -12,7 +12,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.15.33", + "@types/node": "^22.16.4", "typescript": "^5.3.3" } }, @@ -23,9 +23,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.15.34", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.34.tgz", - "integrity": "sha512-8Y6E5WUupYy1Dd0II32BsWAx5MWdcnRd8L84Oys3veg1YrYtNtzgO4CFhiBg6MDSjk7Ay36HYOnU7/tuOzIzcw==", + "version": "22.16.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.5.tgz", + "integrity": "sha512-bJFoMATwIGaxxx8VJPeM8TonI8t579oRvgAuT8zFugJsJZgzqv0Fu8Mhp68iecjzG7cnN3mO2dJQ5uUM2EFrgQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index f2885f4a7b..c1bf8b4f65 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -19,7 +19,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.15.33", + "@types/node": "^22.16.4", "typescript": "^5.3.3" }, "repository": { diff --git a/server/package-lock.json b/server/package-lock.json index 11be49ecb6..1239730fce 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -109,7 +109,7 @@ "@types/luxon": "^3.6.2", "@types/mock-fs": "^4.13.1", "@types/multer": "^1.4.7", - "@types/node": "^22.15.33", + "@types/node": "^22.16.4", "@types/nodemailer": "^6.4.14", "@types/picomatch": "^4.0.0", "@types/pngjs": "^6.0.5", @@ -7258,9 +7258,9 @@ } }, "node_modules/@types/node": { - "version": "22.15.34", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.34.tgz", - "integrity": "sha512-8Y6E5WUupYy1Dd0II32BsWAx5MWdcnRd8L84Oys3veg1YrYtNtzgO4CFhiBg6MDSjk7Ay36HYOnU7/tuOzIzcw==", + "version": "22.16.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.5.tgz", + "integrity": "sha512-bJFoMATwIGaxxx8VJPeM8TonI8t579oRvgAuT8zFugJsJZgzqv0Fu8Mhp68iecjzG7cnN3mO2dJQ5uUM2EFrgQ==", "license": "MIT", "dependencies": { "undici-types": "~6.21.0" diff --git a/server/package.json b/server/package.json index c765786960..5a385193ec 100644 --- a/server/package.json +++ b/server/package.json @@ -135,7 +135,7 @@ "@types/luxon": "^3.6.2", "@types/mock-fs": "^4.13.1", "@types/multer": "^1.4.7", - "@types/node": "^22.15.33", + "@types/node": "^22.16.4", "@types/nodemailer": "^6.4.14", "@types/picomatch": "^4.0.0", "@types/pngjs": "^6.0.5", From 496b0c7076a23a6b34e2ff7ede94ee85bafe9e1a Mon Sep 17 00:00:00 2001 From: Daimolean <92239625+wuzihao051119@users.noreply.github.com> Date: Tue, 22 Jul 2025 17:29:14 +0800 Subject: [PATCH 003/748] fix(server): missing integer type (#20075) --- .../openapi/lib/model/sync_asset_face_v1.dart | 24 +++++++++---------- open-api/immich-openapi-specs.json | 12 +++++----- server/src/dtos/sync.dto.ts | 6 +++++ 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/mobile/openapi/lib/model/sync_asset_face_v1.dart b/mobile/openapi/lib/model/sync_asset_face_v1.dart index 853a8a1514..60d1766e34 100644 --- a/mobile/openapi/lib/model/sync_asset_face_v1.dart +++ b/mobile/openapi/lib/model/sync_asset_face_v1.dart @@ -27,19 +27,19 @@ class SyncAssetFaceV1 { String assetId; - num boundingBoxX1; + int boundingBoxX1; - num boundingBoxX2; + int boundingBoxX2; - num boundingBoxY1; + int boundingBoxY1; - num boundingBoxY2; + int boundingBoxY2; String id; - num imageHeight; + int imageHeight; - num imageWidth; + int imageWidth; String? personId; @@ -104,13 +104,13 @@ class SyncAssetFaceV1 { return SyncAssetFaceV1( assetId: mapValueOfType(json, r'assetId')!, - boundingBoxX1: num.parse('${json[r'boundingBoxX1']}'), - boundingBoxX2: num.parse('${json[r'boundingBoxX2']}'), - boundingBoxY1: num.parse('${json[r'boundingBoxY1']}'), - boundingBoxY2: num.parse('${json[r'boundingBoxY2']}'), + boundingBoxX1: mapValueOfType(json, r'boundingBoxX1')!, + boundingBoxX2: mapValueOfType(json, r'boundingBoxX2')!, + boundingBoxY1: mapValueOfType(json, r'boundingBoxY1')!, + boundingBoxY2: mapValueOfType(json, r'boundingBoxY2')!, id: mapValueOfType(json, r'id')!, - imageHeight: num.parse('${json[r'imageHeight']}'), - imageWidth: num.parse('${json[r'imageWidth']}'), + imageHeight: mapValueOfType(json, r'imageHeight')!, + imageWidth: mapValueOfType(json, r'imageWidth')!, personId: mapValueOfType(json, r'personId'), sourceType: mapValueOfType(json, r'sourceType')!, ); diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 2f41318d6d..4acd431203 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -13805,25 +13805,25 @@ "type": "string" }, "boundingBoxX1": { - "type": "number" + "type": "integer" }, "boundingBoxX2": { - "type": "number" + "type": "integer" }, "boundingBoxY1": { - "type": "number" + "type": "integer" }, "boundingBoxY2": { - "type": "number" + "type": "integer" }, "id": { "type": "string" }, "imageHeight": { - "type": "number" + "type": "integer" }, "imageWidth": { - "type": "number" + "type": "integer" }, "personId": { "nullable": true, diff --git a/server/src/dtos/sync.dto.ts b/server/src/dtos/sync.dto.ts index e0c9c059c4..c8b1a7dde9 100644 --- a/server/src/dtos/sync.dto.ts +++ b/server/src/dtos/sync.dto.ts @@ -261,11 +261,17 @@ export class SyncAssetFaceV1 { id!: string; assetId!: string; personId!: string | null; + @ApiProperty({ type: 'integer' }) imageWidth!: number; + @ApiProperty({ type: 'integer' }) imageHeight!: number; + @ApiProperty({ type: 'integer' }) boundingBoxX1!: number; + @ApiProperty({ type: 'integer' }) boundingBoxY1!: number; + @ApiProperty({ type: 'integer' }) boundingBoxX2!: number; + @ApiProperty({ type: 'integer' }) boundingBoxY2!: number; sourceType!: string; } From bd92f6b12dbe1b2fa84b26100fe1eb03e848343a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 09:31:01 +0000 Subject: [PATCH 004/748] chore(deps): update dependency dotenv to v17 (#20073) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- web/package-lock.json | 8 ++++---- web/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index 2bd5409903..c1147447de 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -66,7 +66,7 @@ "@types/qrcode": "^1.5.5", "@vitest/coverage-v8": "^3.0.0", "autoprefixer": "^10.4.17", - "dotenv": "^16.4.7", + "dotenv": "^17.0.0", "eslint": "^9.18.0", "eslint-config-prettier": "^10.0.0", "eslint-p": "^0.25.0", @@ -4698,9 +4698,9 @@ } }, "node_modules/dotenv": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "version": "17.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.0.tgz", + "integrity": "sha512-Q4sgBT60gzd0BB0lSyYD3xM4YxrXA9y4uBDof1JNYGzOXrQdQ6yX+7XIAqoFOGQFOTK1D3Hts5OllpxMDZFONQ==", "dev": true, "license": "BSD-2-Clause", "engines": { diff --git a/web/package.json b/web/package.json index c63c52a916..158c490b6a 100644 --- a/web/package.json +++ b/web/package.json @@ -83,7 +83,7 @@ "@types/qrcode": "^1.5.5", "@vitest/coverage-v8": "^3.0.0", "autoprefixer": "^10.4.17", - "dotenv": "^16.4.7", + "dotenv": "^17.0.0", "eslint": "^9.18.0", "eslint-config-prettier": "^10.0.0", "eslint-p": "^0.25.0", From 7b41c6348c910fd93e43bc06206145ca83accb93 Mon Sep 17 00:00:00 2001 From: "Weblate (bot)" Date: Tue, 22 Jul 2025 11:49:18 +0200 Subject: [PATCH 005/748] chore(web): update translations (#20076) Co-authored-by: Zack Pollard --- i18n/lv.json | 1 + i18n/nl.json | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/i18n/lv.json b/i18n/lv.json index 01a506a90a..5af3e0511c 100644 --- a/i18n/lv.json +++ b/i18n/lv.json @@ -485,6 +485,7 @@ "cant_get_faces": "Nevar iegūt sejas", "cant_search_people": "Neizdevās veikt peronu meklēšanu", "failed_to_create_album": "Neizdevās izveidot albumu", + "profile_picture_transparent_pixels": "Profila attēlos nevar būt caurspīdīgi pikseļi. Lūdzu, palielini un/vai pārvieto attēlu.", "unable_to_change_description": "Neizdevās nomainīt aprakstu", "unable_to_create_user": "Neizdevās izveidot lietotāju", "unable_to_delete_user": "Neizdevās dzēst lietotāju", diff --git a/i18n/nl.json b/i18n/nl.json index ecd8418fa5..2cedd63b15 100644 --- a/i18n/nl.json +++ b/i18n/nl.json @@ -406,6 +406,7 @@ "album_options": "Albumopties", "album_remove_user": "Gebruiker verwijderen?", "album_remove_user_confirmation": "Weet je zeker dat je {user} wilt verwijderen?", + "album_search_not_found": "Geen albums gevonden die aan je zoekopdracht voldoen", "album_share_no_users": "Het lijkt erop dat je dit album met alle gebruikers hebt gedeeld, of dat je geen gebruikers hebt om mee te delen.", "album_updated": "Album bijgewerkt", "album_updated_setting_description": "Ontvang een e-mailmelding wanneer een gedeeld album nieuwe assets heeft", @@ -605,6 +606,7 @@ "cancel": "Annuleren", "cancel_search": "Zoeken annuleren", "canceled": "Geannuleerd", + "canceling": "Annuleren", "cannot_merge_people": "Kan mensen niet samenvoegen", "cannot_undo_this_action": "Je kunt deze actie niet ongedaan maken!", "cannot_update_the_description": "Kan de beschrijving niet bijwerken", @@ -765,6 +767,7 @@ "description": "Beschrijving", "description_input_hint_text": "Beschrijving toevoegen...", "description_input_submit_error": "Beschrijving bijwerken mislukt, controleer het logboek voor meer details", + "deselect_all": "Alles Deselecteren", "details": "Details", "direction": "Richting", "disabled": "Uitgeschakeld", @@ -839,6 +842,7 @@ "empty_trash": "Prullenbak leegmaken", "empty_trash_confirmation": "Weet je zeker dat je de prullenbak wilt legen? Hiermee worden alle assets in de prullenbak permanent uit Immich verwijderd.\nJe kunt deze actie niet ongedaan maken!", "enable": "Inschakelen", + "enable_backup": "Back-up aanzetten", "enable_biometric_auth_description": "Voer uw pincode in om biometrische authenticatie in te schakelen", "enabled": "Ingeschakeld", "end_date": "Einddatum", @@ -1485,6 +1489,7 @@ "purchase_server_description_2": "Supporterstatus", "purchase_server_title": "Server", "purchase_settings_server_activated": "De licentiesleutel van de server wordt beheerd door de beheerder", + "queue_status": "Wachtrij {count}/{total}", "rating": "Sterwaardering", "rating_clear": "Waardering verwijderen", "rating_count": "{count, plural, one {# ster} other {# sterren}}", @@ -1915,6 +1920,7 @@ "updated_password": "Wachtwoord bijgewerkt", "upload": "Uploaden", "upload_concurrency": "Aantal gelijktijdige uploads", + "upload_details": "Uploaddetails", "upload_dialog_info": "Wil je een backup maken van de geselecteerde asset(s) op de server?", "upload_dialog_title": "Asset uploaden", "upload_errors": "Upload voltooid met {count, plural, one {# fout} other {# fouten}}, vernieuw de pagina om de nieuwe assets te zien.", @@ -1965,6 +1971,7 @@ "view_album": "Bekijk album", "view_all": "Bekijk alle", "view_all_users": "Bekijk alle gebruikers", + "view_details": "Bekijk details", "view_in_timeline": "Bekijk in tijdlijn", "view_link": "Bekijk link", "view_links": "Links bekijken", From f16457d2f9acdb8161f3fee884d0e19f5c5f823e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:57:58 +0100 Subject: [PATCH 006/748] chore(deps): update redis:6.2-alpine docker digest to 7fe72c4 (#20065) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- e2e/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/docker-compose.yml b/e2e/docker-compose.yml index 26c3951278..ccb21e4587 100644 --- a/e2e/docker-compose.yml +++ b/e2e/docker-compose.yml @@ -35,7 +35,7 @@ services: - 2285:2285 redis: - image: redis:6.2-alpine@sha256:03fd052257735b41cd19f3d8ae9782926bf9b704fb6a9dc5e29f9ccfbe8827f0 + image: redis:6.2-alpine@sha256:7fe72c486b910f6b1a9769c937dad5d63648ddee82e056f47417542dd40825bb database: image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:3aef84a0a4fabbda17ef115c3019ba0c914ec73e9f6e59203674322d858b8eea From 637eba6e089fc7ce7d37f61ab72d05ffa7f515aa Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 09:59:16 +0000 Subject: [PATCH 007/748] chore(deps): update node.js to v22.17.1 (#20066) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/.nvmrc | 2 +- cli/.nvmrc | 2 +- cli/package.json | 2 +- docs/.nvmrc | 2 +- docs/package.json | 2 +- e2e/.nvmrc | 2 +- e2e/package.json | 2 +- open-api/typescript-sdk/.nvmrc | 2 +- open-api/typescript-sdk/package.json | 2 +- server/.nvmrc | 2 +- server/package.json | 2 +- web/.nvmrc | 2 +- web/package.json | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/.nvmrc b/.github/.nvmrc index fc37597bcc..7377d130ed 100644 --- a/.github/.nvmrc +++ b/.github/.nvmrc @@ -1 +1 @@ -22.17.0 +22.17.1 diff --git a/cli/.nvmrc b/cli/.nvmrc index fc37597bcc..7377d130ed 100644 --- a/cli/.nvmrc +++ b/cli/.nvmrc @@ -1 +1 @@ -22.17.0 +22.17.1 diff --git a/cli/package.json b/cli/package.json index 3fde7e8b02..bd5b4b5757 100644 --- a/cli/package.json +++ b/cli/package.json @@ -69,6 +69,6 @@ "micromatch": "^4.0.8" }, "volta": { - "node": "22.17.0" + "node": "22.17.1" } } diff --git a/docs/.nvmrc b/docs/.nvmrc index fc37597bcc..7377d130ed 100644 --- a/docs/.nvmrc +++ b/docs/.nvmrc @@ -1 +1 @@ -22.17.0 +22.17.1 diff --git a/docs/package.json b/docs/package.json index b1faa4cfcb..97072d8eb5 100644 --- a/docs/package.json +++ b/docs/package.json @@ -59,6 +59,6 @@ "node": ">=20" }, "volta": { - "node": "22.17.0" + "node": "22.17.1" } } diff --git a/e2e/.nvmrc b/e2e/.nvmrc index fc37597bcc..7377d130ed 100644 --- a/e2e/.nvmrc +++ b/e2e/.nvmrc @@ -1 +1 @@ -22.17.0 +22.17.1 diff --git a/e2e/package.json b/e2e/package.json index c780a93a24..1299b267f2 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -54,6 +54,6 @@ "vitest": "^3.0.0" }, "volta": { - "node": "22.17.0" + "node": "22.17.1" } } diff --git a/open-api/typescript-sdk/.nvmrc b/open-api/typescript-sdk/.nvmrc index fc37597bcc..7377d130ed 100644 --- a/open-api/typescript-sdk/.nvmrc +++ b/open-api/typescript-sdk/.nvmrc @@ -1 +1 @@ -22.17.0 +22.17.1 diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index c1bf8b4f65..677bc146cc 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -28,6 +28,6 @@ "directory": "open-api/typescript-sdk" }, "volta": { - "node": "22.17.0" + "node": "22.17.1" } } diff --git a/server/.nvmrc b/server/.nvmrc index fc37597bcc..7377d130ed 100644 --- a/server/.nvmrc +++ b/server/.nvmrc @@ -1 +1 @@ -22.17.0 +22.17.1 diff --git a/server/package.json b/server/package.json index 5a385193ec..91e68718bb 100644 --- a/server/package.json +++ b/server/package.json @@ -172,7 +172,7 @@ "vitest": "^3.0.0" }, "volta": { - "node": "22.17.0" + "node": "22.17.1" }, "overrides": { "sharp": "^0.34.2" diff --git a/web/.nvmrc b/web/.nvmrc index fc37597bcc..7377d130ed 100644 --- a/web/.nvmrc +++ b/web/.nvmrc @@ -1 +1 @@ -22.17.0 +22.17.1 diff --git a/web/package.json b/web/package.json index 158c490b6a..2b092f057a 100644 --- a/web/package.json +++ b/web/package.json @@ -109,6 +109,6 @@ "vitest": "^3.0.0" }, "volta": { - "node": "22.17.0" + "node": "22.17.1" } } From 5548033cae442d928ee02d03b20382c9d9610a69 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 11:00:28 +0100 Subject: [PATCH 008/748] chore(deps): update dependency @types/multer to v2 (#20069) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- server/package-lock.json | 8 ++++---- server/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/server/package-lock.json b/server/package-lock.json index 1239730fce..ca758d97e4 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -108,7 +108,7 @@ "@types/lodash": "^4.14.197", "@types/luxon": "^3.6.2", "@types/mock-fs": "^4.13.1", - "@types/multer": "^1.4.7", + "@types/multer": "^2.0.0", "@types/node": "^22.16.4", "@types/nodemailer": "^6.4.14", "@types/picomatch": "^4.0.0", @@ -7239,9 +7239,9 @@ } }, "node_modules/@types/multer": { - "version": "1.4.13", - "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.13.tgz", - "integrity": "sha512-bhhdtPw7JqCiEfC9Jimx5LqX9BDIPJEh2q/fQ4bqbBPtyEZYr3cvF22NwG0DmPZNYA0CAf2CnqDB4KIGGpJcaw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-2.0.0.tgz", + "integrity": "sha512-C3Z9v9Evij2yST3RSBktxP9STm6OdMc5uR1xF1SGr98uv8dUlAL2hqwrZ3GVB3uyMyiegnscEK6PGtYvNrjTjw==", "dev": true, "license": "MIT", "dependencies": { diff --git a/server/package.json b/server/package.json index 91e68718bb..86abe8979c 100644 --- a/server/package.json +++ b/server/package.json @@ -134,7 +134,7 @@ "@types/lodash": "^4.14.197", "@types/luxon": "^3.6.2", "@types/mock-fs": "^4.13.1", - "@types/multer": "^1.4.7", + "@types/multer": "^2.0.0", "@types/node": "^22.16.4", "@types/nodemailer": "^6.4.14", "@types/picomatch": "^4.0.0", From 97daf42fd5e83206e2259292cb3a5d1e091bc975 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 22 Jul 2025 10:24:00 -0500 Subject: [PATCH 009/748] chore: don't show beta switcher if connect to server below 1.136 (#20084) --- .../lib/widgets/settings/beta_timeline_list_tile.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/mobile/lib/widgets/settings/beta_timeline_list_tile.dart b/mobile/lib/widgets/settings/beta_timeline_list_tile.dart index a9c873cb67..154ccb3552 100644 --- a/mobile/lib/widgets/settings/beta_timeline_list_tile.dart +++ b/mobile/lib/widgets/settings/beta_timeline_list_tile.dart @@ -1,11 +1,14 @@ import 'dart:math' as math; import 'package:auto_route/auto_route.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/providers/auth.provider.dart'; +import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; @@ -69,6 +72,13 @@ class _BetaTimelineListTileState extends ConsumerState final betaTimelineValue = ref .watch(appSettingsServiceProvider) .getSetting(AppSettingsEnum.betaTimeline); + final serverInfo = ref.watch(serverInfoProvider); + final auth = ref.watch(authProvider); + + if (!auth.isAuthenticated || + (serverInfo.serverVersion.minor < 136 && kReleaseMode)) { + return const SizedBox.shrink(); + } return AnimatedBuilder( animation: _animationController, From 2efca67217ea7c1306c02624c65dc321235aee82 Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Tue, 22 Jul 2025 11:24:32 -0500 Subject: [PATCH 010/748] feat(mobile): beta sync stats page (#19950) * show beta sync stats * show status next to jobs * use drift devtools reset database impl * dcm fixes * fix: hash count * styling --------- Co-authored-by: Alex --- i18n/en.json | 19 + mobile/lib/domain/services/asset.service.dart | 11 + .../domain/services/local_album.service.dart | 4 + .../lib/domain/services/memory.service.dart | 4 + .../domain/services/remote_album.service.dart | 4 + mobile/lib/domain/utils/background_sync.dart | 27 ++ .../repositories/local_album.repository.dart | 4 + .../repositories/local_asset.repository.dart | 10 + .../repositories/memory.repository.dart | 4 + .../repositories/remote_album.repository.dart | 4 + .../repositories/remote_asset.repository.dart | 4 + mobile/lib/pages/common/settings.page.dart | 55 +-- .../settings/beta_sync_settings.page.dart | 29 ++ .../providers/background_sync.provider.dart | 6 + .../lib/providers/sync_status.provider.dart | 69 +++- mobile/lib/routing/router.dart | 6 +- mobile/lib/routing/router.gr.dart | 16 + .../beta_sync_settings.dart | 348 ++++++++++++++++++ .../beta_sync_settings/entity_count_tile.dart | 99 +++++ .../lib/widgets/settings/settings_card.dart | 63 ++++ 20 files changed, 742 insertions(+), 44 deletions(-) create mode 100644 mobile/lib/pages/settings/beta_sync_settings.page.dart create mode 100644 mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart create mode 100644 mobile/lib/widgets/settings/beta_sync_settings/entity_count_tile.dart create mode 100644 mobile/lib/widgets/settings/settings_card.dart diff --git a/i18n/en.json b/i18n/en.json index 8961943678..ddcb01bf94 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -573,6 +573,8 @@ "backup_options_page_title": "Backup options", "backup_setting_subtitle": "Manage background and foreground upload settings", "backward": "Backward", + "beta_sync": "Beta Sync Status", + "beta_sync_subtitle": "Manage the new sync system", "biometric_auth_enabled": "Biometric authentication enabled", "biometric_locked_out": "You are locked out of biometric authentication", "biometric_no_options": "No biometric options available", @@ -1051,6 +1053,9 @@ "haptic_feedback_switch": "Enable haptic feedback", "haptic_feedback_title": "Haptic Feedback", "has_quota": "Has quota", + "hash_asset": "Hash asset", + "hashed_assets": "Hashed assets", + "hashing": "Hashing", "header_settings_add_header_tip": "Add Header", "header_settings_field_validator_msg": "Value cannot be empty", "header_settings_header_name_input": "Header name", @@ -1083,6 +1088,7 @@ "host": "Host", "hour": "Hour", "id": "ID", + "idle": "Idle", "ignore_icloud_photos": "Ignore iCloud photos", "ignore_icloud_photos_description": "Photos that are stored on iCloud will not be uploaded to the Immich server", "image": "Image", @@ -1165,7 +1171,9 @@ "list": "List", "loading": "Loading", "loading_search_results_failed": "Loading search results failed", + "local": "Local", "local_asset_cast_failed": "Unable to cast an asset that is not uploaded to the server", + "local_assets": "Local Assets", "local_network": "Local network", "local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network", "location_permission": "Location permission", @@ -1359,6 +1367,7 @@ "original": "original", "other": "Other", "other_devices": "Other devices", + "other_entities": "Other entities", "other_variables": "Other variables", "owned": "Owned", "owner": "Owner", @@ -1519,6 +1528,8 @@ "refreshing_faces": "Refreshing faces", "refreshing_metadata": "Refreshing metadata", "regenerating_thumbnails": "Regenerating thumbnails", + "remote": "Remote", + "remote_assets": "Remote Assets", "remove": "Remove", "remove_assets_album_confirmation": "Are you sure you want to remove {count, plural, one {# asset} other {# assets}} from the album?", "remove_assets_shared_link_confirmation": "Are you sure you want to remove {count, plural, one {# asset} other {# assets}} from this shared link?", @@ -1556,6 +1567,9 @@ "reset_password": "Reset password", "reset_people_visibility": "Reset people visibility", "reset_pin_code": "Reset PIN code", + "reset_sqlite": "Reset SQLite Database", + "reset_sqlite_confirmation": "Are you sure you want to reset the SQLite database? You will need to log out and log in again to resync the data", + "reset_sqlite_success": "Successfully reset the SQLite database", "reset_to_default": "Reset to default", "resolve_duplicates": "Resolve duplicates", "resolved_all_duplicates": "Resolved all duplicates", @@ -1569,6 +1583,7 @@ "role": "Role", "role_editor": "Editor", "role_viewer": "Viewer", + "running": "Running", "save": "Save", "save_to_gallery": "Save to gallery", "saved_api_key": "Saved API Key", @@ -1822,6 +1837,7 @@ "storage_quota": "Storage Quota", "storage_usage": "{used} of {available} used", "submit": "Submit", + "success": "Success", "suggestions": "Suggestions", "sunrise_on_the_beach": "Sunrise on the beach", "support": "Support", @@ -1831,6 +1847,8 @@ "sync": "Sync", "sync_albums": "Sync albums", "sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums", + "sync_local": "Sync Local", + "sync_remote": "Sync Remote", "sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich", "tag": "Tag", "tag_assets": "Tag assets", @@ -1841,6 +1859,7 @@ "tag_updated": "Updated tag: {tag}", "tagged_assets": "Tagged {count, plural, one {# asset} other {# assets}}", "tags": "Tags", + "tap_to_run_job": "Tap to run job", "template": "Template", "theme": "Theme", "theme_selection": "Theme selection", diff --git a/mobile/lib/domain/services/asset.service.dart b/mobile/lib/domain/services/asset.service.dart index 63b1aad8c1..995ed42dc8 100644 --- a/mobile/lib/domain/services/asset.service.dart +++ b/mobile/lib/domain/services/asset.service.dart @@ -76,4 +76,15 @@ class AssetService { Future> getPlaces() { return _remoteAssetRepository.getPlaces(); } + + Future<(int local, int remote)> getAssetCounts() async { + return ( + await _localAssetRepository.getCount(), + await _remoteAssetRepository.getCount() + ); + } + + Future getLocalHashedCount() { + return _localAssetRepository.getHashedCount(); + } } diff --git a/mobile/lib/domain/services/local_album.service.dart b/mobile/lib/domain/services/local_album.service.dart index 79cc58f3e0..6c1479fdc9 100644 --- a/mobile/lib/domain/services/local_album.service.dart +++ b/mobile/lib/domain/services/local_album.service.dart @@ -18,4 +18,8 @@ class LocalAlbumService { Future update(LocalAlbum album) { return _repository.upsert(album); } + + Future getCount() { + return _repository.getCount(); + } } diff --git a/mobile/lib/domain/services/memory.service.dart b/mobile/lib/domain/services/memory.service.dart index c94b8a9f0a..50ed1e0f75 100644 --- a/mobile/lib/domain/services/memory.service.dart +++ b/mobile/lib/domain/services/memory.service.dart @@ -12,4 +12,8 @@ class DriftMemoryService { Future> getMemoryLane(String ownerId) { return _repository.getAll(ownerId); } + + Future getCount() { + return _repository.getCount(); + } } diff --git a/mobile/lib/domain/services/remote_album.service.dart b/mobile/lib/domain/services/remote_album.service.dart index ebb24d5fe5..0f36fac2b9 100644 --- a/mobile/lib/domain/services/remote_album.service.dart +++ b/mobile/lib/domain/services/remote_album.service.dart @@ -147,4 +147,8 @@ class RemoteAlbumService { return _repository.addUsers(albumId, userIds); } + + Future getCount() { + return _repository.getCount(); + } } diff --git a/mobile/lib/domain/utils/background_sync.dart b/mobile/lib/domain/utils/background_sync.dart index 4a44c4d8f2..af66dda7a9 100644 --- a/mobile/lib/domain/utils/background_sync.dart +++ b/mobile/lib/domain/utils/background_sync.dart @@ -12,6 +12,14 @@ class BackgroundSyncManager { final SyncCallback? onRemoteSyncComplete; final SyncErrorCallback? onRemoteSyncError; + final SyncCallback? onLocalSyncStart; + final SyncCallback? onLocalSyncComplete; + final SyncErrorCallback? onLocalSyncError; + + final SyncCallback? onHashingStart; + final SyncCallback? onHashingComplete; + final SyncErrorCallback? onHashingError; + Cancelable? _syncTask; Cancelable? _syncWebsocketTask; Cancelable? _deviceAlbumSyncTask; @@ -21,6 +29,12 @@ class BackgroundSyncManager { this.onRemoteSyncStart, this.onRemoteSyncComplete, this.onRemoteSyncError, + this.onLocalSyncStart, + this.onLocalSyncComplete, + this.onLocalSyncError, + this.onHashingStart, + this.onHashingComplete, + this.onHashingError, }); Future cancel() { @@ -47,6 +61,8 @@ class BackgroundSyncManager { return _deviceAlbumSyncTask!.future; } + onLocalSyncStart?.call(); + // We use a ternary operator to avoid [_deviceAlbumSyncTask] from being // captured by the closure passed to [runInIsolateGentle]. _deviceAlbumSyncTask = full @@ -61,6 +77,10 @@ class BackgroundSyncManager { return _deviceAlbumSyncTask!.whenComplete(() { _deviceAlbumSyncTask = null; + onLocalSyncComplete?.call(); + }).catchError((error) { + onLocalSyncError?.call(error.toString()); + _deviceAlbumSyncTask = null; }); } @@ -70,10 +90,17 @@ class BackgroundSyncManager { return _hashTask!.future; } + onHashingStart?.call(); + _hashTask = runInIsolateGentle( computation: (ref) => ref.read(hashServiceProvider).hashAssets(), ); + return _hashTask!.whenComplete(() { + onHashingComplete?.call(); + _hashTask = null; + }).catchError((error) { + onHashingError?.call(error.toString()); _hashTask = null; }); } diff --git a/mobile/lib/infrastructure/repositories/local_album.repository.dart b/mobile/lib/infrastructure/repositories/local_album.repository.dart index 5f192a20cf..16c363b839 100644 --- a/mobile/lib/infrastructure/repositories/local_album.repository.dart +++ b/mobile/lib/infrastructure/repositories/local_album.repository.dart @@ -398,4 +398,8 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { return results.isNotEmpty ? results.first : null; } + + Future getCount() { + return _db.managers.localAlbumEntity.count(); + } } diff --git a/mobile/lib/infrastructure/repositories/local_asset.repository.dart b/mobile/lib/infrastructure/repositories/local_asset.repository.dart index 8d21c858a2..bf1ec37615 100644 --- a/mobile/lib/infrastructure/repositories/local_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/local_asset.repository.dart @@ -63,4 +63,14 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository { return query.map((row) => row.toDto()).getSingleOrNull(); } + + Future getCount() { + return _db.managers.localAssetEntity.count(); + } + + Future getHashedCount() { + return _db.managers.localAssetEntity + .filter((e) => e.checksum.isNull().not()) + .count(); + } } diff --git a/mobile/lib/infrastructure/repositories/memory.repository.dart b/mobile/lib/infrastructure/repositories/memory.repository.dart index ff5f75c2ac..8582290c61 100644 --- a/mobile/lib/infrastructure/repositories/memory.repository.dart +++ b/mobile/lib/infrastructure/repositories/memory.repository.dart @@ -58,6 +58,10 @@ class DriftMemoryRepository extends DriftDatabaseRepository { return memoriesMap.values.toList(); } + + Future getCount() { + return _db.managers.memoryEntity.count(); + } } extension on MemoryEntityData { diff --git a/mobile/lib/infrastructure/repositories/remote_album.repository.dart b/mobile/lib/infrastructure/repositories/remote_album.repository.dart index c3c4570559..ca8dc2cfd5 100644 --- a/mobile/lib/infrastructure/repositories/remote_album.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_album.repository.dart @@ -268,6 +268,10 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { return album; }).watchSingleOrNull(); } + + Future getCount() { + return _db.managers.remoteAlbumEntity.count(); + } } extension on RemoteAlbumEntityData { diff --git a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart index 52cfe2e7c2..865c35be54 100644 --- a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart @@ -238,4 +238,8 @@ class RemoteAssetRepository extends DriftDatabaseRepository { }); }); } + + Future getCount() { + return _db.managers.remoteAssetEntity.count(); + } } diff --git a/mobile/lib/pages/common/settings.page.dart b/mobile/lib/pages/common/settings.page.dart index e45001270c..b19ff87aa9 100644 --- a/mobile/lib/pages/common/settings.page.dart +++ b/mobile/lib/pages/common/settings.page.dart @@ -13,6 +13,8 @@ import 'package:immich_mobile/widgets/settings/language_settings.dart'; import 'package:immich_mobile/widgets/settings/networking_settings/networking_settings.dart'; import 'package:immich_mobile/widgets/settings/notification_setting.dart'; import 'package:immich_mobile/widgets/settings/preference_settings/preference_setting.dart'; +import 'package:immich_mobile/entities/store.entity.dart' as app_store; +import 'package:immich_mobile/widgets/settings/settings_card.dart'; enum SettingSection { advanced( @@ -97,47 +99,11 @@ class _MobileLayout extends StatelessWidget { Widget build(BuildContext context) { final List settings = SettingSection.values .map( - (setting) => Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), - child: Card( - elevation: 0, - clipBehavior: Clip.antiAlias, - color: context.colorScheme.surfaceContainer, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(16)), - ), - margin: const EdgeInsets.symmetric(vertical: 4.0), - child: ListTile( - contentPadding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), - leading: Container( - decoration: BoxDecoration( - borderRadius: const BorderRadius.all(Radius.circular(16)), - color: context.isDarkTheme - ? Colors.black26 - : Colors.white.withAlpha(100), - ), - padding: const EdgeInsets.all(16.0), - child: Icon(setting.icon, color: context.primaryColor), - ), - title: Text( - setting.title, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w600, - color: context.primaryColor, - ), - ).tr(), - subtitle: Text( - setting.subtitle, - style: context.textTheme.labelLarge, - ).tr(), - onTap: () => - context.pushRoute(SettingsSubRoute(section: setting)), - ), - ), + (setting) => SettingsCard( + title: setting.title.tr(), + subtitle: setting.subtitle.tr(), + icon: setting.icon, + settingRoute: SettingsSubRoute(section: setting), ), ) .toList(); @@ -146,6 +112,13 @@ class _MobileLayout extends StatelessWidget { padding: const EdgeInsets.only(top: 10.0, bottom: 56), children: [ const BetaTimelineListTile(), + if (app_store.Store.isBetaTimelineEnabled) + SettingsCard( + icon: Icons.sync_outlined, + title: 'beta_sync'.tr(), + subtitle: 'beta_sync_subtitle'.tr(), + settingRoute: const BetaSyncSettingsRoute(), + ), ...settings, ], ); diff --git a/mobile/lib/pages/settings/beta_sync_settings.page.dart b/mobile/lib/pages/settings/beta_sync_settings.page.dart new file mode 100644 index 0000000000..ba23ccf5eb --- /dev/null +++ b/mobile/lib/pages/settings/beta_sync_settings.page.dart @@ -0,0 +1,29 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/widgets/settings/beta_sync_settings/beta_sync_settings.dart'; + +@RoutePage() +class BetaSyncSettingsPage extends StatelessWidget { + const BetaSyncSettingsPage({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + elevation: 0, + title: const Text("beta_sync").t(context: context), + leading: IconButton( + onPressed: () => context.maybePop(true), + splashRadius: 24, + icon: const Icon( + Icons.arrow_back_ios_rounded, + ), + ), + ), + body: const BetaSyncSettings(), + ); + } +} diff --git a/mobile/lib/providers/background_sync.provider.dart b/mobile/lib/providers/background_sync.provider.dart index dc9cc0d59f..e6e83b64df 100644 --- a/mobile/lib/providers/background_sync.provider.dart +++ b/mobile/lib/providers/background_sync.provider.dart @@ -8,6 +8,12 @@ final backgroundSyncProvider = Provider((ref) { onRemoteSyncStart: syncStatusNotifier.startRemoteSync, onRemoteSyncComplete: syncStatusNotifier.completeRemoteSync, onRemoteSyncError: syncStatusNotifier.errorRemoteSync, + onLocalSyncStart: syncStatusNotifier.startLocalSync, + onLocalSyncComplete: syncStatusNotifier.completeLocalSync, + onLocalSyncError: syncStatusNotifier.errorLocalSync, + onHashingStart: syncStatusNotifier.startHashJob, + onHashingComplete: syncStatusNotifier.completeHashJob, + onHashingError: syncStatusNotifier.errorHashJob, ); ref.onDispose(manager.cancel); return manager; diff --git a/mobile/lib/providers/sync_status.provider.dart b/mobile/lib/providers/sync_status.provider.dart index 18d851aa19..5640ad6c89 100644 --- a/mobile/lib/providers/sync_status.provider.dart +++ b/mobile/lib/providers/sync_status.provider.dart @@ -1,43 +1,71 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; enum SyncStatus { idle, syncing, success, - error, + error; + + localized() { + return switch (this) { + SyncStatus.idle => "idle".tr(), + SyncStatus.syncing => "running".tr(), + SyncStatus.success => "success".tr(), + SyncStatus.error => "error".tr() + }; + } } class SyncStatusState { final SyncStatus remoteSyncStatus; + final SyncStatus localSyncStatus; + final SyncStatus hashJobStatus; + final String? errorMessage; const SyncStatusState({ this.remoteSyncStatus = SyncStatus.idle, + this.localSyncStatus = SyncStatus.idle, + this.hashJobStatus = SyncStatus.idle, this.errorMessage, }); SyncStatusState copyWith({ SyncStatus? remoteSyncStatus, + SyncStatus? localSyncStatus, + SyncStatus? hashJobStatus, String? errorMessage, }) { return SyncStatusState( remoteSyncStatus: remoteSyncStatus ?? this.remoteSyncStatus, + localSyncStatus: localSyncStatus ?? this.localSyncStatus, + hashJobStatus: hashJobStatus ?? this.hashJobStatus, errorMessage: errorMessage ?? this.errorMessage, ); } bool get isRemoteSyncing => remoteSyncStatus == SyncStatus.syncing; + bool get isLocalSyncing => localSyncStatus == SyncStatus.syncing; + bool get isHashing => hashJobStatus == SyncStatus.syncing; @override bool operator ==(Object other) { if (identical(this, other)) return true; return other is SyncStatusState && other.remoteSyncStatus == remoteSyncStatus && + other.localSyncStatus == localSyncStatus && + other.hashJobStatus == hashJobStatus && other.errorMessage == errorMessage; } @override - int get hashCode => Object.hash(remoteSyncStatus, errorMessage); + int get hashCode => Object.hash( + remoteSyncStatus, + localSyncStatus, + hashJobStatus, + errorMessage, + ); } class SyncStatusNotifier extends Notifier { @@ -46,9 +74,15 @@ class SyncStatusNotifier extends Notifier { return const SyncStatusState( errorMessage: null, remoteSyncStatus: SyncStatus.idle, + localSyncStatus: SyncStatus.idle, + hashJobStatus: SyncStatus.idle, ); } + /// + /// Remote Sync + /// + void setRemoteSyncStatus(SyncStatus status, [String? errorMessage]) { state = state.copyWith( remoteSyncStatus: status, @@ -60,6 +94,37 @@ class SyncStatusNotifier extends Notifier { void completeRemoteSync() => setRemoteSyncStatus(SyncStatus.success); void errorRemoteSync(String error) => setRemoteSyncStatus(SyncStatus.error, error); + + /// + /// Local Sync + /// + + void setLocalSyncStatus(SyncStatus status, [String? errorMessage]) { + state = state.copyWith( + localSyncStatus: status, + errorMessage: status == SyncStatus.error ? errorMessage : null, + ); + } + + void startLocalSync() => setLocalSyncStatus(SyncStatus.syncing); + void completeLocalSync() => setLocalSyncStatus(SyncStatus.success); + void errorLocalSync(String error) => + setLocalSyncStatus(SyncStatus.error, error); + + /// + /// Hash Job + /// + + void setHashJobStatus(SyncStatus status, [String? errorMessage]) { + state = state.copyWith( + hashJobStatus: status, + errorMessage: status == SyncStatus.error ? errorMessage : null, + ); + } + + void startHashJob() => setHashJobStatus(SyncStatus.syncing); + void completeHashJob() => setHashJobStatus(SyncStatus.success); + void errorHashJob(String error) => setHashJobStatus(SyncStatus.error, error); } final syncStatusProvider = diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index ba31ccef2b..5b3a92d4e2 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -73,6 +73,7 @@ import 'package:immich_mobile/pages/search/map/map_location_picker.page.dart'; import 'package:immich_mobile/pages/search/person_result.page.dart'; import 'package:immich_mobile/pages/search/recently_taken.page.dart'; import 'package:immich_mobile/pages/search/search.page.dart'; +import 'package:immich_mobile/pages/settings/beta_sync_settings.page.dart'; import 'package:immich_mobile/pages/share_intent/share_intent.page.dart'; import 'package:immich_mobile/presentation/pages/dev/feat_in_development.page.dart'; import 'package:immich_mobile/presentation/pages/dev/main_timeline.page.dart'; @@ -481,7 +482,6 @@ class AppRouter extends RootStackRouter { page: DriftUserSelectionRoute.page, guards: [_authGuard, _duplicateGuard], ), - AutoRoute( page: ChangeExperienceRoute.page, guards: [_authGuard, _duplicateGuard], @@ -495,6 +495,10 @@ class AppRouter extends RootStackRouter { page: DriftUploadDetailRoute.page, guards: [_authGuard, _duplicateGuard], ), + AutoRoute( + page: BetaSyncSettingsRoute.page, + guards: [_authGuard, _duplicateGuard], + ), // required to handle all deeplinks in deep_link.service.dart // auto_route_library#1722 RedirectRoute(path: '*', redirectTo: '/'), diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index 0e24f776d8..a59b15856f 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -503,6 +503,22 @@ class BackupOptionsRoute extends PageRouteInfo { ); } +/// generated route for +/// [BetaSyncSettingsPage] +class BetaSyncSettingsRoute extends PageRouteInfo { + const BetaSyncSettingsRoute({List? children}) + : super(BetaSyncSettingsRoute.name, initialChildren: children); + + static const String name = 'BetaSyncSettingsRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const BetaSyncSettingsPage(); + }, + ); +} + /// generated route for /// [ChangeExperiencePage] class ChangeExperienceRoute extends PageRouteInfo { diff --git a/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart b/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart new file mode 100644 index 0000000000..3d37fb102b --- /dev/null +++ b/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart @@ -0,0 +1,348 @@ +import 'package:drift/drift.dart' as drift_db; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/background_sync.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/memory.provider.dart'; +import 'package:immich_mobile/providers/sync_status.provider.dart'; +import 'package:immich_mobile/widgets/settings/beta_sync_settings/entity_count_tile.dart'; + +class BetaSyncSettings extends HookConsumerWidget { + const BetaSyncSettings({ + super.key, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final assetService = ref.watch(assetServiceProvider); + final localAlbumService = ref.watch(localAlbumServiceProvider); + final remoteAlbumService = ref.watch(remoteAlbumServiceProvider); + final memoryService = ref.watch(driftMemoryServiceProvider); + + Future> loadCounts() async { + final assetCounts = assetService.getAssetCounts(); + final localAlbumCounts = localAlbumService.getCount(); + final remoteAlbumCounts = remoteAlbumService.getCount(); + final memoryCount = memoryService.getCount(); + final getLocalHashedCount = assetService.getLocalHashedCount(); + + return await Future.wait([ + assetCounts, + localAlbumCounts, + remoteAlbumCounts, + memoryCount, + getLocalHashedCount, + ]); + } + + Future resetDatabase() async { +// https://github.com/simolus3/drift/commit/bd80a46264b6dd833ef4fd87fffc03f5a832ab41#diff-3f879e03b4a35779344ef16170b9353608dd9c42385f5402ec6035aac4dd8a04R76-R94 + final drift = ref.read(driftProvider); + final database = drift.attachedDatabase; + await database.exclusively(() async { + // https://stackoverflow.com/a/65743498/25690041 + await database.customStatement('PRAGMA writable_schema = 1;'); + await database.customStatement('DELETE FROM sqlite_master;'); + await database.customStatement('VACUUM;'); + await database.customStatement('PRAGMA writable_schema = 0;'); + await database.customStatement('PRAGMA integrity_check'); + + await database.customStatement('PRAGMA user_version = 0'); + await database.beforeOpen( + // ignore: invalid_use_of_internal_member + database.resolvedEngine.executor, + drift_db.OpeningDetails(null, database.schemaVersion), + ); + await database.customStatement( + 'PRAGMA user_version = ${database.schemaVersion}', + ); + + // Refresh all stream queries + database.notifyUpdates({ + for (final table in database.allTables) + drift_db.TableUpdate.onTable(table), + }); + }); + } + + return FutureBuilder>( + future: loadCounts(), + builder: (context, snapshot) { + if (snapshot.connectionState != ConnectionState.done) { + return const CircularProgressIndicator(); + } + + final assetCounts = snapshot.data![0]! as (int, int); + final localAssetCount = assetCounts.$1; + final remoteAssetCount = assetCounts.$2; + + final localAlbumCount = snapshot.data![1]! as int; + final remoteAlbumCount = snapshot.data![2]! as int; + final memoryCount = snapshot.data![3]! as int; + final localHashedCount = snapshot.data![4]! as int; + + return Padding( + padding: const EdgeInsets.only(top: 16, bottom: 32), + child: ListView( + children: [ + _SectionHeaderText(text: "assets".t(context: context)), + Padding( + padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), + child: Flex( + direction: Axis.horizontal, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + spacing: 8.0, + children: [ + Expanded( + child: EntitiyCountTile( + label: "local".t(context: context), + count: localAssetCount, + icon: Icons.smartphone, + ), + ), + Expanded( + child: EntitiyCountTile( + label: "remote".t(context: context), + count: remoteAssetCount, + icon: Icons.cloud, + ), + ), + ], + ), + ), + _SectionHeaderText(text: "albums".t(context: context)), + Padding( + padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), + child: Flex( + direction: Axis.horizontal, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + spacing: 8.0, + children: [ + Expanded( + child: EntitiyCountTile( + label: "local".t(context: context), + count: localAlbumCount, + icon: Icons.smartphone, + ), + ), + Expanded( + child: EntitiyCountTile( + label: "remote".t(context: context), + count: remoteAlbumCount, + icon: Icons.cloud, + ), + ), + ], + ), + ), + _SectionHeaderText(text: "other".t(context: context)), + Padding( + padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), + child: Flex( + direction: Axis.horizontal, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + spacing: 8.0, + children: [ + Expanded( + child: EntitiyCountTile( + label: "memories".t(context: context), + count: memoryCount, + icon: Icons.calendar_today, + ), + ), + Expanded( + child: EntitiyCountTile( + label: "hashed_assets".t(context: context), + count: localHashedCount, + icon: Icons.tag, + ), + ), + ], + ), + ), + const Divider( + height: 1, + indent: 16, + endIndent: 16, + ), + const SizedBox(height: 24), + _SectionHeaderText(text: "jobs".t(context: context)), + ListTile( + title: Text( + "sync_local".t(context: context), + style: const TextStyle( + fontWeight: FontWeight.w500, + ), + ), + subtitle: Text( + "tap_to_run_job".t(context: context), + ), + leading: const Icon(Icons.sync), + trailing: _SyncStatusIcon( + status: ref.watch(syncStatusProvider).localSyncStatus, + ), + onTap: () { + ref.read(backgroundSyncProvider).syncLocal(full: true); + }, + ), + ListTile( + title: Text( + "sync_remote".t(context: context), + style: const TextStyle( + fontWeight: FontWeight.w500, + ), + ), + subtitle: Text( + "tap_to_run_job".t(context: context), + ), + leading: const Icon(Icons.cloud_sync), + trailing: _SyncStatusIcon( + status: ref.watch(syncStatusProvider).remoteSyncStatus, + ), + onTap: () { + ref.read(backgroundSyncProvider).syncRemote(); + }, + ), + ListTile( + title: Text( + "hash_asset".t(context: context), + style: const TextStyle( + fontWeight: FontWeight.w500, + ), + ), + leading: const Icon(Icons.tag), + subtitle: Text( + "tap_to_run_job".t(context: context), + ), + trailing: _SyncStatusIcon( + status: ref.watch(syncStatusProvider).hashJobStatus, + ), + onTap: () { + ref.read(backgroundSyncProvider).hashAssets(); + }, + ), + const Divider( + height: 1, + indent: 16, + endIndent: 16, + ), + const SizedBox(height: 24), + _SectionHeaderText(text: "actions".t(context: context)), + ListTile( + title: Text( + "reset_sqlite".t(context: context), + style: TextStyle( + color: context.colorScheme.error, + fontWeight: FontWeight.w500, + ), + ), + leading: Icon( + Icons.settings_backup_restore_rounded, + color: context.colorScheme.error, + ), + onTap: () async { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text( + "reset_sqlite".t(context: context), + ), + content: Text( + "reset_sqlite_confirmation".t(context: context), + ), + actions: [ + TextButton( + onPressed: () => context.pop(), + child: Text("cancel".t(context: context)), + ), + TextButton( + onPressed: () async { + await resetDatabase(); + context.pop(); + context.scaffoldMessenger.showSnackBar( + SnackBar( + content: Text( + "reset_sqlite_success".t(context: context), + ), + ), + ); + }, + child: Text( + "confirm".t(context: context), + style: TextStyle( + color: context.colorScheme.error, + ), + ), + ), + ], + ); + }, + ); + }, + ), + ], + ), + ); + }, + ); + } +} + +class _SyncStatusIcon extends StatelessWidget { + final SyncStatus status; + + const _SyncStatusIcon({ + required this.status, + }); + + @override + Widget build(BuildContext context) { + return switch (status) { + SyncStatus.idle => const Icon( + Icons.pause_circle_outline_rounded, + ), + SyncStatus.syncing => const SizedBox( + height: 24, + width: 24, + child: CircularProgressIndicator( + strokeWidth: 2, + ), + ), + SyncStatus.success => const Icon( + Icons.check_circle_outline, + color: Colors.green, + ), + SyncStatus.error => Icon( + Icons.error_outline, + color: context.colorScheme.error, + ), + }; + } +} + +class _SectionHeaderText extends StatelessWidget { + final String text; + + const _SectionHeaderText({ + required this.text, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16.0), + child: Text( + text.toUpperCase(), + style: context.textTheme.labelLarge?.copyWith( + fontWeight: FontWeight.w500, + color: context.colorScheme.onSurface.withAlpha(200), + ), + ), + ); + } +} diff --git a/mobile/lib/widgets/settings/beta_sync_settings/entity_count_tile.dart b/mobile/lib/widgets/settings/beta_sync_settings/entity_count_tile.dart new file mode 100644 index 0000000000..1e140fbf2e --- /dev/null +++ b/mobile/lib/widgets/settings/beta_sync_settings/entity_count_tile.dart @@ -0,0 +1,99 @@ +import 'package:flutter/material.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/theme_extensions.dart'; + +class EntitiyCountTile extends StatelessWidget { + final int count; + final String label; + final IconData icon; + + const EntitiyCountTile({ + super.key, + required this.count, + required this.label, + required this.icon, + }); + + String zeroPadding(int number, int targetWidth) { + final numStr = number.toString(); + return numStr.length < targetWidth + ? "0" * (targetWidth - numStr.length) + : ""; + } + + int calculateMaxDigits(double availableWidth) { + const double charWidth = 11.0; + return (availableWidth / charWidth).floor().clamp(1, 20); + } + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: context.colorScheme.surfaceContainerLow, + borderRadius: const BorderRadius.all(Radius.circular(16)), + border: Border.all( + width: 0.5, + color: context.colorScheme.outline.withAlpha(25), + ), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // Icon and Label + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Icon( + icon, + color: context.primaryColor, + ), + const SizedBox(width: 8), + Text( + label, + style: TextStyle( + color: context.primaryColor, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + ], + ), + const SizedBox(height: 12), + // Number + LayoutBuilder( + builder: (context, constraints) { + final maxDigits = calculateMaxDigits(constraints.maxWidth); + return RichText( + text: TextSpan( + style: const TextStyle( + fontSize: 18, + fontFamily: 'OverpassMono', + fontWeight: FontWeight.w600, + ), + children: [ + TextSpan( + text: zeroPadding(count, maxDigits), + style: TextStyle( + color: context.colorScheme.onSurfaceSecondary + .withAlpha(75), + ), + ), + TextSpan( + text: count.toString(), + style: TextStyle( + color: context.primaryColor, + ), + ), + ], + ), + ); + }, + ), + ], + ), + ); + } +} diff --git a/mobile/lib/widgets/settings/settings_card.dart b/mobile/lib/widgets/settings/settings_card.dart new file mode 100644 index 0000000000..257c0ce2d6 --- /dev/null +++ b/mobile/lib/widgets/settings/settings_card.dart @@ -0,0 +1,63 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; + +class SettingsCard extends StatelessWidget { + const SettingsCard({ + super.key, + required this.icon, + required this.title, + required this.subtitle, + required this.settingRoute, + }); + + final IconData icon; + final String title; + final String subtitle; + final PageRouteInfo settingRoute; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + ), + child: Card( + elevation: 0, + clipBehavior: Clip.antiAlias, + color: context.colorScheme.surfaceContainer, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(16)), + ), + margin: const EdgeInsets.symmetric(vertical: 4.0), + child: ListTile( + contentPadding: const EdgeInsets.symmetric( + horizontal: 16.0, + ), + leading: Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(16)), + color: context.isDarkTheme + ? Colors.black26 + : Colors.white.withAlpha(100), + ), + padding: const EdgeInsets.all(16.0), + child: Icon(icon, color: context.primaryColor), + ), + title: Text( + title, + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w600, + color: context.primaryColor, + ), + ), + subtitle: Text( + subtitle, + style: context.textTheme.labelLarge, + ), + onTap: () => context.pushRoute(settingRoute), + ), + ), + ); + } +} From aa344a398984457d55b82584e2cfe1fec6442d7a Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 22 Jul 2025 11:36:00 -0500 Subject: [PATCH 011/748] feat: delete actions (#20034) * chore: show delete local * pr feedback * restore and perm delete action --- i18n/en.json | 1 + .../repositories/remote_asset.repository.dart | 12 ++++ .../repositories/timeline.repository.dart | 42 ++++++++++--- .../presentation/pages/drift_trash.page.dart | 24 ++++++++ ...elete_permanent_action_button.widget.dart} | 3 +- .../delete_trash_action_button.widget.dart | 59 +++++++++++++++++++ .../restore_trash_action_button.widget.dart | 56 ++++++++++++++++++ .../asset_viewer/bottom_sheet.widget.dart | 2 +- .../archive_bottom_sheet.widget.dart | 2 +- .../favorite_bottom_sheet.widget.dart | 2 +- .../general_bottom_sheet.widget.dart | 5 +- .../locked_folder_bottom_sheet.widget.dart | 2 +- .../remote_album_bottom_sheet.widget.dart | 2 +- .../trash_bottom_sheet.widget.dart | 32 ++++++++++ .../infrastructure/action.provider.dart | 21 ++++++- .../timeline/multiselect.provider.dart | 7 +++ .../repositories/asset_api.repository.dart | 13 +++- mobile/lib/services/action.service.dart | 18 +++++- 18 files changed, 285 insertions(+), 18 deletions(-) rename mobile/lib/presentation/widgets/action_buttons/{delete_action_button.widget.dart => delete_permanent_action_button.widget.dart} (94%) create mode 100644 mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart create mode 100644 mobile/lib/presentation/widgets/action_buttons/restore_trash_action_button.widget.dart create mode 100644 mobile/lib/presentation/widgets/bottom_sheet/trash_bottom_sheet.widget.dart diff --git a/i18n/en.json b/i18n/en.json index ddcb01bf94..bbd6debd5e 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1575,6 +1575,7 @@ "resolved_all_duplicates": "Resolved all duplicates", "restore": "Restore", "restore_all": "Restore all", + "restore_trash_action_prompt": "{count} restored from trash", "restore_user": "Restore user", "restored_asset": "Restored asset", "resume": "Resume", diff --git a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart index 865c35be54..2c5006953f 100644 --- a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart @@ -171,6 +171,18 @@ class RemoteAssetRepository extends DriftDatabaseRepository { }); } + Future restoreTrash(List ids) { + return _db.batch((batch) async { + for (final id in ids) { + batch.update( + _db.remoteAssetEntity, + const RemoteAssetEntityCompanion(deletedAt: Value(null)), + where: (e) => e.id.equals(id), + ); + } + }); + } + Future delete(List ids) { return _db.remoteAssetEntity.deleteWhere((row) => row.id.isIn(ids)); } diff --git a/mobile/lib/infrastructure/repositories/timeline.repository.dart b/mobile/lib/infrastructure/repositories/timeline.repository.dart index 0c3eee59af..7bbae9a80a 100644 --- a/mobile/lib/infrastructure/repositories/timeline.repository.dart +++ b/mobile/lib/infrastructure/repositories/timeline.repository.dart @@ -332,6 +332,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { _remoteQueryBuilder( filter: (row) => row.deletedAt.isNotNull() & row.ownerId.equals(userId), groupBy: groupBy, + joinLocal: true, ); TimelineQuery archived(String userId, GroupAssetsBy groupBy) => @@ -443,11 +444,16 @@ class DriftTimelineRepository extends DriftDatabaseRepository { TimelineQuery _remoteQueryBuilder({ required Expression Function($RemoteAssetEntityTable row) filter, GroupAssetsBy groupBy = GroupAssetsBy.day, + bool joinLocal = false, }) { return ( bucketSource: () => _watchRemoteBucket(filter: filter, groupBy: groupBy), - assetSource: (offset, count) => - _getRemoteAssets(filter: filter, offset: offset, count: count), + assetSource: (offset, count) => _getRemoteAssets( + filter: filter, + offset: offset, + count: count, + joinLocal: joinLocal, + ), ); } @@ -480,13 +486,35 @@ class DriftTimelineRepository extends DriftDatabaseRepository { required Expression Function($RemoteAssetEntityTable row) filter, required int offset, required int count, + bool joinLocal = false, }) { - final query = _db.remoteAssetEntity.select() - ..where(filter) - ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) - ..limit(count, offset: offset); + if (joinLocal) { + final query = _db.remoteAssetEntity.select().join([ + leftOuterJoin( + _db.localAssetEntity, + _db.remoteAssetEntity.checksum + .equalsExp(_db.localAssetEntity.checksum), + useColumns: false, + ), + ]) + ..addColumns([_db.localAssetEntity.id]) + ..where(filter(_db.remoteAssetEntity)) + ..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)]) + ..limit(count, offset: offset); - return query.map((row) => row.toDto()).get(); + return query.map((row) { + final asset = row.readTable(_db.remoteAssetEntity).toDto(); + final localId = row.read(_db.localAssetEntity.id); + return asset.copyWith(localId: localId); + }).get(); + } else { + final query = _db.remoteAssetEntity.select() + ..where(filter) + ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) + ..limit(count, offset: offset); + + return query.map((row) => row.toDto()).get(); + } } } diff --git a/mobile/lib/presentation/pages/drift_trash.page.dart b/mobile/lib/presentation/pages/drift_trash.page.dart index 9cd2fac760..108a7e1cc9 100644 --- a/mobile/lib/presentation/pages/drift_trash.page.dart +++ b/mobile/lib/presentation/pages/drift_trash.page.dart @@ -2,8 +2,10 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/bottom_sheet/trash_bottom_sheet.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; @RoutePage() @@ -29,6 +31,7 @@ class DriftTrashPage extends StatelessWidget { ), ], child: Timeline( + showStorageIndicator: true, appBar: SliverAppBar( title: Text('trash'.t(context: context)), floating: true, @@ -37,6 +40,27 @@ class DriftTrashPage extends StatelessWidget { centerTitle: true, elevation: 0, ), + topSliverWidgetHeight: 24, + topSliverWidget: Consumer( + builder: (context, ref, child) { + final trashDays = ref.watch( + serverInfoProvider.select((v) => v.serverConfig.trashDays), + ); + + return SliverPadding( + padding: const EdgeInsets.all(16.0), + sliver: SliverToBoxAdapter( + child: SizedBox( + height: 24.0, + child: const Text( + "trash_page_info", + ).t(context: context, args: {"days": "$trashDays"}), + ), + ), + ); + }, + ), + bottomSheet: const TrashBottomBar(), ), ); } diff --git a/mobile/lib/presentation/widgets/action_buttons/delete_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart similarity index 94% rename from mobile/lib/presentation/widgets/action_buttons/delete_action_button.widget.dart rename to mobile/lib/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart index d81f998a7b..9f88d640dd 100644 --- a/mobile/lib/presentation/widgets/action_buttons/delete_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart @@ -18,7 +18,8 @@ class DeletePermanentActionButton extends ConsumerWidget { return; } - final result = await ref.read(actionProvider.notifier).delete(source); + final result = + await ref.read(actionProvider.notifier).deleteRemoteAndLocal(source); ref.read(multiSelectProvider.notifier).reset(); final successMessage = 'delete_action_prompt'.t( diff --git a/mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart new file mode 100644 index 0000000000..3ec75c60c2 --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; + +class DeleteTrashActionButton extends ConsumerWidget { + final ActionSource source; + + const DeleteTrashActionButton({super.key, required this.source}); + + void _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = + await ref.read(actionProvider.notifier).deleteRemoteAndLocal(source); + ref.read(multiSelectProvider.notifier).reset(); + + final successMessage = 'restore_trash_action_prompt'.t( + context: context, + args: {'count': result.count.toString()}, + ); + + if (context.mounted) { + ImmichToast.show( + context: context, + msg: result.success + ? successMessage + : 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: result.success ? ToastType.success : ToastType.error, + ); + } + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + return TextButton.icon( + icon: Icon( + Icons.delete_forever, + color: Colors.red[400], + ), + label: Text( + "delete".t(context: context), + style: TextStyle( + fontSize: 14, + color: Colors.red[400], + fontWeight: FontWeight.bold, + ), + ), + onPressed: () => _onTap(context, ref), + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/restore_trash_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/restore_trash_action_button.widget.dart new file mode 100644 index 0000000000..05ee469d25 --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/restore_trash_action_button.widget.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; + +class RestoreTrashActionButton extends ConsumerWidget { + final ActionSource source; + + const RestoreTrashActionButton({super.key, required this.source}); + + void _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = await ref.read(actionProvider.notifier).restoreTrash(source); + ref.read(multiSelectProvider.notifier).reset(); + + final successMessage = 'restore_trash_action_prompt'.t( + context: context, + args: {'count': result.count.toString()}, + ); + + if (context.mounted) { + ImmichToast.show( + context: context, + msg: result.success + ? successMessage + : 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: result.success ? ToastType.success : ToastType.error, + ); + } + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + return TextButton.icon( + icon: const Icon( + Icons.history_rounded, + ), + label: Text( + 'restore'.t(), + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + ), + ), + onPressed: () => _onTap(context, ref), + ); + } +} diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart index 89822fef91..0e426fde6d 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart @@ -7,7 +7,7 @@ import 'package:immich_mobile/domain/models/exif.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart'; diff --git a/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart index 9ed35da4cd..deaaea0d39 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart'; diff --git a/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart index a1e1255a9f..8199271bfe 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart'; diff --git a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart index 373d264d82..d83b8e399d 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart'; @@ -44,6 +44,9 @@ class GeneralBottomSheet extends ConsumerWidget { : const DeletePermanentActionButton( source: ActionSource.timeline, ), + if (multiselect.hasLocal || multiselect.hasMerged) ...[ + const DeleteLocalActionButton(source: ActionSource.timeline), + ], const EditDateTimeActionButton(), const EditLocationActionButton(source: ActionSource.timeline), const MoveToLockFolderActionButton( diff --git a/mobile/lib/presentation/widgets/bottom_sheet/locked_folder_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/locked_folder_bottom_sheet.widget.dart index 7f82f750f7..a644e6a035 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/locked_folder_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/locked_folder_bottom_sheet.widget.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/remove_from_lock_folder_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_button.widget.dart'; diff --git a/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart index cfb6fe4f1a..2e6047f0ba 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart @@ -3,7 +3,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/album/album.model.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart'; diff --git a/mobile/lib/presentation/widgets/bottom_sheet/trash_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/trash_bottom_sheet.widget.dart new file mode 100644 index 0000000000..9f8216c4ed --- /dev/null +++ b/mobile/lib/presentation/widgets/bottom_sheet/trash_bottom_sheet.widget.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/restore_trash_action_button.widget.dart'; + +class TrashBottomBar extends ConsumerWidget { + const TrashBottomBar({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return SafeArea( + child: Align( + alignment: Alignment.bottomCenter, + child: SizedBox( + height: 64, + child: Container( + color: context.themeData.canvasColor, + child: const Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + DeleteTrashActionButton(source: ActionSource.timeline), + RestoreTrashActionButton(source: ActionSource.timeline), + ], + ), + ), + ), + ), + ); + } +} diff --git a/mobile/lib/providers/infrastructure/action.provider.dart b/mobile/lib/providers/infrastructure/action.provider.dart index cb025ef941..419ba0f902 100644 --- a/mobile/lib/providers/infrastructure/action.provider.dart +++ b/mobile/lib/providers/infrastructure/action.provider.dart @@ -206,6 +206,7 @@ class ActionNotifier extends Notifier { Future trash(ActionSource source) async { final ids = _getOwnedRemoteIdsForSource(source); + try { await _service.trash(ids); return ActionResult(count: ids.length, success: true); @@ -219,10 +220,26 @@ class ActionNotifier extends Notifier { } } - Future delete(ActionSource source) async { + Future restoreTrash(ActionSource source) async { final ids = _getOwnedRemoteIdsForSource(source); try { - await _service.delete(ids); + await _service.restoreTrash(ids); + return ActionResult(count: ids.length, success: true); + } catch (error, stack) { + _logger.severe('Failed to restore trash assets', error, stack); + return ActionResult( + count: ids.length, + success: false, + error: error.toString(), + ); + } + } + + Future deleteRemoteAndLocal(ActionSource source) async { + final ids = _getOwnedRemoteIdsForSource(source); + final localIds = _getLocalIdsForSource(source); + try { + await _service.deleteRemoteAndLocal(ids, localIds); return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to delete assets', error, stack); diff --git a/mobile/lib/providers/timeline/multiselect.provider.dart b/mobile/lib/providers/timeline/multiselect.provider.dart index b1a926545d..9e0f690e7d 100644 --- a/mobile/lib/providers/timeline/multiselect.provider.dart +++ b/mobile/lib/providers/timeline/multiselect.provider.dart @@ -23,15 +23,22 @@ class MultiSelectState { }); bool get isEnabled => selectedAssets.isNotEmpty; + + /// Cloud only bool get hasRemote => selectedAssets.any( (asset) => asset.storage == AssetState.remote || asset.storage == AssetState.merged, ); + bool get hasLocal => selectedAssets.any( (asset) => asset.storage == AssetState.local, ); + bool get hasMerged => selectedAssets.any( + (asset) => asset.storage == AssetState.merged, + ); + MultiSelectState copyWith({ Set? selectedAssets, Set? lockedSelectionAssets, diff --git a/mobile/lib/repositories/asset_api.repository.dart b/mobile/lib/repositories/asset_api.repository.dart index 4c854973b1..6d08b4f0d9 100644 --- a/mobile/lib/repositories/asset_api.repository.dart +++ b/mobile/lib/repositories/asset_api.repository.dart @@ -13,6 +13,7 @@ final assetApiRepositoryProvider = Provider( ref.watch(apiServiceProvider).assetsApi, ref.watch(apiServiceProvider).searchApi, ref.watch(apiServiceProvider).stacksApi, + ref.watch(apiServiceProvider).trashApi, ), ); @@ -20,8 +21,14 @@ class AssetApiRepository extends ApiRepository { final AssetsApi _api; final SearchApi _searchApi; final StacksApi _stacksApi; + final TrashApi _trashApi; - AssetApiRepository(this._api, this._searchApi, this._stacksApi); + AssetApiRepository( + this._api, + this._searchApi, + this._stacksApi, + this._trashApi, + ); Future update(String id, {String? description}) async { final response = await checkNull( @@ -56,6 +63,10 @@ class AssetApiRepository extends ApiRepository { return _api.deleteAssets(AssetBulkDeleteDto(ids: ids, force: force)); } + Future restoreTrash(List ids) async { + await _trashApi.restoreAssets(BulkIdsDto(ids: ids)); + } + Future updateVisibility( List ids, AssetVisibilityEnum visibility, diff --git a/mobile/lib/services/action.service.dart b/mobile/lib/services/action.service.dart index 7b0d74e420..63bc053a41 100644 --- a/mobile/lib/services/action.service.dart +++ b/mobile/lib/services/action.service.dart @@ -127,9 +127,25 @@ class ActionService { await _remoteAssetRepository.trash(remoteIds); } - Future delete(List remoteIds) async { + Future restoreTrash(List ids) async { + await _assetApiRepository.restoreTrash(ids); + await _remoteAssetRepository.restoreTrash(ids); + } + + Future deleteRemoteAndLocal( + List remoteIds, + List localIds, + ) async { await _assetApiRepository.delete(remoteIds, true); await _remoteAssetRepository.delete(remoteIds); + + if (localIds.isNotEmpty) { + final deletedIds = await _assetMediaRepository.deleteAll(localIds); + + if (deletedIds.isNotEmpty) { + await _localAssetRepository.delete(deletedIds); + } + } } Future deleteLocal(List localIds) async { From ab61bcfcc8c3c94aa1941f0b24352558eb05e2b6 Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Tue, 22 Jul 2025 22:28:34 +0530 Subject: [PATCH 012/748] fix: reduce asset_viewer reloads (#20083) * fix: reduce asset_viewer reloads * limit the result of watch asset to one * fix null reference in map thumbnail * bump hash file limit to 256 --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Co-authored-by: Alex --- mobile/lib/constants/constants.dart | 2 +- .../repositories/remote_asset.repository.dart | 3 +- .../archive_action_button.widget.dart | 6 ++++ .../delete_local_action_button.widget.dart | 6 ++++ ...delete_permanent_action_button.widget.dart | 6 ++++ ...e_to_lock_folder_action_button.widget.dart | 6 ++++ .../trash_action_button.widget.dart | 6 ++++ .../asset_viewer/asset_viewer.page.dart | 30 ++++++++++++++----- .../asset_viewer/asset_viewer.state.dart | 4 +++ mobile/lib/widgets/map/map_thumbnail.dart | 15 +++++----- 10 files changed, 67 insertions(+), 17 deletions(-) diff --git a/mobile/lib/constants/constants.dart b/mobile/lib/constants/constants.dart index 206fbbb28f..b54a1e9ca2 100644 --- a/mobile/lib/constants/constants.dart +++ b/mobile/lib/constants/constants.dart @@ -10,7 +10,7 @@ const int kSyncEventBatchSize = 5000; const int kFetchLocalAssetsBatchSize = 40000; // Hash batch limits -const int kBatchHashFileLimit = 128; +const int kBatchHashFileLimit = 256; const int kBatchHashSizeLimit = 1024 * 1024 * 1024; // 1GB // Secure storage keys diff --git a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart index 2c5006953f..64c602a2c7 100644 --- a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart @@ -62,7 +62,8 @@ class RemoteAssetRepository extends DriftDatabaseRepository { _db.remoteAssetEntity.id, _db.localAssetEntity.id, _db.stackEntity.primaryAssetId, - ]); + ]) + ..limit(1); return query.map((row) { final asset = row.readTable(_db.remoteAssetEntity).toDto(); diff --git a/mobile/lib/presentation/widgets/action_buttons/archive_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/archive_action_button.widget.dart index 86537816e3..061e15e58a 100644 --- a/mobile/lib/presentation/widgets/action_buttons/archive_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/archive_action_button.widget.dart @@ -2,8 +2,10 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; @@ -21,6 +23,10 @@ class ArchiveActionButton extends ConsumerWidget { final result = await ref.read(actionProvider.notifier).archive(source); ref.read(multiSelectProvider.notifier).reset(); + if (source == ActionSource.viewer) { + EventStream.shared.emit(const ViewerReloadAssetEvent()); + } + final successMessage = 'archive_action_prompt'.t( context: context, args: {'count': result.count.toString()}, diff --git a/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart index 90534ca68c..acd3738f7a 100644 --- a/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart @@ -2,8 +2,10 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; @@ -21,6 +23,10 @@ class DeleteLocalActionButton extends ConsumerWidget { final result = await ref.read(actionProvider.notifier).deleteLocal(source); ref.read(multiSelectProvider.notifier).reset(); + if (source == ActionSource.viewer) { + EventStream.shared.emit(const ViewerReloadAssetEvent()); + } + final successMessage = 'delete_local_action_prompt'.t( context: context, args: {'count': result.count.toString()}, diff --git a/mobile/lib/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart index 9f88d640dd..7840e8a41d 100644 --- a/mobile/lib/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart @@ -2,8 +2,10 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; @@ -22,6 +24,10 @@ class DeletePermanentActionButton extends ConsumerWidget { await ref.read(actionProvider.notifier).deleteRemoteAndLocal(source); ref.read(multiSelectProvider.notifier).reset(); + if (source == ActionSource.viewer) { + EventStream.shared.emit(const ViewerReloadAssetEvent()); + } + final successMessage = 'delete_action_prompt'.t( context: context, args: {'count': result.count.toString()}, diff --git a/mobile/lib/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart index 7546f07961..6b3e32a2dc 100644 --- a/mobile/lib/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart @@ -2,8 +2,10 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; @@ -22,6 +24,10 @@ class MoveToLockFolderActionButton extends ConsumerWidget { await ref.read(actionProvider.notifier).moveToLockFolder(source); ref.read(multiSelectProvider.notifier).reset(); + if (source == ActionSource.viewer) { + EventStream.shared.emit(const ViewerReloadAssetEvent()); + } + final successMessage = 'move_to_lock_folder_action_prompt'.t( context: context, args: {'count': result.count.toString()}, diff --git a/mobile/lib/presentation/widgets/action_buttons/trash_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/trash_action_button.widget.dart index 449b688550..2863c71d12 100644 --- a/mobile/lib/presentation/widgets/action_buttons/trash_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/trash_action_button.widget.dart @@ -2,8 +2,10 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; @@ -21,6 +23,10 @@ class TrashActionButton extends ConsumerWidget { final result = await ref.read(actionProvider.notifier).trash(source); ref.read(multiSelectProvider.notifier).reset(); + if (source == ActionSource.viewer) { + EventStream.shared.emit(const ViewerReloadAssetEvent()); + } + final successMessage = 'trash_action_prompt'.t( context: context, args: {'count': result.count.toString()}, diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart index 9356c2f43e..34339c8ba3 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart @@ -85,6 +85,7 @@ class _AssetViewerState extends ConsumerState { bool blockGestures = false; bool dragInProgress = false; bool shouldPopOnDrag = false; + bool assetReloadRequested = false; double? initialScale; double previousExtent = _kBottomSheetMinimumExtent; Offset dragDownPosition = Offset.zero; @@ -404,7 +405,12 @@ class _AssetViewerState extends ConsumerState { void _onEvent(Event event) { if (event is TimelineReloadEvent) { - _onTimelineReload(event); + _onTimelineReloadEvent(); + return; + } + + if (event is ViewerReloadAssetEvent) { + assetReloadRequested = true; return; } @@ -417,14 +423,22 @@ class _AssetViewerState extends ConsumerState { } } - void _onTimelineReload(_) { - setState(() { - totalAssets = ref.read(timelineServiceProvider).totalAssets; - if (totalAssets == 0) { - context.maybePop(); - return; - } + void _onTimelineReloadEvent() { + totalAssets = ref.read(timelineServiceProvider).totalAssets; + if (totalAssets == 0) { + context.maybePop(); + return; + } + if (assetReloadRequested) { + assetReloadRequested = false; + _onAssetReloadEvent(); + return; + } + } + + void _onAssetReloadEvent() { + setState(() { final index = pageController.page?.round() ?? 0; final newAsset = ref.read(timelineServiceProvider).getAsset(index); final currentAsset = ref.read(currentAssetNotifier); diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart index 825b637e8d..e57a6ff7ca 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart @@ -7,6 +7,10 @@ class ViewerOpenBottomSheetEvent extends Event { const ViewerOpenBottomSheetEvent(); } +class ViewerReloadAssetEvent extends Event { + const ViewerReloadAssetEvent(); +} + class AssetViewerState { final int backgroundOpacity; final bool showingBottomSheet; diff --git a/mobile/lib/widgets/map/map_thumbnail.dart b/mobile/lib/widgets/map/map_thumbnail.dart index 1e4b061be6..5be15f2d5f 100644 --- a/mobile/lib/widgets/map/map_thumbnail.dart +++ b/mobile/lib/widgets/map/map_thumbnail.dart @@ -113,13 +113,14 @@ class MapThumbnail extends HookConsumerWidget { ), ValueListenableBuilder( valueListenable: position, - builder: (_, value, __) => value != null - ? PositionedAssetMarkerIcon( - size: height / 2, - point: value, - assetRemoteId: assetMarkerRemoteId!, - ) - : const SizedBox.shrink(), + builder: (_, value, __) => + value != null && assetMarkerRemoteId != null + ? PositionedAssetMarkerIcon( + size: height / 2, + point: value, + assetRemoteId: assetMarkerRemoteId!, + ) + : const SizedBox.shrink(), ), ], ), From ac44f6d1e0ad026c509e8bb71fddf4457defb6fd Mon Sep 17 00:00:00 2001 From: Daimolean <92239625+wuzihao051119@users.noreply.github.com> Date: Wed, 23 Jul 2025 01:17:52 +0800 Subject: [PATCH 013/748] feat(mobile): asset face sync (#20022) * feat(mobile): asset face sync * fix: lint --------- Co-authored-by: Alex --- .../drift_schemas/main/drift_schema_v4.json | 1 + .../lib/domain/models/asset_face.model.dart | 98 + mobile/lib/domain/models/person.model.dart | 7 - .../domain/services/sync_stream.service.dart | 4 + .../entities/asset_face.entity.dart | 34 + .../entities/asset_face.entity.drift.dart | 1013 +++ .../entities/person.entity.dart | 3 - .../entities/person.entity.drift.dart | 60 +- .../repositories/asset_face.repository.dart | 33 + .../repositories/db.repository.dart | 8 +- .../repositories/db.repository.drift.dart | 31 +- .../repositories/db.repository.steps.dart | 449 ++ .../repositories/person.repository.dart | 1 - .../repositories/sync_api.repository.dart | 3 + .../repositories/sync_stream.repository.dart | 58 +- .../pages/dev/feat_in_development.page.dart | 1 + .../pages/dev/media_stat.page.dart | 4 + .../infrastructure/asset_face.provider.dart | 7 + mobile/lib/repositories/auth.repository.dart | 3 +- .../services/sync_stream_service_test.dart | 4 + mobile/test/drift/main/generated/schema.dart | 5 +- .../test/drift/main/generated/schema_v4.dart | 5524 +++++++++++++++++ 22 files changed, 7270 insertions(+), 81 deletions(-) create mode 100644 mobile/drift_schemas/main/drift_schema_v4.json create mode 100644 mobile/lib/domain/models/asset_face.model.dart create mode 100644 mobile/lib/infrastructure/entities/asset_face.entity.dart create mode 100644 mobile/lib/infrastructure/entities/asset_face.entity.drift.dart create mode 100644 mobile/lib/infrastructure/repositories/asset_face.repository.dart create mode 100644 mobile/lib/providers/infrastructure/asset_face.provider.dart create mode 100644 mobile/test/drift/main/generated/schema_v4.dart diff --git a/mobile/drift_schemas/main/drift_schema_v4.json b/mobile/drift_schemas/main/drift_schema_v4.json new file mode 100644 index 0000000000..82ef30adae --- /dev/null +++ b/mobile/drift_schemas/main/drift_schema_v4.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"profile_image_path","getter_name":"profileImagePath","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"quota_usage_in_bytes","getter_name":"quotaUsageInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[2],"type":"index","data":{"on":2,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":5,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_asset_owner_checksum","sql":null,"unique":true,"columns":["checksum","owner_id"]}},{"id":6,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":8,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":9,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":10,"references":[2,9],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":11,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":12,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[1,12],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":14,"references":[12,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":15,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":16,"references":[1,15],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":18,"references":[1,17],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}}]} \ No newline at end of file diff --git a/mobile/lib/domain/models/asset_face.model.dart b/mobile/lib/domain/models/asset_face.model.dart new file mode 100644 index 0000000000..f432b923e3 --- /dev/null +++ b/mobile/lib/domain/models/asset_face.model.dart @@ -0,0 +1,98 @@ +// Model for an asset face stored in the server +class AssetFace { + final String id; + final String assetId; + final String? personId; + final int imageWidth; + final int imageHeight; + final int boundingBoxX1; + final int boundingBoxY1; + final int boundingBoxX2; + final int boundingBoxY2; + final String sourceType; + + const AssetFace({ + required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType, + }); + + AssetFace copyWith({ + String? id, + String? assetId, + String? personId, + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType, + }) { + return AssetFace( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId ?? this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + } + + @override + String toString() { + return '''AssetFace { + id: $id, + assetId: $assetId, + personId: ${personId ?? ""}, + imageWidth: $imageWidth, + imageHeight: $imageHeight, + boundingBoxX1: $boundingBoxX1, + boundingBoxY1: $boundingBoxY1, + boundingBoxX2: $boundingBoxX2, + boundingBoxY2: $boundingBoxY2, + sourceType: $sourceType, +}'''; + } + + @override + bool operator ==(covariant AssetFace other) { + if (identical(this, other)) return true; + + return other.id == id && + other.assetId == assetId && + other.personId == personId && + other.imageWidth == imageWidth && + other.imageHeight == imageHeight && + other.boundingBoxX1 == boundingBoxX1 && + other.boundingBoxY1 == boundingBoxY1 && + other.boundingBoxX2 == boundingBoxX2 && + other.boundingBoxY2 == boundingBoxY2 && + other.sourceType == sourceType; + } + + @override + int get hashCode { + return id.hashCode ^ + assetId.hashCode ^ + personId.hashCode ^ + imageWidth.hashCode ^ + imageHeight.hashCode ^ + boundingBoxX1.hashCode ^ + boundingBoxY1.hashCode ^ + boundingBoxX2.hashCode ^ + boundingBoxY2.hashCode ^ + sourceType.hashCode; + } +} diff --git a/mobile/lib/domain/models/person.model.dart b/mobile/lib/domain/models/person.model.dart index d9eee9ae06..2a6b31cb17 100644 --- a/mobile/lib/domain/models/person.model.dart +++ b/mobile/lib/domain/models/person.model.dart @@ -103,7 +103,6 @@ class Person { final String ownerId; final String name; final String? faceAssetId; - final String thumbnailPath; final bool isFavorite; final bool isHidden; final String? color; @@ -116,7 +115,6 @@ class Person { required this.ownerId, required this.name, this.faceAssetId, - required this.thumbnailPath, required this.isFavorite, required this.isHidden, required this.color, @@ -130,7 +128,6 @@ class Person { String? ownerId, String? name, String? faceAssetId, - String? thumbnailPath, bool? isFavorite, bool? isHidden, String? color, @@ -143,7 +140,6 @@ class Person { ownerId: ownerId ?? this.ownerId, name: name ?? this.name, faceAssetId: faceAssetId ?? this.faceAssetId, - thumbnailPath: thumbnailPath ?? this.thumbnailPath, isFavorite: isFavorite ?? this.isFavorite, isHidden: isHidden ?? this.isHidden, color: color ?? this.color, @@ -160,7 +156,6 @@ class Person { ownerId: $ownerId, name: $name, faceAssetId: ${faceAssetId ?? ""}, - thumbnailPath: $thumbnailPath, isFavorite: $isFavorite, isHidden: $isHidden, color: ${color ?? ""}, @@ -178,7 +173,6 @@ class Person { other.ownerId == ownerId && other.name == name && other.faceAssetId == faceAssetId && - other.thumbnailPath == thumbnailPath && other.isFavorite == isFavorite && other.isHidden == isHidden && other.color == color && @@ -193,7 +187,6 @@ class Person { ownerId.hashCode ^ name.hashCode ^ faceAssetId.hashCode ^ - thumbnailPath.hashCode ^ isFavorite.hashCode ^ isHidden.hashCode ^ color.hashCode ^ diff --git a/mobile/lib/domain/services/sync_stream.service.dart b/mobile/lib/domain/services/sync_stream.service.dart index 9a7d91ced9..ca8295fc8f 100644 --- a/mobile/lib/domain/services/sync_stream.service.dart +++ b/mobile/lib/domain/services/sync_stream.service.dart @@ -244,6 +244,10 @@ class SyncStreamService { return _syncStreamRepository.updatePeopleV1(data.cast()); case SyncEntityType.personDeleteV1: return _syncStreamRepository.deletePeopleV1(data.cast()); + case SyncEntityType.assetFaceV1: + return _syncStreamRepository.updateAssetFacesV1(data.cast()); + case SyncEntityType.assetFaceDeleteV1: + return _syncStreamRepository.deleteAssetFacesV1(data.cast()); default: _logger.warning("Unknown sync data type: $type"); } diff --git a/mobile/lib/infrastructure/entities/asset_face.entity.dart b/mobile/lib/infrastructure/entities/asset_face.entity.dart new file mode 100644 index 0000000000..c54e4e1848 --- /dev/null +++ b/mobile/lib/infrastructure/entities/asset_face.entity.dart @@ -0,0 +1,34 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/infrastructure/entities/person.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart'; +import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; + +class AssetFaceEntity extends Table with DriftDefaultsMixin { + const AssetFaceEntity(); + + TextColumn get id => text()(); + + TextColumn get assetId => + text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)(); + + TextColumn get personId => text() + .nullable() + .references(PersonEntity, #id, onDelete: KeyAction.setNull)(); + + IntColumn get imageWidth => integer()(); + + IntColumn get imageHeight => integer()(); + + IntColumn get boundingBoxX1 => integer()(); + + IntColumn get boundingBoxY1 => integer()(); + + IntColumn get boundingBoxX2 => integer()(); + + IntColumn get boundingBoxY2 => integer()(); + + TextColumn get sourceType => text()(); + + @override + Set get primaryKey => {id}; +} diff --git a/mobile/lib/infrastructure/entities/asset_face.entity.drift.dart b/mobile/lib/infrastructure/entities/asset_face.entity.drift.dart new file mode 100644 index 0000000000..140af60de1 --- /dev/null +++ b/mobile/lib/infrastructure/entities/asset_face.entity.drift.dart @@ -0,0 +1,1013 @@ +// dart format width=80 +// ignore_for_file: type=lint +import 'package:drift/drift.dart' as i0; +import 'package:immich_mobile/infrastructure/entities/asset_face.entity.drift.dart' + as i1; +import 'package:immich_mobile/infrastructure/entities/asset_face.entity.dart' + as i2; +import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' + as i3; +import 'package:drift/internal/modular.dart' as i4; +import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart' + as i5; + +typedef $$AssetFaceEntityTableCreateCompanionBuilder + = i1.AssetFaceEntityCompanion Function({ + required String id, + required String assetId, + i0.Value personId, + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, +}); +typedef $$AssetFaceEntityTableUpdateCompanionBuilder + = i1.AssetFaceEntityCompanion Function({ + i0.Value id, + i0.Value assetId, + i0.Value personId, + i0.Value imageWidth, + i0.Value imageHeight, + i0.Value boundingBoxX1, + i0.Value boundingBoxY1, + i0.Value boundingBoxX2, + i0.Value boundingBoxY2, + i0.Value sourceType, +}); + +final class $$AssetFaceEntityTableReferences extends i0.BaseReferences< + i0.GeneratedDatabase, i1.$AssetFaceEntityTable, i1.AssetFaceEntityData> { + $$AssetFaceEntityTableReferences( + super.$_db, super.$_table, super.$_typedResult); + + static i3.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) => + i4.ReadDatabaseContainer(db) + .resultSet('remote_asset_entity') + .createAlias(i0.$_aliasNameGenerator( + i4.ReadDatabaseContainer(db) + .resultSet('asset_face_entity') + .assetId, + i4.ReadDatabaseContainer(db) + .resultSet('remote_asset_entity') + .id)); + + i3.$$RemoteAssetEntityTableProcessedTableManager get assetId { + final $_column = $_itemColumn('asset_id')!; + + final manager = i3 + .$$RemoteAssetEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer($_db) + .resultSet('remote_asset_entity')) + .filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_assetIdTable($_db)); + if (item == null) return manager; + return i0.ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item])); + } + + static i5.$PersonEntityTable _personIdTable(i0.GeneratedDatabase db) => + i4.ReadDatabaseContainer(db) + .resultSet('person_entity') + .createAlias(i0.$_aliasNameGenerator( + i4.ReadDatabaseContainer(db) + .resultSet('asset_face_entity') + .personId, + i4.ReadDatabaseContainer(db) + .resultSet('person_entity') + .id)); + + i5.$$PersonEntityTableProcessedTableManager? get personId { + final $_column = $_itemColumn('person_id'); + if ($_column == null) return null; + final manager = i5 + .$$PersonEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer($_db) + .resultSet('person_entity')) + .filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_personIdTable($_db)); + if (item == null) return manager; + return i0.ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item])); + } +} + +class $$AssetFaceEntityTableFilterComposer + extends i0.Composer { + $$AssetFaceEntityTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnFilters get id => $composableBuilder( + column: $table.id, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get imageWidth => $composableBuilder( + column: $table.imageWidth, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get imageHeight => $composableBuilder( + column: $table.imageHeight, + builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get boundingBoxX1 => $composableBuilder( + column: $table.boundingBoxX1, + builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get boundingBoxY1 => $composableBuilder( + column: $table.boundingBoxY1, + builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get boundingBoxX2 => $composableBuilder( + column: $table.boundingBoxX2, + builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get boundingBoxY2 => $composableBuilder( + column: $table.boundingBoxY2, + builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get sourceType => $composableBuilder( + column: $table.sourceType, builder: (column) => i0.ColumnFilters(column)); + + i3.$$RemoteAssetEntityTableFilterComposer get assetId { + final i3.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer($db) + .resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i3.$$RemoteAssetEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer($db) + .resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } + + i5.$$PersonEntityTableFilterComposer get personId { + final i5.$$PersonEntityTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.personId, + referencedTable: i4.ReadDatabaseContainer($db) + .resultSet('person_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i5.$$PersonEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer($db) + .resultSet('person_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$AssetFaceEntityTableOrderingComposer + extends i0.Composer { + $$AssetFaceEntityTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnOrderings get id => $composableBuilder( + column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get imageWidth => $composableBuilder( + column: $table.imageWidth, + builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get imageHeight => $composableBuilder( + column: $table.imageHeight, + builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get boundingBoxX1 => $composableBuilder( + column: $table.boundingBoxX1, + builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get boundingBoxY1 => $composableBuilder( + column: $table.boundingBoxY1, + builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get boundingBoxX2 => $composableBuilder( + column: $table.boundingBoxX2, + builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get boundingBoxY2 => $composableBuilder( + column: $table.boundingBoxY2, + builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get sourceType => $composableBuilder( + column: $table.sourceType, + builder: (column) => i0.ColumnOrderings(column)); + + i3.$$RemoteAssetEntityTableOrderingComposer get assetId { + final i3.$$RemoteAssetEntityTableOrderingComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer($db) + .resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i3.$$RemoteAssetEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer($db) + .resultSet( + 'remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } + + i5.$$PersonEntityTableOrderingComposer get personId { + final i5.$$PersonEntityTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.personId, + referencedTable: i4.ReadDatabaseContainer($db) + .resultSet('person_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i5.$$PersonEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer($db) + .resultSet('person_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$AssetFaceEntityTableAnnotationComposer + extends i0.Composer { + $$AssetFaceEntityTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + i0.GeneratedColumn get imageWidth => $composableBuilder( + column: $table.imageWidth, builder: (column) => column); + + i0.GeneratedColumn get imageHeight => $composableBuilder( + column: $table.imageHeight, builder: (column) => column); + + i0.GeneratedColumn get boundingBoxX1 => $composableBuilder( + column: $table.boundingBoxX1, builder: (column) => column); + + i0.GeneratedColumn get boundingBoxY1 => $composableBuilder( + column: $table.boundingBoxY1, builder: (column) => column); + + i0.GeneratedColumn get boundingBoxX2 => $composableBuilder( + column: $table.boundingBoxX2, builder: (column) => column); + + i0.GeneratedColumn get boundingBoxY2 => $composableBuilder( + column: $table.boundingBoxY2, builder: (column) => column); + + i0.GeneratedColumn get sourceType => $composableBuilder( + column: $table.sourceType, builder: (column) => column); + + i3.$$RemoteAssetEntityTableAnnotationComposer get assetId { + final i3.$$RemoteAssetEntityTableAnnotationComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer($db) + .resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i3.$$RemoteAssetEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer($db) + .resultSet( + 'remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } + + i5.$$PersonEntityTableAnnotationComposer get personId { + final i5.$$PersonEntityTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.personId, + referencedTable: i4.ReadDatabaseContainer($db) + .resultSet('person_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i5.$$PersonEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer($db) + .resultSet('person_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$AssetFaceEntityTableTableManager extends i0.RootTableManager< + i0.GeneratedDatabase, + i1.$AssetFaceEntityTable, + i1.AssetFaceEntityData, + i1.$$AssetFaceEntityTableFilterComposer, + i1.$$AssetFaceEntityTableOrderingComposer, + i1.$$AssetFaceEntityTableAnnotationComposer, + $$AssetFaceEntityTableCreateCompanionBuilder, + $$AssetFaceEntityTableUpdateCompanionBuilder, + (i1.AssetFaceEntityData, i1.$$AssetFaceEntityTableReferences), + i1.AssetFaceEntityData, + i0.PrefetchHooks Function({bool assetId, bool personId})> { + $$AssetFaceEntityTableTableManager( + i0.GeneratedDatabase db, i1.$AssetFaceEntityTable table) + : super(i0.TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + i1.$$AssetFaceEntityTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + i1.$$AssetFaceEntityTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => i1 + .$$AssetFaceEntityTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: ({ + i0.Value id = const i0.Value.absent(), + i0.Value assetId = const i0.Value.absent(), + i0.Value personId = const i0.Value.absent(), + i0.Value imageWidth = const i0.Value.absent(), + i0.Value imageHeight = const i0.Value.absent(), + i0.Value boundingBoxX1 = const i0.Value.absent(), + i0.Value boundingBoxY1 = const i0.Value.absent(), + i0.Value boundingBoxX2 = const i0.Value.absent(), + i0.Value boundingBoxY2 = const i0.Value.absent(), + i0.Value sourceType = const i0.Value.absent(), + }) => + i1.AssetFaceEntityCompanion( + id: id, + assetId: assetId, + personId: personId, + imageWidth: imageWidth, + imageHeight: imageHeight, + boundingBoxX1: boundingBoxX1, + boundingBoxY1: boundingBoxY1, + boundingBoxX2: boundingBoxX2, + boundingBoxY2: boundingBoxY2, + sourceType: sourceType, + ), + createCompanionCallback: ({ + required String id, + required String assetId, + i0.Value personId = const i0.Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) => + i1.AssetFaceEntityCompanion.insert( + id: id, + assetId: assetId, + personId: personId, + imageWidth: imageWidth, + imageHeight: imageHeight, + boundingBoxX1: boundingBoxX1, + boundingBoxY1: boundingBoxY1, + boundingBoxX2: boundingBoxX2, + boundingBoxY2: boundingBoxY2, + sourceType: sourceType, + ), + withReferenceMapper: (p0) => p0 + .map((e) => ( + e.readTable(table), + i1.$$AssetFaceEntityTableReferences(db, table, e) + )) + .toList(), + prefetchHooksCallback: ({assetId = false, personId = false}) { + return i0.PrefetchHooks( + db: db, + explicitlyWatchedTables: [], + addJoins: < + T extends i0.TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic>>(state) { + if (assetId) { + state = state.withJoin( + currentTable: table, + currentColumn: table.assetId, + referencedTable: + i1.$$AssetFaceEntityTableReferences._assetIdTable(db), + referencedColumn: i1.$$AssetFaceEntityTableReferences + ._assetIdTable(db) + .id, + ) as T; + } + if (personId) { + state = state.withJoin( + currentTable: table, + currentColumn: table.personId, + referencedTable: + i1.$$AssetFaceEntityTableReferences._personIdTable(db), + referencedColumn: i1.$$AssetFaceEntityTableReferences + ._personIdTable(db) + .id, + ) as T; + } + + return state; + }, + getPrefetchedDataCallback: (items) async { + return []; + }, + ); + }, + )); +} + +typedef $$AssetFaceEntityTableProcessedTableManager = i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$AssetFaceEntityTable, + i1.AssetFaceEntityData, + i1.$$AssetFaceEntityTableFilterComposer, + i1.$$AssetFaceEntityTableOrderingComposer, + i1.$$AssetFaceEntityTableAnnotationComposer, + $$AssetFaceEntityTableCreateCompanionBuilder, + $$AssetFaceEntityTableUpdateCompanionBuilder, + (i1.AssetFaceEntityData, i1.$$AssetFaceEntityTableReferences), + i1.AssetFaceEntityData, + i0.PrefetchHooks Function({bool assetId, bool personId})>; + +class $AssetFaceEntityTable extends i2.AssetFaceEntity + with i0.TableInfo<$AssetFaceEntityTable, i1.AssetFaceEntityData> { + @override + final i0.GeneratedDatabase attachedDatabase; + final String? _alias; + $AssetFaceEntityTable(this.attachedDatabase, [this._alias]); + static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); + @override + late final i0.GeneratedColumn id = i0.GeneratedColumn( + 'id', aliasedName, false, + type: i0.DriftSqlType.string, requiredDuringInsert: true); + static const i0.VerificationMeta _assetIdMeta = + const i0.VerificationMeta('assetId'); + @override + late final i0.GeneratedColumn assetId = i0.GeneratedColumn( + 'asset_id', aliasedName, false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + static const i0.VerificationMeta _personIdMeta = + const i0.VerificationMeta('personId'); + @override + late final i0.GeneratedColumn personId = i0.GeneratedColumn( + 'person_id', aliasedName, true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL')); + static const i0.VerificationMeta _imageWidthMeta = + const i0.VerificationMeta('imageWidth'); + @override + late final i0.GeneratedColumn imageWidth = i0.GeneratedColumn( + 'image_width', aliasedName, false, + type: i0.DriftSqlType.int, requiredDuringInsert: true); + static const i0.VerificationMeta _imageHeightMeta = + const i0.VerificationMeta('imageHeight'); + @override + late final i0.GeneratedColumn imageHeight = i0.GeneratedColumn( + 'image_height', aliasedName, false, + type: i0.DriftSqlType.int, requiredDuringInsert: true); + static const i0.VerificationMeta _boundingBoxX1Meta = + const i0.VerificationMeta('boundingBoxX1'); + @override + late final i0.GeneratedColumn boundingBoxX1 = i0.GeneratedColumn( + 'bounding_box_x1', aliasedName, false, + type: i0.DriftSqlType.int, requiredDuringInsert: true); + static const i0.VerificationMeta _boundingBoxY1Meta = + const i0.VerificationMeta('boundingBoxY1'); + @override + late final i0.GeneratedColumn boundingBoxY1 = i0.GeneratedColumn( + 'bounding_box_y1', aliasedName, false, + type: i0.DriftSqlType.int, requiredDuringInsert: true); + static const i0.VerificationMeta _boundingBoxX2Meta = + const i0.VerificationMeta('boundingBoxX2'); + @override + late final i0.GeneratedColumn boundingBoxX2 = i0.GeneratedColumn( + 'bounding_box_x2', aliasedName, false, + type: i0.DriftSqlType.int, requiredDuringInsert: true); + static const i0.VerificationMeta _boundingBoxY2Meta = + const i0.VerificationMeta('boundingBoxY2'); + @override + late final i0.GeneratedColumn boundingBoxY2 = i0.GeneratedColumn( + 'bounding_box_y2', aliasedName, false, + type: i0.DriftSqlType.int, requiredDuringInsert: true); + static const i0.VerificationMeta _sourceTypeMeta = + const i0.VerificationMeta('sourceType'); + @override + late final i0.GeneratedColumn sourceType = i0.GeneratedColumn( + 'source_type', aliasedName, false, + type: i0.DriftSqlType.string, requiredDuringInsert: true); + @override + List get $columns => [ + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'asset_face_entity'; + @override + i0.VerificationContext validateIntegrity( + i0.Insertable instance, + {bool isInserting = false}) { + final context = i0.VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } else if (isInserting) { + context.missing(_idMeta); + } + if (data.containsKey('asset_id')) { + context.handle(_assetIdMeta, + assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta)); + } else if (isInserting) { + context.missing(_assetIdMeta); + } + if (data.containsKey('person_id')) { + context.handle(_personIdMeta, + personId.isAcceptableOrUnknown(data['person_id']!, _personIdMeta)); + } + if (data.containsKey('image_width')) { + context.handle( + _imageWidthMeta, + imageWidth.isAcceptableOrUnknown( + data['image_width']!, _imageWidthMeta)); + } else if (isInserting) { + context.missing(_imageWidthMeta); + } + if (data.containsKey('image_height')) { + context.handle( + _imageHeightMeta, + imageHeight.isAcceptableOrUnknown( + data['image_height']!, _imageHeightMeta)); + } else if (isInserting) { + context.missing(_imageHeightMeta); + } + if (data.containsKey('bounding_box_x1')) { + context.handle( + _boundingBoxX1Meta, + boundingBoxX1.isAcceptableOrUnknown( + data['bounding_box_x1']!, _boundingBoxX1Meta)); + } else if (isInserting) { + context.missing(_boundingBoxX1Meta); + } + if (data.containsKey('bounding_box_y1')) { + context.handle( + _boundingBoxY1Meta, + boundingBoxY1.isAcceptableOrUnknown( + data['bounding_box_y1']!, _boundingBoxY1Meta)); + } else if (isInserting) { + context.missing(_boundingBoxY1Meta); + } + if (data.containsKey('bounding_box_x2')) { + context.handle( + _boundingBoxX2Meta, + boundingBoxX2.isAcceptableOrUnknown( + data['bounding_box_x2']!, _boundingBoxX2Meta)); + } else if (isInserting) { + context.missing(_boundingBoxX2Meta); + } + if (data.containsKey('bounding_box_y2')) { + context.handle( + _boundingBoxY2Meta, + boundingBoxY2.isAcceptableOrUnknown( + data['bounding_box_y2']!, _boundingBoxY2Meta)); + } else if (isInserting) { + context.missing(_boundingBoxY2Meta); + } + if (data.containsKey('source_type')) { + context.handle( + _sourceTypeMeta, + sourceType.isAcceptableOrUnknown( + data['source_type']!, _sourceTypeMeta)); + } else if (isInserting) { + context.missing(_sourceTypeMeta); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + i1.AssetFaceEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return i1.AssetFaceEntityData( + id: attachedDatabase.typeMapping + .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, + assetId: attachedDatabase.typeMapping + .read(i0.DriftSqlType.string, data['${effectivePrefix}asset_id'])!, + personId: attachedDatabase.typeMapping + .read(i0.DriftSqlType.string, data['${effectivePrefix}person_id']), + imageWidth: attachedDatabase.typeMapping + .read(i0.DriftSqlType.int, data['${effectivePrefix}image_width'])!, + imageHeight: attachedDatabase.typeMapping + .read(i0.DriftSqlType.int, data['${effectivePrefix}image_height'])!, + boundingBoxX1: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, data['${effectivePrefix}bounding_box_x1'])!, + boundingBoxY1: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, data['${effectivePrefix}bounding_box_y1'])!, + boundingBoxX2: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, data['${effectivePrefix}bounding_box_x2'])!, + boundingBoxY2: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, data['${effectivePrefix}bounding_box_y2'])!, + sourceType: attachedDatabase.typeMapping + .read(i0.DriftSqlType.string, data['${effectivePrefix}source_type'])!, + ); + } + + @override + $AssetFaceEntityTable createAlias(String alias) { + return $AssetFaceEntityTable(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AssetFaceEntityData extends i0.DataClass + implements i0.Insertable { + final String id; + final String assetId; + final String? personId; + final int imageWidth; + final int imageHeight; + final int boundingBoxX1; + final int boundingBoxY1; + final int boundingBoxX2; + final int boundingBoxY2; + final String sourceType; + const AssetFaceEntityData( + {required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = i0.Variable(id); + map['asset_id'] = i0.Variable(assetId); + if (!nullToAbsent || personId != null) { + map['person_id'] = i0.Variable(personId); + } + map['image_width'] = i0.Variable(imageWidth); + map['image_height'] = i0.Variable(imageHeight); + map['bounding_box_x1'] = i0.Variable(boundingBoxX1); + map['bounding_box_y1'] = i0.Variable(boundingBoxY1); + map['bounding_box_x2'] = i0.Variable(boundingBoxX2); + map['bounding_box_y2'] = i0.Variable(boundingBoxY2); + map['source_type'] = i0.Variable(sourceType); + return map; + } + + factory AssetFaceEntityData.fromJson(Map json, + {i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return AssetFaceEntityData( + id: serializer.fromJson(json['id']), + assetId: serializer.fromJson(json['assetId']), + personId: serializer.fromJson(json['personId']), + imageWidth: serializer.fromJson(json['imageWidth']), + imageHeight: serializer.fromJson(json['imageHeight']), + boundingBoxX1: serializer.fromJson(json['boundingBoxX1']), + boundingBoxY1: serializer.fromJson(json['boundingBoxY1']), + boundingBoxX2: serializer.fromJson(json['boundingBoxX2']), + boundingBoxY2: serializer.fromJson(json['boundingBoxY2']), + sourceType: serializer.fromJson(json['sourceType']), + ); + } + @override + Map toJson({i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'assetId': serializer.toJson(assetId), + 'personId': serializer.toJson(personId), + 'imageWidth': serializer.toJson(imageWidth), + 'imageHeight': serializer.toJson(imageHeight), + 'boundingBoxX1': serializer.toJson(boundingBoxX1), + 'boundingBoxY1': serializer.toJson(boundingBoxY1), + 'boundingBoxX2': serializer.toJson(boundingBoxX2), + 'boundingBoxY2': serializer.toJson(boundingBoxY2), + 'sourceType': serializer.toJson(sourceType), + }; + } + + i1.AssetFaceEntityData copyWith( + {String? id, + String? assetId, + i0.Value personId = const i0.Value.absent(), + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType}) => + i1.AssetFaceEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId.present ? personId.value : this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + AssetFaceEntityData copyWithCompanion(i1.AssetFaceEntityCompanion data) { + return AssetFaceEntityData( + id: data.id.present ? data.id.value : this.id, + assetId: data.assetId.present ? data.assetId.value : this.assetId, + personId: data.personId.present ? data.personId.value : this.personId, + imageWidth: + data.imageWidth.present ? data.imageWidth.value : this.imageWidth, + imageHeight: + data.imageHeight.present ? data.imageHeight.value : this.imageHeight, + boundingBoxX1: data.boundingBoxX1.present + ? data.boundingBoxX1.value + : this.boundingBoxX1, + boundingBoxY1: data.boundingBoxY1.present + ? data.boundingBoxY1.value + : this.boundingBoxY1, + boundingBoxX2: data.boundingBoxX2.present + ? data.boundingBoxX2.value + : this.boundingBoxX2, + boundingBoxY2: data.boundingBoxY2.present + ? data.boundingBoxY2.value + : this.boundingBoxY2, + sourceType: + data.sourceType.present ? data.sourceType.value : this.sourceType, + ); + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityData(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is i1.AssetFaceEntityData && + other.id == this.id && + other.assetId == this.assetId && + other.personId == this.personId && + other.imageWidth == this.imageWidth && + other.imageHeight == this.imageHeight && + other.boundingBoxX1 == this.boundingBoxX1 && + other.boundingBoxY1 == this.boundingBoxY1 && + other.boundingBoxX2 == this.boundingBoxX2 && + other.boundingBoxY2 == this.boundingBoxY2 && + other.sourceType == this.sourceType); +} + +class AssetFaceEntityCompanion + extends i0.UpdateCompanion { + final i0.Value id; + final i0.Value assetId; + final i0.Value personId; + final i0.Value imageWidth; + final i0.Value imageHeight; + final i0.Value boundingBoxX1; + final i0.Value boundingBoxY1; + final i0.Value boundingBoxX2; + final i0.Value boundingBoxY2; + final i0.Value sourceType; + const AssetFaceEntityCompanion({ + this.id = const i0.Value.absent(), + this.assetId = const i0.Value.absent(), + this.personId = const i0.Value.absent(), + this.imageWidth = const i0.Value.absent(), + this.imageHeight = const i0.Value.absent(), + this.boundingBoxX1 = const i0.Value.absent(), + this.boundingBoxY1 = const i0.Value.absent(), + this.boundingBoxX2 = const i0.Value.absent(), + this.boundingBoxY2 = const i0.Value.absent(), + this.sourceType = const i0.Value.absent(), + }); + AssetFaceEntityCompanion.insert({ + required String id, + required String assetId, + this.personId = const i0.Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) : id = i0.Value(id), + assetId = i0.Value(assetId), + imageWidth = i0.Value(imageWidth), + imageHeight = i0.Value(imageHeight), + boundingBoxX1 = i0.Value(boundingBoxX1), + boundingBoxY1 = i0.Value(boundingBoxY1), + boundingBoxX2 = i0.Value(boundingBoxX2), + boundingBoxY2 = i0.Value(boundingBoxY2), + sourceType = i0.Value(sourceType); + static i0.Insertable custom({ + i0.Expression? id, + i0.Expression? assetId, + i0.Expression? personId, + i0.Expression? imageWidth, + i0.Expression? imageHeight, + i0.Expression? boundingBoxX1, + i0.Expression? boundingBoxY1, + i0.Expression? boundingBoxX2, + i0.Expression? boundingBoxY2, + i0.Expression? sourceType, + }) { + return i0.RawValuesInsertable({ + if (id != null) 'id': id, + if (assetId != null) 'asset_id': assetId, + if (personId != null) 'person_id': personId, + if (imageWidth != null) 'image_width': imageWidth, + if (imageHeight != null) 'image_height': imageHeight, + if (boundingBoxX1 != null) 'bounding_box_x1': boundingBoxX1, + if (boundingBoxY1 != null) 'bounding_box_y1': boundingBoxY1, + if (boundingBoxX2 != null) 'bounding_box_x2': boundingBoxX2, + if (boundingBoxY2 != null) 'bounding_box_y2': boundingBoxY2, + if (sourceType != null) 'source_type': sourceType, + }); + } + + i1.AssetFaceEntityCompanion copyWith( + {i0.Value? id, + i0.Value? assetId, + i0.Value? personId, + i0.Value? imageWidth, + i0.Value? imageHeight, + i0.Value? boundingBoxX1, + i0.Value? boundingBoxY1, + i0.Value? boundingBoxX2, + i0.Value? boundingBoxY2, + i0.Value? sourceType}) { + return i1.AssetFaceEntityCompanion( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId ?? this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = i0.Variable(id.value); + } + if (assetId.present) { + map['asset_id'] = i0.Variable(assetId.value); + } + if (personId.present) { + map['person_id'] = i0.Variable(personId.value); + } + if (imageWidth.present) { + map['image_width'] = i0.Variable(imageWidth.value); + } + if (imageHeight.present) { + map['image_height'] = i0.Variable(imageHeight.value); + } + if (boundingBoxX1.present) { + map['bounding_box_x1'] = i0.Variable(boundingBoxX1.value); + } + if (boundingBoxY1.present) { + map['bounding_box_y1'] = i0.Variable(boundingBoxY1.value); + } + if (boundingBoxX2.present) { + map['bounding_box_x2'] = i0.Variable(boundingBoxX2.value); + } + if (boundingBoxY2.present) { + map['bounding_box_y2'] = i0.Variable(boundingBoxY2.value); + } + if (sourceType.present) { + map['source_type'] = i0.Variable(sourceType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityCompanion(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } +} diff --git a/mobile/lib/infrastructure/entities/person.entity.dart b/mobile/lib/infrastructure/entities/person.entity.dart index 68dd04cb5f..75543baca3 100644 --- a/mobile/lib/infrastructure/entities/person.entity.dart +++ b/mobile/lib/infrastructure/entities/person.entity.dart @@ -16,11 +16,8 @@ class PersonEntity extends Table with DriftDefaultsMixin { TextColumn get name => text()(); - // TODO: foreign key refering to asset faces TextColumn get faceAssetId => text().nullable()(); - TextColumn get thumbnailPath => text()(); - BoolColumn get isFavorite => boolean()(); BoolColumn get isHidden => boolean()(); diff --git a/mobile/lib/infrastructure/entities/person.entity.drift.dart b/mobile/lib/infrastructure/entities/person.entity.drift.dart index f0ced63f0e..70639adc2f 100644 --- a/mobile/lib/infrastructure/entities/person.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/person.entity.drift.dart @@ -17,7 +17,6 @@ typedef $$PersonEntityTableCreateCompanionBuilder = i1.PersonEntityCompanion required String ownerId, required String name, i0.Value faceAssetId, - required String thumbnailPath, required bool isFavorite, required bool isHidden, i0.Value color, @@ -31,7 +30,6 @@ typedef $$PersonEntityTableUpdateCompanionBuilder = i1.PersonEntityCompanion i0.Value ownerId, i0.Value name, i0.Value faceAssetId, - i0.Value thumbnailPath, i0.Value isFavorite, i0.Value isHidden, i0.Value color, @@ -94,10 +92,6 @@ class $$PersonEntityTableFilterComposer column: $table.faceAssetId, builder: (column) => i0.ColumnFilters(column)); - i0.ColumnFilters get thumbnailPath => $composableBuilder( - column: $table.thumbnailPath, - builder: (column) => i0.ColumnFilters(column)); - i0.ColumnFilters get isFavorite => $composableBuilder( column: $table.isFavorite, builder: (column) => i0.ColumnFilters(column)); @@ -160,10 +154,6 @@ class $$PersonEntityTableOrderingComposer column: $table.faceAssetId, builder: (column) => i0.ColumnOrderings(column)); - i0.ColumnOrderings get thumbnailPath => $composableBuilder( - column: $table.thumbnailPath, - builder: (column) => i0.ColumnOrderings(column)); - i0.ColumnOrderings get isFavorite => $composableBuilder( column: $table.isFavorite, builder: (column) => i0.ColumnOrderings(column)); @@ -225,9 +215,6 @@ class $$PersonEntityTableAnnotationComposer i0.GeneratedColumn get faceAssetId => $composableBuilder( column: $table.faceAssetId, builder: (column) => column); - i0.GeneratedColumn get thumbnailPath => $composableBuilder( - column: $table.thumbnailPath, builder: (column) => column); - i0.GeneratedColumn get isFavorite => $composableBuilder( column: $table.isFavorite, builder: (column) => column); @@ -293,7 +280,6 @@ class $$PersonEntityTableTableManager extends i0.RootTableManager< i0.Value ownerId = const i0.Value.absent(), i0.Value name = const i0.Value.absent(), i0.Value faceAssetId = const i0.Value.absent(), - i0.Value thumbnailPath = const i0.Value.absent(), i0.Value isFavorite = const i0.Value.absent(), i0.Value isHidden = const i0.Value.absent(), i0.Value color = const i0.Value.absent(), @@ -306,7 +292,6 @@ class $$PersonEntityTableTableManager extends i0.RootTableManager< ownerId: ownerId, name: name, faceAssetId: faceAssetId, - thumbnailPath: thumbnailPath, isFavorite: isFavorite, isHidden: isHidden, color: color, @@ -319,7 +304,6 @@ class $$PersonEntityTableTableManager extends i0.RootTableManager< required String ownerId, required String name, i0.Value faceAssetId = const i0.Value.absent(), - required String thumbnailPath, required bool isFavorite, required bool isHidden, i0.Value color = const i0.Value.absent(), @@ -332,7 +316,6 @@ class $$PersonEntityTableTableManager extends i0.RootTableManager< ownerId: ownerId, name: name, faceAssetId: faceAssetId, - thumbnailPath: thumbnailPath, isFavorite: isFavorite, isHidden: isHidden, color: color, @@ -443,12 +426,6 @@ class $PersonEntityTable extends i2.PersonEntity late final i0.GeneratedColumn faceAssetId = i0.GeneratedColumn('face_asset_id', aliasedName, true, type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _thumbnailPathMeta = - const i0.VerificationMeta('thumbnailPath'); - @override - late final i0.GeneratedColumn thumbnailPath = - i0.GeneratedColumn('thumbnail_path', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); static const i0.VerificationMeta _isFavoriteMeta = const i0.VerificationMeta('isFavorite'); @override @@ -487,7 +464,6 @@ class $PersonEntityTable extends i2.PersonEntity ownerId, name, faceAssetId, - thumbnailPath, isFavorite, isHidden, color, @@ -535,14 +511,6 @@ class $PersonEntityTable extends i2.PersonEntity faceAssetId.isAcceptableOrUnknown( data['face_asset_id']!, _faceAssetIdMeta)); } - if (data.containsKey('thumbnail_path')) { - context.handle( - _thumbnailPathMeta, - thumbnailPath.isAcceptableOrUnknown( - data['thumbnail_path']!, _thumbnailPathMeta)); - } else if (isInserting) { - context.missing(_thumbnailPathMeta); - } if (data.containsKey('is_favorite')) { context.handle( _isFavoriteMeta, @@ -586,8 +554,6 @@ class $PersonEntityTable extends i2.PersonEntity .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, faceAssetId: attachedDatabase.typeMapping.read( i0.DriftSqlType.string, data['${effectivePrefix}face_asset_id']), - thumbnailPath: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}thumbnail_path'])!, isFavorite: attachedDatabase.typeMapping .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, isHidden: attachedDatabase.typeMapping @@ -618,7 +584,6 @@ class PersonEntityData extends i0.DataClass final String ownerId; final String name; final String? faceAssetId; - final String thumbnailPath; final bool isFavorite; final bool isHidden; final String? color; @@ -630,7 +595,6 @@ class PersonEntityData extends i0.DataClass required this.ownerId, required this.name, this.faceAssetId, - required this.thumbnailPath, required this.isFavorite, required this.isHidden, this.color, @@ -646,7 +610,6 @@ class PersonEntityData extends i0.DataClass if (!nullToAbsent || faceAssetId != null) { map['face_asset_id'] = i0.Variable(faceAssetId); } - map['thumbnail_path'] = i0.Variable(thumbnailPath); map['is_favorite'] = i0.Variable(isFavorite); map['is_hidden'] = i0.Variable(isHidden); if (!nullToAbsent || color != null) { @@ -668,7 +631,6 @@ class PersonEntityData extends i0.DataClass ownerId: serializer.fromJson(json['ownerId']), name: serializer.fromJson(json['name']), faceAssetId: serializer.fromJson(json['faceAssetId']), - thumbnailPath: serializer.fromJson(json['thumbnailPath']), isFavorite: serializer.fromJson(json['isFavorite']), isHidden: serializer.fromJson(json['isHidden']), color: serializer.fromJson(json['color']), @@ -685,7 +647,6 @@ class PersonEntityData extends i0.DataClass 'ownerId': serializer.toJson(ownerId), 'name': serializer.toJson(name), 'faceAssetId': serializer.toJson(faceAssetId), - 'thumbnailPath': serializer.toJson(thumbnailPath), 'isFavorite': serializer.toJson(isFavorite), 'isHidden': serializer.toJson(isHidden), 'color': serializer.toJson(color), @@ -700,7 +661,6 @@ class PersonEntityData extends i0.DataClass String? ownerId, String? name, i0.Value faceAssetId = const i0.Value.absent(), - String? thumbnailPath, bool? isFavorite, bool? isHidden, i0.Value color = const i0.Value.absent(), @@ -712,7 +672,6 @@ class PersonEntityData extends i0.DataClass ownerId: ownerId ?? this.ownerId, name: name ?? this.name, faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, - thumbnailPath: thumbnailPath ?? this.thumbnailPath, isFavorite: isFavorite ?? this.isFavorite, isHidden: isHidden ?? this.isHidden, color: color.present ? color.value : this.color, @@ -727,9 +686,6 @@ class PersonEntityData extends i0.DataClass name: data.name.present ? data.name.value : this.name, faceAssetId: data.faceAssetId.present ? data.faceAssetId.value : this.faceAssetId, - thumbnailPath: data.thumbnailPath.present - ? data.thumbnailPath.value - : this.thumbnailPath, isFavorite: data.isFavorite.present ? data.isFavorite.value : this.isFavorite, isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, @@ -747,7 +703,6 @@ class PersonEntityData extends i0.DataClass ..write('ownerId: $ownerId, ') ..write('name: $name, ') ..write('faceAssetId: $faceAssetId, ') - ..write('thumbnailPath: $thumbnailPath, ') ..write('isFavorite: $isFavorite, ') ..write('isHidden: $isHidden, ') ..write('color: $color, ') @@ -758,7 +713,7 @@ class PersonEntityData extends i0.DataClass @override int get hashCode => Object.hash(id, createdAt, updatedAt, ownerId, name, - faceAssetId, thumbnailPath, isFavorite, isHidden, color, birthDate); + faceAssetId, isFavorite, isHidden, color, birthDate); @override bool operator ==(Object other) => identical(this, other) || @@ -769,7 +724,6 @@ class PersonEntityData extends i0.DataClass other.ownerId == this.ownerId && other.name == this.name && other.faceAssetId == this.faceAssetId && - other.thumbnailPath == this.thumbnailPath && other.isFavorite == this.isFavorite && other.isHidden == this.isHidden && other.color == this.color && @@ -783,7 +737,6 @@ class PersonEntityCompanion extends i0.UpdateCompanion { final i0.Value ownerId; final i0.Value name; final i0.Value faceAssetId; - final i0.Value thumbnailPath; final i0.Value isFavorite; final i0.Value isHidden; final i0.Value color; @@ -795,7 +748,6 @@ class PersonEntityCompanion extends i0.UpdateCompanion { this.ownerId = const i0.Value.absent(), this.name = const i0.Value.absent(), this.faceAssetId = const i0.Value.absent(), - this.thumbnailPath = const i0.Value.absent(), this.isFavorite = const i0.Value.absent(), this.isHidden = const i0.Value.absent(), this.color = const i0.Value.absent(), @@ -808,7 +760,6 @@ class PersonEntityCompanion extends i0.UpdateCompanion { required String ownerId, required String name, this.faceAssetId = const i0.Value.absent(), - required String thumbnailPath, required bool isFavorite, required bool isHidden, this.color = const i0.Value.absent(), @@ -816,7 +767,6 @@ class PersonEntityCompanion extends i0.UpdateCompanion { }) : id = i0.Value(id), ownerId = i0.Value(ownerId), name = i0.Value(name), - thumbnailPath = i0.Value(thumbnailPath), isFavorite = i0.Value(isFavorite), isHidden = i0.Value(isHidden); static i0.Insertable custom({ @@ -826,7 +776,6 @@ class PersonEntityCompanion extends i0.UpdateCompanion { i0.Expression? ownerId, i0.Expression? name, i0.Expression? faceAssetId, - i0.Expression? thumbnailPath, i0.Expression? isFavorite, i0.Expression? isHidden, i0.Expression? color, @@ -839,7 +788,6 @@ class PersonEntityCompanion extends i0.UpdateCompanion { if (ownerId != null) 'owner_id': ownerId, if (name != null) 'name': name, if (faceAssetId != null) 'face_asset_id': faceAssetId, - if (thumbnailPath != null) 'thumbnail_path': thumbnailPath, if (isFavorite != null) 'is_favorite': isFavorite, if (isHidden != null) 'is_hidden': isHidden, if (color != null) 'color': color, @@ -854,7 +802,6 @@ class PersonEntityCompanion extends i0.UpdateCompanion { i0.Value? ownerId, i0.Value? name, i0.Value? faceAssetId, - i0.Value? thumbnailPath, i0.Value? isFavorite, i0.Value? isHidden, i0.Value? color, @@ -866,7 +813,6 @@ class PersonEntityCompanion extends i0.UpdateCompanion { ownerId: ownerId ?? this.ownerId, name: name ?? this.name, faceAssetId: faceAssetId ?? this.faceAssetId, - thumbnailPath: thumbnailPath ?? this.thumbnailPath, isFavorite: isFavorite ?? this.isFavorite, isHidden: isHidden ?? this.isHidden, color: color ?? this.color, @@ -895,9 +841,6 @@ class PersonEntityCompanion extends i0.UpdateCompanion { if (faceAssetId.present) { map['face_asset_id'] = i0.Variable(faceAssetId.value); } - if (thumbnailPath.present) { - map['thumbnail_path'] = i0.Variable(thumbnailPath.value); - } if (isFavorite.present) { map['is_favorite'] = i0.Variable(isFavorite.value); } @@ -922,7 +865,6 @@ class PersonEntityCompanion extends i0.UpdateCompanion { ..write('ownerId: $ownerId, ') ..write('name: $name, ') ..write('faceAssetId: $faceAssetId, ') - ..write('thumbnailPath: $thumbnailPath, ') ..write('isFavorite: $isFavorite, ') ..write('isHidden: $isHidden, ') ..write('color: $color, ') diff --git a/mobile/lib/infrastructure/repositories/asset_face.repository.dart b/mobile/lib/infrastructure/repositories/asset_face.repository.dart new file mode 100644 index 0000000000..a9ad753d84 --- /dev/null +++ b/mobile/lib/infrastructure/repositories/asset_face.repository.dart @@ -0,0 +1,33 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/domain/models/asset_face.model.dart'; +import 'package:immich_mobile/infrastructure/entities/asset_face.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; + +class DriftAssetFaceRepository extends DriftDatabaseRepository { + final Drift _db; + const DriftAssetFaceRepository(this._db) : super(_db); + + Future> getAll() { + return _db.assetFaceEntity + .select() + .map((assetFace) => assetFace.toDto()) + .get(); + } +} + +extension on AssetFaceEntityData { + AssetFace toDto() { + return AssetFace( + id: id, + assetId: assetId, + personId: personId, + imageWidth: imageWidth, + imageHeight: imageHeight, + boundingBoxX1: boundingBoxX1, + boundingBoxY1: boundingBoxY1, + boundingBoxX2: boundingBoxX2, + boundingBoxY2: boundingBoxY2, + sourceType: sourceType, + ); + } +} diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index 7562cf6ff5..ced148f855 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -5,6 +5,7 @@ import 'package:drift_flutter/drift_flutter.dart'; import 'package:flutter/foundation.dart'; import 'package:immich_mobile/domain/interfaces/db.interface.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/asset_face.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'; @@ -56,6 +57,7 @@ class IsarDatabaseRepository implements IDatabaseRepository { MemoryAssetEntity, StackEntity, PersonEntity, + AssetFaceEntity, ], include: { 'package:immich_mobile/infrastructure/entities/merged_asset.drift', @@ -72,7 +74,7 @@ class Drift extends $Drift implements IDatabaseRepository { ); @override - int get schemaVersion => 3; + int get schemaVersion => 4; @override MigrationStrategy get migration => MigrationStrategy( @@ -94,6 +96,10 @@ class Drift extends $Drift implements IDatabaseRepository { // Removed foreign key constraint on stack.primaryAssetId await m.alterTable(TableMigration(v3.stackEntity)); }, + from3To4: (m, v4) async { + await m.alterTable(TableMigration(v4.personEntity)); + await m.create(v4.assetFaceEntity); + }, ), ); diff --git a/mobile/lib/infrastructure/repositories/db.repository.drift.dart b/mobile/lib/infrastructure/repositories/db.repository.drift.dart index 0f822e57eb..7b722dfff6 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.drift.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.drift.dart @@ -31,9 +31,11 @@ import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift. as i14; import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart' as i15; -import 'package:immich_mobile/infrastructure/entities/merged_asset.drift.dart' +import 'package:immich_mobile/infrastructure/entities/asset_face.entity.drift.dart' as i16; -import 'package:drift/internal/modular.dart' as i17; +import 'package:immich_mobile/infrastructure/entities/merged_asset.drift.dart' + as i17; +import 'package:drift/internal/modular.dart' as i18; abstract class $Drift extends i0.GeneratedDatabase { $Drift(i0.QueryExecutor e) : super(e); @@ -64,8 +66,10 @@ abstract class $Drift extends i0.GeneratedDatabase { late final i14.$MemoryAssetEntityTable memoryAssetEntity = i14.$MemoryAssetEntityTable(this); late final i15.$PersonEntityTable personEntity = i15.$PersonEntityTable(this); - i16.MergedAssetDrift get mergedAssetDrift => i17.ReadDatabaseContainer(this) - .accessor(i16.MergedAssetDrift.new); + late final i16.$AssetFaceEntityTable assetFaceEntity = + i16.$AssetFaceEntityTable(this); + i17.MergedAssetDrift get mergedAssetDrift => i18.ReadDatabaseContainer(this) + .accessor(i17.MergedAssetDrift.new); @override Iterable> get allTables => allSchemaEntities.whereType>(); @@ -88,7 +92,8 @@ abstract class $Drift extends i0.GeneratedDatabase { remoteAlbumUserEntity, memoryEntity, memoryAssetEntity, - personEntity + personEntity, + assetFaceEntity ]; @override i0.StreamQueryUpdateRules get streamUpdateRules => @@ -227,6 +232,20 @@ abstract class $Drift extends i0.GeneratedDatabase { i0.TableUpdate('person_entity', kind: i0.UpdateKind.delete), ], ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName('remote_asset_entity', + limitUpdateKind: i0.UpdateKind.delete), + result: [ + i0.TableUpdate('asset_face_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName('person_entity', + limitUpdateKind: i0.UpdateKind.delete), + result: [ + i0.TableUpdate('asset_face_entity', kind: i0.UpdateKind.update), + ], + ), ], ); @override @@ -268,4 +287,6 @@ class $DriftManager { i14.$$MemoryAssetEntityTableTableManager(_db, _db.memoryAssetEntity); i15.$$PersonEntityTableTableManager get personEntity => i15.$$PersonEntityTableTableManager(_db, _db.personEntity); + i16.$$AssetFaceEntityTableTableManager get assetFaceEntity => + i16.$$AssetFaceEntityTableTableManager(_db, _db.assetFaceEntity); } diff --git a/mobile/lib/infrastructure/repositories/db.repository.steps.dart b/mobile/lib/infrastructure/repositories/db.repository.steps.dart index a0703c3714..d8c35707ed 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.steps.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.steps.dart @@ -1266,9 +1266,451 @@ final class Schema3 extends i0.VersionedSchema { i1.GeneratedColumn _column_75(String aliasedName) => i1.GeneratedColumn('primary_asset_id', aliasedName, false, type: i1.DriftSqlType.string); + +final class Schema4 extends i0.VersionedSchema { + Schema4({required super.database}) : super(version: 4); + @override + late final List entities = [ + userEntity, + remoteAssetEntity, + localAssetEntity, + stackEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + localAlbumEntity, + localAlbumAssetEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + ]; + late final Shape0 userEntity = Shape0( + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_4, + _column_5, + _column_6, + _column_7, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape1 remoteAssetEntity = Shape1( + source: i0.VersionedTable( + entityName: 'remote_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_13, + _column_14, + _column_15, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape2 localAssetEntity = Shape2( + source: i0.VersionedTable( + entityName: 'local_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_22, + _column_14, + _column_23, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape3 stackEntity = Shape3( + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_75, + ], + attachedDatabase: database, + ), + alias: null); + final i1.Index idxLocalAssetChecksum = i1.Index('idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); + final i1.Index uQRemoteAssetOwnerChecksum = i1.Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); + final i1.Index idxRemoteAssetChecksum = i1.Index('idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); + late final Shape4 userMetadataEntity = Shape4( + source: i0.VersionedTable( + entityName: 'user_metadata_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(user_id, "key")', + ], + columns: [ + _column_25, + _column_26, + _column_27, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape5 partnerEntity = Shape5( + source: i0.VersionedTable( + entityName: 'partner_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(shared_by_id, shared_with_id)', + ], + columns: [ + _column_28, + _column_29, + _column_30, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape6 localAlbumEntity = Shape6( + source: i0.VersionedTable( + entityName: 'local_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_0, + _column_1, + _column_5, + _column_31, + _column_32, + _column_33, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape7 localAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(asset_id, album_id)', + ], + columns: [ + _column_34, + _column_35, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape8 remoteExifEntity = Shape8( + source: i0.VersionedTable( + entityName: 'remote_exif_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(asset_id)', + ], + columns: [ + _column_36, + _column_37, + _column_38, + _column_39, + _column_40, + _column_41, + _column_11, + _column_10, + _column_42, + _column_43, + _column_44, + _column_45, + _column_46, + _column_47, + _column_48, + _column_49, + _column_50, + _column_51, + _column_52, + _column_53, + _column_54, + _column_55, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape9 remoteAlbumEntity = Shape9( + source: i0.VersionedTable( + entityName: 'remote_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_0, + _column_1, + _column_56, + _column_9, + _column_5, + _column_15, + _column_57, + _column_58, + _column_59, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape7 remoteAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'remote_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(asset_id, album_id)', + ], + columns: [ + _column_36, + _column_60, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape10 remoteAlbumUserEntity = Shape10( + source: i0.VersionedTable( + entityName: 'remote_album_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(album_id, user_id)', + ], + columns: [ + _column_60, + _column_25, + _column_61, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape11 memoryEntity = Shape11( + source: i0.VersionedTable( + entityName: 'memory_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_0, + _column_9, + _column_5, + _column_18, + _column_15, + _column_8, + _column_62, + _column_63, + _column_64, + _column_65, + _column_66, + _column_67, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape12 memoryAssetEntity = Shape12( + source: i0.VersionedTable( + entityName: 'memory_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(asset_id, memory_id)', + ], + columns: [ + _column_36, + _column_68, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape14 personEntity = Shape14( + source: i0.VersionedTable( + entityName: 'person_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_1, + _column_69, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape15 assetFaceEntity = Shape15( + source: i0.VersionedTable( + entityName: 'asset_face_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_0, + _column_36, + _column_76, + _column_77, + _column_78, + _column_79, + _column_80, + _column_81, + _column_82, + _column_83, + ], + attachedDatabase: database, + ), + alias: null); +} + +class Shape14 extends i0.VersionedTable { + Shape14({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get createdAt => + columnsByName['created_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get ownerId => + columnsByName['owner_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get faceAssetId => + columnsByName['face_asset_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get isFavorite => + columnsByName['is_favorite']! as i1.GeneratedColumn; + i1.GeneratedColumn get isHidden => + columnsByName['is_hidden']! as i1.GeneratedColumn; + i1.GeneratedColumn get color => + columnsByName['color']! as i1.GeneratedColumn; + i1.GeneratedColumn get birthDate => + columnsByName['birth_date']! as i1.GeneratedColumn; +} + +class Shape15 extends i0.VersionedTable { + Shape15({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get assetId => + columnsByName['asset_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get personId => + columnsByName['person_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get imageWidth => + columnsByName['image_width']! as i1.GeneratedColumn; + i1.GeneratedColumn get imageHeight => + columnsByName['image_height']! as i1.GeneratedColumn; + i1.GeneratedColumn get boundingBoxX1 => + columnsByName['bounding_box_x1']! as i1.GeneratedColumn; + i1.GeneratedColumn get boundingBoxY1 => + columnsByName['bounding_box_y1']! as i1.GeneratedColumn; + i1.GeneratedColumn get boundingBoxX2 => + columnsByName['bounding_box_x2']! as i1.GeneratedColumn; + i1.GeneratedColumn get boundingBoxY2 => + columnsByName['bounding_box_y2']! as i1.GeneratedColumn; + i1.GeneratedColumn get sourceType => + columnsByName['source_type']! as i1.GeneratedColumn; +} + +i1.GeneratedColumn _column_76(String aliasedName) => + i1.GeneratedColumn('person_id', aliasedName, true, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL')); +i1.GeneratedColumn _column_77(String aliasedName) => + i1.GeneratedColumn('image_width', aliasedName, false, + type: i1.DriftSqlType.int); +i1.GeneratedColumn _column_78(String aliasedName) => + i1.GeneratedColumn('image_height', aliasedName, false, + type: i1.DriftSqlType.int); +i1.GeneratedColumn _column_79(String aliasedName) => + i1.GeneratedColumn('bounding_box_x1', aliasedName, false, + type: i1.DriftSqlType.int); +i1.GeneratedColumn _column_80(String aliasedName) => + i1.GeneratedColumn('bounding_box_y1', aliasedName, false, + type: i1.DriftSqlType.int); +i1.GeneratedColumn _column_81(String aliasedName) => + i1.GeneratedColumn('bounding_box_x2', aliasedName, false, + type: i1.DriftSqlType.int); +i1.GeneratedColumn _column_82(String aliasedName) => + i1.GeneratedColumn('bounding_box_y2', aliasedName, false, + type: i1.DriftSqlType.int); +i1.GeneratedColumn _column_83(String aliasedName) => + i1.GeneratedColumn('source_type', aliasedName, false, + type: i1.DriftSqlType.string); i0.MigrationStepWithVersion migrationSteps({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, required Future Function(i1.Migrator m, Schema3 schema) from2To3, + required Future Function(i1.Migrator m, Schema4 schema) from3To4, }) { return (currentVersion, database) async { switch (currentVersion) { @@ -1282,6 +1724,11 @@ i0.MigrationStepWithVersion migrationSteps({ final migrator = i1.Migrator(database, schema); await from2To3(migrator, schema); return 3; + case 3: + final schema = Schema4(database: database); + final migrator = i1.Migrator(database, schema); + await from3To4(migrator, schema); + return 4; default: throw ArgumentError.value('Unknown migration from $currentVersion'); } @@ -1291,9 +1738,11 @@ i0.MigrationStepWithVersion migrationSteps({ i1.OnUpgrade stepByStep({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, required Future Function(i1.Migrator m, Schema3 schema) from2To3, + required Future Function(i1.Migrator m, Schema4 schema) from3To4, }) => i0.VersionedSchema.stepByStepHelper( step: migrationSteps( from1To2: from1To2, from2To3: from2To3, + from3To4: from3To4, )); diff --git a/mobile/lib/infrastructure/repositories/person.repository.dart b/mobile/lib/infrastructure/repositories/person.repository.dart index 859765d63b..fa336c5480 100644 --- a/mobile/lib/infrastructure/repositories/person.repository.dart +++ b/mobile/lib/infrastructure/repositories/person.repository.dart @@ -26,7 +26,6 @@ extension on PersonEntityData { ownerId: ownerId, name: name, faceAssetId: faceAssetId, - thumbnailPath: thumbnailPath, isFavorite: isFavorite, isHidden: isHidden, color: color, diff --git a/mobile/lib/infrastructure/repositories/sync_api.repository.dart b/mobile/lib/infrastructure/repositories/sync_api.repository.dart index 11d58663e0..e8be84effb 100644 --- a/mobile/lib/infrastructure/repositories/sync_api.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_api.repository.dart @@ -58,6 +58,7 @@ class SyncApiRepository { SyncRequestType.partnerStacksV1, SyncRequestType.userMetadataV1, SyncRequestType.peopleV1, + SyncRequestType.assetFacesV1, ], ).toJson(), ); @@ -176,6 +177,8 @@ const _kResponseMap = { SyncEntityType.userMetadataDeleteV1: SyncUserMetadataDeleteV1.fromJson, SyncEntityType.personV1: SyncPersonV1.fromJson, SyncEntityType.personDeleteV1: SyncPersonDeleteV1.fromJson, + SyncEntityType.assetFaceV1: SyncAssetFaceV1.fromJson, + SyncEntityType.assetFaceDeleteV1: SyncAssetFaceDeleteV1.fromJson, }; class _SyncAckV1 { diff --git a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart index e141c387be..1cca903566 100644 --- a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart @@ -5,6 +5,7 @@ import 'package:immich_mobile/domain/models/album/album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/memory.model.dart'; import 'package:immich_mobile/domain/models/user_metadata.model.dart'; +import 'package:immich_mobile/infrastructure/entities/asset_face.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift.dart'; @@ -546,11 +547,62 @@ class SyncStreamRepository extends DriftDatabaseRepository { Iterable data, ) async { try { - await _db.personEntity.deleteWhere( - (row) => row.id.isIn(data.map((e) => e.personId)), - ); + await _db.batch((batch) { + for (final person in data) { + batch.deleteWhere( + _db.personEntity, + (row) => row.id.equals(person.personId), + ); + } + }); } catch (error, stack) { _logger.severe('Error: deletePeopleV1', error, stack); + rethrow; + } + } + + Future updateAssetFacesV1(Iterable data) async { + try { + await _db.batch((batch) { + for (final assetFace in data) { + final companion = AssetFaceEntityCompanion( + assetId: Value(assetFace.assetId), + personId: Value(assetFace.personId), + imageWidth: Value(assetFace.imageWidth), + imageHeight: Value(assetFace.imageHeight), + boundingBoxX1: Value(assetFace.boundingBoxX1), + boundingBoxY1: Value(assetFace.boundingBoxY1), + boundingBoxX2: Value(assetFace.boundingBoxX2), + boundingBoxY2: Value(assetFace.boundingBoxY2), + sourceType: Value(assetFace.sourceType), + ); + + batch.insert( + _db.assetFaceEntity, + companion.copyWith(id: Value(assetFace.id)), + onConflict: DoUpdate((_) => companion), + ); + } + }); + } catch (error, stack) { + _logger.severe('Error: updateAssetFacesV1', error, stack); + rethrow; + } + } + + Future deleteAssetFacesV1(Iterable data) async { + try { + await _db.batch((batch) { + for (final assetFace in data) { + batch.deleteWhere( + _db.assetFaceEntity, + (row) => row.id.equals(assetFace.assetFaceId), + ); + } + }); + } catch (error, stack) { + _logger.severe('Error: deleteAssetFacesV1', error, stack); + rethrow; } } } diff --git a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart index 7ee151f94d..2334fc5227 100644 --- a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart +++ b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart @@ -121,6 +121,7 @@ final _features = [ await db.memoryAssetEntity.deleteAll(); await db.stackEntity.deleteAll(); await db.personEntity.deleteAll(); + await db.assetFaceEntity.deleteAll(); }, ), _Feature( diff --git a/mobile/lib/presentation/pages/dev/media_stat.page.dart b/mobile/lib/presentation/pages/dev/media_stat.page.dart index acd7b219b3..d1803498b4 100644 --- a/mobile/lib/presentation/pages/dev/media_stat.page.dart +++ b/mobile/lib/presentation/pages/dev/media_stat.page.dart @@ -170,6 +170,10 @@ final _remoteStats = [ name: 'People', load: (db) => db.managers.personEntity.count(), ), + _Stat( + name: 'AssetFaces', + load: (db) => db.managers.assetFaceEntity.count(), + ), ]; @RoutePage() diff --git a/mobile/lib/providers/infrastructure/asset_face.provider.dart b/mobile/lib/providers/infrastructure/asset_face.provider.dart new file mode 100644 index 0000000000..386609ba94 --- /dev/null +++ b/mobile/lib/providers/infrastructure/asset_face.provider.dart @@ -0,0 +1,7 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/infrastructure/repositories/asset_face.repository.dart'; +import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; + +final driftAssetFaceProvider = Provider( + (ref) => DriftAssetFaceRepository(ref.watch(driftProvider)), +); diff --git a/mobile/lib/repositories/auth.repository.dart b/mobile/lib/repositories/auth.repository.dart index 5cf357d5a4..1c51cedf96 100644 --- a/mobile/lib/repositories/auth.repository.dart +++ b/mobile/lib/repositories/auth.repository.dart @@ -34,11 +34,12 @@ class AuthRepository extends DatabaseRepository { _drift.userMetadataEntity.deleteAll(), _drift.partnerEntity.deleteAll(), _drift.stackEntity.deleteAll(), - _drift.personEntity.deleteAll(), + _drift.assetFaceEntity.deleteAll(), ]); // Drift deletions - parent entities await Future.wait([ _drift.memoryEntity.deleteAll(), + _drift.personEntity.deleteAll(), _drift.remoteAlbumEntity.deleteAll(), _drift.remoteAssetEntity.deleteAll(), _drift.userEntity.deleteAll(), diff --git a/mobile/test/domain/services/sync_stream_service_test.dart b/mobile/test/domain/services/sync_stream_service_test.dart index deb19dfcf8..f9d9c4fbe4 100644 --- a/mobile/test/domain/services/sync_stream_service_test.dart +++ b/mobile/test/domain/services/sync_stream_service_test.dart @@ -109,6 +109,10 @@ void main() { .thenAnswer(successHandler); when(() => mockSyncStreamRepo.deletePeopleV1(any())) .thenAnswer(successHandler); + when(() => mockSyncStreamRepo.updateAssetFacesV1(any())) + .thenAnswer(successHandler); + when(() => mockSyncStreamRepo.deleteAssetFacesV1(any())) + .thenAnswer(successHandler); sut = SyncStreamService( syncApiRepository: mockSyncApiRepo, diff --git a/mobile/test/drift/main/generated/schema.dart b/mobile/test/drift/main/generated/schema.dart index 209e70d788..22131b11bb 100644 --- a/mobile/test/drift/main/generated/schema.dart +++ b/mobile/test/drift/main/generated/schema.dart @@ -6,6 +6,7 @@ import 'package:drift/internal/migrations.dart'; import 'schema_v1.dart' as v1; import 'schema_v2.dart' as v2; import 'schema_v3.dart' as v3; +import 'schema_v4.dart' as v4; class GeneratedHelper implements SchemaInstantiationHelper { @override @@ -17,10 +18,12 @@ class GeneratedHelper implements SchemaInstantiationHelper { return v2.DatabaseAtV2(db); case 3: return v3.DatabaseAtV3(db); + case 4: + return v4.DatabaseAtV4(db); default: throw MissingSchemaException(version, versions); } } - static const versions = const [1, 2, 3]; + static const versions = const [1, 2, 3, 4]; } diff --git a/mobile/test/drift/main/generated/schema_v4.dart b/mobile/test/drift/main/generated/schema_v4.dart new file mode 100644 index 0000000000..d02e2ff9c4 --- /dev/null +++ b/mobile/test/drift/main/generated/schema_v4.dart @@ -0,0 +1,5524 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class UserEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn name = GeneratedColumn( + 'name', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("is_admin" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + late final GeneratedColumn email = GeneratedColumn( + 'email', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn profileImagePath = GeneratedColumn( + 'profile_image_path', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn quotaSizeInBytes = GeneratedColumn( + 'quota_size_in_bytes', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn quotaUsageInBytes = GeneratedColumn( + 'quota_usage_in_bytes', aliasedName, false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0')); + @override + List get $columns => [ + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_entity'; + @override + Set get $primaryKey => {id}; + @override + UserEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserEntityData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + name: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}name'])!, + isAdmin: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}is_admin'])!, + email: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}email'])!, + profileImagePath: attachedDatabase.typeMapping.read( + DriftSqlType.string, data['${effectivePrefix}profile_image_path']), + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + quotaSizeInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, data['${effectivePrefix}quota_size_in_bytes']), + quotaUsageInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, data['${effectivePrefix}quota_usage_in_bytes'])!, + ); + } + + @override + UserEntity createAlias(String alias) { + return UserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserEntityData extends DataClass implements Insertable { + final String id; + final String name; + final bool isAdmin; + final String email; + final String? profileImagePath; + final DateTime updatedAt; + final int? quotaSizeInBytes; + final int quotaUsageInBytes; + const UserEntityData( + {required this.id, + required this.name, + required this.isAdmin, + required this.email, + this.profileImagePath, + required this.updatedAt, + this.quotaSizeInBytes, + required this.quotaUsageInBytes}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['is_admin'] = Variable(isAdmin); + map['email'] = Variable(email); + if (!nullToAbsent || profileImagePath != null) { + map['profile_image_path'] = Variable(profileImagePath); + } + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || quotaSizeInBytes != null) { + map['quota_size_in_bytes'] = Variable(quotaSizeInBytes); + } + map['quota_usage_in_bytes'] = Variable(quotaUsageInBytes); + return map; + } + + factory UserEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + isAdmin: serializer.fromJson(json['isAdmin']), + email: serializer.fromJson(json['email']), + profileImagePath: serializer.fromJson(json['profileImagePath']), + updatedAt: serializer.fromJson(json['updatedAt']), + quotaSizeInBytes: serializer.fromJson(json['quotaSizeInBytes']), + quotaUsageInBytes: serializer.fromJson(json['quotaUsageInBytes']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'isAdmin': serializer.toJson(isAdmin), + 'email': serializer.toJson(email), + 'profileImagePath': serializer.toJson(profileImagePath), + 'updatedAt': serializer.toJson(updatedAt), + 'quotaSizeInBytes': serializer.toJson(quotaSizeInBytes), + 'quotaUsageInBytes': serializer.toJson(quotaUsageInBytes), + }; + } + + UserEntityData copyWith( + {String? id, + String? name, + bool? isAdmin, + String? email, + Value profileImagePath = const Value.absent(), + DateTime? updatedAt, + Value quotaSizeInBytes = const Value.absent(), + int? quotaUsageInBytes}) => + UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath.present + ? profileImagePath.value + : this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes.present + ? quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); + UserEntityData copyWithCompanion(UserEntityCompanion data) { + return UserEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, + email: data.email.present ? data.email.value : this.email, + profileImagePath: data.profileImagePath.present + ? data.profileImagePath.value + : this.profileImagePath, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + quotaSizeInBytes: data.quotaSizeInBytes.present + ? data.quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: data.quotaUsageInBytes.present + ? data.quotaUsageInBytes.value + : this.quotaUsageInBytes, + ); + } + + @override + String toString() { + return (StringBuffer('UserEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('profileImagePath: $profileImagePath, ') + ..write('updatedAt: $updatedAt, ') + ..write('quotaSizeInBytes: $quotaSizeInBytes, ') + ..write('quotaUsageInBytes: $quotaUsageInBytes') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, name, isAdmin, email, profileImagePath, + updatedAt, quotaSizeInBytes, quotaUsageInBytes); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserEntityData && + other.id == this.id && + other.name == this.name && + other.isAdmin == this.isAdmin && + other.email == this.email && + other.profileImagePath == this.profileImagePath && + other.updatedAt == this.updatedAt && + other.quotaSizeInBytes == this.quotaSizeInBytes && + other.quotaUsageInBytes == this.quotaUsageInBytes); +} + +class UserEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value isAdmin; + final Value email; + final Value profileImagePath; + final Value updatedAt; + final Value quotaSizeInBytes; + final Value quotaUsageInBytes; + const UserEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.isAdmin = const Value.absent(), + this.email = const Value.absent(), + this.profileImagePath = const Value.absent(), + this.updatedAt = const Value.absent(), + this.quotaSizeInBytes = const Value.absent(), + this.quotaUsageInBytes = const Value.absent(), + }); + UserEntityCompanion.insert({ + required String id, + required String name, + this.isAdmin = const Value.absent(), + required String email, + this.profileImagePath = const Value.absent(), + this.updatedAt = const Value.absent(), + this.quotaSizeInBytes = const Value.absent(), + this.quotaUsageInBytes = const Value.absent(), + }) : id = Value(id), + name = Value(name), + email = Value(email); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? isAdmin, + Expression? email, + Expression? profileImagePath, + Expression? updatedAt, + Expression? quotaSizeInBytes, + Expression? quotaUsageInBytes, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (isAdmin != null) 'is_admin': isAdmin, + if (email != null) 'email': email, + if (profileImagePath != null) 'profile_image_path': profileImagePath, + if (updatedAt != null) 'updated_at': updatedAt, + if (quotaSizeInBytes != null) 'quota_size_in_bytes': quotaSizeInBytes, + if (quotaUsageInBytes != null) 'quota_usage_in_bytes': quotaUsageInBytes, + }); + } + + UserEntityCompanion copyWith( + {Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? profileImagePath, + Value? updatedAt, + Value? quotaSizeInBytes, + Value? quotaUsageInBytes}) { + return UserEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath ?? this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (isAdmin.present) { + map['is_admin'] = Variable(isAdmin.value); + } + if (email.present) { + map['email'] = Variable(email.value); + } + if (profileImagePath.present) { + map['profile_image_path'] = Variable(profileImagePath.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (quotaSizeInBytes.present) { + map['quota_size_in_bytes'] = Variable(quotaSizeInBytes.value); + } + if (quotaUsageInBytes.present) { + map['quota_usage_in_bytes'] = Variable(quotaUsageInBytes.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('profileImagePath: $profileImagePath, ') + ..write('updatedAt: $updatedAt, ') + ..write('quotaSizeInBytes: $quotaSizeInBytes, ') + ..write('quotaUsageInBytes: $quotaUsageInBytes') + ..write(')')) + .toString(); + } +} + +class RemoteAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn type = GeneratedColumn( + 'type', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn width = GeneratedColumn( + 'width', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn height = GeneratedColumn( + 'height', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn localDateTime = + GeneratedColumn('local_date_time', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn livePhotoVideoId = GeneratedColumn( + 'live_photo_video_id', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAssetEntityData( + name: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}name'])!, + type: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}type'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + width: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}width']), + height: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}height']), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + checksum: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}checksum'])!, + isFavorite: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, + ownerId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, data['${effectivePrefix}local_date_time']), + thumbHash: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}thumb_hash']), + deletedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, data['${effectivePrefix}live_photo_video_id']), + visibility: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}visibility'])!, + stackId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}stack_id']), + ); + } + + @override + RemoteAssetEntity createAlias(String alias) { + return RemoteAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String checksum; + final bool isFavorite; + final String ownerId; + final DateTime? localDateTime; + final String? thumbHash; + final DateTime? deletedAt; + final String? livePhotoVideoId; + final int visibility; + final String? stackId; + const RemoteAssetEntityData( + {required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + map['checksum'] = Variable(checksum); + map['is_favorite'] = Variable(isFavorite); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || localDateTime != null) { + map['local_date_time'] = Variable(localDateTime); + } + if (!nullToAbsent || thumbHash != null) { + map['thumb_hash'] = Variable(thumbHash); + } + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + if (!nullToAbsent || livePhotoVideoId != null) { + map['live_photo_video_id'] = Variable(livePhotoVideoId); + } + map['visibility'] = Variable(visibility); + if (!nullToAbsent || stackId != null) { + map['stack_id'] = Variable(stackId); + } + return map; + } + + factory RemoteAssetEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + ownerId: serializer.fromJson(json['ownerId']), + localDateTime: serializer.fromJson(json['localDateTime']), + thumbHash: serializer.fromJson(json['thumbHash']), + deletedAt: serializer.fromJson(json['deletedAt']), + livePhotoVideoId: serializer.fromJson(json['livePhotoVideoId']), + visibility: serializer.fromJson(json['visibility']), + stackId: serializer.fromJson(json['stackId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'ownerId': serializer.toJson(ownerId), + 'localDateTime': serializer.toJson(localDateTime), + 'thumbHash': serializer.toJson(thumbHash), + 'deletedAt': serializer.toJson(deletedAt), + 'livePhotoVideoId': serializer.toJson(livePhotoVideoId), + 'visibility': serializer.toJson(visibility), + 'stackId': serializer.toJson(stackId), + }; + } + + RemoteAssetEntityData copyWith( + {String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent()}) => + RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: + localDateTime.present ? localDateTime.value : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + ); + RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { + return RemoteAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: + data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + localDateTime: data.localDateTime.present + ? data.localDateTime.value + : this.localDateTime, + thumbHash: data.thumbHash.present ? data.thumbHash.value : this.thumbHash, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + livePhotoVideoId: data.livePhotoVideoId.present + ? data.livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: + data.visibility.present ? data.visibility.value : this.visibility, + stackId: data.stackId.present ? data.stackId.value : this.stackId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.ownerId == this.ownerId && + other.localDateTime == this.localDateTime && + other.thumbHash == this.thumbHash && + other.deletedAt == this.deletedAt && + other.livePhotoVideoId == this.livePhotoVideoId && + other.visibility == this.visibility && + other.stackId == this.stackId); +} + +class RemoteAssetEntityCompanion + extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value ownerId; + final Value localDateTime; + final Value thumbHash; + final Value deletedAt; + final Value livePhotoVideoId; + final Value visibility; + final Value stackId; + const RemoteAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.ownerId = const Value.absent(), + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + this.visibility = const Value.absent(), + this.stackId = const Value.absent(), + }); + RemoteAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + required String checksum, + this.isFavorite = const Value.absent(), + required String ownerId, + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + required int visibility, + this.stackId = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? ownerId, + Expression? localDateTime, + Expression? thumbHash, + Expression? deletedAt, + Expression? livePhotoVideoId, + Expression? visibility, + Expression? stackId, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (ownerId != null) 'owner_id': ownerId, + if (localDateTime != null) 'local_date_time': localDateTime, + if (thumbHash != null) 'thumb_hash': thumbHash, + if (deletedAt != null) 'deleted_at': deletedAt, + if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId, + if (visibility != null) 'visibility': visibility, + if (stackId != null) 'stack_id': stackId, + }); + } + + RemoteAssetEntityCompanion copyWith( + {Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId}) { + return RemoteAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime ?? this.localDateTime, + thumbHash: thumbHash ?? this.thumbHash, + deletedAt: deletedAt ?? this.deletedAt, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId ?? this.stackId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (localDateTime.present) { + map['local_date_time'] = Variable(localDateTime.value); + } + if (thumbHash.present) { + map['thumb_hash'] = Variable(thumbHash.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (livePhotoVideoId.present) { + map['live_photo_video_id'] = Variable(livePhotoVideoId.value); + } + if (visibility.present) { + map['visibility'] = Variable(visibility.value); + } + if (stackId.present) { + map['stack_id'] = Variable(stackId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId') + ..write(')')) + .toString(); + } +} + +class LocalAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn type = GeneratedColumn( + 'type', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn width = GeneratedColumn( + 'width', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn height = GeneratedColumn( + 'height', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', aliasedName, false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0')); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAssetEntityData( + name: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}name'])!, + type: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}type'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + width: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}width']), + height: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}height']), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + checksum: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}checksum']), + isFavorite: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, + orientation: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}orientation'])!, + ); + } + + @override + LocalAssetEntity createAlias(String alias) { + return LocalAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String? checksum; + final bool isFavorite; + final int orientation; + const LocalAssetEntityData( + {required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + if (!nullToAbsent || checksum != null) { + map['checksum'] = Variable(checksum); + } + map['is_favorite'] = Variable(isFavorite); + map['orientation'] = Variable(orientation); + return map; + } + + factory LocalAssetEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + orientation: serializer.fromJson(json['orientation']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'orientation': serializer.toJson(orientation), + }; + } + + LocalAssetEntityData copyWith( + {String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation}) => + LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { + return LocalAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: + data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + orientation: + data.orientation.present ? data.orientation.value : this.orientation, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(name, type, createdAt, updatedAt, width, + height, durationInSeconds, id, checksum, isFavorite, orientation); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.orientation == this.orientation); +} + +class LocalAssetEntityCompanion extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value orientation; + const LocalAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }); + LocalAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? orientation, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (orientation != null) 'orientation': orientation, + }); + } + + LocalAssetEntityCompanion copyWith( + {Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation}) { + return LocalAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } +} + +class StackEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StackEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + @override + List get $columns => + [id, createdAt, updatedAt, ownerId, primaryAssetId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'stack_entity'; + @override + Set get $primaryKey => {id}; + @override + StackEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StackEntityData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + ownerId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!, + ); + } + + @override + StackEntity createAlias(String alias) { + return StackEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StackEntityData extends DataClass implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String primaryAssetId; + const StackEntityData( + {required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['primary_asset_id'] = Variable(primaryAssetId); + return map; + } + + factory StackEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StackEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + primaryAssetId: serializer.fromJson(json['primaryAssetId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'primaryAssetId': serializer.toJson(primaryAssetId), + }; + } + + StackEntityData copyWith( + {String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId}) => + StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + StackEntityData copyWithCompanion(StackEntityCompanion data) { + return StackEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, + ); + } + + @override + String toString() { + return (StringBuffer('StackEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StackEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.primaryAssetId == this.primaryAssetId); +} + +class StackEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value primaryAssetId; + const StackEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.primaryAssetId = const Value.absent(), + }); + StackEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String primaryAssetId, + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? primaryAssetId, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, + }); + } + + StackEntityCompanion copyWith( + {Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId}) { + return StackEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (primaryAssetId.present) { + map['primary_asset_id'] = Variable(primaryAssetId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StackEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } +} + +class UserMetadataEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserMetadataEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn key = GeneratedColumn( + 'key', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn value = GeneratedColumn( + 'value', aliasedName, false, + type: DriftSqlType.blob, requiredDuringInsert: true); + @override + List get $columns => [userId, key, value]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_metadata_entity'; + @override + Set get $primaryKey => {userId, key}; + @override + UserMetadataEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserMetadataEntityData( + userId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, + key: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}key'])!, + value: attachedDatabase.typeMapping + .read(DriftSqlType.blob, data['${effectivePrefix}value'])!, + ); + } + + @override + UserMetadataEntity createAlias(String alias) { + return UserMetadataEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserMetadataEntityData extends DataClass + implements Insertable { + final String userId; + final int key; + final Uint8List value; + const UserMetadataEntityData( + {required this.userId, required this.key, required this.value}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['user_id'] = Variable(userId); + map['key'] = Variable(key); + map['value'] = Variable(value); + return map; + } + + factory UserMetadataEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserMetadataEntityData( + userId: serializer.fromJson(json['userId']), + key: serializer.fromJson(json['key']), + value: serializer.fromJson(json['value']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'userId': serializer.toJson(userId), + 'key': serializer.toJson(key), + 'value': serializer.toJson(value), + }; + } + + UserMetadataEntityData copyWith( + {String? userId, int? key, Uint8List? value}) => + UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { + return UserMetadataEntityData( + userId: data.userId.present ? data.userId.value : this.userId, + key: data.key.present ? data.key.value : this.key, + value: data.value.present ? data.value.value : this.value, + ); + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityData(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(userId, key, $driftBlobEquality.hash(value)); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserMetadataEntityData && + other.userId == this.userId && + other.key == this.key && + $driftBlobEquality.equals(other.value, this.value)); +} + +class UserMetadataEntityCompanion + extends UpdateCompanion { + final Value userId; + final Value key; + final Value value; + const UserMetadataEntityCompanion({ + this.userId = const Value.absent(), + this.key = const Value.absent(), + this.value = const Value.absent(), + }); + UserMetadataEntityCompanion.insert({ + required String userId, + required int key, + required Uint8List value, + }) : userId = Value(userId), + key = Value(key), + value = Value(value); + static Insertable custom({ + Expression? userId, + Expression? key, + Expression? value, + }) { + return RawValuesInsertable({ + if (userId != null) 'user_id': userId, + if (key != null) 'key': key, + if (value != null) 'value': value, + }); + } + + UserMetadataEntityCompanion copyWith( + {Value? userId, Value? key, Value? value}) { + return UserMetadataEntityCompanion( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (key.present) { + map['key'] = Variable(key.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityCompanion(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } +} + +class PartnerEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PartnerEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("in_timeline" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + @override + List get $columns => [sharedById, sharedWithId, inTimeline]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'partner_entity'; + @override + Set get $primaryKey => {sharedById, sharedWithId}; + @override + PartnerEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PartnerEntityData( + sharedById: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}shared_by_id'])!, + sharedWithId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}shared_with_id'])!, + inTimeline: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!, + ); + } + + @override + PartnerEntity createAlias(String alias) { + return PartnerEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PartnerEntityData extends DataClass + implements Insertable { + final String sharedById; + final String sharedWithId; + final bool inTimeline; + const PartnerEntityData( + {required this.sharedById, + required this.sharedWithId, + required this.inTimeline}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['shared_by_id'] = Variable(sharedById); + map['shared_with_id'] = Variable(sharedWithId); + map['in_timeline'] = Variable(inTimeline); + return map; + } + + factory PartnerEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PartnerEntityData( + sharedById: serializer.fromJson(json['sharedById']), + sharedWithId: serializer.fromJson(json['sharedWithId']), + inTimeline: serializer.fromJson(json['inTimeline']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'sharedById': serializer.toJson(sharedById), + 'sharedWithId': serializer.toJson(sharedWithId), + 'inTimeline': serializer.toJson(inTimeline), + }; + } + + PartnerEntityData copyWith( + {String? sharedById, String? sharedWithId, bool? inTimeline}) => + PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { + return PartnerEntityData( + sharedById: + data.sharedById.present ? data.sharedById.value : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: + data.inTimeline.present ? data.inTimeline.value : this.inTimeline, + ); + } + + @override + String toString() { + return (StringBuffer('PartnerEntityData(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PartnerEntityData && + other.sharedById == this.sharedById && + other.sharedWithId == this.sharedWithId && + other.inTimeline == this.inTimeline); +} + +class PartnerEntityCompanion extends UpdateCompanion { + final Value sharedById; + final Value sharedWithId; + final Value inTimeline; + const PartnerEntityCompanion({ + this.sharedById = const Value.absent(), + this.sharedWithId = const Value.absent(), + this.inTimeline = const Value.absent(), + }); + PartnerEntityCompanion.insert({ + required String sharedById, + required String sharedWithId, + this.inTimeline = const Value.absent(), + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); + static Insertable custom({ + Expression? sharedById, + Expression? sharedWithId, + Expression? inTimeline, + }) { + return RawValuesInsertable({ + if (sharedById != null) 'shared_by_id': sharedById, + if (sharedWithId != null) 'shared_with_id': sharedWithId, + if (inTimeline != null) 'in_timeline': inTimeline, + }); + } + + PartnerEntityCompanion copyWith( + {Value? sharedById, + Value? sharedWithId, + Value? inTimeline}) { + return PartnerEntityCompanion( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sharedById.present) { + map['shared_by_id'] = Variable(sharedById.value); + } + if (sharedWithId.present) { + map['shared_with_id'] = Variable(sharedWithId.value); + } + if (inTimeline.present) { + map['in_timeline'] = Variable(inTimeline.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PartnerEntityCompanion(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } +} + +class LocalAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn name = GeneratedColumn( + 'name', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', aliasedName, true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("marker" IN (0, 1))')); + @override + List get $columns => + [id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumEntityData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + name: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}name'])!, + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + backupSelection: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}backup_selection'])!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, data['${effectivePrefix}is_ios_shared_album'])!, + marker_: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}marker']), + ); + } + + @override + LocalAlbumEntity createAlias(String alias) { + return LocalAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final DateTime updatedAt; + final int backupSelection; + final bool isIosSharedAlbum; + final bool? marker_; + const LocalAlbumEntityData( + {required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['updated_at'] = Variable(updatedAt); + map['backup_selection'] = Variable(backupSelection); + map['is_ios_shared_album'] = Variable(isIosSharedAlbum); + if (!nullToAbsent || marker_ != null) { + map['marker'] = Variable(marker_); + } + return map; + } + + factory LocalAlbumEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + updatedAt: serializer.fromJson(json['updatedAt']), + backupSelection: serializer.fromJson(json['backupSelection']), + isIosSharedAlbum: serializer.fromJson(json['isIosSharedAlbum']), + marker_: serializer.fromJson(json['marker_']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'updatedAt': serializer.toJson(updatedAt), + 'backupSelection': serializer.toJson(backupSelection), + 'isIosSharedAlbum': serializer.toJson(isIosSharedAlbum), + 'marker_': serializer.toJson(marker_), + }; + } + + LocalAlbumEntityData copyWith( + {String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent()}) => + LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); + LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { + return LocalAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, + marker_: data.marker_.present ? data.marker_.value : this.marker_, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.updatedAt == this.updatedAt && + other.backupSelection == this.backupSelection && + other.isIosSharedAlbum == this.isIosSharedAlbum && + other.marker_ == this.marker_); +} + +class LocalAlbumEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value updatedAt; + final Value backupSelection; + final Value isIosSharedAlbum; + final Value marker_; + const LocalAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.updatedAt = const Value.absent(), + this.backupSelection = const Value.absent(), + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }); + LocalAlbumEntityCompanion.insert({ + required String id, + required String name, + this.updatedAt = const Value.absent(), + required int backupSelection, + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? updatedAt, + Expression? backupSelection, + Expression? isIosSharedAlbum, + Expression? marker_, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (updatedAt != null) 'updated_at': updatedAt, + if (backupSelection != null) 'backup_selection': backupSelection, + if (isIosSharedAlbum != null) 'is_ios_shared_album': isIosSharedAlbum, + if (marker_ != null) 'marker': marker_, + }); + } + + LocalAlbumEntityCompanion copyWith( + {Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_}) { + return LocalAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_ ?? this.marker_, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (backupSelection.present) { + map['backup_selection'] = Variable(backupSelection.value); + } + if (isIosSharedAlbum.present) { + map['is_ios_shared_album'] = Variable(isIosSharedAlbum.value); + } + if (marker_.present) { + map['marker'] = Variable(marker_.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } +} + +class LocalAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE')); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + LocalAlbumAssetEntityData map(Map data, + {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, + albumId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + ); + } + + @override + LocalAlbumAssetEntity createAlias(String alias) { + return LocalAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const LocalAlbumAssetEntityData( + {required this.assetId, required this.albumId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory LocalAlbumAssetEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + LocalAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data) { + return LocalAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const LocalAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + LocalAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + LocalAlbumAssetEntityCompanion copyWith( + {Value? assetId, Value? albumId}) { + return LocalAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class RemoteExifEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteExifEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn city = GeneratedColumn( + 'city', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn state = GeneratedColumn( + 'state', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn country = GeneratedColumn( + 'country', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn('date_time_original', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn description = GeneratedColumn( + 'description', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn height = GeneratedColumn( + 'height', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn width = GeneratedColumn( + 'width', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', aliasedName, true, + type: DriftSqlType.double, requiredDuringInsert: false); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', aliasedName, true, + type: DriftSqlType.double, requiredDuringInsert: false); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', aliasedName, true, + type: DriftSqlType.double, requiredDuringInsert: false); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', aliasedName, true, + type: DriftSqlType.double, requiredDuringInsert: false); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn make = GeneratedColumn( + 'make', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn model = GeneratedColumn( + 'model', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + @override + List get $columns => [ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_exif_entity'; + @override + Set get $primaryKey => {assetId}; + @override + RemoteExifEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteExifEntityData( + assetId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, + city: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}city']), + state: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}state']), + country: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}country']), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, data['${effectivePrefix}date_time_original']), + description: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}description']), + height: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}height']), + width: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}width']), + exposureTime: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}exposure_time']), + fNumber: attachedDatabase.typeMapping + .read(DriftSqlType.double, data['${effectivePrefix}f_number']), + fileSize: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}file_size']), + focalLength: attachedDatabase.typeMapping + .read(DriftSqlType.double, data['${effectivePrefix}focal_length']), + latitude: attachedDatabase.typeMapping + .read(DriftSqlType.double, data['${effectivePrefix}latitude']), + longitude: attachedDatabase.typeMapping + .read(DriftSqlType.double, data['${effectivePrefix}longitude']), + iso: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}iso']), + make: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}make']), + model: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}model']), + lens: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}lens']), + orientation: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}orientation']), + timeZone: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}time_zone']), + rating: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}rating']), + projectionType: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}projection_type']), + ); + } + + @override + RemoteExifEntity createAlias(String alias) { + return RemoteExifEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteExifEntityData extends DataClass + implements Insertable { + final String assetId; + final String? city; + final String? state; + final String? country; + final DateTime? dateTimeOriginal; + final String? description; + final int? height; + final int? width; + final String? exposureTime; + final double? fNumber; + final int? fileSize; + final double? focalLength; + final double? latitude; + final double? longitude; + final int? iso; + final String? make; + final String? model; + final String? lens; + final String? orientation; + final String? timeZone; + final int? rating; + final String? projectionType; + const RemoteExifEntityData( + {required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || city != null) { + map['city'] = Variable(city); + } + if (!nullToAbsent || state != null) { + map['state'] = Variable(state); + } + if (!nullToAbsent || country != null) { + map['country'] = Variable(country); + } + if (!nullToAbsent || dateTimeOriginal != null) { + map['date_time_original'] = Variable(dateTimeOriginal); + } + if (!nullToAbsent || description != null) { + map['description'] = Variable(description); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || exposureTime != null) { + map['exposure_time'] = Variable(exposureTime); + } + if (!nullToAbsent || fNumber != null) { + map['f_number'] = Variable(fNumber); + } + if (!nullToAbsent || fileSize != null) { + map['file_size'] = Variable(fileSize); + } + if (!nullToAbsent || focalLength != null) { + map['focal_length'] = Variable(focalLength); + } + if (!nullToAbsent || latitude != null) { + map['latitude'] = Variable(latitude); + } + if (!nullToAbsent || longitude != null) { + map['longitude'] = Variable(longitude); + } + if (!nullToAbsent || iso != null) { + map['iso'] = Variable(iso); + } + if (!nullToAbsent || make != null) { + map['make'] = Variable(make); + } + if (!nullToAbsent || model != null) { + map['model'] = Variable(model); + } + if (!nullToAbsent || lens != null) { + map['lens'] = Variable(lens); + } + if (!nullToAbsent || orientation != null) { + map['orientation'] = Variable(orientation); + } + if (!nullToAbsent || timeZone != null) { + map['time_zone'] = Variable(timeZone); + } + if (!nullToAbsent || rating != null) { + map['rating'] = Variable(rating); + } + if (!nullToAbsent || projectionType != null) { + map['projection_type'] = Variable(projectionType); + } + return map; + } + + factory RemoteExifEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteExifEntityData( + assetId: serializer.fromJson(json['assetId']), + city: serializer.fromJson(json['city']), + state: serializer.fromJson(json['state']), + country: serializer.fromJson(json['country']), + dateTimeOriginal: + serializer.fromJson(json['dateTimeOriginal']), + description: serializer.fromJson(json['description']), + height: serializer.fromJson(json['height']), + width: serializer.fromJson(json['width']), + exposureTime: serializer.fromJson(json['exposureTime']), + fNumber: serializer.fromJson(json['fNumber']), + fileSize: serializer.fromJson(json['fileSize']), + focalLength: serializer.fromJson(json['focalLength']), + latitude: serializer.fromJson(json['latitude']), + longitude: serializer.fromJson(json['longitude']), + iso: serializer.fromJson(json['iso']), + make: serializer.fromJson(json['make']), + model: serializer.fromJson(json['model']), + lens: serializer.fromJson(json['lens']), + orientation: serializer.fromJson(json['orientation']), + timeZone: serializer.fromJson(json['timeZone']), + rating: serializer.fromJson(json['rating']), + projectionType: serializer.fromJson(json['projectionType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'city': serializer.toJson(city), + 'state': serializer.toJson(state), + 'country': serializer.toJson(country), + 'dateTimeOriginal': serializer.toJson(dateTimeOriginal), + 'description': serializer.toJson(description), + 'height': serializer.toJson(height), + 'width': serializer.toJson(width), + 'exposureTime': serializer.toJson(exposureTime), + 'fNumber': serializer.toJson(fNumber), + 'fileSize': serializer.toJson(fileSize), + 'focalLength': serializer.toJson(focalLength), + 'latitude': serializer.toJson(latitude), + 'longitude': serializer.toJson(longitude), + 'iso': serializer.toJson(iso), + 'make': serializer.toJson(make), + 'model': serializer.toJson(model), + 'lens': serializer.toJson(lens), + 'orientation': serializer.toJson(orientation), + 'timeZone': serializer.toJson(timeZone), + 'rating': serializer.toJson(rating), + 'projectionType': serializer.toJson(projectionType), + }; + } + + RemoteExifEntityData copyWith( + {String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent()}) => + RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: + exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: + projectionType.present ? projectionType.value : this.projectionType, + ); + RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { + return RemoteExifEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + city: data.city.present ? data.city.value : this.city, + state: data.state.present ? data.state.value : this.state, + country: data.country.present ? data.country.value : this.country, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: + data.description.present ? data.description.value : this.description, + height: data.height.present ? data.height.value : this.height, + width: data.width.present ? data.width.value : this.width, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, + fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, + fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, + focalLength: + data.focalLength.present ? data.focalLength.value : this.focalLength, + latitude: data.latitude.present ? data.latitude.value : this.latitude, + longitude: data.longitude.present ? data.longitude.value : this.longitude, + iso: data.iso.present ? data.iso.value : this.iso, + make: data.make.present ? data.make.value : this.make, + model: data.model.present ? data.model.value : this.model, + lens: data.lens.present ? data.lens.value : this.lens, + orientation: + data.orientation.present ? data.orientation.value : this.orientation, + timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, + rating: data.rating.present ? data.rating.value : this.rating, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityData(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteExifEntityData && + other.assetId == this.assetId && + other.city == this.city && + other.state == this.state && + other.country == this.country && + other.dateTimeOriginal == this.dateTimeOriginal && + other.description == this.description && + other.height == this.height && + other.width == this.width && + other.exposureTime == this.exposureTime && + other.fNumber == this.fNumber && + other.fileSize == this.fileSize && + other.focalLength == this.focalLength && + other.latitude == this.latitude && + other.longitude == this.longitude && + other.iso == this.iso && + other.make == this.make && + other.model == this.model && + other.lens == this.lens && + other.orientation == this.orientation && + other.timeZone == this.timeZone && + other.rating == this.rating && + other.projectionType == this.projectionType); +} + +class RemoteExifEntityCompanion extends UpdateCompanion { + final Value assetId; + final Value city; + final Value state; + final Value country; + final Value dateTimeOriginal; + final Value description; + final Value height; + final Value width; + final Value exposureTime; + final Value fNumber; + final Value fileSize; + final Value focalLength; + final Value latitude; + final Value longitude; + final Value iso; + final Value make; + final Value model; + final Value lens; + final Value orientation; + final Value timeZone; + final Value rating; + final Value projectionType; + const RemoteExifEntityCompanion({ + this.assetId = const Value.absent(), + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }); + RemoteExifEntityCompanion.insert({ + required String assetId, + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }) : assetId = Value(assetId); + static Insertable custom({ + Expression? assetId, + Expression? city, + Expression? state, + Expression? country, + Expression? dateTimeOriginal, + Expression? description, + Expression? height, + Expression? width, + Expression? exposureTime, + Expression? fNumber, + Expression? fileSize, + Expression? focalLength, + Expression? latitude, + Expression? longitude, + Expression? iso, + Expression? make, + Expression? model, + Expression? lens, + Expression? orientation, + Expression? timeZone, + Expression? rating, + Expression? projectionType, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (city != null) 'city': city, + if (state != null) 'state': state, + if (country != null) 'country': country, + if (dateTimeOriginal != null) 'date_time_original': dateTimeOriginal, + if (description != null) 'description': description, + if (height != null) 'height': height, + if (width != null) 'width': width, + if (exposureTime != null) 'exposure_time': exposureTime, + if (fNumber != null) 'f_number': fNumber, + if (fileSize != null) 'file_size': fileSize, + if (focalLength != null) 'focal_length': focalLength, + if (latitude != null) 'latitude': latitude, + if (longitude != null) 'longitude': longitude, + if (iso != null) 'iso': iso, + if (make != null) 'make': make, + if (model != null) 'model': model, + if (lens != null) 'lens': lens, + if (orientation != null) 'orientation': orientation, + if (timeZone != null) 'time_zone': timeZone, + if (rating != null) 'rating': rating, + if (projectionType != null) 'projection_type': projectionType, + }); + } + + RemoteExifEntityCompanion copyWith( + {Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType}) { + return RemoteExifEntityCompanion( + assetId: assetId ?? this.assetId, + city: city ?? this.city, + state: state ?? this.state, + country: country ?? this.country, + dateTimeOriginal: dateTimeOriginal ?? this.dateTimeOriginal, + description: description ?? this.description, + height: height ?? this.height, + width: width ?? this.width, + exposureTime: exposureTime ?? this.exposureTime, + fNumber: fNumber ?? this.fNumber, + fileSize: fileSize ?? this.fileSize, + focalLength: focalLength ?? this.focalLength, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + iso: iso ?? this.iso, + make: make ?? this.make, + model: model ?? this.model, + lens: lens ?? this.lens, + orientation: orientation ?? this.orientation, + timeZone: timeZone ?? this.timeZone, + rating: rating ?? this.rating, + projectionType: projectionType ?? this.projectionType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (city.present) { + map['city'] = Variable(city.value); + } + if (state.present) { + map['state'] = Variable(state.value); + } + if (country.present) { + map['country'] = Variable(country.value); + } + if (dateTimeOriginal.present) { + map['date_time_original'] = Variable(dateTimeOriginal.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (exposureTime.present) { + map['exposure_time'] = Variable(exposureTime.value); + } + if (fNumber.present) { + map['f_number'] = Variable(fNumber.value); + } + if (fileSize.present) { + map['file_size'] = Variable(fileSize.value); + } + if (focalLength.present) { + map['focal_length'] = Variable(focalLength.value); + } + if (latitude.present) { + map['latitude'] = Variable(latitude.value); + } + if (longitude.present) { + map['longitude'] = Variable(longitude.value); + } + if (iso.present) { + map['iso'] = Variable(iso.value); + } + if (make.present) { + map['make'] = Variable(make.value); + } + if (model.present) { + map['model'] = Variable(model.value); + } + if (lens.present) { + map['lens'] = Variable(lens.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + if (timeZone.present) { + map['time_zone'] = Variable(timeZone.value); + } + if (rating.present) { + map['rating'] = Variable(rating.value); + } + if (projectionType.present) { + map['projection_type'] = Variable(projectionType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn name = GeneratedColumn( + 'name', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn description = GeneratedColumn( + 'description', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\'')); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', aliasedName, true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL')); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))'), + defaultValue: const CustomExpression('1')); + late final GeneratedColumn order = GeneratedColumn( + 'order', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + @override + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumEntityData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + name: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}name'])!, + description: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}description'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + ownerId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, data['${effectivePrefix}thumbnail_asset_id']), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, data['${effectivePrefix}is_activity_enabled'])!, + order: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}order'])!, + ); + } + + @override + RemoteAlbumEntity createAlias(String alias) { + return RemoteAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String description; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String? thumbnailAssetId; + final bool isActivityEnabled; + final int order; + const RemoteAlbumEntityData( + {required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['description'] = Variable(description); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || thumbnailAssetId != null) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId); + } + map['is_activity_enabled'] = Variable(isActivityEnabled); + map['order'] = Variable(order); + return map; + } + + factory RemoteAlbumEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + description: serializer.fromJson(json['description']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + thumbnailAssetId: serializer.fromJson(json['thumbnailAssetId']), + isActivityEnabled: serializer.fromJson(json['isActivityEnabled']), + order: serializer.fromJson(json['order']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'description': serializer.toJson(description), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'thumbnailAssetId': serializer.toJson(thumbnailAssetId), + 'isActivityEnabled': serializer.toJson(isActivityEnabled), + 'order': serializer.toJson(order), + }; + } + + RemoteAlbumEntityData copyWith( + {String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order}) => + RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { + return RemoteAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + description: + data.description.present ? data.description.value : this.description, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, + order: data.order.present ? data.order.value : this.order, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, name, description, createdAt, updatedAt, + ownerId, thumbnailAssetId, isActivityEnabled, order); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.description == this.description && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.thumbnailAssetId == this.thumbnailAssetId && + other.isActivityEnabled == this.isActivityEnabled && + other.order == this.order); +} + +class RemoteAlbumEntityCompanion + extends UpdateCompanion { + final Value id; + final Value name; + final Value description; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value thumbnailAssetId; + final Value isActivityEnabled; + final Value order; + const RemoteAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + this.order = const Value.absent(), + }); + RemoteAlbumEntityCompanion.insert({ + required String id, + required String name, + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + required int order, + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? description, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? thumbnailAssetId, + Expression? isActivityEnabled, + Expression? order, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (description != null) 'description': description, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (thumbnailAssetId != null) 'thumbnail_asset_id': thumbnailAssetId, + if (isActivityEnabled != null) 'is_activity_enabled': isActivityEnabled, + if (order != null) 'order': order, + }); + } + + RemoteAlbumEntityCompanion copyWith( + {Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order}) { + return RemoteAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (thumbnailAssetId.present) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId.value); + } + if (isActivityEnabled.present) { + map['is_activity_enabled'] = Variable(isActivityEnabled.value); + } + if (order.present) { + map['order'] = Variable(order.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + RemoteAlbumAssetEntityData map(Map data, + {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, + albumId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + ); + } + + @override + RemoteAlbumAssetEntity createAlias(String alias) { + return RemoteAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const RemoteAlbumAssetEntityData( + {required this.assetId, required this.albumId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory RemoteAlbumAssetEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data) { + return RemoteAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const RemoteAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + RemoteAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + RemoteAlbumAssetEntityCompanion copyWith( + {Value? assetId, Value? albumId}) { + return RemoteAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumUserEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn role = GeneratedColumn( + 'role', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + @override + List get $columns => [albumId, userId, role]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_user_entity'; + @override + Set get $primaryKey => {albumId, userId}; + @override + RemoteAlbumUserEntityData map(Map data, + {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumUserEntityData( + albumId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + userId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, + role: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}role'])!, + ); + } + + @override + RemoteAlbumUserEntity createAlias(String alias) { + return RemoteAlbumUserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { + final String albumId; + final String userId; + final int role; + const RemoteAlbumUserEntityData( + {required this.albumId, required this.userId, required this.role}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['album_id'] = Variable(albumId); + map['user_id'] = Variable(userId); + map['role'] = Variable(role); + return map; + } + + factory RemoteAlbumUserEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumUserEntityData( + albumId: serializer.fromJson(json['albumId']), + userId: serializer.fromJson(json['userId']), + role: serializer.fromJson(json['role']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'albumId': serializer.toJson(albumId), + 'userId': serializer.toJson(userId), + 'role': serializer.toJson(role), + }; + } + + RemoteAlbumUserEntityData copyWith( + {String? albumId, String? userId, int? role}) => + RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data) { + return RemoteAlbumUserEntityData( + albumId: data.albumId.present ? data.albumId.value : this.albumId, + userId: data.userId.present ? data.userId.value : this.userId, + role: data.role.present ? data.role.value : this.role, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityData(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(albumId, userId, role); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumUserEntityData && + other.albumId == this.albumId && + other.userId == this.userId && + other.role == this.role); +} + +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { + final Value albumId; + final Value userId; + final Value role; + const RemoteAlbumUserEntityCompanion({ + this.albumId = const Value.absent(), + this.userId = const Value.absent(), + this.role = const Value.absent(), + }); + RemoteAlbumUserEntityCompanion.insert({ + required String albumId, + required String userId, + required int role, + }) : albumId = Value(albumId), + userId = Value(userId), + role = Value(role); + static Insertable custom({ + Expression? albumId, + Expression? userId, + Expression? role, + }) { + return RawValuesInsertable({ + if (albumId != null) 'album_id': albumId, + if (userId != null) 'user_id': userId, + if (role != null) 'role': role, + }); + } + + RemoteAlbumUserEntityCompanion copyWith( + {Value? albumId, Value? userId, Value? role}) { + return RemoteAlbumUserEntityCompanion( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (role.present) { + map['role'] = Variable(role.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityCompanion(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } +} + +class MemoryEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn type = GeneratedColumn( + 'type', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn data = GeneratedColumn( + 'data', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("is_saved" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', aliasedName, false, + type: DriftSqlType.dateTime, requiredDuringInsert: true); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_entity'; + @override + Set get $primaryKey => {id}; + @override + MemoryEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryEntityData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + deletedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), + ownerId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + type: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}type'])!, + data: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}data'])!, + isSaved: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}is_saved'])!, + memoryAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}memory_at'])!, + seenAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}seen_at']), + showAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}show_at']), + hideAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}hide_at']), + ); + } + + @override + MemoryEntity createAlias(String alias) { + return MemoryEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final DateTime? deletedAt; + final String ownerId; + final int type; + final String data; + final bool isSaved; + final DateTime memoryAt; + final DateTime? seenAt; + final DateTime? showAt; + final DateTime? hideAt; + const MemoryEntityData( + {required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + map['owner_id'] = Variable(ownerId); + map['type'] = Variable(type); + map['data'] = Variable(data); + map['is_saved'] = Variable(isSaved); + map['memory_at'] = Variable(memoryAt); + if (!nullToAbsent || seenAt != null) { + map['seen_at'] = Variable(seenAt); + } + if (!nullToAbsent || showAt != null) { + map['show_at'] = Variable(showAt); + } + if (!nullToAbsent || hideAt != null) { + map['hide_at'] = Variable(hideAt); + } + return map; + } + + factory MemoryEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + deletedAt: serializer.fromJson(json['deletedAt']), + ownerId: serializer.fromJson(json['ownerId']), + type: serializer.fromJson(json['type']), + data: serializer.fromJson(json['data']), + isSaved: serializer.fromJson(json['isSaved']), + memoryAt: serializer.fromJson(json['memoryAt']), + seenAt: serializer.fromJson(json['seenAt']), + showAt: serializer.fromJson(json['showAt']), + hideAt: serializer.fromJson(json['hideAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'deletedAt': serializer.toJson(deletedAt), + 'ownerId': serializer.toJson(ownerId), + 'type': serializer.toJson(type), + 'data': serializer.toJson(data), + 'isSaved': serializer.toJson(isSaved), + 'memoryAt': serializer.toJson(memoryAt), + 'seenAt': serializer.toJson(seenAt), + 'showAt': serializer.toJson(showAt), + 'hideAt': serializer.toJson(hideAt), + }; + } + + MemoryEntityData copyWith( + {String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent()}) => + MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); + MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { + return MemoryEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + type: data.type.present ? data.type.value : this.type, + data: data.data.present ? data.data.value : this.data, + isSaved: data.isSaved.present ? data.isSaved.value : this.isSaved, + memoryAt: data.memoryAt.present ? data.memoryAt.value : this.memoryAt, + seenAt: data.seenAt.present ? data.seenAt.value : this.seenAt, + showAt: data.showAt.present ? data.showAt.value : this.showAt, + hideAt: data.hideAt.present ? data.hideAt.value : this.hideAt, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, createdAt, updatedAt, deletedAt, ownerId, + type, data, isSaved, memoryAt, seenAt, showAt, hideAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.deletedAt == this.deletedAt && + other.ownerId == this.ownerId && + other.type == this.type && + other.data == this.data && + other.isSaved == this.isSaved && + other.memoryAt == this.memoryAt && + other.seenAt == this.seenAt && + other.showAt == this.showAt && + other.hideAt == this.hideAt); +} + +class MemoryEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value deletedAt; + final Value ownerId; + final Value type; + final Value data; + final Value isSaved; + final Value memoryAt; + final Value seenAt; + final Value showAt; + final Value hideAt; + const MemoryEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.type = const Value.absent(), + this.data = const Value.absent(), + this.isSaved = const Value.absent(), + this.memoryAt = const Value.absent(), + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }); + MemoryEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + required String ownerId, + required int type, + required String data, + this.isSaved = const Value.absent(), + required DateTime memoryAt, + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? deletedAt, + Expression? ownerId, + Expression? type, + Expression? data, + Expression? isSaved, + Expression? memoryAt, + Expression? seenAt, + Expression? showAt, + Expression? hideAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (deletedAt != null) 'deleted_at': deletedAt, + if (ownerId != null) 'owner_id': ownerId, + if (type != null) 'type': type, + if (data != null) 'data': data, + if (isSaved != null) 'is_saved': isSaved, + if (memoryAt != null) 'memory_at': memoryAt, + if (seenAt != null) 'seen_at': seenAt, + if (showAt != null) 'show_at': showAt, + if (hideAt != null) 'hide_at': hideAt, + }); + } + + MemoryEntityCompanion copyWith( + {Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt}) { + return MemoryEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt ?? this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt ?? this.seenAt, + showAt: showAt ?? this.showAt, + hideAt: hideAt ?? this.hideAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (data.present) { + map['data'] = Variable(data.value); + } + if (isSaved.present) { + map['is_saved'] = Variable(isSaved.value); + } + if (memoryAt.present) { + map['memory_at'] = Variable(memoryAt.value); + } + if (seenAt.present) { + map['seen_at'] = Variable(seenAt.value); + } + if (showAt.present) { + map['show_at'] = Variable(showAt.value); + } + if (hideAt.present) { + map['hide_at'] = Variable(hideAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } +} + +class MemoryAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE')); + @override + List get $columns => [assetId, memoryId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_asset_entity'; + @override + Set get $primaryKey => {assetId, memoryId}; + @override + MemoryAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryAssetEntityData( + assetId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, + memoryId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}memory_id'])!, + ); + } + + @override + MemoryAssetEntity createAlias(String alias) { + return MemoryAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String memoryId; + const MemoryAssetEntityData({required this.assetId, required this.memoryId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['memory_id'] = Variable(memoryId); + return map; + } + + factory MemoryAssetEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + memoryId: serializer.fromJson(json['memoryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'memoryId': serializer.toJson(memoryId), + }; + } + + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + MemoryAssetEntityData copyWithCompanion(MemoryAssetEntityCompanion data) { + return MemoryAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + memoryId: data.memoryId.present ? data.memoryId.value : this.memoryId, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, memoryId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); +} + +class MemoryAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value memoryId; + const MemoryAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.memoryId = const Value.absent(), + }); + MemoryAssetEntityCompanion.insert({ + required String assetId, + required String memoryId, + }) : assetId = Value(assetId), + memoryId = Value(memoryId); + static Insertable custom({ + Expression? assetId, + Expression? memoryId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (memoryId != null) 'memory_id': memoryId, + }); + } + + MemoryAssetEntityCompanion copyWith( + {Value? assetId, Value? memoryId}) { + return MemoryAssetEntityCompanion( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (memoryId.present) { + map['memory_id'] = Variable(memoryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } +} + +class PersonEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PersonEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn name = GeneratedColumn( + 'name', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))')); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("is_hidden" IN (0, 1))')); + late final GeneratedColumn color = GeneratedColumn( + 'color', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'person_entity'; + @override + Set get $primaryKey => {id}; + @override + PersonEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PersonEntityData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + ownerId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + name: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}name'])!, + faceAssetId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}face_asset_id']), + isFavorite: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, + isHidden: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}is_hidden'])!, + color: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}color']), + birthDate: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}birth_date']), + ); + } + + @override + PersonEntity createAlias(String alias) { + return PersonEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PersonEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + const PersonEntityData( + {required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['name'] = Variable(name); + if (!nullToAbsent || faceAssetId != null) { + map['face_asset_id'] = Variable(faceAssetId); + } + map['is_favorite'] = Variable(isFavorite); + map['is_hidden'] = Variable(isHidden); + if (!nullToAbsent || color != null) { + map['color'] = Variable(color); + } + if (!nullToAbsent || birthDate != null) { + map['birth_date'] = Variable(birthDate); + } + return map; + } + + factory PersonEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PersonEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + name: serializer.fromJson(json['name']), + faceAssetId: serializer.fromJson(json['faceAssetId']), + isFavorite: serializer.fromJson(json['isFavorite']), + isHidden: serializer.fromJson(json['isHidden']), + color: serializer.fromJson(json['color']), + birthDate: serializer.fromJson(json['birthDate']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'name': serializer.toJson(name), + 'faceAssetId': serializer.toJson(faceAssetId), + 'isFavorite': serializer.toJson(isFavorite), + 'isHidden': serializer.toJson(isHidden), + 'color': serializer.toJson(color), + 'birthDate': serializer.toJson(birthDate), + }; + } + + PersonEntityData copyWith( + {String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent()}) => + PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); + PersonEntityData copyWithCompanion(PersonEntityCompanion data) { + return PersonEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + name: data.name.present ? data.name.value : this.name, + faceAssetId: + data.faceAssetId.present ? data.faceAssetId.value : this.faceAssetId, + isFavorite: + data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + color: data.color.present ? data.color.value : this.color, + birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, + ); + } + + @override + String toString() { + return (StringBuffer('PersonEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, createdAt, updatedAt, ownerId, name, + faceAssetId, isFavorite, isHidden, color, birthDate); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PersonEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.name == this.name && + other.faceAssetId == this.faceAssetId && + other.isFavorite == this.isFavorite && + other.isHidden == this.isHidden && + other.color == this.color && + other.birthDate == this.birthDate); +} + +class PersonEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value name; + final Value faceAssetId; + final Value isFavorite; + final Value isHidden; + final Value color; + final Value birthDate; + const PersonEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.name = const Value.absent(), + this.faceAssetId = const Value.absent(), + this.isFavorite = const Value.absent(), + this.isHidden = const Value.absent(), + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }); + PersonEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String name, + this.faceAssetId = const Value.absent(), + required bool isFavorite, + required bool isHidden, + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? name, + Expression? faceAssetId, + Expression? isFavorite, + Expression? isHidden, + Expression? color, + Expression? birthDate, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (name != null) 'name': name, + if (faceAssetId != null) 'face_asset_id': faceAssetId, + if (isFavorite != null) 'is_favorite': isFavorite, + if (isHidden != null) 'is_hidden': isHidden, + if (color != null) 'color': color, + if (birthDate != null) 'birth_date': birthDate, + }); + } + + PersonEntityCompanion copyWith( + {Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate}) { + return PersonEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (faceAssetId.present) { + map['face_asset_id'] = Variable(faceAssetId.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (isHidden.present) { + map['is_hidden'] = Variable(isHidden.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (birthDate.present) { + map['birth_date'] = Variable(birthDate.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PersonEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } +} + +class AssetFaceEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + AssetFaceEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn personId = GeneratedColumn( + 'person_id', aliasedName, true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL')); + late final GeneratedColumn imageWidth = GeneratedColumn( + 'image_width', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn imageHeight = GeneratedColumn( + 'image_height', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn boundingBoxX1 = GeneratedColumn( + 'bounding_box_x1', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn boundingBoxY1 = GeneratedColumn( + 'bounding_box_y1', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn boundingBoxX2 = GeneratedColumn( + 'bounding_box_x2', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn boundingBoxY2 = GeneratedColumn( + 'bounding_box_y2', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn sourceType = GeneratedColumn( + 'source_type', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + @override + List get $columns => [ + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'asset_face_entity'; + @override + Set get $primaryKey => {id}; + @override + AssetFaceEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return AssetFaceEntityData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + assetId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, + personId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}person_id']), + imageWidth: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}image_width'])!, + imageHeight: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}image_height'])!, + boundingBoxX1: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}bounding_box_x1'])!, + boundingBoxY1: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}bounding_box_y1'])!, + boundingBoxX2: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}bounding_box_x2'])!, + boundingBoxY2: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}bounding_box_y2'])!, + sourceType: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}source_type'])!, + ); + } + + @override + AssetFaceEntity createAlias(String alias) { + return AssetFaceEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AssetFaceEntityData extends DataClass + implements Insertable { + final String id; + final String assetId; + final String? personId; + final int imageWidth; + final int imageHeight; + final int boundingBoxX1; + final int boundingBoxY1; + final int boundingBoxX2; + final int boundingBoxY2; + final String sourceType; + const AssetFaceEntityData( + {required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || personId != null) { + map['person_id'] = Variable(personId); + } + map['image_width'] = Variable(imageWidth); + map['image_height'] = Variable(imageHeight); + map['bounding_box_x1'] = Variable(boundingBoxX1); + map['bounding_box_y1'] = Variable(boundingBoxY1); + map['bounding_box_x2'] = Variable(boundingBoxX2); + map['bounding_box_y2'] = Variable(boundingBoxY2); + map['source_type'] = Variable(sourceType); + return map; + } + + factory AssetFaceEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return AssetFaceEntityData( + id: serializer.fromJson(json['id']), + assetId: serializer.fromJson(json['assetId']), + personId: serializer.fromJson(json['personId']), + imageWidth: serializer.fromJson(json['imageWidth']), + imageHeight: serializer.fromJson(json['imageHeight']), + boundingBoxX1: serializer.fromJson(json['boundingBoxX1']), + boundingBoxY1: serializer.fromJson(json['boundingBoxY1']), + boundingBoxX2: serializer.fromJson(json['boundingBoxX2']), + boundingBoxY2: serializer.fromJson(json['boundingBoxY2']), + sourceType: serializer.fromJson(json['sourceType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'assetId': serializer.toJson(assetId), + 'personId': serializer.toJson(personId), + 'imageWidth': serializer.toJson(imageWidth), + 'imageHeight': serializer.toJson(imageHeight), + 'boundingBoxX1': serializer.toJson(boundingBoxX1), + 'boundingBoxY1': serializer.toJson(boundingBoxY1), + 'boundingBoxX2': serializer.toJson(boundingBoxX2), + 'boundingBoxY2': serializer.toJson(boundingBoxY2), + 'sourceType': serializer.toJson(sourceType), + }; + } + + AssetFaceEntityData copyWith( + {String? id, + String? assetId, + Value personId = const Value.absent(), + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType}) => + AssetFaceEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId.present ? personId.value : this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + AssetFaceEntityData copyWithCompanion(AssetFaceEntityCompanion data) { + return AssetFaceEntityData( + id: data.id.present ? data.id.value : this.id, + assetId: data.assetId.present ? data.assetId.value : this.assetId, + personId: data.personId.present ? data.personId.value : this.personId, + imageWidth: + data.imageWidth.present ? data.imageWidth.value : this.imageWidth, + imageHeight: + data.imageHeight.present ? data.imageHeight.value : this.imageHeight, + boundingBoxX1: data.boundingBoxX1.present + ? data.boundingBoxX1.value + : this.boundingBoxX1, + boundingBoxY1: data.boundingBoxY1.present + ? data.boundingBoxY1.value + : this.boundingBoxY1, + boundingBoxX2: data.boundingBoxX2.present + ? data.boundingBoxX2.value + : this.boundingBoxX2, + boundingBoxY2: data.boundingBoxY2.present + ? data.boundingBoxY2.value + : this.boundingBoxY2, + sourceType: + data.sourceType.present ? data.sourceType.value : this.sourceType, + ); + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityData(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is AssetFaceEntityData && + other.id == this.id && + other.assetId == this.assetId && + other.personId == this.personId && + other.imageWidth == this.imageWidth && + other.imageHeight == this.imageHeight && + other.boundingBoxX1 == this.boundingBoxX1 && + other.boundingBoxY1 == this.boundingBoxY1 && + other.boundingBoxX2 == this.boundingBoxX2 && + other.boundingBoxY2 == this.boundingBoxY2 && + other.sourceType == this.sourceType); +} + +class AssetFaceEntityCompanion extends UpdateCompanion { + final Value id; + final Value assetId; + final Value personId; + final Value imageWidth; + final Value imageHeight; + final Value boundingBoxX1; + final Value boundingBoxY1; + final Value boundingBoxX2; + final Value boundingBoxY2; + final Value sourceType; + const AssetFaceEntityCompanion({ + this.id = const Value.absent(), + this.assetId = const Value.absent(), + this.personId = const Value.absent(), + this.imageWidth = const Value.absent(), + this.imageHeight = const Value.absent(), + this.boundingBoxX1 = const Value.absent(), + this.boundingBoxY1 = const Value.absent(), + this.boundingBoxX2 = const Value.absent(), + this.boundingBoxY2 = const Value.absent(), + this.sourceType = const Value.absent(), + }); + AssetFaceEntityCompanion.insert({ + required String id, + required String assetId, + this.personId = const Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) : id = Value(id), + assetId = Value(assetId), + imageWidth = Value(imageWidth), + imageHeight = Value(imageHeight), + boundingBoxX1 = Value(boundingBoxX1), + boundingBoxY1 = Value(boundingBoxY1), + boundingBoxX2 = Value(boundingBoxX2), + boundingBoxY2 = Value(boundingBoxY2), + sourceType = Value(sourceType); + static Insertable custom({ + Expression? id, + Expression? assetId, + Expression? personId, + Expression? imageWidth, + Expression? imageHeight, + Expression? boundingBoxX1, + Expression? boundingBoxY1, + Expression? boundingBoxX2, + Expression? boundingBoxY2, + Expression? sourceType, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (assetId != null) 'asset_id': assetId, + if (personId != null) 'person_id': personId, + if (imageWidth != null) 'image_width': imageWidth, + if (imageHeight != null) 'image_height': imageHeight, + if (boundingBoxX1 != null) 'bounding_box_x1': boundingBoxX1, + if (boundingBoxY1 != null) 'bounding_box_y1': boundingBoxY1, + if (boundingBoxX2 != null) 'bounding_box_x2': boundingBoxX2, + if (boundingBoxY2 != null) 'bounding_box_y2': boundingBoxY2, + if (sourceType != null) 'source_type': sourceType, + }); + } + + AssetFaceEntityCompanion copyWith( + {Value? id, + Value? assetId, + Value? personId, + Value? imageWidth, + Value? imageHeight, + Value? boundingBoxX1, + Value? boundingBoxY1, + Value? boundingBoxX2, + Value? boundingBoxY2, + Value? sourceType}) { + return AssetFaceEntityCompanion( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId ?? this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (personId.present) { + map['person_id'] = Variable(personId.value); + } + if (imageWidth.present) { + map['image_width'] = Variable(imageWidth.value); + } + if (imageHeight.present) { + map['image_height'] = Variable(imageHeight.value); + } + if (boundingBoxX1.present) { + map['bounding_box_x1'] = Variable(boundingBoxX1.value); + } + if (boundingBoxY1.present) { + map['bounding_box_y1'] = Variable(boundingBoxY1.value); + } + if (boundingBoxX2.present) { + map['bounding_box_x2'] = Variable(boundingBoxX2.value); + } + if (boundingBoxY2.present) { + map['bounding_box_y2'] = Variable(boundingBoxY2.value); + } + if (sourceType.present) { + map['source_type'] = Variable(sourceType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityCompanion(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV4 extends GeneratedDatabase { + DatabaseAtV4(QueryExecutor e) : super(e); + late final UserEntity userEntity = UserEntity(this); + late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); + late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); + late final StackEntity stackEntity = StackEntity(this); + late final Index idxLocalAssetChecksum = Index('idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); + late final Index uQRemoteAssetOwnerChecksum = Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); + late final Index idxRemoteAssetChecksum = Index('idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); + late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); + late final PartnerEntity partnerEntity = PartnerEntity(this); + late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); + late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); + late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); + late final MemoryEntity memoryEntity = MemoryEntity(this); + late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); + late final PersonEntity personEntity = PersonEntity(this); + late final AssetFaceEntity assetFaceEntity = AssetFaceEntity(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + userEntity, + remoteAssetEntity, + localAssetEntity, + stackEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + localAlbumEntity, + localAlbumAssetEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity + ]; + @override + int get schemaVersion => 4; + @override + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); +} From 250548dea67c0e21196a65a766e54069690d00c9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 17:42:07 +0000 Subject: [PATCH 014/748] fix(deps): update typescript-projects (#19939) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Zack Pollard Co-authored-by: Daniel Dietzler --- cli/package-lock.json | 161 ++-- e2e/package-lock.json | 416 +++++----- server/package-lock.json | 16 +- server/package.json | 2 +- web/package-lock.json | 731 ++++++++++-------- web/package.json | 10 +- .../settings/setting-accordion-state.svelte | 3 +- .../side-bar/purchase-info.svelte | 3 +- .../timeline-manager/day-group.svelte.ts | 7 +- .../group-insertion-cache.svelte.ts | 9 +- .../internal/operations-support.svelte.ts | 11 +- .../timeline-manager/month-group.svelte.ts | 7 +- .../timeline-manager.svelte.ts | 12 +- web/src/lib/modals/ApiKeyModal.svelte | 3 +- web/src/lib/utils/timeline-util.ts | 5 +- 15 files changed, 783 insertions(+), 613 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index 0d3db4cffa..a575b630cc 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -682,9 +682,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz", - "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", + "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", "dev": true, "license": "MIT", "engines": { @@ -1365,17 +1365,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.36.0.tgz", - "integrity": "sha512-lZNihHUVB6ZZiPBNgOQGSxUASI7UJWhT8nHyUGCnaQ28XFCw98IfrMCG3rUl1uwUWoAvodJQby2KTs79UTcrAg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", + "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/type-utils": "8.36.0", - "@typescript-eslint/utils": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/type-utils": "8.37.0", + "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -1389,7 +1389,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.36.0", + "@typescript-eslint/parser": "^8.37.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } @@ -1405,16 +1405,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.36.0.tgz", - "integrity": "sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", + "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4" }, "engines": { @@ -1430,14 +1430,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", - "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", + "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.36.0", - "@typescript-eslint/types": "^8.36.0", + "@typescript-eslint/tsconfig-utils": "^8.37.0", + "@typescript-eslint/types": "^8.37.0", "debug": "^4.3.4" }, "engines": { @@ -1452,14 +1452,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", - "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", + "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0" + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1470,9 +1470,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", - "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", + "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", "dev": true, "license": "MIT", "engines": { @@ -1487,14 +1487,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.36.0.tgz", - "integrity": "sha512-5aaGYG8cVDd6cxfk/ynpYzxBRZJk7w/ymto6uiyUFtdCozQIsQWh7M28/6r57Fwkbweng8qAzoMCPwSJfWlmsg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", + "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/utils": "8.36.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/utils": "8.37.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -1511,9 +1512,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", - "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", + "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", "dev": true, "license": "MIT", "engines": { @@ -1525,16 +1526,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", - "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", + "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.36.0", - "@typescript-eslint/tsconfig-utils": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/project-service": "8.37.0", + "@typescript-eslint/tsconfig-utils": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1580,16 +1581,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz", - "integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", + "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0" + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1604,13 +1605,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", - "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", + "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", + "@typescript-eslint/types": "8.37.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -2305,9 +2306,9 @@ } }, "node_modules/eslint": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.1.tgz", - "integrity": "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", + "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2315,9 +2316,9 @@ "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.14.0", + "@eslint/core": "^0.15.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.30.1", + "@eslint/js": "9.31.0", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -2504,6 +2505,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/@eslint/core": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/espree": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", @@ -4125,15 +4139,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.36.0.tgz", - "integrity": "sha512-fTCqxthY+h9QbEgSIBfL9iV6CvKDFuoxg6bHPNpJ9HIUzS+jy2lCEyCmGyZRWEBSaykqcDPf1SJ+BfCI8DRopA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.37.0.tgz", + "integrity": "sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.36.0", - "@typescript-eslint/parser": "8.36.0", - "@typescript-eslint/utils": "8.36.0" + "@typescript-eslint/eslint-plugin": "8.37.0", + "@typescript-eslint/parser": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/utils": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4196,9 +4211,9 @@ } }, "node_modules/vite": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.4.tgz", - "integrity": "sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.5.tgz", + "integrity": "sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw==", "dev": true, "license": "MIT", "dependencies": { @@ -4329,9 +4344,9 @@ } }, "node_modules/vite/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 1884b22dd5..3ff7670739 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -181,9 +181,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", - "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz", + "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==", "dev": true, "license": "MIT", "optional": true, @@ -734,9 +734,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz", - "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", + "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", "dev": true, "license": "MIT", "engines": { @@ -837,9 +837,9 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.2.tgz", - "integrity": "sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.3.tgz", + "integrity": "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==", "cpu": [ "arm64" ], @@ -856,13 +856,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.1.0" + "@img/sharp-libvips-darwin-arm64": "1.2.0" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.2.tgz", - "integrity": "sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.3.tgz", + "integrity": "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==", "cpu": [ "x64" ], @@ -879,13 +879,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.1.0" + "@img/sharp-libvips-darwin-x64": "1.2.0" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.1.0.tgz", - "integrity": "sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.0.tgz", + "integrity": "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==", "cpu": [ "arm64" ], @@ -900,9 +900,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.1.0.tgz", - "integrity": "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.0.tgz", + "integrity": "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==", "cpu": [ "x64" ], @@ -917,9 +917,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.1.0.tgz", - "integrity": "sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.0.tgz", + "integrity": "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==", "cpu": [ "arm" ], @@ -934,9 +934,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.1.0.tgz", - "integrity": "sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.0.tgz", + "integrity": "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==", "cpu": [ "arm64" ], @@ -951,9 +951,9 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.1.0.tgz", - "integrity": "sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.0.tgz", + "integrity": "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==", "cpu": [ "ppc64" ], @@ -968,9 +968,9 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.1.0.tgz", - "integrity": "sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.0.tgz", + "integrity": "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==", "cpu": [ "s390x" ], @@ -985,9 +985,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.1.0.tgz", - "integrity": "sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.0.tgz", + "integrity": "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==", "cpu": [ "x64" ], @@ -1002,9 +1002,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.1.0.tgz", - "integrity": "sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.0.tgz", + "integrity": "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==", "cpu": [ "arm64" ], @@ -1019,9 +1019,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.1.0.tgz", - "integrity": "sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.0.tgz", + "integrity": "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==", "cpu": [ "x64" ], @@ -1036,9 +1036,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.2.tgz", - "integrity": "sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.3.tgz", + "integrity": "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==", "cpu": [ "arm" ], @@ -1055,13 +1055,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.1.0" + "@img/sharp-libvips-linux-arm": "1.2.0" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.2.tgz", - "integrity": "sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.3.tgz", + "integrity": "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==", "cpu": [ "arm64" ], @@ -1078,13 +1078,36 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.1.0" + "@img/sharp-libvips-linux-arm64": "1.2.0" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.3.tgz", + "integrity": "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.0" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.2.tgz", - "integrity": "sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.3.tgz", + "integrity": "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==", "cpu": [ "s390x" ], @@ -1101,13 +1124,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.1.0" + "@img/sharp-libvips-linux-s390x": "1.2.0" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.2.tgz", - "integrity": "sha512-sD7J+h5nFLMMmOXYH4DD9UtSNBD05tWSSdWAcEyzqW8Cn5UxXvsHAxmxSesYUsTOBmUnjtxghKDl15EvfqLFbQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.3.tgz", + "integrity": "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==", "cpu": [ "x64" ], @@ -1124,13 +1147,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.1.0" + "@img/sharp-libvips-linux-x64": "1.2.0" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.2.tgz", - "integrity": "sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.3.tgz", + "integrity": "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==", "cpu": [ "arm64" ], @@ -1147,13 +1170,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.1.0" + "@img/sharp-libvips-linuxmusl-arm64": "1.2.0" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.2.tgz", - "integrity": "sha512-DOYMrDm5E6/8bm/yQLCWyuDJwUnlevR8xtF8bs+gjZ7cyUNYXiSf/E8Kp0Ss5xasIaXSHzb888V1BE4i1hFhAA==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.3.tgz", + "integrity": "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==", "cpu": [ "x64" ], @@ -1170,13 +1193,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.1.0" + "@img/sharp-libvips-linuxmusl-x64": "1.2.0" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.2.tgz", - "integrity": "sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.3.tgz", + "integrity": "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==", "cpu": [ "wasm32" ], @@ -1184,7 +1207,7 @@ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.4.3" + "@emnapi/runtime": "^1.4.4" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -1194,9 +1217,9 @@ } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.2.tgz", - "integrity": "sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.3.tgz", + "integrity": "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==", "cpu": [ "arm64" ], @@ -1214,9 +1237,9 @@ } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.2.tgz", - "integrity": "sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.3.tgz", + "integrity": "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==", "cpu": [ "ia32" ], @@ -1234,9 +1257,9 @@ } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.2.tgz", - "integrity": "sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.3.tgz", + "integrity": "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==", "cpu": [ "x64" ], @@ -1356,12 +1379,13 @@ } }, "node_modules/@koa/router": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/@koa/router/-/router-13.1.0.tgz", - "integrity": "sha512-mNVu1nvkpSd8Q8gMebGbCkDWJ51ODetrFvLKYusej+V0ByD4btqHYnPIzTBLXnQMVUlm/oxVwqmWBY3zQfZilw==", + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/@koa/router/-/router-13.1.1.tgz", + "integrity": "sha512-JQEuMANYRVHs7lm7KY9PCIjkgJk73h4m4J+g2mkw2Vo1ugPZ17UJVqEH8F+HeAdjKz5do1OaLe7ArDz+z308gw==", "dev": true, "license": "MIT", "dependencies": { + "debug": "^4.4.1", "http-errors": "^2.0.0", "koa-compose": "^4.1.0", "path-to-regexp": "^6.3.0" @@ -1510,13 +1534,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.53.2", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.53.2.tgz", - "integrity": "sha512-tEB2U5z74ebBeyfGNZ3Jfg29AnW+5HlWhvHtb/Mqco9pFdZU1ZLNdVb2UtB5CvmiilNr2ZfVH/qMmAROG/XTzw==", + "version": "1.54.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.54.1.tgz", + "integrity": "sha512-FS8hQ12acieG2dYSksmLOF7BNxnVf2afRJdCuM1eMSxj6QTSE6G4InGF7oApGgDb65MX7AwMVlIkpru0yZA4Xw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.53.2" + "playwright": "1.54.1" }, "bin": { "playwright": "cli.js" @@ -2101,17 +2125,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.36.0.tgz", - "integrity": "sha512-lZNihHUVB6ZZiPBNgOQGSxUASI7UJWhT8nHyUGCnaQ28XFCw98IfrMCG3rUl1uwUWoAvodJQby2KTs79UTcrAg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", + "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/type-utils": "8.36.0", - "@typescript-eslint/utils": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/type-utils": "8.37.0", + "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -2125,7 +2149,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.36.0", + "@typescript-eslint/parser": "^8.37.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } @@ -2141,16 +2165,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.36.0.tgz", - "integrity": "sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", + "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4" }, "engines": { @@ -2166,14 +2190,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", - "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", + "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.36.0", - "@typescript-eslint/types": "^8.36.0", + "@typescript-eslint/tsconfig-utils": "^8.37.0", + "@typescript-eslint/types": "^8.37.0", "debug": "^4.3.4" }, "engines": { @@ -2188,14 +2212,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", - "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", + "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0" + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2206,9 +2230,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", - "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", + "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", "dev": true, "license": "MIT", "engines": { @@ -2223,14 +2247,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.36.0.tgz", - "integrity": "sha512-5aaGYG8cVDd6cxfk/ynpYzxBRZJk7w/ymto6uiyUFtdCozQIsQWh7M28/6r57Fwkbweng8qAzoMCPwSJfWlmsg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", + "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/utils": "8.36.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/utils": "8.37.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -2247,9 +2272,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", - "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", + "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", "dev": true, "license": "MIT", "engines": { @@ -2261,16 +2286,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", - "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", + "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.36.0", - "@typescript-eslint/tsconfig-utils": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/project-service": "8.37.0", + "@typescript-eslint/tsconfig-utils": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -2316,16 +2341,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz", - "integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", + "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0" + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2340,13 +2365,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", - "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", + "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", + "@typescript-eslint/types": "8.37.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -3439,9 +3464,9 @@ } }, "node_modules/eslint": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.1.tgz", - "integrity": "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", + "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3449,9 +3474,9 @@ "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.14.0", + "@eslint/core": "^0.15.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.30.1", + "@eslint/js": "9.31.0", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -3638,6 +3663,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/@eslint/core": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/espree": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", @@ -5154,17 +5192,17 @@ } }, "node_modules/oidc-provider": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/oidc-provider/-/oidc-provider-9.2.0.tgz", - "integrity": "sha512-L0JL1ymI/hLKzDRqYzhKluNfRRQUR0++q5fTTziniKmJgNrJ6DnI5h5SP6w8Z0U/3wZrCndpVmbbu0VpKpY0CA==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/oidc-provider/-/oidc-provider-9.3.0.tgz", + "integrity": "sha512-JVocwYM+Fs76nOCED2hMf3iMfrzhN4jISmCYVuFhBEnZiFk3QlODzQXkO1XS/Spw8VwRlKxwIl3otkiintFIjw==", "dev": true, "license": "MIT", "dependencies": { "@koa/cors": "^5.0.0", - "@koa/router": "^13.1.0", + "@koa/router": "^13.1.1", "debug": "^4.4.1", "eta": "^3.5.0", - "jose": "^6.0.11", + "jose": "^6.0.12", "jsesc": "^3.1.0", "koa": "^3.0.0", "nanoid": "^5.1.5", @@ -5177,9 +5215,9 @@ } }, "node_modules/oidc-provider/node_modules/jose": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.0.11.tgz", - "integrity": "sha512-QxG7EaliDARm1O1S8BGakqncGT9s25bKL1WSf6/oa17Tkqwi8D2ZNglqCF+DsYF88/rV66Q/Q2mFAy697E1DUg==", + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.0.12.tgz", + "integrity": "sha512-T8xypXs8CpmiIi78k0E+Lk7T2zlK4zDyg+o1CZ4AkOHgDg98ogdP2BeZ61lTFKFyoEwJ9RgAgN+SdM3iPgNonQ==", "dev": true, "license": "MIT", "funding": { @@ -5488,13 +5526,13 @@ } }, "node_modules/playwright": { - "version": "1.53.2", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.53.2.tgz", - "integrity": "sha512-6K/qQxVFuVQhRQhFsVZ9fGeatxirtrpPgxzBYWyZLEXJzqYwuL4fuNmfOfD5et1tJE4GScKyPNeLhZeRwuTU3A==", + "version": "1.54.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.54.1.tgz", + "integrity": "sha512-peWpSwIBmSLi6aW2auvrUtf2DqY16YYcCMO8rTVx486jKmDTJg7UAhyrraP98GB8BoPURZP8+nxO7TSd4cPr5g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.53.2" + "playwright-core": "1.54.1" }, "bin": { "playwright": "cli.js" @@ -5507,9 +5545,9 @@ } }, "node_modules/playwright-core": { - "version": "1.53.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.53.2.tgz", - "integrity": "sha512-ox/OytMy+2w1jcYEYlOo1Hhp8hZkLCximMTUTMBXjGUA1KoFfiSZ+DU+3a739jsPY0yoKH2TFy9S2fsJas8yAw==", + "version": "1.54.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.54.1.tgz", + "integrity": "sha512-Nbjs2zjj0htNhzgiy5wu+3w09YetDx5pkrpI/kZotDlDUaYk0HVA5xrBVPdow4SAUIlhgKcJeJg4GRKW6xHusA==", "dev": true, "license": "Apache-2.0", "bin": { @@ -5993,9 +6031,9 @@ "license": "ISC" }, "node_modules/sharp": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.2.tgz", - "integrity": "sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz", + "integrity": "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -6011,27 +6049,28 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.2", - "@img/sharp-darwin-x64": "0.34.2", - "@img/sharp-libvips-darwin-arm64": "1.1.0", - "@img/sharp-libvips-darwin-x64": "1.1.0", - "@img/sharp-libvips-linux-arm": "1.1.0", - "@img/sharp-libvips-linux-arm64": "1.1.0", - "@img/sharp-libvips-linux-ppc64": "1.1.0", - "@img/sharp-libvips-linux-s390x": "1.1.0", - "@img/sharp-libvips-linux-x64": "1.1.0", - "@img/sharp-libvips-linuxmusl-arm64": "1.1.0", - "@img/sharp-libvips-linuxmusl-x64": "1.1.0", - "@img/sharp-linux-arm": "0.34.2", - "@img/sharp-linux-arm64": "0.34.2", - "@img/sharp-linux-s390x": "0.34.2", - "@img/sharp-linux-x64": "0.34.2", - "@img/sharp-linuxmusl-arm64": "0.34.2", - "@img/sharp-linuxmusl-x64": "0.34.2", - "@img/sharp-wasm32": "0.34.2", - "@img/sharp-win32-arm64": "0.34.2", - "@img/sharp-win32-ia32": "0.34.2", - "@img/sharp-win32-x64": "0.34.2" + "@img/sharp-darwin-arm64": "0.34.3", + "@img/sharp-darwin-x64": "0.34.3", + "@img/sharp-libvips-darwin-arm64": "1.2.0", + "@img/sharp-libvips-darwin-x64": "1.2.0", + "@img/sharp-libvips-linux-arm": "1.2.0", + "@img/sharp-libvips-linux-arm64": "1.2.0", + "@img/sharp-libvips-linux-ppc64": "1.2.0", + "@img/sharp-libvips-linux-s390x": "1.2.0", + "@img/sharp-libvips-linux-x64": "1.2.0", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", + "@img/sharp-libvips-linuxmusl-x64": "1.2.0", + "@img/sharp-linux-arm": "0.34.3", + "@img/sharp-linux-arm64": "0.34.3", + "@img/sharp-linux-ppc64": "0.34.3", + "@img/sharp-linux-s390x": "0.34.3", + "@img/sharp-linux-x64": "0.34.3", + "@img/sharp-linuxmusl-arm64": "0.34.3", + "@img/sharp-linuxmusl-x64": "0.34.3", + "@img/sharp-wasm32": "0.34.3", + "@img/sharp-win32-arm64": "0.34.3", + "@img/sharp-win32-ia32": "0.34.3", + "@img/sharp-win32-x64": "0.34.3" } }, "node_modules/shebang-command": { @@ -6778,15 +6817,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.36.0.tgz", - "integrity": "sha512-fTCqxthY+h9QbEgSIBfL9iV6CvKDFuoxg6bHPNpJ9HIUzS+jy2lCEyCmGyZRWEBSaykqcDPf1SJ+BfCI8DRopA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.37.0.tgz", + "integrity": "sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.36.0", - "@typescript-eslint/parser": "8.36.0", - "@typescript-eslint/utils": "8.36.0" + "@typescript-eslint/eslint-plugin": "8.37.0", + "@typescript-eslint/parser": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/utils": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/server/package-lock.json b/server/package-lock.json index ca758d97e4..207776873e 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -30,7 +30,7 @@ "@opentelemetry/sdk-metrics": "^2.0.1", "@opentelemetry/sdk-node": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.34.0", - "@react-email/components": "^0.2.0", + "@react-email/components": "^0.3.0", "@react-email/render": "^1.1.2", "@socket.io/redis-adapter": "^8.3.0", "archiver": "^7.0.0", @@ -6031,9 +6031,9 @@ } }, "node_modules/@react-email/components": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@react-email/components/-/components-0.2.0.tgz", - "integrity": "sha512-y45D+oYDgvL1fuFnauwUk8MwT54l0hWwnUAzzP0bVuwhsmVJFelKOGGMCRch0pcgyINilVlAEk0Xjtcu0Su4cw==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@react-email/components/-/components-0.3.2.tgz", + "integrity": "sha512-nVbo0KtBdZbj19lvfFpe0ZhjKPh6LE229+NyQLuTDt6dfaLzNRpSu/rHP+jlvdWBAk93slsoGyWDRldbqklpaA==", "license": "MIT", "dependencies": { "@react-email/body": "0.0.11", @@ -6054,7 +6054,7 @@ "@react-email/render": "1.1.3", "@react-email/row": "0.0.12", "@react-email/section": "0.0.16", - "@react-email/tailwind": "1.1.0", + "@react-email/tailwind": "1.2.2", "@react-email/text": "0.1.5" }, "engines": { @@ -6227,9 +6227,9 @@ } }, "node_modules/@react-email/tailwind": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@react-email/tailwind/-/tailwind-1.1.0.tgz", - "integrity": "sha512-m4sh5d1c8P9TPA6Ea8qHrboE5s9PmRQREIreYMn1l5ca0pCV/UBEY15e1RgoaseAzy2cy+gwI+nKhMwqUJsD1g==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@react-email/tailwind/-/tailwind-1.2.2.tgz", + "integrity": "sha512-heO9Khaqxm6Ulm6p7HQ9h01oiiLRrZuuEQuYds/O7Iyp3c58sMVHZGIxiRXO/kSs857NZQycpjewEVKF3jhNTw==", "license": "MIT", "engines": { "node": ">=18.0.0" diff --git a/server/package.json b/server/package.json index 86abe8979c..d461f1d767 100644 --- a/server/package.json +++ b/server/package.json @@ -55,7 +55,7 @@ "@opentelemetry/sdk-metrics": "^2.0.1", "@opentelemetry/sdk-node": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.34.0", - "@react-email/components": "^0.2.0", + "@react-email/components": "^0.3.0", "@react-email/render": "^1.1.2", "@socket.io/redis-adapter": "^8.3.0", "archiver": "^7.0.0", diff --git a/web/package-lock.json b/web/package-lock.json index c1147447de..1d4d3bdb34 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -51,9 +51,9 @@ "@koddsson/eslint-plugin-tscompat": "^0.2.0", "@socket.io/component-emitter": "^3.1.0", "@sveltejs/adapter-static": "^3.0.8", - "@sveltejs/enhanced-img": "^0.6.0", - "@sveltejs/kit": "^2.15.2", - "@sveltejs/vite-plugin-svelte": "^6.0.0", + "@sveltejs/enhanced-img": "^0.7.0", + "@sveltejs/kit": "^2.25.0", + "@sveltejs/vite-plugin-svelte": "6.1.0", "@tailwindcss/vite": "^4.1.7", "@testing-library/jest-dom": "^6.4.2", "@testing-library/svelte": "^5.2.8", @@ -81,14 +81,14 @@ "prettier-plugin-sort-json": "^4.1.1", "prettier-plugin-svelte": "^3.3.3", "rollup-plugin-visualizer": "^6.0.0", - "svelte": "^5.25.3", + "svelte": "5.35.5", "svelte-check": "^4.1.5", "svelte-eslint-parser": "^1.2.0", "tailwindcss": "^4.1.7", "tslib": "^2.6.2", "typescript": "^5.7.3", "typescript-eslint": "^8.28.0", - "vite": "^7.0.0", + "vite": "^7.0.5", "vitest": "^3.0.0" } }, @@ -496,9 +496,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", - "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz", + "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", "cpu": [ "arm64" ], @@ -529,9 +529,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", - "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz", + "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", "cpu": [ "arm64" ], @@ -561,6 +561,23 @@ "node": ">=12" } }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz", + "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/sunos-x64": { "version": "0.19.12", "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", @@ -667,6 +684,31 @@ "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", + "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/core": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", @@ -718,9 +760,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz", - "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", + "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", "dev": true, "license": "MIT", "engines": { @@ -2142,9 +2184,9 @@ } }, "node_modules/@sveltejs/enhanced-img": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@sveltejs/enhanced-img/-/enhanced-img-0.6.1.tgz", - "integrity": "sha512-8oj/cXc/M1soGQOkkkuEzeaiE/LTa3MJnoRwoRzG7GOPKHOfNRJDzsCcx3s1GqxQlcoHc4BJK3HoU1m0OV9UMA==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@sveltejs/enhanced-img/-/enhanced-img-0.7.0.tgz", + "integrity": "sha512-nvWETsp2IsUoRlt2dFg8swJ8BXmEAZCmOTpIrJ+SGGf54V+rYQ01IbIOPNkmzlejz0Tr0PmslzwMunie8eKU7A==", "dev": true, "license": "MIT", "dependencies": { @@ -2155,15 +2197,15 @@ "zimmerframe": "^1.1.2" }, "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^5.0.0 || ^6.0.0-next.0", + "@sveltejs/vite-plugin-svelte": "^6.0.0", "svelte": "^5.0.0", - "vite": ">= 5.0.0" + "vite": "^6.3.0 || >=7.0.0" } }, "node_modules/@sveltejs/kit": { - "version": "2.22.4", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.22.4.tgz", - "integrity": "sha512-BXK9hTbP8AeQIfoz6+P3uoyVYStVHc5CIKqoTSF7hXm3Q5P9BwFMdEus4jsQuhaYmXGHzukcGlxe2QrsE8BJfQ==", + "version": "2.25.1", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.25.1.tgz", + "integrity": "sha512-8H+fxDEp7Xq6tLFdrGdS5fLu6ONDQQ9DgyjboXpChubuFdfH9QoFX09ypssBpyNkJNZFt9eW3yLmXIc9CesPCA==", "dev": true, "license": "MIT", "dependencies": { @@ -2193,9 +2235,9 @@ } }, "node_modules/@sveltejs/vite-plugin-svelte": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.0.0.tgz", - "integrity": "sha512-mma5GJ23pYiWpTNbN//g9XI3Hfob3aAlXPP42qRtvjgTAU6pfJyLyNPTdLjFuj+jfC9JslP4J3AkeiJNhjtLLA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.1.0.tgz", + "integrity": "sha512-+U6lz1wvGEG/BvQyL4z/flyNdQ9xDNv5vrh+vWBWTHaebqT0c9RNggpZTo/XSPoHsSCWBlYaTlRX8pZ9GATXCw==", "dev": true, "license": "MIT", "dependencies": { @@ -2919,17 +2961,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.36.0.tgz", - "integrity": "sha512-lZNihHUVB6ZZiPBNgOQGSxUASI7UJWhT8nHyUGCnaQ28XFCw98IfrMCG3rUl1uwUWoAvodJQby2KTs79UTcrAg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", + "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/type-utils": "8.36.0", - "@typescript-eslint/utils": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/type-utils": "8.37.0", + "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -2943,20 +2985,20 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.36.0", + "@typescript-eslint/parser": "^8.37.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/project-service": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", - "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", + "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.36.0", - "@typescript-eslint/types": "^8.36.0", + "@typescript-eslint/tsconfig-utils": "^8.37.0", + "@typescript-eslint/types": "^8.37.0", "debug": "^4.3.4" }, "engines": { @@ -2971,14 +3013,14 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", - "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", + "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0" + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2989,9 +3031,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", - "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", + "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", "dev": true, "license": "MIT", "engines": { @@ -3006,14 +3048,15 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.36.0.tgz", - "integrity": "sha512-5aaGYG8cVDd6cxfk/ynpYzxBRZJk7w/ymto6uiyUFtdCozQIsQWh7M28/6r57Fwkbweng8qAzoMCPwSJfWlmsg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", + "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/utils": "8.36.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/utils": "8.37.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -3030,9 +3073,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", - "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", + "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", "dev": true, "license": "MIT", "engines": { @@ -3044,16 +3087,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", - "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", + "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.36.0", - "@typescript-eslint/tsconfig-utils": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/project-service": "8.37.0", + "@typescript-eslint/tsconfig-utils": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3073,16 +3116,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz", - "integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", + "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0" + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3097,13 +3140,13 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", - "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", + "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", + "@typescript-eslint/types": "8.37.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -3151,16 +3194,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.36.0.tgz", - "integrity": "sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", + "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4" }, "engines": { @@ -3176,14 +3219,14 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/project-service": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", - "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", + "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.36.0", - "@typescript-eslint/types": "^8.36.0", + "@typescript-eslint/tsconfig-utils": "^8.37.0", + "@typescript-eslint/types": "^8.37.0", "debug": "^4.3.4" }, "engines": { @@ -3198,14 +3241,14 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", - "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", + "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0" + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3216,9 +3259,9 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", - "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", + "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", "dev": true, "license": "MIT", "engines": { @@ -3233,9 +3276,9 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", - "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", + "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", "dev": true, "license": "MIT", "engines": { @@ -3247,16 +3290,16 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", - "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", + "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.36.0", - "@typescript-eslint/tsconfig-utils": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/project-service": "8.37.0", + "@typescript-eslint/tsconfig-utils": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3276,13 +3319,13 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", - "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", + "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", + "@typescript-eslint/types": "8.37.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -3320,14 +3363,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", - "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", + "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.37.0", - "@typescript-eslint/types": "^8.37.0", + "@typescript-eslint/tsconfig-utils": "^8.38.0", + "@typescript-eslint/types": "^8.38.0", "debug": "^4.3.4" }, "engines": { @@ -3342,14 +3385,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", - "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", + "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0" + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3360,9 +3403,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", - "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", + "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", "dev": true, "license": "MIT", "engines": { @@ -3377,15 +3420,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", - "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", + "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -3402,9 +3445,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", - "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", + "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", "dev": true, "license": "MIT", "engines": { @@ -3416,16 +3459,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", - "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", + "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.37.0", - "@typescript-eslint/tsconfig-utils": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/project-service": "8.38.0", + "@typescript-eslint/tsconfig-utils": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3471,16 +3514,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", - "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", + "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0" + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3495,13 +3538,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", - "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", + "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/types": "8.38.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -5031,9 +5074,9 @@ } }, "node_modules/eslint": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.1.tgz", - "integrity": "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", + "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5041,9 +5084,9 @@ "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.14.0", + "@eslint/core": "^0.15.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.30.1", + "@eslint/js": "9.31.0", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -5123,6 +5166,80 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/eslint-p/node_modules/@eslint/js": { + "version": "9.30.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz", + "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/eslint-p/node_modules/eslint": { + "version": "9.30.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.1.tgz", + "integrity": "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.0", + "@eslint/core": "^0.14.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.30.1", + "@eslint/plugin-kit": "^0.3.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, "node_modules/eslint-plugin-compat": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-6.0.2.tgz", @@ -5160,9 +5277,9 @@ } }, "node_modules/eslint-plugin-svelte": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-3.10.1.tgz", - "integrity": "sha512-csCh2x0ge/DugXC7dCANh46Igi7bjMZEy6rHZCdS13AoGVJSu7a90Kru3I8oMYLGEemPRE1hQXadxvRPVMAAXQ==", + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-3.11.0.tgz", + "integrity": "sha512-KliWlkieHyEa65aQIkRwUFfHzT5Cn4u3BQQsu3KlkJOs7c1u7ryn84EWaOjEzilbKgttT4OfBURA8Uc4JBSQIw==", "dev": true, "license": "MIT", "dependencies": { @@ -5175,7 +5292,7 @@ "postcss-load-config": "^3.1.4", "postcss-safe-parser": "^7.0.0", "semver": "^7.6.3", - "svelte-eslint-parser": "^1.2.0" + "svelte-eslint-parser": "^1.3.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5285,31 +5402,19 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/@eslint/config-array": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", - "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "node_modules/eslint/node_modules/@eslint/core": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.6", - "debug": "^4.3.1", - "minimatch": "^3.1.2" + "@types/json-schema": "^7.0.15" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/eslint/node_modules/@eslint/config-helpers": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", - "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/esm-env": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", @@ -9093,9 +9198,9 @@ } }, "node_modules/svelte-eslint-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-1.2.0.tgz", - "integrity": "sha512-mbPtajIeuiyU80BEyGvwAktBeTX7KCr5/0l+uRGLq1dafwRNrjfM5kHGJScEBlPG3ipu6dJqfW/k0/fujvIEVw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-1.3.0.tgz", + "integrity": "sha512-VCgMHKV7UtOGcGLGNFSbmdm6kEKjtzo5nnpGU/mnx4OsFY6bZ7QwRF5DUx+Hokw5Lvdyo8dpk8B1m8mliomrNg==", "dev": true, "license": "MIT", "dependencies": { @@ -9608,15 +9713,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.36.0.tgz", - "integrity": "sha512-fTCqxthY+h9QbEgSIBfL9iV6CvKDFuoxg6bHPNpJ9HIUzS+jy2lCEyCmGyZRWEBSaykqcDPf1SJ+BfCI8DRopA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.37.0.tgz", + "integrity": "sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.36.0", - "@typescript-eslint/parser": "8.36.0", - "@typescript-eslint/utils": "8.36.0" + "@typescript-eslint/eslint-plugin": "8.37.0", + "@typescript-eslint/parser": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/utils": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -9631,14 +9737,14 @@ } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/project-service": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", - "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", + "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.36.0", - "@typescript-eslint/types": "^8.36.0", + "@typescript-eslint/tsconfig-utils": "^8.37.0", + "@typescript-eslint/types": "^8.37.0", "debug": "^4.3.4" }, "engines": { @@ -9653,14 +9759,14 @@ } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", - "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", + "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0" + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -9671,9 +9777,9 @@ } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", - "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", + "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", "dev": true, "license": "MIT", "engines": { @@ -9688,9 +9794,9 @@ } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", - "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", + "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", "dev": true, "license": "MIT", "engines": { @@ -9702,16 +9808,16 @@ } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", - "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", + "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.36.0", - "@typescript-eslint/tsconfig-utils": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/project-service": "8.37.0", + "@typescript-eslint/tsconfig-utils": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -9731,16 +9837,16 @@ } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz", - "integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", + "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0" + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -9755,13 +9861,13 @@ } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", - "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", + "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", + "@typescript-eslint/types": "8.37.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -9888,9 +9994,9 @@ "license": "MIT" }, "node_modules/vite": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.4.tgz", - "integrity": "sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.5.tgz", + "integrity": "sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw==", "dev": true, "license": "MIT", "dependencies": { @@ -10001,9 +10107,9 @@ } }, "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", - "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", + "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", "cpu": [ "ppc64" ], @@ -10018,9 +10124,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", - "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz", + "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", "cpu": [ "arm" ], @@ -10035,9 +10141,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", - "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz", + "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", "cpu": [ "arm64" ], @@ -10052,9 +10158,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", - "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz", + "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", "cpu": [ "x64" ], @@ -10069,9 +10175,9 @@ } }, "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", - "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz", + "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", "cpu": [ "arm64" ], @@ -10086,9 +10192,9 @@ } }, "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", - "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz", + "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", "cpu": [ "x64" ], @@ -10103,9 +10209,9 @@ } }, "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", - "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz", + "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", "cpu": [ "arm64" ], @@ -10120,9 +10226,9 @@ } }, "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", - "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz", + "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", "cpu": [ "x64" ], @@ -10137,9 +10243,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", - "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz", + "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", "cpu": [ "arm" ], @@ -10154,9 +10260,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", - "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz", + "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", "cpu": [ "arm64" ], @@ -10171,9 +10277,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", - "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz", + "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", "cpu": [ "ia32" ], @@ -10188,9 +10294,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", - "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz", + "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", "cpu": [ "loong64" ], @@ -10205,9 +10311,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", - "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz", + "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", "cpu": [ "mips64el" ], @@ -10222,9 +10328,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", - "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz", + "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", "cpu": [ "ppc64" ], @@ -10239,9 +10345,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", - "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz", + "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", "cpu": [ "riscv64" ], @@ -10256,9 +10362,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", - "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz", + "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", "cpu": [ "s390x" ], @@ -10273,9 +10379,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", - "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz", + "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", "cpu": [ "x64" ], @@ -10290,9 +10396,9 @@ } }, "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", - "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz", + "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", "cpu": [ "x64" ], @@ -10307,9 +10413,9 @@ } }, "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", - "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz", + "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", "cpu": [ "x64" ], @@ -10324,9 +10430,9 @@ } }, "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", - "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz", + "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", "cpu": [ "x64" ], @@ -10341,9 +10447,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", - "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz", + "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", "cpu": [ "arm64" ], @@ -10358,9 +10464,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", - "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz", + "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", "cpu": [ "ia32" ], @@ -10375,9 +10481,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", - "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz", + "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", "cpu": [ "x64" ], @@ -10392,9 +10498,9 @@ } }, "node_modules/vite/node_modules/esbuild": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", - "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", + "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -10405,31 +10511,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.4", - "@esbuild/android-arm": "0.25.4", - "@esbuild/android-arm64": "0.25.4", - "@esbuild/android-x64": "0.25.4", - "@esbuild/darwin-arm64": "0.25.4", - "@esbuild/darwin-x64": "0.25.4", - "@esbuild/freebsd-arm64": "0.25.4", - "@esbuild/freebsd-x64": "0.25.4", - "@esbuild/linux-arm": "0.25.4", - "@esbuild/linux-arm64": "0.25.4", - "@esbuild/linux-ia32": "0.25.4", - "@esbuild/linux-loong64": "0.25.4", - "@esbuild/linux-mips64el": "0.25.4", - "@esbuild/linux-ppc64": "0.25.4", - "@esbuild/linux-riscv64": "0.25.4", - "@esbuild/linux-s390x": "0.25.4", - "@esbuild/linux-x64": "0.25.4", - "@esbuild/netbsd-arm64": "0.25.4", - "@esbuild/netbsd-x64": "0.25.4", - "@esbuild/openbsd-arm64": "0.25.4", - "@esbuild/openbsd-x64": "0.25.4", - "@esbuild/sunos-x64": "0.25.4", - "@esbuild/win32-arm64": "0.25.4", - "@esbuild/win32-ia32": "0.25.4", - "@esbuild/win32-x64": "0.25.4" + "@esbuild/aix-ppc64": "0.25.8", + "@esbuild/android-arm": "0.25.8", + "@esbuild/android-arm64": "0.25.8", + "@esbuild/android-x64": "0.25.8", + "@esbuild/darwin-arm64": "0.25.8", + "@esbuild/darwin-x64": "0.25.8", + "@esbuild/freebsd-arm64": "0.25.8", + "@esbuild/freebsd-x64": "0.25.8", + "@esbuild/linux-arm": "0.25.8", + "@esbuild/linux-arm64": "0.25.8", + "@esbuild/linux-ia32": "0.25.8", + "@esbuild/linux-loong64": "0.25.8", + "@esbuild/linux-mips64el": "0.25.8", + "@esbuild/linux-ppc64": "0.25.8", + "@esbuild/linux-riscv64": "0.25.8", + "@esbuild/linux-s390x": "0.25.8", + "@esbuild/linux-x64": "0.25.8", + "@esbuild/netbsd-arm64": "0.25.8", + "@esbuild/netbsd-x64": "0.25.8", + "@esbuild/openbsd-arm64": "0.25.8", + "@esbuild/openbsd-x64": "0.25.8", + "@esbuild/openharmony-arm64": "0.25.8", + "@esbuild/sunos-x64": "0.25.8", + "@esbuild/win32-arm64": "0.25.8", + "@esbuild/win32-ia32": "0.25.8", + "@esbuild/win32-x64": "0.25.8" } }, "node_modules/vitefu": { diff --git a/web/package.json b/web/package.json index 2b092f057a..753b0a15a6 100644 --- a/web/package.json +++ b/web/package.json @@ -68,9 +68,9 @@ "@koddsson/eslint-plugin-tscompat": "^0.2.0", "@socket.io/component-emitter": "^3.1.0", "@sveltejs/adapter-static": "^3.0.8", - "@sveltejs/enhanced-img": "^0.6.0", - "@sveltejs/kit": "^2.15.2", - "@sveltejs/vite-plugin-svelte": "^6.0.0", + "@sveltejs/enhanced-img": "^0.7.0", + "@sveltejs/kit": "^2.25.0", + "@sveltejs/vite-plugin-svelte": "6.1.0", "@tailwindcss/vite": "^4.1.7", "@testing-library/jest-dom": "^6.4.2", "@testing-library/svelte": "^5.2.8", @@ -98,14 +98,14 @@ "prettier-plugin-sort-json": "^4.1.1", "prettier-plugin-svelte": "^3.3.3", "rollup-plugin-visualizer": "^6.0.0", - "svelte": "^5.25.3", + "svelte": "5.35.5", "svelte-check": "^4.1.5", "svelte-eslint-parser": "^1.2.0", "tailwindcss": "^4.1.7", "tslib": "^2.6.2", "typescript": "^5.7.3", "typescript-eslint": "^8.28.0", - "vite": "^7.0.0", + "vite": "^7.0.5", "vitest": "^3.0.0" }, "volta": { diff --git a/web/src/lib/components/shared-components/settings/setting-accordion-state.svelte b/web/src/lib/components/shared-components/settings/setting-accordion-state.svelte index 6b3ae81685..b0deb64316 100644 --- a/web/src/lib/components/shared-components/settings/setting-accordion-state.svelte +++ b/web/src/lib/components/shared-components/settings/setting-accordion-state.svelte @@ -12,6 +12,7 @@ import { goto } from '$app/navigation'; import type { Snippet } from 'svelte'; import { handlePromiseError } from '$lib/utils'; + import { SvelteURLSearchParams } from 'svelte/reactivity'; const getParamValues = (param: string) => { return new Set((page.url.searchParams.get(param) || '').split(' ').filter((x) => x !== '')); @@ -26,7 +27,7 @@ let { queryParam, state = writable(getParamValues(queryParam)), children }: Props = $props(); setAccordionState(state); - const searchParams = new URLSearchParams(page.url.searchParams); + const searchParams = new SvelteURLSearchParams(page.url.searchParams); $effect(() => { if ($state.size > 0) { diff --git a/web/src/lib/components/shared-components/side-bar/purchase-info.svelte b/web/src/lib/components/shared-components/side-bar/purchase-info.svelte index 5a984e94be..627292ea1b 100644 --- a/web/src/lib/components/shared-components/side-bar/purchase-info.svelte +++ b/web/src/lib/components/shared-components/side-bar/purchase-info.svelte @@ -17,6 +17,7 @@ import { mdiClose, mdiInformationOutline } from '@mdi/js'; import { t } from 'svelte-i18n'; import { fade } from 'svelte/transition'; + import { SvelteDate } from 'svelte/reactivity'; let showMessage = $state(false); let hoverMessage = $state(false); @@ -37,7 +38,7 @@ }; const hideButton = async (always: boolean) => { - const hideBuyButtonUntil = new Date(); + const hideBuyButtonUntil = new SvelteDate(); if (always) { hideBuyButtonUntil.setFullYear(2124); // see ya in 100 years diff --git a/web/src/lib/managers/timeline-manager/day-group.svelte.ts b/web/src/lib/managers/timeline-manager/day-group.svelte.ts index 2a949499ec..9d5008bf83 100644 --- a/web/src/lib/managers/timeline-manager/day-group.svelte.ts +++ b/web/src/lib/managers/timeline-manager/day-group.svelte.ts @@ -4,6 +4,7 @@ import type { CommonLayoutOptions } from '$lib/utils/layout-utils'; import { getJustifiedLayoutFromAssets, getPosition } from '$lib/utils/layout-utils'; import { plainDateTimeCompare } from '$lib/utils/timeline-util'; +import { SvelteSet } from 'svelte/reactivity'; import type { MonthGroup } from './month-group.svelte'; import type { AssetOperation, Direction, MoveAsset, TimelineAsset } from './types'; import { ViewerAsset } from './viewer-asset.svelte'; @@ -109,13 +110,13 @@ export class DayGroup { if (ids.size === 0) { return { moveAssets: [] as MoveAsset[], - processedIds: new Set(), + processedIds: new SvelteSet(), unprocessedIds: ids, changedGeometry: false, }; } - const unprocessedIds = new Set(ids); - const processedIds = new Set(); + const unprocessedIds = new SvelteSet(ids); + const processedIds = new SvelteSet(); const moveAssets: MoveAsset[] = []; let changedGeometry = false; for (const assetId of unprocessedIds) { diff --git a/web/src/lib/managers/timeline-manager/group-insertion-cache.svelte.ts b/web/src/lib/managers/timeline-manager/group-insertion-cache.svelte.ts index 66cca61d45..e511df9bf0 100644 --- a/web/src/lib/managers/timeline-manager/group-insertion-cache.svelte.ts +++ b/web/src/lib/managers/timeline-manager/group-insertion-cache.svelte.ts @@ -1,5 +1,6 @@ import { setDifference, type TimelinePlainDate } from '$lib/utils/timeline-util'; import { AssetOrder } from '@immich/sdk'; +import { SvelteSet } from 'svelte/reactivity'; import type { DayGroup } from './day-group.svelte'; import type { MonthGroup } from './month-group.svelte'; import type { TimelineAsset } from './types'; @@ -9,8 +10,8 @@ export class GroupInsertionCache { [year: number]: { [month: number]: { [day: number]: DayGroup } }; } = {}; unprocessedAssets: TimelineAsset[] = []; - changedDayGroups = new Set(); - newDayGroups = new Set(); + changedDayGroups = new SvelteSet(); + newDayGroups = new SvelteSet(); getDayGroup({ year, month, day }: TimelinePlainDate): DayGroup | undefined { return this.#lookupCache[year]?.[month]?.[day]; @@ -31,7 +32,7 @@ export class GroupInsertionCache { } get updatedBuckets() { - const updated = new Set(); + const updated = new SvelteSet(); for (const group of this.changedDayGroups) { updated.add(group.monthGroup); } @@ -39,7 +40,7 @@ export class GroupInsertionCache { } get bucketsWithNewDayGroups() { - const updated = new Set(); + const updated = new SvelteSet(); for (const group of this.newDayGroups) { updated.add(group.monthGroup); } diff --git a/web/src/lib/managers/timeline-manager/internal/operations-support.svelte.ts b/web/src/lib/managers/timeline-manager/internal/operations-support.svelte.ts index 82ec78499b..4419de2103 100644 --- a/web/src/lib/managers/timeline-manager/internal/operations-support.svelte.ts +++ b/web/src/lib/managers/timeline-manager/internal/operations-support.svelte.ts @@ -1,6 +1,7 @@ import { setDifference, type TimelinePlainDate } from '$lib/utils/timeline-util'; import { AssetOrder } from '@immich/sdk'; +import { SvelteSet } from 'svelte/reactivity'; import { GroupInsertionCache } from '../group-insertion-cache.svelte'; import { MonthGroup } from '../month-group.svelte'; import type { TimelineManager } from '../timeline-manager.svelte'; @@ -18,7 +19,7 @@ export function addAssetsToMonthGroups( } const addContext = new GroupInsertionCache(); - const updatedMonthGroups = new Set(); + const updatedMonthGroups = new SvelteSet(); const monthCount = timelineManager.months.length; for (const asset of assets) { let month = getMonthGroupByDate(timelineManager, asset.localDateTime); @@ -63,12 +64,12 @@ export function runAssetOperation( options: { order: AssetOrder }, ) { if (ids.size === 0) { - return { processedIds: new Set(), unprocessedIds: ids, changedGeometry: false }; + return { processedIds: new SvelteSet(), unprocessedIds: ids, changedGeometry: false }; } - const changedMonthGroups = new Set(); - let idsToProcess = new Set(ids); - const idsProcessed = new Set(); + const changedMonthGroups = new SvelteSet(); + let idsToProcess = new SvelteSet(ids); + const idsProcessed = new SvelteSet(); const combinedMoveAssets: { asset: TimelineAsset; date: TimelinePlainDate }[][] = []; for (const month of timelineManager.months) { if (idsToProcess.size > 0) { diff --git a/web/src/lib/managers/timeline-manager/month-group.svelte.ts b/web/src/lib/managers/timeline-manager/month-group.svelte.ts index bbcfe88caa..9f7112963a 100644 --- a/web/src/lib/managers/timeline-manager/month-group.svelte.ts +++ b/web/src/lib/managers/timeline-manager/month-group.svelte.ts @@ -17,6 +17,7 @@ import { import { t } from 'svelte-i18n'; import { get } from 'svelte/store'; +import { SvelteSet } from 'svelte/reactivity'; import { DayGroup } from './day-group.svelte'; import { GroupInsertionCache } from './group-insertion-cache.svelte'; import type { TimelineManager } from './timeline-manager.svelte'; @@ -115,15 +116,15 @@ export class MonthGroup { if (ids.size === 0) { return { moveAssets: [] as MoveAsset[], - processedIds: new Set(), + processedIds: new SvelteSet(), unprocessedIds: ids, changedGeometry: false, }; } const { dayGroups } = this; let combinedChangedGeometry = false; - let idsToProcess = new Set(ids); - const idsProcessed = new Set(); + let idsToProcess = new SvelteSet(ids); + const idsProcessed = new SvelteSet(); const combinedMoveAssets: MoveAsset[][] = []; let index = dayGroups.length; while (index--) { diff --git a/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts b/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts index 8aacd0a90a..c66a55fa11 100644 --- a/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts +++ b/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts @@ -6,7 +6,7 @@ import { CancellableTask } from '$lib/utils/cancellable-task'; import { toTimelineAsset, type TimelinePlainDateTime, type TimelinePlainYearMonth } from '$lib/utils/timeline-util'; import { clamp, debounce, isEqual } from 'lodash-es'; -import { SvelteSet } from 'svelte/reactivity'; +import { SvelteDate, SvelteMap, SvelteSet } from 'svelte/reactivity'; import { updateIntersectionMonthGroup } from '$lib/managers/timeline-manager/internal/intersection-support.svelte'; import { updateGeometry } from '$lib/managers/timeline-manager/internal/layout-support.svelte'; @@ -293,7 +293,7 @@ export class TimelineManager { }); this.months = timebuckets.map((timeBucket) => { - const date = new Date(timeBucket.timeBucket); + const date = new SvelteDate(timeBucket.timeBucket); return new MonthGroup( this, { year: date.getUTCFullYear(), month: date.getUTCMonth() + 1 }, @@ -456,14 +456,14 @@ export class TimelineManager { } updateAssetOperation(ids: string[], operation: AssetOperation) { - runAssetOperation(this, new Set(ids), operation, { order: this.#options.order ?? AssetOrder.Desc }); + runAssetOperation(this, new SvelteSet(ids), operation, { order: this.#options.order ?? AssetOrder.Desc }); } updateAssets(assets: TimelineAsset[]) { - const lookup = new Map(assets.map((asset) => [asset.id, asset])); + const lookup = new SvelteMap(assets.map((asset) => [asset.id, asset])); const { unprocessedIds } = runAssetOperation( this, - new Set(lookup.keys()), + new SvelteSet(lookup.keys()), (asset) => { updateObject(asset, lookup.get(asset.id)); return { remove: false }; @@ -480,7 +480,7 @@ export class TimelineManager { removeAssets(ids: string[]) { const { unprocessedIds } = runAssetOperation( this, - new Set(ids), + new SvelteSet(ids), () => { return { remove: true }; }, diff --git a/web/src/lib/modals/ApiKeyModal.svelte b/web/src/lib/modals/ApiKeyModal.svelte index 15902c8e53..55896c631d 100644 --- a/web/src/lib/modals/ApiKeyModal.svelte +++ b/web/src/lib/modals/ApiKeyModal.svelte @@ -9,6 +9,7 @@ import { mdiKeyVariant } from '@mdi/js'; import { onMount } from 'svelte'; import { t } from 'svelte-i18n'; + import { SvelteMap } from 'svelte/reactivity'; interface Props { apiKey: { name: string; permissions: Permission[] }; @@ -23,7 +24,7 @@ let selectedItems: Permission[] = $state(apiKey.permissions); let selectAllItems = $derived(selectedItems.length === Object.keys(Permission).length - 1); - const permissions: Map = new Map(); + const permissions: Map = new SvelteMap(); permissions.set('activity', [ Permission.ActivityCreate, diff --git a/web/src/lib/utils/timeline-util.ts b/web/src/lib/utils/timeline-util.ts index 7646d0d6d4..dc237c2223 100644 --- a/web/src/lib/utils/timeline-util.ts +++ b/web/src/lib/utils/timeline-util.ts @@ -3,6 +3,7 @@ import { locale } from '$lib/stores/preferences.store'; import { getAssetRatio } from '$lib/utils/asset-utils'; import { AssetTypeEnum, type AssetResponseDto } from '@immich/sdk'; import { DateTime, type LocaleOptions } from 'luxon'; +import { SvelteSet } from 'svelte/reactivity'; import { get } from 'svelte/store'; // Move type definitions to the top @@ -216,8 +217,8 @@ export const plainDateTimeCompare = (ascending: boolean, a: TimelinePlainDateTim return aDateTime.millisecond - bDateTime.millisecond; }; -export function setDifference(setA: Set, setB: Set): Set { - const result = new Set(); +export function setDifference(setA: Set, setB: Set): SvelteSet { + const result = new SvelteSet(); for (const value of setA) { if (!setB.has(value)) { result.add(value); From b3061f1e4f47e5ad5b28b69fb1f47a35fa8f098b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 19:18:29 +0100 Subject: [PATCH 015/748] fix(deps): update typescript-projects (#20086) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Daniel Dietzler --- web/package-lock.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index 1d4d3bdb34..51f60a83c3 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -100,7 +100,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.15.33", + "@types/node": "^22.16.4", "typescript": "^5.3.3" } }, @@ -9174,9 +9174,9 @@ } }, "node_modules/svelte-check": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.2.2.tgz", - "integrity": "sha512-1+31EOYZ7NKN0YDMKusav2hhEoA51GD9Ws6o//0SphMT0ve9mBTsTUEX7OmDMadUP3KjNHsSKtJrqdSaD8CrGQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.3.0.tgz", + "integrity": "sha512-Iz8dFXzBNAM7XlEIsUjUGQhbEE+Pvv9odb9+0+ITTgFWZBGeJRRYqHUUglwe2EkLD5LIsQaAc4IUJyvtKuOO5w==", "dev": true, "license": "MIT", "dependencies": { From 277e39ac982c25b5074f7142959aaf23ca6cd325 Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Tue, 22 Jul 2025 15:01:18 -0500 Subject: [PATCH 016/748] fix(mobile): sync icon rotation direction (#20088) spin the sync icon the right direction --- mobile/lib/widgets/common/immich_sliver_app_bar.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mobile/lib/widgets/common/immich_sliver_app_bar.dart b/mobile/lib/widgets/common/immich_sliver_app_bar.dart index c7ddeca6e0..09c84e0c20 100644 --- a/mobile/lib/widgets/common/immich_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/immich_sliver_app_bar.dart @@ -364,7 +364,10 @@ class _SyncStatusIndicatorState extends ConsumerState<_SyncStatusIndicator> child: Opacity( opacity: isSyncing ? 1.0 : _dismissalAnimation.value, child: Transform.rotate( - angle: _rotationAnimation.value * 2 * 3.14159, + angle: _rotationAnimation.value * + 2 * + 3.14159 * + -1, // Rotate counter-clockwise child: Icon( Icons.sync, size: 24, From 3c7f0a2900f47dc5c35868ca2459c964550ef1f5 Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Tue, 22 Jul 2025 16:02:52 -0500 Subject: [PATCH 017/748] chore(mobile): use hides instead of changing the namespace (#20090) --- mobile/lib/pages/common/settings.page.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mobile/lib/pages/common/settings.page.dart b/mobile/lib/pages/common/settings.page.dart index b19ff87aa9..439e5068a3 100644 --- a/mobile/lib/pages/common/settings.page.dart +++ b/mobile/lib/pages/common/settings.page.dart @@ -1,7 +1,7 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_hooks/flutter_hooks.dart' hide Store; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/widgets/settings/advanced_settings.dart'; @@ -13,7 +13,7 @@ import 'package:immich_mobile/widgets/settings/language_settings.dart'; import 'package:immich_mobile/widgets/settings/networking_settings/networking_settings.dart'; import 'package:immich_mobile/widgets/settings/notification_setting.dart'; import 'package:immich_mobile/widgets/settings/preference_settings/preference_setting.dart'; -import 'package:immich_mobile/entities/store.entity.dart' as app_store; +import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/widgets/settings/settings_card.dart'; enum SettingSection { @@ -112,7 +112,7 @@ class _MobileLayout extends StatelessWidget { padding: const EdgeInsets.only(top: 10.0, bottom: 56), children: [ const BetaTimelineListTile(), - if (app_store.Store.isBetaTimelineEnabled) + if (Store.isBetaTimelineEnabled) SettingsCard( icon: Icons.sync_outlined, title: 'beta_sync'.tr(), From f1cac122ed44d9efa26eada3fe4a2c661b1d0823 Mon Sep 17 00:00:00 2001 From: Sebastian Di Luzio Date: Tue, 22 Jul 2025 23:29:36 +0200 Subject: [PATCH 018/748] fix: more inclusive language (#20092) --- docs/docs/features/mobile-app.mdx | 4 ++-- i18n/en.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/docs/features/mobile-app.mdx b/docs/docs/features/mobile-app.mdx index 38222479b0..cd837741f1 100644 --- a/docs/docs/features/mobile-app.mdx +++ b/docs/docs/features/mobile-app.mdx @@ -88,9 +88,9 @@ It will only reflect files you add. ::: If the same asset is in more than one album it will only sync to the first album it's in, after that it won't sync again even if the user clicks sync albums manually. -To overcome this limitation, the files must be removed from the blacklist by +To overcome this limitation, the files must be removed from the ignore list by App settings -> Advanced -> Duplicate Assets -> Clear :::info -Cleaning duplicate assets from the list will cause all the previously uploaded duplicate files to be re-uploaded, the files will not actually be uploaded and will be rejected on the server side (due to duplication) but will be synchronized to the album and at the end will be added to the black list again at the end of the synchronization. +Cleaning duplicate assets from the list will cause all the previously uploaded duplicate files to be re-uploaded, the files will not actually be uploaded and will be rejected on the server side (due to duplication) but will be synchronized to the album and at the end will be added to the ignore list again at the end of the synchronization. ::: diff --git a/i18n/en.json b/i18n/en.json index bbd6debd5e..4dc72b4c42 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -592,7 +592,7 @@ "cache_settings_clear_cache_button": "Clear cache", "cache_settings_clear_cache_button_title": "Clears the app's cache. This will significantly impact the app's performance until the cache has rebuilt.", "cache_settings_duplicated_assets_clear_button": "CLEAR", - "cache_settings_duplicated_assets_subtitle": "Photos and videos that are black listed by the app", + "cache_settings_duplicated_assets_subtitle": "Photos and videos that are ignore listed by the app", "cache_settings_duplicated_assets_title": "Duplicated Assets ({count})", "cache_settings_statistics_album": "Library thumbnails", "cache_settings_statistics_full": "Full images", From 1011cdb37699a53ee61d876067d3cfc435e2eafb Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 22 Jul 2025 17:11:06 -0500 Subject: [PATCH 019/748] chore: handle requeue upload when target albums changed (#20089) * chore: handle requeue upload when target albums changed * chore: remove debug --- i18n/en.json | 1 + .../lib/pages/backup/drift_backup.page.dart | 16 +- .../drift_backup_album_selection.page.dart | 364 ++++++++++-------- .../backup/drift_upload_detail.page.dart | 6 +- .../backup/drift_backup.provider.dart | 32 +- 5 files changed, 237 insertions(+), 182 deletions(-) diff --git a/i18n/en.json b/i18n/en.json index 4dc72b4c42..54c7ca6f1b 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1330,6 +1330,7 @@ "no_results": "No results", "no_results_description": "Try a synonym or more general keyword", "no_shared_albums_message": "Create an album to share photos and videos with people in your network", + "no_uploads_in_progress": "No uploads in progress", "not_in_any_album": "Not in any album", "not_selected": "Not selected", "note_apply_storage_label_to_previously_uploaded assets": "Note: To apply the Storage Label to previously uploaded assets, run the", diff --git a/mobile/lib/pages/backup/drift_backup.page.dart b/mobile/lib/pages/backup/drift_backup.page.dart index 1b9ec8ad07..7780413399 100644 --- a/mobile/lib/pages/backup/drift_backup.page.dart +++ b/mobile/lib/pages/backup/drift_backup.page.dart @@ -44,9 +44,6 @@ class _DriftBackupPageState extends ConsumerState { (album) => album.backupSelection == BackupSelection.selected, ) .toList(); - final uploadItems = ref.watch( - driftBackupProvider.select((state) => state.uploadItems), - ); return Scaffold( appBar: AppBar( @@ -85,14 +82,13 @@ class _DriftBackupPageState extends ConsumerState { onStart: () async => await startBackup(), onStop: () async => await stopBackup(), ), - if (uploadItems.isNotEmpty) - TextButton.icon( - icon: const Icon(Icons.info_outline_rounded), - onPressed: () => context.pushRoute( - const DriftUploadDetailRoute(), - ), - label: Text("view_details".t(context: context)), + TextButton.icon( + icon: const Icon(Icons.info_outline_rounded), + onPressed: () => context.pushRoute( + const DriftUploadDetailRoute(), ), + label: Text("view_details".t(context: context)), + ), ], ], ), diff --git a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart index fd39f0a579..18d3ee1156 100644 --- a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart +++ b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart @@ -9,6 +9,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; import 'package:immich_mobile/providers/backup/backup_album.provider.dart'; +import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/widgets/backup/drift_album_info_list_tile.dart'; @@ -28,6 +29,8 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState { String _searchQuery = ''; bool _isSearchMode = false; + int _initialTotalAssetCount = 0; + bool _hasPopped = false; late ValueNotifier _enableSyncUploadAlbum; late TextEditingController _searchController; late FocusNode _searchFocusNode; @@ -43,6 +46,9 @@ class _DriftBackupAlbumSelectionPageState .read(appSettingsServiceProvider) .getSetting(AppSettingsEnum.syncAlbums); ref.read(backupAlbumProvider.notifier).getAll(); + + _initialTotalAssetCount = + ref.read(driftBackupProvider.select((p) => p.totalCount)); } @override @@ -79,179 +85,207 @@ class _DriftBackupAlbumSelectionPageState } } - return Scaffold( - appBar: AppBar( - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), - title: _isSearchMode - ? SearchField( - hintText: 'search_albums'.t(context: context), - autofocus: true, - controller: _searchController, - focusNode: _searchFocusNode, - onChanged: (value) => - setState(() => _searchQuery = value.trim()), + return PopScope( + onPopInvokedWithResult: (didPop, result) async { + // There is an issue with Flutter where the pop event + // can be triggered multiple times, so we guard it with _hasPopped + if (didPop && !_hasPopped) { + _hasPopped = true; + + await ref.read(driftBackupProvider.notifier).getBackupStatus(); + final currentTotalAssetCount = + ref.read(driftBackupProvider.select((p) => p.totalCount)); + + if (currentTotalAssetCount != _initialTotalAssetCount) { + final isBackupEnabled = ref + .read(appSettingsServiceProvider) + .getSetting(AppSettingsEnum.enableBackup); + + if (!isBackupEnabled) { + return; + } + final backupNotifier = ref.read(driftBackupProvider.notifier); + + backupNotifier.cancel().then((_) { + backupNotifier.backup(); + }); + } + } + }, + child: Scaffold( + appBar: AppBar( + leading: IconButton( + onPressed: () async => await context.maybePop(), + icon: const Icon(Icons.arrow_back_ios_rounded), + ), + title: _isSearchMode + ? SearchField( + hintText: 'search_albums'.t(context: context), + autofocus: true, + controller: _searchController, + focusNode: _searchFocusNode, + onChanged: (value) => + setState(() => _searchQuery = value.trim()), + ) + : const Text( + "backup_album_selection_page_select_albums", + ).t(context: context), + actions: [ + if (!_isSearchMode) + IconButton( + icon: const Icon(Icons.search), + onPressed: () => setState(() { + _isSearchMode = true; + _searchQuery = ''; + }), ) - : const Text( - "backup_album_selection_page_select_albums", - ).t(context: context), - actions: [ - if (!_isSearchMode) - IconButton( - icon: const Icon(Icons.search), - onPressed: () => setState(() { - _isSearchMode = true; - _searchQuery = ''; - }), - ) - else - IconButton( - icon: const Icon(Icons.close), - onPressed: () => setState(() { - _isSearchMode = false; - _searchQuery = ''; - _searchController.clear(); - }), - ), - ], - elevation: 0, - ), - body: CustomScrollView( - physics: const ClampingScrollPhysics(), - slivers: [ - SliverToBoxAdapter( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.symmetric( - vertical: 8.0, - horizontal: 16.0, - ), - child: Text( - "backup_album_selection_page_selection_info", - style: context.textTheme.titleSmall, - ).t(context: context), - ), - // Selected Album Chips - - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Wrap( - children: [ - _SelectedAlbumNameChips( - selectedBackupAlbums: selectedBackupAlbums, - ), - _ExcludedAlbumNameChips( - excludedBackupAlbums: excludedBackupAlbums, - ), - ], - ), - ), - - SettingsSwitchListTile( - valueNotifier: _enableSyncUploadAlbum, - title: "sync_albums".t(context: context), - subtitle: - "sync_upload_album_setting_subtitle".t(context: context), - contentPadding: const EdgeInsets.symmetric(horizontal: 16), - titleStyle: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.bold, - ), - subtitleStyle: context.textTheme.labelLarge?.copyWith( - color: context.colorScheme.primary, - ), - onChanged: handleSyncAlbumToggle, - ), - - ListTile( - title: Text( - "albums_on_device_count".t( - context: context, - args: {'count': albumCount.toString()}, + else + IconButton( + icon: const Icon(Icons.close), + onPressed: () => setState(() { + _isSearchMode = false; + _searchQuery = ''; + _searchController.clear(); + }), + ), + ], + elevation: 0, + ), + body: CustomScrollView( + physics: const ClampingScrollPhysics(), + slivers: [ + SliverToBoxAdapter( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric( + vertical: 8.0, + horizontal: 16.0, ), - style: context.textTheme.titleSmall, - ), - subtitle: Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), child: Text( - "backup_album_selection_page_albums_tap", - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), + "backup_album_selection_page_selection_info", + style: context.textTheme.titleSmall, ).t(context: context), ), - trailing: IconButton( - splashRadius: 16, - icon: Icon( - Icons.info, - size: 20, - color: context.primaryColor, - ), - onPressed: () { - // show the dialog - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - shape: const RoundedRectangleBorder( - borderRadius: - BorderRadius.all(Radius.circular(10)), - ), - elevation: 5, - title: Text( - 'backup_album_selection_page_selection_info', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), - ).t(context: context), - content: SingleChildScrollView( - child: ListBody( - children: [ - const Text( - 'backup_album_selection_page_assets_scatter', - style: TextStyle( - fontSize: 14, - ), - ).t(context: context), - ], - ), - ), - ); - }, - ); - }, - ), - ), + // Selected Album Chips - if (Platform.isAndroid) - _SelectAllButton( - filteredAlbums: filteredAlbums, - selectedBackupAlbums: selectedBackupAlbums, + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Wrap( + children: [ + _SelectedAlbumNameChips( + selectedBackupAlbums: selectedBackupAlbums, + ), + _ExcludedAlbumNameChips( + excludedBackupAlbums: excludedBackupAlbums, + ), + ], + ), ), - ], + + SettingsSwitchListTile( + valueNotifier: _enableSyncUploadAlbum, + title: "sync_albums".t(context: context), + subtitle: "sync_upload_album_setting_subtitle" + .t(context: context), + contentPadding: const EdgeInsets.symmetric(horizontal: 16), + titleStyle: context.textTheme.bodyLarge?.copyWith( + fontWeight: FontWeight.bold, + ), + subtitleStyle: context.textTheme.labelLarge?.copyWith( + color: context.colorScheme.primary, + ), + onChanged: handleSyncAlbumToggle, + ), + + ListTile( + title: Text( + "albums_on_device_count".t( + context: context, + args: {'count': albumCount.toString()}, + ), + style: context.textTheme.titleSmall, + ), + subtitle: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Text( + "backup_album_selection_page_albums_tap", + style: context.textTheme.labelLarge?.copyWith( + color: context.primaryColor, + ), + ).t(context: context), + ), + trailing: IconButton( + splashRadius: 16, + icon: Icon( + Icons.info, + size: 20, + color: context.primaryColor, + ), + onPressed: () { + // show the dialog + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + shape: const RoundedRectangleBorder( + borderRadius: + BorderRadius.all(Radius.circular(10)), + ), + elevation: 5, + title: Text( + 'backup_album_selection_page_selection_info', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: context.primaryColor, + ), + ).t(context: context), + content: SingleChildScrollView( + child: ListBody( + children: [ + const Text( + 'backup_album_selection_page_assets_scatter', + style: TextStyle( + fontSize: 14, + ), + ).t(context: context), + ], + ), + ), + ); + }, + ); + }, + ), + ), + + if (Platform.isAndroid) + _SelectAllButton( + filteredAlbums: filteredAlbums, + selectedBackupAlbums: selectedBackupAlbums, + ), + ], + ), ), - ), - SliverLayoutBuilder( - builder: (context, constraints) { - if (constraints.crossAxisExtent > 600) { - return _AlbumSelectionGrid( - filteredAlbums: filteredAlbums, - searchQuery: _searchQuery, - ); - } else { - return _AlbumSelectionList( - filteredAlbums: filteredAlbums, - searchQuery: _searchQuery, - ); - } - }, - ), - ], + SliverLayoutBuilder( + builder: (context, constraints) { + if (constraints.crossAxisExtent > 600) { + return _AlbumSelectionGrid( + filteredAlbums: filteredAlbums, + searchQuery: _searchQuery, + ); + } else { + return _AlbumSelectionList( + filteredAlbums: filteredAlbums, + searchQuery: _searchQuery, + ); + } + }, + ), + ], + ), ), ); } diff --git a/mobile/lib/pages/backup/drift_upload_detail.page.dart b/mobile/lib/pages/backup/drift_upload_detail.page.dart index 66803265e6..058bfa1aaf 100644 --- a/mobile/lib/pages/backup/drift_upload_detail.page.dart +++ b/mobile/lib/pages/backup/drift_upload_detail.page.dart @@ -39,7 +39,7 @@ class DriftUploadDetailPage extends ConsumerWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( - Icons.cloud_upload_outlined, + Icons.cloud_off_rounded, size: 80, color: context.colorScheme.onSurface.withValues(alpha: 0.3), ), @@ -79,7 +79,9 @@ class DriftUploadDetailPage extends ConsumerWidget { return Card( elevation: 0, - color: context.colorScheme.surfaceContainer, + color: item.isFailed != null + ? context.colorScheme.errorContainer + : context.colorScheme.surfaceContainer, shape: RoundedRectangleBorder( borderRadius: const BorderRadius.all( Radius.circular(16), diff --git a/mobile/lib/providers/backup/drift_backup.provider.dart b/mobile/lib/providers/backup/drift_backup.provider.dart index c51c40775e..3a2c7fd9ce 100644 --- a/mobile/lib/providers/backup/drift_backup.provider.dart +++ b/mobile/lib/providers/backup/drift_backup.provider.dart @@ -41,6 +41,7 @@ class DriftUploadStatus { final double progress; final int fileSize; final String networkSpeedAsString; + final bool? isFailed; const DriftUploadStatus({ required this.taskId, @@ -48,6 +49,7 @@ class DriftUploadStatus { required this.progress, required this.fileSize, required this.networkSpeedAsString, + this.isFailed, }); DriftUploadStatus copyWith({ @@ -56,6 +58,7 @@ class DriftUploadStatus { double? progress, int? fileSize, String? networkSpeedAsString, + bool? isFailed, }) { return DriftUploadStatus( taskId: taskId ?? this.taskId, @@ -63,12 +66,13 @@ class DriftUploadStatus { progress: progress ?? this.progress, fileSize: fileSize ?? this.fileSize, networkSpeedAsString: networkSpeedAsString ?? this.networkSpeedAsString, + isFailed: isFailed ?? this.isFailed, ); } @override String toString() { - return 'DriftUploadStatus(taskId: $taskId, filename: $filename, progress: $progress, fileSize: $fileSize, networkSpeedAsString: $networkSpeedAsString)'; + return 'DriftUploadStatus(taskId: $taskId, filename: $filename, progress: $progress, fileSize: $fileSize, networkSpeedAsString: $networkSpeedAsString, isFailed: $isFailed)'; } @override @@ -79,7 +83,8 @@ class DriftUploadStatus { other.filename == filename && other.progress == progress && other.fileSize == fileSize && - other.networkSpeedAsString == networkSpeedAsString; + other.networkSpeedAsString == networkSpeedAsString && + other.isFailed == isFailed; } @override @@ -88,7 +93,8 @@ class DriftUploadStatus { filename.hashCode ^ progress.hashCode ^ fileSize.hashCode ^ - networkSpeedAsString.hashCode; + networkSpeedAsString.hashCode ^ + isFailed.hashCode; } Map toMap() { @@ -98,6 +104,7 @@ class DriftUploadStatus { 'progress': progress, 'fileSize': fileSize, 'networkSpeedAsString': networkSpeedAsString, + 'isFailed': isFailed, }; } @@ -108,6 +115,7 @@ class DriftUploadStatus { progress: map['progress'] as double, fileSize: map['fileSize'] as int, networkSpeedAsString: map['networkSpeedAsString'] as String, + isFailed: map['isFailed'] != null ? map['isFailed'] as bool : null, ); } @@ -235,6 +243,8 @@ class ExpBackupNotifier extends StateNotifier { } void _handleTaskStatusUpdate(TaskStatusUpdate update) { + final taskId = update.task.taskId; + switch (update.status) { case TaskStatus.complete: if (update.task.group == kBackupGroup) { @@ -245,14 +255,26 @@ class ExpBackupNotifier extends StateNotifier { } // Remove the completed task from the upload items - final taskId = update.task.taskId; if (state.uploadItems.containsKey(taskId)) { - Future.delayed(const Duration(milliseconds: 500), () { + Future.delayed(const Duration(milliseconds: 1000), () { _removeUploadItem(taskId); }); } case TaskStatus.failed: + final currentItem = state.uploadItems[taskId]; + if (currentItem == null) { + return; + } + + state = state.copyWith( + uploadItems: { + ...state.uploadItems, + taskId: currentItem.copyWith( + isFailed: true, + ), + }, + ); break; case TaskStatus.canceled: From 1a70896113cc05a64287d75e97400f7b75f3b712 Mon Sep 17 00:00:00 2001 From: xCJPECKOVERx Date: Tue, 22 Jul 2025 22:17:06 -0400 Subject: [PATCH 020/748] feat(web): Remove from Stack (#19703) * - add component - update server's StackCreateDto for merge parameter - Update stackRepo to only merge stacks when merge=true (default) - update web action handlers to show stack changes * - make open-api * lint & format * - Add proper icon to 'remove from stack' - change web unstack icon to image-off-outline * - cleanup * - format & lint * - make open-api: StackCreateDto merge optional * initial addition of new endpoint * remove stack endpoint * - fix up remove stack endpoint - open-api * - Undo stackCreate merge parameter * - open-api typescript * open-api dart * Tests: - add tests - update assetStub.imageFrom2015 to have required stack attributes to include it with tests * update event name * Fix event name in test * remove asset_update check * - merge stack.removeAsset params into one object - refactor asset existence check (no need for asset fetch) - fix tests * Don't return updated stack * Create specialized stack id & primary asset fetch for asset removal checks * Correct new permission names * make sql * - fix open-api * - cleanup --- mobile/openapi/README.md | 1 + mobile/openapi/lib/api/stacks_api.dart | 45 +++++++++++++++++ open-api/immich-openapi-specs.json | 44 +++++++++++++++++ open-api/typescript-sdk/src/fetch-client.ts | 9 ++++ server/src/controllers/stack.controller.ts | 9 +++- server/src/queries/stack.repository.sql | 10 ++++ server/src/repositories/stack.repository.ts | 10 ++++ server/src/services/stack.service.spec.ts | 49 +++++++++++++++++++ server/src/services/stack.service.ts | 19 +++++++ server/src/validation.ts | 40 +++++++++------ server/test/fixtures/asset.stub.ts | 5 +- .../components/asset-viewer/actions/action.ts | 3 +- .../actions/remove-asset-from-stack.svelte | 31 ++++++++++++ .../actions/unstack-action.svelte | 4 +- .../asset-viewer/asset-viewer-nav-bar.svelte | 4 ++ .../asset-viewer/asset-viewer.svelte | 7 +++ .../photos-page/actions/stack-action.svelte | 4 +- .../components/photos-page/asset-grid.svelte | 17 +++++++ web/src/lib/constants.ts | 1 + 19 files changed, 289 insertions(+), 23 deletions(-) create mode 100644 web/src/lib/components/asset-viewer/actions/remove-asset-from-stack.svelte diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 3e7fa4c2f1..b20a0694c5 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -221,6 +221,7 @@ Class | Method | HTTP request | Description *StacksApi* | [**deleteStack**](doc//StacksApi.md#deletestack) | **DELETE** /stacks/{id} | *StacksApi* | [**deleteStacks**](doc//StacksApi.md#deletestacks) | **DELETE** /stacks | *StacksApi* | [**getStack**](doc//StacksApi.md#getstack) | **GET** /stacks/{id} | +*StacksApi* | [**removeAssetFromStack**](doc//StacksApi.md#removeassetfromstack) | **DELETE** /stacks/{id}/assets/{assetId} | *StacksApi* | [**searchStacks**](doc//StacksApi.md#searchstacks) | **GET** /stacks | *StacksApi* | [**updateStack**](doc//StacksApi.md#updatestack) | **PUT** /stacks/{id} | *SyncApi* | [**deleteSyncAck**](doc//SyncApi.md#deletesyncack) | **DELETE** /sync/ack | diff --git a/mobile/openapi/lib/api/stacks_api.dart b/mobile/openapi/lib/api/stacks_api.dart index 84f23ec55d..6d6c4506be 100644 --- a/mobile/openapi/lib/api/stacks_api.dart +++ b/mobile/openapi/lib/api/stacks_api.dart @@ -190,6 +190,51 @@ class StacksApi { return null; } + /// Performs an HTTP 'DELETE /stacks/{id}/assets/{assetId}' operation and returns the [Response]. + /// Parameters: + /// + /// * [String] assetId (required): + /// + /// * [String] id (required): + Future removeAssetFromStackWithHttpInfo(String assetId, String id,) async { + // ignore: prefer_const_declarations + final apiPath = r'/stacks/{id}/assets/{assetId}' + .replaceAll('{assetId}', assetId) + .replaceAll('{id}', id); + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'DELETE', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [String] assetId (required): + /// + /// * [String] id (required): + Future removeAssetFromStack(String assetId, String id,) async { + final response = await removeAssetFromStackWithHttpInfo(assetId, id,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + } + /// Performs an HTTP 'GET /stacks' operation and returns the [Response]. /// Parameters: /// diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 4acd431203..cd61f3e004 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -6746,6 +6746,50 @@ ] } }, + "/stacks/{id}/assets/{assetId}": { + "delete": { + "operationId": "removeAssetFromStack", + "parameters": [ + { + "name": "assetId", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "type": "string" + } + }, + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Stacks" + ] + } + }, "/sync/ack": { "delete": { "operationId": "deleteSyncAck", diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index d5f7fde52a..81d279407c 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -3373,6 +3373,15 @@ export function updateStack({ id, stackUpdateDto }: { body: stackUpdateDto }))); } +export function removeAssetFromStack({ assetId, id }: { + assetId: string; + id: string; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchText(`/stacks/${encodeURIComponent(id)}/assets/${encodeURIComponent(assetId)}`, { + ...opts, + method: "DELETE" + })); +} export function deleteSyncAck({ syncAckDeleteDto }: { syncAckDeleteDto: SyncAckDeleteDto; }, opts?: Oazapfts.RequestOpts) { diff --git a/server/src/controllers/stack.controller.ts b/server/src/controllers/stack.controller.ts index 238753734c..5b153a163b 100644 --- a/server/src/controllers/stack.controller.ts +++ b/server/src/controllers/stack.controller.ts @@ -6,7 +6,7 @@ import { StackCreateDto, StackResponseDto, StackSearchDto, StackUpdateDto } from import { Permission } from 'src/enum'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; import { StackService } from 'src/services/stack.service'; -import { UUIDParamDto } from 'src/validation'; +import { UUIDAssetIDParamDto, UUIDParamDto } from 'src/validation'; @ApiTags('Stacks') @Controller('stacks') @@ -54,4 +54,11 @@ export class StackController { deleteStack(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.delete(auth, id); } + + @Delete(':id/assets/:assetId') + @Authenticated({ permission: Permission.StackUpdate }) + @HttpCode(HttpStatus.NO_CONTENT) + removeAssetFromStack(@Auth() auth: AuthDto, @Param() dto: UUIDAssetIDParamDto): Promise { + return this.service.removeAsset(auth, dto); + } } diff --git a/server/src/queries/stack.repository.sql b/server/src/queries/stack.repository.sql index a256cdfc76..94a24f69e4 100644 --- a/server/src/queries/stack.repository.sql +++ b/server/src/queries/stack.repository.sql @@ -143,3 +143,13 @@ from "stack" where "id" = $1::uuid + +-- StackRepository.getForAssetRemoval +select + "stackId" as "id", + "stack"."primaryAssetId" +from + "asset" + left join "stack" on "stack"."id" = "asset"."stackId" +where + "asset"."id" = $1 diff --git a/server/src/repositories/stack.repository.ts b/server/src/repositories/stack.repository.ts index fe16c8b5eb..ace9468177 100644 --- a/server/src/repositories/stack.repository.ts +++ b/server/src/repositories/stack.repository.ts @@ -152,4 +152,14 @@ export class StackRepository { .where('id', '=', asUuid(id)) .executeTakeFirst(); } + + @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID] }) + getForAssetRemoval(assetId: string) { + return this.db + .selectFrom('asset') + .leftJoin('stack', 'stack.id', 'asset.stackId') + .select(['stackId as id', 'stack.primaryAssetId']) + .where('asset.id', '=', assetId) + .executeTakeFirst(); + } } diff --git a/server/src/services/stack.service.spec.ts b/server/src/services/stack.service.spec.ts index 5c7b505cd9..5517cf17f8 100644 --- a/server/src/services/stack.service.spec.ts +++ b/server/src/services/stack.service.spec.ts @@ -188,4 +188,53 @@ describe(StackService.name, () => { }); }); }); + + describe('removeAsset', () => { + it('should require stack.update permissions', async () => { + await expect(sut.removeAsset(authStub.admin, { id: 'stack-id', assetId: 'asset-id' })).rejects.toBeInstanceOf( + BadRequestException, + ); + + expect(mocks.stack.getForAssetRemoval).not.toHaveBeenCalled(); + expect(mocks.asset.update).not.toHaveBeenCalled(); + expect(mocks.event.emit).not.toHaveBeenCalled(); + }); + + it('should fail if the asset is not in the stack', async () => { + mocks.access.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id'])); + mocks.stack.getForAssetRemoval.mockResolvedValue({ id: null, primaryAssetId: null }); + + await expect( + sut.removeAsset(authStub.admin, { id: 'stack-id', assetId: assetStub.imageFrom2015.id }), + ).rejects.toBeInstanceOf(BadRequestException); + + expect(mocks.asset.update).not.toHaveBeenCalled(); + expect(mocks.event.emit).not.toHaveBeenCalled(); + }); + + it('should fail if the assetId is the primaryAssetId', async () => { + mocks.access.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id'])); + mocks.stack.getForAssetRemoval.mockResolvedValue({ id: 'stack-id', primaryAssetId: assetStub.image.id }); + + await expect( + sut.removeAsset(authStub.admin, { id: 'stack-id', assetId: assetStub.image.id }), + ).rejects.toBeInstanceOf(BadRequestException); + + expect(mocks.asset.update).not.toHaveBeenCalled(); + expect(mocks.event.emit).not.toHaveBeenCalled(); + }); + + it("should update the asset to nullify it's stack-id", async () => { + mocks.access.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id'])); + mocks.stack.getForAssetRemoval.mockResolvedValue({ id: 'stack-id', primaryAssetId: assetStub.image.id }); + + await sut.removeAsset(authStub.admin, { id: 'stack-id', assetId: assetStub.image1.id }); + + expect(mocks.asset.update).toHaveBeenCalledWith({ id: assetStub.image1.id, stackId: null }); + expect(mocks.event.emit).toHaveBeenCalledWith('StackUpdate', { + stackId: 'stack-id', + userId: authStub.admin.user.id, + }); + }); + }); }); diff --git a/server/src/services/stack.service.ts b/server/src/services/stack.service.ts index 18600abd12..c84ec70fbf 100644 --- a/server/src/services/stack.service.ts +++ b/server/src/services/stack.service.ts @@ -4,6 +4,7 @@ import { AuthDto } from 'src/dtos/auth.dto'; import { StackCreateDto, StackResponseDto, StackSearchDto, StackUpdateDto, mapStack } from 'src/dtos/stack.dto'; import { Permission } from 'src/enum'; import { BaseService } from 'src/services/base.service'; +import { UUIDAssetIDParamDto } from 'src/validation'; @Injectable() export class StackService extends BaseService { @@ -58,6 +59,24 @@ export class StackService extends BaseService { await this.eventRepository.emit('StackDeleteAll', { stackIds: dto.ids, userId: auth.user.id }); } + async removeAsset(auth: AuthDto, dto: UUIDAssetIDParamDto): Promise { + const { id: stackId, assetId } = dto; + await this.requireAccess({ auth, permission: Permission.StackUpdate, ids: [stackId] }); + + const stack = await this.stackRepository.getForAssetRemoval(assetId); + + if (!stack?.id || stack.id !== stackId) { + throw new BadRequestException('Asset not in stack'); + } + + if (stack.primaryAssetId === assetId) { + throw new BadRequestException("Cannot remove stack's primary asset"); + } + + await this.assetRepository.update({ id: assetId, stackId: null }); + await this.eventRepository.emit('StackUpdate', { stackId, userId: auth.user.id }); + } + private async findOrFail(id: string) { const stack = await this.stackRepository.getById(id); if (!stack) { diff --git a/server/src/validation.ts b/server/src/validation.ts index 049b5432d6..3f7e1c6f3b 100644 --- a/server/src/validation.ts +++ b/server/src/validation.ts @@ -63,6 +63,22 @@ export class FileNotEmptyValidator extends FileValidator { } } +type UUIDOptions = { optional?: boolean; each?: boolean; nullable?: boolean }; +export const ValidateUUID = (options?: UUIDOptions & ApiPropertyOptions) => { + const { optional, each, nullable, ...apiPropertyOptions } = { + optional: false, + each: false, + nullable: false, + ...options, + }; + return applyDecorators( + IsUUID('4', { each }), + ApiProperty({ format: 'uuid', ...apiPropertyOptions }), + optional ? Optional({ nullable }) : IsNotEmpty(), + each ? IsArray() : IsString(), + ); +}; + export class UUIDParamDto { @IsNotEmpty() @IsUUID('4') @@ -70,6 +86,14 @@ export class UUIDParamDto { id!: string; } +export class UUIDAssetIDParamDto { + @ValidateUUID() + id!: string; + + @ValidateUUID() + assetId!: string; +} + type PinCodeOptions = { optional?: boolean } & OptionalOptions; export const PinCode = (options?: PinCodeOptions & ApiPropertyOptions) => { const { optional, nullable, emptyToNull, ...apiPropertyOptions } = { @@ -131,22 +155,6 @@ export const ValidateHexColor = () => { return applyDecorators(...decorators); }; -type UUIDOptions = { optional?: boolean; each?: boolean; nullable?: boolean }; -export const ValidateUUID = (options?: UUIDOptions & ApiPropertyOptions) => { - const { optional, each, nullable, ...apiPropertyOptions } = { - optional: false, - each: false, - nullable: false, - ...options, - }; - return applyDecorators( - IsUUID('4', { each }), - ApiProperty({ format: 'uuid', ...apiPropertyOptions }), - optional ? Optional({ nullable }) : IsNotEmpty(), - each ? IsArray() : IsString(), - ); -}; - type DateOptions = { optional?: boolean; nullable?: boolean; format?: 'date' | 'date-time' }; export const ValidateDate = (options?: DateOptions & ApiPropertyOptions) => { const { optional, nullable, format, ...apiPropertyOptions } = { diff --git a/server/test/fixtures/asset.stub.ts b/server/test/fixtures/asset.stub.ts index 991c5d2c4f..76cf71d34d 100644 --- a/server/test/fixtures/asset.stub.ts +++ b/server/test/fixtures/asset.stub.ts @@ -462,7 +462,7 @@ export const assetStub = { }), imageFrom2015: Object.freeze({ - id: 'asset-id-1', + id: 'asset-id-2015', status: AssetStatus.Active, deviceAssetId: 'device-asset-id', fileModifiedAt: new Date('2015-02-23T05:06:29.716Z'), @@ -484,6 +484,9 @@ export const assetStub = { duration: null, livePhotoVideo: null, livePhotoVideoId: null, + updateId: 'foo', + libraryId: null, + stackId: null, sharedLinks: [], originalFileName: 'asset-id.ext', faces: [], diff --git a/web/src/lib/components/asset-viewer/actions/action.ts b/web/src/lib/components/asset-viewer/actions/action.ts index d823f17df4..446a004cbb 100644 --- a/web/src/lib/components/asset-viewer/actions/action.ts +++ b/web/src/lib/components/asset-viewer/actions/action.ts @@ -1,6 +1,6 @@ import type { AssetAction } from '$lib/constants'; import type { TimelineAsset } from '$lib/managers/timeline-manager/types'; -import type { AlbumResponseDto, StackResponseDto } from '@immich/sdk'; +import type { AlbumResponseDto, AssetResponseDto, StackResponseDto } from '@immich/sdk'; type ActionMap = { [AssetAction.ARCHIVE]: { asset: TimelineAsset }; @@ -15,6 +15,7 @@ type ActionMap = { [AssetAction.UNSTACK]: { assets: TimelineAsset[] }; [AssetAction.KEEP_THIS_DELETE_OTHERS]: { asset: TimelineAsset }; [AssetAction.SET_STACK_PRIMARY_ASSET]: { stack: StackResponseDto }; + [AssetAction.REMOVE_ASSET_FROM_STACK]: { stack: StackResponseDto | null; asset: AssetResponseDto }; [AssetAction.SET_VISIBILITY_LOCKED]: { asset: TimelineAsset }; [AssetAction.SET_VISIBILITY_TIMELINE]: { asset: TimelineAsset }; }; diff --git a/web/src/lib/components/asset-viewer/actions/remove-asset-from-stack.svelte b/web/src/lib/components/asset-viewer/actions/remove-asset-from-stack.svelte new file mode 100644 index 0000000000..0c77f3a1a6 --- /dev/null +++ b/web/src/lib/components/asset-viewer/actions/remove-asset-from-stack.svelte @@ -0,0 +1,31 @@ + + + diff --git a/web/src/lib/components/asset-viewer/actions/unstack-action.svelte b/web/src/lib/components/asset-viewer/actions/unstack-action.svelte index 1adeead05f..0c8192a9e3 100644 --- a/web/src/lib/components/asset-viewer/actions/unstack-action.svelte +++ b/web/src/lib/components/asset-viewer/actions/unstack-action.svelte @@ -4,7 +4,7 @@ import { deleteStack } from '$lib/utils/asset-utils'; import { toTimelineAsset } from '$lib/utils/timeline-util'; import type { StackResponseDto } from '@immich/sdk'; - import { mdiImageMinusOutline } from '@mdi/js'; + import { mdiImageOffOutline } from '@mdi/js'; import { t } from 'svelte-i18n'; import type { OnAction } from './action'; @@ -23,4 +23,4 @@ }; - + diff --git a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte index a376c37139..66061ebb01 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte @@ -9,6 +9,7 @@ import DownloadAction from '$lib/components/asset-viewer/actions/download-action.svelte'; import FavoriteAction from '$lib/components/asset-viewer/actions/favorite-action.svelte'; import KeepThisDeleteOthersAction from '$lib/components/asset-viewer/actions/keep-this-delete-others.svelte'; + import RemoveAssetFromStack from '$lib/components/asset-viewer/actions/remove-asset-from-stack.svelte'; import RestoreAction from '$lib/components/asset-viewer/actions/restore-action.svelte'; import SetAlbumCoverAction from '$lib/components/asset-viewer/actions/set-album-cover-action.svelte'; import SetFeaturedPhotoAction from '$lib/components/asset-viewer/actions/set-person-featured-action.svelte'; @@ -195,6 +196,9 @@ {#if stack?.primaryAssetId !== asset.id} + {#if stack?.assets?.length > 2} + + {/if} {/if} {/if} {#if album} diff --git a/web/src/lib/components/asset-viewer/asset-viewer.svelte b/web/src/lib/components/asset-viewer/asset-viewer.svelte index 4f793e7b52..d82b2e6532 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer.svelte @@ -328,6 +328,13 @@ await handleGetAllAlbums(); break; } + case AssetAction.REMOVE_ASSET_FROM_STACK: { + stack = action.stack; + if (stack) { + asset = stack.assets[0]; + } + break; + } case AssetAction.SET_STACK_PRIMARY_ASSET: { stack = action.stack; break; diff --git a/web/src/lib/components/photos-page/actions/stack-action.svelte b/web/src/lib/components/photos-page/actions/stack-action.svelte index 1e817d9e61..8ed3dea22c 100644 --- a/web/src/lib/components/photos-page/actions/stack-action.svelte +++ b/web/src/lib/components/photos-page/actions/stack-action.svelte @@ -4,7 +4,7 @@ import type { OnStack, OnUnstack } from '$lib/utils/actions'; import { deleteStack, stackAssets } from '$lib/utils/asset-utils'; import { toTimelineAsset } from '$lib/utils/timeline-util'; - import { mdiImageMinusOutline, mdiImageMultipleOutline } from '@mdi/js'; + import { mdiImageMultipleOutline, mdiImageOffOutline } from '@mdi/js'; import { t } from 'svelte-i18n'; interface Props { @@ -42,7 +42,7 @@ {#if unstack} - + {:else} {/if} diff --git a/web/src/lib/components/photos-page/asset-grid.svelte b/web/src/lib/components/photos-page/asset-grid.svelte index 2d40857f99..182b24f3eb 100644 --- a/web/src/lib/components/photos-page/asset-grid.svelte +++ b/web/src/lib/components/photos-page/asset-grid.svelte @@ -523,6 +523,23 @@ updateUnstackedAssetInTimeline(timelineManager, action.assets); break; } + case AssetAction.REMOVE_ASSET_FROM_STACK: { + timelineManager.addAssets([toTimelineAsset(action.asset)]); + if (action.stack) { + //Have to unstack then restack assets in timeline in order to update the stack count in the timeline. + updateUnstackedAssetInTimeline( + timelineManager, + action.stack.assets.map((asset) => toTimelineAsset(asset)), + ); + updateStackedAssetInTimeline(timelineManager, { + stack: action.stack, + toDeleteIds: action.stack.assets + .filter((asset) => asset.id !== action.stack?.primaryAssetId) + .map((asset) => asset.id), + }); + } + break; + } case AssetAction.SET_STACK_PRIMARY_ASSET: { //Have to unstack then restack assets in timeline in order for the currently removed new primary asset to be made visible. updateUnstackedAssetInTimeline( diff --git a/web/src/lib/constants.ts b/web/src/lib/constants.ts index 1a40f8522e..b354989e17 100644 --- a/web/src/lib/constants.ts +++ b/web/src/lib/constants.ts @@ -11,6 +11,7 @@ export enum AssetAction { UNSTACK = 'unstack', KEEP_THIS_DELETE_OTHERS = 'keep-this-delete-others', SET_STACK_PRIMARY_ASSET = 'set-stack-primary-asset', + REMOVE_ASSET_FROM_STACK = 'remove-asset-from-stack', SET_VISIBILITY_LOCKED = 'set-visibility-locked', SET_VISIBILITY_TIMELINE = 'set-visibility-timeline', } From c7853fbe9d59b97ac2480a2e4ad105960bac1a8a Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 22 Jul 2025 23:21:58 -0500 Subject: [PATCH 021/748] chore: refactor download group (#20099) --- mobile/lib/constants/constants.dart | 3 +++ mobile/lib/main.dart | 8 ++++---- mobile/lib/repositories/download.repository.dart | 16 ++++++++-------- mobile/lib/services/download.service.dart | 8 ++++---- mobile/lib/utils/download.dart | 3 --- 5 files changed, 19 insertions(+), 19 deletions(-) delete mode 100644 mobile/lib/utils/download.dart diff --git a/mobile/lib/constants/constants.dart b/mobile/lib/constants/constants.dart index b54a1e9ca2..b3d9d138c4 100644 --- a/mobile/lib/constants/constants.dart +++ b/mobile/lib/constants/constants.dart @@ -20,6 +20,9 @@ const String kSecuredPinCode = "secured_pin_code"; const String kManualUploadGroup = 'manual_upload_group'; const String kBackupGroup = 'backup_group'; const String kBackupLivePhotoGroup = 'backup_live_photo_group'; +const String kDownloadGroupImage = 'group_image'; +const String kDownloadGroupVideo = 'group_video'; +const String kDownloadGroupLivePhoto = 'group_livephoto'; // Timeline constants const int kTimelineNoneSegmentSize = 120; diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index f036fd9bc3..9f39a89b33 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -10,6 +10,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_displaymode/flutter_displaymode.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/constants/locales.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/generated/codegen_loader.g.dart'; @@ -29,7 +30,6 @@ import 'package:immich_mobile/theme/dynamic_theme.dart'; import 'package:immich_mobile/theme/theme_data.dart'; import 'package:immich_mobile/utils/bootstrap.dart'; import 'package:immich_mobile/utils/cache/widgets_binding.dart'; -import 'package:immich_mobile/utils/download.dart'; import 'package:immich_mobile/utils/http_ssl_options.dart'; import 'package:immich_mobile/utils/licenses.dart'; import 'package:immich_mobile/utils/migration.dart'; @@ -101,7 +101,7 @@ Future initApp() async { ); await FileDownloader().trackTasksInGroup( - downloadGroupLivePhoto, + kDownloadGroupLivePhoto, markDownloadedComplete: false, ); @@ -179,7 +179,7 @@ class ImmichAppState extends ConsumerState void _configureFileDownloaderNotifications() { FileDownloader().configureNotificationForGroup( - downloadGroupImage, + kDownloadGroupImage, running: TaskNotification( 'downloading_media'.tr(), '${'file_name'.tr()}: {filename}', @@ -192,7 +192,7 @@ class ImmichAppState extends ConsumerState ); FileDownloader().configureNotificationForGroup( - downloadGroupVideo, + kDownloadGroupVideo, running: TaskNotification( 'downloading_media'.tr(), '${'file_name'.tr()}: {filename}', diff --git a/mobile/lib/repositories/download.repository.dart b/mobile/lib/repositories/download.repository.dart index f1dae3c251..a1ad4ee3d9 100644 --- a/mobile/lib/repositories/download.repository.dart +++ b/mobile/lib/repositories/download.repository.dart @@ -4,10 +4,10 @@ import 'dart:io'; import 'package:background_downloader/background_downloader.dart'; import 'package:collection/collection.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/models/download/livephotos_medatada.model.dart'; import 'package:immich_mobile/services/api.service.dart'; -import 'package:immich_mobile/utils/download.dart'; import 'package:immich_mobile/utils/image_url_builder.dart'; final downloadRepositoryProvider = Provider((ref) => DownloadRepository()); @@ -33,19 +33,19 @@ class DownloadRepository { DownloadRepository() { _downloader.registerCallbacks( - group: downloadGroupImage, + group: kDownloadGroupImage, taskStatusCallback: (update) => onImageDownloadStatus?.call(update), taskProgressCallback: (update) => onTaskProgress?.call(update), ); _downloader.registerCallbacks( - group: downloadGroupVideo, + group: kDownloadGroupVideo, taskStatusCallback: (update) => onVideoDownloadStatus?.call(update), taskProgressCallback: (update) => onTaskProgress?.call(update), ); _downloader.registerCallbacks( - group: downloadGroupLivePhoto, + group: kDownloadGroupLivePhoto, taskStatusCallback: (update) => onLivePhotoDownloadStatus?.call(update), taskProgressCallback: (update) => onTaskProgress?.call(update), ); @@ -66,7 +66,7 @@ class DownloadRepository { Future> getLiveVideoTasks() { return _downloader.database.allRecordsWithStatus( TaskStatus.complete, - group: downloadGroupLivePhoto, + group: kDownloadGroupLivePhoto, ); } @@ -100,7 +100,7 @@ class DownloadRepository { headers: headers, filename: asset.name, updates: Updates.statusAndProgress, - group: isVideo ? downloadGroupVideo : downloadGroupImage, + group: isVideo ? kDownloadGroupVideo : kDownloadGroupImage, ); continue; } @@ -113,7 +113,7 @@ class DownloadRepository { headers: headers, filename: asset.name, updates: Updates.statusAndProgress, - group: downloadGroupLivePhoto, + group: kDownloadGroupLivePhoto, metaData: json.encode(_dummyMetadata), ); @@ -126,7 +126,7 @@ class DownloadRepository { .toUpperCase() .replaceAll(RegExp(r"\.(JPG|HEIC)$"), '.MOV'), updates: Updates.statusAndProgress, - group: downloadGroupLivePhoto, + group: kDownloadGroupLivePhoto, metaData: json.encode(_dummyMetadata), ); } diff --git a/mobile/lib/services/download.service.dart b/mobile/lib/services/download.service.dart index 98f1765d04..fbd3f406fd 100644 --- a/mobile/lib/services/download.service.dart +++ b/mobile/lib/services/download.service.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:background_downloader/background_downloader.dart'; import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; @@ -10,7 +11,6 @@ import 'package:immich_mobile/models/download/livephotos_medatada.model.dart'; import 'package:immich_mobile/repositories/download.repository.dart'; import 'package:immich_mobile/repositories/file_media.repository.dart'; import 'package:immich_mobile/services/api.service.dart'; -import 'package:immich_mobile/utils/download.dart'; import 'package:logging/logging.dart'; final downloadServiceProvider = Provider( @@ -173,7 +173,7 @@ class DownloadService { _buildDownloadTask( asset.remoteId!, asset.fileName, - group: downloadGroupLivePhoto, + group: kDownloadGroupLivePhoto, metadata: LivePhotosMetadata( part: LivePhotosPart.image, id: asset.remoteId!, @@ -184,7 +184,7 @@ class DownloadService { asset.fileName .toUpperCase() .replaceAll(RegExp(r"\.(JPG|HEIC)$"), '.MOV'), - group: downloadGroupLivePhoto, + group: kDownloadGroupLivePhoto, metadata: LivePhotosMetadata( part: LivePhotosPart.video, id: asset.remoteId!, @@ -201,7 +201,7 @@ class DownloadService { _buildDownloadTask( asset.remoteId!, asset.fileName, - group: asset.isImage ? downloadGroupImage : downloadGroupVideo, + group: asset.isImage ? kDownloadGroupImage : kDownloadGroupVideo, ), ]; } diff --git a/mobile/lib/utils/download.dart b/mobile/lib/utils/download.dart deleted file mode 100644 index c701f353a2..0000000000 --- a/mobile/lib/utils/download.dart +++ /dev/null @@ -1,3 +0,0 @@ -const downloadGroupImage = 'group_image'; -const downloadGroupVideo = 'group_video'; -const downloadGroupLivePhoto = 'group_livephoto'; From c91382625cf3939a35bc74d8da9e651a0031a17e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 23 Jul 2025 13:26:28 +0100 Subject: [PATCH 022/748] fix(deps): update typescript-projects (#20103) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Zack Pollard --- e2e/package-lock.json | 6 +- server/package-lock.json | 447 +++++++++++++++++++-------------------- server/package.json | 4 +- 3 files changed, 223 insertions(+), 234 deletions(-) diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 3ff7670739..278d3a7f93 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -5192,9 +5192,9 @@ } }, "node_modules/oidc-provider": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/oidc-provider/-/oidc-provider-9.3.0.tgz", - "integrity": "sha512-JVocwYM+Fs76nOCED2hMf3iMfrzhN4jISmCYVuFhBEnZiFk3QlODzQXkO1XS/Spw8VwRlKxwIl3otkiintFIjw==", + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/oidc-provider/-/oidc-provider-9.4.0.tgz", + "integrity": "sha512-1mEUejJq7cQV/b6cw2nitqOyIlOJTfQ6RNwGFcA7/Pp+vKIWBn8p48ylFtogP3Hbvrkf9s9W5HUeFe+v1KpcEQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/server/package-lock.json b/server/package-lock.json index 207776873e..c0c048c554 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -54,7 +54,7 @@ "i18n-iso-countries": "^7.6.0", "ioredis": "^5.3.2", "js-yaml": "^4.1.0", - "kysely": "^0.28.0", + "kysely": "^0.28.2", "kysely-postgres-js": "^2.0.0", "lodash": "^4.17.21", "luxon": "^3.4.2", @@ -297,6 +297,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@angular-devkit/schematics-cli/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@angular-devkit/schematics-cli/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -369,6 +382,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@angular-devkit/schematics/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@angular-devkit/schematics/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -1159,9 +1185,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", - "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1209,9 +1235,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz", - "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", + "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", "dev": true, "license": "MIT", "engines": { @@ -1245,19 +1271,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.0.tgz", - "integrity": "sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@golevelup/nestjs-discovery": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/@golevelup/nestjs-discovery/-/nestjs-discovery-4.0.3.tgz", @@ -2736,6 +2749,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@nestjs/cli/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@nestjs/cli/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -2757,9 +2783,9 @@ } }, "node_modules/@nestjs/common": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.3.tgz", - "integrity": "sha512-ogEK+GriWodIwCw6buQ1rpcH4Kx+G7YQ9EwuPySI3rS05pSdtQ++UhucjusSI9apNidv+QURBztJkRecwwJQXg==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.5.tgz", + "integrity": "sha512-DQpWdr3ShO0BHWkHl3I4W/jR6R3pDtxyBlmrpTuZF+PXxQyBXNvsUne0Wyo6QHPEDi+pAz9XchBFoKbqOhcdTg==", "license": "MIT", "dependencies": { "file-type": "21.0.0", @@ -2788,9 +2814,9 @@ } }, "node_modules/@nestjs/core": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.3.tgz", - "integrity": "sha512-5lTni0TCh8x7bXETRD57pQFnKnEg1T6M+VLE7wAmyQRIecKQU+2inRGZD+A4v2DC1I04eA0WffP0GKLxjOKlzw==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.5.tgz", + "integrity": "sha512-Qr25MEY9t8VsMETy7eXQ0cNXqu0lzuFrrTr+f+1G57ABCtV5Pogm7n9bF71OU2bnkDD32Bi4hQLeFR90cku3Tw==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -2862,14 +2888,14 @@ } }, "node_modules/@nestjs/platform-express": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.3.tgz", - "integrity": "sha512-hEDNMlaPiBO72fxxX/CuRQL3MEhKRc/sIYGVoXjrnw6hTxZdezvvM6A95UaLsYknfmcZZa/CdG1SMBZOu9agHQ==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.5.tgz", + "integrity": "sha512-OsoiUBY9Shs5IG3uvDIt9/IDfY5OlvWBESuB/K4Eun8xILw1EK5d5qMfC3d2sIJ+kA3l+kBR1d/RuzH7VprLIg==", "license": "MIT", "dependencies": { "cors": "2.8.5", "express": "5.1.0", - "multer": "2.0.1", + "multer": "2.0.2", "path-to-regexp": "8.2.0", "tslib": "2.8.1" }, @@ -2882,71 +2908,10 @@ "@nestjs/core": "^11.0.0" } }, - "node_modules/@nestjs/platform-express/node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@nestjs/platform-express/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@nestjs/platform-express/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@nestjs/platform-express/node_modules/multer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.1.tgz", - "integrity": "sha512-Ug8bXeTIUlxurg8xLTEskKShvcKDZALo1THEX5E41pYCD2sCVub5/kIRIGqWNoqV6szyLyQKV6mD4QUrWE5GCQ==", - "license": "MIT", - "dependencies": { - "append-field": "^1.0.0", - "busboy": "^1.6.0", - "concat-stream": "^2.0.0", - "mkdirp": "^0.5.6", - "object-assign": "^4.1.1", - "type-is": "^1.6.18", - "xtend": "^4.0.2" - }, - "engines": { - "node": ">= 10.16.0" - } - }, - "node_modules/@nestjs/platform-express/node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/@nestjs/platform-socket.io": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-11.1.3.tgz", - "integrity": "sha512-jQ+ccprmh3kKolBp+bb97zoaS3vKaiyeNqyctGqV4CSG8P6mXSaaUObWxAsw6Jdgn5YQAVEBWJ6FhvF4s6QZbg==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-11.1.5.tgz", + "integrity": "sha512-DY3zNY+BjbrYpV/t8HL8ptrusrWK8J0cfkfY1iZsfCd+0/1+j8IKno+QMLkerNQAZ7/Frh5tkaKHVwWk18TkMw==", "license": "MIT", "dependencies": { "socket.io": "4.8.1", @@ -3063,6 +3028,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@nestjs/schematics/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@nestjs/schematics/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -3117,9 +3095,9 @@ } }, "node_modules/@nestjs/testing": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.3.tgz", - "integrity": "sha512-CeXG6/eEqgFIkPkmU00y18Dd3DLOIDFhPItzJK1SWckKo6IhcnfoRJzGx75bmuvUMjb51j6An96S/+MJ2ty9jA==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.5.tgz", + "integrity": "sha512-ZYRYF750SefmuIo7ZqPlHDcin1OHh6My0OkOfGEFjrD9mJ0vMVIpwMTOOkpzCfCcpqUuxeHBuecpiIn+NLrQbQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3145,9 +3123,9 @@ } }, "node_modules/@nestjs/websockets": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-11.1.3.tgz", - "integrity": "sha512-IjhWKfRf0D247JxYIEs8USblJJbcxUsKJpzbCPaZ7TrVy4LrpG3IRQDlSTOw599TRIYP5ixyH9C0+v5DyaI9uA==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-11.1.5.tgz", + "integrity": "sha512-mAM11HwyS7aeSUbXdOqHbNCRoHwB0OOb+cmx5sgxvszhdG0Y6bwR60nKA4+EXL9xUEeWoxmbfLmSHlTSIJ9GKA==", "license": "MIT", "dependencies": { "iterare": "1.2.1", @@ -5838,9 +5816,9 @@ } }, "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.34.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.34.0.tgz", - "integrity": "sha512-aKcOkyrorBGlajjRdVoJWHTxfxO1vCNHLJVlSDaRHDIdjU+pX8IYQPvPDkYiujKLbRnWU+1TBwEt0QRgSm4SGA==", + "version": "1.36.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.36.0.tgz", + "integrity": "sha512-TtxJSRD8Ohxp6bKkhrm27JRHAxPczQA7idtcTOMYI+wQRRrfgqxHv1cFbCApcSnNjtXkmzFozn6jQtFrOmbjPQ==", "license": "Apache-2.0", "engines": { "node": ">=14" @@ -6627,9 +6605,9 @@ "license": "MIT" }, "node_modules/@swc/core": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.12.11.tgz", - "integrity": "sha512-P3GM+0lqjFctcp5HhR9mOcvLSX3SptI9L1aux0Fuvgt8oH4f92rCUrkodAa0U2ktmdjcyIiG37xg2mb/dSCYSA==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.0.tgz", + "integrity": "sha512-7Fh16ZH/Rj3Di720if+sw9BictD4N5kbTpsyDC+URXhvsZ7qRt1lH7PaeIQYyJJQHwFhoKpwwGxfGU9SHgPLdw==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -6645,16 +6623,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.12.11", - "@swc/core-darwin-x64": "1.12.11", - "@swc/core-linux-arm-gnueabihf": "1.12.11", - "@swc/core-linux-arm64-gnu": "1.12.11", - "@swc/core-linux-arm64-musl": "1.12.11", - "@swc/core-linux-x64-gnu": "1.12.11", - "@swc/core-linux-x64-musl": "1.12.11", - "@swc/core-win32-arm64-msvc": "1.12.11", - "@swc/core-win32-ia32-msvc": "1.12.11", - "@swc/core-win32-x64-msvc": "1.12.11" + "@swc/core-darwin-arm64": "1.13.0", + "@swc/core-darwin-x64": "1.13.0", + "@swc/core-linux-arm-gnueabihf": "1.13.0", + "@swc/core-linux-arm64-gnu": "1.13.0", + "@swc/core-linux-arm64-musl": "1.13.0", + "@swc/core-linux-x64-gnu": "1.13.0", + "@swc/core-linux-x64-musl": "1.13.0", + "@swc/core-win32-arm64-msvc": "1.13.0", + "@swc/core-win32-ia32-msvc": "1.13.0", + "@swc/core-win32-x64-msvc": "1.13.0" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" @@ -6666,9 +6644,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.12.11.tgz", - "integrity": "sha512-J19Jj9Y5x/N0loExH7W0OI9OwwoVyxutDdkyq1o/kgXyBqmmzV7Y/Q9QekI2Fm/qc5mNeAdP7aj4boY4AY/JPw==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.0.tgz", + "integrity": "sha512-SkmR9u7MHDu2X8hf7SjZTmsAfQTmel0mi+TJ7AGtufLwGySv6pwQfJ/CIJpcPxYENVqDJAFnDrHaKV8mgA6kxQ==", "cpu": [ "arm64" ], @@ -6683,9 +6661,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.12.11.tgz", - "integrity": "sha512-PTuUQrfStQ6cjW+uprGO2lpQHy84/l0v+GqRqq8s/jdK55rFRjMfCeyf6FAR0l6saO5oNOQl+zWR1aNpj8pMQw==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.13.0.tgz", + "integrity": "sha512-15/SyDjXRtFJ09fYHBXUXrj4tpiSpCkjgsF1z3/sSpHH1POWpQUQzxmFyomPQVZ/SsDqP18WGH09Vph4Qriuiw==", "cpu": [ "x64" ], @@ -6700,9 +6678,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.12.11.tgz", - "integrity": "sha512-poxBq152HsupOtnZilenvHmxZ9a8SRj4LtfxUnkMDNOGrZR9oxbQNwEzNKfi3RXEcXz+P8c0Rai1ubBazXv8oQ==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.13.0.tgz", + "integrity": "sha512-AHauVHZQEJI/dCZQg6VYNNQ6HROz8dSOnCSheXzzBw1DGWo77BlcxRP0fF0jaAXM9WNqtCUOY1HiJ9ohkAE61Q==", "cpu": [ "arm" ], @@ -6717,9 +6695,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.12.11.tgz", - "integrity": "sha512-y1HNamR/D0Hc8xIE910ysyLe269UYiGaQPoLjQS0phzWFfWdMj9bHM++oydVXZ4RSWycO7KyJ3uvw4NilvyMKQ==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.0.tgz", + "integrity": "sha512-qyZmBZF7asF6954/x7yn6R7Bzd45KRG05rK2atIF9J3MTa8az7vubP1Q3BWmmss1j8699DELpbuoJucGuhsNXw==", "cpu": [ "arm64" ], @@ -6734,9 +6712,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.12.11.tgz", - "integrity": "sha512-LlBxPh/32pyQsu2emMEOFRm7poEFLsw12Y1mPY7FWZiZeptomKSOSHRzKDz9EolMiV4qhK1caP1lvW4vminYgQ==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.13.0.tgz", + "integrity": "sha512-whskQCOUlLQT7MjnronpHmyHegBka5ig9JkQvecbqhWzRfdwN+c2xTJs3kQsWy2Vc2f1hcL3D8hGIwY5TwPxMQ==", "cpu": [ "arm64" ], @@ -6751,9 +6729,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.12.11.tgz", - "integrity": "sha512-bOjiZB8O/1AzHkzjge1jqX62HGRIpOHqFUrGPfAln/NC6NR+Z2A78u3ixV7k5KesWZFhCV0YVGJL+qToL27myA==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.13.0.tgz", + "integrity": "sha512-51n4P4nv6rblXyH3zCEktvmR9uSAZ7+zbfeby0sxbj8LS/IKuVd7iCwD5dwMj4CxG9Fs+HgjN73dLQF/OerHhg==", "cpu": [ "x64" ], @@ -6768,9 +6746,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.12.11.tgz", - "integrity": "sha512-4dzAtbT/m3/UjF045+33gLiHd8aSXJDoqof7gTtu4q0ZyAf7XJ3HHspz+/AvOJLVo4FHHdFcdXhmo/zi1nFn8A==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.13.0.tgz", + "integrity": "sha512-VMqelgvnXs27eQyhDf1S2O2MxSdchIH7c1tkxODRtu9eotcAeniNNgqqLjZ5ML0MGeRk/WpbsAY/GWi7eSpiHw==", "cpu": [ "x64" ], @@ -6785,9 +6763,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.12.11.tgz", - "integrity": "sha512-h8HiwBZErKvCAmjW92JvQp0iOqm6bncU4ac5jxBGkRApabpUenNJcj3h2g5O6GL5K6T9/WhnXE5gyq/s1fhPQg==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.13.0.tgz", + "integrity": "sha512-NLJmseWJngWeENgat+O/WB4ptNxtx2X4OfPnSG5a/A4sxcn2E4jq91OPvbeUQwDkH+ZQWKXmbXFzt7Nn661QYA==", "cpu": [ "arm64" ], @@ -6802,9 +6780,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.12.11.tgz", - "integrity": "sha512-1pwr325mXRNUhxTtXmx1IokV5SiRL+6iDvnt3FRXj+X5UvXXKtg2zeyftk+03u8v8v8WUr5I32hIypVJPTNxNg==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.13.0.tgz", + "integrity": "sha512-UBfwrp0xW37KQGTA08mwrCLIm1ZKy6pXK8IVwou7BvhMgrItRNweTGyUrCnvDLUfyYFuJCmzcEaJ3NudtctD6g==", "cpu": [ "ia32" ], @@ -6819,9 +6797,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.12.11.tgz", - "integrity": "sha512-5gggWo690Gvs7XiPxAmb5tHwzB9RTVXUV7AWoGb6bmyUd1OXYaebQF0HAOtade5jIoNhfQMQJ7QReRgt/d2jAA==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.13.0.tgz", + "integrity": "sha512-BAB1P7Z/y2EENsfsPytPnjIyBVRZN2WULY+s3ozW4QkGmYHde6XXG28n0ABTHhcIOmmR2VzM+uaW1x48laSimw==", "cpu": [ "x64" ], @@ -7500,17 +7478,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.36.0.tgz", - "integrity": "sha512-lZNihHUVB6ZZiPBNgOQGSxUASI7UJWhT8nHyUGCnaQ28XFCw98IfrMCG3rUl1uwUWoAvodJQby2KTs79UTcrAg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", + "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/type-utils": "8.36.0", - "@typescript-eslint/utils": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/type-utils": "8.37.0", + "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -7524,7 +7502,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.36.0", + "@typescript-eslint/parser": "^8.37.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } @@ -7540,16 +7518,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.36.0.tgz", - "integrity": "sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", + "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4" }, "engines": { @@ -7565,14 +7543,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", - "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", + "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.36.0", - "@typescript-eslint/types": "^8.36.0", + "@typescript-eslint/tsconfig-utils": "^8.37.0", + "@typescript-eslint/types": "^8.37.0", "debug": "^4.3.4" }, "engines": { @@ -7587,14 +7565,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", - "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", + "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0" + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7605,9 +7583,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", - "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", + "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", "dev": true, "license": "MIT", "engines": { @@ -7622,14 +7600,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.36.0.tgz", - "integrity": "sha512-5aaGYG8cVDd6cxfk/ynpYzxBRZJk7w/ymto6uiyUFtdCozQIsQWh7M28/6r57Fwkbweng8qAzoMCPwSJfWlmsg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", + "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/utils": "8.36.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/utils": "8.37.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -7646,9 +7625,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", - "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", + "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", "dev": true, "license": "MIT", "engines": { @@ -7660,16 +7639,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", - "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", + "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.36.0", - "@typescript-eslint/tsconfig-utils": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/project-service": "8.37.0", + "@typescript-eslint/tsconfig-utils": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -7715,16 +7694,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz", - "integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", + "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0" + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7739,13 +7718,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", - "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", + "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", + "@typescript-eslint/types": "8.37.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -8905,9 +8884,9 @@ } }, "node_modules/bullmq": { - "version": "5.56.2", - "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.56.2.tgz", - "integrity": "sha512-bq0PSxPCWeNlFBc5yjBs3eR+e6GxIEIeHY0xxq6WELzG65GPjL+A2ni1NS7NroKsur0C3UJdabw51IswiSTSYw==", + "version": "5.56.4", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.56.4.tgz", + "integrity": "sha512-5wSHd0oXs2jS6P+6tay/01Iz0cWRK8iYcscKtpS/GewEq0bJZwbkMZc77GJVOT9SP+UQuXA2y+pQTdCQJel7kQ==", "license": "MIT", "dependencies": { "cron-parser": "^4.9.0", @@ -9556,16 +9535,16 @@ } }, "node_modules/compression": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz", - "integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", "license": "MIT", "dependencies": { "bytes": "3.1.2", "compressible": "~2.0.18", "debug": "2.6.9", "negotiator": "~0.6.4", - "on-headers": "~1.0.2", + "on-headers": "~1.1.0", "safe-buffer": "5.2.1", "vary": "~1.1.2" }, @@ -9588,6 +9567,15 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/compression/node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -10685,9 +10673,9 @@ } }, "node_modules/eslint": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.1.tgz", - "integrity": "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", + "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10695,9 +10683,9 @@ "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.14.0", + "@eslint/core": "^0.15.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.30.1", + "@eslint/js": "9.31.0", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -13823,9 +13811,9 @@ "license": "MIT" }, "node_modules/nest-commander": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/nest-commander/-/nest-commander-3.17.0.tgz", - "integrity": "sha512-1R9vppZT2j/9njKiG0zYTDLAyQOj14KdGWdNuhluveK8VXoQepXNb0t09dRNWy4KCWrI7wDZ2tQTEwb43JyHOw==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/nest-commander/-/nest-commander-3.18.0.tgz", + "integrity": "sha512-NWtodOl2aStnApWp9oajCoJW71lqN0CCjf9ygOWxpXnG3o4nQ8ZO5CgrExfVw2+0CVC877hr0rFR7FSu2rypGg==", "license": "MIT", "dependencies": { "@fig/complete-commander": "^3.0.0", @@ -13926,9 +13914,9 @@ "license": "MIT" }, "node_modules/node-addon-api": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.4.0.tgz", - "integrity": "sha512-D9DI/gXHvVmjHS08SVch0Em8G5S1P+QWtU31appcKT/8wFSPRcdHadIFSAntdMMVM5zz+/DL+bL/gz3UDppqtg==", + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", "license": "MIT", "engines": { "node": "^18 || ^20 || >= 21" @@ -14687,9 +14675,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", "engines": { "node": ">=12" @@ -15397,9 +15385,9 @@ } }, "node_modules/react-email": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/react-email/-/react-email-4.1.0.tgz", - "integrity": "sha512-UvG5z1/gNOsLNwKPO87vgMoF7tdzUGd0kIy4fozzdBBsyLUju7hNVLBRm9j+Li/CwP5CXFT8Y5jZBtIFvSyr0w==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/react-email/-/react-email-4.2.3.tgz", + "integrity": "sha512-LUKyk9nNVFuTqAyp4yCEQFQjBe+s8nl3VauMWuOhBZ4VhGnimbrnv01U8yD2YwzaHKtytS0U659x5dc/0+xu+Q==", "license": "MIT", "dependencies": { "@babel/parser": "^7.27.0", @@ -18447,15 +18435,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.36.0.tgz", - "integrity": "sha512-fTCqxthY+h9QbEgSIBfL9iV6CvKDFuoxg6bHPNpJ9HIUzS+jy2lCEyCmGyZRWEBSaykqcDPf1SJ+BfCI8DRopA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.37.0.tgz", + "integrity": "sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.36.0", - "@typescript-eslint/parser": "8.36.0", - "@typescript-eslint/utils": "8.36.0" + "@typescript-eslint/eslint-plugin": "8.37.0", + "@typescript-eslint/parser": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/utils": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/server/package.json b/server/package.json index d461f1d767..5dbe83381a 100644 --- a/server/package.json +++ b/server/package.json @@ -79,7 +79,7 @@ "i18n-iso-countries": "^7.6.0", "ioredis": "^5.3.2", "js-yaml": "^4.1.0", - "kysely": "^0.28.0", + "kysely": "^0.28.2", "kysely-postgres-js": "^2.0.0", "lodash": "^4.17.21", "luxon": "^3.4.2", @@ -113,7 +113,6 @@ "validator": "^13.12.0" }, "devDependencies": { - "canvas": "^3.1.0", "@eslint/eslintrc": "^3.1.0", "@eslint/js": "^9.8.0", "@nestjs/cli": "^11.0.2", @@ -146,6 +145,7 @@ "@types/ua-parser-js": "^0.7.36", "@types/validator": "^13.15.2", "@vitest/coverage-v8": "^3.0.0", + "canvas": "^3.1.0", "eslint": "^9.14.0", "eslint-config-prettier": "^10.0.0", "eslint-plugin-prettier": "^5.1.3", From 05d26dc68390b3831162bf09539b7270dd36875a Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Wed, 23 Jul 2025 18:53:49 +0530 Subject: [PATCH 023/748] fix: remove safe area from bottom bar (#20102) * remove safe area from bottom bar * fix: video not playing in search view * stop foreground / background back on migration --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- .../pages/common/change_experience.page.dart | 15 +++++++ .../asset_viewer/bottom_bar.widget.dart | 45 +++++++++---------- .../asset_viewer/video_viewer.widget.dart | 22 ++++++++- 3 files changed, 58 insertions(+), 24 deletions(-) diff --git a/mobile/lib/pages/common/change_experience.page.dart b/mobile/lib/pages/common/change_experience.page.dart index a8569b25a0..3cd545ea33 100644 --- a/mobile/lib/pages/common/change_experience.page.dart +++ b/mobile/lib/pages/common/change_experience.page.dart @@ -2,10 +2,14 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; import 'package:immich_mobile/providers/asset.provider.dart'; import 'package:immich_mobile/providers/background_sync.provider.dart'; +import 'package:immich_mobile/providers/backup/backup.provider.dart'; +import 'package:immich_mobile/providers/backup/manual_upload.provider.dart'; import 'package:immich_mobile/providers/gallery_permission.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/providers/websocket.provider.dart'; @@ -43,6 +47,17 @@ class _ChangeExperiencePageState extends ConsumerState { albumNotifier.dispose(); } + // Cancel uploads + await Store.put(StoreKey.backgroundBackup, false); + ref.read(backupProvider.notifier).configureBackgroundBackup( + enabled: false, + onBatteryInfo: () {}, + onError: (_) {}, + ); + ref.read(backupProvider.notifier).setAutoBackup(false); + ref.read(backupProvider.notifier).cancelBackup(); + ref.read(manualUploadProvider.notifier).cancelBackup(); + // Start listening to new websocket events ref.read(websocketProvider.notifier).stopListenToOldEvents(); ref.read(websocketProvider.notifier).startListeningToBetaEvents(); diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart index d35a315f48..cb558804d2 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart @@ -50,31 +50,30 @@ class ViewerBottomBar extends ConsumerWidget { duration: Durations.short4, child: isSheetOpen ? const SizedBox.shrink() - : SafeArea( - child: Theme( - data: context.themeData.copyWith( - iconTheme: - const IconThemeData(size: 22, color: Colors.white), - textTheme: context.themeData.textTheme.copyWith( - labelLarge: - context.themeData.textTheme.labelLarge?.copyWith( - color: Colors.white, - ), + : Theme( + data: context.themeData.copyWith( + iconTheme: + const IconThemeData(size: 22, color: Colors.white), + textTheme: context.themeData.textTheme.copyWith( + labelLarge: + context.themeData.textTheme.labelLarge?.copyWith( + color: Colors.white, ), ), - child: Container( - height: asset.isVideo ? 160 : 80, - color: Colors.black.withAlpha(125), - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - if (asset.isVideo) const VideoControls(), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: actions, - ), - ], - ), + ), + child: Container( + height: context.padding.bottom + (asset.isVideo ? 160 : 80), + color: Colors.black.withAlpha(125), + padding: EdgeInsets.only(bottom: context.padding.bottom), + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + if (asset.isVideo) const VideoControls(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: actions, + ), + ], ), ), ), diff --git a/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart index 17880da3e7..2accf465ed 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart @@ -27,6 +27,26 @@ import 'package:logging/logging.dart'; import 'package:native_video_player/native_video_player.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; +bool _isCurrentAsset( + BaseAsset asset, + BaseAsset? currentAsset, +) { + if (asset is RemoteAsset) { + return switch (currentAsset) { + RemoteAsset remoteAsset => remoteAsset.id == asset.id, + LocalAsset localAsset => localAsset.remoteId == asset.id, + _ => false, + }; + } else if (asset is LocalAsset) { + return switch (currentAsset) { + RemoteAsset remoteAsset => remoteAsset.localId == asset.id, + LocalAsset localAsset => localAsset.id == asset.id, + _ => false, + }; + } + return false; +} + class NativeVideoViewer extends HookConsumerWidget { final BaseAsset asset; final bool showControls; @@ -56,7 +76,7 @@ class NativeVideoViewer extends HookConsumerWidget { // If the swipe is completed, `isCurrent` will be true for video B after a delay. // If the swipe is canceled, `currentAsset` will not have changed and video A will continue to play. final currentAsset = useState(ref.read(currentAssetNotifier)); - final isCurrent = currentAsset.value == asset; + final isCurrent = _isCurrentAsset(asset, currentAsset.value); // Used to show the placeholder during hero animations for remote videos to avoid a stutter final isVisible = useState(Platform.isIOS && asset.hasLocal); From 1d9cc4ca5f0dec7fe402f69d6a4016979fdf3beb Mon Sep 17 00:00:00 2001 From: "Weblate (bot)" Date: Wed, 23 Jul 2025 15:38:29 +0200 Subject: [PATCH 024/748] chore(web): update translations (#20082) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alberto Serluca Co-authored-by: DevServs Co-authored-by: Florian Ostertag Co-authored-by: Gilbert Co-authored-by: Indrek Haav Co-authored-by: Jozef Gaal Co-authored-by: KecskeTech Co-authored-by: Kuno Claes Co-authored-by: MSDNicrosoft Co-authored-by: Matjaž T Co-authored-by: Mārtiņš Bruņenieks Co-authored-by: Vegard Fladby Co-authored-by: adri1m64 Co-authored-by: waclaw66 --- i18n/cs.json | 23 +++++++- i18n/de.json | 21 ++++++++ i18n/et.json | 23 +++++++- i18n/fr.json | 28 ++++++++++ i18n/hu.json | 58 +++++++++++--------- i18n/id.json | 10 +++- i18n/it.json | 12 +++++ i18n/lv.json | 11 ++++ i18n/nb_NO.json | 23 +++++++- i18n/nl.json | 24 ++++++++- i18n/ru.json | 47 +++++++++++----- i18n/sk.json | 117 +++++++++++++++++++++++----------------- i18n/sl.json | 25 ++++++++- i18n/zh_SIMPLIFIED.json | 23 +++++++- 14 files changed, 350 insertions(+), 95 deletions(-) diff --git a/i18n/cs.json b/i18n/cs.json index c14a4b6d0c..80ea9674c0 100644 --- a/i18n/cs.json +++ b/i18n/cs.json @@ -573,6 +573,8 @@ "backup_options_page_title": "Nastavení záloh", "backup_setting_subtitle": "Správa nastavení zálohování na pozadí a na popředí", "backward": "Pozpátku", + "beta_sync": "Stav synchronizace beta verze", + "beta_sync_subtitle": "Správa nového systému synchronizace", "biometric_auth_enabled": "Biometrické ověřování je povoleno", "biometric_locked_out": "Jste vyloučeni z biometrického ověřování", "biometric_no_options": "Biometrické možnosti nejsou k dispozici", @@ -590,7 +592,7 @@ "cache_settings_clear_cache_button": "Vymazat vyrovnávací paměť", "cache_settings_clear_cache_button_title": "Vymaže vyrovnávací paměť aplikace. To výrazně ovlivní výkon aplikace, dokud se vyrovnávací paměť neobnoví.", "cache_settings_duplicated_assets_clear_button": "VYMAZAT", - "cache_settings_duplicated_assets_subtitle": "Fotografie a videa, které aplikace zařadila na černou listinu", + "cache_settings_duplicated_assets_subtitle": "Fotografie a videa, které aplikace ignoruje", "cache_settings_duplicated_assets_title": "Duplicitní položky ({count})", "cache_settings_statistics_album": "Knihovna náhledů", "cache_settings_statistics_full": "Kompletní fotografie", @@ -1051,6 +1053,9 @@ "haptic_feedback_switch": "Povolit dotykovou zpětnou vazbu", "haptic_feedback_title": "Dotyková zpětná vazba", "has_quota": "Má kvótu", + "hash_asset": "Hash položky", + "hashed_assets": "Hashované položky", + "hashing": "Hashování", "header_settings_add_header_tip": "Přidat hlavičku", "header_settings_field_validator_msg": "Hodnota nemůže být prázdná", "header_settings_header_name_input": "Název hlavičky", @@ -1083,6 +1088,7 @@ "host": "Hostitel", "hour": "Hodina", "id": "ID", + "idle": "Nečinnost", "ignore_icloud_photos": "Ignorovat fotografie na iCloudu", "ignore_icloud_photos_description": "Fotografie uložené na iCloudu se nebudou nahrávat na Immich server", "image": "Obrázek", @@ -1165,7 +1171,9 @@ "list": "Seznam", "loading": "Načítání", "loading_search_results_failed": "Načítání výsledků vyhledávání se nezdařilo", + "local": "Místní", "local_asset_cast_failed": "Nelze odeslat položku, která není nahraná na serveru", + "local_assets": "Místní položky", "local_network": "Místní síť", "local_network_sheet_info": "Aplikace se při použití zadané sítě Wi-Fi připojí k serveru prostřednictvím tohoto URL", "location_permission": "Oprávnění polohy", @@ -1322,6 +1330,7 @@ "no_results": "Žádné výsledky", "no_results_description": "Zkuste použít synonymum nebo obecnější klíčové slovo", "no_shared_albums_message": "Vytvořte si album a sdílejte fotografie a videa s lidmi ve své síti", + "no_uploads_in_progress": "Neprobíhá žádné nahrávání", "not_in_any_album": "Bez alba", "not_selected": "Není vybráno", "note_apply_storage_label_to_previously_uploaded assets": "Upozornění: Chcete-li použít štítek úložiště na dříve nahrané položky, spusťte příkaz", @@ -1359,6 +1368,7 @@ "original": "originál", "other": "Ostatní", "other_devices": "Ostatní zařízení", + "other_entities": "Ostatní entity", "other_variables": "Další proměnné", "owned": "Vlastní", "owner": "Vlastník", @@ -1519,6 +1529,8 @@ "refreshing_faces": "Obnovování obličejů", "refreshing_metadata": "Obnovování metadat", "regenerating_thumbnails": "Regenerace miniatur", + "remote": "Vzdálený", + "remote_assets": "Vzdálené položky", "remove": "Odstranit", "remove_assets_album_confirmation": "Opravdu chcete z alba odstranit {count, plural, one {# položku} few {# položky} other {# položek}}?", "remove_assets_shared_link_confirmation": "Opravdu chcete ze sdíleného odkazu odstranit {count, plural, one {# položku} few {# položky} other {# položek}}?", @@ -1556,11 +1568,15 @@ "reset_password": "Obnovit heslo", "reset_people_visibility": "Obnovit viditelnost lidí", "reset_pin_code": "Resetovat PIN kód", + "reset_sqlite": "Obnovit SQLite databázi", + "reset_sqlite_confirmation": "Jste si jisti, že chcete obnovit SQLite databázi? Pro opětovnou synchronizaci dat se budete muset odhlásit a znovu přihlásit", + "reset_sqlite_success": "Obnovení SQLite databáze proběhlo úspěšně", "reset_to_default": "Obnovit výchozí nastavení", "resolve_duplicates": "Vyřešit duplicity", "resolved_all_duplicates": "Vyřešeny všechny duplicity", "restore": "Obnovit", "restore_all": "Obnovit vše", + "restore_trash_action_prompt": "{count} obnoveno z koše", "restore_user": "Obnovit uživatele", "restored_asset": "Položka obnovena", "resume": "Pokračovat", @@ -1569,6 +1585,7 @@ "role": "Role", "role_editor": "Editor", "role_viewer": "Divák", + "running": "Probíhá", "save": "Uložit", "save_to_gallery": "Uložit do galerie", "saved_api_key": "API klíč uložen", @@ -1822,6 +1839,7 @@ "storage_quota": "Kvóta úložiště", "storage_usage": "Využito {used} z {available}", "submit": "Odeslat", + "success": "Úspěch", "suggestions": "Návrhy", "sunrise_on_the_beach": "Východ slunce na pláži", "support": "Podpora", @@ -1831,6 +1849,8 @@ "sync": "Synchronizovat", "sync_albums": "Synchronizovat alba", "sync_albums_manual_subtitle": "Synchronizovat všechna nahraná videa a fotografie do vybraných záložních alb", + "sync_local": "Synchronizovat místní", + "sync_remote": "Synchronizovat vzdálené", "sync_upload_album_setting_subtitle": "Vytvořit a nahrát fotografie a videa do vybraných alb na Immich", "tag": "Značka", "tag_assets": "Přiřadit značku", @@ -1841,6 +1861,7 @@ "tag_updated": "Aktualizována značka: {tag}", "tagged_assets": "Přiřazena značka {count, plural, one {# položce} other {# položkám}}", "tags": "Značky", + "tap_to_run_job": "Klepnutím na spustíte úlohu", "template": "Šablona", "theme": "Motiv", "theme_selection": "Výběr motivu", diff --git a/i18n/de.json b/i18n/de.json index 79ba1251d5..45245b182b 100644 --- a/i18n/de.json +++ b/i18n/de.json @@ -573,6 +573,8 @@ "backup_options_page_title": "Sicherungsoptionen", "backup_setting_subtitle": "Verwaltung der Upload-Einstellungen im Hintergrund und im Vordergrund", "backward": "Rückwärts", + "beta_sync": "Status des Beta Sync", + "beta_sync_subtitle": "Verwalte das neue Synchronisierungssystem", "biometric_auth_enabled": "Biometrische Authentifizierung aktiviert", "biometric_locked_out": "Du bist von der biometrischen Authentifizierung ausgeschlossen", "biometric_no_options": "Keine biometrischen Optionen verfügbar", @@ -1051,6 +1053,9 @@ "haptic_feedback_switch": "Haptisches Feedback aktivieren", "haptic_feedback_title": "Haptisches Feedback", "has_quota": "Kontingent", + "hash_asset": "Dateihash", + "hashed_assets": "Gehashte Dateien", + "hashing": "Hashen", "header_settings_add_header_tip": "Header hinzufügen", "header_settings_field_validator_msg": "Der Wert darf nicht leer sein", "header_settings_header_name_input": "Header-Name", @@ -1083,6 +1088,7 @@ "host": "Host", "hour": "Stunde", "id": "ID", + "idle": "Untätig", "ignore_icloud_photos": "iCloud Fotos ignorieren", "ignore_icloud_photos_description": "Fotos, die in der iCloud gespeichert sind, werden nicht auf den immich Server hochgeladen", "image": "Bild", @@ -1165,7 +1171,9 @@ "list": "Liste", "loading": "Laden", "loading_search_results_failed": "Laden von Suchergebnissen fehlgeschlagen", + "local": "Lokal", "local_asset_cast_failed": "Eine Datei, die nicht auf den Server hochgeladen wurde, kann nicht gecastet werden", + "local_assets": "Lokale Dateien", "local_network": "Lokales Netzwerk", "local_network_sheet_info": "Die App stellt über diese URL eine Verbindung zum Server her, wenn sie das angegebene WLAN-Netzwerk verwendet", "location_permission": "Standort Genehmigung", @@ -1322,6 +1330,7 @@ "no_results": "Keine Ergebnisse", "no_results_description": "Versuche es mit einem Synonym oder einem allgemeineren Stichwort", "no_shared_albums_message": "Erstelle ein Album, um Fotos und Videos mit Personen in deinem Netzwerk zu teilen", + "no_uploads_in_progress": "Kein Upload in Bearbeitung", "not_in_any_album": "In keinem Album", "not_selected": "Nicht ausgewählt", "note_apply_storage_label_to_previously_uploaded assets": "Hinweis: Um eine Speicherpfadbezeichnung anzuwenden, starte den", @@ -1359,6 +1368,7 @@ "original": "Original", "other": "Sonstiges", "other_devices": "Andere Geräte", + "other_entities": "Andere Entitäten", "other_variables": "Sonstige Variablen", "owned": "Eigenes", "owner": "Besitzer", @@ -1519,6 +1529,8 @@ "refreshing_faces": "Gesichter werden aktualisiert", "refreshing_metadata": "Metadaten werden aktualisiert", "regenerating_thumbnails": "Miniaturansichten werden neu erstellt", + "remote": "Entfernt", + "remote_assets": "Entfernte Dateien", "remove": "Entfernen", "remove_assets_album_confirmation": "Bist du sicher, dass du {count, plural, one {# Datei} other {# Dateien}} aus dem Album entfernen willst?", "remove_assets_shared_link_confirmation": "Bist du sicher, dass du {count, plural, one {# Datei} other {# Dateien}} von diesem geteilten Link entfernen willst?", @@ -1556,11 +1568,15 @@ "reset_password": "Passwort zurücksetzen", "reset_people_visibility": "Sichtbarkeit von Personen zurücksetzen", "reset_pin_code": "PIN Code zurücksetzen", + "reset_sqlite": "SQLite Datenbank zurücksetzen", + "reset_sqlite_confirmation": "Bist du sicher, dass du die SQLite-Datenbank zurücksetzen willst? Du musst dich ab- und wieder anmelden, um die Daten neu zu synchronisieren", + "reset_sqlite_success": "SQLite Datenbank erfolgreich zurückgesetzt", "reset_to_default": "Auf Standard zurücksetzen", "resolve_duplicates": "Duplikate entfernen", "resolved_all_duplicates": "Alle Duplikate aufgelöst", "restore": "Wiederherstellen", "restore_all": "Alle wiederherstellen", + "restore_trash_action_prompt": "{count} aus dem Papierkorb wiederhergestellt", "restore_user": "Nutzer wiederherstellen", "restored_asset": "Datei wiederhergestellt", "resume": "Fortsetzen", @@ -1569,6 +1585,7 @@ "role": "Rolle", "role_editor": "Bearbeiter", "role_viewer": "Betrachter", + "running": "Läuft", "save": "Speichern", "save_to_gallery": "In Galerie speichern", "saved_api_key": "API-Schlüssel wurde gespeichert", @@ -1822,6 +1839,7 @@ "storage_quota": "Speicherplatz-Kontingent", "storage_usage": "{used} von {available} verwendet", "submit": "Bestätigen", + "success": "Erfolgreich", "suggestions": "Vorschläge", "sunrise_on_the_beach": "Sonnenaufgang am Strand", "support": "Unterstützung", @@ -1831,6 +1849,8 @@ "sync": "Synchronisieren", "sync_albums": "Alben synchronisieren", "sync_albums_manual_subtitle": "Synchronisiere alle hochgeladenen Videos und Fotos in die ausgewählten Backup-Alben", + "sync_local": "Lokal synchronisieren", + "sync_remote": "Entfernt synchronisieren", "sync_upload_album_setting_subtitle": "Erstelle deine ausgewählten Alben in Immich und lade die Fotos und Videos dort hoch", "tag": "Tag", "tag_assets": "Dateien taggen", @@ -1841,6 +1861,7 @@ "tag_updated": "Tag aktualisiert: {tag}", "tagged_assets": "{count, plural, one {# Datei} other {# Dateien}} getagged", "tags": "Tags", + "tap_to_run_job": "Tippen um den Job zu starten", "template": "Vorlage", "theme": "Theme", "theme_selection": "Themenauswahl", diff --git a/i18n/et.json b/i18n/et.json index 804d0c6c46..1d293a3bdf 100644 --- a/i18n/et.json +++ b/i18n/et.json @@ -573,6 +573,8 @@ "backup_options_page_title": "Varundamise valikud", "backup_setting_subtitle": "Halda taustal ja esiplaanil üleslaadimise seadeid", "backward": "Tagasi", + "beta_sync": "Beeta sünkroonimise staatus", + "beta_sync_subtitle": "Halda uut sünkroonimissüsteemi", "biometric_auth_enabled": "Biomeetriline autentimine lubatud", "biometric_locked_out": "Biomeetriline autentimine on blokeeritud", "biometric_no_options": "Biomeetrilisi valikuid ei ole", @@ -590,7 +592,7 @@ "cache_settings_clear_cache_button": "Tühjenda puhver", "cache_settings_clear_cache_button_title": "Tühjendab rakenduse puhvri. See mõjutab oluliselt rakenduse jõudlust, kuni puhver uuesti täidetakse.", "cache_settings_duplicated_assets_clear_button": "TÜHJENDA", - "cache_settings_duplicated_assets_subtitle": "Fotod ja videod, mis on rakenduse poolt mustfiltreeritud", + "cache_settings_duplicated_assets_subtitle": "Fotod ja videod, mis on rakenduse poolt ignoreeritud", "cache_settings_duplicated_assets_title": "Dubleeritud üksused ({count})", "cache_settings_statistics_album": "Kogu pisipildid", "cache_settings_statistics_full": "Täismõõdus pildid", @@ -1051,6 +1053,9 @@ "haptic_feedback_switch": "Luba haptiline tagasiside", "haptic_feedback_title": "Haptiline tagasiside", "has_quota": "On kvoot", + "hash_asset": "Arvuta üksuse räsi", + "hashed_assets": "Räsiga üksused", + "hashing": "Räsi arvutamine", "header_settings_add_header_tip": "Lisa päis", "header_settings_field_validator_msg": "Väärtus ei saa olla tühi", "header_settings_header_name_input": "Päise nimi", @@ -1083,6 +1088,7 @@ "host": "Host", "hour": "Tund", "id": "ID", + "idle": "Jõude", "ignore_icloud_photos": "Ignoreeri iCloud fotosid", "ignore_icloud_photos_description": "Fotosid, mis on iCloud'is, ei laadita üles Immich'i serverisse", "image": "Pilt", @@ -1165,7 +1171,9 @@ "list": "Loend", "loading": "Laadimine", "loading_search_results_failed": "Otsitulemuste laadimine ebaõnnestus", + "local": "Lokaalne üksus", "local_asset_cast_failed": "Ei saa edastada üksust, mis pole serverisse üles laaditud", + "local_assets": "Lokaalsed üksused", "local_network": "Kohalik võrk", "local_network_sheet_info": "Rakendus ühendub valitud Wi-Fi võrgus olles serveriga selle URL-i kaudu", "location_permission": "Asukoha luba", @@ -1322,6 +1330,7 @@ "no_results": "Vasteid pole", "no_results_description": "Proovi sünonüümi või üldisemat märksõna", "no_shared_albums_message": "Lisa album, et fotosid ja videosid teistega jagada", + "no_uploads_in_progress": "Üleslaadimisi käimas ei ole", "not_in_any_album": "Pole üheski albumis", "not_selected": "Ei ole valitud", "note_apply_storage_label_to_previously_uploaded assets": "Märkus: Et rakendada talletussilt varem üleslaaditud üksustele, käivita", @@ -1359,6 +1368,7 @@ "original": "originaal", "other": "Muud", "other_devices": "Muud seadmed", + "other_entities": "Muud objektid", "other_variables": "Muud muutujad", "owned": "Minu omad", "owner": "Omanik", @@ -1519,6 +1529,8 @@ "refreshing_faces": "Nägude värskendamine", "refreshing_metadata": "Metaandmete värskendamine", "regenerating_thumbnails": "Pisipiltide uuesti genereerimine", + "remote": "Kaugüksus", + "remote_assets": "Kaugüksused", "remove": "Eemalda", "remove_assets_album_confirmation": "Kas oled kindel, et soovid {count, plural, one {# üksuse} other {# üksust}} albumist eemaldada?", "remove_assets_shared_link_confirmation": "Kas oled kindel, et soovid eemaldada {count, plural, one {# üksuse} other {# üksust}} sellelt jagatud lingilt?", @@ -1556,11 +1568,15 @@ "reset_password": "Lähtesta parool", "reset_people_visibility": "Lähtesta isikute nähtavus", "reset_pin_code": "Lähtesta PIN-kood", + "reset_sqlite": "Lähtesta SQLite andmebaas", + "reset_sqlite_confirmation": "Kas oled kindel, et soovid SQLite andmebaasi lähtestada? Andmete uuesti sünkroonimiseks pead välja ja jälle sisse logima", + "reset_sqlite_success": "SQLite andmebaas edukalt lähtestatud", "reset_to_default": "Lähtesta", "resolve_duplicates": "Lahenda duplikaadid", "resolved_all_duplicates": "Kõik duplikaadid lahendatud", "restore": "Taasta", "restore_all": "Taasta kõik", + "restore_trash_action_prompt": "{count} prügikastust taastatud", "restore_user": "Taasta kasutaja", "restored_asset": "Üksus taastatud", "resume": "Jätka", @@ -1569,6 +1585,7 @@ "role": "Roll", "role_editor": "Muutja", "role_viewer": "Vaataja", + "running": "Käimas", "save": "Salvesta", "save_to_gallery": "Salvesta galeriisse", "saved_api_key": "API võti salvestatud", @@ -1822,6 +1839,7 @@ "storage_quota": "Talletuskvoot", "storage_usage": "{used}/{available} kasutatud", "submit": "Saada", + "success": "Õnnestus", "suggestions": "Soovitused", "sunrise_on_the_beach": "Päikesetõus rannal", "support": "Tugi", @@ -1831,6 +1849,8 @@ "sync": "Sünkrooni", "sync_albums": "Sünkrooni albumid", "sync_albums_manual_subtitle": "Sünkrooni kõik üleslaaditud videod ja fotod valitud varundusalbumitesse", + "sync_local": "Sünkrooni lokaalsed üksused", + "sync_remote": "Sünkrooni kaugüksused", "sync_upload_album_setting_subtitle": "Loo ja laadi oma pildid ja videod üles Immich'isse valitud albumitesse", "tag": "Silt", "tag_assets": "Sildista üksuseid", @@ -1841,6 +1861,7 @@ "tag_updated": "Muudetud silt: {tag}", "tagged_assets": "{count, plural, one {# üksus} other {# üksust}} sildistatud", "tags": "Sildid", + "tap_to_run_job": "Puuduta tööte käivitamiseks", "template": "Mall", "theme": "Teema", "theme_selection": "Teema valik", diff --git a/i18n/fr.json b/i18n/fr.json index cebd4b694f..ff9f783771 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -406,6 +406,7 @@ "album_options": "Options de l'album", "album_remove_user": "Supprimer l'utilisateur ?", "album_remove_user_confirmation": "Êtes-vous sûr de vouloir supprimer {user} ?", + "album_search_not_found": "Aucun album trouvé ne correspond à votre recherche", "album_share_no_users": "Il semble que vous ayez partagé cet album avec tous les utilisateurs ou que vous n'ayez aucun utilisateur avec lequel le partager.", "album_updated": "Album mis à jour", "album_updated_setting_description": "Recevoir une notification par courriel lorsqu'un album partagé a de nouveaux médias", @@ -425,6 +426,7 @@ "albums_default_sort_order": "Ordre de tri par défaut des albums", "albums_default_sort_order_description": "Ordre de tri des médias pour les nouveaux albums créés.", "albums_feature_description": "Bibliothèques de médias pouvant être partagés avec d'autres utilisateurs.", + "albums_on_device_count": "Album sur l'appareil ({count})", "all": "Tout", "all_albums": "Tous les albums", "all_people": "Toutes les personnes", @@ -571,6 +573,8 @@ "backup_options_page_title": "Options de sauvegarde", "backup_setting_subtitle": "Ajuster les paramètres d'envoi au premier et en arrière-plan", "backward": "Arrière", + "beta_sync": "Statut de la synchronisation béta", + "beta_sync_subtitle": "Gérer le nouveau système de synchronisation", "biometric_auth_enabled": "Authentification biométrique activée", "biometric_locked_out": "L'authentification biométrique est verrouillé", "biometric_no_options": "Aucune option biométrique disponible", @@ -605,6 +609,7 @@ "cancel": "Annuler", "cancel_search": "Annuler la recherche", "canceled": "Annulé", + "canceling": "Annulation", "cannot_merge_people": "Impossible de fusionner les personnes", "cannot_undo_this_action": "Vous ne pouvez pas annuler cette action !", "cannot_update_the_description": "Impossible de mettre à jour la description", @@ -765,6 +770,7 @@ "description": "Description", "description_input_hint_text": "Ajouter une description...", "description_input_submit_error": "Erreur de mise à jour de la description, vérifier le journal pour plus de détails", + "deselect_all": "Tout désélectionner", "details": "Détails", "direction": "Ordre", "disabled": "Désactivé", @@ -839,6 +845,7 @@ "empty_trash": "Vider la corbeille", "empty_trash_confirmation": "Êtes-vous sûr de vouloir vider la corbeille ? Cela supprimera définitivement de Immich tous les médias qu'elle contient.\nVous ne pouvez pas annuler cette action !", "enable": "Active", + "enable_backup": "Activer Backup", "enable_biometric_auth_description": "Entrez votre code PIN pour activer l'authentification biométrique", "enabled": "Activé", "end_date": "Date de fin", @@ -1046,6 +1053,9 @@ "haptic_feedback_switch": "Activer le retour haptique", "haptic_feedback_title": "Retour haptique", "has_quota": "Quota", + "hash_asset": "Hasher le média", + "hashed_assets": "Média hashés", + "hashing": "Hash", "header_settings_add_header_tip": "Ajouter un en-tête", "header_settings_field_validator_msg": "Cette valeur ne peut pas être vide", "header_settings_header_name_input": "Nom de l'en-tête", @@ -1160,7 +1170,9 @@ "list": "Liste", "loading": "Chargement", "loading_search_results_failed": "Chargement des résultats échoué", + "local": "Local", "local_asset_cast_failed": "Impossible de caster un média qui n'a pas envoyé vers le serveur", + "local_assets": "Média locaux", "local_network": "Réseau local", "local_network_sheet_info": "L'application va se connecter au serveur via cette URL quand l'appareil est connecté à ce réseau Wi-Fi", "location_permission": "Autorisation de localisation", @@ -1317,6 +1329,7 @@ "no_results": "Aucun résultat", "no_results_description": "Essayez un synonyme ou un mot-clé plus général", "no_shared_albums_message": "Créer un album pour partager vos photos et vidéos avec les personnes de votre réseau", + "no_uploads_in_progress": "Pas d'envoi en cours", "not_in_any_album": "Dans aucun album", "not_selected": "Non sélectionné", "note_apply_storage_label_to_previously_uploaded assets": "Note : Pour appliquer l'étiquette de stockage aux médias précédemment envoyés, exécutez", @@ -1354,6 +1367,7 @@ "original": "original", "other": "Autre", "other_devices": "Autres appareils", + "other_entities": "Autres entités", "other_variables": "Autres variables", "owned": "Possédé", "owner": "Propriétaire", @@ -1485,6 +1499,7 @@ "purchase_server_description_2": "Statut de contributeur", "purchase_server_title": "Serveur", "purchase_settings_server_activated": "La clé du produit pour le Serveur est gérée par l'administrateur", + "queue_status": "File d'attente {count}/{total}", "rating": "Étoile d'évaluation", "rating_clear": "Effacer l'évaluation", "rating_count": "{count, plural, one {# étoile} other {# étoiles}}", @@ -1513,6 +1528,8 @@ "refreshing_faces": "Actualisation des visages", "refreshing_metadata": "Actualisation des métadonnées", "regenerating_thumbnails": "Regénération des miniatures", + "remote": "A distance", + "remote_assets": "Média à distance", "remove": "Supprimer", "remove_assets_album_confirmation": "Êtes-vous sûr de vouloir supprimer {count, plural, one {# média} other {# médias}} de l'album ?", "remove_assets_shared_link_confirmation": "Êtes-vous sûr de vouloir supprimer {count, plural, one {# média} other {# médias}} de ce lien partagé ?", @@ -1550,11 +1567,15 @@ "reset_password": "Réinitialiser le mot de passe", "reset_people_visibility": "Réinitialiser la visibilité des personnes", "reset_pin_code": "Réinitialiser le code PIN", + "reset_sqlite": "Réinitialiser la base de données SQLite", + "reset_sqlite_confirmation": "Êtes vous sur que vous voulez réinitialiser la base de données SQLite ? Vous devrez vous déconnecter and vous reconnecter à nouveau pour re-synchroniser les données", + "reset_sqlite_success": "La base de données SQLite à été réinitialisé avec succès", "reset_to_default": "Rétablir les valeurs par défaut", "resolve_duplicates": "Résoudre les doublons", "resolved_all_duplicates": "Résolution de tous les doublons", "restore": "Restaurer", "restore_all": "Tout restaurer", + "restore_trash_action_prompt": "{count} restauré de la corbeille", "restore_user": "Restaurer l'utilisateur", "restored_asset": "Média restauré", "resume": "Reprendre", @@ -1563,6 +1584,7 @@ "role": "Rôle", "role_editor": "Éditeur", "role_viewer": "Visionneuse", + "running": "En marche", "save": "Sauvegarder", "save_to_gallery": "Enregistrer", "saved_api_key": "Clé API sauvegardée", @@ -1816,6 +1838,7 @@ "storage_quota": "Quota de stockage", "storage_usage": "{used} sur {available} utilisé", "submit": "Soumettre", + "success": "Réussi", "suggestions": "Suggestions", "sunrise_on_the_beach": "Lever de soleil sur la plage", "support": "Soutenir", @@ -1825,6 +1848,8 @@ "sync": "Synchroniser", "sync_albums": "Synchroniser dans des albums", "sync_albums_manual_subtitle": "Synchroniser toutes les vidéos et photos envoyées dans les albums sélectionnés", + "sync_local": "Synchronisation locale", + "sync_remote": "Synchronisation à distance", "sync_upload_album_setting_subtitle": "Créez et envoyez vos photos et vidéos dans les albums sélectionnés sur Immich", "tag": "Étiquette", "tag_assets": "Étiqueter les médias", @@ -1835,6 +1860,7 @@ "tag_updated": "Étiquette mise à jour : {tag}", "tagged_assets": "Étiquette ajoutée à {count, plural, one {# média} other {# médias}}", "tags": "Étiquettes", + "tap_to_run_job": "Appuyez pour démarrer la tâche", "template": "Modèle", "theme": "Thème", "theme_selection": "Sélection du thème", @@ -1915,6 +1941,7 @@ "updated_password": "Mot de passe mis à jour", "upload": "Envoyer", "upload_concurrency": "Envois simultanés", + "upload_details": "Uploader les details", "upload_dialog_info": "Voulez-vous sauvegarder la sélection vers le serveur ?", "upload_dialog_title": "Envoyer le média", "upload_errors": "L'envoi s'est complété avec {count, plural, one {# erreur} other {# erreurs}}. Rafraîchissez la page pour voir les nouveaux médias envoyés.", @@ -1965,6 +1992,7 @@ "view_album": "Afficher l'album", "view_all": "Voir tout", "view_all_users": "Voir tous les utilisateurs", + "view_details": "Voir les détails", "view_in_timeline": "Voir dans la vue chronologique", "view_link": "Voir le lien", "view_links": "Voir les liens", diff --git a/i18n/hu.json b/i18n/hu.json index c00fb37224..df995c0d6c 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -140,7 +140,7 @@ "machine_learning_smart_search_description": "Képek szemantikai keresése CLIP beágyazások segítségével", "machine_learning_smart_search_enabled": "Okos keresés engedélyezése", "machine_learning_smart_search_enabled_description": "Ha ki van kapcsolva, a képek nem lesznek átalakítva okos kereséshez.", - "machine_learning_url_description": "Gépi tanulás szerver URL címe. Ha többi, mint egy URL van megadva, mindegyik szervert egyenként próbálja meg, amíg az egyik sikeresen nem válaszol, sorrendben az elsőtől az utólsóig. A nem válaszoló szervereket átmenetileg figyelmen kívül hagyja, amíg újra online nem lesznek.", + "machine_learning_url_description": "Gépi tanulás szerver URL címe. Ha többi, mint egy URL van megadva, mindegyik szervert egyenként próbálja meg, amíg az egyik sikeresen nem válaszol, sorrendben az elsőtől az utólsóig. A nem elérhető szervereket átmenetileg figyelmen kívül lesznek hagyva, amíg újra online nem lesznek.", "manage_concurrency": "Párhuzamos Feladatok Kezelése", "manage_log_settings": "Naplózási beállítások kezelése", "map_dark_style": "Sötét stílus", @@ -166,6 +166,12 @@ "metadata_settings_description": "Metaadat beállítások kezelése", "migration_job": "Migrálás", "migration_job_description": "Az elemek és arcok bélyegképeinek migrálása a legújabb mappastruktúrába", + "nightly_tasks_cluster_faces_setting_description": "Arcfelismerés futtatása az újonnan érzékelt arcokon", + "nightly_tasks_database_cleanup_setting": "Adatbázis-tisztítási feladatok", + "nightly_tasks_database_cleanup_setting_description": "A régi, lejárt adatok törlése az adatbázisból", + "nightly_tasks_generate_memories_setting": "Emlékek generálása", + "nightly_tasks_generate_memories_setting_description": "Új emlékek létrehozása elemekből", + "nightly_tasks_missing_thumbnails_setting": "Hiányzó indexképek generálása", "no_paths_added": "Nincs megadva elérési útvonal", "no_pattern_added": "Nincs megadva minta (pattern)", "note_apply_storage_label_previous_assets": "Megjegyzés: Ha a korábban feltöltött elemekhez is szeretne Tárhely Címkéket társítani, akkor futtassa ezt", @@ -360,7 +366,7 @@ "advanced_settings_enable_alternate_media_filter_subtitle": "Ezzel a beállítással a szinkronizálás során alternatív kritériumok alapján szűrheted a fájlokat. Csak akkor próbáld ki, ha problémáid vannak azzal, hogy az alkalmazás nem ismeri fel az összes albumot.", "advanced_settings_enable_alternate_media_filter_title": "[KÍSÉRLETI] Alternatív eszköz album szinkronizálási szűrő használata", "advanced_settings_log_level_title": "Naplózás szintje: {level}", - "advanced_settings_prefer_remote_subtitle": "Néhány eszköz fájdalmasan lassan tölti be az eszközön lévő bélyegképeket. Ez a beállítás inkább a távoli képeket tölti be helyettük.", + "advanced_settings_prefer_remote_subtitle": "Néhány eszköz fájdalmasan lassan tölti be az eszközön lévő indexképeket. Ez a beállítás inkább a távoli képeket (a szerverről) tölti be helyettük.", "advanced_settings_prefer_remote_title": "Távoli képek előnyben részesítése", "advanced_settings_proxy_headers_subtitle": "Add meg azokat a proxy fejléceket, amiket az app elküldjön minden hálózati kérésnél", "advanced_settings_proxy_headers_title": "Proxy Fejlécek", @@ -517,7 +523,7 @@ "backup_controller_page_background_is_on": "Automatikus mentés a háttérben be van kapcsolva", "backup_controller_page_background_turn_off": "Háttérszolgáltatás kikapcsolása", "backup_controller_page_background_turn_on": "Háttérszolgáltatás bekapcsolása", - "backup_controller_page_background_wifi": "Csak WiFi-n", + "backup_controller_page_background_wifi": "Csak Wi-Fi-n", "backup_controller_page_backup": "Mentés", "backup_controller_page_backup_selected": "Kiválasztva: ", "backup_controller_page_backup_sub": "Mentett fotók és videók", @@ -551,8 +557,8 @@ "backup_setting_subtitle": "A háttérben és előtérben mentés beállításainak kezelése", "backward": "Visszafele", "biometric_auth_enabled": "Biometrikus azonosítás engedélyezve", - "biometric_locked_out": "A biometrikus azonosításból kizárva", - "biometric_no_options": "A biometrikus azonosítás nem elérhető", + "biometric_locked_out": "Ki vagy zárva a biometrikus azonosításból", + "biometric_no_options": "Nincsen elérhető biometrikus azonosítás", "biometric_not_available": "Biometrikus azonosítás ezen az eszközön nem elérhető", "birthdate_saved": "Születésnap elmentve", "birthdate_set_description": "A születés napját a rendszer arra használja, hogy kiírja, hogy a fénykép készítésekor a személy hány éves volt.", @@ -642,7 +648,7 @@ "confirm_keep_this_delete_others": "Minden más elem a készletben törlésre kerül, kivéve ezt az elemet. Biztosan folytatni szeretnéd?", "confirm_new_pin_code": "Új PIN kód megerősítése", "confirm_password": "Jelszó megerősítése", - "confirm_tag_face": "Szeretnéd ezt az arcot {name}-ként megjelölni?", + "confirm_tag_face": "Szeretnéd ezt az arcot {name}-nak/nek megjelölni?", "confirm_tag_face_unnamed": "Szeretnéd ezt az arcot megjelölni?", "connected_device": "Kapcsolt eszköz", "connected_to": "Kapcsolódva", @@ -770,7 +776,7 @@ "download_started": "Letöltés megkezdve", "download_sucess": "Sikeres letöltés", "download_sucess_android": "Média letöltve a DCIM/Immich mappába", - "download_waiting_to_retry": "Várakozás újrapróbálkozáshoz", + "download_waiting_to_retry": "Várás az újrapróbálkozásra", "downloading": "Letöltés", "downloading_asset_filename": "{filename} elem letöltése", "downloading_media": "Média letöltése", @@ -816,7 +822,7 @@ "enqueued": "Sorba állítva", "enter_wifi_name": "Add meg a Wi-Fi hálózat nevét", "enter_your_pin_code": "Add meg a jelszavad", - "enter_your_pin_code_subtitle": "Add meg a jelszavad a zárolt mappa megnyitásához", + "enter_your_pin_code_subtitle": "Add meg a PIN kódodat a zárolt mappa megnyitásához", "error": "Hiba", "error_change_sort_album": "Album sorbarendezésének megváltoztatása sikertelen", "error_delete_face": "Hiba az arc törlése során", @@ -916,7 +922,7 @@ "unable_to_remove_partner": "Partner eltávolítása sikertelen", "unable_to_remove_reaction": "Reakció eltávolítása sikertelen", "unable_to_reset_password": "Jelszó visszaállítása sikertelen", - "unable_to_reset_pin_code": "Jelszó visszaállítása sikertelen", + "unable_to_reset_pin_code": "PIN kód visszaállítása sikertelen", "unable_to_resolve_duplicate": "Duplikátum feloldása sikertelen", "unable_to_restore_assets": "Elemek visszaállítása sikertelen", "unable_to_restore_trash": "Az összes elem visszaállítása sikertelen", @@ -988,7 +994,7 @@ "filetype": "Fájltípus", "filter": "Szűrő", "filter_people": "Személyek szűrése", - "filter_places": "Helyek szűrése", + "filter_places": "Helyszínek szűrése", "find_them_fast": "Név alapján kereséssel gyorsan megtalálhatóak", "fix_incorrect_match": "Hibás találat javítása", "folder": "Mappa", @@ -1029,9 +1035,9 @@ "hide_person": "Személy elrejtése", "hide_unnamed_people": "Név nélküli személyek elrejtése", "home_page_add_to_album_conflicts": "{added} elem hozzáadva a(z) \"{album}\" albumhoz. {failed} elem már eleve az albumban volt.", - "home_page_add_to_album_err_local": "Helyi elemeket még nem lehet albumba tenni, kihagyás", + "home_page_add_to_album_err_local": "Helyi elemeket még nem lehet albumba tenni, ki lesznek hagyva", "home_page_add_to_album_success": "{added} elem hozzáadva a(z) \"{album}\" albumhoz.", - "home_page_album_err_partner": "Még nem lehet a partner elemeit albumokhoz adni, kihagyás", + "home_page_album_err_partner": "Még nem lehet a partner elemeit albumokhoz adni, ki lesznek hagyva", "home_page_archive_err_local": "Helyi elemek archiválása még nem támogatott, úgyhogy kihagyjuk", "home_page_archive_err_partner": "Partner elemeit nem lehet archiválni, úgyhogy kihagyjuk", "home_page_building_timeline": "Idővonal összeállítása", @@ -1040,7 +1046,7 @@ "home_page_favorite_err_local": "Helyi elemeket még nem lehet a kedvencek közé tenni, úgyhogy ezeket kihagyjuk", "home_page_favorite_err_partner": "Partner elemeit még nem lehet a kedvencek közé tenni, úgyhogy ezeket kihagyjuk", "home_page_first_time_notice": "Ha most használod először az alkalmazást, a fotók és videók megjelenítéséhez az idővonaladon, állítsd be, hogy melyik albumaidról készüljön biztonsági mentés", - "home_page_locked_error_local": "Helyi elemek nem mozgathatóak a zárolt mappába, átugorva", + "home_page_locked_error_local": "A Helyi elemek nem mozgathatóak a zárolt mappába, ki lesznek hagyva", "home_page_locked_error_partner": "Partner elemek nem mozgathatóak a zárolt mappába, átugorva", "home_page_share_err_local": "Helyi elemekről nem lehet megosztott linket készíteni, úgyhogy kihagyjuk", "home_page_upload_err_limit": "Csak 30 elemet tudsz egyszerre feltölteni, úgyhogy kihagyjuk", @@ -1087,10 +1093,10 @@ "invite_people": "Személyek Meghívása", "invite_to_album": "Meghívás az albumba", "ios_debug_info_last_sync_at": "Utoljára szinkronizálva {dateTime}", - "ios_debug_info_no_processes_queued": "Nincs sorba állított hátterfolyamat", + "ios_debug_info_no_processes_queued": "Nincs a sorban háttérfolyamat jelenleg", "ios_debug_info_no_sync_yet": "Még nem futott szinkronizáló háttérfolyamat", "ios_debug_info_processes_queued": "{count, plural, one {{count} háttérfolyamat előkészítve} other {{count} háttérfolyamat előkészítve}}", - "ios_debug_info_processing_ran_at": "Feldolgozás futott {dateTime}", + "ios_debug_info_processing_ran_at": "A feldolgozás ekkor futott: {dateTime}", "items_count": "{count, plural, other {# elem}}", "jobs": "Feladatok", "keep": "Megtart", @@ -1099,7 +1105,7 @@ "kept_this_deleted_others": "Ez az elem és a töröltek meg lettek hagyva {count, plural, one {# asset} other {# assets}}", "keyboard_shortcuts": "Billentyűparancsok", "language": "Nyelv", - "language_no_results_subtitle": "Próbáld a keresésed módosítását", + "language_no_results_subtitle": "Próbáld módosítani a szavaidat a keresésnél", "language_search_hint": "Nyelvek keresése...", "language_setting_description": "Válaszd ki preferált nyelvet", "last_seen": "Utoljára láttuk", @@ -1129,7 +1135,7 @@ "local_network": "Helyi hálózat", "local_network_sheet_info": "Az alkalmazés ezen az URL címen fogja elérni a szervert, ha a megadott WiFi hálózathoz van csatlankozva", "location_permission": "Helymeghatározási engedély", - "location_permission_content": "Hálózatok automatikus váltásához az Immich-nek szüksége van a pontos helymeghatározásra, hogy az alkalmazás le tudja kérni a Wi-Fi hálózat nevét", + "location_permission_content": "A Hálózatok automatikus váltásához az Immich-nek szüksége van a pontos helymeghatározásra, hogy az alkalmazás le tudja kérni a Wi-Fi hálózat nevét", "location_picker_choose_on_map": "Válassz a térképen", "location_picker_latitude_error": "Érvényes szélességi kört írj be", "location_picker_latitude_hint": "Ide írd a szélességi kört", @@ -1139,7 +1145,7 @@ "locked_folder": "Zárolt mappa", "log_out": "Kijelentkezés", "log_out_all_devices": "Kijelentkezés Minden Eszközön", - "logged_in_as": "{user}-ként belépve", + "logged_in_as": "Belépve: {user} néven", "logged_out_all_devices": "Minden eszköz kijelentkeztetve", "logged_out_device": "Eszköz kijelentkeztetve", "login": "Bejelentkezés", @@ -1234,9 +1240,9 @@ "monthly_title_text_date_format": "y MMMM", "more": "Továbbiak", "move": "Áthelyezés", - "move_off_locked_folder": "Zárolt mappából kivonás", + "move_off_locked_folder": "Átmozgatás a zárolt mappából", "move_to_locked_folder": "Áthelyezés a zárolt mappába", - "move_to_locked_folder_confirmation": "Ezek a képek és videók az összes albumból kikerülnek, és csak a zárolt mappából lesznek elérhetőek", + "move_to_locked_folder_confirmation": "Ezek a képek és videók az összes albumból kikerülnek, és csak a zárolt mappában lesznek elérhetőek", "moved_to_archive": "{count, plural, one {# Elem} other {# Elemek}} archiválva", "moved_to_library": "{count, plural, one {# Elem} other {# Elemek}} másik könyvtárba költöztetve", "moved_to_trash": "Áthelyezve a lomtárba", @@ -1254,7 +1260,7 @@ "new_password": "Új jelszó", "new_person": "Új személy", "new_pin_code": "Új PIN kód", - "new_pin_code_subtitle": "Ez az első alkalom hogy megnyitod a zárolt mappát. Hozz létre egy jelszót az oldal biztosítására", + "new_pin_code_subtitle": "Ez az első alkalom hogy megnyitod a zárolt mappát. Hozz létre egy jelszót a mappa biztonságos eléréséhez", "new_user_created": "Új felhasználó létrehozva", "new_version_available": "ÚJ VERZIÓ ÉRHETŐ EL", "newest_first": "Legújabb először", @@ -1283,7 +1289,7 @@ "not_selected": "Nincs kiválasztva", "note_apply_storage_label_to_previously_uploaded assets": "Megjegyzés: a korábban feltöltött elemek Tárhely Címkézéséhez futtasd a(z)", "notes": "Megjegyzések", - "nothing_here_yet": "Itt még nincs semmi", + "nothing_here_yet": "Még semmi sincs itt", "notification_permission_dialog_content": "Az értesítések bekapcsolásához a Beállítások menüben válaszd ki az Engedélyezés-t.", "notification_permission_list_tile_content": "Értesítések engedélyezése.", "notification_permission_list_tile_enable_button": "Értesítések Bekapcsolása", @@ -1293,7 +1299,7 @@ "notifications_setting_description": "Értesítések kezelése", "oauth": "OAuth", "official_immich_resources": "Hivatalos Immich Források", - "offline": "Offline", + "offline": "Nem elérhető (offline)", "ok": "Rendben", "oldest_first": "Legrégebbi először", "on_this_device": "Ezen az eszközön", @@ -1303,7 +1309,7 @@ "onboarding_theme_description": "Válassz egy színtémát. Ezt bármikor megváltoztathatod a beállításokban.", "onboarding_user_welcome_description": "Kezdjünk bele!", "onboarding_welcome_user": "Üdvözöllek {user}", - "online": "Online", + "online": "Online (elérhető)", "only_favorites": "Csak kedvencek", "open": "Nyitva", "open_in_map_view": "Megnyitás térkép nézetben", @@ -1754,7 +1760,7 @@ "stack_select_one_photo": "Válassz egy fő képet a csoportból", "stack_selected_photos": "Kiválasztott fényképek csoportosítása", "stacked_assets_count": "{count, plural, other {# elem}} csoportosítva", - "stacktrace": "Hibaleírás", + "stacktrace": "Hiba leírása", "start": "Elindít", "start_date": "Kezdő dátum", "state": "Megye/Állam", @@ -1882,7 +1888,7 @@ "user_liked": "{user} felhasználónak {type, select, photo {ez a fénykép} video {ez a videó} asset {ez az elem} other {ez}} tetszik", "user_pin_code_settings": "PIN kód", "user_pin_code_settings_description": "PIN kód kezelése", - "user_privacy": "Felhasználói biztonság", + "user_privacy": "Felhasználói adatvédelem", "user_purchase_settings": "Megvásárlás", "user_purchase_settings_description": "Vásárlás kezelése", "user_role_set": "{user} felhasználónak {role} jogkör biztosítása", diff --git a/i18n/id.json b/i18n/id.json index ff5e2524e2..92cb07ff30 100644 --- a/i18n/id.json +++ b/i18n/id.json @@ -169,15 +169,23 @@ "nightly_tasks_cluster_faces_setting_description": "Mulai pengenalan wajah pada semua wajah yang baru saja terdeteksi", "nightly_tasks_cluster_new_faces_setting": "Kelompokkan semua wajah baru", "nightly_tasks_database_cleanup_setting": "Tugas pembersihan basis data", + "nightly_tasks_database_cleanup_setting_description": "Membersihkan data lama, kadaluarsa dari database", "nightly_tasks_generate_memories_setting": "Buat kenang-kenangan", "nightly_tasks_generate_memories_setting_description": "Buat kenang-kenangan baru dari berbagai aset", + "nightly_tasks_missing_thumbnails_setting": "Membuat thumbnail yang hilang", + "nightly_tasks_missing_thumbnails_setting_description": "Mengantrikan aset tanpa thumbnail untuk pembuatan thumbnail", + "nightly_tasks_settings": "Pengaturan Tugas Malam", + "nightly_tasks_settings_description": "Atur tugas malam", "nightly_tasks_start_time_setting": "Waktu mulai", + "nightly_tasks_start_time_setting_description": "Waktu saat server mulai menjalankan tugas malam", + "nightly_tasks_sync_quota_usage_setting": "Sinkronisasi penggunaan kuota", + "nightly_tasks_sync_quota_usage_setting_description": "Pembaruan kuota penyimpanan pengguna, berdasarkan penggunaan sekarang", "no_paths_added": "Tidak ada jalur yang ditambahkan", "no_pattern_added": "Tidak ada pola yang ditambahkan", "note_apply_storage_label_previous_assets": "Catatan: Untuk menerapkan Label Penyimpanan untuk aset yang telah diunggah sebelumnya, jalankan", "note_cannot_be_changed_later": "CATATAN: Ini tidak akan dapat diubah lagi!", "notification_email_from_address": "Dari alamat", - "notification_email_from_address_description": "Alamat surel pengirim, misalnya: \"Server Foto Immich \". Pastikan untuk menggunakan alamat yang diizinkan untuk mengirim email", + "notification_email_from_address_description": "Alamat surel pengirim, misalnya: \"Server Foto Immich \". Pastikan untuk menggunakan alamat yang diizinkan untuk mengirim email.", "notification_email_host_description": "Hos server surel (mis. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Abaikan eror sertifikat", "notification_email_ignore_certificate_errors_description": "Abaikan eror validasi sertifikat TLS (tidak disarankan)", diff --git a/i18n/it.json b/i18n/it.json index 5e12bdfc97..0aec538a85 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -406,6 +406,7 @@ "album_options": "Impostazioni Album", "album_remove_user": "Rimuovi l'utente?", "album_remove_user_confirmation": "Sicuro di voler rimuovere l'utente {user}?", + "album_search_not_found": "Nessun album trovato corrispondente alla tua ricerca", "album_share_no_users": "Sembra che tu abbia condiviso questo album con tutti gli utenti oppure non hai nessun utente con cui condividere.", "album_updated": "Album aggiornato", "album_updated_setting_description": "Ricevi una notifica email quando un album condiviso ha nuovi media", @@ -425,6 +426,7 @@ "albums_default_sort_order": "Ordinamento predefinito degli album", "albums_default_sort_order_description": "Ordine iniziale degli elementi alla creazione di nuovi album.", "albums_feature_description": "Raggruppamento di elementi che possono essere condivisi con altri utenti.", + "albums_on_device_count": "Album sul dispositivo ({count})", "all": "Tutti", "all_albums": "Tutti gli album", "all_people": "Tutte le persone", @@ -605,6 +607,7 @@ "cancel": "Annulla", "cancel_search": "Annulla ricerca", "canceled": "Annullato", + "canceling": "Annullamento", "cannot_merge_people": "Impossibile unire le persone", "cannot_undo_this_action": "Non puoi annullare questa azione!", "cannot_update_the_description": "Impossibile aggiornare la descrizione", @@ -751,6 +754,7 @@ "delete_key": "Elimina chiave", "delete_library": "Elimina libreria", "delete_link": "Elimina link", + "delete_local_action_prompt": "{count} elementi rimossi in locale", "delete_local_dialog_ok_backed_up_only": "Elimina solo con backup", "delete_local_dialog_ok_force": "Elimina comunque", "delete_others": "Elimina gli altri", @@ -764,6 +768,7 @@ "description": "Descrizione", "description_input_hint_text": "Aggiungi descrizione...", "description_input_submit_error": "Errore modificare descrizione, controlli I log per maggiori dettagli", + "deselect_all": "Deseleziona Tutto", "details": "Dettagli", "direction": "Direzione", "disabled": "Disabilitato", @@ -781,6 +786,7 @@ "documentation": "Documentazione", "done": "Fatto", "download": "Scarica", + "download_action_prompt": "Scaricando {count} elementi", "download_canceled": "Download annullato", "download_complete": "Download completato", "download_enqueue": "Download in coda", @@ -837,6 +843,7 @@ "empty_trash": "Svuota cestino", "empty_trash_confirmation": "Sei sicuro di volere svuotare il cestino? Questo rimuoverà tutte le risorse nel cestino in modo permanente da Immich.\nNon puoi annullare questa azione!", "enable": "Abilita", + "enable_backup": "Abilita Backup", "enable_biometric_auth_description": "Inserire il codice PIN per abilitare l'autenticazione biometrica", "enabled": "Abilitato", "end_date": "Data Fine", @@ -1483,6 +1490,7 @@ "purchase_server_description_2": "Stato di Contributore", "purchase_server_title": "Server", "purchase_settings_server_activated": "La chiave del prodotto del server è gestita dall'amministratore", + "queue_status": "Messi in coda {count}/{total}", "rating": "Valutazione a stelle", "rating_clear": "Crea valutazione", "rating_count": "{count, plural, one {# stella} other {# stelle}}", @@ -1518,6 +1526,7 @@ "remove_custom_date_range": "Rimuovi intervallo data personalizzato", "remove_deleted_assets": "Rimuovi file offline", "remove_from_album": "Rimuovere dall'album", + "remove_from_album_action_prompt": "{count} elementi rimossi dall'album", "remove_from_favorites": "Rimuovi dai preferiti", "remove_from_lock_folder_action_prompt": "{count} elementi rimossi dalla cartella sicura", "remove_from_locked_folder": "Rimuovi dalla cartella privata", @@ -1691,6 +1700,7 @@ "settings_saved": "Impostazioni salvate", "setup_pin_code": "Configura un codice PIN", "share": "Condivisione", + "share_action_prompt": "Condivisi {count} elementi", "share_add_photos": "Aggiungi foto", "share_assets_selected": "{count} selezionati", "share_dialog_preparing": "Preparo…", @@ -1792,6 +1802,7 @@ "sort_title": "Titolo", "source": "Fonte", "stack": "Raggruppa", + "stack_action_prompt": "{count} elementi raggruppati", "stack_duplicates": "Raggruppa i duplicati", "stack_select_one_photo": "Seleziona una foto principale per il gruppo", "stack_selected_photos": "Impila foto selezionate", @@ -1880,6 +1891,7 @@ "unable_to_change_pin_code": "Impossibile cambiare il codice PIN", "unable_to_setup_pin_code": "Impossibile configurare il codice PIN", "unarchive": "Annulla l'archiviazione", + "unarchive_action_prompt": "{count} elementi rimossi dall'Archivio", "unarchived_count": "{count, plural, other {Non archiviati #}}", "undo": "Annulla", "unfavorite": "Rimuovi preferito", diff --git a/i18n/lv.json b/i18n/lv.json index 5af3e0511c..2e6b702e22 100644 --- a/i18n/lv.json +++ b/i18n/lv.json @@ -707,6 +707,9 @@ "next_memory": "Nākamā atmiņa", "no": "Nē", "no_albums_message": "Izveido albumu, lai organizētu savas fotogrāfijas un video", + "no_albums_with_name_yet": "Izskatās, ka tev vēl nav albumu ar šādu nosaukumu.", + "no_albums_yet": "Izskatās, ka tev vēl nav neviena albuma.", + "no_archived_assets_message": "Arhivē fotoattēlus un videoklipus, lai paslēptu tos no Fotoattēli skata", "no_assets_message": "NOKLIKŠĶINIET, LAI AUGŠUPIELĀDĒTU SAVU PIRMO FOTOATTĒLU", "no_assets_to_show": "Nav uzrādāmo aktīvu", "no_duplicates_found": "Dublikāti netika atrasti.", @@ -730,6 +733,10 @@ "official_immich_resources": "Oficiālie Immich resursi", "offline": "Bezsaistē", "ok": "Labi", + "onboarding": "Uzņemšana", + "onboarding_locale_description": "Izvēlies vēlamo valodu. To vēlāk var mainīt iestatījumos.", + "onboarding_theme_description": "Izvēlies savas instances krāsu motīvu. To vēlāk var mainīt iestatījumos.", + "onboarding_user_welcome_description": "Sāksim darbu!", "online": "Tiešsaistē", "only_favorites": "Tikai izlase", "open_in_map_view": "Atvērt kartes skatā", @@ -737,13 +744,16 @@ "open_the_search_filters": "Atvērt meklēšanas filtrus", "options": "Iestatījumi", "or": "vai", + "organize_your_library": "Bibliotēkas organizēšana", "original": "oriģināls", "other": "Citi", "other_devices": "Citas ierīces", "other_variables": "Citi mainīgie", "owned": "Īpašumā", "owner": "Īpašnieks", + "partner": "Partneris", "partner_can_access": "{partner} var piekļūt", + "partner_can_access_location": "Fotogrāfiju uzņemšanas vieta", "partner_list_user_photos": "{user} fotoattēli", "partner_list_view_all": "Apskatīt visu", "partner_page_empty_message": "Jūsu fotogrāfijas pagaidām nav kopīgotas ar nevienu partneri.", @@ -757,6 +767,7 @@ "password_does_not_match": "Parole nesakrīt", "path": "Ceļš", "pause": "Pauzēt", + "pause_memories": "Pauzēt atmiņas", "paused": "Nopauzēts", "people": "Cilvēki", "permission_onboarding_back": "Atpakaļ", diff --git a/i18n/nb_NO.json b/i18n/nb_NO.json index 89b5e298bc..2abc08e7f2 100644 --- a/i18n/nb_NO.json +++ b/i18n/nb_NO.json @@ -573,6 +573,8 @@ "backup_options_page_title": "Backupinnstillinger", "backup_setting_subtitle": "Administrer opplastingsinnstillinger for bakgrunn og forgrunn", "backward": "Bakover", + "beta_sync": "Beta synkroniseringsstatus", + "beta_sync_subtitle": "Håndter det nye synkroniseringssystemet", "biometric_auth_enabled": "Biometrisk autentisering aktivert", "biometric_locked_out": "Du er låst ute av biometrisk verifisering", "biometric_no_options": "Ingen biometriske valg tilgjengelige", @@ -590,7 +592,7 @@ "cache_settings_clear_cache_button": "Tøm buffer", "cache_settings_clear_cache_button_title": "Tømmer app-ens buffer. Dette vil ha betydelig innvirkning på appens ytelse inntil bufferen er gjenoppbygd.", "cache_settings_duplicated_assets_clear_button": "TØM", - "cache_settings_duplicated_assets_subtitle": "Bilder og videoer som er svartelistet av app'en", + "cache_settings_duplicated_assets_subtitle": "Bilder og videoer som er ignorert av app'en", "cache_settings_duplicated_assets_title": "Dupliserte objekter ({count})", "cache_settings_statistics_album": "Bibliotekminiatyrbilder", "cache_settings_statistics_full": "Originalbilder", @@ -1051,6 +1053,9 @@ "haptic_feedback_switch": "Aktivert haptisk tilbakemelding", "haptic_feedback_title": "Haptisk tilbakemelding", "has_quota": "Kvote", + "hash_asset": "Hash objekter", + "hashed_assets": "Hashede objekter", + "hashing": "Hasher", "header_settings_add_header_tip": "Legg til header", "header_settings_field_validator_msg": "Verdi kan ikke være null", "header_settings_header_name_input": "Header navn", @@ -1083,6 +1088,7 @@ "host": "Vert", "hour": "Time", "id": "ID", + "idle": "Uvirksom", "ignore_icloud_photos": "Ignorer iCloud bilder", "ignore_icloud_photos_description": "Bilder som er lagret på iCloud vil ikke lastes opp til Immich", "image": "Bilde", @@ -1165,7 +1171,9 @@ "list": "Liste", "loading": "Laster", "loading_search_results_failed": "Klarte ikke å laste inn søkeresultater", + "local": "Lokal", "local_asset_cast_failed": "Kan ikke caste et bilde som ikke er lastet opp til serveren", + "local_assets": "Lokale objekter", "local_network": "Lokalt nettverk", "local_network_sheet_info": "Appen vil koble til serveren via denne URL-en når du bruker det angitte Wi-Fi-nettverket", "location_permission": "Stedstillatelse", @@ -1322,6 +1330,7 @@ "no_results": "Ingen resultater", "no_results_description": "Prøv et synonym eller mer generelt søkeord", "no_shared_albums_message": "Opprett et album for å dele bilder og videoer med personer i nettverket ditt", + "no_uploads_in_progress": "Ingen opplasting pågår", "not_in_any_album": "Ikke i noe album", "not_selected": "Ikke valgt", "note_apply_storage_label_to_previously_uploaded assets": "Merk: For å bruke lagringsetiketten på tidligere opplastede filer, kjør", @@ -1359,6 +1368,7 @@ "original": "original", "other": "Annet", "other_devices": "Andre enheter", + "other_entities": "Andre objekter", "other_variables": "Andre variabler", "owned": "Dine", "owner": "Eier", @@ -1519,6 +1529,8 @@ "refreshing_faces": "Oppdaterer ansikter", "refreshing_metadata": "Oppdaterer matadata", "regenerating_thumbnails": "Regenererer miniatyrbilder", + "remote": "Eksternt", + "remote_assets": "Eksterne objekter", "remove": "Fjern", "remove_assets_album_confirmation": "Er du sikker på at du fil slette {count, plural, one {# asset} other {# assets}} fra albumet?", "remove_assets_shared_link_confirmation": "Er du sikker på at du vil slette {count, plural, one {# asset} other {# assets}} fra den delte lenken?", @@ -1556,11 +1568,15 @@ "reset_password": "Tilbakestill passord", "reset_people_visibility": "Tilbakestill personsynlighet", "reset_pin_code": "Resett PINkode", + "reset_sqlite": "Reset SQLite Databasen", + "reset_sqlite_confirmation": "Er du sikker på at du vil resette SQLite databasen? Du blir nødt til å logge ut og inn igjen for å resynkronisere data", + "reset_sqlite_success": "Vellykket resetting av SQLite databasen", "reset_to_default": "Tilbakestill til standard", "resolve_duplicates": "Løs duplikater", "resolved_all_duplicates": "Løste alle duplikater", "restore": "Gjenopprett", "restore_all": "Gjenopprett alle", + "restore_trash_action_prompt": "{count} gjenopprettet fra søppelbøtten", "restore_user": "Gjenopprett bruker", "restored_asset": "Gjenopprettet ressurs", "resume": "Fortsett", @@ -1569,6 +1585,7 @@ "role": "Rolle", "role_editor": "Redigerer", "role_viewer": "Visning", + "running": "Kjører", "save": "Lagre", "save_to_gallery": "Lagre til galleriet", "saved_api_key": "Lagret API-nøkkel", @@ -1822,6 +1839,7 @@ "storage_quota": "Lagringsplass", "storage_usage": "{used} av {available} brukt", "submit": "Send inn", + "success": "Vellykket", "suggestions": "Forslag", "sunrise_on_the_beach": "Soloppgang på stranden", "support": "Støtte", @@ -1831,6 +1849,8 @@ "sync": "Synkroniser", "sync_albums": "Synkroniser albumer", "sync_albums_manual_subtitle": "Synkroniser alle opplastede videoer og bilder til det valgte backupalbumet", + "sync_local": "Synkroniser lokalt", + "sync_remote": "Synkroniser eksternt", "sync_upload_album_setting_subtitle": "Opprett og last opp dine bilder og videoer til det valgte albumet på Immich", "tag": "Tagg", "tag_assets": "Merk ressurser", @@ -1841,6 +1861,7 @@ "tag_updated": "Oppdater merke: {tag}", "tagged_assets": "Merket {count, plural, one {# asset} other {# assets}}", "tags": "Merker", + "tap_to_run_job": "Trykk for å kjøre jobben", "template": "Mal", "theme": "Tema", "theme_selection": "Temavalg", diff --git a/i18n/nl.json b/i18n/nl.json index 2cedd63b15..86cb0fba6e 100644 --- a/i18n/nl.json +++ b/i18n/nl.json @@ -426,6 +426,7 @@ "albums_default_sort_order": "Standaard sorteervolgorde album", "albums_default_sort_order_description": "Initiële sorteervolgorde bij het maken van nieuwe albums.", "albums_feature_description": "Collectie van assets die je kan delen met andere gebruikers.", + "albums_on_device_count": "Albums op apparaat ({count})", "all": "Alle", "all_albums": "Alle albums", "all_people": "Alle mensen", @@ -572,6 +573,8 @@ "backup_options_page_title": "Back-up instellingen", "backup_setting_subtitle": "Beheer achtergrond en voorgrond uploadinstellingen", "backward": "Achteruit", + "beta_sync": "Beta Sync Status", + "beta_sync_subtitle": "Beheer het nieuwe synchronisatiesysteem", "biometric_auth_enabled": "Biometrische authenticatie ingeschakeld", "biometric_locked_out": "Biometrische authenticatie is vergrendeld", "biometric_no_options": "Geen biometrische opties beschikbaar", @@ -589,7 +592,7 @@ "cache_settings_clear_cache_button": "Cache wissen", "cache_settings_clear_cache_button_title": "Wist de cache van de app. Dit zal de presentaties van de app aanzienlijk beïnvloeden totdat de cache opnieuw is opgebouwd.", "cache_settings_duplicated_assets_clear_button": "MAAK VRIJ", - "cache_settings_duplicated_assets_subtitle": "Foto's en video's op de zwarte lijst van de app", + "cache_settings_duplicated_assets_subtitle": "Foto’s en video's die de app negeert", "cache_settings_duplicated_assets_title": "Gedupliceerde assets ({count})", "cache_settings_statistics_album": "Bibliotheekthumbnails", "cache_settings_statistics_full": "Volledige afbeeldingen", @@ -1050,6 +1053,9 @@ "haptic_feedback_switch": "Aanraaktrillingen inschakelen", "haptic_feedback_title": "Aanraaktrillingen", "has_quota": "Heeft limiet", + "hash_asset": "Hash asset", + "hashed_assets": "Gehashte assets", + "hashing": "Hashen", "header_settings_add_header_tip": "Header toevoegen", "header_settings_field_validator_msg": "Waarde kan niet leeg zijn", "header_settings_header_name_input": "Header naam", @@ -1082,6 +1088,7 @@ "host": "Host", "hour": "Uur", "id": "ID", + "idle": "Idle", "ignore_icloud_photos": "Negeer iCloud foto's", "ignore_icloud_photos_description": "Foto's die op iCloud zijn opgeslagen, worden niet geüpload naar de Immich server", "image": "Afbeelding", @@ -1164,7 +1171,9 @@ "list": "Lijst", "loading": "Laden", "loading_search_results_failed": "Laden van zoekresultaten mislukt", + "local": "Lokaal", "local_asset_cast_failed": "Kan geen asset casten die nog niet geüpload is naar de server", + "local_assets": "Lokale Assets", "local_network": "Lokaal netwerk", "local_network_sheet_info": "De app maakt verbinding met de server via deze URL wanneer het opgegeven WiFi-netwerk wordt gebruikt", "location_permission": "Locatietoestemming", @@ -1321,6 +1330,7 @@ "no_results": "Geen resultaten", "no_results_description": "Probeer een synoniem of een algemener zoekwoord", "no_shared_albums_message": "Maak een album om foto's en video's te delen met mensen in je netwerk", + "no_uploads_in_progress": "Geen uploads bezig", "not_in_any_album": "Niet in een album", "not_selected": "Niet geselecteerd", "note_apply_storage_label_to_previously_uploaded assets": "Opmerking: om het opslaglabel toe te passen op eerder geüploade assets, voer de volgende taak uit", @@ -1358,6 +1368,7 @@ "original": "origineel", "other": "Overige", "other_devices": "Andere apparaten", + "other_entities": "Andere entities", "other_variables": "Andere variabelen", "owned": "Eigenaar", "owner": "Eigenaar", @@ -1518,6 +1529,8 @@ "refreshing_faces": "Gezichten aan het vernieuwen", "refreshing_metadata": "Metadata aan het vernieuwen", "regenerating_thumbnails": "Thumbnails opnieuw aan het genereren", + "remote": "Remote", + "remote_assets": "Remote Assets", "remove": "Verwijderen", "remove_assets_album_confirmation": "Weet je zeker dat je {count, plural, one {# asset} other {# assets}} uit het album wilt verwijderen?", "remove_assets_shared_link_confirmation": "Weet je zeker dat je {count, plural, one {# asset} other {# assets}} uit deze gedeelde link wilt verwijderen?", @@ -1555,11 +1568,15 @@ "reset_password": "Wachtwoord resetten", "reset_people_visibility": "Zichtbaarheid mensen resetten", "reset_pin_code": "Reset PIN code", + "reset_sqlite": "Reset SQLite Database", + "reset_sqlite_confirmation": "Ben je zeker dat je de SQLite database wilt resetten? Je zal moetenn uitloggen om de data opnieuw te synchroniseren.", + "reset_sqlite_success": "De SQLite database is succesvol gereset", "reset_to_default": "Resetten naar standaard", "resolve_duplicates": "Duplicaten oplossen", "resolved_all_duplicates": "Alle duplicaten opgelost", "restore": "Herstellen", "restore_all": "Herstel alle", + "restore_trash_action_prompt": "{count} teruggezet uit prullenbak", "restore_user": "Gebruiker herstellen", "restored_asset": "Asset hersteld", "resume": "Hervatten", @@ -1568,6 +1585,7 @@ "role": "Rol", "role_editor": "Bewerker", "role_viewer": "Bekijker", + "running": "Actief", "save": "Opslaan", "save_to_gallery": "Opslaan in galerij", "saved_api_key": "API-sleutel opgeslagen", @@ -1821,6 +1839,7 @@ "storage_quota": "Opslaglimiet", "storage_usage": "{used} van {available} gebruikt", "submit": "Verzenden", + "success": "Succes", "suggestions": "Suggesties", "sunrise_on_the_beach": "Zonsopkomst op het strand", "support": "Ondersteuning", @@ -1830,6 +1849,8 @@ "sync": "Sync", "sync_albums": "Albums synchroniseren", "sync_albums_manual_subtitle": "Synchroniseer alle geüploade video’s en foto’s naar de geselecteerde back-up albums", + "sync_local": "Lokaal synchroniseren", + "sync_remote": "Op afstand synchroniseren", "sync_upload_album_setting_subtitle": "Maak en upload je foto's en video's naar de geselecteerde albums op Immich", "tag": "Tag", "tag_assets": "Assets taggen", @@ -1840,6 +1861,7 @@ "tag_updated": "Tag bijgewerkt: {tag}", "tagged_assets": "{count, plural, one {# asset} other {# assets}} getagd", "tags": "Tags", + "tap_to_run_job": "Klik om job te starten", "template": "Template", "theme": "Thema", "theme_selection": "Thema selectie", diff --git a/i18n/ru.json b/i18n/ru.json index 84db991027..c0a01b0be2 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -228,7 +228,7 @@ "password_settings_description": "Управление настройками входа по паролю", "paths_validated_successfully": "Все пути успешно прошли проверку", "person_cleanup_job": "Очистка персоны", - "quota_size_gib": "Размер квоты (ГБ)", + "quota_size_gib": "Размер квоты (GiB)", "refreshing_all_libraries": "Обновление всех библиотек", "registration": "Регистрация администратора", "registration_description": "Первый зарегистрированный пользователь будет назначен администратором. В дальнейшем этой учетной записи будет доступно создание дополнительных пользователей и управление сервером.", @@ -241,7 +241,7 @@ "server_external_domain_settings": "Внешний домен", "server_external_domain_settings_description": "Домен для публичных ссылок, включая http(s)://", "server_public_users": "Публичные пользователи", - "server_public_users_description": "Отображать всех пользователей (имена и email) для добавления в общие альбомы. Когда отключено, список пользователей будет доступен только администраторам.", + "server_public_users_description": "Выводить список пользователей (имена и email) в общих альбомах. Когда отключено, список доступен только администраторам, пользователи смогут делиться только ссылкой.", "server_settings": "Настройки сервера", "server_settings_description": "Управление настройками сервера", "server_welcome_message": "Приветственное сообщение", @@ -407,7 +407,7 @@ "album_remove_user": "Удалить пользователя?", "album_remove_user_confirmation": "Вы уверены, что хотите удалить пользователя {user}?", "album_search_not_found": "Не найдено альбомов по вашему запросу", - "album_share_no_users": "Похоже, вы поделились этим альбомом со всеми пользователями или у вас нет пользователей, с которыми можно поделиться.", + "album_share_no_users": "Нет доступных пользователей, с которыми можно поделиться альбомом.", "album_updated": "Альбом обновлён", "album_updated_setting_description": "Получать уведомление по электронной почте при добавлении новых ресурсов в общий альбом", "album_user_left": "Вы покинули {album}", @@ -433,8 +433,8 @@ "all_videos": "Все видео", "allow_dark_mode": "Разрешить темный режим", "allow_edits": "Разрешить редактирование", - "allow_public_user_to_download": "Разрешить скачивание публичным пользователям", - "allow_public_user_to_upload": "Разрешить публичным пользователям загружать файлы", + "allow_public_user_to_download": "Разрешить скачивание", + "allow_public_user_to_upload": "Разрешить добавление файлов", "alt_text_qr_code": "QR-код", "anti_clockwise": "Против часовой", "api_key": "API ключ", @@ -573,6 +573,8 @@ "backup_options_page_title": "Резервное копирование", "backup_setting_subtitle": "Настройка активного и фонового резервного копирования", "backward": "Назад", + "beta_sync": "Статус бета-синхронизации", + "beta_sync_subtitle": "Управление новой системой синхронизации", "biometric_auth_enabled": "Биометрическая аутентификация включена", "biometric_locked_out": "Вам закрыт доступ к биометрической аутентификации", "biometric_no_options": "Биометрическая аутентификация недоступна", @@ -590,7 +592,7 @@ "cache_settings_clear_cache_button": "Очистить кэш", "cache_settings_clear_cache_button_title": "Очищает кэш приложения. Это негативно повлияет на производительность, пока кэш не будет создан заново.", "cache_settings_duplicated_assets_clear_button": "ОЧИСТИТЬ", - "cache_settings_duplicated_assets_subtitle": "Фото и видео, занесенные приложением в черный список", + "cache_settings_duplicated_assets_subtitle": "Фото и видео, пропускаемые приложением", "cache_settings_duplicated_assets_title": "Дублирующиеся объекты ({count})", "cache_settings_statistics_album": "Миниатюры библиотеки", "cache_settings_statistics_full": "Полные изображения", @@ -616,7 +618,7 @@ "change_date": "Изменить дату", "change_description": "Изменить описание", "change_display_order": "Изменить порядок отображения", - "change_expiration_time": "Изменить время окончания", + "change_expiration_time": "Изменить срок действия", "change_location": "Изменить местоположение", "change_name": "Изменить имя", "change_name_successfully": "Имя успешно изменено", @@ -692,7 +694,7 @@ "copy_link": "Копировать ссылку", "copy_link_to_clipboard": "Скопировать ссылку в буфер обмена", "copy_password": "Скопировать пароль", - "copy_to_clipboard": "Скопировать в буфер обмена", + "copy_to_clipboard": "Скопировать настройки в буфер обмена", "country": "Страна", "cover": "Обложка", "covers": "Обложки", @@ -830,7 +832,7 @@ "edit_people": "Редактировать людей", "edit_tag": "Изменить тег", "edit_title": "Редактировать Заголовок", - "edit_user": "Редактирование пользователя", + "edit_user": "Изменить пользователя", "edited": "Отредактировано", "editor": "Редактор", "editor_close_without_save_prompt": "Изменения не будут сохранены", @@ -994,7 +996,7 @@ "experimental_settings_subtitle": "Используйте на свой страх и риск!", "experimental_settings_title": "Экспериментальные функции", "expire_after": "Истекает через", - "expired": "Срок действия истек", + "expired": "Срок действия истёк", "expires_date": "Срок действия до {date}", "explore": "Поиск", "explorer": "Проводник", @@ -1051,6 +1053,9 @@ "haptic_feedback_switch": "Включить тактильную отдачу", "haptic_feedback_title": "Тактильная отдача", "has_quota": "Квота", + "hash_asset": "Хешированный объект", + "hashed_assets": "Хешированные объекты", + "hashing": "Хеширование", "header_settings_add_header_tip": "Добавить заголовок", "header_settings_field_validator_msg": "Значение не может быть пустым", "header_settings_header_name_input": "Имя заголовка", @@ -1083,6 +1088,7 @@ "host": "Хост", "hour": "Час", "id": "ID", + "idle": "В ожидании", "ignore_icloud_photos": "Пропускать файлы из iCloud", "ignore_icloud_photos_description": "Не загружать файлы в Immich, если они хранятся в iCloud", "image": "Изображения", @@ -1109,8 +1115,8 @@ "include_archived": "Отображать архив", "include_shared_albums": "Включать общие альбомы", "include_shared_partner_assets": "Включать общие ресурсы партнера", - "individual_share": "Персональный доступ", - "individual_shares": "Индивидуальный доступ", + "individual_share": "Индивидуальная подборка", + "individual_shares": "Подборки", "info": "Информация", "interval": { "day_at_onepm": "Каждый день в 13:00", @@ -1165,7 +1171,9 @@ "list": "Список", "loading": "Загрузка", "loading_search_results_failed": "Загрузка результатов поиска не удалась", + "local": "На устройстве", "local_asset_cast_failed": "Невозможно транслировать объект, который ещё не загружен на сервер", + "local_assets": "Объекты на устройстве", "local_network": "Локальная сеть", "local_network_sheet_info": "Приложение будет подключаться к серверу по этому адресу, когда устройство подключено к выбранной Wi-Fi сети", "location_permission": "Доступ к местоположению", @@ -1322,6 +1330,7 @@ "no_results": "Нет результатов", "no_results_description": "Попробуйте использовать синоним или более общее ключевое слово", "no_shared_albums_message": "Создайте альбом для обмена фотографиями и видеозаписями с людьми в вашей сети", + "no_uploads_in_progress": "Нет активных загрузок", "not_in_any_album": "Ни в одном альбоме", "not_selected": "Не выбрано", "note_apply_storage_label_to_previously_uploaded assets": "Примечание: Чтобы применить метку хранилища к ранее загруженным ресурсам, запустите", @@ -1359,6 +1368,7 @@ "original": "оригинал", "other": "Другое", "other_devices": "Другие устройства", + "other_entities": "Другие объекты", "other_variables": "Другие переменные", "owned": "Мои", "owner": "Владелец", @@ -1456,7 +1466,7 @@ "profile_drawer_server_out_of_date_minor": "Версия сервера устарела. Пожалуйста, обновите его.", "profile_image_of_user": "Изображение профиля {user}", "profile_picture_set": "Фото профиля установлено.", - "public_album": "Публичный альбом", + "public_album": "Общий альбом", "public_share": "Публичный доступ", "purchase_account_info": "Поддержка", "purchase_activated_subtitle": "Благодарим вас за поддержку Immich и программного обеспечения с открытым исходным кодом", @@ -1519,6 +1529,8 @@ "refreshing_faces": "Обновление лиц", "refreshing_metadata": "Обновление метаданных", "regenerating_thumbnails": "Восстановление миниатюр", + "remote": "На сервере", + "remote_assets": "Объекты на сервере", "remove": "Удалить", "remove_assets_album_confirmation": "Вы действительно хотите удалить {count, plural, one {# объект} many {# объектов} other {# объекта}} из альбома?", "remove_assets_shared_link_confirmation": "Вы действительно хотите удалить {count, plural, one {# объект} many {# объектов} other {# объекта}} из публичного доступа по этой ссылке?", @@ -1556,11 +1568,15 @@ "reset_password": "Сброс пароля", "reset_people_visibility": "Восстановить видимость людей", "reset_pin_code": "Сбросить PIN-код", + "reset_sqlite": "Очистить базу данных SQLite", + "reset_sqlite_confirmation": "Вы уверены, что хотите очистить базу данных SQLite? Вам потребуется выйти из системы и снова войти для повторной синхронизации данных.", + "reset_sqlite_success": "База данных SQLite успешно очищена", "reset_to_default": "Восстановление значений по умолчанию", "resolve_duplicates": "Устранить дубликаты", "resolved_all_duplicates": "Все дубликаты устранены", "restore": "Восстановить", "restore_all": "Восстановить все", + "restore_trash_action_prompt": "{count} восстановлено из корзины", "restore_user": "Восстановить пользователя", "restored_asset": "Восстановленный объект", "resume": "Продолжить", @@ -1569,6 +1585,7 @@ "role": "Роль", "role_editor": "Редактор", "role_viewer": "Зритель", + "running": "Выполняется", "save": "Сохранить", "save_to_gallery": "Сохранить в галерею", "saved_api_key": "API ключ изменён", @@ -1822,6 +1839,7 @@ "storage_quota": "Квота хранилища", "storage_usage": "{used} из {available}", "submit": "Подтвердить", + "success": "Успешно", "suggestions": "Предложения", "sunrise_on_the_beach": "Восход солнца на пляже", "support": "Поддержка", @@ -1831,6 +1849,8 @@ "sync": "Синхр.", "sync_albums": "Синхронизировать альбомы", "sync_albums_manual_subtitle": "Синхронизировать все загруженные фото и видео в выбранные альбомы для резервного копирования", + "sync_local": "Синхронизировать локально", + "sync_remote": "Синхронизация с сервером", "sync_upload_album_setting_subtitle": "Создавайте и загружайте свои фотографии и видео в выбранные альбомы на сервер Immich", "tag": "Тег", "tag_assets": "Добавить теги", @@ -1841,6 +1861,7 @@ "tag_updated": "Тег {tag} изменен", "tagged_assets": "Тег назначен для {count, plural, one {# объекта} other {# объектов}}", "tags": "Теги", + "tap_to_run_job": "Нажмите для запуска задачи", "template": "Шаблон", "theme": "Тема", "theme_selection": "Выбор темы", diff --git a/i18n/sk.json b/i18n/sk.json index c8c1cb5303..a29262a6e8 100644 --- a/i18n/sk.json +++ b/i18n/sk.json @@ -45,14 +45,14 @@ "backup_database_enable_description": "Povoliť výpisy z databázy", "backup_keep_last_amount": "Množstvo predchádzajúcich výpisov, ktoré sa majú zachovať", "backup_settings": "Nastavenia výpisu databázy", - "backup_settings_description": "Správa nastavení výpisu databázy.", + "backup_settings_description": "Spravovať nastavenia výpisu databázy.", "cleared_jobs": "Hotové úlohy pre: {job}", "config_set_by_file": "Konfigurácia je v súčasnosti nastavená konfiguračným súborom", "confirm_delete_library": "Naozaj chcete vymazať knižnicu {library}?", "confirm_delete_library_assets": "Ste si istí, že chcete vymazať túto knižnicu? Tato operácia nenávratne odstráni {count, plural, one {# zahrnutú položku} few {# zahrnuté položky} other {všetkých # zahrnutých položiek}} z aplikácie Immich. Súbory budú ponechané na disku.", "confirm_email_below": "Pre potvrdenie zadajte \"{email}\" nižšie", "confirm_reprocess_all_faces": "Naozaj chcete spracovať všetky tváre znova? Tento proces vymaže pomenovaných ľudí.", - "confirm_user_password_reset": "Naozaj chcete resetovať heslo pre {user}?", + "confirm_user_password_reset": "Naozaj chcete obnoviť heslo pre {user}?", "confirm_user_pin_code_reset": "Ste si istí, že chcete opätovne nastaviť PIN kód používateľa {user}?", "create_job": "Vytvoriť úlohu", "cron_expression": "Výraz cron", @@ -61,10 +61,10 @@ "disable_login": "Zakázať prihlásenie", "duplicate_detection_job_description": "Spustite strojové učenie na položkách pre detekciu podobných obrázkov. Spolieha sa na inteligentné vyhľadávanie", "exclusion_pattern_description": "Vylučovacie vzory Vám umožňujú ignorovať súbory a priečinky pri skenovaní Vašej knižnice. Toto je užitočné, ak máte priečinky obsahujúce súbory, ktoré nechcete importovať, napríklad RAW súbory.", - "external_library_management": "Správa Externej Knižnice", + "external_library_management": "Spravovanie externej knižnice", "face_detection": "Detekcia tvárí", - "face_detection_description": "Rozpoznajte tváre v položkách pomocou strojového učenia. V prípade videí sa berie do úvahy len náhľad. „Obnoviť“ (znovu) spracuje všetky položky. „Resetovať“ dodatočne vymaže všetky aktuálne údaje o tvárach. „Chýbajúce“ zaradí do poradia médiá, ktoré ešte neboli spracované. Zistené tváre sa po dokončení rozpoznávania tvárí zaradia do poradia na rozpoznávanie tvárí, pričom sa zoskupia do existujúcich alebo nových osôb.", - "facial_recognition_job_description": "Zoskupte rozpoznané tváre do osôb. Tento krok sa vykoná po dokončení rozpoznávania tvárí. „Resetovať“ (znovu) zoskupí všetky tváre. „Chýbajúce“ zaradí tváre, ktoré nemajú pridelenú osobu.", + "face_detection_description": "Rozpoznajte tváre v položkách pomocou strojového učenia. V prípade videí sa berie do úvahy len náhľad. „Aktualizovať“ (znovu) spracuje všetky položky. „Obnoviť“ dodatočne vymaže všetky aktuálne údaje o tvárach. „Chýbajúce“ zaradí do poradia médiá, ktoré ešte neboli spracované. Zistené tváre sa po dokončení rozpoznávania tvárí zaradia do poradia na rozpoznávanie tvárí, pričom sa zoskupia do existujúcich alebo nových osôb.", + "facial_recognition_job_description": "Zoskupte rozpoznané tváre do osôb. Tento krok sa vykoná po dokončení rozpoznávania tvárí. „Obnoviť“ (znovu) zoskupí všetky tváre. „Chýbajúce“ zaradí tváre, ktoré nemajú pridelenú osobu.", "failed_job_command": "Príkaz {command} zlyhal pre úlohu: {job}", "force_delete_user_warning": "VAROVANIE: Toto okamžite odstráni používateľa a všetky položky. Tento krok nie je možné vrátiť späť a súbory nebude možné obnoviť.", "image_format": "Formát", @@ -94,7 +94,7 @@ "job_not_concurrency_safe": "Táto úloha nie je bezpečná pre súbežné spracovanie.", "job_settings": "Úlohy", "job_settings_description": "Spravovať súbežnosť úloh", - "job_status": "Stav Úloh", + "job_status": "Stav úloh", "jobs_delayed": "{jobCount, plural, one {# oneskorený} few {# oneskorené} other {# oneskorených}}", "jobs_failed": "{jobCount, plural, one {# neúspešný} few {# neúspešné} other {# neúspešných}}", "library_created": "Vytvorená knižnica: {library}", @@ -141,15 +141,15 @@ "machine_learning_smart_search_enabled": "Povoliť inteligentné vyhľadávanie", "machine_learning_smart_search_enabled_description": "Ak je vypnuté, obrázky nebudú spracované pre inteligentné vyhľadávanie.", "machine_learning_url_description": "URL adresa servera strojového učenia. Ak je zadaných viacero adries URL, každý server bude testovaný postupne, kým jeden z nich neodpovie úspešne, v poradí od prvého po posledný. Servery, ktoré neodpovedajú, budú dočasne ignorované, kým nebudú opäť online.", - "manage_concurrency": "Správa súbežnosti", + "manage_concurrency": "Spravovať súbežnosť", "manage_log_settings": "Spravovať nastavenia ukladania záznamov", "map_dark_style": "Tmavý štýl", "map_enable_description": "Povoliť funkcie mapy", "map_gps_settings": "Mapa a nastavenia GPS", - "map_gps_settings_description": "Spravujte nastavenia mapy a GPS (reverzné geokódovanie)", + "map_gps_settings_description": "Spravovať nastavenia mapy a GPS (reverzné geokódovanie)", "map_implications": "Táto funkčnosť sa spolieha na externý servis spracovania mapových dlaždíc (tiles.immich.cloud)", "map_light_style": "Svetlý štýl", - "map_manage_reverse_geocoding_settings": "Správa nastavení Reverzného geokódovania", + "map_manage_reverse_geocoding_settings": "Spravovať nastavenia reverzného geokódovania", "map_reverse_geocoding": "Reverzné Geokódovanie", "map_reverse_geocoding_enable_description": "Povoliť reverzné geokódovanie", "map_reverse_geocoding_settings": "Reverzné geokódovanie", @@ -214,7 +214,7 @@ "oauth_role_claim_description": "Automaticky udeliť prístup správcu na základe prítomnosti tejto požiadavky. Požiadavka môže mať príznak „user“ alebo „admin“.", "oauth_settings": "OAuth", "oauth_settings_description": "Spravovať nastavenia prihlásenia OAuth", - "oauth_settings_more_details": "Pre viac informácii o tejto funkcii, prejdite na docs.", + "oauth_settings_more_details": "Pre viac informácii o tejto funkcii, prejdite na dokumentáciu.", "oauth_storage_label_claim": "Nárokovať Štítok úložiska", "oauth_storage_label_claim_description": "Automaticky nastaviť štítok úložiska používateľa na hodnotu tohto nároku.", "oauth_storage_quota_claim": "Deklarácia kvóty úložiska", @@ -234,7 +234,7 @@ "registration_description": "Keďže ste prvým používateľom v systéme, budú vám pridelené správcovské práva na vykonávanie všetkých úloh a vrátane tvorby nových používateľov.", "require_password_change_on_login": "Vyžadovať od používateľa zmenu hesla pri prvom prihlásení", "reset_settings_to_default": "Obnoviť pôvodné nastavenia", - "reset_settings_to_recent_saved": "Obnoviť naposledy uložené nastavenia", + "reset_settings_to_recent_saved": "Nastavenia boli obnovené na posledné uložené nastavenia", "scanning_library": "Knižnica sa skenuje", "search_jobs": "Vyhľadať úlohy…", "send_welcome_email": "Odoslať uvítací e-mail", @@ -259,7 +259,7 @@ "storage_template_migration_description": "Použite aktuálnu {template} na predtým nahrané médiá", "storage_template_migration_info": "Šablóna úložiska skonvertuje všetky prípony na malé písmená. Zmeny šablón sa budú vzťahovať iba na nové diela. Ak chcete šablónu spätne použiť na predtým nahrané médiá, spustite {job}.", "storage_template_migration_job": "Úloha migrácie šablóny úložiska", - "storage_template_more_details": "Ďalšie podrobnosti o tejto funkcii nájdete v Šablóna úložiska a jej dôsledky", + "storage_template_more_details": "Podrobnejšie informácie o tejto funkcii nájdete v časti šablóna úložiska a jej následky", "storage_template_onboarding_description_v2": "Ak je táto funkcia zapnutá, automaticky usporiada súbory na základe šablóny definovanej používateľom. Ďalšie informácie nájdete v dokumentácii.", "storage_template_path_length": "Približný limit dĺžky cesty: {length, number}/{limit, number}", "storage_template_settings": "Šablóna úložiska", @@ -278,9 +278,9 @@ "template_settings_description": "Spravovanie vlastných šablón upozornení", "theme_custom_css_settings": "Vlastné CSS", "theme_custom_css_settings_description": "CSS štýly umožňujú prispôsobiť dizajn Immich.", - "theme_settings": "Motívy", + "theme_settings": "Nastavenia témy", "theme_settings_description": "Spravovať prispôsobenie webového rozhrania Immich", - "thumbnail_generation_job": "Generovať Miniatúry", + "thumbnail_generation_job": "Generovať miniatúry", "thumbnail_generation_job_description": "Generujte veľké, malé a rozmazané miniatúry pre každú položku, ako aj miniatúry pre každú osobu", "transcoding_acceleration_api": "API pre akceleráciu", "transcoding_acceleration_api_description": "Rozhranie API, ktoré bude spolupracovať s vaším zariadením s cieľom urýchliť prekódovanie. Toto nastavenie je „najlepšie úsilie“: pri zlyhaní sa vráti k softvérovému prekódovaniu. VP9 môže alebo nemusí fungovať v závislosti od vášho hardvéru.", @@ -300,7 +300,7 @@ "transcoding_bitrate_description": "Videá presahujúce maximálnu bitovú rýchlosť alebo videá, ktoré nie sú v akceptovanom formáte", "transcoding_codecs_learn_more": "Ak sa chcete dozvedieť viac o tu použitej terminológii, pozrite si dokumentáciu FFmpeg pre kodek H.264, kodek HEVC a VP9 kodek.", "transcoding_constant_quality_mode": "Režim konštantnej kvality", - "transcoding_constant_quality_mode_description": "ICQ je lepšie ako CQP, ale niektoré zariadenia na hardvérovú akceleráciu tento režim nepodporujú. Nastavenie tejto možnosti uprednostní špecifikovaný režim pri použití kódovania založeného na kvalite. Ignorované spoločnosťou NVENC, pretože nepodporuje ICQ.", + "transcoding_constant_quality_mode_description": "ICQ je lepšie ako CQP, ale niektoré zariadenia na hardvérovú akceleráciu tento režim nepodporujú. Nastavenie tejto možnosti uprednostní špecifikovaný režim pri použití kódovania založeného na kvalite. Ignorované funkciou NVENC, pretože nepodporuje ICQ.", "transcoding_constant_rate_factor": "Faktor konštantnej rýchlosti (-crf)", "transcoding_constant_rate_factor_description": "Úroveň kvality videa. Typické hodnoty sú 23 pre H.264, 28 pre HEVC, 31 pre VP9 a 35 pre AV1. Nižšie je lepšie, ale vytvára väčšie súbory.", "transcoding_disabled_description": "Neprekódovať žiadne videá, na niektorých klientoch môže prerušiť prehrávanie", @@ -321,13 +321,13 @@ "transcoding_policy_description": "Nastavte, kedy bude video prekódované", "transcoding_preferred_hardware_device": "Uprednostňované hardvérové zariadenie", "transcoding_preferred_hardware_device_description": "Platí len pre VAAPI a QSV. Nastavuje uzol dri, ktorý sa používa na hardvérové prekódovanie.", - "transcoding_preset_preset": "Prednastavenie (-preset)", + "transcoding_preset_preset": "Predvoľba (-preset)", "transcoding_preset_preset_description": "Rýchlosť kompresie. Pomalšie predvoľby vytvárajú menšie súbory a zvyšujú kvalitu, keď sa zameriavajú na určitý dátový tok. VP9 ignoruje rýchlosti vyššie ako „rýchlejšie“.", "transcoding_reference_frames": "Referenčné snímky", "transcoding_reference_frames_description": "Počet snímok, na ktoré sa má odkazovať pri kompresii daného snímku. Vyššie hodnoty zvyšujú účinnosť kompresie, ale spomaľujú kódovanie. Hodnota 0 sa nastavuje automaticky.", "transcoding_required_description": "Iba videá, ktoré nie sú v prijatom formáte", "transcoding_settings": "Nastavenia prekódovania videa", - "transcoding_settings_description": "Spravujte, ktoré videá sa majú prekódovať a ako ich spracovať", + "transcoding_settings_description": "Spravovať, ktoré videá sa majú prekódovať a ako sa majú spracovať", "transcoding_target_resolution": "Cieľové rozlíšenie", "transcoding_target_resolution_description": "Vyššie rozlíšenia môžu zachovať viac detailov, ale ich kódovanie trvá dlhšie, majú väčšiu veľkosť súborov a môžu znížiť odozvu aplikácie.", "transcoding_temporal_aq": "Časové AQ", @@ -349,13 +349,13 @@ "trash_settings_description": "Spravovať nastavenia koša", "user_cleanup_job": "Premazanie používateľov", "user_delete_delay": "Konto {user} a jeho médiá budú podľa plánu natrvalo vymazané za {delay, plural, one {# deň} few {# dni} other {# dní}}.", - "user_delete_delay_settings": "Odstrániť oneskorenie", - "user_delete_delay_settings_description": "Počet dní po odstránení na trvalé vymazanie účtu a médií používateľa. Úloha odstraňovania používateľov sa spúšťa o polnoci, aby sa skontrolovali používatelia, ktorí sú pripravení na odstránenie. Zmeny tohto nastavenia sa vyhodnotia pri ďalšom spustení.", + "user_delete_delay_settings": "Oneskorenie vymazania", + "user_delete_delay_settings_description": "Počet dní, po ktorých sa po odstránení používateľa natrvalo odstráni jeho účet a položky. Úloha odstraňovania používateľov sa spúšťa o polnoci, aby sa skontrolovali používatelia, ktorí sú pripravení na odstránenie. Zmeny tohto nastavenia sa vyhodnotia pri ďalšom spustení.", "user_delete_immediately": "Konto a médiá používateľa {user} budú zaradené do poradia na trvalé vymazanie okamžite.", "user_delete_immediately_checkbox": "Používateľ a médiá budú zaradení do frontu na okamžité vymazanie", "user_details": "Podrobnosti o používateľovi", - "user_management": "Správa používateľov", - "user_password_has_been_reset": "Heslo používateľa bolo resetované:", + "user_management": "Spravovanie používateľov", + "user_password_has_been_reset": "Heslo používateľa bolo obnovené:", "user_password_reset_description": "Poskytnite používateľovi dočasné heslo a informujte ho, že si ho bude musieť zmeniť pri ďalšom prihlásení.", "user_restore_description": "{user} bude účet obnovený.", "user_restore_scheduled_removal": "Obnoviť používateľa - plánované odstránenie na {date, date, long}", @@ -393,7 +393,7 @@ "age_year_months": "Vek 1 rok, {months, plural, one {# month} other {# months}}", "age_years": "{years, plural, other {Vek #}}", "album_added": "Album bol pridaný", - "album_added_notification_setting_description": "Obdržať upozornenie emailom, keď ste pridaní do zdieľaného albumu", + "album_added_notification_setting_description": "Obdržať upozornenie emailom, keď vás pridajú do zdieľaného albumu", "album_cover_updated": "Obal albumu aktualizovaný", "album_delete_confirmation": "Ste si istý, že chcete odstrániť album {album}?", "album_delete_confirmation_description": "Ak je tento album zdieľaný, ostatní používatelia k nemu už nebudú mať prístup.", @@ -573,6 +573,8 @@ "backup_options_page_title": "Možnosti zálohovania", "backup_setting_subtitle": "Spravovať nastavenia odosielania na pozadí a v popredí", "backward": "Dozadu", + "beta_sync": "Stav synchronizácie verzie Beta", + "beta_sync_subtitle": "Spravovať nový systém synchronizácie", "biometric_auth_enabled": "Biometrické overovanie je povolené", "biometric_locked_out": "Ste vymknutí z biometrického overovania", "biometric_no_options": "Nie sú k dispozícii žiadne biometrické možnosti", @@ -590,7 +592,7 @@ "cache_settings_clear_cache_button": "Vymazať vyrovnávaciu pamäť", "cache_settings_clear_cache_button_title": "Vymaže vyrovnávaciu pamäť aplikácie. To výrazne ovplyvní výkon aplikácie, kým sa vyrovnávacia pamäť neobnoví.", "cache_settings_duplicated_assets_clear_button": "VYČISTIŤ", - "cache_settings_duplicated_assets_subtitle": "Fotky a videá ktoré sú na čiernej listine zvolené aplikáciou", + "cache_settings_duplicated_assets_subtitle": "Fotografie a videá, ktoré aplikácia ignoruje podľa zoznamu", "cache_settings_duplicated_assets_title": "Duplicitné položky ({count})", "cache_settings_statistics_album": "Knižnica náhľadov", "cache_settings_statistics_full": "Kompletné fotografie", @@ -628,7 +630,7 @@ "change_password_form_password_mismatch": "Heslá sa nezhodujú", "change_password_form_reenter_new_password": "Znova zadajte nové heslo", "change_pin_code": "Zmeniť PIN kód", - "change_your_password": "Zmeňte si heslo", + "change_your_password": "Zmeniť heslo", "changed_visibility_successfully": "Viditeľnosť bola úspešne zmenená", "check_corrupt_asset_backup": "Skontrolovať, či nie sú poškodené zálohy položiek", "check_corrupt_asset_backup_button": "Vykonať kontrolu", @@ -723,7 +725,7 @@ "custom_locale_description": "Formátovanie dátumov a čísel podľa jazyka a regiónu", "daily_title_text_date": "EEEE, d. MMMM", "daily_title_text_date_year": "EEEE, d. MMMM y", - "dark": "Tmavý", + "dark": "Tmavá", "dark_theme": "Prepnúť tmavú tému", "date_after": "Dátum po", "date_and_time": "Dátum a Čas", @@ -949,7 +951,7 @@ "unable_to_remove_library": "Nie je možné odstrániť knižnicu", "unable_to_remove_partner": "Nie je možné odstrániť partnera", "unable_to_remove_reaction": "Nie je možné odstrániť reakciu", - "unable_to_reset_password": "Nie je možné resetovať heslo", + "unable_to_reset_password": "Nie je možné obnoviť heslo", "unable_to_reset_pin_code": "Nie je možné obnoviť PIN kód", "unable_to_resolve_duplicate": "Nie je možné vyriešiť duplikát", "unable_to_restore_assets": "Nie je možné obnoviť položky", @@ -987,7 +989,7 @@ "exif_bottom_sheet_person_age_months": "Vek {months} mesiacov", "exif_bottom_sheet_person_age_year_months": "Vek 1 rok, {months} mesiacov", "exif_bottom_sheet_person_age_years": "Vek {years}", - "exit_slideshow": "Opustiť Slideshow", + "exit_slideshow": "Opustiť prezentáciu", "expand_all": "Rozbaliť všetko", "experimental_settings_new_asset_list_subtitle": "Prebiehajúca práca", "experimental_settings_new_asset_list_title": "Povolenie experimentálnej mriežky fotografií", @@ -1051,6 +1053,9 @@ "haptic_feedback_switch": "Povoliť hmatovú odozvu", "haptic_feedback_title": "Hmatová odozva", "has_quota": "Má kvótu", + "hash_asset": "Hashovať položku", + "hashed_assets": "Hashované položky", + "hashing": "Hashovanie", "header_settings_add_header_tip": "Pridať hlavičku", "header_settings_field_validator_msg": "Hodnota nemôže byť prázdna", "header_settings_header_name_input": "Názov hlavičky", @@ -1083,6 +1088,7 @@ "host": "Hostiteľ", "hour": "Hodina", "id": "ID", + "idle": "Nečinné", "ignore_icloud_photos": "Ignorovať fotky v službe iCloud", "ignore_icloud_photos_description": "Fotografie uložené v službe iCloud sa nebudú odosielať na server Immich", "image": "Obrázok", @@ -1139,7 +1145,7 @@ "language_no_results_subtitle": "Skúste upraviť hľadaný výraz", "language_no_results_title": "Neboli nájdené žiadne jazyky", "language_search_hint": "Vyhľadať jazyky...", - "language_setting_description": "Vyberte preferovaný jazyk", + "language_setting_description": "Vyberte požadovaný jazyk", "last_seen": "Naposledy videné", "latest_version": "Najnovšia verzia", "latitude": "Zemepisná šírka", @@ -1156,7 +1162,7 @@ "library_page_sort_last_modified": "Naposledy upravené", "library_page_sort_title": "Podľa názvu albumu", "licenses": "Licencie", - "light": "Svetlý", + "light": "Svetlá", "like_deleted": "Like odstránený", "link_motion_video": "Pripojiť pohyblivé video", "link_options": "Možnosti odkazu", @@ -1165,7 +1171,9 @@ "list": "Zoznam", "loading": "Načítavanie", "loading_search_results_failed": "Načítanie výsledkov hľadania sa nepodarilo", + "local": "Lokálne", "local_asset_cast_failed": "Nie je možné preniesť médium, ktoré nie je nahrané na serveri", + "local_assets": "Lokálne položky", "local_network": "Miestna sieť", "local_network_sheet_info": "Pri použití zadanej siete Wi-Fi sa aplikácia pripojí k serveru prostredníctvom tejto URL adresy", "location_permission": "Povolenie na určenie polohy", @@ -1277,7 +1285,7 @@ "move_off_locked_folder": "Presunúť zo zamknutého priečinka", "move_to_lock_folder_action_prompt": "{count} pridaných do zamknutého priečinka", "move_to_locked_folder": "Presunúť do zamknutého priečinka", - "move_to_locked_folder_confirmation": "Tieto fotografie a videá budú odstránené zo všetkých albumov a bude ich možné zobraziť len v zamknutom priečinku", + "move_to_locked_folder_confirmation": "Tieto fotografie a videá budú odobrané zo všetkých albumov a bude ich možné zobraziť len v zamknutom priečinku", "moved_to_archive": "{count, plural, one {Presunutá # položka} few {Presunuté # položky} other {Presunutých # položiek}} do archívu", "moved_to_library": "{count, plural, one {Presunutá # položka} few {Presunuté # položky} other {Presunutých # položiek}} do knižnice", "moved_to_trash": "Presunuté do koša", @@ -1322,6 +1330,7 @@ "no_results": "Žiadne výsledky", "no_results_description": "Skúste synonymum alebo všeobecnejší výraz", "no_shared_albums_message": "Vytvorí album na zdieľanie fotiek a videí s ľuďmi vo vašej sieti", + "no_uploads_in_progress": "Žiadne prebiehajúce nahrávanie", "not_in_any_album": "Nie je v žiadnom albume", "not_selected": "Nevybrané", "note_apply_storage_label_to_previously_uploaded assets": "Poznámka: Ak chcete použiť Štítok úložiska na predtým nahrané médiá, spustite príkaz", @@ -1343,7 +1352,7 @@ "onboarding": "Na palube", "onboarding_locale_description": "Vyberte požadovaný jazyk. Neskôr ho môžete zmeniť v nastaveniach.", "onboarding_privacy_description": "Nasledujúce (voliteľné) funkcie závisia na externých službách a kedykoľvek ich môžete vypnúť nastaveniach.", - "onboarding_server_welcome_description": "Nastavme vašu inštanciu pomocou bežných nastavení.", + "onboarding_server_welcome_description": "Poďme si nastaviť vašu inštanciu s niekoľkými bežnými nastaveniami.", "onboarding_theme_description": "Vyberte farbu témy pre váš server. Môžete to aj neskôr zmeniť vo vašich nastaveniach.", "onboarding_user_welcome_description": "Začnime!", "onboarding_welcome_user": "Vitaj, {user}", @@ -1359,6 +1368,7 @@ "original": "originál", "other": "Ostatné", "other_devices": "Ďalšie zariadenia", + "other_entities": "Ostatné subjekty", "other_variables": "Ostatné premenné", "owned": "Vlastnené", "owner": "Vlastník", @@ -1434,9 +1444,9 @@ "play_or_pause_video": "Pustí alebo pozastaví video", "please_auth_to_access": "Prosím, potvrďte overenie pre prístup", "port": "Port", - "preferences_settings_subtitle": "Spravujte predvoľby aplikácie", + "preferences_settings_subtitle": "Spravovať predvoľby aplikácie", "preferences_settings_title": "Predvoľby", - "preset": "Prednastavenie", + "preset": "Predvoľba", "preview": "Náhľad", "previous": "Predošlé", "previous_memory": "Predošlá spomienka", @@ -1508,7 +1518,7 @@ "recently_added_page_title": "Nedávno pridané", "recently_taken": "Nedávno nasnímané", "recently_taken_page_title": "Nedávno zhotovené", - "refresh": "Obnoviť", + "refresh": "Aktualizovať", "refresh_encoded_videos": "Obnoviť enkódované videá", "refresh_faces": "Obnoviť tváre", "refresh_metadata": "Obnoviť metadáta", @@ -1519,6 +1529,8 @@ "refreshing_faces": "Obnovovanie tvárí", "refreshing_metadata": "Obnovovanie metadát", "regenerating_thumbnails": "Pregenerovanie náhľadov", + "remote": "Vzdialené", + "remote_assets": "Vzdialené položky", "remove": "Odstrániť", "remove_assets_album_confirmation": "Naozaj chcete odstrániť {count, plural, one {# položku} few {# položky} other {# položiek}} z albumu?", "remove_assets_shared_link_confirmation": "Naozaj chcete odstrániť {count, plural, one {# položku} few {# položky} other {# položiek}} z tohoto zdieľaného odkazu?", @@ -1529,8 +1541,8 @@ "remove_from_album_action_prompt": "{count} odstránené z albumu", "remove_from_favorites": "Odstrániť z obľúbených", "remove_from_lock_folder_action_prompt": "{count} odobrané zo zamknutého priečinka", - "remove_from_locked_folder": "Odstrániť zo zamknutého priečinka", - "remove_from_locked_folder_confirmation": "Ste si istí, že chcete tieto fotografie a videá presunúť zo zamknutého priečinka? Budú viditeľné vo vašej knižnici.", + "remove_from_locked_folder": "Odobrať zo zamknutého priečinka", + "remove_from_locked_folder_confirmation": "Ste si istí, že chcete tieto fotografie a videá odobrať zo zamknutého priečinka? Budú viditeľné vo vašej knižnici.", "remove_from_shared_link": "Odstrániť zo zdieľaného odkazu", "remove_memory": "Odstrániť spomienku", "remove_photo_from_memory": "Odstrániť fotografiu z tejto spomienky", @@ -1552,28 +1564,33 @@ "require_password": "Vyžadovať heslo", "require_user_to_change_password_on_first_login": "Vyžadovať zmenu hesla po prvom prihlásení", "rescan": "Opätovné vyhľadávanie", - "reset": "Resetovať", + "reset": "Obnoviť", "reset_password": "Obnoviť heslo", - "reset_people_visibility": "Resetovať viditeľnosť ľudí", + "reset_people_visibility": "Obnoviť viditeľnosť ľudí", "reset_pin_code": "Obnoviť PIN kód", - "reset_to_default": "Resetovať na predvolené", + "reset_sqlite": "Obnoviť SQLite databázu", + "reset_sqlite_confirmation": "Ste si istí, že chcete obnoviť SQLite databázu? Na opätovnú synchronizáciu údajov sa budete musieť odhlásiť a znova prihlásiť", + "reset_sqlite_success": "Úspešné obnovenie databázy SQLite", + "reset_to_default": "Obnoviť na predvolené", "resolve_duplicates": "Vyriešiť duplicity", "resolved_all_duplicates": "Vyriešené všetky duplicity", "restore": "Navrátiť", "restore_all": "Navrátit všetko", + "restore_trash_action_prompt": "{count} obnovených z koša", "restore_user": "Navrátiť používateľa", "restored_asset": "Navrátené položky", "resume": "Pokračovať", "retry_upload": "Zopakovať nahrávanie", - "review_duplicates": "Prezrieť duplikáty", + "review_duplicates": "Preskúmať duplikáty", "role": "Rola", "role_editor": "Editor", "role_viewer": "Divák", + "running": "Spustené", "save": "Uložiť", "save_to_gallery": "Uložiť do galérie", "saved_api_key": "Uložený API Kľúč", "saved_profile": "Uložený profil", - "saved_settings": "Uložené nastavenia", + "saved_settings": "Nastavenia boli uložené", "say_something": "Napíšte niečo", "scaffold_body_error_occurred": "Vyskytla sa chyba", "scan_all_libraries": "Preskenovať všetky knižnice", @@ -1585,7 +1602,7 @@ "search_by_context": "Hľadať s kontextom", "search_by_description": "Vyhľadávanie podľa popisu", "search_by_description_example": "Pešia turistika v Sape", - "search_by_filename": "Hľadať s názvom alebo príponou súboru", + "search_by_filename": "Hľadať podľa názvu alebo prípony súboru", "search_by_filename_example": "napr. IMG_1234.JPG alebo PNG", "search_camera_make": "Hľadať značku fotoaparátu...", "search_camera_model": "Hľadať model fotoaparátu...", @@ -1809,7 +1826,7 @@ "stacked_assets_count": "{count, plural, one {Zoskupená # položka} few {Zoskupené # položky} other {Zoskupených # položiek}}", "stacktrace": "Výpis zásobníku", "start": "Štart", - "start_date": "Začiatočný dátum", + "start_date": "Počiatočný dátum", "state": "Štát", "status": "Stav", "stop_casting": "Zastaviť prenos", @@ -1822,6 +1839,7 @@ "storage_quota": "Úložný limit", "storage_usage": "Využitých {used} z {available}", "submit": "Odoslať", + "success": "Úspech", "suggestions": "Návrhy", "sunrise_on_the_beach": "Východ slnka na pláži", "support": "Podpora", @@ -1831,9 +1849,11 @@ "sync": "Synchronizovať", "sync_albums": "Synchronizovať albumy", "sync_albums_manual_subtitle": "Synchronizujte všetky nahrané videá a fotografie s vybranými záložnými albumami", + "sync_local": "Synchronizovať lokálne", + "sync_remote": "Synchronizovať vzdialené", "sync_upload_album_setting_subtitle": "Vytvárajte a nahrávajte svoje fotografie a videá do vybraných albumov na Immich", "tag": "Štítok", - "tag_assets": "Označiť položky", + "tag_assets": "Pridať štítky", "tag_created": "Vytvorený štítok: {tag}", "tag_feature_description": "Prehliadanie fotiek a videá zoskupených podľa tematických štítkov", "tag_not_found_question": "Neviete nájsť štítok? Vytvorte nový štítok.", @@ -1841,6 +1861,7 @@ "tag_updated": "Upravený štítok: {tag}", "tagged_assets": "Štítok priradený {count, plural, one {# položke} other {# položkám}}", "tags": "Štítky", + "tap_to_run_job": "Ťuknutím na položku spustíte úlohu", "template": "Šablóna", "theme": "Téma", "theme_selection": "Výber témy", @@ -1863,7 +1884,7 @@ "time_based_memories": "Časové spomienky", "timeline": "Časová os", "timezone": "Časové pásmo", - "to_archive": "Archív", + "to_archive": "Archivovať", "to_change_password": "Zmeniť heslo", "to_favorite": "Obľúbiť", "to_login": "Prihlásiť", @@ -1898,7 +1919,7 @@ "unfavorite_action_prompt": "{count} odstránené z Obľúbených", "unhide_person": "Odkryť osobu", "unknown": "Neznáme", - "unknown_country": "Neznámy štát", + "unknown_country": "Neznáma krajina", "unknown_year": "Neznámy rok", "unlimited": "Neobmedzené", "unlink_motion_video": "Odpojiť pohyblivé video", @@ -1986,7 +2007,7 @@ "viewer_stack_use_as_main_asset": "Použiť ako hlavnú fotku", "viewer_unstack": "Odskupiť", "visibility_changed": "Viditeľnosť zmenená pre {count, plural, one {# osobu} few {# osoby} other {# osôb}}", - "waiting": "Čaká", + "waiting": "Čakajúce", "warning": "Varovanie", "week": "Týždeň", "welcome": "Vitajte", @@ -1996,7 +2017,7 @@ "year": "Rok", "years_ago": "pred {years, plural, one {# rokom} other {# rokmi}}", "yes": "Áno", - "you_dont_have_any_shared_links": "Nemáte žiadne zdielané linky", + "you_dont_have_any_shared_links": "Nemáte žiadne zdielané odkazy", "your_wifi_name": "Váš názov siete Wi-Fi", "zoom_image": "Priblížiť obrázok" } diff --git a/i18n/sl.json b/i18n/sl.json index c4409c717e..e08a9dd506 100644 --- a/i18n/sl.json +++ b/i18n/sl.json @@ -573,6 +573,8 @@ "backup_options_page_title": "Možnosti varnostne kopije", "backup_setting_subtitle": "Upravljaj nastavitve nalaganja v ozadju in ospredju", "backward": "Nazaj", + "beta_sync": "Stanje sinhronizacije beta različice", + "beta_sync_subtitle": "Upravljanje novega sistema sinhronizacije", "biometric_auth_enabled": "Biometrična avtentikacija omogočena", "biometric_locked_out": "Biometrična avtentikacija vam je onemogočena", "biometric_no_options": "Biometrične možnosti niso na voljo", @@ -590,7 +592,7 @@ "cache_settings_clear_cache_button": "Počisti predpomnilnik", "cache_settings_clear_cache_button_title": "Počisti predpomnilnik aplikacije. To bo znatno vplivalo na delovanje aplikacije, dokler se predpomnilnik ne obnovi.", "cache_settings_duplicated_assets_clear_button": "POČISTI", - "cache_settings_duplicated_assets_subtitle": "Fotografije in videoposnetki, ki jih je aplikacija uvrstila na črni seznam", + "cache_settings_duplicated_assets_subtitle": "Fotografije in videoposnetki, ki so prezrti s strani aplikacije", "cache_settings_duplicated_assets_title": "Podvojena sredstva ({count})", "cache_settings_statistics_album": "Sličice knjižnice", "cache_settings_statistics_full": "Izvirne slike", @@ -1051,6 +1053,9 @@ "haptic_feedback_switch": "Uporabi haptičen odziv", "haptic_feedback_title": "Haptičen odziv", "has_quota": "Ima kvoto", + "hash_asset": "Zgoščeno sredstvo", + "hashed_assets": "Zgoščena sredstva", + "hashing": "Zgoščevanje", "header_settings_add_header_tip": "Dodaj glavo", "header_settings_field_validator_msg": "Vrednost ne sme biti prazna", "header_settings_header_name_input": "Ime glave", @@ -1083,6 +1088,7 @@ "host": "Gostitelj", "hour": "Ura", "id": "ID", + "idle": "Nedejavnost", "ignore_icloud_photos": "Ignoriraj fotografije iCloud", "ignore_icloud_photos_description": "Fotografije, shranjene v iCloud, ne bodo naložene na strežnik Immich", "image": "Slika", @@ -1165,7 +1171,9 @@ "list": "Seznam", "loading": "Nalaganje", "loading_search_results_failed": "Nalaganje rezultatov iskanja ni uspelo", + "local": "Lokalno", "local_asset_cast_failed": "Sredstva, ki niso naložena na strežnik, ni mogoče predvajati", + "local_assets": "Lokalna sredstva", "local_network": "Lokalno omrežje", "local_network_sheet_info": "Aplikacija se bo povezala s strežnikom prek tega URL-ja, ko bo uporabljala navedeno omrežje Wi-Fi", "location_permission": "Dovoljenje za lokacijo", @@ -1322,6 +1330,7 @@ "no_results": "Brez rezultatov", "no_results_description": "Poskusite s sinonimom ali bolj splošno ključno besedo", "no_shared_albums_message": "Ustvarite album za skupno rabo fotografij in videoposnetkov z osebami v vašem omrežju", + "no_uploads_in_progress": "Ni nalaganj v teku", "not_in_any_album": "Ni v nobenem albumu", "not_selected": "Ni izbrano", "note_apply_storage_label_to_previously_uploaded assets": "Opomba: Če želite oznako za shranjevanje uporabiti za predhodno naložena sredstva, zaženite", @@ -1359,6 +1368,7 @@ "original": "izvirnik", "other": "drugo", "other_devices": "Druge naprave", + "other_entities": "Drugi subjekti", "other_variables": "Druge spremenljivke", "owned": "V lasti", "owner": "Lastnik", @@ -1390,7 +1400,7 @@ "pause": "Premor", "pause_memories": "Zaustavi spomine", "paused": "Zaustavljeno", - "pending": "V teku", + "pending": "Čakanje", "people": "Osebe", "people_edits_count": "{count, plural, one {Urejena # oseba} two {Urejeni # osebi} few {Urejene # osebe} other {Urejenih # oseb}}", "people_feature_description": "Brskanje po fotografijah in videoposnetkih, razvrščenih po osebah", @@ -1519,6 +1529,8 @@ "refreshing_faces": "Osveževanje obrazev", "refreshing_metadata": "Osveževanje metapodatkov", "regenerating_thumbnails": "Obnavljanje sličic", + "remote": "Oddaljeno", + "remote_assets": "Oddaljena sredstva", "remove": "Odstrani", "remove_assets_album_confirmation": "Ali ste prepričani, da želite odstraniti {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} iz albuma?", "remove_assets_shared_link_confirmation": "Ali ste prepričani, da želite odstraniti {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} iz te skupne povezave?", @@ -1556,11 +1568,15 @@ "reset_password": "Ponastavi geslo", "reset_people_visibility": "Ponastavi vidnost ljudi", "reset_pin_code": "Ponastavi PIN kodo", + "reset_sqlite": "Ponastavi bazo podatkov SQLite", + "reset_sqlite_confirmation": "Ali ste prepričani, da želite ponastaviti bazo podatkov SQLite? Za ponovno sinhronizacijo podatkov se boste morali odjaviti in znova prijaviti", + "reset_sqlite_success": "Uspešno ponastavljena baza podatkov SQLite", "reset_to_default": "Ponastavi na privzeto", "resolve_duplicates": "Razreši dvojnike", "resolved_all_duplicates": "Razrešeni vsi dvojniki", "restore": "Obnovi", "restore_all": "Obnovi vse", + "restore_trash_action_prompt": "{count} obnovljenih iz koša", "restore_user": "Obnovi uporabnika", "restored_asset": "Obnovljeno sredstvo", "resume": "Nadaljuj", @@ -1569,6 +1585,7 @@ "role": "Vloga", "role_editor": "Urejevalec", "role_viewer": "Gledalec", + "running": "V teku", "save": "Shrani", "save_to_gallery": "Shrani v galerijo", "saved_api_key": "Shranjen API ključ", @@ -1822,6 +1839,7 @@ "storage_quota": "Kvota shranjevanja", "storage_usage": "uporabljeno {used} od {available}", "submit": "Predloži", + "success": "Uspeh", "suggestions": "Predlogi", "sunrise_on_the_beach": "Sončni vzhod na plaži", "support": "Podpora", @@ -1831,6 +1849,8 @@ "sync": "Sinhronizacija", "sync_albums": "Sinhronizacija albumov", "sync_albums_manual_subtitle": "Sinhronizirajte vse naložene videoposnetke in fotografije v izbrane varnostne albume", + "sync_local": "Sinhroniziraj lokalno", + "sync_remote": "Sinhroniziraj oddaljeno", "sync_upload_album_setting_subtitle": "Ustvarite in naložite svoje fotografije in videoposnetke v izbrane albume na Immich", "tag": "Oznaka", "tag_assets": "Označi sredstva", @@ -1841,6 +1861,7 @@ "tag_updated": "Posodobljena oznaka: {tag}", "tagged_assets": "Označeno {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", "tags": "Oznake", + "tap_to_run_job": "Dotaknite se za zagon opravila", "template": "Predloga", "theme": "Tema", "theme_selection": "Izbira teme", diff --git a/i18n/zh_SIMPLIFIED.json b/i18n/zh_SIMPLIFIED.json index 28d6a5c0d5..bcbdcc5d6b 100644 --- a/i18n/zh_SIMPLIFIED.json +++ b/i18n/zh_SIMPLIFIED.json @@ -573,6 +573,8 @@ "backup_options_page_title": "备份选项", "backup_setting_subtitle": "管理后台和前台上传设置", "backward": "后退", + "beta_sync": "测试版同步状态", + "beta_sync_subtitle": "管理新的同步系统", "biometric_auth_enabled": "生物识别身份验证已启用", "biometric_locked_out": "您被锁定在生物识别身份验证之外", "biometric_no_options": "没有可用的生物识别选项", @@ -590,7 +592,7 @@ "cache_settings_clear_cache_button": "清除缓存", "cache_settings_clear_cache_button_title": "清除应用缓存。在重新生成缓存之前,将显著影响应用的性能。", "cache_settings_duplicated_assets_clear_button": "清除", - "cache_settings_duplicated_assets_subtitle": "已加入黑名单的照片和视频", + "cache_settings_duplicated_assets_subtitle": "应用程序忽略的照片和视频", "cache_settings_duplicated_assets_title": "重复项目({count})", "cache_settings_statistics_album": "图库缩略图", "cache_settings_statistics_full": "完整图像", @@ -1051,6 +1053,9 @@ "haptic_feedback_switch": "启用振动反馈", "haptic_feedback_title": "振动反馈", "has_quota": "配额大小", + "hash_asset": "哈希项目", + "hashed_assets": "已哈希的项目", + "hashing": "正在哈希", "header_settings_add_header_tip": "添加标头", "header_settings_field_validator_msg": "设置不可为空", "header_settings_header_name_input": "标头名称", @@ -1083,6 +1088,7 @@ "host": "服务器", "hour": "时", "id": "ID", + "idle": "空闲", "ignore_icloud_photos": "忽略 iCloud 照片", "ignore_icloud_photos_description": "存储在 iCloud 中的照片不会上传至 Immich 服务器", "image": "图片", @@ -1165,7 +1171,9 @@ "list": "列表", "loading": "加载中", "loading_search_results_failed": "加载搜索结果失败", + "local": "本地", "local_asset_cast_failed": "无法投放未上传至服务器的项目", + "local_assets": "本地项目", "local_network": "本地网络", "local_network_sheet_info": "当使用指定的 Wi-Fi 网络时,应用程序将通过此 URL 访问服务器", "location_permission": "定位权限", @@ -1322,6 +1330,7 @@ "no_results": "无结果", "no_results_description": "尝试使用同义词或更通用的关键词", "no_shared_albums_message": "创建相册以共享照片和视频", + "no_uploads_in_progress": "没有正在进行的上传", "not_in_any_album": "不在任何相册中", "not_selected": "未选择", "note_apply_storage_label_to_previously_uploaded assets": "提示:要将存储标签应用于之前上传的项目,需要运行", @@ -1359,6 +1368,7 @@ "original": "原图", "other": "其它", "other_devices": "其它设备", + "other_entities": "其他实体", "other_variables": "其它变量", "owned": "我的", "owner": "所有者", @@ -1519,6 +1529,8 @@ "refreshing_faces": "正在面部重新识别", "refreshing_metadata": "正在刷新元数据", "regenerating_thumbnails": "正在重新生成缩略图", + "remote": "远程", + "remote_assets": "远程项目", "remove": "移除", "remove_assets_album_confirmation": "确定要从图库中移除{count, plural, one {#个项目} other {#个项目}}?", "remove_assets_shared_link_confirmation": "确定要从共享链接中移除{count, plural, one {#个项目} other {#个项目}}?", @@ -1556,11 +1568,15 @@ "reset_password": "重置密码", "reset_people_visibility": "重置人物识别", "reset_pin_code": "重置PIN码", + "reset_sqlite": "重置 SQLite 数据库", + "reset_sqlite_confirmation": "您确定要重置 SQLite 数据库吗?您需要注销并重新登录才能重新同步数据", + "reset_sqlite_success": "已成功重置 SQLite 数据库", "reset_to_default": "恢复默认值", "resolve_duplicates": "处理重复项", "resolved_all_duplicates": "处理所有重复项", "restore": "恢复", "restore_all": "恢复全部", + "restore_trash_action_prompt": "从回收站中恢复了 {count} 项", "restore_user": "恢复用户", "restored_asset": "已恢复项目", "resume": "继续", @@ -1569,6 +1585,7 @@ "role": "选择用户权限", "role_editor": "可编辑", "role_viewer": "仅查看", + "running": "正在运行", "save": "保存", "save_to_gallery": "保存到图库", "saved_api_key": "已保存的 API 密钥", @@ -1822,6 +1839,7 @@ "storage_quota": "存储配额", "storage_usage": "已用:{used}/{available}", "submit": "提交", + "success": "成功", "suggestions": "建议", "sunrise_on_the_beach": "海滩上的日出", "support": "支持", @@ -1831,6 +1849,8 @@ "sync": "同步", "sync_albums": "同步相册", "sync_albums_manual_subtitle": "将所有上传的视频和照片同步到选定的备份相册", + "sync_local": "同步本地", + "sync_remote": "同步远程", "sync_upload_album_setting_subtitle": "创建照片和视频并上传到 Immich 上的选定相册中", "tag": "标签", "tag_assets": "标记项目", @@ -1841,6 +1861,7 @@ "tag_updated": "已更新标签:{tag}", "tagged_assets": "{count, plural, one {# 个项目} other {# 个项目}}被加上标签", "tags": "标签", + "tap_to_run_job": "点击运行作业", "template": "模版", "theme": "主题", "theme_selection": "主题选项", From ab597155fa66664b582fe83bc558ecd1e78123a5 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 23 Jul 2025 09:59:21 -0400 Subject: [PATCH 025/748] fix: immich-dev live reload (#20104) --- server/bin/immich-dev | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/bin/immich-dev b/server/bin/immich-dev index 533c10ef9d..e861c0ee06 100755 --- a/server/bin/immich-dev +++ b/server/bin/immich-dev @@ -6,4 +6,4 @@ if [ "$IMMICH_ENV" != "development" ]; then fi cd /usr/src/app/server || exit 1 -npm exec nest start --debug "0.0.0.0:9230" --watch -- "$@" +npm exec nest -- start --debug "0.0.0.0:9230" --watch -- "$@" From 92384c28de047c1b697c3f48343e137e7756a078 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 23 Jul 2025 09:59:33 -0400 Subject: [PATCH 026/748] feat: sync auth user (#20067) --- e2e/test-assets | 2 +- mobile/openapi/README.md | 1 + mobile/openapi/lib/api.dart | 1 + mobile/openapi/lib/api_client.dart | 2 + .../openapi/lib/model/sync_auth_user_v1.dart | 215 ++++++++++++++++++ .../openapi/lib/model/sync_entity_type.dart | 3 + .../openapi/lib/model/sync_request_type.dart | 3 + mobile/openapi/lib/model/sync_user_v1.dart | 14 +- mobile/test/fixtures/sync_stream.stub.dart | 2 + open-api/immich-openapi-specs.json | 81 +++++++ open-api/typescript-sdk/src/fetch-client.ts | 2 + server/src/database.ts | 1 + server/src/dtos/sync.dto.ts | 18 ++ server/src/enum.ts | 3 + server/src/queries/sync.repository.sql | 24 ++ server/src/repositories/sync.repository.ts | 29 ++- server/src/services/sync.service.ts | 10 + server/test/medium.factory.ts | 7 + .../medium/specs/sync/sync-auth-user.spec.ts | 87 +++++++ .../test/medium/specs/sync/sync-user.spec.ts | 43 +--- 20 files changed, 507 insertions(+), 41 deletions(-) create mode 100644 mobile/openapi/lib/model/sync_auth_user_v1.dart create mode 100644 server/test/medium/specs/sync/sync-auth-user.spec.ts diff --git a/e2e/test-assets b/e2e/test-assets index 18736fc27a..37f60ea537 160000 --- a/e2e/test-assets +++ b/e2e/test-assets @@ -1 +1 @@ -Subproject commit 18736fc27a80c99c68e856cdb4f842bc81ed3445 +Subproject commit 37f60ea537c0228f5f92e4f42dc42f0bb39a6d7f diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index b20a0694c5..2d764aa153 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -479,6 +479,7 @@ Class | Method | HTTP request | Description - [SyncAssetFaceDeleteV1](doc//SyncAssetFaceDeleteV1.md) - [SyncAssetFaceV1](doc//SyncAssetFaceV1.md) - [SyncAssetV1](doc//SyncAssetV1.md) + - [SyncAuthUserV1](doc//SyncAuthUserV1.md) - [SyncEntityType](doc//SyncEntityType.md) - [SyncMemoryAssetDeleteV1](doc//SyncMemoryAssetDeleteV1.md) - [SyncMemoryAssetV1](doc//SyncMemoryAssetV1.md) diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart index 545955a184..8b8acc0042 100644 --- a/mobile/openapi/lib/api.dart +++ b/mobile/openapi/lib/api.dart @@ -260,6 +260,7 @@ part 'model/sync_asset_exif_v1.dart'; part 'model/sync_asset_face_delete_v1.dart'; part 'model/sync_asset_face_v1.dart'; part 'model/sync_asset_v1.dart'; +part 'model/sync_auth_user_v1.dart'; part 'model/sync_entity_type.dart'; part 'model/sync_memory_asset_delete_v1.dart'; part 'model/sync_memory_asset_v1.dart'; diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart index 55d6f4108b..d9cae66dd3 100644 --- a/mobile/openapi/lib/api_client.dart +++ b/mobile/openapi/lib/api_client.dart @@ -576,6 +576,8 @@ class ApiClient { return SyncAssetFaceV1.fromJson(value); case 'SyncAssetV1': return SyncAssetV1.fromJson(value); + case 'SyncAuthUserV1': + return SyncAuthUserV1.fromJson(value); case 'SyncEntityType': return SyncEntityTypeTypeTransformer().decode(value); case 'SyncMemoryAssetDeleteV1': diff --git a/mobile/openapi/lib/model/sync_auth_user_v1.dart b/mobile/openapi/lib/model/sync_auth_user_v1.dart new file mode 100644 index 0000000000..1dab7f47e3 --- /dev/null +++ b/mobile/openapi/lib/model/sync_auth_user_v1.dart @@ -0,0 +1,215 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class SyncAuthUserV1 { + /// Returns a new [SyncAuthUserV1] instance. + SyncAuthUserV1({ + required this.avatarColor, + required this.deletedAt, + required this.email, + required this.hasProfileImage, + required this.id, + required this.isAdmin, + required this.name, + required this.oauthId, + required this.pinCode, + required this.profileChangedAt, + required this.quotaSizeInBytes, + required this.quotaUsageInBytes, + required this.storageLabel, + }); + + UserAvatarColor? avatarColor; + + DateTime? deletedAt; + + String email; + + bool hasProfileImage; + + String id; + + bool isAdmin; + + String name; + + String oauthId; + + String? pinCode; + + DateTime profileChangedAt; + + int? quotaSizeInBytes; + + int quotaUsageInBytes; + + String? storageLabel; + + @override + bool operator ==(Object other) => identical(this, other) || other is SyncAuthUserV1 && + other.avatarColor == avatarColor && + other.deletedAt == deletedAt && + other.email == email && + other.hasProfileImage == hasProfileImage && + other.id == id && + other.isAdmin == isAdmin && + other.name == name && + other.oauthId == oauthId && + other.pinCode == pinCode && + other.profileChangedAt == profileChangedAt && + other.quotaSizeInBytes == quotaSizeInBytes && + other.quotaUsageInBytes == quotaUsageInBytes && + other.storageLabel == storageLabel; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (avatarColor == null ? 0 : avatarColor!.hashCode) + + (deletedAt == null ? 0 : deletedAt!.hashCode) + + (email.hashCode) + + (hasProfileImage.hashCode) + + (id.hashCode) + + (isAdmin.hashCode) + + (name.hashCode) + + (oauthId.hashCode) + + (pinCode == null ? 0 : pinCode!.hashCode) + + (profileChangedAt.hashCode) + + (quotaSizeInBytes == null ? 0 : quotaSizeInBytes!.hashCode) + + (quotaUsageInBytes.hashCode) + + (storageLabel == null ? 0 : storageLabel!.hashCode); + + @override + String toString() => 'SyncAuthUserV1[avatarColor=$avatarColor, deletedAt=$deletedAt, email=$email, hasProfileImage=$hasProfileImage, id=$id, isAdmin=$isAdmin, name=$name, oauthId=$oauthId, pinCode=$pinCode, profileChangedAt=$profileChangedAt, quotaSizeInBytes=$quotaSizeInBytes, quotaUsageInBytes=$quotaUsageInBytes, storageLabel=$storageLabel]'; + + Map toJson() { + final json = {}; + if (this.avatarColor != null) { + json[r'avatarColor'] = this.avatarColor; + } else { + // json[r'avatarColor'] = null; + } + if (this.deletedAt != null) { + json[r'deletedAt'] = this.deletedAt!.toUtc().toIso8601String(); + } else { + // json[r'deletedAt'] = null; + } + json[r'email'] = this.email; + json[r'hasProfileImage'] = this.hasProfileImage; + json[r'id'] = this.id; + json[r'isAdmin'] = this.isAdmin; + json[r'name'] = this.name; + json[r'oauthId'] = this.oauthId; + if (this.pinCode != null) { + json[r'pinCode'] = this.pinCode; + } else { + // json[r'pinCode'] = null; + } + json[r'profileChangedAt'] = this.profileChangedAt.toUtc().toIso8601String(); + if (this.quotaSizeInBytes != null) { + json[r'quotaSizeInBytes'] = this.quotaSizeInBytes; + } else { + // json[r'quotaSizeInBytes'] = null; + } + json[r'quotaUsageInBytes'] = this.quotaUsageInBytes; + if (this.storageLabel != null) { + json[r'storageLabel'] = this.storageLabel; + } else { + // json[r'storageLabel'] = null; + } + return json; + } + + /// Returns a new [SyncAuthUserV1] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static SyncAuthUserV1? fromJson(dynamic value) { + upgradeDto(value, "SyncAuthUserV1"); + if (value is Map) { + final json = value.cast(); + + return SyncAuthUserV1( + avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']), + deletedAt: mapDateTime(json, r'deletedAt', r''), + email: mapValueOfType(json, r'email')!, + hasProfileImage: mapValueOfType(json, r'hasProfileImage')!, + id: mapValueOfType(json, r'id')!, + isAdmin: mapValueOfType(json, r'isAdmin')!, + name: mapValueOfType(json, r'name')!, + oauthId: mapValueOfType(json, r'oauthId')!, + pinCode: mapValueOfType(json, r'pinCode'), + profileChangedAt: mapDateTime(json, r'profileChangedAt', r'')!, + quotaSizeInBytes: mapValueOfType(json, r'quotaSizeInBytes'), + quotaUsageInBytes: mapValueOfType(json, r'quotaUsageInBytes')!, + storageLabel: mapValueOfType(json, r'storageLabel'), + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = SyncAuthUserV1.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = SyncAuthUserV1.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of SyncAuthUserV1-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = SyncAuthUserV1.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'avatarColor', + 'deletedAt', + 'email', + 'hasProfileImage', + 'id', + 'isAdmin', + 'name', + 'oauthId', + 'pinCode', + 'profileChangedAt', + 'quotaSizeInBytes', + 'quotaUsageInBytes', + 'storageLabel', + }; +} + diff --git a/mobile/openapi/lib/model/sync_entity_type.dart b/mobile/openapi/lib/model/sync_entity_type.dart index 65ed78105c..5368126923 100644 --- a/mobile/openapi/lib/model/sync_entity_type.dart +++ b/mobile/openapi/lib/model/sync_entity_type.dart @@ -23,6 +23,7 @@ class SyncEntityType { String toJson() => value; + static const authUserV1 = SyncEntityType._(r'AuthUserV1'); static const userV1 = SyncEntityType._(r'UserV1'); static const userDeleteV1 = SyncEntityType._(r'UserDeleteV1'); static const assetV1 = SyncEntityType._(r'AssetV1'); @@ -67,6 +68,7 @@ class SyncEntityType { /// List of all possible values in this [enum][SyncEntityType]. static const values = [ + authUserV1, userV1, userDeleteV1, assetV1, @@ -146,6 +148,7 @@ class SyncEntityTypeTypeTransformer { SyncEntityType? decode(dynamic data, {bool allowNull = true}) { if (data != null) { switch (data) { + case r'AuthUserV1': return SyncEntityType.authUserV1; case r'UserV1': return SyncEntityType.userV1; case r'UserDeleteV1': return SyncEntityType.userDeleteV1; case r'AssetV1': return SyncEntityType.assetV1; diff --git a/mobile/openapi/lib/model/sync_request_type.dart b/mobile/openapi/lib/model/sync_request_type.dart index 800b3f4485..8a1857366e 100644 --- a/mobile/openapi/lib/model/sync_request_type.dart +++ b/mobile/openapi/lib/model/sync_request_type.dart @@ -30,6 +30,7 @@ class SyncRequestType { static const albumAssetExifsV1 = SyncRequestType._(r'AlbumAssetExifsV1'); static const assetsV1 = SyncRequestType._(r'AssetsV1'); static const assetExifsV1 = SyncRequestType._(r'AssetExifsV1'); + static const authUsersV1 = SyncRequestType._(r'AuthUsersV1'); static const memoriesV1 = SyncRequestType._(r'MemoriesV1'); static const memoryToAssetsV1 = SyncRequestType._(r'MemoryToAssetsV1'); static const partnersV1 = SyncRequestType._(r'PartnersV1'); @@ -51,6 +52,7 @@ class SyncRequestType { albumAssetExifsV1, assetsV1, assetExifsV1, + authUsersV1, memoriesV1, memoryToAssetsV1, partnersV1, @@ -107,6 +109,7 @@ class SyncRequestTypeTypeTransformer { case r'AlbumAssetExifsV1': return SyncRequestType.albumAssetExifsV1; case r'AssetsV1': return SyncRequestType.assetsV1; case r'AssetExifsV1': return SyncRequestType.assetExifsV1; + case r'AuthUsersV1': return SyncRequestType.authUsersV1; case r'MemoriesV1': return SyncRequestType.memoriesV1; case r'MemoryToAssetsV1': return SyncRequestType.memoryToAssetsV1; case r'PartnersV1': return SyncRequestType.partnersV1; diff --git a/mobile/openapi/lib/model/sync_user_v1.dart b/mobile/openapi/lib/model/sync_user_v1.dart index b9b41bb723..c01ddcc9fc 100644 --- a/mobile/openapi/lib/model/sync_user_v1.dart +++ b/mobile/openapi/lib/model/sync_user_v1.dart @@ -13,12 +13,15 @@ part of openapi.api; class SyncUserV1 { /// Returns a new [SyncUserV1] instance. SyncUserV1({ + required this.avatarColor, required this.deletedAt, required this.email, required this.id, required this.name, }); + UserAvatarColor? avatarColor; + DateTime? deletedAt; String email; @@ -29,6 +32,7 @@ class SyncUserV1 { @override bool operator ==(Object other) => identical(this, other) || other is SyncUserV1 && + other.avatarColor == avatarColor && other.deletedAt == deletedAt && other.email == email && other.id == id && @@ -37,16 +41,22 @@ class SyncUserV1 { @override int get hashCode => // ignore: unnecessary_parenthesis + (avatarColor == null ? 0 : avatarColor!.hashCode) + (deletedAt == null ? 0 : deletedAt!.hashCode) + (email.hashCode) + (id.hashCode) + (name.hashCode); @override - String toString() => 'SyncUserV1[deletedAt=$deletedAt, email=$email, id=$id, name=$name]'; + String toString() => 'SyncUserV1[avatarColor=$avatarColor, deletedAt=$deletedAt, email=$email, id=$id, name=$name]'; Map toJson() { final json = {}; + if (this.avatarColor != null) { + json[r'avatarColor'] = this.avatarColor; + } else { + // json[r'avatarColor'] = null; + } if (this.deletedAt != null) { json[r'deletedAt'] = this.deletedAt!.toUtc().toIso8601String(); } else { @@ -67,6 +77,7 @@ class SyncUserV1 { final json = value.cast(); return SyncUserV1( + avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']), deletedAt: mapDateTime(json, r'deletedAt', r''), email: mapValueOfType(json, r'email')!, id: mapValueOfType(json, r'id')!, @@ -118,6 +129,7 @@ class SyncUserV1 { /// The list of required keys that must be present in a JSON. static const requiredKeys = { + 'avatarColor', 'deletedAt', 'email', 'id', diff --git a/mobile/test/fixtures/sync_stream.stub.dart b/mobile/test/fixtures/sync_stream.stub.dart index de2d58bc9d..6c47b9172e 100644 --- a/mobile/test/fixtures/sync_stream.stub.dart +++ b/mobile/test/fixtures/sync_stream.stub.dart @@ -9,6 +9,7 @@ abstract final class SyncStreamStub { email: "admin@admin", id: "1", name: "Admin", + avatarColor: null, ), ack: "1", ); @@ -19,6 +20,7 @@ abstract final class SyncStreamStub { email: "user@user", id: "5", name: "User", + avatarColor: null, ), ack: "5", ); diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index cd61f3e004..270a560fb0 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -13978,8 +13978,79 @@ ], "type": "object" }, + "SyncAuthUserV1": { + "properties": { + "avatarColor": { + "allOf": [ + { + "$ref": "#/components/schemas/UserAvatarColor" + } + ], + "nullable": true + }, + "deletedAt": { + "format": "date-time", + "nullable": true, + "type": "string" + }, + "email": { + "type": "string" + }, + "hasProfileImage": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "isAdmin": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "oauthId": { + "type": "string" + }, + "pinCode": { + "nullable": true, + "type": "string" + }, + "profileChangedAt": { + "format": "date-time", + "type": "string" + }, + "quotaSizeInBytes": { + "nullable": true, + "type": "integer" + }, + "quotaUsageInBytes": { + "type": "integer" + }, + "storageLabel": { + "nullable": true, + "type": "string" + } + }, + "required": [ + "avatarColor", + "deletedAt", + "email", + "hasProfileImage", + "id", + "isAdmin", + "name", + "oauthId", + "pinCode", + "profileChangedAt", + "quotaSizeInBytes", + "quotaUsageInBytes", + "storageLabel" + ], + "type": "object" + }, "SyncEntityType": { "enum": [ + "AuthUserV1", "UserV1", "UserDeleteV1", "AssetV1", @@ -14242,6 +14313,7 @@ "AlbumAssetExifsV1", "AssetsV1", "AssetExifsV1", + "AuthUsersV1", "MemoriesV1", "MemoryToAssetsV1", "PartnersV1", @@ -14364,6 +14436,14 @@ }, "SyncUserV1": { "properties": { + "avatarColor": { + "allOf": [ + { + "$ref": "#/components/schemas/UserAvatarColor" + } + ], + "nullable": true + }, "deletedAt": { "format": "date-time", "nullable": true, @@ -14380,6 +14460,7 @@ } }, "required": [ + "avatarColor", "deletedAt", "email", "id", diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 81d279407c..bcbe757066 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -4099,6 +4099,7 @@ export enum Error2 { NotFound = "not_found" } export enum SyncEntityType { + AuthUserV1 = "AuthUserV1", UserV1 = "UserV1", UserDeleteV1 = "UserDeleteV1", AssetV1 = "AssetV1", @@ -4149,6 +4150,7 @@ export enum SyncRequestType { AlbumAssetExifsV1 = "AlbumAssetExifsV1", AssetsV1 = "AssetsV1", AssetExifsV1 = "AssetExifsV1", + AuthUsersV1 = "AuthUsersV1", MemoriesV1 = "MemoriesV1", MemoryToAssetsV1 = "MemoryToAssetsV1", PartnersV1 = "PartnersV1", diff --git a/server/src/database.ts b/server/src/database.ts index dc99fc5b31..53c39b7383 100644 --- a/server/src/database.ts +++ b/server/src/database.ts @@ -356,6 +356,7 @@ export const columns = { ], syncAlbumUser: ['album_user.albumsId as albumId', 'album_user.usersId as userId', 'album_user.role'], syncStack: ['stack.id', 'stack.createdAt', 'stack.updatedAt', 'stack.primaryAssetId', 'stack.ownerId'], + syncUser: ['id', 'name', 'email', 'avatarColor', 'deletedAt', 'updateId'], stack: ['stack.id', 'stack.primaryAssetId', 'ownerId'], syncAssetExif: [ 'asset_exif.assetId', diff --git a/server/src/dtos/sync.dto.ts b/server/src/dtos/sync.dto.ts index c8b1a7dde9..8614bd5776 100644 --- a/server/src/dtos/sync.dto.ts +++ b/server/src/dtos/sync.dto.ts @@ -10,6 +10,7 @@ import { MemoryType, SyncEntityType, SyncRequestType, + UserAvatarColor, UserMetadataKey, } from 'src/enum'; import { UserMetadata } from 'src/types'; @@ -58,9 +59,25 @@ export class SyncUserV1 { id!: string; name!: string; email!: string; + @ValidateEnum({ enum: UserAvatarColor, name: 'UserAvatarColor', nullable: true }) + avatarColor!: UserAvatarColor | null; deletedAt!: Date | null; } +@ExtraModel() +export class SyncAuthUserV1 extends SyncUserV1 { + isAdmin!: boolean; + pinCode!: string | null; + oauthId!: string; + storageLabel!: string | null; + @ApiProperty({ type: 'integer' }) + quotaSizeInBytes!: number | null; + @ApiProperty({ type: 'integer' }) + quotaUsageInBytes!: number; + hasProfileImage!: boolean; + profileChangedAt!: Date; +} + @ExtraModel() export class SyncUserDeleteV1 { userId!: string; @@ -301,6 +318,7 @@ export class SyncAckV1 {} export class SyncResetV1 {} export type SyncItem = { + [SyncEntityType.AuthUserV1]: SyncAuthUserV1; [SyncEntityType.UserV1]: SyncUserV1; [SyncEntityType.UserDeleteV1]: SyncUserDeleteV1; [SyncEntityType.PartnerV1]: SyncPartnerV1; diff --git a/server/src/enum.ts b/server/src/enum.ts index f2eae615ab..75a96dfe67 100644 --- a/server/src/enum.ts +++ b/server/src/enum.ts @@ -559,6 +559,7 @@ export enum SyncRequestType { AlbumAssetExifsV1 = 'AlbumAssetExifsV1', AssetsV1 = 'AssetsV1', AssetExifsV1 = 'AssetExifsV1', + AuthUsersV1 = 'AuthUsersV1', MemoriesV1 = 'MemoriesV1', MemoryToAssetsV1 = 'MemoryToAssetsV1', PartnersV1 = 'PartnersV1', @@ -573,6 +574,8 @@ export enum SyncRequestType { } export enum SyncEntityType { + AuthUserV1 = 'AuthUserV1', + UserV1 = 'UserV1', UserDeleteV1 = 'UserDeleteV1', diff --git a/server/src/queries/sync.repository.sql b/server/src/queries/sync.repository.sql index 7502b79f57..7c7774a020 100644 --- a/server/src/queries/sync.repository.sql +++ b/server/src/queries/sync.repository.sql @@ -444,6 +444,29 @@ where order by "asset_face"."updateId" asc +-- SyncRepository.authUser.getUpserts +select + "id", + "name", + "email", + "avatarColor", + "deletedAt", + "updateId", + "isAdmin", + "pinCode", + "oauthId", + "storageLabel", + "quotaSizeInBytes", + "quotaUsageInBytes", + "profileImagePath", + "profileChangedAt" +from + "user" +where + "updatedAt" < now() - interval '1 millisecond' +order by + "updateId" asc + -- SyncRepository.memory.getDeletes select "id", @@ -871,6 +894,7 @@ select "id", "name", "email", + "avatarColor", "deletedAt", "updateId" from diff --git a/server/src/repositories/sync.repository.ts b/server/src/repositories/sync.repository.ts index dba52d25a0..486984118d 100644 --- a/server/src/repositories/sync.repository.ts +++ b/server/src/repositories/sync.repository.ts @@ -43,6 +43,7 @@ export class SyncRepository { asset: AssetSync; assetExif: AssetExifSync; assetFace: AssetFaceSync; + authUser: AuthUserSync; memory: MemorySync; memoryToAsset: MemoryToAssetSync; partner: PartnerSync; @@ -63,6 +64,7 @@ export class SyncRepository { this.asset = new AssetSync(this.db); this.assetExif = new AssetExifSync(this.db); this.assetFace = new AssetFaceSync(this.db); + this.authUser = new AuthUserSync(this.db); this.memory = new MemorySync(this.db); this.memoryToAsset = new MemoryToAssetSync(this.db); this.partner = new PartnerSync(this.db); @@ -367,6 +369,27 @@ class AssetSync extends BaseSync { } } +class AuthUserSync extends BaseSync { + @GenerateSql({ params: [], stream: true }) + getUpserts(ack?: SyncAck) { + return this.db + .selectFrom('user') + .select(columns.syncUser) + .select([ + 'isAdmin', + 'pinCode', + 'oauthId', + 'storageLabel', + 'quotaSizeInBytes', + 'quotaUsageInBytes', + 'profileImagePath', + 'profileChangedAt', + ]) + .$call(this.upsertTableFilters(ack)) + .stream(); + } +} + class PersonSync extends BaseSync { @GenerateSql({ params: [DummyValue.UUID], stream: true }) getDeletes(userId: string, ack?: SyncAck) { @@ -693,11 +716,7 @@ class UserSync extends BaseSync { @GenerateSql({ params: [], stream: true }) getUpserts(ack?: SyncAck) { - return this.db - .selectFrom('user') - .select(['id', 'name', 'email', 'deletedAt', 'updateId']) - .$call(this.upsertTableFilters(ack)) - .stream(); + return this.db.selectFrom('user').select(columns.syncUser).$call(this.upsertTableFilters(ack)).stream(); } } diff --git a/server/src/services/sync.service.ts b/server/src/services/sync.service.ts index fb582ab038..57b953f12e 100644 --- a/server/src/services/sync.service.ts +++ b/server/src/services/sync.service.ts @@ -54,6 +54,7 @@ const sendEntityBackfillCompleteAck = (response: Writable, ackType: SyncEntityTy const FULL_SYNC = { needsFullSync: true, deleted: [], upserted: [] }; export const SYNC_TYPES_ORDER = [ + SyncRequestType.AuthUsersV1, SyncRequestType.UsersV1, SyncRequestType.PartnersV1, SyncRequestType.AssetsV1, @@ -140,6 +141,7 @@ export class SyncService extends BaseService { const checkpointMap: CheckpointMap = Object.fromEntries(checkpoints.map(({ type, ack }) => [type, fromAck(ack)])); const handlers: Record Promise> = { + [SyncRequestType.AuthUsersV1]: () => this.syncAuthUsersV1(response, checkpointMap), [SyncRequestType.UsersV1]: () => this.syncUsersV1(response, checkpointMap), [SyncRequestType.PartnersV1]: () => this.syncPartnersV1(response, checkpointMap, auth), [SyncRequestType.AssetsV1]: () => this.syncAssetsV1(response, checkpointMap, auth), @@ -169,6 +171,14 @@ export class SyncService extends BaseService { response.end(); } + private async syncAuthUsersV1(response: Writable, checkpointMap: CheckpointMap) { + const upsertType = SyncEntityType.AuthUserV1; + const upserts = this.syncRepository.authUser.getUpserts(checkpointMap[upsertType]); + for await (const { updateId, profileImagePath, ...data } of upserts) { + send(response, { type: upsertType, ids: [updateId], data: { ...data, hasProfileImage: !!profileImagePath } }); + } + } + private async syncUsersV1(response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.UserDeleteV1; const deletes = this.syncRepository.user.getDeletes(checkpointMap[deleteType]); diff --git a/server/test/medium.factory.ts b/server/test/medium.factory.ts index d6038b6b84..1b669e83e4 100644 --- a/server/test/medium.factory.ts +++ b/server/test/medium.factory.ts @@ -507,7 +507,14 @@ const userInsert = (user: Partial> = {}) => { deletedAt: null, isAdmin: false, profileImagePath: '', + profileChangedAt: newDate(), shouldChangePassword: true, + storageLabel: null, + pinCode: null, + oauthId: '', + avatarColor: null, + quotaSizeInBytes: null, + quotaUsageInBytes: 0, }; return { ...defaults, ...user, id }; diff --git a/server/test/medium/specs/sync/sync-auth-user.spec.ts b/server/test/medium/specs/sync/sync-auth-user.spec.ts new file mode 100644 index 0000000000..80ce8b37fa --- /dev/null +++ b/server/test/medium/specs/sync/sync-auth-user.spec.ts @@ -0,0 +1,87 @@ +import { Kysely } from 'kysely'; +import { SyncEntityType, SyncRequestType } from 'src/enum'; +import { UserRepository } from 'src/repositories/user.repository'; +import { DB } from 'src/schema'; +import { SyncTestContext } from 'test/medium.factory'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = async (db?: Kysely) => { + const ctx = new SyncTestContext(db || defaultDatabase); + const { auth, user, session } = await ctx.newSyncAuthUser(); + return { auth, user, session, ctx }; +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe(SyncEntityType.AuthUserV1, () => { + it('should detect and sync the first user', async () => { + const { auth, user, ctx } = await setup(await getKyselyDB()); + + const response = await ctx.syncStream(auth, [SyncRequestType.AuthUsersV1]); + expect(response).toHaveLength(1); + expect(response).toEqual([ + { + ack: expect.any(String), + data: { + id: user.id, + isAdmin: user.isAdmin, + deletedAt: user.deletedAt, + name: user.name, + avatarColor: user.avatarColor, + email: user.email, + pinCode: user.pinCode, + hasProfileImage: false, + profileChangedAt: (user.profileChangedAt as Date).toISOString(), + oauthId: user.oauthId, + quotaSizeInBytes: user.quotaSizeInBytes, + quotaUsageInBytes: user.quotaUsageInBytes, + storageLabel: user.storageLabel, + }, + type: 'AuthUserV1', + }, + ]); + + await ctx.syncAckAll(auth, response); + await expect(ctx.syncStream(auth, [SyncRequestType.AuthUsersV1])).resolves.toEqual([]); + }); + + it('should sync a change and then another change to that same user', async () => { + const { auth, user, ctx } = await setup(await getKyselyDB()); + + const userRepo = ctx.get(UserRepository); + + const response = await ctx.syncStream(auth, [SyncRequestType.AuthUsersV1]); + expect(response).toHaveLength(1); + expect(response).toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + id: user.id, + isAdmin: false, + }), + type: 'AuthUserV1', + }, + ]); + + await ctx.syncAckAll(auth, response); + + await userRepo.update(user.id, { isAdmin: true }); + + const newResponse = await ctx.syncStream(auth, [SyncRequestType.AuthUsersV1]); + expect(newResponse).toHaveLength(1); + expect(newResponse).toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + id: user.id, + isAdmin: true, + }), + type: 'AuthUserV1', + }, + ]); + }); +}); diff --git a/server/test/medium/specs/sync/sync-user.spec.ts b/server/test/medium/specs/sync/sync-user.spec.ts index 24137e3aea..72661e119c 100644 --- a/server/test/medium/specs/sync/sync-user.spec.ts +++ b/server/test/medium/specs/sync/sync-user.spec.ts @@ -37,6 +37,7 @@ describe(SyncEntityType.UserV1, () => { email: user.email, id: user.id, name: user.name, + avatarColor: user.avatarColor, }, type: 'UserV1', }, @@ -49,8 +50,7 @@ describe(SyncEntityType.UserV1, () => { it('should detect and sync a soft deleted user', async () => { const { auth, ctx } = await setup(await getKyselyDB()); - const deletedAt = new Date().toISOString(); - const { user: deleted } = await ctx.newUser({ deletedAt }); + const { user: deleted } = await ctx.newUser({ deletedAt: new Date().toISOString() }); const response = await ctx.syncStream(auth, [SyncRequestType.UsersV1]); @@ -59,22 +59,12 @@ describe(SyncEntityType.UserV1, () => { expect.arrayContaining([ { ack: expect.any(String), - data: { - deletedAt: null, - email: auth.user.email, - id: auth.user.id, - name: auth.user.name, - }, + data: expect.objectContaining({ id: auth.user.id }), type: 'UserV1', }, { ack: expect.any(String), - data: { - deletedAt, - email: deleted.email, - id: deleted.id, - name: deleted.name, - }, + data: expect.objectContaining({ id: deleted.id }), type: 'UserV1', }, ]), @@ -85,7 +75,7 @@ describe(SyncEntityType.UserV1, () => { }); it('should detect and sync a deleted user', async () => { - const { auth, ctx } = await setup(await getKyselyDB()); + const { auth, user: authUser, ctx } = await setup(await getKyselyDB()); const userRepo = ctx.get(UserRepository); @@ -104,12 +94,7 @@ describe(SyncEntityType.UserV1, () => { }, { ack: expect.any(String), - data: { - deletedAt: null, - email: auth.user.email, - id: auth.user.id, - name: auth.user.name, - }, + data: expect.objectContaining({ id: authUser.id }), type: 'UserV1', }, ]); @@ -119,7 +104,7 @@ describe(SyncEntityType.UserV1, () => { }); it('should sync a user and then an update to that same user', async () => { - const { auth, ctx } = await setup(await getKyselyDB()); + const { auth, user, ctx } = await setup(await getKyselyDB()); const userRepo = ctx.get(UserRepository); @@ -128,12 +113,7 @@ describe(SyncEntityType.UserV1, () => { expect(response).toEqual([ { ack: expect.any(String), - data: { - deletedAt: null, - email: auth.user.email, - id: auth.user.id, - name: auth.user.name, - }, + data: expect.objectContaining({ id: user.id }), type: 'UserV1', }, ]); @@ -147,12 +127,7 @@ describe(SyncEntityType.UserV1, () => { expect(newResponse).toEqual([ { ack: expect.any(String), - data: { - deletedAt: null, - email: auth.user.email, - id: auth.user.id, - name: updated.name, - }, + data: expect.objectContaining({ id: user.id, name: updated.name }), type: 'UserV1', }, ]); From 08122d687128e9b5b60baf6d861fa4683bdf0b0d Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Wed, 23 Jul 2025 19:43:15 +0530 Subject: [PATCH 027/748] fix: show only local assets from albums selected for backup (#20050) * show only local assets from albums selected for backup # Conflicts: # mobile/lib/infrastructure/repositories/db.repository.drift.dart * ignore backup selection * fix: backup album ownerId * fix: backup album ownerId * only show local only assets that are selected for backup * add index on visibility and backup selection * fix: video not playing in search view * remove safe area from bottom bar * refactor stack count with a CTE and local asset with a SELECT * fix lint * remove redundant COALESCE * remove stack count from main timeline query --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Co-authored-by: Alex --- .../drift_schemas/main/drift_schema_v4.json | 2 +- .../models/asset/remote_asset.model.dart | 11 +- .../entities/merged_asset.drift | 143 +- .../entities/merged_asset.drift.dart | 47 +- .../repositories/backup.repository.dart | 11 +- .../repositories/db.repository.dart | 4 +- .../repositories/db.repository.drift.dart | 98 +- .../repositories/db.repository.steps.dart | 84 +- .../repositories/remote_asset.repository.dart | 30 +- .../repositories/timeline.repository.dart | 3 +- .../lib/pages/backup/drift_backup.page.dart | 27 +- .../drift_backup_album_selection.page.dart | 18 +- mobile/lib/pages/common/tab_shell.page.dart | 10 +- .../asset_viewer/asset_stack.provider.dart | 6 +- .../widgets/images/thumbnail_tile.widget.dart | 40 +- .../providers/app_life_cycle.provider.dart | 10 +- .../backup/drift_backup.provider.dart | 14 +- mobile/lib/services/drift_backup.service.dart | 11 +- .../test/drift/main/generated/schema_v4.dart | 1282 ++++++++--------- 19 files changed, 916 insertions(+), 935 deletions(-) diff --git a/mobile/drift_schemas/main/drift_schema_v4.json b/mobile/drift_schemas/main/drift_schema_v4.json index 82ef30adae..2488319a5e 100644 --- a/mobile/drift_schemas/main/drift_schema_v4.json +++ b/mobile/drift_schemas/main/drift_schema_v4.json @@ -1 +1 @@ -{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"profile_image_path","getter_name":"profileImagePath","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"quota_usage_in_bytes","getter_name":"quotaUsageInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[2],"type":"index","data":{"on":2,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":5,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_asset_owner_checksum","sql":null,"unique":true,"columns":["checksum","owner_id"]}},{"id":6,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":8,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":9,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":10,"references":[2,9],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":11,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":12,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[1,12],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":14,"references":[12,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":15,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":16,"references":[1,15],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":18,"references":[1,17],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}}]} \ No newline at end of file +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"profile_image_path","getter_name":"profileImagePath","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"quota_usage_in_bytes","getter_name":"quotaUsageInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":5,"references":[3,4],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":6,"references":[3],"type":"index","data":{"on":3,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_asset_owner_checksum","sql":null,"unique":true,"columns":["checksum","owner_id"]}},{"id":8,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":9,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":10,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":11,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":12,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[1,12],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":14,"references":[12,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":15,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":16,"references":[1,15],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":18,"references":[1,17],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}}]} \ No newline at end of file diff --git a/mobile/lib/domain/models/asset/remote_asset.model.dart b/mobile/lib/domain/models/asset/remote_asset.model.dart index 760a16170b..f96fa18a74 100644 --- a/mobile/lib/domain/models/asset/remote_asset.model.dart +++ b/mobile/lib/domain/models/asset/remote_asset.model.dart @@ -15,7 +15,6 @@ class RemoteAsset extends BaseAsset { final AssetVisibility visibility; final String ownerId; final String? stackId; - final int stackCount; const RemoteAsset({ required this.id, @@ -34,7 +33,6 @@ class RemoteAsset extends BaseAsset { this.visibility = AssetVisibility.timeline, super.livePhotoVideoId, this.stackId, - this.stackCount = 0, }); @override @@ -61,7 +59,6 @@ class RemoteAsset extends BaseAsset { thumbHash: ${thumbHash ?? ""}, visibility: $visibility, stackId: ${stackId ?? ""}, - stackCount: $stackCount, checksum: $checksum, livePhotoVideoId: ${livePhotoVideoId ?? ""}, }'''; @@ -77,8 +74,7 @@ class RemoteAsset extends BaseAsset { ownerId == other.ownerId && thumbHash == other.thumbHash && visibility == other.visibility && - stackId == other.stackId && - stackCount == other.stackCount; + stackId == other.stackId; } @override @@ -89,8 +85,7 @@ class RemoteAsset extends BaseAsset { localId.hashCode ^ thumbHash.hashCode ^ visibility.hashCode ^ - stackId.hashCode ^ - stackCount.hashCode; + stackId.hashCode; RemoteAsset copyWith({ String? id, @@ -109,7 +104,6 @@ class RemoteAsset extends BaseAsset { AssetVisibility? visibility, String? livePhotoVideoId, String? stackId, - int? stackCount, }) { return RemoteAsset( id: id ?? this.id, @@ -128,7 +122,6 @@ class RemoteAsset extends BaseAsset { visibility: visibility ?? this.visibility, livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, stackId: stackId ?? this.stackId, - stackCount: stackCount ?? this.stackCount, ); } } diff --git a/mobile/lib/infrastructure/entities/merged_asset.drift b/mobile/lib/infrastructure/entities/merged_asset.drift index 3dc7221c15..9778ba723b 100644 --- a/mobile/lib/infrastructure/entities/merged_asset.drift +++ b/mobile/lib/infrastructure/entities/merged_asset.drift @@ -1,76 +1,68 @@ import 'remote_asset.entity.dart'; -import 'local_asset.entity.dart'; import 'stack.entity.dart'; +import 'local_asset.entity.dart'; +import 'local_album.entity.dart'; +import 'local_album_asset.entity.dart'; -mergedAsset: SELECT * FROM -( - SELECT - rae.id as remote_id, - lae.id as local_id, - rae.name, - rae."type", - rae.created_at, - rae.updated_at, - rae.width, - rae.height, - rae.duration_in_seconds, - rae.is_favorite, - rae.thumb_hash, - rae.checksum, - rae.owner_id, - rae.live_photo_video_id, - 0 as orientation, - rae.stack_id, - COALESCE(stack_count.total_count, 0) AS stack_count - FROM - remote_asset_entity rae - LEFT JOIN - local_asset_entity lae ON rae.checksum = lae.checksum - LEFT JOIN - stack_entity se ON rae.stack_id = se.id - LEFT JOIN - (SELECT - stack_id, - COUNT(*) AS total_count - FROM remote_asset_entity - WHERE deleted_at IS NULL - AND visibility = 0 - AND stack_id IS NOT NULL - GROUP BY stack_id - ) AS stack_count ON rae.stack_id = stack_count.stack_id - WHERE - rae.deleted_at IS NULL - AND rae.visibility = 0 - AND rae.owner_id in ? - AND ( - rae.stack_id IS NULL - OR rae.id = se.primary_asset_id - ) - UNION ALL - SELECT - NULL as remote_id, - lae.id as local_id, - lae.name, - lae."type", - lae.created_at, - lae.updated_at, - lae.width, - lae.height, - lae.duration_in_seconds, - lae.is_favorite, - NULL as thumb_hash, - lae.checksum, - NULL as owner_id, - NULL as live_photo_video_id, - lae.orientation, - NULL as stack_id, - 0 AS stack_count - FROM - local_asset_entity lae - LEFT JOIN - remote_asset_entity rae ON rae.checksum = lae.checksum - WHERE - rae.id IS NULL +mergedAsset: +SELECT + rae.id as remote_id, + (SELECT lae.id FROM local_asset_entity lae WHERE lae.checksum = rae.checksum LIMIT 1) as local_id, + rae.name, + rae."type", + rae.created_at as created_at, + rae.updated_at, + rae.width, + rae.height, + rae.duration_in_seconds, + rae.is_favorite, + rae.thumb_hash, + rae.checksum, + rae.owner_id, + rae.live_photo_video_id, + 0 as orientation, + rae.stack_id +FROM + remote_asset_entity rae +LEFT JOIN + stack_entity se ON rae.stack_id = se.id +WHERE + rae.deleted_at IS NULL + AND rae.visibility = 0 -- timeline visibility + AND rae.owner_id in ? + AND ( + rae.stack_id IS NULL + OR rae.id = se.primary_asset_id + ) + +UNION ALL + +SELECT + NULL as remote_id, + lae.id as local_id, + lae.name, + lae."type", + lae.created_at as created_at, + lae.updated_at, + lae.width, + lae.height, + lae.duration_in_seconds, + lae.is_favorite, + NULL as thumb_hash, + lae.checksum, + NULL as owner_id, + NULL as live_photo_video_id, + lae.orientation, + NULL as stack_id +FROM + local_asset_entity lae +WHERE NOT EXISTS ( + SELECT 1 FROM remote_asset_entity rae WHERE rae.checksum = lae.checksum +) +AND EXISTS ( + SELECT 1 FROM local_album_asset_entity laa + INNER JOIN local_album_entity la on laa.album_id = la.id + WHERE laa.asset_id = lae.id AND la.backup_selection = 0 -- selected ) ORDER BY created_at DESC LIMIT $limit; @@ -85,17 +77,14 @@ SELECT FROM ( SELECT - rae.name, rae.created_at FROM remote_asset_entity rae - LEFT JOIN - local_asset_entity lae ON rae.checksum = lae.checksum LEFT JOIN stack_entity se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL - AND rae.visibility = 0 + AND rae.visibility = 0 -- timeline visibility AND rae.owner_id in ? AND ( rae.stack_id IS NULL @@ -103,14 +92,18 @@ FROM ) UNION ALL SELECT - lae.name, lae.created_at FROM local_asset_entity lae LEFT JOIN remote_asset_entity rae ON rae.checksum = lae.checksum + LEFT JOIN + local_album_asset_entity laa ON laa.asset_id = lae.id + LEFT JOIN + local_album_entity la ON la.id = laa.album_id WHERE rae.id IS NULL + AND la.backup_selection = 0 -- selected ) GROUP BY bucket_date ORDER BY bucket_date DESC; diff --git a/mobile/lib/infrastructure/entities/merged_asset.drift.dart b/mobile/lib/infrastructure/entities/merged_asset.drift.dart index ac3db868e1..7f56b25d4e 100644 --- a/mobile/lib/infrastructure/entities/merged_asset.drift.dart +++ b/mobile/lib/infrastructure/entities/merged_asset.drift.dart @@ -3,24 +3,29 @@ import 'package:drift/drift.dart' as i0; import 'package:drift/internal/modular.dart' as i1; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart' as i2; -import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' - as i3; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart' + as i3; +import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' as i4; import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart' as i5; +import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart' + as i6; +import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart' + as i7; class MergedAssetDrift extends i1.ModularAccessor { MergedAssetDrift(i0.GeneratedDatabase db) : super(db); i0.Selectable mergedAsset(List var1, - {required i0.Limit limit}) { + {required MergedAsset$limit limit}) { var $arrayStartIndex = 1; final expandedvar1 = $expandVar($arrayStartIndex, var1.length); $arrayStartIndex += var1.length; - final generatedlimit = $write(limit, startIndex: $arrayStartIndex); + final generatedlimit = $write(limit(alias(this.localAssetEntity, 'lae')), + startIndex: $arrayStartIndex); $arrayStartIndex += generatedlimit.amountOfVariables; return customSelect( - 'SELECT * FROM (SELECT rae.id AS remote_id, lae.id AS local_id, rae.name, rae.type, rae.created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id, COALESCE(stack_count.total_count, 0) AS stack_count FROM remote_asset_entity AS rae LEFT JOIN local_asset_entity AS lae ON rae.checksum = lae.checksum LEFT JOIN stack_entity AS se ON rae.stack_id = se.id LEFT JOIN (SELECT stack_id, COUNT(*) AS total_count FROM remote_asset_entity WHERE deleted_at IS NULL AND visibility = 0 AND stack_id IS NOT NULL GROUP BY stack_id) AS stack_count ON rae.stack_id = stack_count.stack_id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar1) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id, 0 AS stack_count FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum WHERE rae.id IS NULL) ORDER BY created_at DESC ${generatedlimit.sql}', + 'SELECT rae.id AS remote_id, (SELECT lae.id FROM local_asset_entity AS lae WHERE lae.checksum = rae.checksum LIMIT 1) AS local_id, rae.name, rae.type, rae.created_at AS created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar1) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at AS created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) ORDER BY created_at DESC ${generatedlimit.sql}', variables: [ for (var $ in var1) i0.Variable($), ...generatedlimit.introducedVariables @@ -29,12 +34,14 @@ class MergedAssetDrift extends i1.ModularAccessor { remoteAssetEntity, localAssetEntity, stackEntity, + localAlbumAssetEntity, + localAlbumEntity, ...generatedlimit.watchedTables, }).map((i0.QueryRow row) => MergedAssetResult( remoteId: row.readNullable('remote_id'), localId: row.readNullable('local_id'), name: row.read('name'), - type: i3.$RemoteAssetEntityTable.$convertertype + type: i4.$RemoteAssetEntityTable.$convertertype .fromSql(row.read('type')), createdAt: row.read('created_at'), updatedAt: row.read('updated_at'), @@ -48,7 +55,6 @@ class MergedAssetDrift extends i1.ModularAccessor { livePhotoVideoId: row.readNullable('live_photo_video_id'), orientation: row.read('orientation'), stackId: row.readNullable('stack_id'), - stackCount: row.read('stack_count'), )); } @@ -58,30 +64,39 @@ class MergedAssetDrift extends i1.ModularAccessor { final expandedvar2 = $expandVar($arrayStartIndex, var2.length); $arrayStartIndex += var2.length; return customSelect( - 'SELECT COUNT(*) AS asset_count, CASE WHEN ?1 = 0 THEN STRFTIME(\'%Y-%m-%d\', created_at, \'localtime\') WHEN ?1 = 1 THEN STRFTIME(\'%Y-%m\', created_at, \'localtime\') END AS bucket_date FROM (SELECT rae.name, rae.created_at FROM remote_asset_entity AS rae LEFT JOIN local_asset_entity AS lae ON rae.checksum = lae.checksum LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar2) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT lae.name, lae.created_at FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum WHERE rae.id IS NULL) GROUP BY bucket_date ORDER BY bucket_date DESC', + 'SELECT COUNT(*) AS asset_count, CASE WHEN ?1 = 0 THEN STRFTIME(\'%Y-%m-%d\', created_at, \'localtime\') WHEN ?1 = 1 THEN STRFTIME(\'%Y-%m\', created_at, \'localtime\') END AS bucket_date FROM (SELECT rae.created_at FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar2) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT lae.created_at FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum LEFT JOIN local_album_asset_entity AS laa ON laa.asset_id = lae.id LEFT JOIN local_album_entity AS la ON la.id = laa.album_id WHERE rae.id IS NULL AND la.backup_selection = 0) GROUP BY bucket_date ORDER BY bucket_date DESC', variables: [ i0.Variable(groupBy), for (var $ in var2) i0.Variable($) ], readsFrom: { remoteAssetEntity, - localAssetEntity, stackEntity, + localAssetEntity, + localAlbumAssetEntity, + localAlbumEntity, }).map((i0.QueryRow row) => MergedBucketResult( assetCount: row.read('asset_count'), bucketDate: row.read('bucket_date'), )); } - i3.$RemoteAssetEntityTable get remoteAssetEntity => + i4.$RemoteAssetEntityTable get remoteAssetEntity => i1.ReadDatabaseContainer(attachedDatabase) - .resultSet('remote_asset_entity'); - i4.$LocalAssetEntityTable get localAssetEntity => - i1.ReadDatabaseContainer(attachedDatabase) - .resultSet('local_asset_entity'); + .resultSet('remote_asset_entity'); i5.$StackEntityTable get stackEntity => i1.ReadDatabaseContainer(attachedDatabase) .resultSet('stack_entity'); + i3.$LocalAssetEntityTable get localAssetEntity => + i1.ReadDatabaseContainer(attachedDatabase) + .resultSet('local_asset_entity'); + i6.$LocalAlbumAssetEntityTable get localAlbumAssetEntity => + i1.ReadDatabaseContainer(attachedDatabase) + .resultSet( + 'local_album_asset_entity'); + i7.$LocalAlbumEntityTable get localAlbumEntity => + i1.ReadDatabaseContainer(attachedDatabase) + .resultSet('local_album_entity'); } class MergedAssetResult { @@ -101,7 +116,6 @@ class MergedAssetResult { final String? livePhotoVideoId; final int orientation; final String? stackId; - final int stackCount; MergedAssetResult({ this.remoteId, this.localId, @@ -119,10 +133,11 @@ class MergedAssetResult { this.livePhotoVideoId, required this.orientation, this.stackId, - required this.stackCount, }); } +typedef MergedAsset$limit = i0.Limit Function(i3.$LocalAssetEntityTable lae); + class MergedBucketResult { final int assetCount; final String bucketDate; diff --git a/mobile/lib/infrastructure/repositories/backup.repository.dart b/mobile/lib/infrastructure/repositories/backup.repository.dart index 99df206db5..aed7118e5a 100644 --- a/mobile/lib/infrastructure/repositories/backup.repository.dart +++ b/mobile/lib/infrastructure/repositories/backup.repository.dart @@ -50,7 +50,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { return query.get().then((rows) => rows.length); } - Future getRemainderCount() async { + Future getRemainderCount(String userId) async { final query = _db.localAlbumAssetEntity.selectOnly(distinct: true) ..addColumns([_db.localAlbumAssetEntity.assetId]) ..join([ @@ -74,7 +74,8 @@ class DriftBackupRepository extends DriftDatabaseRepository { ..where( _db.localAlbumEntity.backupSelection .equalsValue(BackupSelection.selected) & - _db.remoteAssetEntity.id.isNull() & + (_db.remoteAssetEntity.id.isNull() | + _db.remoteAssetEntity.ownerId.equals(userId).not()) & _db.localAlbumAssetEntity.assetId .isNotInQuery(_getExcludedSubquery()), ); @@ -82,7 +83,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { return query.get().then((rows) => rows.length); } - Future getBackupCount() async { + Future getBackupCount(String userId) async { final query = _db.localAlbumAssetEntity.selectOnly(distinct: true) ..addColumns( [_db.localAlbumAssetEntity.assetId], @@ -109,6 +110,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { _db.localAlbumEntity.backupSelection .equalsValue(BackupSelection.selected) & _db.remoteAssetEntity.id.isNotNull() & + _db.remoteAssetEntity.ownerId.equals(userId) & _db.localAlbumAssetEntity.assetId .isNotInQuery(_getExcludedSubquery()), ); @@ -116,7 +118,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { return query.get().then((rows) => rows.length); } - Future> getCandidates() async { + Future> getCandidates(String userId) async { final selectedAlbumIds = _db.localAlbumEntity.selectOnly(distinct: true) ..addColumns([_db.localAlbumEntity.id]) ..where( @@ -141,6 +143,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { ..addColumns([_db.remoteAssetEntity.checksum]) ..where( _db.remoteAssetEntity.checksum.equalsExp(lae.checksum) & + _db.remoteAssetEntity.ownerId.equals(userId) & lae.checksum.isNotNull(), ), ) & diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index ced148f855..5cbbb3b4f4 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -4,8 +4,8 @@ import 'package:drift/drift.dart'; import 'package:drift_flutter/drift_flutter.dart'; import 'package:flutter/foundation.dart'; import 'package:immich_mobile/domain/interfaces/db.interface.dart'; -import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; import 'package:immich_mobile/infrastructure/entities/asset_face.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'; @@ -97,7 +97,9 @@ class Drift extends $Drift implements IDatabaseRepository { await m.alterTable(TableMigration(v3.stackEntity)); }, from3To4: (m, v4) async { + // Thumbnail path column got removed from person_entity await m.alterTable(TableMigration(v4.personEntity)); + // asset_face_entity is added await m.create(v4.assetFaceEntity); }, ), diff --git a/mobile/lib/infrastructure/repositories/db.repository.drift.dart b/mobile/lib/infrastructure/repositories/db.repository.drift.dart index 7b722dfff6..f5962f09ab 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.drift.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.drift.dart @@ -5,17 +5,17 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i1; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' as i2; -import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart' - as i3; import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart' + as i3; +import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart' as i4; -import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart' - as i5; -import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart' - as i6; import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart' - as i7; + as i5; import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart' + as i6; +import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart' + as i7; +import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart' as i8; import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart' as i9; @@ -43,17 +43,17 @@ abstract class $Drift extends i0.GeneratedDatabase { late final i1.$UserEntityTable userEntity = i1.$UserEntityTable(this); late final i2.$RemoteAssetEntityTable remoteAssetEntity = i2.$RemoteAssetEntityTable(this); - late final i3.$LocalAssetEntityTable localAssetEntity = - i3.$LocalAssetEntityTable(this); - late final i4.$StackEntityTable stackEntity = i4.$StackEntityTable(this); - late final i5.$UserMetadataEntityTable userMetadataEntity = - i5.$UserMetadataEntityTable(this); - late final i6.$PartnerEntityTable partnerEntity = - i6.$PartnerEntityTable(this); - late final i7.$LocalAlbumEntityTable localAlbumEntity = - i7.$LocalAlbumEntityTable(this); - late final i8.$LocalAlbumAssetEntityTable localAlbumAssetEntity = - i8.$LocalAlbumAssetEntityTable(this); + late final i3.$StackEntityTable stackEntity = i3.$StackEntityTable(this); + late final i4.$LocalAssetEntityTable localAssetEntity = + i4.$LocalAssetEntityTable(this); + late final i5.$LocalAlbumEntityTable localAlbumEntity = + i5.$LocalAlbumEntityTable(this); + late final i6.$LocalAlbumAssetEntityTable localAlbumAssetEntity = + i6.$LocalAlbumAssetEntityTable(this); + late final i7.$UserMetadataEntityTable userMetadataEntity = + i7.$UserMetadataEntityTable(this); + late final i8.$PartnerEntityTable partnerEntity = + i8.$PartnerEntityTable(this); late final i9.$RemoteExifEntityTable remoteExifEntity = i9.$RemoteExifEntityTable(this); late final i10.$RemoteAlbumEntityTable remoteAlbumEntity = @@ -77,15 +77,15 @@ abstract class $Drift extends i0.GeneratedDatabase { List get allSchemaEntities => [ userEntity, remoteAssetEntity, - localAssetEntity, stackEntity, - i3.idxLocalAssetChecksum, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + i4.idxLocalAssetChecksum, i2.uQRemoteAssetOwnerChecksum, i2.idxRemoteAssetChecksum, userMetadataEntity, partnerEntity, - localAlbumEntity, - localAlbumAssetEntity, remoteExifEntity, remoteAlbumEntity, remoteAlbumAssetEntity, @@ -113,6 +113,22 @@ abstract class $Drift extends i0.GeneratedDatabase { i0.TableUpdate('stack_entity', kind: i0.UpdateKind.delete), ], ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName('local_asset_entity', + limitUpdateKind: i0.UpdateKind.delete), + result: [ + i0.TableUpdate('local_album_asset_entity', + kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName('local_album_entity', + limitUpdateKind: i0.UpdateKind.delete), + result: [ + i0.TableUpdate('local_album_asset_entity', + kind: i0.UpdateKind.delete), + ], + ), i0.WritePropagation( on: i0.TableUpdateQuery.onTableName('user_entity', limitUpdateKind: i0.UpdateKind.delete), @@ -135,22 +151,6 @@ abstract class $Drift extends i0.GeneratedDatabase { i0.TableUpdate('partner_entity', kind: i0.UpdateKind.delete), ], ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('local_asset_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('local_album_asset_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('local_album_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('local_album_asset_entity', - kind: i0.UpdateKind.delete), - ], - ), i0.WritePropagation( on: i0.TableUpdateQuery.onTableName('remote_asset_entity', limitUpdateKind: i0.UpdateKind.delete), @@ -260,18 +260,18 @@ class $DriftManager { i1.$$UserEntityTableTableManager(_db, _db.userEntity); i2.$$RemoteAssetEntityTableTableManager get remoteAssetEntity => i2.$$RemoteAssetEntityTableTableManager(_db, _db.remoteAssetEntity); - i3.$$LocalAssetEntityTableTableManager get localAssetEntity => - i3.$$LocalAssetEntityTableTableManager(_db, _db.localAssetEntity); - i4.$$StackEntityTableTableManager get stackEntity => - i4.$$StackEntityTableTableManager(_db, _db.stackEntity); - i5.$$UserMetadataEntityTableTableManager get userMetadataEntity => - i5.$$UserMetadataEntityTableTableManager(_db, _db.userMetadataEntity); - i6.$$PartnerEntityTableTableManager get partnerEntity => - i6.$$PartnerEntityTableTableManager(_db, _db.partnerEntity); - i7.$$LocalAlbumEntityTableTableManager get localAlbumEntity => - i7.$$LocalAlbumEntityTableTableManager(_db, _db.localAlbumEntity); - i8.$$LocalAlbumAssetEntityTableTableManager get localAlbumAssetEntity => i8 + i3.$$StackEntityTableTableManager get stackEntity => + i3.$$StackEntityTableTableManager(_db, _db.stackEntity); + i4.$$LocalAssetEntityTableTableManager get localAssetEntity => + i4.$$LocalAssetEntityTableTableManager(_db, _db.localAssetEntity); + i5.$$LocalAlbumEntityTableTableManager get localAlbumEntity => + i5.$$LocalAlbumEntityTableTableManager(_db, _db.localAlbumEntity); + i6.$$LocalAlbumAssetEntityTableTableManager get localAlbumAssetEntity => i6 .$$LocalAlbumAssetEntityTableTableManager(_db, _db.localAlbumAssetEntity); + i7.$$UserMetadataEntityTableTableManager get userMetadataEntity => + i7.$$UserMetadataEntityTableTableManager(_db, _db.userMetadataEntity); + i8.$$PartnerEntityTableTableManager get partnerEntity => + i8.$$PartnerEntityTableTableManager(_db, _db.partnerEntity); i9.$$RemoteExifEntityTableTableManager get remoteExifEntity => i9.$$RemoteExifEntityTableTableManager(_db, _db.remoteExifEntity); i10.$$RemoteAlbumEntityTableTableManager get remoteAlbumEntity => diff --git a/mobile/lib/infrastructure/repositories/db.repository.steps.dart b/mobile/lib/infrastructure/repositories/db.repository.steps.dart index d8c35707ed..7f179fb5f0 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.steps.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.steps.dart @@ -1273,15 +1273,15 @@ final class Schema4 extends i0.VersionedSchema { late final List entities = [ userEntity, remoteAssetEntity, - localAssetEntity, stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, idxLocalAssetChecksum, uQRemoteAssetOwnerChecksum, idxRemoteAssetChecksum, userMetadataEntity, partnerEntity, - localAlbumEntity, - localAlbumAssetEntity, remoteExifEntity, remoteAlbumEntity, remoteAlbumAssetEntity, @@ -1342,6 +1342,24 @@ final class Schema4 extends i0.VersionedSchema { attachedDatabase: database, ), alias: null); + late final Shape3 stackEntity = Shape3( + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_75, + ], + attachedDatabase: database, + ), + alias: null); late final Shape2 localAssetEntity = Shape2( source: i0.VersionedTable( entityName: 'local_asset_entity', @@ -1366,9 +1384,9 @@ final class Schema4 extends i0.VersionedSchema { attachedDatabase: database, ), alias: null); - late final Shape3 stackEntity = Shape3( + late final Shape6 localAlbumEntity = Shape6( source: i0.VersionedTable( - entityName: 'stack_entity', + entityName: 'local_album_entity', withoutRowId: true, isStrict: true, tableConstraints: [ @@ -1376,10 +1394,26 @@ final class Schema4 extends i0.VersionedSchema { ], columns: [ _column_0, - _column_9, + _column_1, _column_5, - _column_15, - _column_75, + _column_31, + _column_32, + _column_33, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape7 localAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(asset_id, album_id)', + ], + columns: [ + _column_34, + _column_35, ], attachedDatabase: database, ), @@ -1423,40 +1457,6 @@ final class Schema4 extends i0.VersionedSchema { attachedDatabase: database, ), alias: null); - late final Shape6 localAlbumEntity = Shape6( - source: i0.VersionedTable( - entityName: 'local_album_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_1, - _column_5, - _column_31, - _column_32, - _column_33, - ], - attachedDatabase: database, - ), - alias: null); - late final Shape7 localAlbumAssetEntity = Shape7( - source: i0.VersionedTable( - entityName: 'local_album_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id, album_id)', - ], - columns: [ - _column_34, - _column_35, - ], - attachedDatabase: database, - ), - alias: null); late final Shape8 remoteExifEntity = Shape8( source: i0.VersionedTable( entityName: 'remote_exif_entity', diff --git a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart index 64c602a2c7..c3b15efb83 100644 --- a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart @@ -32,49 +32,21 @@ class RemoteAssetRepository extends DriftDatabaseRepository { } Stream watchAsset(String id) { - final stackCountRef = _db.stackEntity.id.count(); - final query = _db.remoteAssetEntity.select().addColumns([ _db.localAssetEntity.id, - _db.stackEntity.primaryAssetId, - stackCountRef, ]).join([ leftOuterJoin( _db.localAssetEntity, _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), useColumns: false, ), - leftOuterJoin( - _db.stackEntity, - _db.stackEntity.primaryAssetId.equalsExp(_db.remoteAssetEntity.id), - useColumns: false, - ), - leftOuterJoin( - _db.remoteAssetEntity.createAlias('stacked_assets'), - _db.stackEntity.id.equalsExp( - _db.remoteAssetEntity.createAlias('stacked_assets').stackId, - ), - useColumns: false, - ), ]) ..where(_db.remoteAssetEntity.id.equals(id)) - ..groupBy([ - _db.remoteAssetEntity.id, - _db.localAssetEntity.id, - _db.stackEntity.primaryAssetId, - ]) ..limit(1); return query.map((row) { final asset = row.readTable(_db.remoteAssetEntity).toDto(); - final primaryAssetId = row.read(_db.stackEntity.primaryAssetId); - final stackCount = - primaryAssetId == id ? (row.read(stackCountRef) ?? 0) : 0; - - return asset.copyWith( - localId: row.read(_db.localAssetEntity.id), - stackCount: stackCount, - ); + return asset.copyWith(localId: row.read(_db.localAssetEntity.id)); }).watchSingleOrNull(); } diff --git a/mobile/lib/infrastructure/repositories/timeline.repository.dart b/mobile/lib/infrastructure/repositories/timeline.repository.dart index 7bbae9a80a..fe9ae1e60d 100644 --- a/mobile/lib/infrastructure/repositories/timeline.repository.dart +++ b/mobile/lib/infrastructure/repositories/timeline.repository.dart @@ -71,7 +71,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { required int count, }) { return _db.mergedAssetDrift - .mergedAsset(userIds, limit: Limit(count, offset)) + .mergedAsset(userIds, limit: (_) => Limit(count, offset)) .map( (row) => row.remoteId != null && row.ownerId != null ? RemoteAsset( @@ -90,7 +90,6 @@ class DriftTimelineRepository extends DriftDatabaseRepository { durationInSeconds: row.durationInSeconds, livePhotoVideoId: row.livePhotoVideoId, stackId: row.stackId, - stackCount: row.stackCount, ) : LocalAsset( id: row.localId!, diff --git a/mobile/lib/pages/backup/drift_backup.page.dart b/mobile/lib/pages/backup/drift_backup.page.dart index 7780413399..08b09250e1 100644 --- a/mobile/lib/pages/backup/drift_backup.page.dart +++ b/mobile/lib/pages/backup/drift_backup.page.dart @@ -9,6 +9,7 @@ import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/backup/backup_toggle_button.widget.dart'; import 'package:immich_mobile/providers/backup/backup_album.provider.dart'; import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/widgets/backup/backup_info_card.dart'; @@ -24,12 +25,24 @@ class _DriftBackupPageState extends ConsumerState { @override void initState() { super.initState(); - ref.read(driftBackupProvider.notifier).getBackupStatus(); + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id); } Future startBackup() async { - await ref.read(driftBackupProvider.notifier).getBackupStatus(); - await ref.read(driftBackupProvider.notifier).backup(); + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + await ref + .read(driftBackupProvider.notifier) + .getBackupStatus(currentUser.id); + await ref.read(driftBackupProvider.notifier).backup(currentUser.id); } Future stopBackup() async { @@ -207,7 +220,13 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { trailing: ElevatedButton( onPressed: () async { await context.pushRoute(const DriftBackupAlbumSelectionRoute()); - ref.read(driftBackupProvider.notifier).getBackupStatus(); + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + ref + .read(driftBackupProvider.notifier) + .getBackupStatus(currentUser.id); }, child: const Text( "select", diff --git a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart index 18d3ee1156..f0f3bedaab 100644 --- a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart +++ b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart @@ -4,17 +4,17 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/album/local_album.model.dart'; - import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/backup/backup_album.provider.dart'; import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; -import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/widgets/backup/drift_album_info_list_tile.dart'; -import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/widgets/common/search_field.dart'; +import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; @RoutePage() class DriftBackupAlbumSelectionPage extends ConsumerStatefulWidget { @@ -92,7 +92,15 @@ class _DriftBackupAlbumSelectionPageState if (didPop && !_hasPopped) { _hasPopped = true; - await ref.read(driftBackupProvider.notifier).getBackupStatus(); + super.initState(); + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + await ref + .read(driftBackupProvider.notifier) + .getBackupStatus(currentUser.id); final currentTotalAssetCount = ref.read(driftBackupProvider.select((p) => p.totalCount)); @@ -107,7 +115,7 @@ class _DriftBackupAlbumSelectionPageState final backupNotifier = ref.read(driftBackupProvider.notifier); backupNotifier.cancel().then((_) { - backupNotifier.backup(); + backupNotifier.backup(currentUser.id); }); } } diff --git a/mobile/lib/pages/common/tab_shell.page.dart b/mobile/lib/pages/common/tab_shell.page.dart index b0be136a15..418c8b9e39 100644 --- a/mobile/lib/pages/common/tab_shell.page.dart +++ b/mobile/lib/pages/common/tab_shell.page.dart @@ -11,6 +11,7 @@ import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/search/search_input_focus.provider.dart'; import 'package:immich_mobile/providers/tab.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/providers/websocket.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; @@ -38,7 +39,14 @@ class _TabShellPageState extends ConsumerState { await runNewSync(ref, full: true).then((_) async { if (isEnableBackup) { - await ref.read(driftBackupProvider.notifier).handleBackupResume(); + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + await ref + .read(driftBackupProvider.notifier) + .handleBackupResume(currentUser.id); } }); }); diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart index cb4e02b56c..5b86258e72 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart @@ -6,11 +6,7 @@ class StackChildrenNotifier extends AutoDisposeFamilyAsyncNotifier, BaseAsset?> { @override Future> build(BaseAsset? asset) async { - if (asset == null || - asset is! RemoteAsset || - asset.stackId == null || - // The stackCount check is to ensure we only fetch stacks for timelines that have stacks - asset.stackCount == 0) { + if (asset == null || asset is! RemoteAsset || asset.stackId == null) { return const []; } diff --git a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart index ce3d39629f..b059f5feaa 100644 --- a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart +++ b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart @@ -54,7 +54,7 @@ class ThumbnailTile extends ConsumerWidget { : const BoxDecoration(); final hasStack = - asset is RemoteAsset && (asset as RemoteAsset).stackCount > 0; + asset is RemoteAsset && (asset as RemoteAsset).stackId != null; return Stack( children: [ @@ -86,9 +86,7 @@ class ThumbnailTile extends ConsumerWidget { right: 10.0, top: asset.isVideo ? 24.0 : 6.0, ), - child: _StackIndicator( - stackCount: (asset as RemoteAsset).stackCount, - ), + child: const _TileOverlayIcon(Icons.burst_mode_rounded), ), ), if (asset.isVideo) @@ -198,40 +196,6 @@ class _SelectionIndicator extends StatelessWidget { } } -class _StackIndicator extends StatelessWidget { - final int stackCount; - - const _StackIndicator({required this.stackCount}); - - @override - Widget build(BuildContext context) { - return Row( - spacing: 3, - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.end, - // CrossAxisAlignment.start looks more centered vertically than CrossAxisAlignment.center - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - stackCount.toString(), - style: const TextStyle( - color: Colors.white, - fontSize: 12, - fontWeight: FontWeight.bold, - shadows: [ - Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - ), - ], - ), - ), - const _TileOverlayIcon(Icons.burst_mode_rounded), - ], - ); - } -} - class _VideoIndicator extends StatelessWidget { final Duration duration; const _VideoIndicator(this.duration); diff --git a/mobile/lib/providers/app_life_cycle.provider.dart b/mobile/lib/providers/app_life_cycle.provider.dart index 3be46d2fbd..647ada4a64 100644 --- a/mobile/lib/providers/app_life_cycle.provider.dart +++ b/mobile/lib/providers/app_life_cycle.provider.dart @@ -19,6 +19,7 @@ import 'package:immich_mobile/providers/memory.provider.dart'; import 'package:immich_mobile/providers/notification_permission.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/tab.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/providers/websocket.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/services/background.service.dart'; @@ -110,7 +111,14 @@ class AppLifeCycleNotifier extends StateNotifier { .getSetting(AppSettingsEnum.enableBackup); if (isEnableBackup) { - await _ref.read(driftBackupProvider.notifier).handleBackupResume(); + final currentUser = _ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + await _ref + .read(driftBackupProvider.notifier) + .handleBackupResume(currentUser.id); } }); } catch (e, stackTrace) { diff --git a/mobile/lib/providers/backup/drift_backup.provider.dart b/mobile/lib/providers/backup/drift_backup.provider.dart index 3a2c7fd9ce..45941639a0 100644 --- a/mobile/lib/providers/backup/drift_backup.provider.dart +++ b/mobile/lib/providers/backup/drift_backup.provider.dart @@ -329,11 +329,11 @@ class ExpBackupNotifier extends StateNotifier { ); } - Future getBackupStatus() async { + Future getBackupStatus(String userId) async { final [totalCount, backupCount, remainderCount] = await Future.wait([ _backupService.getTotalCount(), - _backupService.getBackupCount(), - _backupService.getRemainderCount(), + _backupService.getBackupCount(userId), + _backupService.getRemainderCount(userId), ]); state = state.copyWith( @@ -343,8 +343,8 @@ class ExpBackupNotifier extends StateNotifier { ); } - Future backup() { - return _backupService.backup(_updateEnqueueCount); + Future backup(String userId) { + return _backupService.backup(userId, _updateEnqueueCount); } void _updateEnqueueCount(EnqueueStatus status) { @@ -379,11 +379,11 @@ class ExpBackupNotifier extends StateNotifier { } } - Future handleBackupResume() async { + Future handleBackupResume(String userId) async { final tasks = await FileDownloader().allTasks(group: kBackupGroup); if (tasks.isEmpty) { // Start a new backup queue - await backup(); + await backup(userId); } debugPrint("Tasks to resume: ${tasks.length}"); diff --git a/mobile/lib/services/drift_backup.service.dart b/mobile/lib/services/drift_backup.service.dart index 2f51c261fb..7373643c95 100644 --- a/mobile/lib/services/drift_backup.service.dart +++ b/mobile/lib/services/drift_backup.service.dart @@ -48,20 +48,21 @@ class DriftBackupService { return _backupRepository.getTotalCount(); } - Future getRemainderCount() { - return _backupRepository.getRemainderCount(); + Future getRemainderCount(String userId) { + return _backupRepository.getRemainderCount(userId); } - Future getBackupCount() { - return _backupRepository.getBackupCount(); + Future getBackupCount(String userId) { + return _backupRepository.getBackupCount(userId); } Future backup( + String userId, void Function(EnqueueStatus status) onEnqueueTasks, ) async { shouldCancel = false; - final candidates = await _backupRepository.getCandidates(); + final candidates = await _backupRepository.getCandidates(userId); if (candidates.isEmpty) { return; } diff --git a/mobile/test/drift/main/generated/schema_v4.dart b/mobile/test/drift/main/generated/schema_v4.dart index d02e2ff9c4..9cf72a098b 100644 --- a/mobile/test/drift/main/generated/schema_v4.dart +++ b/mobile/test/drift/main/generated/schema_v4.dart @@ -982,6 +982,255 @@ class RemoteAssetEntityCompanion } } +class StackEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StackEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + @override + List get $columns => + [id, createdAt, updatedAt, ownerId, primaryAssetId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'stack_entity'; + @override + Set get $primaryKey => {id}; + @override + StackEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StackEntityData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + ownerId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!, + ); + } + + @override + StackEntity createAlias(String alias) { + return StackEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StackEntityData extends DataClass implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String primaryAssetId; + const StackEntityData( + {required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['primary_asset_id'] = Variable(primaryAssetId); + return map; + } + + factory StackEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StackEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + primaryAssetId: serializer.fromJson(json['primaryAssetId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'primaryAssetId': serializer.toJson(primaryAssetId), + }; + } + + StackEntityData copyWith( + {String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId}) => + StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + StackEntityData copyWithCompanion(StackEntityCompanion data) { + return StackEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, + ); + } + + @override + String toString() { + return (StringBuffer('StackEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StackEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.primaryAssetId == this.primaryAssetId); +} + +class StackEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value primaryAssetId; + const StackEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.primaryAssetId = const Value.absent(), + }); + StackEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String primaryAssetId, + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? primaryAssetId, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, + }); + } + + StackEntityCompanion copyWith( + {Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId}) { + return StackEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (primaryAssetId.present) { + map['primary_asset_id'] = Variable(primaryAssetId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StackEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } +} + class LocalAssetEntity extends Table with TableInfo { @override @@ -1415,640 +1664,6 @@ class LocalAssetEntityCompanion extends UpdateCompanion { } } -class StackEntity extends Table with TableInfo { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - StackEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn ownerId = GeneratedColumn( - 'owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn primaryAssetId = GeneratedColumn( - 'primary_asset_id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - @override - List get $columns => - [id, createdAt, updatedAt, ownerId, primaryAssetId]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'stack_entity'; - @override - Set get $primaryKey => {id}; - @override - StackEntityData map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return StackEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - primaryAssetId: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!, - ); - } - - @override - StackEntity createAlias(String alias) { - return StackEntity(attachedDatabase, alias); - } - - @override - bool get withoutRowId => true; - @override - bool get isStrict => true; -} - -class StackEntityData extends DataClass implements Insertable { - final String id; - final DateTime createdAt; - final DateTime updatedAt; - final String ownerId; - final String primaryAssetId; - const StackEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.primaryAssetId}); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['id'] = Variable(id); - map['created_at'] = Variable(createdAt); - map['updated_at'] = Variable(updatedAt); - map['owner_id'] = Variable(ownerId); - map['primary_asset_id'] = Variable(primaryAssetId); - return map; - } - - factory StackEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return StackEntityData( - id: serializer.fromJson(json['id']), - createdAt: serializer.fromJson(json['createdAt']), - updatedAt: serializer.fromJson(json['updatedAt']), - ownerId: serializer.fromJson(json['ownerId']), - primaryAssetId: serializer.fromJson(json['primaryAssetId']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'id': serializer.toJson(id), - 'createdAt': serializer.toJson(createdAt), - 'updatedAt': serializer.toJson(updatedAt), - 'ownerId': serializer.toJson(ownerId), - 'primaryAssetId': serializer.toJson(primaryAssetId), - }; - } - - StackEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? primaryAssetId}) => - StackEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - primaryAssetId: primaryAssetId ?? this.primaryAssetId, - ); - StackEntityData copyWithCompanion(StackEntityCompanion data) { - return StackEntityData( - id: data.id.present ? data.id.value : this.id, - createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, - updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, - primaryAssetId: data.primaryAssetId.present - ? data.primaryAssetId.value - : this.primaryAssetId, - ); - } - - @override - String toString() { - return (StringBuffer('StackEntityData(') - ..write('id: $id, ') - ..write('createdAt: $createdAt, ') - ..write('updatedAt: $updatedAt, ') - ..write('ownerId: $ownerId, ') - ..write('primaryAssetId: $primaryAssetId') - ..write(')')) - .toString(); - } - - @override - int get hashCode => - Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is StackEntityData && - other.id == this.id && - other.createdAt == this.createdAt && - other.updatedAt == this.updatedAt && - other.ownerId == this.ownerId && - other.primaryAssetId == this.primaryAssetId); -} - -class StackEntityCompanion extends UpdateCompanion { - final Value id; - final Value createdAt; - final Value updatedAt; - final Value ownerId; - final Value primaryAssetId; - const StackEntityCompanion({ - this.id = const Value.absent(), - this.createdAt = const Value.absent(), - this.updatedAt = const Value.absent(), - this.ownerId = const Value.absent(), - this.primaryAssetId = const Value.absent(), - }); - StackEntityCompanion.insert({ - required String id, - this.createdAt = const Value.absent(), - this.updatedAt = const Value.absent(), - required String ownerId, - required String primaryAssetId, - }) : id = Value(id), - ownerId = Value(ownerId), - primaryAssetId = Value(primaryAssetId); - static Insertable custom({ - Expression? id, - Expression? createdAt, - Expression? updatedAt, - Expression? ownerId, - Expression? primaryAssetId, - }) { - return RawValuesInsertable({ - if (id != null) 'id': id, - if (createdAt != null) 'created_at': createdAt, - if (updatedAt != null) 'updated_at': updatedAt, - if (ownerId != null) 'owner_id': ownerId, - if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, - }); - } - - StackEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? primaryAssetId}) { - return StackEntityCompanion( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - primaryAssetId: primaryAssetId ?? this.primaryAssetId, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (id.present) { - map['id'] = Variable(id.value); - } - if (createdAt.present) { - map['created_at'] = Variable(createdAt.value); - } - if (updatedAt.present) { - map['updated_at'] = Variable(updatedAt.value); - } - if (ownerId.present) { - map['owner_id'] = Variable(ownerId.value); - } - if (primaryAssetId.present) { - map['primary_asset_id'] = Variable(primaryAssetId.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('StackEntityCompanion(') - ..write('id: $id, ') - ..write('createdAt: $createdAt, ') - ..write('updatedAt: $updatedAt, ') - ..write('ownerId: $ownerId, ') - ..write('primaryAssetId: $primaryAssetId') - ..write(')')) - .toString(); - } -} - -class UserMetadataEntity extends Table - with TableInfo { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - UserMetadataEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn userId = GeneratedColumn( - 'user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn key = GeneratedColumn( - 'key', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn value = GeneratedColumn( - 'value', aliasedName, false, - type: DriftSqlType.blob, requiredDuringInsert: true); - @override - List get $columns => [userId, key, value]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'user_metadata_entity'; - @override - Set get $primaryKey => {userId, key}; - @override - UserMetadataEntityData map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return UserMetadataEntityData( - userId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - key: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}key'])!, - value: attachedDatabase.typeMapping - .read(DriftSqlType.blob, data['${effectivePrefix}value'])!, - ); - } - - @override - UserMetadataEntity createAlias(String alias) { - return UserMetadataEntity(attachedDatabase, alias); - } - - @override - bool get withoutRowId => true; - @override - bool get isStrict => true; -} - -class UserMetadataEntityData extends DataClass - implements Insertable { - final String userId; - final int key; - final Uint8List value; - const UserMetadataEntityData( - {required this.userId, required this.key, required this.value}); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['user_id'] = Variable(userId); - map['key'] = Variable(key); - map['value'] = Variable(value); - return map; - } - - factory UserMetadataEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return UserMetadataEntityData( - userId: serializer.fromJson(json['userId']), - key: serializer.fromJson(json['key']), - value: serializer.fromJson(json['value']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'userId': serializer.toJson(userId), - 'key': serializer.toJson(key), - 'value': serializer.toJson(value), - }; - } - - UserMetadataEntityData copyWith( - {String? userId, int? key, Uint8List? value}) => - UserMetadataEntityData( - userId: userId ?? this.userId, - key: key ?? this.key, - value: value ?? this.value, - ); - UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { - return UserMetadataEntityData( - userId: data.userId.present ? data.userId.value : this.userId, - key: data.key.present ? data.key.value : this.key, - value: data.value.present ? data.value.value : this.value, - ); - } - - @override - String toString() { - return (StringBuffer('UserMetadataEntityData(') - ..write('userId: $userId, ') - ..write('key: $key, ') - ..write('value: $value') - ..write(')')) - .toString(); - } - - @override - int get hashCode => Object.hash(userId, key, $driftBlobEquality.hash(value)); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is UserMetadataEntityData && - other.userId == this.userId && - other.key == this.key && - $driftBlobEquality.equals(other.value, this.value)); -} - -class UserMetadataEntityCompanion - extends UpdateCompanion { - final Value userId; - final Value key; - final Value value; - const UserMetadataEntityCompanion({ - this.userId = const Value.absent(), - this.key = const Value.absent(), - this.value = const Value.absent(), - }); - UserMetadataEntityCompanion.insert({ - required String userId, - required int key, - required Uint8List value, - }) : userId = Value(userId), - key = Value(key), - value = Value(value); - static Insertable custom({ - Expression? userId, - Expression? key, - Expression? value, - }) { - return RawValuesInsertable({ - if (userId != null) 'user_id': userId, - if (key != null) 'key': key, - if (value != null) 'value': value, - }); - } - - UserMetadataEntityCompanion copyWith( - {Value? userId, Value? key, Value? value}) { - return UserMetadataEntityCompanion( - userId: userId ?? this.userId, - key: key ?? this.key, - value: value ?? this.value, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (userId.present) { - map['user_id'] = Variable(userId.value); - } - if (key.present) { - map['key'] = Variable(key.value); - } - if (value.present) { - map['value'] = Variable(value.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('UserMetadataEntityCompanion(') - ..write('userId: $userId, ') - ..write('key: $key, ') - ..write('value: $value') - ..write(')')) - .toString(); - } -} - -class PartnerEntity extends Table - with TableInfo { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - PartnerEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn sharedById = GeneratedColumn( - 'shared_by_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn sharedWithId = GeneratedColumn( - 'shared_with_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn inTimeline = GeneratedColumn( - 'in_timeline', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("in_timeline" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - @override - List get $columns => [sharedById, sharedWithId, inTimeline]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'partner_entity'; - @override - Set get $primaryKey => {sharedById, sharedWithId}; - @override - PartnerEntityData map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return PartnerEntityData( - sharedById: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}shared_by_id'])!, - sharedWithId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}shared_with_id'])!, - inTimeline: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!, - ); - } - - @override - PartnerEntity createAlias(String alias) { - return PartnerEntity(attachedDatabase, alias); - } - - @override - bool get withoutRowId => true; - @override - bool get isStrict => true; -} - -class PartnerEntityData extends DataClass - implements Insertable { - final String sharedById; - final String sharedWithId; - final bool inTimeline; - const PartnerEntityData( - {required this.sharedById, - required this.sharedWithId, - required this.inTimeline}); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['shared_by_id'] = Variable(sharedById); - map['shared_with_id'] = Variable(sharedWithId); - map['in_timeline'] = Variable(inTimeline); - return map; - } - - factory PartnerEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return PartnerEntityData( - sharedById: serializer.fromJson(json['sharedById']), - sharedWithId: serializer.fromJson(json['sharedWithId']), - inTimeline: serializer.fromJson(json['inTimeline']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'sharedById': serializer.toJson(sharedById), - 'sharedWithId': serializer.toJson(sharedWithId), - 'inTimeline': serializer.toJson(inTimeline), - }; - } - - PartnerEntityData copyWith( - {String? sharedById, String? sharedWithId, bool? inTimeline}) => - PartnerEntityData( - sharedById: sharedById ?? this.sharedById, - sharedWithId: sharedWithId ?? this.sharedWithId, - inTimeline: inTimeline ?? this.inTimeline, - ); - PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { - return PartnerEntityData( - sharedById: - data.sharedById.present ? data.sharedById.value : this.sharedById, - sharedWithId: data.sharedWithId.present - ? data.sharedWithId.value - : this.sharedWithId, - inTimeline: - data.inTimeline.present ? data.inTimeline.value : this.inTimeline, - ); - } - - @override - String toString() { - return (StringBuffer('PartnerEntityData(') - ..write('sharedById: $sharedById, ') - ..write('sharedWithId: $sharedWithId, ') - ..write('inTimeline: $inTimeline') - ..write(')')) - .toString(); - } - - @override - int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is PartnerEntityData && - other.sharedById == this.sharedById && - other.sharedWithId == this.sharedWithId && - other.inTimeline == this.inTimeline); -} - -class PartnerEntityCompanion extends UpdateCompanion { - final Value sharedById; - final Value sharedWithId; - final Value inTimeline; - const PartnerEntityCompanion({ - this.sharedById = const Value.absent(), - this.sharedWithId = const Value.absent(), - this.inTimeline = const Value.absent(), - }); - PartnerEntityCompanion.insert({ - required String sharedById, - required String sharedWithId, - this.inTimeline = const Value.absent(), - }) : sharedById = Value(sharedById), - sharedWithId = Value(sharedWithId); - static Insertable custom({ - Expression? sharedById, - Expression? sharedWithId, - Expression? inTimeline, - }) { - return RawValuesInsertable({ - if (sharedById != null) 'shared_by_id': sharedById, - if (sharedWithId != null) 'shared_with_id': sharedWithId, - if (inTimeline != null) 'in_timeline': inTimeline, - }); - } - - PartnerEntityCompanion copyWith( - {Value? sharedById, - Value? sharedWithId, - Value? inTimeline}) { - return PartnerEntityCompanion( - sharedById: sharedById ?? this.sharedById, - sharedWithId: sharedWithId ?? this.sharedWithId, - inTimeline: inTimeline ?? this.inTimeline, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (sharedById.present) { - map['shared_by_id'] = Variable(sharedById.value); - } - if (sharedWithId.present) { - map['shared_with_id'] = Variable(sharedWithId.value); - } - if (inTimeline.present) { - map['in_timeline'] = Variable(inTimeline.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('PartnerEntityCompanion(') - ..write('sharedById: $sharedById, ') - ..write('sharedWithId: $sharedWithId, ') - ..write('inTimeline: $inTimeline') - ..write(')')) - .toString(); - } -} - class LocalAlbumEntity extends Table with TableInfo { @override @@ -2498,6 +2113,391 @@ class LocalAlbumAssetEntityCompanion } } +class UserMetadataEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserMetadataEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn key = GeneratedColumn( + 'key', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn value = GeneratedColumn( + 'value', aliasedName, false, + type: DriftSqlType.blob, requiredDuringInsert: true); + @override + List get $columns => [userId, key, value]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_metadata_entity'; + @override + Set get $primaryKey => {userId, key}; + @override + UserMetadataEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserMetadataEntityData( + userId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, + key: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}key'])!, + value: attachedDatabase.typeMapping + .read(DriftSqlType.blob, data['${effectivePrefix}value'])!, + ); + } + + @override + UserMetadataEntity createAlias(String alias) { + return UserMetadataEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserMetadataEntityData extends DataClass + implements Insertable { + final String userId; + final int key; + final Uint8List value; + const UserMetadataEntityData( + {required this.userId, required this.key, required this.value}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['user_id'] = Variable(userId); + map['key'] = Variable(key); + map['value'] = Variable(value); + return map; + } + + factory UserMetadataEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserMetadataEntityData( + userId: serializer.fromJson(json['userId']), + key: serializer.fromJson(json['key']), + value: serializer.fromJson(json['value']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'userId': serializer.toJson(userId), + 'key': serializer.toJson(key), + 'value': serializer.toJson(value), + }; + } + + UserMetadataEntityData copyWith( + {String? userId, int? key, Uint8List? value}) => + UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { + return UserMetadataEntityData( + userId: data.userId.present ? data.userId.value : this.userId, + key: data.key.present ? data.key.value : this.key, + value: data.value.present ? data.value.value : this.value, + ); + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityData(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(userId, key, $driftBlobEquality.hash(value)); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserMetadataEntityData && + other.userId == this.userId && + other.key == this.key && + $driftBlobEquality.equals(other.value, this.value)); +} + +class UserMetadataEntityCompanion + extends UpdateCompanion { + final Value userId; + final Value key; + final Value value; + const UserMetadataEntityCompanion({ + this.userId = const Value.absent(), + this.key = const Value.absent(), + this.value = const Value.absent(), + }); + UserMetadataEntityCompanion.insert({ + required String userId, + required int key, + required Uint8List value, + }) : userId = Value(userId), + key = Value(key), + value = Value(value); + static Insertable custom({ + Expression? userId, + Expression? key, + Expression? value, + }) { + return RawValuesInsertable({ + if (userId != null) 'user_id': userId, + if (key != null) 'key': key, + if (value != null) 'value': value, + }); + } + + UserMetadataEntityCompanion copyWith( + {Value? userId, Value? key, Value? value}) { + return UserMetadataEntityCompanion( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (key.present) { + map['key'] = Variable(key.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityCompanion(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } +} + +class PartnerEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PartnerEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("in_timeline" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + @override + List get $columns => [sharedById, sharedWithId, inTimeline]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'partner_entity'; + @override + Set get $primaryKey => {sharedById, sharedWithId}; + @override + PartnerEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PartnerEntityData( + sharedById: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}shared_by_id'])!, + sharedWithId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}shared_with_id'])!, + inTimeline: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!, + ); + } + + @override + PartnerEntity createAlias(String alias) { + return PartnerEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PartnerEntityData extends DataClass + implements Insertable { + final String sharedById; + final String sharedWithId; + final bool inTimeline; + const PartnerEntityData( + {required this.sharedById, + required this.sharedWithId, + required this.inTimeline}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['shared_by_id'] = Variable(sharedById); + map['shared_with_id'] = Variable(sharedWithId); + map['in_timeline'] = Variable(inTimeline); + return map; + } + + factory PartnerEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PartnerEntityData( + sharedById: serializer.fromJson(json['sharedById']), + sharedWithId: serializer.fromJson(json['sharedWithId']), + inTimeline: serializer.fromJson(json['inTimeline']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'sharedById': serializer.toJson(sharedById), + 'sharedWithId': serializer.toJson(sharedWithId), + 'inTimeline': serializer.toJson(inTimeline), + }; + } + + PartnerEntityData copyWith( + {String? sharedById, String? sharedWithId, bool? inTimeline}) => + PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { + return PartnerEntityData( + sharedById: + data.sharedById.present ? data.sharedById.value : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: + data.inTimeline.present ? data.inTimeline.value : this.inTimeline, + ); + } + + @override + String toString() { + return (StringBuffer('PartnerEntityData(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PartnerEntityData && + other.sharedById == this.sharedById && + other.sharedWithId == this.sharedWithId && + other.inTimeline == this.inTimeline); +} + +class PartnerEntityCompanion extends UpdateCompanion { + final Value sharedById; + final Value sharedWithId; + final Value inTimeline; + const PartnerEntityCompanion({ + this.sharedById = const Value.absent(), + this.sharedWithId = const Value.absent(), + this.inTimeline = const Value.absent(), + }); + PartnerEntityCompanion.insert({ + required String sharedById, + required String sharedWithId, + this.inTimeline = const Value.absent(), + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); + static Insertable custom({ + Expression? sharedById, + Expression? sharedWithId, + Expression? inTimeline, + }) { + return RawValuesInsertable({ + if (sharedById != null) 'shared_by_id': sharedById, + if (sharedWithId != null) 'shared_with_id': sharedWithId, + if (inTimeline != null) 'in_timeline': inTimeline, + }); + } + + PartnerEntityCompanion copyWith( + {Value? sharedById, + Value? sharedWithId, + Value? inTimeline}) { + return PartnerEntityCompanion( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sharedById.present) { + map['shared_by_id'] = Variable(sharedById.value); + } + if (sharedWithId.present) { + map['shared_with_id'] = Variable(sharedWithId.value); + } + if (inTimeline.present) { + map['in_timeline'] = Variable(inTimeline.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PartnerEntityCompanion(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } +} + class RemoteExifEntity extends Table with TableInfo { @override @@ -5467,8 +5467,11 @@ class DatabaseAtV4 extends GeneratedDatabase { DatabaseAtV4(QueryExecutor e) : super(e); late final UserEntity userEntity = UserEntity(this); late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); - late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); late final StackEntity stackEntity = StackEntity(this); + late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); + late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); late final Index idxLocalAssetChecksum = Index('idx_local_asset_checksum', 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); late final Index uQRemoteAssetOwnerChecksum = Index( @@ -5478,9 +5481,6 @@ class DatabaseAtV4 extends GeneratedDatabase { 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); late final PartnerEntity partnerEntity = PartnerEntity(this); - late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); - late final LocalAlbumAssetEntity localAlbumAssetEntity = - LocalAlbumAssetEntity(this); late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = @@ -5498,15 +5498,15 @@ class DatabaseAtV4 extends GeneratedDatabase { List get allSchemaEntities => [ userEntity, remoteAssetEntity, - localAssetEntity, stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, idxLocalAssetChecksum, uQRemoteAssetOwnerChecksum, idxRemoteAssetChecksum, userMetadataEntity, partnerEntity, - localAlbumEntity, - localAlbumAssetEntity, remoteExifEntity, remoteAlbumEntity, remoteAlbumAssetEntity, From 1a35a01149fda1d33470905203e3524c6db944cd Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 23 Jul 2025 09:20:52 -0500 Subject: [PATCH 028/748] feat: drift manual upload (#20101) --- i18n/en.json | 3 ++ .../repositories/timeline.repository.dart | 5 +++ mobile/lib/main.dart | 13 +++++++ .../upload_action_button.widget.dart | 36 ++++++++++++++++++- .../asset_viewer/bottom_bar.widget.dart | 3 ++ .../asset_viewer/bottom_sheet.widget.dart | 2 +- .../archive_bottom_sheet.widget.dart | 2 +- .../favorite_bottom_sheet.widget.dart | 2 +- .../general_bottom_sheet.widget.dart | 2 +- .../local_album_bottom_sheet.widget.dart | 2 +- .../remote_album_bottom_sheet.widget.dart | 2 +- .../infrastructure/action.provider.dart | 18 ++++++++++ .../lib/repositories/upload.repository.dart | 5 +++ mobile/lib/services/drift_backup.service.dart | 28 +++++++++++++-- 14 files changed, 114 insertions(+), 9 deletions(-) diff --git a/i18n/en.json b/i18n/en.json index 54c7ca6f1b..e2e0fd3c4f 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1941,11 +1941,13 @@ "updated_at": "Updated", "updated_password": "Updated password", "upload": "Upload", + "upload_action_prompt": "{count} queued for upload", "upload_concurrency": "Upload concurrency", "upload_details": "Upload Details", "upload_dialog_info": "Do you want to backup the selected Asset(s) to the server?", "upload_dialog_title": "Upload Asset", "upload_errors": "Upload completed with {count, plural, one {# error} other {# errors}}, refresh the page to see new upload assets.", + "upload_finished": "Upload finished", "upload_progress": "Remaining {remaining, number} - Processed {processed, number}/{total, number}", "upload_skipped_duplicates": "Skipped {count, plural, one {# duplicate asset} other {# duplicate assets}}", "upload_status_duplicates": "Duplicates", @@ -1954,6 +1956,7 @@ "upload_success": "Upload success, refresh the page to see new upload assets.", "upload_to_immich": "Upload to Immich ({count})", "uploading": "Uploading", + "uploading_media": "Uploading media", "url": "URL", "usage": "Usage", "use_biometric": "Use biometric", diff --git a/mobile/lib/infrastructure/repositories/timeline.repository.dart b/mobile/lib/infrastructure/repositories/timeline.repository.dart index fe9ae1e60d..2916993a9b 100644 --- a/mobile/lib/infrastructure/repositories/timeline.repository.dart +++ b/mobile/lib/infrastructure/repositories/timeline.repository.dart @@ -141,6 +141,11 @@ class DriftTimelineRepository extends DriftDatabaseRepository { _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), useColumns: false, ), + leftOuterJoin( + _db.remoteAssetEntity, + _db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum), + useColumns: false, + ), ]) ..addColumns([assetCountExp, dateExp]) ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 9f39a89b33..b62fb180e5 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -203,6 +203,19 @@ class ImmichAppState extends ConsumerState ), progressBar: true, ); + + FileDownloader().configureNotificationForGroup( + kManualUploadGroup, + running: TaskNotification( + 'uploading_media'.tr(), + '${'file_name'.tr()}: {displayName}', + ), + complete: TaskNotification( + 'upload_finished'.tr(), + '${'file_name'.tr()}: {displayName}', + ), + progressBar: true, + ); } Future _deepLinkBuilder(PlatformDeepLink deepLink) async { diff --git a/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart index 66467e44a3..d67c952b1a 100644 --- a/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart @@ -1,16 +1,50 @@ import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; class UploadActionButton extends ConsumerWidget { - const UploadActionButton({super.key}); + final ActionSource source; + + const UploadActionButton({super.key, required this.source}); + + void _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = await ref.read(actionProvider.notifier).upload(source); + + final successMessage = 'upload_action_prompt'.t( + context: context, + args: {'count': result.count.toString()}, + ); + + if (context.mounted) { + ImmichToast.show( + context: context, + msg: result.success + ? successMessage + : 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: result.success ? ToastType.success : ToastType.error, + ); + + ref.read(multiSelectProvider.notifier).reset(); + } + } @override Widget build(BuildContext context, WidgetRef ref) { return BaseActionButton( iconData: Icons.backup_outlined, label: "upload".t(context: context), + onPressed: () => _onTap(context, ref), ); } } diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart index cb558804d2..65bab5acb8 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart @@ -5,6 +5,7 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; @@ -37,6 +38,8 @@ class ViewerBottomBar extends ConsumerWidget { final actions = [ const ShareActionButton(source: ActionSource.viewer), + if (asset.isLocalOnly) + const UploadActionButton(source: ActionSource.viewer), if (asset.hasRemote && isOwner) const ArchiveActionButton(source: ActionSource.viewer), ]; diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart index 0e426fde6d..9111469629 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart @@ -63,7 +63,7 @@ class AssetDetailBottomSheet extends ConsumerWidget { ], if (asset.storage == AssetState.local) ...[ const DeleteLocalActionButton(source: ActionSource.viewer), - const UploadActionButton(), + const UploadActionButton(source: ActionSource.timeline), ], ]; diff --git a/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart index deaaea0d39..76243cf803 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart @@ -53,7 +53,7 @@ class ArchiveBottomSheet extends ConsumerWidget { ], if (multiselect.hasLocal) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), - const UploadActionButton(), + const UploadActionButton(source: ActionSource.timeline), ], ], ); diff --git a/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart index 8199271bfe..3f3f933745 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart @@ -53,7 +53,7 @@ class FavoriteBottomSheet extends ConsumerWidget { ], if (multiselect.hasLocal) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), - const UploadActionButton(), + const UploadActionButton(source: ActionSource.timeline), ], ], ); diff --git a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart index d83b8e399d..c148861b43 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart @@ -56,7 +56,7 @@ class GeneralBottomSheet extends ConsumerWidget { ], if (multiselect.hasLocal) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), - const UploadActionButton(), + const UploadActionButton(source: ActionSource.timeline), ], ], ); diff --git a/mobile/lib/presentation/widgets/bottom_sheet/local_album_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/local_album_bottom_sheet.widget.dart index 2ad0fe7485..b1e87dfaea 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/local_album_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/local_album_bottom_sheet.widget.dart @@ -18,7 +18,7 @@ class LocalAlbumBottomSheet extends ConsumerWidget { actions: [ ShareActionButton(source: ActionSource.timeline), DeleteLocalActionButton(source: ActionSource.timeline), - UploadActionButton(), + UploadActionButton(source: ActionSource.timeline), ], ); } diff --git a/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart index 2e6047f0ba..ff77c79906 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart @@ -56,7 +56,7 @@ class RemoteAlbumBottomSheet extends ConsumerWidget { ], if (multiselect.hasLocal) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), - const UploadActionButton(), + const UploadActionButton(source: ActionSource.timeline), ], RemoveFromAlbumActionButton( source: ActionSource.timeline, diff --git a/mobile/lib/providers/infrastructure/action.provider.dart b/mobile/lib/providers/infrastructure/action.provider.dart index 419ba0f902..3102511295 100644 --- a/mobile/lib/providers/infrastructure/action.provider.dart +++ b/mobile/lib/providers/infrastructure/action.provider.dart @@ -5,6 +5,7 @@ import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asse import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/services/action.service.dart'; +import 'package:immich_mobile/services/drift_backup.service.dart'; import 'package:immich_mobile/services/timeline.service.dart'; import 'package:logging/logging.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -32,12 +33,14 @@ class ActionResult { class ActionNotifier extends Notifier { final Logger _logger = Logger('ActionNotifier'); late ActionService _service; + late DriftBackupService _backupService; ActionNotifier() : super(); @override void build() { _service = ref.watch(actionServiceProvider); + _backupService = ref.watch(driftBackupServiceProvider); } List _getRemoteIdsForSource(ActionSource source) { @@ -368,6 +371,21 @@ class ActionNotifier extends Notifier { ); } } + + Future upload(ActionSource source) async { + final assets = _getAssets(source).whereType().toList(); + try { + await _backupService.manualBackup(assets); + return ActionResult(count: assets.length, success: true); + } catch (error, stack) { + _logger.severe('Failed manually upload assets', error, stack); + return ActionResult( + count: assets.length, + success: false, + error: error.toString(), + ); + } + } } extension on Iterable { diff --git a/mobile/lib/repositories/upload.repository.dart b/mobile/lib/repositories/upload.repository.dart index b98eece656..6d75313a24 100644 --- a/mobile/lib/repositories/upload.repository.dart +++ b/mobile/lib/repositories/upload.repository.dart @@ -20,6 +20,11 @@ class UploadRepository { taskStatusCallback: (update) => onUploadStatus?.call(update), taskProgressCallback: (update) => onTaskProgress?.call(update), ); + FileDownloader().registerCallbacks( + group: kManualUploadGroup, + taskStatusCallback: (_) => {}, + taskProgressCallback: (_) => {}, + ); } void enqueueAll(List tasks) { diff --git a/mobile/lib/services/drift_backup.service.dart b/mobile/lib/services/drift_backup.service.dart index 7373643c95..ab878969a0 100644 --- a/mobile/lib/services/drift_backup.service.dart +++ b/mobile/lib/services/drift_backup.service.dart @@ -26,6 +26,7 @@ final driftBackupServiceProvider = Provider( ), ); +// TODO: Rename to UploadService after removing Isar class DriftBackupService { DriftBackupService( this._backupRepository, @@ -56,6 +57,24 @@ class DriftBackupService { return _backupRepository.getBackupCount(userId); } + Future manualBackup(List localAssets) async { + List tasks = []; + for (final asset in localAssets) { + final task = await _getUploadTask( + asset, + group: kManualUploadGroup, + priority: 1, // High priority after upload motion photo part + ); + if (task != null) { + tasks.add(task); + } + } + + if (tasks.isNotEmpty) { + _uploadService.enqueueTasks(tasks); + } + } + Future backup( String userId, void Function(EnqueueStatus status) onEnqueueTasks, @@ -147,7 +166,11 @@ class DriftBackupService { } } - Future _getUploadTask(LocalAsset asset) async { + Future _getUploadTask( + LocalAsset asset, { + String group = kBackupGroup, + int? priority, + }) async { final entity = await _storageRepository.getAssetEntityForAsset(asset); if (entity == null) { return null; @@ -193,7 +216,8 @@ class DriftBackupService { originalFileName: originalFileName, deviceAssetId: asset.id, metadata: metadata, - group: kBackupGroup, + group: group, + priority: priority, ); } From 0174de19dd712aa647289b0b9c4c161cae3a6628 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 23 Jul 2025 09:40:16 -0500 Subject: [PATCH 029/748] feat: export database option (#20098) --- i18n/en.json | 2 + .../beta_sync_settings.dart | 79 +++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/i18n/en.json b/i18n/en.json index e2e0fd3c4f..73338ff6cc 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1002,6 +1002,8 @@ "explorer": "Explorer", "export": "Export", "export_as_json": "Export as JSON", + "export_database": "Export Database", + "export_database_description": "Export the SQLite database", "extension": "Extension", "external": "External", "external_libraries": "External Libraries", diff --git a/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart b/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart index 3d37fb102b..e69717d3a4 100644 --- a/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart +++ b/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:drift/drift.dart' as drift_db; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -10,6 +12,9 @@ import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/providers/infrastructure/memory.provider.dart'; import 'package:immich_mobile/providers/sync_status.provider.dart'; import 'package:immich_mobile/widgets/settings/beta_sync_settings/entity_count_tile.dart'; +import 'package:path/path.dart' as path; +import 'package:path_provider/path_provider.dart'; +import 'package:share_plus/share_plus.dart'; class BetaSyncSettings extends HookConsumerWidget { const BetaSyncSettings({ @@ -69,6 +74,67 @@ class BetaSyncSettings extends HookConsumerWidget { }); } + Future exportDatabase() async { + try { + // WAL Checkpoint to ensure all changes are written to the database + await ref + .read(driftProvider) + .customStatement("pragma wal_checkpoint(truncate)"); + final documentsDir = await getApplicationDocumentsDirectory(); + final dbFile = File(path.join(documentsDir.path, 'immich.sqlite')); + + if (!await dbFile.exists()) { + if (context.mounted) { + context.scaffoldMessenger.showSnackBar( + SnackBar( + content: Text("Database file not found".t(context: context)), + ), + ); + } + return; + } + + final timestamp = DateTime.now().millisecondsSinceEpoch; + final exportFile = File( + path.join( + documentsDir.path, + 'immich_export_$timestamp.sqlite', + ), + ); + + await dbFile.copy(exportFile.path); + + await Share.shareXFiles( + [XFile(exportFile.path)], + text: 'Immich Database Export', + ); + + Future.delayed(const Duration(seconds: 30), () async { + if (await exportFile.exists()) { + await exportFile.delete(); + } + }); + + if (context.mounted) { + context.scaffoldMessenger.showSnackBar( + SnackBar( + content: + Text("Database exported successfully".t(context: context)), + ), + ); + } + } catch (e) { + if (context.mounted) { + context.scaffoldMessenger.showSnackBar( + SnackBar( + content: + Text("Failed to export database: $e".t(context: context)), + ), + ); + } + } + } + return FutureBuilder>( future: loadCounts(), builder: (context, snapshot) { @@ -232,6 +298,19 @@ class BetaSyncSettings extends HookConsumerWidget { ), const SizedBox(height: 24), _SectionHeaderText(text: "actions".t(context: context)), + ListTile( + title: Text( + "export_database".t(context: context), + style: const TextStyle( + fontWeight: FontWeight.w500, + ), + ), + subtitle: Text( + "export_database_description".t(context: context), + ), + leading: const Icon(Icons.download), + onTap: exportDatabase, + ), ListTile( title: Text( "reset_sqlite".t(context: context), From 0e1c8c2b8086bd2fc6a7433431e9c18c598eb6de Mon Sep 17 00:00:00 2001 From: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Wed, 23 Jul 2025 19:12:06 +0200 Subject: [PATCH 030/748] fix: ML recognition distance UI form validation (#20107) --- .../machine-learning-settings.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/lib/components/admin-page/settings/machine-learning-settings/machine-learning-settings.svelte b/web/src/lib/components/admin-page/settings/machine-learning-settings/machine-learning-settings.svelte index c120cb3750..acc920981b 100644 --- a/web/src/lib/components/admin-page/settings/machine-learning-settings/machine-learning-settings.svelte +++ b/web/src/lib/components/admin-page/settings/machine-learning-settings/machine-learning-settings.svelte @@ -183,7 +183,7 @@ label={$t('admin.machine_learning_min_detection_score')} description={$t('admin.machine_learning_min_detection_score_description')} bind:value={config.machineLearning.facialRecognition.minScore} - step="0.1" + step="0.01" min={0.1} max={1} disabled={disabled || !config.machineLearning.enabled || !config.machineLearning.facialRecognition.enabled} @@ -196,7 +196,7 @@ label={$t('admin.machine_learning_max_recognition_distance')} description={$t('admin.machine_learning_max_recognition_distance_description')} bind:value={config.machineLearning.facialRecognition.maxDistance} - step="0.1" + step="0.01" min={0.1} max={2} disabled={disabled || !config.machineLearning.enabled || !config.machineLearning.facialRecognition.enabled} From 2bead445bdb6c9717216e61ba8e1f7370e6653f1 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 23 Jul 2025 16:00:19 -0400 Subject: [PATCH 031/748] docs: remove outdated note (#20110) --- docs/docs/install/environment-variables.md | 31 ++++++++++------------ 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/docs/docs/install/environment-variables.md b/docs/docs/install/environment-variables.md index 8d5ab55049..939c42439d 100644 --- a/docs/docs/install/environment-variables.md +++ b/docs/docs/install/environment-variables.md @@ -29,29 +29,26 @@ These environment variables are used by the `docker-compose.yml` file and do **N ## General -| Variable | Description | Default | Containers | Workers | -| :---------------------------------- | :---------------------------------------------------------------------------------------- | :---------------------------------: | :----------------------- | :----------------- | -| `TZ` | Timezone | \*1 | server | microservices | -| `IMMICH_ENV` | Environment (production, development) | `production` | server, machine learning | api, microservices | -| `IMMICH_LOG_LEVEL` | Log level (verbose, debug, log, warn, error) | `log` | server, machine learning | api, microservices | -| `IMMICH_MEDIA_LOCATION` | Media location inside the container ⚠️**You probably shouldn't set this**\*2⚠️ | `/usr/src/app/upload`\*3 | server | api, microservices | -| `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices | -| `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | | -| `CPU_CORES` | Number of cores available to the Immich server | auto-detected CPU core count | server | | -| `IMMICH_API_METRICS_PORT` | Port for the OTEL metrics | `8081` | server | api | -| `IMMICH_MICROSERVICES_METRICS_PORT` | Port for the OTEL metrics | `8082` | server | microservices | -| `IMMICH_PROCESS_INVALID_IMAGES` | When `true`, generate thumbnails for invalid images | | server | microservices | -| `IMMICH_TRUSTED_PROXIES` | List of comma-separated IPs set as trusted proxies | | server | api | -| `IMMICH_IGNORE_MOUNT_CHECK_ERRORS` | See [System Integrity](/docs/administration/system-integrity) | | server | api, microservices | +| Variable | Description | Default | Containers | Workers | +| :---------------------------------- | :---------------------------------------------------------------------------------------- | :--------------------------: | :----------------------- | :----------------- | +| `TZ` | Timezone | \*1 | server | microservices | +| `IMMICH_ENV` | Environment (production, development) | `production` | server, machine learning | api, microservices | +| `IMMICH_LOG_LEVEL` | Log level (verbose, debug, log, warn, error) | `log` | server, machine learning | api, microservices | +| `IMMICH_MEDIA_LOCATION` | Media location inside the container ⚠️**You probably shouldn't set this**\*2⚠️ | `/usr/src/app/upload` | server | api, microservices | +| `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices | +| `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | | +| `CPU_CORES` | Number of cores available to the Immich server | auto-detected CPU core count | server | | +| `IMMICH_API_METRICS_PORT` | Port for the OTEL metrics | `8081` | server | api | +| `IMMICH_MICROSERVICES_METRICS_PORT` | Port for the OTEL metrics | `8082` | server | microservices | +| `IMMICH_PROCESS_INVALID_IMAGES` | When `true`, generate thumbnails for invalid images | | server | microservices | +| `IMMICH_TRUSTED_PROXIES` | List of comma-separated IPs set as trusted proxies | | server | api | +| `IMMICH_IGNORE_MOUNT_CHECK_ERRORS` | See [System Integrity](/docs/administration/system-integrity) | | server | api, microservices | \*1: `TZ` should be set to a `TZ identifier` from [this list][tz-list]. For example, `TZ="Etc/UTC"`. `TZ` is used by `exiftool` as a fallback in case the timezone cannot be determined from the image metadata. It is also used for logfile timestamps and cron job execution. \*2: This path is where the Immich code looks for the files, which is internal to the docker container. Setting it to a path on your host will certainly break things, you should use the `UPLOAD_LOCATION` variable instead. -\*3: With the default `WORKDIR` of `/usr/src/app`, this path will resolve to `/usr/src/app/upload`. -It only needs to be set if the Immich deployment method is changing. - ## Workers | Variable | Description | Default | Containers | From a6759221727c64c82d7329cf276095129098e33a Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 23 Jul 2025 16:52:59 -0400 Subject: [PATCH 032/748] fix: unset prewarn param (#20109) --- server/src/repositories/logging.repository.ts | 9 +++-- .../1750323941566-UnsetPrewarmDimParameter.ts | 35 +++++++++---------- .../1752759108283-ConvertToAbsolutePaths.ts | 3 +- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/server/src/repositories/logging.repository.ts b/server/src/repositories/logging.repository.ts index 1833168f3e..939ecb718f 100644 --- a/server/src/repositories/logging.repository.ts +++ b/server/src/repositories/logging.repository.ts @@ -85,8 +85,13 @@ export class LoggingRepository { this.logger = new MyConsoleLogger(cls, { context: LoggingRepository.name, color: !noColor }); } - static create() { - return new LoggingRepository(undefined, undefined); + static create(context?: string) { + const logger = new LoggingRepository(undefined, undefined); + if (context) { + logger.setContext(context); + } + + return logger; } setAppName(name: string): void { diff --git a/server/src/schema/migrations/1750323941566-UnsetPrewarmDimParameter.ts b/server/src/schema/migrations/1750323941566-UnsetPrewarmDimParameter.ts index 776b51d1db..d687694490 100644 --- a/server/src/schema/migrations/1750323941566-UnsetPrewarmDimParameter.ts +++ b/server/src/schema/migrations/1750323941566-UnsetPrewarmDimParameter.ts @@ -1,25 +1,22 @@ import { Kysely, sql } from 'kysely'; +import { LoggingRepository } from 'src/repositories/logging.repository'; -export async function up(qb: Kysely): Promise { - type Conf = { db: string; guc: string[] }; - const res = await sql` - select current_database() db, to_json(setconfig) guc - from pg_db_role_setting - where setdatabase = (select oid from pg_database where datname = current_database()) - and setrole = 0;`.execute(qb); - if (res.rows.length === 0) { - return; - } +const logger = LoggingRepository.create('Migrations'); - const { db, guc } = res.rows[0]; - await sql.raw(`alter database "${db}" reset all;`).execute(qb); - for (const parameter of guc) { - const [key, value] = parameter.split('='); - if (key === 'vchordrq.prewarm_dim') { - continue; - } - await sql.raw(`alter database "${db}" set ${key} to ${value};`).execute(qb); +export async function up(db: Kysely): Promise { + const { rows } = await sql<{ db: string }>`SELECT current_database() as db;`.execute(db); + const databaseName = rows[0].db; + try { + await sql.raw(`ALTER DATABASE "${databaseName}" RESET vchordrq.prewarm_dim;`).execute(db); + } catch { + logger.warn('Failed to reset vchordrq.prewarm_dim, skipping'); } } -export async function down(): Promise {} +export async function down(db: Kysely): Promise { + const { rows } = await sql<{ db: string }>`SELECT current_database() as db;`.execute(db); + const databaseName = rows[0].db; + await sql + .raw(`ALTER DATABASE "${databaseName}" SET vchordrq.prewarm_dim = '512,640,768,1024,1152,1536';`) + .execute(db); +} diff --git a/server/src/schema/migrations/1752759108283-ConvertToAbsolutePaths.ts b/server/src/schema/migrations/1752759108283-ConvertToAbsolutePaths.ts index 68b0c7931e..839af40cf5 100644 --- a/server/src/schema/migrations/1752759108283-ConvertToAbsolutePaths.ts +++ b/server/src/schema/migrations/1752759108283-ConvertToAbsolutePaths.ts @@ -1,8 +1,7 @@ import { Kysely, sql } from 'kysely'; import { LoggingRepository } from 'src/repositories/logging.repository'; -const logger = LoggingRepository.create(); -logger.setContext('Migrations'); +const logger = LoggingRepository.create('Migrations'); export async function up(db: Kysely): Promise { if (process.env.IMMICH_MEDIA_LOCATION) { From bc8cb9b671412ff8aa2999a74ee45d5ff272b058 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 23 Jul 2025 16:56:38 -0400 Subject: [PATCH 033/748] fix: default route permission (#20113) --- server/src/services/auth.service.spec.ts | 32 ++++++++++++++++++------ server/src/services/auth.service.ts | 7 +++--- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/server/src/services/auth.service.spec.ts b/server/src/services/auth.service.spec.ts index 129877bbdd..9f678162c6 100644 --- a/server/src/services/auth.service.spec.ts +++ b/server/src/services/auth.service.spec.ts @@ -459,18 +459,34 @@ describe(AuthService.name, () => { mocks.apiKey.getKey.mockResolvedValue({ ...authApiKey, user: authUser }); - await expect( - sut.authenticate({ - headers: { 'x-api-key': 'auth_token' }, - queryParams: {}, - metadata: { adminRoute: false, sharedLinkRoute: false, uri: 'test', permission: Permission.AssetRead }, - }), - ).rejects.toBeInstanceOf(ForbiddenException); + const result = sut.authenticate({ + headers: { 'x-api-key': 'auth_token' }, + queryParams: {}, + metadata: { adminRoute: false, sharedLinkRoute: false, uri: 'test', permission: Permission.AssetRead }, + }); + + await expect(result).rejects.toBeInstanceOf(ForbiddenException); + await expect(result).rejects.toThrow('Missing required permission: asset.read'); + }); + + it('should default to requiring the all permission when omitted', async () => { + const authUser = factory.authUser(); + const authApiKey = factory.authApiKey({ permissions: [Permission.AssetRead] }); + + mocks.apiKey.getKey.mockResolvedValue({ ...authApiKey, user: authUser }); + + const result = sut.authenticate({ + headers: { 'x-api-key': 'auth_token' }, + queryParams: {}, + metadata: { adminRoute: false, sharedLinkRoute: false, uri: 'test' }, + }); + await expect(result).rejects.toBeInstanceOf(ForbiddenException); + await expect(result).rejects.toThrow('Missing required permission: all'); }); it('should return an auth dto', async () => { const authUser = factory.authUser(); - const authApiKey = factory.authApiKey({ permissions: [] }); + const authApiKey = factory.authApiKey({ permissions: [Permission.All] }); mocks.apiKey.getKey.mockResolvedValue({ ...authApiKey, user: authUser }); diff --git a/server/src/services/auth.service.ts b/server/src/services/auth.service.ts index a5b0de25cd..f757bf5a3e 100644 --- a/server/src/services/auth.service.ts +++ b/server/src/services/auth.service.ts @@ -174,7 +174,8 @@ export class AuthService extends BaseService { async authenticate({ headers, queryParams, metadata }: ValidateRequest): Promise { const authDto = await this.validate({ headers, queryParams }); - const { adminRoute, sharedLinkRoute, permission, uri } = metadata; + const { adminRoute, sharedLinkRoute, uri } = metadata; + const requestedPermission = metadata.permission ?? Permission.All; if (!authDto.user.isAdmin && adminRoute) { this.logger.warn(`Denied access to admin only route: ${uri}`); @@ -186,8 +187,8 @@ export class AuthService extends BaseService { throw new ForbiddenException('Forbidden'); } - if (authDto.apiKey && permission && !isGranted({ requested: [permission], current: authDto.apiKey.permissions })) { - throw new ForbiddenException(`Missing required permission: ${permission}`); + if (authDto.apiKey && !isGranted({ requested: [requestedPermission], current: authDto.apiKey.permissions })) { + throw new ForbiddenException(`Missing required permission: ${requestedPermission}`); } return authDto; From c1c9f30ea4b7879febc98188ba89a4a3f2ef1bc9 Mon Sep 17 00:00:00 2001 From: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Wed, 23 Jul 2025 22:56:56 +0200 Subject: [PATCH 034/748] chore: migrate to immich/ui confirm modal (#20114) --- web/package-lock.json | 8 +-- web/package.json | 2 +- .../asset-viewer/editor/editor-panel.svelte | 3 +- .../photos-page/delete-asset-dialog.svelte | 3 +- .../shared-components/change-date.spec.ts | 4 +- .../shared-components/change-date.svelte | 4 +- .../shared-components/change-location.svelte | 2 +- web/src/lib/managers/modal-manager.svelte.ts | 2 +- .../AssetUpdateDecriptionConfirmModal.svelte | 3 +- web/src/lib/modals/ConfirmModal.svelte | 52 ------------------- web/src/lib/modals/JobCreateModal.svelte | 2 +- .../lib/modals/UserDeleteConfirmModal.svelte | 3 +- 12 files changed, 15 insertions(+), 73 deletions(-) delete mode 100644 web/src/lib/modals/ConfirmModal.svelte diff --git a/web/package-lock.json b/web/package-lock.json index 51f60a83c3..46b0a059ed 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", "@immich/sdk": "file:../open-api/typescript-sdk", - "@immich/ui": "^0.23.2", + "@immich/ui": "^0.23.5", "@mapbox/mapbox-gl-rtl-text": "0.2.3", "@mdi/js": "^7.4.47", "@photo-sphere-viewer/core": "^5.11.5", @@ -1357,9 +1357,9 @@ "link": true }, "node_modules/@immich/ui": { - "version": "0.23.3", - "resolved": "https://registry.npmjs.org/@immich/ui/-/ui-0.23.3.tgz", - "integrity": "sha512-YbYJSv3HqDu2+6MmiHhLThSessZ6HkoVOWun/ZoGb8mKj5x/ZZ4AyXGPIqbyKTamsjzbcD9FInij70G+m4egkg==", + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@immich/ui/-/ui-0.23.5.tgz", + "integrity": "sha512-1wlFMmfDmtGC+Kcc8cYTT00mQaSumR41KEOOOmVn5Rw/8z9pUhpNY8mGl1AxY4qhtnaz+G3dH6vowYzL23D+YQ==", "license": "GNU Affero General Public License version 3", "dependencies": { "@mdi/js": "^7.4.47", diff --git a/web/package.json b/web/package.json index 753b0a15a6..7e1b769c52 100644 --- a/web/package.json +++ b/web/package.json @@ -28,7 +28,7 @@ "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", "@immich/sdk": "file:../open-api/typescript-sdk", - "@immich/ui": "^0.23.2", + "@immich/ui": "^0.23.5", "@mapbox/mapbox-gl-rtl-text": "0.2.3", "@mdi/js": "^7.4.47", "@photo-sphere-viewer/core": "^5.11.5", diff --git a/web/src/lib/components/asset-viewer/editor/editor-panel.svelte b/web/src/lib/components/asset-viewer/editor/editor-panel.svelte index f4d5c69c62..203f1c6587 100644 --- a/web/src/lib/components/asset-viewer/editor/editor-panel.svelte +++ b/web/src/lib/components/asset-viewer/editor/editor-panel.svelte @@ -1,10 +1,9 @@ - - onClose(false)} {size}> - - {#if promptSnippet}{@render promptSnippet()}{:else} -

{prompt}

- {/if} -
- - - - - - - -
diff --git a/web/src/lib/modals/JobCreateModal.svelte b/web/src/lib/modals/JobCreateModal.svelte index dbb97fdcf7..95751cbb98 100644 --- a/web/src/lib/modals/JobCreateModal.svelte +++ b/web/src/lib/modals/JobCreateModal.svelte @@ -4,9 +4,9 @@ notificationController, NotificationType, } from '$lib/components/shared-components/notification/notification'; - import ConfirmModal from '$lib/modals/ConfirmModal.svelte'; import { handleError } from '$lib/utils/handle-error'; import { createJob, ManualJobName } from '@immich/sdk'; + import { ConfirmModal } from '@immich/ui'; import { t } from 'svelte-i18n'; type Props = { onClose: (confirmed: boolean) => void }; diff --git a/web/src/lib/modals/UserDeleteConfirmModal.svelte b/web/src/lib/modals/UserDeleteConfirmModal.svelte index 3fde7a7ef9..bbb045ecb7 100644 --- a/web/src/lib/modals/UserDeleteConfirmModal.svelte +++ b/web/src/lib/modals/UserDeleteConfirmModal.svelte @@ -1,10 +1,9 @@ - -
-
- {#if showLogo} - - {:else if icon} - - {/if} -

- {title} -

-
- - -
diff --git a/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte b/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte index b079ecb241..b1746932b1 100644 --- a/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte +++ b/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte @@ -3,13 +3,12 @@ import { focusTrap } from '$lib/actions/focus-trap'; import Icon from '$lib/components/elements/icon.svelte'; import { AppRoute } from '$lib/constants'; - import { modalManager } from '$lib/managers/modal-manager.svelte'; import AvatarEditModal from '$lib/modals/AvatarEditModal.svelte'; import HelpAndFeedbackModal from '$lib/modals/HelpAndFeedbackModal.svelte'; import { user } from '$lib/stores/user.store'; import { userInteraction } from '$lib/stores/user.svelte'; import { getAboutInfo, type ServerAboutResponseDto } from '@immich/sdk'; - import { Button, IconButton } from '@immich/ui'; + import { Button, IconButton, modalManager } from '@immich/ui'; import { mdiCog, mdiLogout, mdiPencil, mdiWrench } from '@mdi/js'; import { onMount } from 'svelte'; import { t } from 'svelte-i18n'; diff --git a/web/src/lib/components/shared-components/search-bar/search-bar.svelte b/web/src/lib/components/shared-components/search-bar/search-bar.svelte index 9f36431f19..4c807e7652 100644 --- a/web/src/lib/components/shared-components/search-bar/search-bar.svelte +++ b/web/src/lib/components/shared-components/search-bar/search-bar.svelte @@ -3,14 +3,13 @@ import { focusOutside } from '$lib/actions/focus-outside'; import { shortcuts } from '$lib/actions/shortcut'; import { AppRoute } from '$lib/constants'; - import { modalManager } from '$lib/managers/modal-manager.svelte'; import SearchFilterModal from '$lib/modals/SearchFilterModal.svelte'; import { searchStore } from '$lib/stores/search.svelte'; import { handlePromiseError } from '$lib/utils'; import { generateId } from '$lib/utils/generate-id'; import { getMetadataSearchQuery } from '$lib/utils/metadata-search'; import type { MetadataSearchDto, SmartSearchDto } from '@immich/sdk'; - import { IconButton } from '@immich/ui'; + import { IconButton, modalManager } from '@immich/ui'; import { mdiClose, mdiMagnify, mdiTune } from '@mdi/js'; import { onDestroy, tick } from 'svelte'; import { t } from 'svelte-i18n'; diff --git a/web/src/lib/components/shared-components/side-bar/purchase-info.svelte b/web/src/lib/components/shared-components/side-bar/purchase-info.svelte index 627292ea1b..f21cc7de4d 100644 --- a/web/src/lib/components/shared-components/side-bar/purchase-info.svelte +++ b/web/src/lib/components/shared-components/side-bar/purchase-info.svelte @@ -5,7 +5,6 @@ import Portal from '$lib/components/shared-components/portal/portal.svelte'; import SupporterBadge from '$lib/components/shared-components/side-bar/supporter-badge.svelte'; import { AppRoute } from '$lib/constants'; - import { modalManager } from '$lib/managers/modal-manager.svelte'; import PurchaseModal from '$lib/modals/PurchaseModal.svelte'; import { purchaseStore } from '$lib/stores/purchase.store'; import { preferences } from '$lib/stores/user.store'; @@ -13,11 +12,11 @@ import { handleError } from '$lib/utils/handle-error'; import { getButtonVisibility } from '$lib/utils/purchase-utils'; import { updateMyPreferences } from '@immich/sdk'; - import { Button, IconButton } from '@immich/ui'; + import { Button, IconButton, modalManager } from '@immich/ui'; import { mdiClose, mdiInformationOutline } from '@mdi/js'; import { t } from 'svelte-i18n'; - import { fade } from 'svelte/transition'; import { SvelteDate } from 'svelte/reactivity'; + import { fade } from 'svelte/transition'; let showMessage = $state(false); let hoverMessage = $state(false); diff --git a/web/src/lib/components/shared-components/side-bar/server-status.svelte b/web/src/lib/components/shared-components/side-bar/server-status.svelte index 0a9f3d9d8c..3c3da8eb5f 100644 --- a/web/src/lib/components/shared-components/side-bar/server-status.svelte +++ b/web/src/lib/components/shared-components/side-bar/server-status.svelte @@ -1,6 +1,5 @@ -
+
{ + value = value.toLowerCase(); + return ([title, items]: [string, Permission[]]) => { + return title.toLowerCase().includes(value) || items.some((item) => item.toLowerCase().includes(value)); + }; + }; interface Props { apiKey: { name: string; permissions: Permission[] }; @@ -20,137 +26,26 @@ } let { apiKey = $bindable(), title, cancelText = $t('cancel'), submitText = $t('save'), onClose }: Props = $props(); + let name = $derived(apiKey.name); let selectedItems: Permission[] = $state(apiKey.permissions); let selectAllItems = $derived(selectedItems.length === Object.keys(Permission).length - 1); - const permissions: Map = new SvelteMap(); + const permissions: Record = {}; + for (const permission of Object.values(Permission)) { + if (permission === Permission.All) { + continue; + } - permissions.set('activity', [ - Permission.ActivityCreate, - Permission.ActivityRead, - Permission.ActivityUpdate, - Permission.ActivityDelete, - Permission.ActivityStatistics, - ]); + const [group] = permission.split('.'); + if (!permissions[group]) { + permissions[group] = []; + } + permissions[group].push(permission); + } - permissions.set('api_key', [ - Permission.ApiKeyCreate, - Permission.ApiKeyRead, - Permission.ApiKeyUpdate, - Permission.ApiKeyDelete, - ]); - - permissions.set('asset', [ - Permission.AssetRead, - Permission.AssetUpdate, - Permission.AssetDelete, - Permission.AssetShare, - Permission.AssetView, - Permission.AssetDownload, - Permission.AssetUpload, - ]); - - permissions.set('album', [ - Permission.AlbumCreate, - Permission.AlbumRead, - Permission.AlbumUpdate, - Permission.AlbumDelete, - Permission.AlbumStatistics, - - Permission.AlbumAddAsset, - Permission.AlbumRemoveAsset, - Permission.AlbumShare, - Permission.AlbumDownload, - ]); - - permissions.set('auth_device', [Permission.AuthDeviceDelete]); - - permissions.set('archive', [Permission.ArchiveRead]); - - permissions.set('face', [Permission.FaceCreate, Permission.FaceRead, Permission.FaceUpdate, Permission.FaceDelete]); - - permissions.set('library', [ - Permission.LibraryCreate, - Permission.LibraryRead, - Permission.LibraryUpdate, - Permission.LibraryDelete, - Permission.LibraryStatistics, - ]); - - permissions.set('timeline', [Permission.TimelineRead, Permission.TimelineDownload]); - - permissions.set('memory', [ - Permission.MemoryCreate, - Permission.MemoryRead, - Permission.MemoryUpdate, - Permission.MemoryDelete, - ]); - - permissions.set('notification', [ - Permission.NotificationCreate, - Permission.NotificationRead, - Permission.NotificationUpdate, - Permission.NotificationDelete, - ]); - - permissions.set('partner', [ - Permission.PartnerCreate, - Permission.PartnerRead, - Permission.PartnerUpdate, - Permission.PartnerDelete, - ]); - - permissions.set('person', [ - Permission.PersonCreate, - Permission.PersonRead, - Permission.PersonUpdate, - Permission.PersonDelete, - Permission.PersonStatistics, - Permission.PersonMerge, - Permission.PersonReassign, - ]); - - permissions.set('session', [ - Permission.SessionCreate, - Permission.SessionRead, - Permission.SessionUpdate, - Permission.SessionDelete, - Permission.SessionLock, - ]); - - permissions.set('sharedLink', [ - Permission.SharedLinkCreate, - Permission.SharedLinkRead, - Permission.SharedLinkUpdate, - Permission.SharedLinkDelete, - ]); - - permissions.set('stack', [ - Permission.StackCreate, - Permission.StackRead, - Permission.StackUpdate, - Permission.StackDelete, - ]); - - permissions.set('systemConfig', [Permission.SystemConfigRead, Permission.SystemConfigUpdate]); - - permissions.set('systemMetadata', [Permission.SystemMetadataRead, Permission.SystemMetadataUpdate]); - - permissions.set('tag', [ - Permission.TagCreate, - Permission.TagRead, - Permission.TagUpdate, - Permission.TagDelete, - Permission.TagAsset, - ]); - - permissions.set('adminUser', [ - Permission.AdminUserCreate, - Permission.AdminUserRead, - Permission.AdminUserUpdate, - Permission.AdminUserDelete, - ]); + let searchValue = $state(''); + let filteredResults = $derived(Object.entries(permissions).filter(matches(searchValue))); const handleSelectItems = (permissions: Permission[]) => { selectedItems = Array.from(new Set([...selectedItems, ...permissions])); @@ -177,9 +72,9 @@ }); } else { if (selectAllItems) { - onClose({ name: apiKey.name, permissions: [Permission.All] }); + onClose({ name, permissions: [Permission.All] }); } else { - onClose({ name: apiKey.name, permissions: selectedItems }); + onClose({ name, permissions: selectedItems }); } } }; @@ -200,22 +95,37 @@
- - + + +
- -
- -
{/await} {:then { default: Map }} - + {/await}
From 7e7b8da1286992072bf08f9722411d096acd2356 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Mon, 28 Jul 2025 12:41:22 -0400 Subject: [PATCH 086/748] fix: debug source maps (#20363) --- .vscode/launch.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index ed3da9f667..0cc9b256ca 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,7 +7,7 @@ "restart": true, "port": 9231, "name": "Immich API Server", - "remoteRoot": "/usr/src/app", + "remoteRoot": "/usr/src/app/server", "localRoot": "${workspaceFolder}/server" }, { @@ -16,7 +16,7 @@ "restart": true, "port": 9230, "name": "Immich Workers", - "remoteRoot": "/usr/src/app", + "remoteRoot": "/usr/src/app/server", "localRoot": "${workspaceFolder}/server" } ] From 16b14b390ff7128dc3503b83d2b38c45a3e5a361 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Mon, 28 Jul 2025 13:30:49 -0400 Subject: [PATCH 087/748] fix: file samples (#20364) --- server/src/queries/asset.repository.sql | 2 ++ server/src/repositories/asset.repository.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/server/src/queries/asset.repository.sql b/server/src/queries/asset.repository.sql index 9425bd9a11..185afa562b 100644 --- a/server/src/queries/asset.repository.sql +++ b/server/src/queries/asset.repository.sql @@ -189,6 +189,8 @@ select ) as "files" from "asset" +where + "asset"."libraryId" is null limit 3 diff --git a/server/src/repositories/asset.repository.ts b/server/src/repositories/asset.repository.ts index edbafaa22d..286a66e9af 100644 --- a/server/src/repositories/asset.repository.ts +++ b/server/src/repositories/asset.repository.ts @@ -349,6 +349,7 @@ export class AssetRepository { 'files', ), ]) + .where('asset.libraryId', 'is', null) .limit(sql.lit(3)) .execute(); } From 9b3718120b055813cf2ffc7ca65eced276ed08b7 Mon Sep 17 00:00:00 2001 From: Jed-Giblin Date: Mon, 28 Jul 2025 14:16:55 -0400 Subject: [PATCH 088/748] feat: shared links custom URL (#19999) * feat: custom url for shared links * feat: use a separate route and query param --------- Co-authored-by: Jason Rasmussen --- i18n/en.json | 4 +- mobile/openapi/lib/api/albums_api.dart | 26 +++- mobile/openapi/lib/api/assets_api.dart | 78 +++++++--- mobile/openapi/lib/api/download_api.dart | 26 +++- mobile/openapi/lib/api/shared_links_api.dart | 39 +++-- mobile/openapi/lib/api/timeline_api.dart | 26 +++- .../lib/model/shared_link_create_dto.dart | 25 ++-- .../lib/model/shared_link_edit_dto.dart | 29 ++-- .../lib/model/shared_link_response_dto.dart | 14 +- open-api/immich-openapi-specs.json | 137 ++++++++++++++++++ open-api/typescript-sdk/src/fetch-client.ts | 93 ++++++++---- server/src/database.ts | 1 + server/src/dtos/shared-link.dto.ts | 32 ++-- server/src/enum.ts | 2 + server/src/middleware/auth.guard.ts | 5 +- server/src/queries/shared.link.repository.sql | 44 +++++- .../repositories/shared-link.repository.ts | 15 +- .../1753471866748-AddSharedLinkSlug.ts | 11 ++ server/src/schema/tables/shared-link.table.ts | 3 + server/src/services/api.service.ts | 2 +- server/src/services/auth.service.spec.ts | 46 +++++- server/src/services/auth.service.ts | 38 +++-- .../src/services/shared-link.service.spec.ts | 5 + server/src/services/shared-link.service.ts | 68 +++++---- server/test/fixtures/shared-link.stub.ts | 8 + .../components/album-page/albums-list.svelte | 2 +- .../actions/download-action.svelte | 2 +- .../asset-viewer/actions/share-action.svelte | 2 +- .../asset-viewer/asset-viewer.svelte | 4 +- .../detail-panel-star-rating.svelte | 2 +- .../asset-viewer/detail-panel-tags.svelte | 2 +- .../asset-viewer/detail-panel.svelte | 4 +- .../asset-viewer/image-panorama-viewer.svelte | 2 +- .../assets/thumbnail/thumbnail.svelte | 4 +- .../memory-page/memory-viewer.svelte | 2 +- .../pages/SharedLinkErrorPage.svelte | 14 ++ .../components/pages/SharedLinkPage.svelte | 108 ++++++++++++++ .../actions/create-shared-link.svelte | 2 +- .../actions/download-action.svelte | 4 +- .../actions/link-live-photo-action.svelte | 6 +- .../actions/remove-from-shared-link.svelte | 2 +- .../components/photos-page/asset-grid.svelte | 8 +- .../individual-shared-viewer.svelte | 8 +- .../drag-and-drop-upload-overlay.svelte | 2 +- .../actions/shared-link-copy.svelte | 2 +- .../sharedlinks-page/shared-link-card.svelte | 13 +- web/src/lib/managers/auth-manager.svelte.ts | 3 +- .../internal/load-support.svelte.ts | 5 +- .../timeline-manager.svelte.ts | 4 +- web/src/lib/modals/AlbumShareModal.svelte | 2 +- .../lib/modals/SharedLinkCreateModal.svelte | 117 +++++++-------- web/src/lib/stores/asset-viewing.store.ts | 2 +- web/src/lib/utils.ts | 11 +- web/src/lib/utils/asset-utils.ts | 17 ++- web/src/lib/utils/file-uploader.ts | 10 +- web/src/lib/utils/navigation.ts | 3 +- web/src/lib/utils/shared-links.ts | 58 ++++++++ .../[[assetId=id]]/+page.svelte | 3 +- web/src/routes/(user)/s/[slug]/+error.svelte | 5 + .../[[assetId=id]]/+page.svelte | 12 ++ .../[[photos=photos]]/[[assetId=id]]/+page.ts | 4 + .../routes/(user)/share/[key]/+error.svelte | 13 +- .../[[assetId=id]]/+page.svelte | 93 +----------- .../[[photos=photos]]/[[assetId=id]]/+page.ts | 44 +----- .../factories/shared-link-factory.ts | 1 + 65 files changed, 947 insertions(+), 432 deletions(-) create mode 100644 server/src/schema/migrations/1753471866748-AddSharedLinkSlug.ts create mode 100644 web/src/lib/components/pages/SharedLinkErrorPage.svelte create mode 100644 web/src/lib/components/pages/SharedLinkPage.svelte create mode 100644 web/src/lib/utils/shared-links.ts create mode 100644 web/src/routes/(user)/s/[slug]/+error.svelte create mode 100644 web/src/routes/(user)/s/[slug]/[[photos=photos]]/[[assetId=id]]/+page.svelte create mode 100644 web/src/routes/(user)/s/[slug]/[[photos=photos]]/[[assetId=id]]/+page.ts diff --git a/i18n/en.json b/i18n/en.json index cfc8ffccee..39baff3381 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -725,6 +725,7 @@ "current_server_address": "Current server address", "custom_locale": "Custom Locale", "custom_locale_description": "Format dates and numbers based on the language and the region", + "custom_url": "Custom URL", "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Dark", @@ -1172,7 +1173,6 @@ "light": "Light", "like_deleted": "Like deleted", "link_motion_video": "Link motion video", - "link_options": "Link options", "link_to_oauth": "Link to OAuth", "linked_oauth_account": "Linked OAuth account", "list": "List", @@ -1745,6 +1745,7 @@ "shared_link_clipboard_copied_massage": "Copied to clipboard", "shared_link_clipboard_text": "Link: {link}\nPassword: {password}", "shared_link_create_error": "Error while creating shared link", + "shared_link_custom_url_description": "Access this shared link with a custom URL", "shared_link_edit_description_hint": "Enter the share description", "shared_link_edit_expire_after_option_day": "1 day", "shared_link_edit_expire_after_option_days": "{count} days", @@ -1770,6 +1771,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Manage Shared links", "shared_link_options": "Shared link options", + "shared_link_password_description": "Require a password to access this shared link", "shared_links": "Shared links", "shared_links_description": "Share photos and videos with a link", "shared_photos_and_videos_count": "{assetCount, plural, other {# shared photos & videos.}}", diff --git a/mobile/openapi/lib/api/albums_api.dart b/mobile/openapi/lib/api/albums_api.dart index a8c518ace2..fa7a562adb 100644 --- a/mobile/openapi/lib/api/albums_api.dart +++ b/mobile/openapi/lib/api/albums_api.dart @@ -24,7 +24,9 @@ class AlbumsApi { /// * [BulkIdsDto] bulkIdsDto (required): /// /// * [String] key: - Future addAssetsToAlbumWithHttpInfo(String id, BulkIdsDto bulkIdsDto, { String? key, }) async { + /// + /// * [String] slug: + Future addAssetsToAlbumWithHttpInfo(String id, BulkIdsDto bulkIdsDto, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/albums/{id}/assets' .replaceAll('{id}', id); @@ -39,6 +41,9 @@ class AlbumsApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = ['application/json']; @@ -61,8 +66,10 @@ class AlbumsApi { /// * [BulkIdsDto] bulkIdsDto (required): /// /// * [String] key: - Future?> addAssetsToAlbum(String id, BulkIdsDto bulkIdsDto, { String? key, }) async { - final response = await addAssetsToAlbumWithHttpInfo(id, bulkIdsDto, key: key, ); + /// + /// * [String] slug: + Future?> addAssetsToAlbum(String id, BulkIdsDto bulkIdsDto, { String? key, String? slug, }) async { + final response = await addAssetsToAlbumWithHttpInfo(id, bulkIdsDto, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -225,8 +232,10 @@ class AlbumsApi { /// /// * [String] key: /// + /// * [String] slug: + /// /// * [bool] withoutAssets: - Future getAlbumInfoWithHttpInfo(String id, { String? key, bool? withoutAssets, }) async { + Future getAlbumInfoWithHttpInfo(String id, { String? key, String? slug, bool? withoutAssets, }) async { // ignore: prefer_const_declarations final apiPath = r'/albums/{id}' .replaceAll('{id}', id); @@ -241,6 +250,9 @@ class AlbumsApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } if (withoutAssets != null) { queryParams.addAll(_queryParams('', 'withoutAssets', withoutAssets)); } @@ -265,9 +277,11 @@ class AlbumsApi { /// /// * [String] key: /// + /// * [String] slug: + /// /// * [bool] withoutAssets: - Future getAlbumInfo(String id, { String? key, bool? withoutAssets, }) async { - final response = await getAlbumInfoWithHttpInfo(id, key: key, withoutAssets: withoutAssets, ); + Future getAlbumInfo(String id, { String? key, String? slug, bool? withoutAssets, }) async { + final response = await getAlbumInfoWithHttpInfo(id, key: key, slug: slug, withoutAssets: withoutAssets, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/assets_api.dart b/mobile/openapi/lib/api/assets_api.dart index db6a2e78a3..3cb62785be 100644 --- a/mobile/openapi/lib/api/assets_api.dart +++ b/mobile/openapi/lib/api/assets_api.dart @@ -173,7 +173,9 @@ class AssetsApi { /// * [String] id (required): /// /// * [String] key: - Future downloadAssetWithHttpInfo(String id, { String? key, }) async { + /// + /// * [String] slug: + Future downloadAssetWithHttpInfo(String id, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}/original' .replaceAll('{id}', id); @@ -188,6 +190,9 @@ class AssetsApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = []; @@ -208,8 +213,10 @@ class AssetsApi { /// * [String] id (required): /// /// * [String] key: - Future downloadAsset(String id, { String? key, }) async { - final response = await downloadAssetWithHttpInfo(id, key: key, ); + /// + /// * [String] slug: + Future downloadAsset(String id, { String? key, String? slug, }) async { + final response = await downloadAssetWithHttpInfo(id, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -289,7 +296,9 @@ class AssetsApi { /// * [String] id (required): /// /// * [String] key: - Future getAssetInfoWithHttpInfo(String id, { String? key, }) async { + /// + /// * [String] slug: + Future getAssetInfoWithHttpInfo(String id, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}' .replaceAll('{id}', id); @@ -304,6 +313,9 @@ class AssetsApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = []; @@ -324,8 +336,10 @@ class AssetsApi { /// * [String] id (required): /// /// * [String] key: - Future getAssetInfo(String id, { String? key, }) async { - final response = await getAssetInfoWithHttpInfo(id, key: key, ); + /// + /// * [String] slug: + Future getAssetInfo(String id, { String? key, String? slug, }) async { + final response = await getAssetInfoWithHttpInfo(id, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -469,7 +483,9 @@ class AssetsApi { /// * [String] id (required): /// /// * [String] key: - Future playAssetVideoWithHttpInfo(String id, { String? key, }) async { + /// + /// * [String] slug: + Future playAssetVideoWithHttpInfo(String id, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}/video/playback' .replaceAll('{id}', id); @@ -484,6 +500,9 @@ class AssetsApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = []; @@ -504,8 +523,10 @@ class AssetsApi { /// * [String] id (required): /// /// * [String] key: - Future playAssetVideo(String id, { String? key, }) async { - final response = await playAssetVideoWithHttpInfo(id, key: key, ); + /// + /// * [String] slug: + Future playAssetVideo(String id, { String? key, String? slug, }) async { + final response = await playAssetVideoWithHttpInfo(id, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -541,10 +562,12 @@ class AssetsApi { /// /// * [String] key: /// + /// * [String] slug: + /// /// * [String] duration: /// /// * [String] filename: - Future replaceAssetWithHttpInfo(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? duration, String? filename, }) async { + Future replaceAssetWithHttpInfo(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? duration, String? filename, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}/original' .replaceAll('{id}', id); @@ -559,6 +582,9 @@ class AssetsApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = ['multipart/form-data']; @@ -628,11 +654,13 @@ class AssetsApi { /// /// * [String] key: /// + /// * [String] slug: + /// /// * [String] duration: /// /// * [String] filename: - Future replaceAsset(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? duration, String? filename, }) async { - final response = await replaceAssetWithHttpInfo(id, assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, duration: duration, filename: filename, ); + Future replaceAsset(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? duration, String? filename, }) async { + final response = await replaceAssetWithHttpInfo(id, assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, slug: slug, duration: duration, filename: filename, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -791,6 +819,8 @@ class AssetsApi { /// /// * [String] key: /// + /// * [String] slug: + /// /// * [String] xImmichChecksum: /// sha1 checksum that can be used for duplicate detection before the file is uploaded /// @@ -805,7 +835,7 @@ class AssetsApi { /// * [MultipartFile] sidecarData: /// /// * [AssetVisibility] visibility: - Future uploadAssetWithHttpInfo(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async { + Future uploadAssetWithHttpInfo(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets'; @@ -819,6 +849,9 @@ class AssetsApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } if (xImmichChecksum != null) { headerParams[r'x-immich-checksum'] = parameterToString(xImmichChecksum); @@ -903,6 +936,8 @@ class AssetsApi { /// /// * [String] key: /// + /// * [String] slug: + /// /// * [String] xImmichChecksum: /// sha1 checksum that can be used for duplicate detection before the file is uploaded /// @@ -917,8 +952,8 @@ class AssetsApi { /// * [MultipartFile] sidecarData: /// /// * [AssetVisibility] visibility: - Future uploadAsset(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async { - final response = await uploadAssetWithHttpInfo(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, xImmichChecksum: xImmichChecksum, duration: duration, filename: filename, isFavorite: isFavorite, livePhotoVideoId: livePhotoVideoId, sidecarData: sidecarData, visibility: visibility, ); + Future uploadAsset(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async { + final response = await uploadAssetWithHttpInfo(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, slug: slug, xImmichChecksum: xImmichChecksum, duration: duration, filename: filename, isFavorite: isFavorite, livePhotoVideoId: livePhotoVideoId, sidecarData: sidecarData, visibility: visibility, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -940,7 +975,9 @@ class AssetsApi { /// * [String] key: /// /// * [AssetMediaSize] size: - Future viewAssetWithHttpInfo(String id, { String? key, AssetMediaSize? size, }) async { + /// + /// * [String] slug: + Future viewAssetWithHttpInfo(String id, { String? key, AssetMediaSize? size, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}/thumbnail' .replaceAll('{id}', id); @@ -958,6 +995,9 @@ class AssetsApi { if (size != null) { queryParams.addAll(_queryParams('', 'size', size)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = []; @@ -980,8 +1020,10 @@ class AssetsApi { /// * [String] key: /// /// * [AssetMediaSize] size: - Future viewAsset(String id, { String? key, AssetMediaSize? size, }) async { - final response = await viewAssetWithHttpInfo(id, key: key, size: size, ); + /// + /// * [String] slug: + Future viewAsset(String id, { String? key, AssetMediaSize? size, String? slug, }) async { + final response = await viewAssetWithHttpInfo(id, key: key, size: size, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/download_api.dart b/mobile/openapi/lib/api/download_api.dart index 3b11c2f630..675996f932 100644 --- a/mobile/openapi/lib/api/download_api.dart +++ b/mobile/openapi/lib/api/download_api.dart @@ -22,7 +22,9 @@ class DownloadApi { /// * [AssetIdsDto] assetIdsDto (required): /// /// * [String] key: - Future downloadArchiveWithHttpInfo(AssetIdsDto assetIdsDto, { String? key, }) async { + /// + /// * [String] slug: + Future downloadArchiveWithHttpInfo(AssetIdsDto assetIdsDto, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/download/archive'; @@ -36,6 +38,9 @@ class DownloadApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = ['application/json']; @@ -56,8 +61,10 @@ class DownloadApi { /// * [AssetIdsDto] assetIdsDto (required): /// /// * [String] key: - Future downloadArchive(AssetIdsDto assetIdsDto, { String? key, }) async { - final response = await downloadArchiveWithHttpInfo(assetIdsDto, key: key, ); + /// + /// * [String] slug: + Future downloadArchive(AssetIdsDto assetIdsDto, { String? key, String? slug, }) async { + final response = await downloadArchiveWithHttpInfo(assetIdsDto, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -77,7 +84,9 @@ class DownloadApi { /// * [DownloadInfoDto] downloadInfoDto (required): /// /// * [String] key: - Future getDownloadInfoWithHttpInfo(DownloadInfoDto downloadInfoDto, { String? key, }) async { + /// + /// * [String] slug: + Future getDownloadInfoWithHttpInfo(DownloadInfoDto downloadInfoDto, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/download/info'; @@ -91,6 +100,9 @@ class DownloadApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = ['application/json']; @@ -111,8 +123,10 @@ class DownloadApi { /// * [DownloadInfoDto] downloadInfoDto (required): /// /// * [String] key: - Future getDownloadInfo(DownloadInfoDto downloadInfoDto, { String? key, }) async { - final response = await getDownloadInfoWithHttpInfo(downloadInfoDto, key: key, ); + /// + /// * [String] slug: + Future getDownloadInfo(DownloadInfoDto downloadInfoDto, { String? key, String? slug, }) async { + final response = await getDownloadInfoWithHttpInfo(downloadInfoDto, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/shared_links_api.dart b/mobile/openapi/lib/api/shared_links_api.dart index 5bac8988dc..dd372b962b 100644 --- a/mobile/openapi/lib/api/shared_links_api.dart +++ b/mobile/openapi/lib/api/shared_links_api.dart @@ -24,7 +24,9 @@ class SharedLinksApi { /// * [AssetIdsDto] assetIdsDto (required): /// /// * [String] key: - Future addSharedLinkAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto, { String? key, }) async { + /// + /// * [String] slug: + Future addSharedLinkAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/shared-links/{id}/assets' .replaceAll('{id}', id); @@ -39,6 +41,9 @@ class SharedLinksApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = ['application/json']; @@ -61,8 +66,10 @@ class SharedLinksApi { /// * [AssetIdsDto] assetIdsDto (required): /// /// * [String] key: - Future?> addSharedLinkAssets(String id, AssetIdsDto assetIdsDto, { String? key, }) async { - final response = await addSharedLinkAssetsWithHttpInfo(id, assetIdsDto, key: key, ); + /// + /// * [String] slug: + Future?> addSharedLinkAssets(String id, AssetIdsDto assetIdsDto, { String? key, String? slug, }) async { + final response = await addSharedLinkAssetsWithHttpInfo(id, assetIdsDto, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -187,8 +194,10 @@ class SharedLinksApi { /// /// * [String] password: /// + /// * [String] slug: + /// /// * [String] token: - Future getMySharedLinkWithHttpInfo({ String? key, String? password, String? token, }) async { + Future getMySharedLinkWithHttpInfo({ String? key, String? password, String? slug, String? token, }) async { // ignore: prefer_const_declarations final apiPath = r'/shared-links/me'; @@ -205,6 +214,9 @@ class SharedLinksApi { if (password != null) { queryParams.addAll(_queryParams('', 'password', password)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } if (token != null) { queryParams.addAll(_queryParams('', 'token', token)); } @@ -229,9 +241,11 @@ class SharedLinksApi { /// /// * [String] password: /// + /// * [String] slug: + /// /// * [String] token: - Future getMySharedLink({ String? key, String? password, String? token, }) async { - final response = await getMySharedLinkWithHttpInfo( key: key, password: password, token: token, ); + Future getMySharedLink({ String? key, String? password, String? slug, String? token, }) async { + final response = await getMySharedLinkWithHttpInfo( key: key, password: password, slug: slug, token: token, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -341,7 +355,9 @@ class SharedLinksApi { /// * [AssetIdsDto] assetIdsDto (required): /// /// * [String] key: - Future removeSharedLinkAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto, { String? key, }) async { + /// + /// * [String] slug: + Future removeSharedLinkAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/shared-links/{id}/assets' .replaceAll('{id}', id); @@ -356,6 +372,9 @@ class SharedLinksApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = ['application/json']; @@ -378,8 +397,10 @@ class SharedLinksApi { /// * [AssetIdsDto] assetIdsDto (required): /// /// * [String] key: - Future?> removeSharedLinkAssets(String id, AssetIdsDto assetIdsDto, { String? key, }) async { - final response = await removeSharedLinkAssetsWithHttpInfo(id, assetIdsDto, key: key, ); + /// + /// * [String] slug: + Future?> removeSharedLinkAssets(String id, AssetIdsDto assetIdsDto, { String? key, String? slug, }) async { + final response = await removeSharedLinkAssetsWithHttpInfo(id, assetIdsDto, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/timeline_api.dart b/mobile/openapi/lib/api/timeline_api.dart index 042bc70401..2d3ced610b 100644 --- a/mobile/openapi/lib/api/timeline_api.dart +++ b/mobile/openapi/lib/api/timeline_api.dart @@ -39,6 +39,8 @@ class TimelineApi { /// * [String] personId: /// Filter assets containing a specific person (face recognition) /// + /// * [String] slug: + /// /// * [String] tagId: /// Filter assets with a specific tag /// @@ -53,7 +55,7 @@ class TimelineApi { /// /// * [bool] withStacked: /// Include stacked assets in the response. When true, only primary assets from stacks are returned. - Future getTimeBucketWithHttpInfo(String timeBucket, { String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { + Future getTimeBucketWithHttpInfo(String timeBucket, { String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? slug, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { // ignore: prefer_const_declarations final apiPath = r'/timeline/bucket'; @@ -82,6 +84,9 @@ class TimelineApi { if (personId != null) { queryParams.addAll(_queryParams('', 'personId', personId)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } if (tagId != null) { queryParams.addAll(_queryParams('', 'tagId', tagId)); } @@ -135,6 +140,8 @@ class TimelineApi { /// * [String] personId: /// Filter assets containing a specific person (face recognition) /// + /// * [String] slug: + /// /// * [String] tagId: /// Filter assets with a specific tag /// @@ -149,8 +156,8 @@ class TimelineApi { /// /// * [bool] withStacked: /// Include stacked assets in the response. When true, only primary assets from stacks are returned. - Future getTimeBucket(String timeBucket, { String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { - final response = await getTimeBucketWithHttpInfo(timeBucket, albumId: albumId, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, personId: personId, tagId: tagId, userId: userId, visibility: visibility, withPartners: withPartners, withStacked: withStacked, ); + Future getTimeBucket(String timeBucket, { String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? slug, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { + final response = await getTimeBucketWithHttpInfo(timeBucket, albumId: albumId, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, personId: personId, slug: slug, tagId: tagId, userId: userId, visibility: visibility, withPartners: withPartners, withStacked: withStacked, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -184,6 +191,8 @@ class TimelineApi { /// * [String] personId: /// Filter assets containing a specific person (face recognition) /// + /// * [String] slug: + /// /// * [String] tagId: /// Filter assets with a specific tag /// @@ -198,7 +207,7 @@ class TimelineApi { /// /// * [bool] withStacked: /// Include stacked assets in the response. When true, only primary assets from stacks are returned. - Future getTimeBucketsWithHttpInfo({ String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { + Future getTimeBucketsWithHttpInfo({ String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? slug, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { // ignore: prefer_const_declarations final apiPath = r'/timeline/buckets'; @@ -227,6 +236,9 @@ class TimelineApi { if (personId != null) { queryParams.addAll(_queryParams('', 'personId', personId)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } if (tagId != null) { queryParams.addAll(_queryParams('', 'tagId', tagId)); } @@ -276,6 +288,8 @@ class TimelineApi { /// * [String] personId: /// Filter assets containing a specific person (face recognition) /// + /// * [String] slug: + /// /// * [String] tagId: /// Filter assets with a specific tag /// @@ -290,8 +304,8 @@ class TimelineApi { /// /// * [bool] withStacked: /// Include stacked assets in the response. When true, only primary assets from stacks are returned. - Future?> getTimeBuckets({ String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { - final response = await getTimeBucketsWithHttpInfo( albumId: albumId, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, personId: personId, tagId: tagId, userId: userId, visibility: visibility, withPartners: withPartners, withStacked: withStacked, ); + Future?> getTimeBuckets({ String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? slug, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { + final response = await getTimeBucketsWithHttpInfo( albumId: albumId, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, personId: personId, slug: slug, tagId: tagId, userId: userId, visibility: visibility, withPartners: withPartners, withStacked: withStacked, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/model/shared_link_create_dto.dart b/mobile/openapi/lib/model/shared_link_create_dto.dart index bc96b31fd2..644227bd6e 100644 --- a/mobile/openapi/lib/model/shared_link_create_dto.dart +++ b/mobile/openapi/lib/model/shared_link_create_dto.dart @@ -21,6 +21,7 @@ class SharedLinkCreateDto { this.expiresAt, this.password, this.showMetadata = true, + this.slug, required this.type, }); @@ -44,26 +45,16 @@ class SharedLinkCreateDto { List assetIds; - /// - /// Please note: This property should have been non-nullable! Since the specification file - /// does not include a default value (using the "default:" property), however, the generated - /// source code must fall back to having a nullable type. - /// Consider adding a "default:" property in the specification file to hide this note. - /// String? description; DateTime? expiresAt; - /// - /// Please note: This property should have been non-nullable! Since the specification file - /// does not include a default value (using the "default:" property), however, the generated - /// source code must fall back to having a nullable type. - /// Consider adding a "default:" property in the specification file to hide this note. - /// String? password; bool showMetadata; + String? slug; + SharedLinkType type; @override @@ -76,6 +67,7 @@ class SharedLinkCreateDto { other.expiresAt == expiresAt && other.password == password && other.showMetadata == showMetadata && + other.slug == slug && other.type == type; @override @@ -89,10 +81,11 @@ class SharedLinkCreateDto { (expiresAt == null ? 0 : expiresAt!.hashCode) + (password == null ? 0 : password!.hashCode) + (showMetadata.hashCode) + + (slug == null ? 0 : slug!.hashCode) + (type.hashCode); @override - String toString() => 'SharedLinkCreateDto[albumId=$albumId, allowDownload=$allowDownload, allowUpload=$allowUpload, assetIds=$assetIds, description=$description, expiresAt=$expiresAt, password=$password, showMetadata=$showMetadata, type=$type]'; + String toString() => 'SharedLinkCreateDto[albumId=$albumId, allowDownload=$allowDownload, allowUpload=$allowUpload, assetIds=$assetIds, description=$description, expiresAt=$expiresAt, password=$password, showMetadata=$showMetadata, slug=$slug, type=$type]'; Map toJson() { final json = {}; @@ -124,6 +117,11 @@ class SharedLinkCreateDto { // json[r'password'] = null; } json[r'showMetadata'] = this.showMetadata; + if (this.slug != null) { + json[r'slug'] = this.slug; + } else { + // json[r'slug'] = null; + } json[r'type'] = this.type; return json; } @@ -147,6 +145,7 @@ class SharedLinkCreateDto { expiresAt: mapDateTime(json, r'expiresAt', r''), password: mapValueOfType(json, r'password'), showMetadata: mapValueOfType(json, r'showMetadata') ?? true, + slug: mapValueOfType(json, r'slug'), type: SharedLinkType.fromJson(json[r'type'])!, ); } diff --git a/mobile/openapi/lib/model/shared_link_edit_dto.dart b/mobile/openapi/lib/model/shared_link_edit_dto.dart index a394ba9b3b..f13bc6977b 100644 --- a/mobile/openapi/lib/model/shared_link_edit_dto.dart +++ b/mobile/openapi/lib/model/shared_link_edit_dto.dart @@ -20,6 +20,7 @@ class SharedLinkEditDto { this.expiresAt, this.password, this.showMetadata, + this.slug, }); /// @@ -47,22 +48,10 @@ class SharedLinkEditDto { /// bool? changeExpiryTime; - /// - /// Please note: This property should have been non-nullable! Since the specification file - /// does not include a default value (using the "default:" property), however, the generated - /// source code must fall back to having a nullable type. - /// Consider adding a "default:" property in the specification file to hide this note. - /// String? description; DateTime? expiresAt; - /// - /// Please note: This property should have been non-nullable! Since the specification file - /// does not include a default value (using the "default:" property), however, the generated - /// source code must fall back to having a nullable type. - /// Consider adding a "default:" property in the specification file to hide this note. - /// String? password; /// @@ -73,6 +62,8 @@ class SharedLinkEditDto { /// bool? showMetadata; + String? slug; + @override bool operator ==(Object other) => identical(this, other) || other is SharedLinkEditDto && other.allowDownload == allowDownload && @@ -81,7 +72,8 @@ class SharedLinkEditDto { other.description == description && other.expiresAt == expiresAt && other.password == password && - other.showMetadata == showMetadata; + other.showMetadata == showMetadata && + other.slug == slug; @override int get hashCode => @@ -92,10 +84,11 @@ class SharedLinkEditDto { (description == null ? 0 : description!.hashCode) + (expiresAt == null ? 0 : expiresAt!.hashCode) + (password == null ? 0 : password!.hashCode) + - (showMetadata == null ? 0 : showMetadata!.hashCode); + (showMetadata == null ? 0 : showMetadata!.hashCode) + + (slug == null ? 0 : slug!.hashCode); @override - String toString() => 'SharedLinkEditDto[allowDownload=$allowDownload, allowUpload=$allowUpload, changeExpiryTime=$changeExpiryTime, description=$description, expiresAt=$expiresAt, password=$password, showMetadata=$showMetadata]'; + String toString() => 'SharedLinkEditDto[allowDownload=$allowDownload, allowUpload=$allowUpload, changeExpiryTime=$changeExpiryTime, description=$description, expiresAt=$expiresAt, password=$password, showMetadata=$showMetadata, slug=$slug]'; Map toJson() { final json = {}; @@ -134,6 +127,11 @@ class SharedLinkEditDto { } else { // json[r'showMetadata'] = null; } + if (this.slug != null) { + json[r'slug'] = this.slug; + } else { + // json[r'slug'] = null; + } return json; } @@ -153,6 +151,7 @@ class SharedLinkEditDto { expiresAt: mapDateTime(json, r'expiresAt', r''), password: mapValueOfType(json, r'password'), showMetadata: mapValueOfType(json, r'showMetadata'), + slug: mapValueOfType(json, r'slug'), ); } return null; diff --git a/mobile/openapi/lib/model/shared_link_response_dto.dart b/mobile/openapi/lib/model/shared_link_response_dto.dart index 9cc8b3ac80..d81e1dfa31 100644 --- a/mobile/openapi/lib/model/shared_link_response_dto.dart +++ b/mobile/openapi/lib/model/shared_link_response_dto.dart @@ -24,6 +24,7 @@ class SharedLinkResponseDto { required this.key, required this.password, required this.showMetadata, + required this.slug, this.token, required this.type, required this.userId, @@ -57,6 +58,8 @@ class SharedLinkResponseDto { bool showMetadata; + String? slug; + String? token; SharedLinkType type; @@ -76,6 +79,7 @@ class SharedLinkResponseDto { other.key == key && other.password == password && other.showMetadata == showMetadata && + other.slug == slug && other.token == token && other.type == type && other.userId == userId; @@ -94,12 +98,13 @@ class SharedLinkResponseDto { (key.hashCode) + (password == null ? 0 : password!.hashCode) + (showMetadata.hashCode) + + (slug == null ? 0 : slug!.hashCode) + (token == null ? 0 : token!.hashCode) + (type.hashCode) + (userId.hashCode); @override - String toString() => 'SharedLinkResponseDto[album=$album, allowDownload=$allowDownload, allowUpload=$allowUpload, assets=$assets, createdAt=$createdAt, description=$description, expiresAt=$expiresAt, id=$id, key=$key, password=$password, showMetadata=$showMetadata, token=$token, type=$type, userId=$userId]'; + String toString() => 'SharedLinkResponseDto[album=$album, allowDownload=$allowDownload, allowUpload=$allowUpload, assets=$assets, createdAt=$createdAt, description=$description, expiresAt=$expiresAt, id=$id, key=$key, password=$password, showMetadata=$showMetadata, slug=$slug, token=$token, type=$type, userId=$userId]'; Map toJson() { final json = {}; @@ -130,6 +135,11 @@ class SharedLinkResponseDto { // json[r'password'] = null; } json[r'showMetadata'] = this.showMetadata; + if (this.slug != null) { + json[r'slug'] = this.slug; + } else { + // json[r'slug'] = null; + } if (this.token != null) { json[r'token'] = this.token; } else { @@ -160,6 +170,7 @@ class SharedLinkResponseDto { key: mapValueOfType(json, r'key')!, password: mapValueOfType(json, r'password'), showMetadata: mapValueOfType(json, r'showMetadata')!, + slug: mapValueOfType(json, r'slug'), token: mapValueOfType(json, r'token'), type: SharedLinkType.fromJson(json[r'type'])!, userId: mapValueOfType(json, r'userId')!, @@ -220,6 +231,7 @@ class SharedLinkResponseDto { 'key', 'password', 'showMetadata', + 'slug', 'type', 'userId', }; diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 576592413d..5000eaa5c7 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -956,6 +956,14 @@ "type": "string" } }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, { "name": "withoutAssets", "required": false, @@ -1116,6 +1124,14 @@ "schema": { "type": "string" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "requestBody": { @@ -1550,6 +1566,14 @@ "type": "string" } }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, { "name": "x-immich-checksum", "in": "header", @@ -1929,6 +1953,14 @@ "schema": { "type": "string" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "responses": { @@ -2029,6 +2061,14 @@ "schema": { "type": "string" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "responses": { @@ -2079,6 +2119,14 @@ "schema": { "type": "string" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "requestBody": { @@ -2151,6 +2199,14 @@ "schema": { "$ref": "#/components/schemas/AssetMediaSize" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "responses": { @@ -2202,6 +2258,14 @@ "schema": { "type": "string" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "responses": { @@ -2605,6 +2669,14 @@ "schema": { "type": "string" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "requestBody": { @@ -2657,6 +2729,14 @@ "schema": { "type": "string" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "requestBody": { @@ -6217,6 +6297,14 @@ "type": "string" } }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, { "name": "token", "required": false, @@ -6399,6 +6487,14 @@ "schema": { "type": "string" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "requestBody": { @@ -6460,6 +6556,14 @@ "schema": { "type": "string" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "requestBody": { @@ -7730,6 +7834,14 @@ "type": "string" } }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, { "name": "tagId", "required": false, @@ -7875,6 +7987,14 @@ "type": "string" } }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, { "name": "tagId", "required": false, @@ -13027,6 +13147,7 @@ "type": "array" }, "description": { + "nullable": true, "type": "string" }, "expiresAt": { @@ -13036,12 +13157,17 @@ "type": "string" }, "password": { + "nullable": true, "type": "string" }, "showMetadata": { "default": true, "type": "boolean" }, + "slug": { + "nullable": true, + "type": "string" + }, "type": { "allOf": [ { @@ -13068,6 +13194,7 @@ "type": "boolean" }, "description": { + "nullable": true, "type": "string" }, "expiresAt": { @@ -13076,10 +13203,15 @@ "type": "string" }, "password": { + "nullable": true, "type": "string" }, "showMetadata": { "type": "boolean" + }, + "slug": { + "nullable": true, + "type": "string" } }, "type": "object" @@ -13127,6 +13259,10 @@ "showMetadata": { "type": "boolean" }, + "slug": { + "nullable": true, + "type": "string" + }, "token": { "nullable": true, "type": "string" @@ -13153,6 +13289,7 @@ "key", "password", "showMetadata", + "slug", "type", "userId" ], diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 7c030d43c2..063fc86782 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -1199,6 +1199,7 @@ export type SharedLinkResponseDto = { key: string; password: string | null; showMetadata: boolean; + slug: string | null; token?: string | null; "type": SharedLinkType; userId: string; @@ -1208,10 +1209,11 @@ export type SharedLinkCreateDto = { allowDownload?: boolean; allowUpload?: boolean; assetIds?: string[]; - description?: string; + description?: string | null; expiresAt?: string | null; - password?: string; + password?: string | null; showMetadata?: boolean; + slug?: string | null; "type": SharedLinkType; }; export type SharedLinkEditDto = { @@ -1221,10 +1223,11 @@ export type SharedLinkEditDto = { Setting this flag and not sending expiryAt is considered as null instead. Clients that can send null values can ignore this. */ changeExpiryTime?: boolean; - description?: string; + description?: string | null; expiresAt?: string | null; - password?: string; + password?: string | null; showMetadata?: boolean; + slug?: string | null; }; export type AssetIdsResponseDto = { assetId: string; @@ -1821,9 +1824,10 @@ export function deleteAlbum({ id }: { method: "DELETE" })); } -export function getAlbumInfo({ id, key, withoutAssets }: { +export function getAlbumInfo({ id, key, slug, withoutAssets }: { id: string; key?: string; + slug?: string; withoutAssets?: boolean; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ @@ -1831,6 +1835,7 @@ export function getAlbumInfo({ id, key, withoutAssets }: { data: AlbumResponseDto; }>(`/albums/${encodeURIComponent(id)}${QS.query(QS.explode({ key, + slug, withoutAssets }))}`, { ...opts @@ -1862,16 +1867,18 @@ export function removeAssetFromAlbum({ id, bulkIdsDto }: { body: bulkIdsDto }))); } -export function addAssetsToAlbum({ id, key, bulkIdsDto }: { +export function addAssetsToAlbum({ id, key, slug, bulkIdsDto }: { id: string; key?: string; + slug?: string; bulkIdsDto: BulkIdsDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: BulkIdResponseDto[]; }>(`/albums/${encodeURIComponent(id)}/assets${QS.query(QS.explode({ - key + key, + slug }))}`, oazapfts.json({ ...opts, method: "PUT", @@ -1971,8 +1978,9 @@ export function deleteAssets({ assetBulkDeleteDto }: { body: assetBulkDeleteDto }))); } -export function uploadAsset({ key, xImmichChecksum, assetMediaCreateDto }: { +export function uploadAsset({ key, slug, xImmichChecksum, assetMediaCreateDto }: { key?: string; + slug?: string; xImmichChecksum?: string; assetMediaCreateDto: AssetMediaCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -1980,7 +1988,8 @@ export function uploadAsset({ key, xImmichChecksum, assetMediaCreateDto }: { status: 201; data: AssetMediaResponseDto; }>(`/assets${QS.query(QS.explode({ - key + key, + slug }))}`, oazapfts.multipart({ ...opts, method: "POST", @@ -2082,15 +2091,17 @@ export function getAssetStatistics({ isFavorite, isTrashed, visibility }: { ...opts })); } -export function getAssetInfo({ id, key }: { +export function getAssetInfo({ id, key, slug }: { id: string; key?: string; + slug?: string; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: AssetResponseDto; }>(`/assets/${encodeURIComponent(id)}${QS.query(QS.explode({ - key + key, + slug }))}`, { ...opts })); @@ -2108,15 +2119,17 @@ export function updateAsset({ id, updateAssetDto }: { body: updateAssetDto }))); } -export function downloadAsset({ id, key }: { +export function downloadAsset({ id, key, slug }: { id: string; key?: string; + slug?: string; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchBlob<{ status: 200; data: Blob; }>(`/assets/${encodeURIComponent(id)}/original${QS.query(QS.explode({ - key + key, + slug }))}`, { ...opts })); @@ -2124,46 +2137,52 @@ export function downloadAsset({ id, key }: { /** * replaceAsset */ -export function replaceAsset({ id, key, assetMediaReplaceDto }: { +export function replaceAsset({ id, key, slug, assetMediaReplaceDto }: { id: string; key?: string; + slug?: string; assetMediaReplaceDto: AssetMediaReplaceDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: AssetMediaResponseDto; }>(`/assets/${encodeURIComponent(id)}/original${QS.query(QS.explode({ - key + key, + slug }))}`, oazapfts.multipart({ ...opts, method: "PUT", body: assetMediaReplaceDto }))); } -export function viewAsset({ id, key, size }: { +export function viewAsset({ id, key, size, slug }: { id: string; key?: string; size?: AssetMediaSize; + slug?: string; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchBlob<{ status: 200; data: Blob; }>(`/assets/${encodeURIComponent(id)}/thumbnail${QS.query(QS.explode({ key, - size + size, + slug }))}`, { ...opts })); } -export function playAssetVideo({ id, key }: { +export function playAssetVideo({ id, key, slug }: { id: string; key?: string; + slug?: string; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchBlob<{ status: 200; data: Blob; }>(`/assets/${encodeURIComponent(id)}/video/playback${QS.query(QS.explode({ - key + key, + slug }))}`, { ...opts })); @@ -2272,30 +2291,34 @@ export function validateAccessToken(opts?: Oazapfts.RequestOpts) { method: "POST" })); } -export function downloadArchive({ key, assetIdsDto }: { +export function downloadArchive({ key, slug, assetIdsDto }: { key?: string; + slug?: string; assetIdsDto: AssetIdsDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchBlob<{ status: 200; data: Blob; }>(`/download/archive${QS.query(QS.explode({ - key + key, + slug }))}`, oazapfts.json({ ...opts, method: "POST", body: assetIdsDto }))); } -export function getDownloadInfo({ key, downloadInfoDto }: { +export function getDownloadInfo({ key, slug, downloadInfoDto }: { key?: string; + slug?: string; downloadInfoDto: DownloadInfoDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 201; data: DownloadResponseDto; }>(`/download/info${QS.query(QS.explode({ - key + key, + slug }))}`, oazapfts.json({ ...opts, method: "POST", @@ -3230,9 +3253,10 @@ export function createSharedLink({ sharedLinkCreateDto }: { body: sharedLinkCreateDto }))); } -export function getMySharedLink({ key, password, token }: { +export function getMySharedLink({ key, password, slug, token }: { key?: string; password?: string; + slug?: string; token?: string; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ @@ -3241,6 +3265,7 @@ export function getMySharedLink({ key, password, token }: { }>(`/shared-links/me${QS.query(QS.explode({ key, password, + slug, token }))}`, { ...opts @@ -3277,32 +3302,36 @@ export function updateSharedLink({ id, sharedLinkEditDto }: { body: sharedLinkEditDto }))); } -export function removeSharedLinkAssets({ id, key, assetIdsDto }: { +export function removeSharedLinkAssets({ id, key, slug, assetIdsDto }: { id: string; key?: string; + slug?: string; assetIdsDto: AssetIdsDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: AssetIdsResponseDto[]; }>(`/shared-links/${encodeURIComponent(id)}/assets${QS.query(QS.explode({ - key + key, + slug }))}`, oazapfts.json({ ...opts, method: "DELETE", body: assetIdsDto }))); } -export function addSharedLinkAssets({ id, key, assetIdsDto }: { +export function addSharedLinkAssets({ id, key, slug, assetIdsDto }: { id: string; key?: string; + slug?: string; assetIdsDto: AssetIdsDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: AssetIdsResponseDto[]; }>(`/shared-links/${encodeURIComponent(id)}/assets${QS.query(QS.explode({ - key + key, + slug }))}`, oazapfts.json({ ...opts, method: "PUT", @@ -3611,13 +3640,14 @@ export function tagAssets({ id, bulkIdsDto }: { body: bulkIdsDto }))); } -export function getTimeBucket({ albumId, isFavorite, isTrashed, key, order, personId, tagId, timeBucket, userId, visibility, withPartners, withStacked }: { +export function getTimeBucket({ albumId, isFavorite, isTrashed, key, order, personId, slug, tagId, timeBucket, userId, visibility, withPartners, withStacked }: { albumId?: string; isFavorite?: boolean; isTrashed?: boolean; key?: string; order?: AssetOrder; personId?: string; + slug?: string; tagId?: string; timeBucket: string; userId?: string; @@ -3635,6 +3665,7 @@ export function getTimeBucket({ albumId, isFavorite, isTrashed, key, order, pers key, order, personId, + slug, tagId, timeBucket, userId, @@ -3645,13 +3676,14 @@ export function getTimeBucket({ albumId, isFavorite, isTrashed, key, order, pers ...opts })); } -export function getTimeBuckets({ albumId, isFavorite, isTrashed, key, order, personId, tagId, userId, visibility, withPartners, withStacked }: { +export function getTimeBuckets({ albumId, isFavorite, isTrashed, key, order, personId, slug, tagId, userId, visibility, withPartners, withStacked }: { albumId?: string; isFavorite?: boolean; isTrashed?: boolean; key?: string; order?: AssetOrder; personId?: string; + slug?: string; tagId?: string; userId?: string; visibility?: AssetVisibility; @@ -3668,6 +3700,7 @@ export function getTimeBuckets({ albumId, isFavorite, isTrashed, key, order, per key, order, personId, + slug, tagId, userId, visibility, diff --git a/server/src/database.ts b/server/src/database.ts index 53c39b7383..44bdefa080 100644 --- a/server/src/database.ts +++ b/server/src/database.ts @@ -192,6 +192,7 @@ export type SharedLink = { showExif: boolean; type: SharedLinkType; userId: string; + slug: string | null; }; export type Album = Selectable & { diff --git a/server/src/dtos/shared-link.dto.ts b/server/src/dtos/shared-link.dto.ts index 299590c0e3..011707f1f7 100644 --- a/server/src/dtos/shared-link.dto.ts +++ b/server/src/dtos/shared-link.dto.ts @@ -22,13 +22,17 @@ export class SharedLinkCreateDto { @ValidateUUID({ optional: true }) albumId?: string; + @Optional({ nullable: true, emptyToNull: true }) @IsString() - @Optional() - description?: string; + description?: string | null; + @Optional({ nullable: true, emptyToNull: true }) @IsString() - @Optional() - password?: string; + password?: string | null; + + @Optional({ nullable: true, emptyToNull: true }) + @IsString() + slug?: string | null; @ValidateDate({ optional: true, nullable: true }) expiresAt?: Date | null = null; @@ -44,16 +48,22 @@ export class SharedLinkCreateDto { } export class SharedLinkEditDto { - @Optional() - description?: string; + @Optional({ nullable: true, emptyToNull: true }) + @IsString() + description?: string | null; - @Optional() - password?: string; + @Optional({ nullable: true, emptyToNull: true }) + @IsString() + password?: string | null; + + @Optional({ nullable: true, emptyToNull: true }) + @IsString() + slug?: string | null; @Optional({ nullable: true }) expiresAt?: Date | null; - @Optional() + @ValidateBoolean({ optional: true }) allowUpload?: boolean; @ValidateBoolean({ optional: true }) @@ -99,6 +109,8 @@ export class SharedLinkResponseDto { allowDownload!: boolean; showMetadata!: boolean; + + slug!: string | null; } export function mapSharedLink(sharedLink: SharedLink): SharedLinkResponseDto { @@ -118,6 +130,7 @@ export function mapSharedLink(sharedLink: SharedLink): SharedLinkResponseDto { allowUpload: sharedLink.allowUpload, allowDownload: sharedLink.allowDownload, showMetadata: sharedLink.showExif, + slug: sharedLink.slug, }; } @@ -141,5 +154,6 @@ export function mapSharedLinkWithoutMetadata(sharedLink: SharedLink): SharedLink allowUpload: sharedLink.allowUpload, allowDownload: sharedLink.allowDownload, showMetadata: sharedLink.showExif, + slug: sharedLink.slug, }; } diff --git a/server/src/enum.ts b/server/src/enum.ts index 70e94ed43f..d17b5dc901 100644 --- a/server/src/enum.ts +++ b/server/src/enum.ts @@ -17,12 +17,14 @@ export enum ImmichHeader { UserToken = 'x-immich-user-token', SessionToken = 'x-immich-session-token', SharedLinkKey = 'x-immich-share-key', + SharedLinkSlug = 'x-immich-share-slug', Checksum = 'x-immich-checksum', Cid = 'x-immich-cid', } export enum ImmichQuery { SharedLinkKey = 'key', + SharedLinkSlug = 'slug', ApiKey = 'apiKey', SessionKey = 'sessionKey', } diff --git a/server/src/middleware/auth.guard.ts b/server/src/middleware/auth.guard.ts index 238f99257a..69b3cb5ecc 100644 --- a/server/src/middleware/auth.guard.ts +++ b/server/src/middleware/auth.guard.ts @@ -28,7 +28,10 @@ export const Authenticated = (options?: AuthenticatedOptions): MethodDecorator = ]; if ((options as SharedLinkRoute)?.sharedLink) { - decorators.push(ApiQuery({ name: ImmichQuery.SharedLinkKey, type: String, required: false })); + decorators.push( + ApiQuery({ name: ImmichQuery.SharedLinkKey, type: String, required: false }), + ApiQuery({ name: ImmichQuery.SharedLinkSlug, type: String, required: false }), + ); } return applyDecorators(...decorators); diff --git a/server/src/queries/shared.link.repository.sql b/server/src/queries/shared.link.repository.sql index ed3507fa2f..0e13b98b5d 100644 --- a/server/src/queries/shared.link.repository.sql +++ b/server/src/queries/shared.link.repository.sql @@ -188,9 +188,47 @@ from "shared_link" left join "album" on "album"."id" = "shared_link"."albumId" where - "shared_link"."key" = $1 - and "album"."deletedAt" is null + "album"."deletedAt" is null and ( - "shared_link"."type" = $2 + "shared_link"."type" = $1 or "album"."id" is not null ) + and "shared_link"."key" = $2 + +-- SharedLinkRepository.getBySlug +select + "shared_link"."id", + "shared_link"."userId", + "shared_link"."expiresAt", + "shared_link"."showExif", + "shared_link"."allowUpload", + "shared_link"."allowDownload", + "shared_link"."password", + ( + select + to_json(obj) + from + ( + select + "user"."id", + "user"."name", + "user"."email", + "user"."isAdmin", + "user"."quotaUsageInBytes", + "user"."quotaSizeInBytes" + from + "user" + where + "user"."id" = "shared_link"."userId" + ) as obj + ) as "user" +from + "shared_link" + left join "album" on "album"."id" = "shared_link"."albumId" +where + "album"."deletedAt" is null + and ( + "shared_link"."type" = $1 + or "album"."id" is not null + ) + and "shared_link"."slug" = $2 diff --git a/server/src/repositories/shared-link.repository.ts b/server/src/repositories/shared-link.repository.ts index d5fb3be47d..54eab7c86f 100644 --- a/server/src/repositories/shared-link.repository.ts +++ b/server/src/repositories/shared-link.repository.ts @@ -173,10 +173,18 @@ export class SharedLinkRepository { } @GenerateSql({ params: [DummyValue.BUFFER] }) - async getByKey(key: Buffer) { + getByKey(key: Buffer) { + return this.authBuilder().where('shared_link.key', '=', key).executeTakeFirst(); + } + + @GenerateSql({ params: [DummyValue.BUFFER] }) + getBySlug(slug: string) { + return this.authBuilder().where('shared_link.slug', '=', slug).executeTakeFirst(); + } + + private authBuilder() { return this.db .selectFrom('shared_link') - .where('shared_link.key', '=', key) .leftJoin('album', 'album.id', 'shared_link.albumId') .where('album.deletedAt', 'is', null) .select((eb) => [ @@ -185,8 +193,7 @@ export class SharedLinkRepository { eb.selectFrom('user').select(columns.authUser).whereRef('user.id', '=', 'shared_link.userId'), ).as('user'), ]) - .where((eb) => eb.or([eb('shared_link.type', '=', SharedLinkType.Individual), eb('album.id', 'is not', null)])) - .executeTakeFirst(); + .where((eb) => eb.or([eb('shared_link.type', '=', SharedLinkType.Individual), eb('album.id', 'is not', null)])); } async create(entity: Insertable & { assetIds?: string[] }) { diff --git a/server/src/schema/migrations/1753471866748-AddSharedLinkSlug.ts b/server/src/schema/migrations/1753471866748-AddSharedLinkSlug.ts new file mode 100644 index 0000000000..1d77eddd07 --- /dev/null +++ b/server/src/schema/migrations/1753471866748-AddSharedLinkSlug.ts @@ -0,0 +1,11 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await sql`ALTER TABLE "shared_link" ADD "slug" character varying;`.execute(db); + await sql`ALTER TABLE "shared_link" ADD CONSTRAINT "shared_link_slug_uq" UNIQUE ("slug");`.execute(db); +} + +export async function down(db: Kysely): Promise { + await sql`ALTER TABLE "shared_link" DROP CONSTRAINT "shared_link_slug_uq";`.execute(db); + await sql`ALTER TABLE "shared_link" DROP COLUMN "slug";`.execute(db); +} diff --git a/server/src/schema/tables/shared-link.table.ts b/server/src/schema/tables/shared-link.table.ts index 40fd2bf6a9..80e2d7cdf4 100644 --- a/server/src/schema/tables/shared-link.table.ts +++ b/server/src/schema/tables/shared-link.table.ts @@ -48,4 +48,7 @@ export class SharedLinkTable { @Column({ type: 'character varying', nullable: true }) password!: string | null; + + @Column({ type: 'character varying', nullable: true, unique: true }) + slug!: string | null; } diff --git a/server/src/services/api.service.ts b/server/src/services/api.service.ts index 27a776e867..ced74482c2 100644 --- a/server/src/services/api.service.ts +++ b/server/src/services/api.service.ts @@ -80,7 +80,7 @@ export class ApiService { if (shareMatches) { try { const key = shareMatches[1]; - const auth = await this.authService.validateSharedLink(key); + const auth = await this.authService.validateSharedLinkKey(key); const meta = await this.sharedLinkService.getMetadataTags( auth, request.host ? `${request.protocol}://${request.host}` : undefined, diff --git a/server/src/services/auth.service.spec.ts b/server/src/services/auth.service.spec.ts index 9f678162c6..58c2542310 100644 --- a/server/src/services/auth.service.spec.ts +++ b/server/src/services/auth.service.spec.ts @@ -322,15 +322,18 @@ describe(AuthService.name, () => { mocks.sharedLink.getByKey.mockResolvedValue(sharedLink); mocks.user.get.mockResolvedValue(user); + const buffer = sharedLink.key; + const key = buffer.toString('base64url'); + await expect( sut.authenticate({ - headers: { 'x-immich-share-key': sharedLink.key.toString('base64url') }, + headers: { 'x-immich-share-key': key }, queryParams: {}, metadata: { adminRoute: false, sharedLinkRoute: true, uri: 'test' }, }), ).resolves.toEqual({ user, sharedLink }); - expect(mocks.sharedLink.getByKey).toHaveBeenCalledWith(sharedLink.key); + expect(mocks.sharedLink.getByKey).toHaveBeenCalledWith(buffer); }); it('should accept a hex key', async () => { @@ -340,15 +343,50 @@ describe(AuthService.name, () => { mocks.sharedLink.getByKey.mockResolvedValue(sharedLink); mocks.user.get.mockResolvedValue(user); + const buffer = sharedLink.key; + const key = buffer.toString('hex'); + await expect( sut.authenticate({ - headers: { 'x-immich-share-key': sharedLink.key.toString('hex') }, + headers: { 'x-immich-share-key': key }, queryParams: {}, metadata: { adminRoute: false, sharedLinkRoute: true, uri: 'test' }, }), ).resolves.toEqual({ user, sharedLink }); - expect(mocks.sharedLink.getByKey).toHaveBeenCalledWith(sharedLink.key); + expect(mocks.sharedLink.getByKey).toHaveBeenCalledWith(buffer); + }); + }); + + describe('validate - shared link slug', () => { + it('should not accept a non-existent slug', async () => { + mocks.sharedLink.getBySlug.mockResolvedValue(void 0); + + await expect( + sut.authenticate({ + headers: { 'x-immich-share-slug': 'slug' }, + queryParams: {}, + metadata: { adminRoute: false, sharedLinkRoute: true, uri: 'test' }, + }), + ).rejects.toBeInstanceOf(UnauthorizedException); + }); + + it('should accept a valid slug', async () => { + const user = factory.userAdmin(); + const sharedLink = { ...sharedLinkStub.valid, slug: 'slug-123', user } as any; + + mocks.sharedLink.getBySlug.mockResolvedValue(sharedLink); + mocks.user.get.mockResolvedValue(user); + + await expect( + sut.authenticate({ + headers: { 'x-immich-share-slug': 'slug-123' }, + queryParams: {}, + metadata: { adminRoute: false, sharedLinkRoute: true, uri: 'test' }, + }), + ).resolves.toEqual({ user, sharedLink }); + + expect(mocks.sharedLink.getBySlug).toHaveBeenCalledWith('slug-123'); }); }); diff --git a/server/src/services/auth.service.ts b/server/src/services/auth.service.ts index f757bf5a3e..fcaeb06af0 100644 --- a/server/src/services/auth.service.ts +++ b/server/src/services/auth.service.ts @@ -6,7 +6,7 @@ import { IncomingHttpHeaders } from 'node:http'; import { join } from 'node:path'; import { LOGIN_URL, MOBILE_REDIRECT, SALT_ROUNDS } from 'src/constants'; import { StorageCore } from 'src/cores/storage.core'; -import { UserAdmin } from 'src/database'; +import { AuthSharedLink, AuthUser, UserAdmin } from 'src/database'; import { AuthDto, AuthStatusResponseDto, @@ -196,6 +196,7 @@ export class AuthService extends BaseService { private async validate({ headers, queryParams }: Omit): Promise { const shareKey = (headers[ImmichHeader.SharedLinkKey] || queryParams[ImmichQuery.SharedLinkKey]) as string; + const shareSlug = (headers[ImmichHeader.SharedLinkSlug] || queryParams[ImmichQuery.SharedLinkSlug]) as string; const session = (headers[ImmichHeader.UserToken] || headers[ImmichHeader.SessionToken] || queryParams[ImmichQuery.SessionKey] || @@ -204,7 +205,11 @@ export class AuthService extends BaseService { const apiKey = (headers[ImmichHeader.ApiKey] || queryParams[ImmichQuery.ApiKey]) as string; if (shareKey) { - return this.validateSharedLink(shareKey); + return this.validateSharedLinkKey(shareKey); + } + + if (shareSlug) { + return this.validateSharedLinkSlug(shareSlug); } if (session) { @@ -403,18 +408,33 @@ export class AuthService extends BaseService { return cookies[ImmichCookie.OAuthCodeVerifier] || null; } - async validateSharedLink(key: string | string[]): Promise { + async validateSharedLinkKey(key: string | string[]): Promise { key = Array.isArray(key) ? key[0] : key; const bytes = Buffer.from(key, key.length === 100 ? 'hex' : 'base64url'); const sharedLink = await this.sharedLinkRepository.getByKey(bytes); - if (sharedLink?.user && (!sharedLink.expiresAt || new Date(sharedLink.expiresAt) > new Date())) { - return { - user: sharedLink.user, - sharedLink, - }; + if (!this.isValidSharedLink(sharedLink)) { + throw new UnauthorizedException('Invalid share key'); } - throw new UnauthorizedException('Invalid share key'); + + return { user: sharedLink.user, sharedLink }; + } + + async validateSharedLinkSlug(slug: string | string[]): Promise { + slug = Array.isArray(slug) ? slug[0] : slug; + + const sharedLink = await this.sharedLinkRepository.getBySlug(slug); + if (!this.isValidSharedLink(sharedLink)) { + throw new UnauthorizedException('Invalid share slug'); + } + + return { user: sharedLink.user, sharedLink }; + } + + private isValidSharedLink( + sharedLink?: AuthSharedLink & { user: AuthUser | null }, + ): sharedLink is AuthSharedLink & { user: AuthUser } { + return !!sharedLink?.user && (!sharedLink.expiresAt || new Date(sharedLink.expiresAt) > new Date()); } private async validateApiKey(key: string): Promise { diff --git a/server/src/services/shared-link.service.spec.ts b/server/src/services/shared-link.service.spec.ts index 8e09580d55..9483cdddff 100644 --- a/server/src/services/shared-link.service.spec.ts +++ b/server/src/services/shared-link.service.spec.ts @@ -136,6 +136,7 @@ describe(SharedLinkService.name, () => { allowUpload: true, description: null, expiresAt: null, + slug: null, showExif: true, key: Buffer.from('random-bytes', 'utf8'), }); @@ -163,6 +164,7 @@ describe(SharedLinkService.name, () => { userId: authStub.admin.user.id, albumId: null, allowDownload: true, + slug: null, allowUpload: true, assetIds: [assetStub.image.id], description: null, @@ -199,6 +201,7 @@ describe(SharedLinkService.name, () => { description: null, expiresAt: null, showExif: false, + slug: null, key: Buffer.from('random-bytes', 'utf8'), }); }); @@ -223,6 +226,7 @@ describe(SharedLinkService.name, () => { expect(mocks.sharedLink.get).toHaveBeenCalledWith(authStub.user1.user.id, sharedLinkStub.valid.id); expect(mocks.sharedLink.update).toHaveBeenCalledWith({ id: sharedLinkStub.valid.id, + slug: null, userId: authStub.user1.user.id, allowDownload: false, }); @@ -277,6 +281,7 @@ describe(SharedLinkService.name, () => { expect(mocks.sharedLink.update).toHaveBeenCalled(); expect(mocks.sharedLink.update).toHaveBeenCalledWith({ ...sharedLinkStub.individual, + slug: null, assetIds: ['asset-3'], }); }); diff --git a/server/src/services/shared-link.service.ts b/server/src/services/shared-link.service.ts index 9f8e238c43..096739d056 100644 --- a/server/src/services/shared-link.service.ts +++ b/server/src/services/shared-link.service.ts @@ -1,4 +1,5 @@ import { BadRequestException, ForbiddenException, Injectable, UnauthorizedException } from '@nestjs/common'; +import { PostgresError } from 'postgres'; import { SharedLink } from 'src/database'; import { AssetIdErrorReason, AssetIdsResponseDto } from 'src/dtos/asset-ids.response.dto'; import { AssetIdsDto } from 'src/dtos/asset.dto'; @@ -64,36 +65,53 @@ export class SharedLinkService extends BaseService { } } - const sharedLink = await this.sharedLinkRepository.create({ - key: this.cryptoRepository.randomBytes(50), - userId: auth.user.id, - type: dto.type, - albumId: dto.albumId || null, - assetIds: dto.assetIds, - description: dto.description || null, - password: dto.password, - expiresAt: dto.expiresAt || null, - allowUpload: dto.allowUpload ?? true, - allowDownload: dto.showMetadata === false ? false : (dto.allowDownload ?? true), - showExif: dto.showMetadata ?? true, - }); + try { + const sharedLink = await this.sharedLinkRepository.create({ + key: this.cryptoRepository.randomBytes(50), + userId: auth.user.id, + type: dto.type, + albumId: dto.albumId || null, + assetIds: dto.assetIds, + description: dto.description || null, + password: dto.password, + expiresAt: dto.expiresAt || null, + allowUpload: dto.allowUpload ?? true, + allowDownload: dto.showMetadata === false ? false : (dto.allowDownload ?? true), + showExif: dto.showMetadata ?? true, + slug: dto.slug || null, + }); - return this.mapToSharedLink(sharedLink, { withExif: true }); + return this.mapToSharedLink(sharedLink, { withExif: true }); + } catch (error) { + this.handleError(error); + } + } + + private handleError(error: unknown): never { + if ((error as PostgresError).constraint_name === 'shared_link_slug_uq') { + throw new BadRequestException('Shared link with this slug already exists'); + } + throw error; } async update(auth: AuthDto, id: string, dto: SharedLinkEditDto) { await this.findOrFail(auth.user.id, id); - const sharedLink = await this.sharedLinkRepository.update({ - id, - userId: auth.user.id, - description: dto.description, - password: dto.password, - expiresAt: dto.changeExpiryTime && !dto.expiresAt ? null : dto.expiresAt, - allowUpload: dto.allowUpload, - allowDownload: dto.allowDownload, - showExif: dto.showMetadata, - }); - return this.mapToSharedLink(sharedLink, { withExif: true }); + try { + const sharedLink = await this.sharedLinkRepository.update({ + id, + userId: auth.user.id, + description: dto.description, + password: dto.password, + expiresAt: dto.changeExpiryTime && !dto.expiresAt ? null : dto.expiresAt, + allowUpload: dto.allowUpload, + allowDownload: dto.allowDownload, + showExif: dto.showMetadata, + slug: dto.slug || null, + }); + return this.mapToSharedLink(sharedLink, { withExif: true }); + } catch (error) { + this.handleError(error); + } } async remove(auth: AuthDto, id: string): Promise { diff --git a/server/test/fixtures/shared-link.stub.ts b/server/test/fixtures/shared-link.stub.ts index 47201a5b3b..1cd36f1f23 100644 --- a/server/test/fixtures/shared-link.stub.ts +++ b/server/test/fixtures/shared-link.stub.ts @@ -118,6 +118,7 @@ export const sharedLinkStub = { description: null, assets: [assetStub.image], password: 'password', + slug: null, }), valid: Object.freeze({ id: '123', @@ -135,6 +136,7 @@ export const sharedLinkStub = { password: null, assets: [] as MapAsset[], album: null, + slug: null, }), expired: Object.freeze({ id: '123', @@ -152,6 +154,7 @@ export const sharedLinkStub = { albumId: null, assets: [] as MapAsset[], album: null, + slug: null, }), readonlyNoExif: Object.freeze({ id: '123', @@ -166,6 +169,7 @@ export const sharedLinkStub = { description: null, password: null, assets: [], + slug: null, albumId: 'album-123', album: { id: 'album-123', @@ -266,6 +270,7 @@ export const sharedLinkStub = { allowUpload: true, allowDownload: true, showExif: true, + slug: null, description: null, password: 'password', assets: [], @@ -288,6 +293,7 @@ export const sharedLinkResponseStub = { showMetadata: true, type: SharedLinkType.Album, userId: 'admin_id', + slug: null, }), expired: Object.freeze({ album: undefined, @@ -303,6 +309,7 @@ export const sharedLinkResponseStub = { showMetadata: true, type: SharedLinkType.Album, userId: 'admin_id', + slug: null, }), readonlyNoMetadata: Object.freeze({ id: '123', @@ -316,6 +323,7 @@ export const sharedLinkResponseStub = { allowUpload: false, allowDownload: false, showMetadata: false, + slug: null, album: { ...albumResponse, startDate: assetResponse.localDateTime, endDate: assetResponse.localDateTime }, assets: [{ ...assetResponseWithoutMetadata, exifInfo: undefined }], }), diff --git a/web/src/lib/components/album-page/albums-list.svelte b/web/src/lib/components/album-page/albums-list.svelte index 29031bee2e..668f624af5 100644 --- a/web/src/lib/components/album-page/albums-list.svelte +++ b/web/src/lib/components/album-page/albums-list.svelte @@ -369,7 +369,7 @@ if (sharedLink) { handleSharedLinkCreated(albumToShare); - await modalManager.show(QrCodeModal, { title: $t('view_link'), value: makeSharedLinkUrl(sharedLink.key) }); + await modalManager.show(QrCodeModal, { title: $t('view_link'), value: makeSharedLinkUrl(sharedLink) }); } return; } diff --git a/web/src/lib/components/asset-viewer/actions/download-action.svelte b/web/src/lib/components/asset-viewer/actions/download-action.svelte index e6c96da016..677550e2da 100644 --- a/web/src/lib/components/asset-viewer/actions/download-action.svelte +++ b/web/src/lib/components/asset-viewer/actions/download-action.svelte @@ -16,7 +16,7 @@ let { asset, menuItem = false }: Props = $props(); - const onDownloadFile = async () => downloadFile(await getAssetInfo({ id: asset.id, key: authManager.key })); + const onDownloadFile = async () => downloadFile(await getAssetInfo({ ...authManager.params, id: asset.id })); diff --git a/web/src/lib/components/asset-viewer/actions/share-action.svelte b/web/src/lib/components/asset-viewer/actions/share-action.svelte index 25b3520d09..24a67848a9 100644 --- a/web/src/lib/components/asset-viewer/actions/share-action.svelte +++ b/web/src/lib/components/asset-viewer/actions/share-action.svelte @@ -17,7 +17,7 @@ const sharedLink = await modalManager.show(SharedLinkCreateModal, { assetIds: [asset.id] }); if (sharedLink) { - await modalManager.show(QrCodeModal, { title: $t('view_link'), value: makeSharedLinkUrl(sharedLink.key) }); + await modalManager.show(QrCodeModal, { title: $t('view_link'), value: makeSharedLinkUrl(sharedLink) }); } }; diff --git a/web/src/lib/components/asset-viewer/asset-viewer.svelte b/web/src/lib/components/asset-viewer/asset-viewer.svelte index d82b2e6532..452510f508 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer.svelte @@ -111,7 +111,7 @@ let zoomToggle = $state(() => void 0); const refreshStack = async () => { - if (authManager.key) { + if (authManager.isSharedLink) { return; } @@ -191,7 +191,7 @@ }); const handleGetAllAlbums = async () => { - if (authManager.key) { + if (authManager.isSharedLink) { return; } diff --git a/web/src/lib/components/asset-viewer/detail-panel-star-rating.svelte b/web/src/lib/components/asset-viewer/detail-panel-star-rating.svelte index 0ec8692180..d333d73be1 100644 --- a/web/src/lib/components/asset-viewer/detail-panel-star-rating.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel-star-rating.svelte @@ -25,7 +25,7 @@ }; -{#if !authManager.key && $preferences?.ratings.enabled} +{#if !authManager.isSharedLink && $preferences?.ratings.enabled}
handlePromiseError(handleChangeRating(rating))} />
diff --git a/web/src/lib/components/asset-viewer/detail-panel-tags.svelte b/web/src/lib/components/asset-viewer/detail-panel-tags.svelte index 007e20b7c6..4dd05f520a 100644 --- a/web/src/lib/components/asset-viewer/detail-panel-tags.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel-tags.svelte @@ -37,7 +37,7 @@ -{#if isOwner && !authManager.key} +{#if isOwner && !authManager.isSharedLink}

{$t('tags').toUpperCase()}

diff --git a/web/src/lib/components/asset-viewer/detail-panel.svelte b/web/src/lib/components/asset-viewer/detail-panel.svelte index ef4ddc13ce..e3c29e5c1f 100644 --- a/web/src/lib/components/asset-viewer/detail-panel.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel.svelte @@ -85,7 +85,7 @@ const handleNewAsset = async (newAsset: AssetResponseDto) => { // TODO: check if reloading asset data is necessary - if (newAsset.id && !authManager.key) { + if (newAsset.id && !authManager.isSharedLink) { const data = await getAssetInfo({ id: asset.id }); people = data?.people || []; unassignedFaces = data?.unassignedFaces || []; @@ -195,7 +195,7 @@ - {#if !authManager.key && isOwner} + {#if !authManager.isSharedLink && isOwner}

{$t('people').toUpperCase()}

diff --git a/web/src/lib/components/asset-viewer/image-panorama-viewer.svelte b/web/src/lib/components/asset-viewer/image-panorama-viewer.svelte index d678b00ddb..996efa7f30 100644 --- a/web/src/lib/components/asset-viewer/image-panorama-viewer.svelte +++ b/web/src/lib/components/asset-viewer/image-panorama-viewer.svelte @@ -14,7 +14,7 @@ const { asset }: Props = $props(); const loadAssetData = async (id: string) => { - const data = await viewAsset({ id, size: AssetMediaSize.Preview, key: authManager.key }); + const data = await viewAsset({ ...authManager.params, id, size: AssetMediaSize.Preview }); return URL.createObjectURL(data); }; diff --git a/web/src/lib/components/assets/thumbnail/thumbnail.svelte b/web/src/lib/components/assets/thumbnail/thumbnail.svelte index e07e5e99c6..53dce2cdf8 100644 --- a/web/src/lib/components/assets/thumbnail/thumbnail.svelte +++ b/web/src/lib/components/assets/thumbnail/thumbnail.svelte @@ -270,13 +270,13 @@ {/if} - {#if !authManager.key && asset.isFavorite} + {#if !authManager.isSharedLink && asset.isFavorite}
{/if} - {#if !authManager.key && showArchiveIcon && asset.visibility === AssetVisibility.Archive} + {#if !authManager.isSharedLink && showArchiveIcon && asset.visibility === AssetVisibility.Archive}
diff --git a/web/src/lib/components/memory-page/memory-viewer.svelte b/web/src/lib/components/memory-page/memory-viewer.svelte index f1a15f4429..f724c4e811 100644 --- a/web/src/lib/components/memory-page/memory-viewer.svelte +++ b/web/src/lib/components/memory-page/memory-viewer.svelte @@ -69,7 +69,7 @@ let paused = $state(false); let current = $state(undefined); let currentMemoryAssetFull = $derived.by(async () => - current?.asset ? await getAssetInfo({ id: current.asset.id, key: authManager.key }) : undefined, + current?.asset ? await getAssetInfo({ ...authManager.params, id: current.asset.id }) : undefined, ); let currentTimelineAssets = $derived(current?.memory.assets.map((asset) => toTimelineAsset(asset)) || []); diff --git a/web/src/lib/components/pages/SharedLinkErrorPage.svelte b/web/src/lib/components/pages/SharedLinkErrorPage.svelte new file mode 100644 index 0000000000..9103a7710c --- /dev/null +++ b/web/src/lib/components/pages/SharedLinkErrorPage.svelte @@ -0,0 +1,14 @@ + + + + Oops! Error - Immich + + +
+

Page not found :/

+ {#if page.error?.message} +

{page.error.message}

+ {/if} +
diff --git a/web/src/lib/components/pages/SharedLinkPage.svelte b/web/src/lib/components/pages/SharedLinkPage.svelte new file mode 100644 index 0000000000..a3f5599077 --- /dev/null +++ b/web/src/lib/components/pages/SharedLinkPage.svelte @@ -0,0 +1,108 @@ + + + + {title} + + +{#if passwordRequired} +
+
+
{$t('password_required')}
+
+ {$t('sharing_enter_password')} +
+
+
+ + + +
+
+
+
+ + {#snippet leading()} + + {/snippet} + + {#snippet trailing()} + + {/snippet} + +
+{/if} + +{#if !passwordRequired && sharedLink?.type == SharedLinkType.Album} + +{/if} +{#if !passwordRequired && sharedLink?.type == SharedLinkType.Individual} +
+ +
+{/if} diff --git a/web/src/lib/components/photos-page/actions/create-shared-link.svelte b/web/src/lib/components/photos-page/actions/create-shared-link.svelte index e49cce24e3..40fea4acb9 100644 --- a/web/src/lib/components/photos-page/actions/create-shared-link.svelte +++ b/web/src/lib/components/photos-page/actions/create-shared-link.svelte @@ -15,7 +15,7 @@ }); if (sharedLink) { - await modalManager.show(QrCodeModal, { title: $t('view_link'), value: makeSharedLinkUrl(sharedLink.key) }); + await modalManager.show(QrCodeModal, { title: $t('view_link'), value: makeSharedLinkUrl(sharedLink) }); } }; diff --git a/web/src/lib/components/photos-page/actions/download-action.svelte b/web/src/lib/components/photos-page/actions/download-action.svelte index 73f1a77742..0a1376374c 100644 --- a/web/src/lib/components/photos-page/actions/download-action.svelte +++ b/web/src/lib/components/photos-page/actions/download-action.svelte @@ -4,11 +4,11 @@ import { authManager } from '$lib/managers/auth-manager.svelte'; import { downloadArchive, downloadFile } from '$lib/utils/asset-utils'; import { getAssetInfo } from '@immich/sdk'; + import { IconButton } from '@immich/ui'; import { mdiCloudDownloadOutline, mdiFileDownloadOutline, mdiFolderDownloadOutline } from '@mdi/js'; import { t } from 'svelte-i18n'; import MenuOption from '../../shared-components/context-menu/menu-option.svelte'; import { getAssetControlContext } from '../asset-select-control-bar.svelte'; - import { IconButton } from '@immich/ui'; interface Props { filename?: string; @@ -23,7 +23,7 @@ const assets = [...getAssets()]; if (assets.length === 1) { clearSelect(); - let asset = await getAssetInfo({ id: assets[0].id, key: authManager.key }); + let asset = await getAssetInfo({ ...authManager.params, id: assets[0].id }); await downloadFile(asset); return; } diff --git a/web/src/lib/components/photos-page/actions/link-live-photo-action.svelte b/web/src/lib/components/photos-page/actions/link-live-photo-action.svelte index 09ca94cb25..6e3a7c789c 100644 --- a/web/src/lib/components/photos-page/actions/link-live-photo-action.svelte +++ b/web/src/lib/components/photos-page/actions/link-live-photo-action.svelte @@ -1,15 +1,15 @@ diff --git a/web/src/lib/components/sharedlinks-page/shared-link-card.svelte b/web/src/lib/components/sharedlinks-page/shared-link-card.svelte index 3e827281e7..bd1c2924b0 100644 --- a/web/src/lib/components/sharedlinks-page/shared-link-card.svelte +++ b/web/src/lib/components/sharedlinks-page/shared-link-card.svelte @@ -1,16 +1,16 @@ + + diff --git a/web/src/routes/(user)/s/[slug]/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/s/[slug]/[[photos=photos]]/[[assetId=id]]/+page.svelte new file mode 100644 index 0000000000..25c24ab028 --- /dev/null +++ b/web/src/routes/(user)/s/[slug]/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -0,0 +1,12 @@ + + + diff --git a/web/src/routes/(user)/s/[slug]/[[photos=photos]]/[[assetId=id]]/+page.ts b/web/src/routes/(user)/s/[slug]/[[photos=photos]]/[[assetId=id]]/+page.ts new file mode 100644 index 0000000000..92d4dd042e --- /dev/null +++ b/web/src/routes/(user)/s/[slug]/[[photos=photos]]/[[assetId=id]]/+page.ts @@ -0,0 +1,4 @@ +import { loadSharedLink } from '$lib/utils/shared-links'; +import type { PageLoad } from './$types'; + +export const load = (async ({ params, url }) => loadSharedLink({ params, url })) satisfies PageLoad; diff --git a/web/src/routes/(user)/share/[key]/+error.svelte b/web/src/routes/(user)/share/[key]/+error.svelte index 9103a7710c..2c4d2a4d0e 100644 --- a/web/src/routes/(user)/share/[key]/+error.svelte +++ b/web/src/routes/(user)/share/[key]/+error.svelte @@ -1,14 +1,5 @@ - - Oops! Error - Immich - - -
-

Page not found :/

- {#if page.error?.message} -

{page.error.message}

- {/if} -
+ diff --git a/web/src/routes/(user)/share/[key]/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/share/[key]/[[photos=photos]]/[[assetId=id]]/+page.svelte index d16ba622e9..25c24ab028 100644 --- a/web/src/routes/(user)/share/[key]/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/share/[key]/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -1,97 +1,12 @@ - - {title} - - -{#if passwordRequired} -
-
-
{$t('password_required')}
-
- {$t('sharing_enter_password')} -
-
-
- - - -
-
-
-
- - {#snippet leading()} - - {/snippet} - - {#snippet trailing()} - - {/snippet} - -
-{/if} - -{#if !passwordRequired && sharedLink?.type == SharedLinkType.Album} - -{/if} -{#if !passwordRequired && sharedLink?.type == SharedLinkType.Individual} -
- -
-{/if} + diff --git a/web/src/routes/(user)/share/[key]/[[photos=photos]]/[[assetId=id]]/+page.ts b/web/src/routes/(user)/share/[key]/[[photos=photos]]/[[assetId=id]]/+page.ts index c0edb5e669..92d4dd042e 100644 --- a/web/src/routes/(user)/share/[key]/[[photos=photos]]/[[assetId=id]]/+page.ts +++ b/web/src/routes/(user)/share/[key]/[[photos=photos]]/[[assetId=id]]/+page.ts @@ -1,44 +1,4 @@ -import { getAssetThumbnailUrl, setSharedLink } from '$lib/utils'; -import { authenticate } from '$lib/utils/auth'; -import { getFormatter } from '$lib/utils/i18n'; -import { getAssetInfoFromParam } from '$lib/utils/navigation'; -import { getMySharedLink, isHttpError } from '@immich/sdk'; +import { loadSharedLink } from '$lib/utils/shared-links'; import type { PageLoad } from './$types'; -export const load = (async ({ params, url }) => { - const { key } = params; - await authenticate(url, { public: true }); - - const $t = await getFormatter(); - - try { - const [sharedLink, asset] = await Promise.all([getMySharedLink({ key }), getAssetInfoFromParam(params)]); - setSharedLink(sharedLink); - const assetCount = sharedLink.assets.length; - const assetId = sharedLink.album?.albumThumbnailAssetId || sharedLink.assets[0]?.id; - const assetPath = assetId ? getAssetThumbnailUrl(assetId) : '/feature-panel.png'; - - return { - sharedLink, - sharedLinkKey: key, - asset, - meta: { - title: sharedLink.album ? sharedLink.album.albumName : $t('public_share'), - description: sharedLink.description || $t('shared_photos_and_videos_count', { values: { assetCount } }), - imageUrl: assetPath, - }, - }; - } catch (error) { - if (isHttpError(error) && error.data.message === 'Invalid password') { - return { - passwordRequired: true, - sharedLinkKey: key, - meta: { - title: $t('password_required'), - }, - }; - } - - throw error; - } -}) satisfies PageLoad; +export const load = (async ({ params, url }) => loadSharedLink({ params, url })) satisfies PageLoad; diff --git a/web/src/test-data/factories/shared-link-factory.ts b/web/src/test-data/factories/shared-link-factory.ts index a057bc936b..5768a5f9f7 100644 --- a/web/src/test-data/factories/shared-link-factory.ts +++ b/web/src/test-data/factories/shared-link-factory.ts @@ -16,4 +16,5 @@ export const sharedLinkFactory = Sync.makeFactory({ allowUpload: Sync.each(() => faker.datatype.boolean()), allowDownload: Sync.each(() => faker.datatype.boolean()), showMetadata: Sync.each(() => faker.datatype.boolean()), + slug: null, }); From e52b9d15b526ee131f14a343d0494d4f82198f72 Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Tue, 29 Jul 2025 00:34:03 +0530 Subject: [PATCH 089/748] chore: bump dart sdk to 3.8 (#20355) * chore: bump dart sdk to 3.8 * chore: make build * make pigeon * chore: format files --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- mobile/lib/constants/colors.dart | 13 +- mobile/lib/constants/enums.dart | 11 +- mobile/lib/constants/filters.dart | 713 +-- mobile/lib/constants/locales.dart | 5 +- .../domain/models/asset/base_asset.model.dart | 6 +- .../models/asset/remote_asset.model.dart | 7 +- .../lib/domain/models/device_asset.model.dart | 12 +- mobile/lib/domain/models/log.model.dart | 13 +- mobile/lib/domain/models/memory.model.dart | 20 +- .../domain/models/search_result.model.dart | 15 +- mobile/lib/domain/models/setting.model.dart | 3 +- mobile/lib/domain/models/stack.model.dart | 14 +- mobile/lib/domain/models/timeline.model.dart | 14 +- mobile/lib/domain/models/user.model.dart | 39 +- .../domain/models/user_metadata.model.dart | 49 +- mobile/lib/domain/services/asset.service.dart | 6 +- mobile/lib/domain/services/hash.service.dart | 13 +- .../domain/services/local_sync.service.dart | 60 +- mobile/lib/domain/services/log.service.dart | 11 +- .../lib/domain/services/partner.service.dart | 14 +- .../domain/services/remote_album.service.dart | 33 +- .../lib/domain/services/search.service.dart | 12 +- mobile/lib/domain/services/store.service.dart | 12 +- .../domain/services/sync_stream.service.dart | 109 +- .../lib/domain/services/timeline.service.dart | 42 +- mobile/lib/domain/services/user.service.dart | 11 +- mobile/lib/domain/utils/background_sync.dart | 75 +- mobile/lib/domain/utils/event_stream.dart | 7 +- mobile/lib/entities/album.entity.dart | 9 +- mobile/lib/entities/album.entity.g.dart | 1299 +++--- .../android_device_asset.entity.g.dart | 310 +- mobile/lib/entities/asset.entity.dart | 148 +- mobile/lib/entities/asset.entity.g.dart | 2305 +++++----- mobile/lib/entities/backup_album.entity.dart | 18 +- .../lib/entities/backup_album.entity.g.dart | 374 +- .../entities/duplicated_asset.entity.g.dart | 269 +- mobile/lib/entities/etag.entity.g.dart | 419 +- .../entities/ios_device_asset.entity.g.dart | 580 ++- mobile/lib/extensions/asset_extensions.dart | 10 +- .../lib/extensions/collection_extensions.dart | 14 +- .../lib/extensions/datetime_extensions.dart | 6 +- .../maplibrecontroller_extensions.dart | 18 +- mobile/lib/extensions/scroll_extensions.dart | 22 +- mobile/lib/extensions/string_extensions.dart | 6 +- mobile/lib/extensions/theme_extensions.dart | 10 +- .../lib/extensions/translate_extensions.dart | 6 +- .../entities/asset_face.entity.drift.dart | 1086 +++-- .../entities/device_asset.entity.dart | 19 +- .../entities/device_asset.entity.g.dart | 623 ++- .../infrastructure/entities/exif.entity.dart | 118 +- .../entities/exif.entity.drift.dart | 1619 ++++--- .../entities/exif.entity.g.dart | 2084 +++++---- .../entities/local_album.entity.drift.dart | 530 ++- .../local_album_asset.entity.drift.dart | 610 +-- .../entities/local_asset.entity.dart | 26 +- .../entities/local_asset.entity.drift.dart | 818 ++-- .../infrastructure/entities/log.entity.g.dart | 913 ++-- .../entities/memory.entity.drift.dart | 1019 +++-- .../entities/memory_asset.entity.drift.dart | 573 ++- .../entities/merged_asset.drift.dart | 152 +- .../entities/partner.entity.drift.dart | 663 +-- .../entities/person.entity.drift.dart | 913 ++-- .../entities/remote_album.entity.drift.dart | 1052 +++-- .../remote_album_asset.entity.drift.dart | 611 +-- .../remote_album_user.entity.drift.dart | 689 +-- .../entities/remote_asset.entity.dart | 40 +- .../entities/remote_asset.entity.drift.dart | 1390 +++--- .../entities/stack.entity.drift.dart | 586 ++- .../entities/store.entity.g.dart | 320 +- .../infrastructure/entities/user.entity.dart | 56 +- .../entities/user.entity.drift.dart | 602 ++- .../entities/user.entity.g.dart | 979 ++-- .../entities/user_metadata.entity.drift.dart | 548 ++- .../repositories/asset_media.repository.dart | 26 +- .../repositories/backup.repository.dart | 18 +- .../repositories/db.repository.dart | 90 +- .../repositories/db.repository.drift.dart | 419 +- .../repositories/db.repository.steps.dart | 2887 +++++++----- .../repositories/exif.repository.dart | 4 +- .../repositories/local_album.repository.dart | 128 +- .../repositories/local_asset.repository.dart | 7 +- .../repositories/memory.repository.dart | 70 +- .../repositories/partner.repository.dart | 91 +- .../repositories/remote_album.repository.dart | 207 +- .../repositories/remote_asset.repository.dart | 100 +- .../repositories/search_api.repository.dart | 9 +- .../repositories/stack.repository.dart | 8 +- .../repositories/store.repository.dart | 34 +- .../repositories/sync_api.repository.dart | 10 +- .../repositories/sync_stream.repository.dart | 226 +- .../repositories/timeline.repository.dart | 401 +- .../repositories/user_api.repository.dart | 11 +- .../user_metadata.repository.dart | 20 +- .../infrastructure/utils/user.converter.dart | 100 +- mobile/lib/main.dart | 83 +- .../album_add_asset_response.model.dart | 15 +- .../lib/models/albums/album_search.model.dart | 6 +- .../albums/album_viewer_page_state.model.dart | 6 +- .../asset_selection_page_result.model.dart | 4 +- mobile/lib/models/asset_selection_state.dart | 15 +- .../models/auth/auxilary_endpoint.model.dart | 40 +- .../models/auth/biometric_status.model.dart | 10 +- .../models/backup/available_album.model.dart | 12 +- .../lib/models/backup/backup_state.model.dart | 5 +- .../backup/success_upload_asset.model.dart | 12 +- .../models/download/download_state.model.dart | 30 +- .../download/livephotos_medatada.model.dart | 30 +- .../models/folder/recursive_folder.model.dart | 6 +- .../lib/models/folder/root_folder.model.dart | 5 +- mobile/lib/models/map/map_marker.model.dart | 19 +- mobile/lib/models/memories/memory.model.dart | 15 +- .../search/search_curated_content.model.dart | 24 +- .../models/search/search_filter.model.dart | 77 +- .../models/search/search_result.model.dart | 15 +- .../server_info/server_config.model.dart | 16 +- .../server_info/server_disk_info.model.dart | 15 +- .../server_info/server_features.model.dart | 15 +- .../server_info/server_version.model.dart | 23 +- .../models/shared_link/shared_link.model.dart | 34 +- .../upload/share_intent_attachment.model.dart | 21 +- ...additional_shared_user_selection.page.dart | 72 +- .../album/album_asset_selection.page.dart | 20 +- .../lib/pages/album/album_control_button.dart | 6 +- mobile/lib/pages/album/album_date_range.dart | 9 +- mobile/lib/pages/album/album_description.dart | 5 +- .../lib/pages/album/album_options.page.dart | 49 +- .../pages/album/album_shared_user_icons.dart | 6 +- .../album_shared_user_selection.page.dart | 63 +- mobile/lib/pages/album/album_title.dart | 18 +- mobile/lib/pages/album/album_viewer.dart | 19 +- mobile/lib/pages/album/album_viewer.page.dart | 4 +- mobile/lib/pages/albums/albums.page.dart | 161 +- .../lib/pages/backup/album_preview.page.dart | 27 +- .../backup/backup_album_selection.page.dart | 120 +- .../pages/backup/backup_controller.page.dart | 148 +- .../lib/pages/backup/backup_options.page.dart | 4 +- .../lib/pages/backup/drift_backup.page.dart | 68 +- .../drift_backup_album_selection.page.dart | 143 +- .../backup/drift_upload_detail.page.dart | 172 +- .../backup/failed_backup_status.page.dart | 42 +- mobile/lib/pages/common/activities.page.dart | 13 +- mobile/lib/pages/common/app_log.page.dart | 70 +- .../lib/pages/common/app_log_detail.page.dart | 52 +- .../pages/common/change_experience.page.dart | 30 +- .../lib/pages/common/create_album.page.dart | 68 +- mobile/lib/pages/common/download_panel.dart | 58 +- .../common/gallery_stacked_children.dart | 10 +- .../lib/pages/common/gallery_viewer.page.dart | 102 +- .../pages/common/headers_settings.page.dart | 19 +- .../lib/pages/common/large_leading_tile.dart | 15 +- .../common/native_video_viewer.page.dart | 122 +- mobile/lib/pages/common/settings.page.dart | 89 +- .../lib/pages/common/splash_screen.page.dart | 27 +- .../lib/pages/common/tab_controller.page.dart | 60 +- mobile/lib/pages/common/tab_shell.page.dart | 66 +- mobile/lib/pages/editing/crop.page.dart | 55 +- mobile/lib/pages/editing/edit.page.dart | 111 +- mobile/lib/pages/editing/filter.page.dart | 55 +- mobile/lib/pages/library/archive.page.dart | 9 +- mobile/lib/pages/library/favorite.page.dart | 9 +- .../lib/pages/library/folder/folder.page.dart | 136 +- mobile/lib/pages/library/library.page.dart | 128 +- .../lib/pages/library/local_albums.page.dart | 27 +- .../lib/pages/library/locked/locked.page.dart | 45 +- .../pages/library/locked/pin_auth.page.dart | 30 +- .../library/partner/drift_partner.page.dart | 33 +- .../pages/library/partner/partner.page.dart | 34 +- .../library/partner/partner_detail.page.dart | 49 +- .../people/people_collection.page.dart | 25 +- .../places/places_collection.page.dart | 27 +- .../library/shared_link/shared_link.page.dart | 44 +- .../shared_link/shared_link_edit.page.dart | 191 +- mobile/lib/pages/library/trash.page.dart | 72 +- .../lib/pages/login/change_password.page.dart | 4 +- mobile/lib/pages/login/login.page.dart | 10 +- .../permission_onboarding.page.dart | 70 +- mobile/lib/pages/photos/memory.page.dart | 72 +- mobile/lib/pages/photos/photos.page.dart | 21 +- .../pages/search/all_motion_videos.page.dart | 11 +- mobile/lib/pages/search/all_people.page.dart | 9 +- mobile/lib/pages/search/all_places.page.dart | 15 +- mobile/lib/pages/search/all_videos.page.dart | 5 +- mobile/lib/pages/search/map/map.page.dart | 76 +- .../search/map/map_location_picker.page.dart | 30 +- .../lib/pages/search/person_result.page.dart | 58 +- .../lib/pages/search/recently_taken.page.dart | 11 +- mobile/lib/pages/search/search.page.dart | 272 +- .../settings/beta_sync_settings.page.dart | 8 +- .../pages/share_intent/share_intent.page.dart | 147 +- mobile/lib/platform/native_sync_api.g.dart | 44 +- .../pages/dev/feat_in_development.page.dart | 73 +- .../pages/dev/media_stat.page.dart | 97 +- .../presentation/pages/drift_album.page.dart | 13 +- .../pages/drift_archive.page.dart | 20 +- .../drift_asset_selection_timeline.page.dart | 33 +- .../pages/drift_create_album.page.dart | 152 +- .../pages/drift_favorite.page.dart | 20 +- .../pages/drift_library.page.dart | 137 +- .../pages/drift_local_album.page.dart | 51 +- .../pages/drift_locked_folder.page.dart | 24 +- .../presentation/pages/drift_memory.page.dart | 86 +- .../pages/drift_partner_detail.page.dart | 54 +- .../presentation/pages/drift_place.page.dart | 40 +- .../pages/drift_place_detail.page.dart | 22 +- .../pages/drift_recently_taken.page.dart | 26 +- .../pages/drift_remote_album.page.dart | 127 +- .../presentation/pages/drift_trash.page.dart | 28 +- .../pages/drift_user_selection.page.dart | 68 +- .../presentation/pages/drift_video.page.dart | 24 +- .../pages/local_timeline.page.dart | 12 +- .../pages/search/drift_search.page.dart | 287 +- .../search/paginated_search.provider.dart | 5 +- .../archive_action_button.widget.dart | 5 +- .../base_action_button.widget.dart | 13 +- .../cast_action_button.widget.dart | 5 +- .../delete_action_button.widget.dart | 9 +- .../delete_local_action_button.widget.dart | 5 +- .../delete_trash_action_button.widget.dart | 11 +- .../edit_location_action_button.widget.dart | 5 +- .../favorite_action_button.widget.dart | 11 +- ...emove_from_album_action_button.widget.dart | 6 +- .../restore_trash_action_button.widget.dart | 17 +- .../stack_action_button.widget.dart | 5 +- .../trash_action_button.widget.dart | 5 +- .../unarchive_action_button.widget.dart | 5 +- .../unfavorite_action_button.widget.dart | 11 +- .../unstack_action_button.widget.dart | 5 +- .../upload_action_button.widget.dart | 5 +- .../widgets/album/album_selector.widget.dart | 309 +- .../asset_viewer/asset_stack.provider.dart | 6 +- .../asset_viewer/asset_stack.widget.dart | 34 +- .../asset_viewer/asset_viewer.page.dart | 86 +- .../asset_viewer/asset_viewer.state.dart | 10 +- .../asset_viewer/bottom_bar.widget.dart | 26 +- .../asset_viewer/bottom_sheet.widget.dart | 32 +- .../bottom_sheet/location_details.widget.dart | 37 +- .../asset_viewer/top_app_bar.widget.dart | 27 +- .../asset_viewer/video_viewer.widget.dart | 123 +- .../video_viewer_controls.widget.dart | 38 +- .../backup/backup_toggle_button.widget.dart | 72 +- .../archive_bottom_sheet.widget.dart | 12 +- .../base_bottom_sheet.widget.dart | 10 +- .../favorite_bottom_sheet.widget.dart | 12 +- .../general_bottom_sheet.widget.dart | 31 +- .../remote_album_bottom_sheet.widget.dart | 17 +- .../widgets/images/image_provider.dart | 30 +- .../images/local_album_thumbnail.widget.dart | 26 +- .../widgets/images/local_image_provider.dart | 46 +- .../widgets/images/remote_image_provider.dart | 24 +- .../widgets/images/thumb_hash_provider.dart | 19 +- .../widgets/images/thumbnail.widget.dart | 36 +- .../widgets/images/thumbnail_tile.widget.dart | 116 +- .../memory/memory_bottom_info.widget.dart | 72 +- .../widgets/memory/memory_card.widget.dart | 46 +- .../widgets/memory/memory_lane.widget.dart | 47 +- .../drift_album_option.widget.dart | 48 +- .../widgets/timeline/fixed/row.dart | 12 +- .../widgets/timeline/fixed/segment.model.dart | 53 +- .../timeline/fixed/segment_builder.dart | 3 +- .../widgets/timeline/header.widget.dart | 51 +- .../widgets/timeline/scrubber.widget.dart | 158 +- .../widgets/timeline/segment.model.dart | 4 +- .../widgets/timeline/segment_builder.dart | 36 +- .../widgets/timeline/timeline.state.dart | 58 +- .../widgets/timeline/timeline.widget.dart | 143 +- mobile/lib/providers/activity.provider.dart | 9 +- mobile/lib/providers/activity.provider.g.dart | 77 +- .../activity_statistics.provider.g.dart | 65 +- .../lib/providers/album/album.provider.dart | 34 +- .../album/album_sort_by_options.provider.dart | 28 +- .../album_sort_by_options.provider.g.dart | 32 +- .../providers/album/album_title.provider.dart | 4 +- .../album/album_viewer.provider.dart | 29 +- .../album/current_album.provider.g.dart | 15 +- mobile/lib/providers/api.provider.g.dart | 5 +- .../providers/app_life_cycle.provider.dart | 31 +- mobile/lib/providers/asset.provider.dart | 29 +- .../asset_viewer/asset_people.provider.g.dart | 77 +- .../current_asset.provider.g.dart | 15 +- .../asset_viewer/download.provider.dart | 59 +- .../share_intent_upload.provider.dart | 27 +- .../video_player_controls_provider.dart | 14 +- .../video_player_value_provider.dart | 40 +- mobile/lib/providers/auth.provider.dart | 44 +- .../lib/providers/backup/backup.provider.dart | 172 +- .../backup/backup_album.provider.dart | 4 +- .../backup/backup_verification.provider.dart | 16 +- .../backup_verification.provider.g.dart | 16 +- .../backup/drift_backup.provider.dart | 88 +- .../backup/manual_upload.provider.dart | 95 +- mobile/lib/providers/cast.provider.dart | 22 +- mobile/lib/providers/folder.provider.dart | 11 +- .../gallery_permission.provider.dart | 5 +- .../providers/image/cache/image_loader.dart | 11 +- .../cache/remote_image_cache_manager.dart | 9 +- .../cache/thumbnail_image_cache_manager.dart | 9 +- .../image/immich_local_image_provider.dart | 12 +- .../immich_local_thumbnail_provider.dart | 24 +- .../image/immich_remote_image_provider.dart | 38 +- .../immich_remote_thumbnail_provider.dart | 38 +- .../lib/providers/immich_logo_provider.g.dart | 5 +- .../infrastructure/action.provider.dart | 148 +- .../infrastructure/album.provider.dart | 5 +- .../asset_viewer/current_asset.provider.dart | 20 +- .../infrastructure/db.provider.g.dart | 5 +- .../infrastructure/person.provider.dart | 4 +- .../infrastructure/remote_album.provider.dart | 102 +- .../infrastructure/search.provider.dart | 8 +- .../infrastructure/stack.provider.dart | 4 +- .../infrastructure/storage.provider.dart | 4 +- .../infrastructure/store.provider.g.dart | 5 +- .../infrastructure/sync.provider.dart | 8 +- .../infrastructure/timeline.provider.dart | 16 +- .../infrastructure/user.provider.dart | 17 +- .../infrastructure/user.provider.g.dart | 5 +- mobile/lib/providers/local_auth.provider.dart | 22 +- .../providers/map/map_marker.provider.g.dart | 5 +- .../providers/map/map_service.provider.g.dart | 5 +- .../lib/providers/map/map_state.provider.dart | 45 +- .../providers/map/map_state.provider.g.dart | 16 +- mobile/lib/providers/network.provider.dart | 4 +- .../notification_permission.provider.dart | 4 +- mobile/lib/providers/partner.provider.dart | 54 +- .../search/paginated_search.provider.dart | 14 +- .../search/paginated_search.provider.g.dart | 16 +- .../lib/providers/search/people.provider.dart | 10 +- .../providers/search/people.provider.g.dart | 116 +- .../search/search_filter.provider.g.dart | 46 +- .../search/search_page_state.provider.dart | 16 +- .../lib/providers/server_info.provider.dart | 85 +- .../lib/providers/shared_link.provider.dart | 4 +- .../lib/providers/sync_status.provider.dart | 28 +- mobile/lib/providers/tab.provider.dart | 4 +- mobile/lib/providers/theme.provider.dart | 9 +- mobile/lib/providers/timeline.provider.dart | 37 +- .../timeline/multiselect.provider.dart | 63 +- mobile/lib/providers/trash.provider.dart | 8 +- .../upload_profile_image.provider.dart | 34 +- mobile/lib/providers/websocket.provider.dart | 79 +- .../repositories/activity_api.repository.dart | 21 +- mobile/lib/repositories/album.repository.dart | 24 +- .../repositories/album_api.repository.dart | 53 +- .../repositories/album_media.repository.dart | 48 +- mobile/lib/repositories/asset.repository.dart | 78 +- .../repositories/asset_api.repository.dart | 66 +- .../repositories/asset_media.repository.dart | 8 +- .../lib/repositories/auth_api.repository.dart | 13 +- .../repositories/biometric.repository.dart | 9 +- .../lib/repositories/download.repository.dart | 5 +- .../drift_album_api_repository.dart | 50 +- .../repositories/file_media.repository.dart | 49 +- .../repositories/folder_api.repository.dart | 6 +- mobile/lib/repositories/gcast.repository.dart | 10 +- .../lib/repositories/partner.repository.dart | 14 +- .../repositories/partner_api.repository.dart | 22 +- .../repositories/person_api.repository.dart | 20 +- .../repositories/sessions_api.repository.dart | 18 +- .../share_handler.repository.dart | 8 +- .../lib/repositories/timeline.repository.dart | 25 +- .../lib/repositories/upload.repository.dart | 25 +- .../lib/repositories/widget.repository.dart | 5 +- .../lib/routing/app_navigation_observer.dart | 29 +- mobile/lib/routing/duplicate_guard.dart | 4 +- mobile/lib/routing/locked_guard.dart | 10 +- mobile/lib/routing/router.dart | 289 +- mobile/lib/routing/router.gr.dart | 549 +-- mobile/lib/services/action.service.dart | 76 +- mobile/lib/services/activity.service.dart | 19 +- mobile/lib/services/album.service.dart | 143 +- mobile/lib/services/api.service.dart | 16 +- mobile/lib/services/app_settings.service.dart | 69 +- mobile/lib/services/asset.service.dart | 133 +- mobile/lib/services/auth.service.dart | 5 +- mobile/lib/services/background.service.dart | 105 +- mobile/lib/services/backup.service.dart | 106 +- .../services/backup_verification.service.dart | 83 +- mobile/lib/services/deep_link.service.dart | 18 +- mobile/lib/services/download.service.dart | 49 +- mobile/lib/services/entity.service.dart | 10 +- mobile/lib/services/folder.service.dart | 34 +- mobile/lib/services/gcast.service.dart | 47 +- mobile/lib/services/hash.service.dart | 25 +- mobile/lib/services/local_auth.service.dart | 6 +- .../services/local_files_manager.service.dart | 9 +- .../services/local_notification.service.dart | 34 +- mobile/lib/services/memory.service.dart | 29 +- mobile/lib/services/network.service.dart | 5 +- mobile/lib/services/oauth.service.dart | 38 +- mobile/lib/services/partner.service.dart | 16 +- mobile/lib/services/person.service.dart | 14 +- mobile/lib/services/search.service.dart | 6 +- .../lib/services/secure_storage.service.dart | 4 +- mobile/lib/services/server_info.service.dart | 6 +- mobile/lib/services/share.service.dart | 14 +- mobile/lib/services/share_intent_service.dart | 10 +- mobile/lib/services/shared_link.service.dart | 4 +- mobile/lib/services/stack.service.dart | 19 +- mobile/lib/services/sync.service.dart | 251 +- mobile/lib/services/timeline.service.dart | 31 +- mobile/lib/services/trash.service.dart | 20 +- mobile/lib/services/upload.service.dart | 58 +- mobile/lib/services/widget.service.dart | 4 +- mobile/lib/theme/color_scheme.dart | 45 +- mobile/lib/theme/dynamic_theme.dart | 10 +- mobile/lib/theme/theme_data.dart | 111 +- mobile/lib/utils/backup_progress.dart | 6 +- mobile/lib/utils/bootstrap.dart | 5 +- .../lib/utils/cache/custom_image_cache.dart | 6 +- mobile/lib/utils/color_filter_generator.dart | 8 +- mobile/lib/utils/debounce.dart | 20 +- .../utils/draggable_scroll_controller.dart | 14 +- .../utils/hooks/app_settings_update_hook.dart | 9 +- mobile/lib/utils/hooks/blurhash_hook.dart | 8 +- .../lib/utils/hooks/crop_controller_hook.dart | 6 +- mobile/lib/utils/hooks/interval_hook.dart | 11 +- mobile/lib/utils/hooks/timer_hook.dart | 17 +- mobile/lib/utils/http_ssl_cert_override.dart | 6 +- mobile/lib/utils/http_ssl_options.dart | 15 +- mobile/lib/utils/image_url_builder.dart | 40 +- mobile/lib/utils/immich_loading_overlay.dart | 5 +- mobile/lib/utils/isolate.dart | 10 +- mobile/lib/utils/map_utils.dart | 62 +- mobile/lib/utils/migration.dart | 83 +- mobile/lib/utils/openapi_patching.dart | 12 +- mobile/lib/utils/remote_album.utils.dart | 45 +- mobile/lib/utils/selection_handlers.dart | 70 +- mobile/lib/utils/throttle.dart | 10 +- mobile/lib/utils/thumbnail_utils.dart | 11 +- mobile/lib/utils/url_helper.dart | 34 +- mobile/lib/utils/version_compatibility.dart | 7 +- .../activities/activity_text_field.dart | 34 +- .../lib/widgets/activities/activity_tile.dart | 27 +- .../activities/dismissible_activity.dart | 21 +- .../album/add_to_album_bottom_sheet.dart | 62 +- .../album/add_to_album_sliverlist.dart | 11 +- .../album/album_action_filled_button.dart | 27 +- .../widgets/album/album_thumbnail_card.dart | 46 +- .../album/album_thumbnail_listtile.dart | 51 +- .../widgets/album/album_title_text_field.dart | 19 +- .../widgets/album/album_viewer_appbar.dart | 60 +- .../album_viewer_editable_description.dart | 34 +- .../album/album_viewer_editable_title.dart | 47 +- .../album/remote_album_shared_user_icons.dart | 11 +- .../album/shared_album_thumbnail_image.dart | 10 +- .../widgets/asset_grid/asset_drag_region.dart | 16 +- .../asset_grid/asset_grid_data_structure.dart | 60 +- .../asset_grid/control_bottom_app_bar.dart | 110 +- .../lib/widgets/asset_grid/delete_dialog.dart | 38 +- .../disable_multi_select_button.dart | 16 +- .../asset_grid/draggable_scrollbar.dart | 160 +- .../draggable_scrollbar_custom.dart | 117 +- .../asset_grid/group_divider_title.dart | 15 +- .../widgets/asset_grid/immich_asset_grid.dart | 37 +- .../asset_grid/immich_asset_grid_view.dart | 107 +- .../widgets/asset_grid/multiselect_grid.dart | 165 +- .../multiselect_grid_status_indicator.dart | 19 +- .../widgets/asset_grid/thumbnail_image.dart | 171 +- .../asset_grid/thumbnail_placeholder.dart | 13 +- .../lib/widgets/asset_grid/upload_dialog.dart | 14 +- .../asset_viewer/advanced_bottom_sheet.dart | 40 +- .../asset_viewer/animated_play_pause.dart | 7 +- .../asset_viewer/bottom_gallery_bar.dart | 69 +- .../lib/widgets/asset_viewer/cast_dialog.dart | 44 +- .../asset_viewer/center_play_button.dart | 10 +- .../custom_video_player_controls.dart | 38 +- .../asset_viewer/description_input.dart | 39 +- .../detail_panel/asset_date_time.dart | 14 +- .../detail_panel/asset_details.dart | 6 +- .../detail_panel/asset_location.dart | 30 +- .../detail_panel/camera_info.dart | 15 +- .../asset_viewer/detail_panel/exif_map.dart | 23 +- .../asset_viewer/detail_panel/file_info.dart | 15 +- .../detail_panel/people_info.dart | 19 +- .../asset_viewer/formatted_duration.dart | 6 +- .../widgets/asset_viewer/gallery_app_bar.dart | 16 +- .../asset_viewer/top_control_app_bar.dart | 56 +- .../widgets/asset_viewer/video_controls.dart | 5 +- .../widgets/asset_viewer/video_position.dart | 23 +- .../lib/widgets/backup/album_info_card.dart | 53 +- .../widgets/backup/album_info_list_tile.dart | 33 +- .../lib/widgets/backup/asset_info_table.dart | 51 +- .../lib/widgets/backup/backup_info_card.dart | 31 +- .../backup/current_backup_asset_info_box.dart | 11 +- .../backup/drift_album_info_list_tile.dart | 29 +- mobile/lib/widgets/backup/error_chip.dart | 5 +- .../lib/widgets/backup/error_chip_text.dart | 6 +- .../backup/icloud_download_progress_bar.dart | 25 +- .../widgets/backup/ios_debug_info_tile.dart | 28 +- .../widgets/backup/upload_progress_bar.dart | 29 +- mobile/lib/widgets/backup/upload_stats.dart | 21 +- .../common/app_bar_dialog/app_bar_dialog.dart | 120 +- .../app_bar_dialog/app_bar_profile_info.dart | 50 +- .../app_bar_dialog/app_bar_server_info.dart | 57 +- mobile/lib/widgets/common/confirm_dialog.dart | 14 +- .../lib/widgets/common/date_time_picker.dart | 94 +- .../common/delayed_loading_indicator.dart | 17 +- mobile/lib/widgets/common/drag_sheet.dart | 12 +- .../widgets/common/dropdown_search_menu.dart | 37 +- .../common/fade_in_placeholder_image.dart | 7 +- mobile/lib/widgets/common/immich_app_bar.dart | 72 +- mobile/lib/widgets/common/immich_image.dart | 37 +- .../common/immich_loading_indicator.dart | 32 +- mobile/lib/widgets/common/immich_logo.dart | 6 +- .../widgets/common/immich_sliver_app_bar.dart | 104 +- .../lib/widgets/common/immich_thumbnail.dart | 48 +- .../lib/widgets/common/immich_title_text.dart | 10 +- mobile/lib/widgets/common/immich_toast.dart | 40 +- .../common/local_album_sliver_app_bar.dart | 8 +- .../lib/widgets/common/location_picker.dart | 48 +- .../common/mesmerizing_sliver_app_bar.dart | 144 +- .../common/remote_album_sliver_app_bar.dart | 192 +- .../widgets/common/scaffold_error_body.dart | 12 +- mobile/lib/widgets/common/search_field.dart | 36 +- .../common/selection_sliver_app_bar.dart | 29 +- mobile/lib/widgets/common/share_dialog.dart | 5 +- .../widgets/common/thumbhash_placeholder.dart | 11 +- .../widgets/common/user_circle_avatar.dart | 15 +- .../widgets/forms/change_password_form.dart | 40 +- .../lib/widgets/forms/login/email_input.dart | 12 +- .../lib/widgets/forms/login/loading_icon.dart | 10 +- .../lib/widgets/forms/login/login_button.dart | 14 +- .../lib/widgets/forms/login/login_form.dart | 110 +- .../forms/login/o_auth_login_button.dart | 5 +- .../widgets/forms/login/password_input.dart | 16 +- .../forms/login/server_endpoint_input.dart | 7 +- mobile/lib/widgets/forms/pin_input.dart | 34 +- .../widgets/forms/pin_registration_form.dart | 32 +- .../widgets/forms/pin_verification_form.dart | 10 +- mobile/lib/widgets/map/map_app_bar.dart | 56 +- mobile/lib/widgets/map/map_asset_grid.dart | 86 +- mobile/lib/widgets/map/map_bottom_sheet.dart | 10 +- .../map_settings/map_settings_list_tile.dart | 12 +- .../map_settings_time_dropdown.dart | 57 +- .../map/map_settings/map_theme_picker.dart | 15 +- .../lib/widgets/map/map_theme_override.dart | 6 +- mobile/lib/widgets/map/map_thumbnail.dart | 6 +- .../map/positioned_asset_marker_icon.dart | 50 +- .../widgets/memories/memory_bottom_info.dart | 62 +- mobile/lib/widgets/memories/memory_card.dart | 57 +- .../lib/widgets/memories/memory_epilogue.dart | 38 +- mobile/lib/widgets/memories/memory_lane.dart | 48 +- .../memories/memory_progress_indicator.dart | 15 +- mobile/lib/widgets/photo_view/photo_view.dart | 100 +- .../photo_view/photo_view_gallery.dart | 39 +- .../src/controller/photo_view_controller.dart | 35 +- .../photo_view_controller_delegate.dart | 48 +- .../photo_view_scalestate_controller.dart | 5 +- .../photo_view/src/core/photo_view_core.dart | 80 +- .../src/core/photo_view_gesture_detector.dart | 35 +- .../src/core/photo_view_hit_corners.dart | 5 +- .../src/photo_view_default_widgets.dart | 14 +- .../photo_view/src/photo_view_wrappers.dart | 27 +- .../src/utils/ignorable_change_notifier.dart | 6 +- .../src/utils/photo_view_utils.dart | 25 +- .../widgets/search/curated_people_row.dart | 34 +- .../widgets/search/curated_places_row.dart | 4 +- mobile/lib/widgets/search/explore_grid.dart | 28 +- .../widgets/search/person_name_edit_form.dart | 29 +- .../search/search_filter/camera_picker.dart | 49 +- .../search/search_filter/common/dropdown.dart | 4 +- .../search_filter/display_option_picker.dart | 27 +- .../filter_bottom_sheet_scaffold.dart | 5 +- .../search/search_filter/location_picker.dart | 53 +- .../search/search_filter/people_picker.dart | 18 +- .../search_filter/search_filter_chip.dart | 38 +- .../widgets/search/search_map_thumbnail.dart | 16 +- .../widgets/search/search_row_section.dart | 5 +- .../lib/widgets/search/search_row_title.dart | 17 +- .../widgets/search/thumbnail_with_info.dart | 7 +- .../search/thumbnail_with_info_container.dart | 11 +- .../widgets/settings/advanced_settings.dart | 5 +- .../asset_list_group_settings.dart | 24 +- .../asset_list_layout_settings.dart | 4 +- .../asset_list_settings.dart | 9 +- .../asset_viewer_settings.dart | 14 +- .../image_viewer_quality_setting.dart | 9 +- .../video_viewer_settings.dart | 4 +- .../backup_settings/background_settings.dart | 103 +- .../backup_settings/backup_settings.dart | 18 +- .../beta_sync_settings.dart | 160 +- .../beta_sync_settings/entity_count_tile.dart | 37 +- .../settings/beta_timeline_list_tile.dart | 91 +- .../custome_proxy_headers_settings.dart | 8 +- .../widgets/settings/language_settings.dart | 104 +- .../settings/local_storage_settings.dart | 19 +- .../networking_settings/endpoint_input.dart | 19 +- .../external_network_preference.dart | 62 +- .../local_network_preference.dart | 88 +- .../networking_settings.dart | 129 +- .../settings/notification_setting.dart | 22 +- .../preference_settings/haptic_setting.dart | 4 +- .../preference_setting.dart | 14 +- .../primary_color_setting.dart | 43 +- .../preference_settings/theme_setting.dart | 4 +- .../settings/settings_button_list_tile.dart | 11 +- .../lib/widgets/settings/settings_card.dart | 22 +- .../settings/settings_radio_list_tile.dart | 14 +- .../settings/settings_slider_list_tile.dart | 7 +- .../settings/settings_sub_page_scaffold.dart | 12 +- .../widgets/settings/settings_sub_title.dart | 10 +- .../settings/settings_switch_list_tile.dart | 13 +- .../settings/ssl_client_cert_settings.dart | 54 +- .../widgets/shared_link/shared_link_item.dart | 72 +- mobile/pubspec.lock | 2 +- mobile/pubspec.yaml | 2 +- .../domain/services/hash_service_test.dart | 15 +- .../domain/services/log_service_test.dart | 33 +- .../domain/services/store_service_test.dart | 20 +- .../services/sync_stream_service_test.dart | 157 +- .../domain/services/user_service_test.dart | 18 +- .../test/drift/main/generated/schema_v1.dart | 3698 ++++++++++----- .../test/drift/main/generated/schema_v2.dart | 3698 ++++++++++----- .../test/drift/main/generated/schema_v3.dart | 3693 ++++++++++----- .../test/drift/main/generated/schema_v4.dart | 4002 +++++++++++------ mobile/test/fixtures/album.stub.dart | 29 +- mobile/test/fixtures/sync_stream.stub.dart | 32 +- .../local_album_repository_test.dart | 39 +- .../repositories/store_repository_test.dart | 31 +- .../sync_api_repository_test.dart | 174 +- mobile/test/mock_http_override.dart | 9 +- .../activity/activities_page_test.dart | 199 +- .../activity/activity_provider_test.dart | 123 +- .../activity_statistics_provider_test.dart | 33 +- .../activity/activity_text_field_test.dart | 91 +- .../modules/activity/activity_tile_test.dart | 84 +- .../activity/dismissible_activity_test.dart | 28 +- .../album_sort_by_options_provider_test.dart | 184 +- .../extensions/asset_extensions_test.dart | 21 +- .../extensions/builtin_extensions_test.dart | 9 +- .../home/asset_grid_data_structure_test.dart | 30 +- .../modules/map/map_theme_override_test.dart | 9 +- .../modules/shared/sync_service_test.dart | 79 +- .../test/modules/utils/async_mutex_test.dart | 28 +- .../modules/utils/thumbnail_utils_test.dart | 21 +- .../utils/version_compatibility_test.dart | 5 +- .../test/pages/search/search.page_test.dart | 40 +- mobile/test/services/album.service_test.dart | 90 +- mobile/test/services/asset.service_test.dart | 8 +- mobile/test/services/auth.service_test.dart | 84 +- mobile/test/services/entity.service_test.dart | 54 +- mobile/test/services/hash_service_test.dart | 100 +- mobile/test/test_utils.dart | 27 +- mobile/test/widget_tester_extensions.dart | 5 +- 643 files changed, 32561 insertions(+), 35292 deletions(-) diff --git a/mobile/lib/constants/colors.dart b/mobile/lib/constants/colors.dart index 1614a308e0..069ed519cf 100644 --- a/mobile/lib/constants/colors.dart +++ b/mobile/lib/constants/colors.dart @@ -1,17 +1,6 @@ import 'package:flutter/material.dart'; -enum ImmichColorPreset { - indigo, - deepPurple, - pink, - red, - orange, - yellow, - lime, - green, - cyan, - slateGray, -} +enum ImmichColorPreset { indigo, deepPurple, pink, red, orange, yellow, lime, green, cyan, slateGray } const ImmichColorPreset defaultColorPreset = ImmichColorPreset.indigo; const String defaultColorPresetName = "indigo"; diff --git a/mobile/lib/constants/enums.dart b/mobile/lib/constants/enums.dart index febc71032e..6ec0ce37ea 100644 --- a/mobile/lib/constants/enums.dart +++ b/mobile/lib/constants/enums.dart @@ -1,13 +1,6 @@ -enum SortOrder { - asc, - desc, -} +enum SortOrder { asc, desc } -enum TextSearchType { - context, - filename, - description, -} +enum TextSearchType { context, filename, description } enum AssetVisibilityEnum { timeline, hidden, archive, locked } diff --git a/mobile/lib/constants/filters.dart b/mobile/lib/constants/filters.dart index 61597f08d1..e77bcfbf1f 100644 --- a/mobile/lib/constants/filters.dart +++ b/mobile/lib/constants/filters.dart @@ -2,511 +2,49 @@ import 'package:flutter/material.dart'; const List filters = [ //Original - ColorFilter.matrix([ - 1, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0]), //Vintage - ColorFilter.matrix([ - 0.8, - 0.1, - 0.1, - 0, - 20, - 0.1, - 0.8, - 0.1, - 0, - 20, - 0.1, - 0.1, - 0.8, - 0, - 20, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.8, 0.1, 0.1, 0, 20, 0.1, 0.8, 0.1, 0, 20, 0.1, 0.1, 0.8, 0, 20, 0, 0, 0, 1, 0]), //Mood - ColorFilter.matrix([ - 1.2, - 0.1, - 0.1, - 0, - 10, - 0.1, - 1, - 0.1, - 0, - 10, - 0.1, - 0.1, - 1, - 0, - 10, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.2, 0.1, 0.1, 0, 10, 0.1, 1, 0.1, 0, 10, 0.1, 0.1, 1, 0, 10, 0, 0, 0, 1, 0]), //Crisp - ColorFilter.matrix([ - 1.2, - 0, - 0, - 0, - 0, - 0, - 1.2, - 0, - 0, - 0, - 0, - 0, - 1.2, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.2, 0, 0, 0, 0, 0, 1.2, 0, 0, 0, 0, 0, 1.2, 0, 0, 0, 0, 0, 1, 0]), //Cool - ColorFilter.matrix([ - 0.9, - 0, - 0.2, - 0, - 0, - 0, - 1, - 0.1, - 0, - 0, - 0.1, - 0, - 1.2, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.9, 0, 0.2, 0, 0, 0, 1, 0.1, 0, 0, 0.1, 0, 1.2, 0, 0, 0, 0, 0, 1, 0]), //Blush - ColorFilter.matrix([ - 1.1, - 0.1, - 0.1, - 0, - 10, - 0.1, - 1, - 0.1, - 0, - 10, - 0.1, - 0.1, - 1, - 0, - 5, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.1, 0.1, 0.1, 0, 10, 0.1, 1, 0.1, 0, 10, 0.1, 0.1, 1, 0, 5, 0, 0, 0, 1, 0]), //Sunkissed - ColorFilter.matrix([ - 1.3, - 0, - 0.1, - 0, - 15, - 0, - 1.1, - 0.1, - 0, - 10, - 0, - 0, - 0.9, - 0, - 5, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.3, 0, 0.1, 0, 15, 0, 1.1, 0.1, 0, 10, 0, 0, 0.9, 0, 5, 0, 0, 0, 1, 0]), //Fresh - ColorFilter.matrix([ - 1.2, - 0, - 0, - 0, - 20, - 0, - 1.2, - 0, - 0, - 20, - 0, - 0, - 1.1, - 0, - 20, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.2, 0, 0, 0, 20, 0, 1.2, 0, 0, 20, 0, 0, 1.1, 0, 20, 0, 0, 0, 1, 0]), //Classic - ColorFilter.matrix([ - 1.1, - 0, - -0.1, - 0, - 10, - -0.1, - 1.1, - 0.1, - 0, - 5, - 0, - -0.1, - 1.1, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.1, 0, -0.1, 0, 10, -0.1, 1.1, 0.1, 0, 5, 0, -0.1, 1.1, 0, 0, 0, 0, 0, 1, 0]), //Lomo-ish - ColorFilter.matrix([ - 1.5, - 0, - 0.1, - 0, - 0, - 0, - 1.45, - 0, - 0, - 0, - 0.1, - 0, - 1.3, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.5, 0, 0.1, 0, 0, 0, 1.45, 0, 0, 0, 0.1, 0, 1.3, 0, 0, 0, 0, 0, 1, 0]), //Nashville - ColorFilter.matrix([ - 1.2, - 0.15, - -0.15, - 0, - 15, - 0.1, - 1.1, - 0.1, - 0, - 10, - -0.05, - 0.2, - 1.25, - 0, - 5, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.2, 0.15, -0.15, 0, 15, 0.1, 1.1, 0.1, 0, 10, -0.05, 0.2, 1.25, 0, 5, 0, 0, 0, 1, 0]), //Valencia - ColorFilter.matrix([ - 1.15, - 0.1, - 0.1, - 0, - 20, - 0.1, - 1.1, - 0, - 0, - 10, - 0.1, - 0.1, - 1.2, - 0, - 5, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.15, 0.1, 0.1, 0, 20, 0.1, 1.1, 0, 0, 10, 0.1, 0.1, 1.2, 0, 5, 0, 0, 0, 1, 0]), //Clarendon - ColorFilter.matrix([ - 1.2, - 0, - 0, - 0, - 10, - 0, - 1.25, - 0, - 0, - 10, - 0, - 0, - 1.3, - 0, - 10, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.2, 0, 0, 0, 10, 0, 1.25, 0, 0, 10, 0, 0, 1.3, 0, 10, 0, 0, 0, 1, 0]), //Moon - ColorFilter.matrix([ - 0.33, - 0.33, - 0.33, - 0, - 0, - 0.33, - 0.33, - 0.33, - 0, - 0, - 0.33, - 0.33, - 0.33, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.33, 0.33, 0.33, 0, 0, 0.33, 0.33, 0.33, 0, 0, 0.33, 0.33, 0.33, 0, 0, 0, 0, 0, 1, 0]), //Willow - ColorFilter.matrix([ - 0.5, - 0.5, - 0.5, - 0, - 20, - 0.5, - 0.5, - 0.5, - 0, - 20, - 0.5, - 0.5, - 0.5, - 0, - 20, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.5, 0.5, 0.5, 0, 20, 0.5, 0.5, 0.5, 0, 20, 0.5, 0.5, 0.5, 0, 20, 0, 0, 0, 1, 0]), //Kodak - ColorFilter.matrix([ - 1.3, - 0.1, - -0.1, - 0, - 10, - 0, - 1.25, - 0.1, - 0, - 10, - 0, - -0.1, - 1.1, - 0, - 5, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.3, 0.1, -0.1, 0, 10, 0, 1.25, 0.1, 0, 10, 0, -0.1, 1.1, 0, 5, 0, 0, 0, 1, 0]), //Frost - ColorFilter.matrix([ - 0.8, - 0.2, - 0.1, - 0, - 0, - 0.2, - 1.1, - 0.1, - 0, - 0, - 0.1, - 0.1, - 1.2, - 0, - 10, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.8, 0.2, 0.1, 0, 0, 0.2, 1.1, 0.1, 0, 0, 0.1, 0.1, 1.2, 0, 10, 0, 0, 0, 1, 0]), //Night Vision - ColorFilter.matrix([ - 0.1, - 0.95, - 0.2, - 0, - 0, - 0.1, - 1.5, - 0.1, - 0, - 0, - 0.2, - 0.7, - 0, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.1, 0.95, 0.2, 0, 0, 0.1, 1.5, 0.1, 0, 0, 0.2, 0.7, 0, 0, 0, 0, 0, 0, 1, 0]), //Sunset - ColorFilter.matrix([ - 1.5, - 0.2, - 0, - 0, - 0, - 0.1, - 0.9, - 0.1, - 0, - 0, - -0.1, - -0.2, - 1.3, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.5, 0.2, 0, 0, 0, 0.1, 0.9, 0.1, 0, 0, -0.1, -0.2, 1.3, 0, 0, 0, 0, 0, 1, 0]), //Noir - ColorFilter.matrix([ - 1.3, - -0.3, - 0.1, - 0, - 0, - -0.1, - 1.2, - -0.1, - 0, - 0, - 0.1, - -0.2, - 1.3, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.3, -0.3, 0.1, 0, 0, -0.1, 1.2, -0.1, 0, 0, 0.1, -0.2, 1.3, 0, 0, 0, 0, 0, 1, 0]), //Dreamy - ColorFilter.matrix([ - 1.1, - 0.1, - 0.1, - 0, - 0, - 0.1, - 1.1, - 0.1, - 0, - 0, - 0.1, - 0.1, - 1.1, - 0, - 15, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.1, 0.1, 0.1, 0, 0, 0.1, 1.1, 0.1, 0, 0, 0.1, 0.1, 1.1, 0, 15, 0, 0, 0, 1, 0]), //Sepia - ColorFilter.matrix([ - 0.393, - 0.769, - 0.189, - 0, - 0, - 0.349, - 0.686, - 0.168, - 0, - 0, - 0.272, - 0.534, - 0.131, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.393, 0.769, 0.189, 0, 0, 0.349, 0.686, 0.168, 0, 0, 0.272, 0.534, 0.131, 0, 0, 0, 0, 0, 1, 0]), //Radium ColorFilter.matrix([ 1.438, @@ -554,212 +92,23 @@ const List filters = [ 0, ]), //Purple Haze - ColorFilter.matrix([ - 1.3, - 0, - 1.2, - 0, - 0, - 0, - 1.1, - 0, - 0, - 0, - 0.2, - 0, - 1.3, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.3, 0, 1.2, 0, 0, 0, 1.1, 0, 0, 0, 0.2, 0, 1.3, 0, 0, 0, 0, 0, 1, 0]), //Lemonade - ColorFilter.matrix([ - 1.2, - 0.1, - 0, - 0, - 0, - 0, - 1.1, - 0.2, - 0, - 0, - 0.1, - 0, - 0.7, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.2, 0.1, 0, 0, 0, 0, 1.1, 0.2, 0, 0, 0.1, 0, 0.7, 0, 0, 0, 0, 0, 1, 0]), //Caramel - ColorFilter.matrix([ - 1.6, - 0.2, - 0, - 0, - 0, - 0.1, - 1.3, - 0.1, - 0, - 0, - 0, - 0.1, - 0.9, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.6, 0.2, 0, 0, 0, 0.1, 1.3, 0.1, 0, 0, 0, 0.1, 0.9, 0, 0, 0, 0, 0, 1, 0]), //Peachy - ColorFilter.matrix([ - 1.3, - 0.5, - 0, - 0, - 0, - 0.2, - 1.1, - 0.3, - 0, - 0, - 0.1, - 0.1, - 1.2, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.3, 0.5, 0, 0, 0, 0.2, 1.1, 0.3, 0, 0, 0.1, 0.1, 1.2, 0, 0, 0, 0, 0, 1, 0]), //Neon - ColorFilter.matrix([ - 1, - 0, - 1, - 0, - 0, - 0, - 2, - 0, - 0, - 0, - 0, - 0, - 3, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 1, 0]), //Cold Morning - ColorFilter.matrix([ - 0.9, - 0.1, - 0.2, - 0, - 0, - 0, - 1, - 0.1, - 0, - 0, - 0.1, - 0, - 1.2, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.9, 0.1, 0.2, 0, 0, 0, 1, 0.1, 0, 0, 0.1, 0, 1.2, 0, 0, 0, 0, 0, 1, 0]), //Lush - ColorFilter.matrix([ - 0.9, - 0.2, - 0, - 0, - 0, - 0, - 1.2, - 0, - 0, - 0, - 0, - 0, - 1.1, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.9, 0.2, 0, 0, 0, 0, 1.2, 0, 0, 0, 0, 0, 1.1, 0, 0, 0, 0, 0, 1, 0]), //Urban Neon - ColorFilter.matrix([ - 1.1, - 0, - 0.3, - 0, - 0, - 0, - 0.9, - 0.3, - 0, - 0, - 0.3, - 0.1, - 1.2, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.1, 0, 0.3, 0, 0, 0, 0.9, 0.3, 0, 0, 0.3, 0.1, 1.2, 0, 0, 0, 0, 0, 1, 0]), //Monochrome - ColorFilter.matrix([ - 0.6, - 0.2, - 0.2, - 0, - 0, - 0.2, - 0.6, - 0.2, - 0, - 0, - 0.2, - 0.2, - 0.7, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.6, 0.2, 0.2, 0, 0, 0.2, 0.6, 0.2, 0, 0, 0.2, 0.2, 0.7, 0, 0, 0, 0, 0, 1, 0]), ]; const List filterNames = [ diff --git a/mobile/lib/constants/locales.dart b/mobile/lib/constants/locales.dart index 601a83b563..f3c24384b0 100644 --- a/mobile/lib/constants/locales.dart +++ b/mobile/lib/constants/locales.dart @@ -51,7 +51,4 @@ const Map locales = { const String translationsPath = 'assets/i18n'; -const List localesNotSupportedByOverpass = [ - Locale('el', 'GR'), - Locale('sr', 'Cyrl'), -]; +const List localesNotSupportedByOverpass = [Locale('el', 'GR'), Locale('sr', 'Cyrl')]; diff --git a/mobile/lib/domain/models/asset/base_asset.model.dart b/mobile/lib/domain/models/asset/base_asset.model.dart index 7cd4caab6a..4d40be2d32 100644 --- a/mobile/lib/domain/models/asset/base_asset.model.dart +++ b/mobile/lib/domain/models/asset/base_asset.model.dart @@ -9,11 +9,7 @@ enum AssetType { audio, } -enum AssetState { - local, - remote, - merged, -} +enum AssetState { local, remote, merged } sealed class BaseAsset { final String name; diff --git a/mobile/lib/domain/models/asset/remote_asset.model.dart b/mobile/lib/domain/models/asset/remote_asset.model.dart index db3e53cd2e..8648255167 100644 --- a/mobile/lib/domain/models/asset/remote_asset.model.dart +++ b/mobile/lib/domain/models/asset/remote_asset.model.dart @@ -1,11 +1,6 @@ part of 'base_asset.model.dart'; -enum AssetVisibility { - timeline, - hidden, - archive, - locked, -} +enum AssetVisibility { timeline, hidden, archive, locked } // Model for an asset stored in the server class RemoteAsset extends BaseAsset { diff --git a/mobile/lib/domain/models/device_asset.model.dart b/mobile/lib/domain/models/device_asset.model.dart index b0949ccc96..a404f5a9e2 100644 --- a/mobile/lib/domain/models/device_asset.model.dart +++ b/mobile/lib/domain/models/device_asset.model.dart @@ -5,11 +5,7 @@ class DeviceAsset { final Uint8List hash; final DateTime modifiedTime; - const DeviceAsset({ - required this.assetId, - required this.hash, - required this.modifiedTime, - }); + const DeviceAsset({required this.assetId, required this.hash, required this.modifiedTime}); @override bool operator ==(covariant DeviceAsset other) { @@ -28,11 +24,7 @@ class DeviceAsset { return 'DeviceAsset(assetId: $assetId, hash: $hash, modifiedTime: $modifiedTime)'; } - DeviceAsset copyWith({ - String? assetId, - Uint8List? hash, - DateTime? modifiedTime, - }) { + DeviceAsset copyWith({String? assetId, Uint8List? hash, DateTime? modifiedTime}) { return DeviceAsset( assetId: assetId ?? this.assetId, hash: hash ?? this.hash, diff --git a/mobile/lib/domain/models/log.model.dart b/mobile/lib/domain/models/log.model.dart index f58cae8063..9902ca04ca 100644 --- a/mobile/lib/domain/models/log.model.dart +++ b/mobile/lib/domain/models/log.model.dart @@ -1,16 +1,5 @@ /// Log levels according to dart logging [Level] -enum LogLevel { - all, - finest, - finer, - fine, - config, - info, - warning, - severe, - shout, - off, -} +enum LogLevel { all, finest, finer, fine, config, info, warning, severe, shout, off } class LogMessage { final String message; diff --git a/mobile/lib/domain/models/memory.model.dart b/mobile/lib/domain/models/memory.model.dart index 39a6d4518b..40117c5ac6 100644 --- a/mobile/lib/domain/models/memory.model.dart +++ b/mobile/lib/domain/models/memory.model.dart @@ -13,28 +13,18 @@ enum MemoryTypeEnum { class MemoryData { final int year; - const MemoryData({ - required this.year, - }); + const MemoryData({required this.year}); - MemoryData copyWith({ - int? year, - }) { - return MemoryData( - year: year ?? this.year, - ); + MemoryData copyWith({int? year}) { + return MemoryData(year: year ?? this.year); } Map toMap() { - return { - 'year': year, - }; + return {'year': year}; } factory MemoryData.fromMap(Map map) { - return MemoryData( - year: map['year'] as int, - ); + return MemoryData(year: map['year'] as int); } String toJson() => json.encode(toMap()); diff --git a/mobile/lib/domain/models/search_result.model.dart b/mobile/lib/domain/models/search_result.model.dart index e8c9429432..bae8b8e821 100644 --- a/mobile/lib/domain/models/search_result.model.dart +++ b/mobile/lib/domain/models/search_result.model.dart @@ -5,21 +5,12 @@ class SearchResult { final List assets; final int? nextPage; - const SearchResult({ - required this.assets, - this.nextPage, - }); + const SearchResult({required this.assets, this.nextPage}); int get totalAssets => assets.length; - SearchResult copyWith({ - List? assets, - int? nextPage, - }) { - return SearchResult( - assets: assets ?? this.assets, - nextPage: nextPage ?? this.nextPage, - ); + SearchResult copyWith({List? assets, int? nextPage}) { + return SearchResult(assets: assets ?? this.assets, nextPage: nextPage ?? this.nextPage); } @override diff --git a/mobile/lib/domain/models/setting.model.dart b/mobile/lib/domain/models/setting.model.dart index 150545eba9..f427d93285 100644 --- a/mobile/lib/domain/models/setting.model.dart +++ b/mobile/lib/domain/models/setting.model.dart @@ -8,8 +8,7 @@ enum Setting { loadOriginalVideo(StoreKey.loadOriginalVideo, false), preferRemoteImage(StoreKey.preferRemoteImage, false), advancedTroubleshooting(StoreKey.advancedTroubleshooting, false), - enableBackup(StoreKey.enableBackup, false), - ; + enableBackup(StoreKey.enableBackup, false); const Setting(this.storeKey, this.defaultValue); diff --git a/mobile/lib/domain/models/stack.model.dart b/mobile/lib/domain/models/stack.model.dart index 0db65d105b..d5ccf5558d 100644 --- a/mobile/lib/domain/models/stack.model.dart +++ b/mobile/lib/domain/models/stack.model.dart @@ -14,13 +14,7 @@ class Stack { required this.primaryAssetId, }); - Stack copyWith({ - String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? primaryAssetId, - }) { + Stack copyWith({String? id, DateTime? createdAt, DateTime? updatedAt, String? ownerId, String? primaryAssetId}) { return Stack( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -63,11 +57,7 @@ class StackResponse { final String primaryAssetId; final List assetIds; - const StackResponse({ - required this.id, - required this.primaryAssetId, - required this.assetIds, - }); + const StackResponse({required this.id, required this.primaryAssetId, required this.assetIds}); @override bool operator ==(covariant StackResponse other) { diff --git a/mobile/lib/domain/models/timeline.model.dart b/mobile/lib/domain/models/timeline.model.dart index 3751500f0f..d4cc5ab5c6 100644 --- a/mobile/lib/domain/models/timeline.model.dart +++ b/mobile/lib/domain/models/timeline.model.dart @@ -1,18 +1,8 @@ import 'package:immich_mobile/domain/utils/event_stream.dart'; -enum GroupAssetsBy { - day, - month, - auto, - none; -} +enum GroupAssetsBy { day, month, auto, none } -enum HeaderType { - none, - month, - day, - monthAndDay; -} +enum HeaderType { none, month, day, monthAndDay } class Bucket { final int assetCount; diff --git a/mobile/lib/domain/models/user.model.dart b/mobile/lib/domain/models/user.model.dart index 9af8abadc2..1e83fa498d 100644 --- a/mobile/lib/domain/models/user.model.dart +++ b/mobile/lib/domain/models/user.model.dart @@ -74,22 +74,21 @@ quotaSizeInBytes: $quotaSizeInBytes, bool? isPartnerSharedWith, int? quotaUsageInBytes, int? quotaSizeInBytes, - }) => - UserDto( - id: id ?? this.id, - email: email ?? this.email, - name: name ?? this.name, - isAdmin: isAdmin ?? this.isAdmin, - updatedAt: updatedAt ?? this.updatedAt, - profileImagePath: profileImagePath ?? this.profileImagePath, - avatarColor: avatarColor ?? this.avatarColor, - memoryEnabled: memoryEnabled ?? this.memoryEnabled, - inTimeline: inTimeline ?? this.inTimeline, - isPartnerSharedBy: isPartnerSharedBy ?? this.isPartnerSharedBy, - isPartnerSharedWith: isPartnerSharedWith ?? this.isPartnerSharedWith, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, - quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, - ); + }) => UserDto( + id: id ?? this.id, + email: email ?? this.email, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + updatedAt: updatedAt ?? this.updatedAt, + profileImagePath: profileImagePath ?? this.profileImagePath, + avatarColor: avatarColor ?? this.avatarColor, + memoryEnabled: memoryEnabled ?? this.memoryEnabled, + inTimeline: inTimeline ?? this.inTimeline, + isPartnerSharedBy: isPartnerSharedBy ?? this.isPartnerSharedBy, + isPartnerSharedWith: isPartnerSharedWith ?? this.isPartnerSharedWith, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, + ); @override bool operator ==(covariant UserDto other) { @@ -143,13 +142,7 @@ class PartnerUserDto { this.profileImagePath, }); - PartnerUserDto copyWith({ - String? id, - String? email, - String? name, - bool? inTimeline, - String? profileImagePath, - }) { + PartnerUserDto copyWith({String? id, String? email, String? name, bool? inTimeline, String? profileImagePath}) { return PartnerUserDto( id: id ?? this.id, email: email ?? this.email, diff --git a/mobile/lib/domain/models/user_metadata.model.dart b/mobile/lib/domain/models/user_metadata.model.dart index 8b7ca1ffa9..1c371a9d3e 100644 --- a/mobile/lib/domain/models/user_metadata.model.dart +++ b/mobile/lib/domain/models/user_metadata.model.dart @@ -24,17 +24,17 @@ enum AvatarColor { const AvatarColor(this.value); Color toColor({bool isDarkTheme = false}) => switch (this) { - AvatarColor.primary => isDarkTheme ? const Color(0xFFABCBFA) : const Color(0xFF4250AF), - AvatarColor.pink => const Color.fromARGB(255, 244, 114, 182), - AvatarColor.red => const Color.fromARGB(255, 239, 68, 68), - AvatarColor.yellow => const Color.fromARGB(255, 234, 179, 8), - AvatarColor.blue => const Color.fromARGB(255, 59, 130, 246), - AvatarColor.green => const Color.fromARGB(255, 22, 163, 74), - AvatarColor.purple => const Color.fromARGB(255, 147, 51, 234), - AvatarColor.orange => const Color.fromARGB(255, 234, 88, 12), - AvatarColor.gray => const Color.fromARGB(255, 75, 85, 99), - AvatarColor.amber => const Color.fromARGB(255, 217, 119, 6), - }; + AvatarColor.primary => isDarkTheme ? const Color(0xFFABCBFA) : const Color(0xFF4250AF), + AvatarColor.pink => const Color.fromARGB(255, 244, 114, 182), + AvatarColor.red => const Color.fromARGB(255, 239, 68, 68), + AvatarColor.yellow => const Color.fromARGB(255, 234, 179, 8), + AvatarColor.blue => const Color.fromARGB(255, 59, 130, 246), + AvatarColor.green => const Color.fromARGB(255, 22, 163, 74), + AvatarColor.purple => const Color.fromARGB(255, 147, 51, 234), + AvatarColor.orange => const Color.fromARGB(255, 234, 88, 12), + AvatarColor.gray => const Color.fromARGB(255, 75, 85, 99), + AvatarColor.amber => const Color.fromARGB(255, 217, 119, 6), + }; } class Onboarding { @@ -193,17 +193,9 @@ class License { final String activationKey; final String licenseKey; - const License({ - required this.activatedAt, - required this.activationKey, - required this.licenseKey, - }); + const License({required this.activatedAt, required this.activationKey, required this.licenseKey}); - License copyWith({ - DateTime? activatedAt, - String? activationKey, - String? licenseKey, - }) { + License copyWith({DateTime? activatedAt, String? activationKey, String? licenseKey}) { return License( activatedAt: activatedAt ?? this.activatedAt, activationKey: activationKey ?? this.activationKey, @@ -255,16 +247,11 @@ class UserMetadata { final Preferences? preferences; final License? license; - const UserMetadata({ - required this.userId, - required this.key, - this.onboarding, - this.preferences, - this.license, - }) : assert( - onboarding != null || preferences != null || license != null, - 'One of onboarding, preferences and license must be provided', - ); + const UserMetadata({required this.userId, required this.key, this.onboarding, this.preferences, this.license}) + : assert( + onboarding != null || preferences != null || license != null, + 'One of onboarding, preferences and license must be provided', + ); UserMetadata copyWith({ String? userId, diff --git a/mobile/lib/domain/services/asset.service.dart b/mobile/lib/domain/services/asset.service.dart index 5006e2d45c..c8cc61314e 100644 --- a/mobile/lib/domain/services/asset.service.dart +++ b/mobile/lib/domain/services/asset.service.dart @@ -13,9 +13,9 @@ class AssetService { const AssetService({ required RemoteAssetRepository remoteAssetRepository, required DriftLocalAssetRepository localAssetRepository, - }) : _remoteAssetRepository = remoteAssetRepository, - _localAssetRepository = localAssetRepository, - _platform = const LocalPlatform(); + }) : _remoteAssetRepository = remoteAssetRepository, + _localAssetRepository = localAssetRepository, + _platform = const LocalPlatform(); Stream watchAsset(BaseAsset asset) { final id = asset is LocalAsset ? asset.id : (asset as RemoteAsset).id; diff --git a/mobile/lib/domain/services/hash.service.dart b/mobile/lib/domain/services/hash.service.dart index 3bbb75b003..2eb9aec4db 100644 --- a/mobile/lib/domain/services/hash.service.dart +++ b/mobile/lib/domain/services/hash.service.dart @@ -25,19 +25,16 @@ class HashService { required NativeSyncApi nativeSyncApi, this.batchSizeLimit = kBatchHashSizeLimit, this.batchFileLimit = kBatchHashFileLimit, - }) : _localAlbumRepository = localAlbumRepository, - _localAssetRepository = localAssetRepository, - _storageRepository = storageRepository, - _nativeSyncApi = nativeSyncApi; + }) : _localAlbumRepository = localAlbumRepository, + _localAssetRepository = localAssetRepository, + _storageRepository = storageRepository, + _nativeSyncApi = nativeSyncApi; Future hashAssets() async { final Stopwatch stopwatch = Stopwatch()..start(); // Sorted by backupSelection followed by isCloud final localAlbums = await _localAlbumRepository.getAll( - sortBy: { - SortLocalAlbumsBy.backupSelection, - SortLocalAlbumsBy.isIosSharedAlbum, - }, + sortBy: {SortLocalAlbumsBy.backupSelection, SortLocalAlbumsBy.isIosSharedAlbum}, ); for (final album in localAlbums) { diff --git a/mobile/lib/domain/services/local_sync.service.dart b/mobile/lib/domain/services/local_sync.service.dart index 4204761054..cb1bb40619 100644 --- a/mobile/lib/domain/services/local_sync.service.dart +++ b/mobile/lib/domain/services/local_sync.service.dart @@ -21,9 +21,9 @@ class LocalSyncService { required DriftLocalAlbumRepository localAlbumRepository, required NativeSyncApi nativeSyncApi, Platform? platform, - }) : _localAlbumRepository = localAlbumRepository, - _nativeSyncApi = nativeSyncApi, - _platform = platform ?? const LocalPlatform(); + }) : _localAlbumRepository = localAlbumRepository, + _nativeSyncApi = nativeSyncApi, + _platform = platform ?? const LocalPlatform(); Future sync({bool full = false}) async { final Stopwatch stopwatch = Stopwatch()..start(); @@ -70,9 +70,7 @@ class LocalSyncService { for (final album in cloudAlbums) { final dbAlbum = dbAlbums.firstWhereOrNull((a) => a.id == album.id); if (dbAlbum == null) { - _log.warning( - "Cloud album ${album.name} not found in local database. Skipping sync.", - ); + _log.warning("Cloud album ${album.name} not found in local database. Skipping sync."); continue; } await updateAlbum(dbAlbum, album); @@ -120,10 +118,7 @@ class LocalSyncService { final assets = album.assetCount > 0 ? await _nativeSyncApi.getAssetsForAlbum(album.id) : []; - await _localAlbumRepository.upsert( - album, - toUpsert: assets.toLocalAssets(), - ); + await _localAlbumRepository.upsert(album, toUpsert: assets.toLocalAssets()); _log.fine("Successfully added device album ${album.name}"); } catch (e, s) { _log.warning("Error while adding device album", e, s); @@ -146,9 +141,7 @@ class LocalSyncService { _log.fine("Syncing device album ${dbAlbum.name}"); if (_albumsEqual(deviceAlbum, dbAlbum)) { - _log.fine( - "Device album ${dbAlbum.name} has not changed. Skipping sync.", - ); + _log.fine("Device album ${dbAlbum.name} has not changed. Skipping sync."); return false; } @@ -172,10 +165,7 @@ class LocalSyncService { @visibleForTesting // The [deviceAlbum] is expected to be refreshed before calling this method // with modified time and asset count - Future checkAddition( - LocalAlbum dbAlbum, - LocalAlbum deviceAlbum, - ) async { + Future checkAddition(LocalAlbum dbAlbum, LocalAlbum deviceAlbum) async { try { _log.fine("Fast syncing device album ${dbAlbum.name}"); // Assets has been modified @@ -189,9 +179,7 @@ class LocalSyncService { // Early return if no new assets were found if (newAssetsCount == 0) { - _log.fine( - "No new assets found despite album having changes. Proceeding to full sync for ${dbAlbum.name}", - ); + _log.fine("No new assets found despite album having changes. Proceeding to full sync for ${dbAlbum.name}"); return false; } @@ -201,10 +189,7 @@ class LocalSyncService { return false; } - final newAssets = await _nativeSyncApi.getAssetsForAlbum( - deviceAlbum.id, - updatedTimeCond: updatedTime, - ); + final newAssets = await _nativeSyncApi.getAssetsForAlbum(deviceAlbum.id, updatedTimeCond: updatedTime); await _localAlbumRepository.upsert( deviceAlbum.copyWith(backupSelection: dbAlbum.backupSelection), @@ -229,9 +214,7 @@ class LocalSyncService { final assetsInDb = dbAlbum.assetCount > 0 ? await _localAlbumRepository.getAssets(dbAlbum.id) : []; if (deviceAlbum.assetCount == 0) { - _log.fine( - "Device album ${deviceAlbum.name} is empty. Removing assets from DB.", - ); + _log.fine("Device album ${deviceAlbum.name} is empty. Removing assets from DB."); await _localAlbumRepository.upsert( deviceAlbum.copyWith(backupSelection: dbAlbum.backupSelection), toDelete: assetsInDb.map((a) => a.id), @@ -239,18 +222,11 @@ class LocalSyncService { return true; } - final updatedDeviceAlbum = deviceAlbum.copyWith( - backupSelection: dbAlbum.backupSelection, - ); + final updatedDeviceAlbum = deviceAlbum.copyWith(backupSelection: dbAlbum.backupSelection); if (dbAlbum.assetCount == 0) { - _log.fine( - "Device album ${deviceAlbum.name} is empty. Adding assets to DB.", - ); - await _localAlbumRepository.upsert( - updatedDeviceAlbum, - toUpsert: assetsInDevice, - ); + _log.fine("Device album ${deviceAlbum.name} is empty. Adding assets to DB."); + await _localAlbumRepository.upsert(updatedDeviceAlbum, toUpsert: assetsInDevice); return true; } @@ -282,18 +258,12 @@ class LocalSyncService { ); if (assetsToUpsert.isEmpty && assetsToDelete.isEmpty) { - _log.fine( - "No asset changes detected in album ${deviceAlbum.name}. Updating metadata.", - ); + _log.fine("No asset changes detected in album ${deviceAlbum.name}. Updating metadata."); _localAlbumRepository.upsert(updatedDeviceAlbum); return true; } - await _localAlbumRepository.upsert( - updatedDeviceAlbum, - toUpsert: assetsToUpsert, - toDelete: assetsToDelete, - ); + await _localAlbumRepository.upsert(updatedDeviceAlbum, toUpsert: assetsToUpsert, toDelete: assetsToDelete); return true; } catch (e, s) { diff --git a/mobile/lib/domain/services/log.service.dart b/mobile/lib/domain/services/log.service.dart index 72fb4d9bf7..ff72ec5502 100644 --- a/mobile/lib/domain/services/log.service.dart +++ b/mobile/lib/domain/services/log.service.dart @@ -61,11 +61,7 @@ class LogService { return instance; } - LogService._( - this._logRepository, - this._storeRepository, - this._shouldBuffer, - ) { + LogService._(this._logRepository, this._storeRepository, this._shouldBuffer) { _logSubscription = Logger.root.onRecord.listen(_handleLogRecord); } @@ -89,10 +85,7 @@ class LogService { if (_shouldBuffer) { _msgBuffer.add(record); - _flushTimer ??= Timer( - const Duration(seconds: 5), - () => unawaited(flushBuffer()), - ); + _flushTimer ??= Timer(const Duration(seconds: 5), () => unawaited(flushBuffer())); } else { unawaited(_logRepository.insert(record)); } diff --git a/mobile/lib/domain/services/partner.service.dart b/mobile/lib/domain/services/partner.service.dart index 11299b9d6d..7733b5be6b 100644 --- a/mobile/lib/domain/services/partner.service.dart +++ b/mobile/lib/domain/services/partner.service.dart @@ -7,10 +7,7 @@ class DriftPartnerService { final DriftPartnerRepository _driftPartnerRepository; final PartnerApiRepository _partnerApiRepository; - const DriftPartnerService( - this._driftPartnerRepository, - this._partnerApiRepository, - ); + const DriftPartnerService(this._driftPartnerRepository, this._partnerApiRepository); Future> getSharedWith(String userId) { return _driftPartnerRepository.getSharedWith(userId); @@ -20,9 +17,7 @@ class DriftPartnerService { return _driftPartnerRepository.getSharedBy(userId); } - Future> getAvailablePartners( - String currentUserId, - ) async { + Future> getAvailablePartners(String currentUserId) async { final otherUsers = await _driftPartnerRepository.getAvailablePartners(currentUserId); final currentPartners = await _driftPartnerRepository.getSharedBy(currentUserId); final available = otherUsers.where((user) { @@ -39,10 +34,7 @@ class DriftPartnerService { return; } - await _partnerApiRepository.update( - partnerId, - inTimeline: !partner.inTimeline, - ); + await _partnerApiRepository.update(partnerId, inTimeline: !partner.inTimeline); await _driftPartnerRepository.toggleShowInTimeline(partner, userId); } diff --git a/mobile/lib/domain/services/remote_album.service.dart b/mobile/lib/domain/services/remote_album.service.dart index 6c3b2e32af..f6c596f24a 100644 --- a/mobile/lib/domain/services/remote_album.service.dart +++ b/mobile/lib/domain/services/remote_album.service.dart @@ -26,11 +26,7 @@ class RemoteAlbumService { return _repository.get(albumId); } - List sortAlbums( - List albums, - RemoteAlbumSortMode sortMode, { - bool isReverse = false, - }) { + List sortAlbums(List albums, RemoteAlbumSortMode sortMode, {bool isReverse = false}) { return sortMode.sortFn(albums, isReverse); } @@ -69,16 +65,8 @@ class RemoteAlbumService { return filtered; } - Future createAlbum({ - required String title, - required List assetIds, - String? description, - }) async { - final album = await _albumApiRepository.createDriftAlbum( - title, - description: description, - assetIds: assetIds, - ); + Future createAlbum({required String title, required List assetIds, String? description}) async { + final album = await _albumApiRepository.createDriftAlbum(title, description: description, assetIds: assetIds); await _repository.create(album, assetIds); @@ -120,14 +108,8 @@ class RemoteAlbumService { return _repository.getAssets(albumId); } - Future addAssets({ - required String albumId, - required List assetIds, - }) async { - final album = await _albumApiRepository.addAssets( - albumId, - assetIds, - ); + Future addAssets({required String albumId, required List assetIds}) async { + final album = await _albumApiRepository.addAssets(albumId, assetIds); await _repository.addAssets(albumId, album.added); @@ -140,10 +122,7 @@ class RemoteAlbumService { await _repository.deleteAlbum(albumId); } - Future addUsers({ - required String albumId, - required List userIds, - }) async { + Future addUsers({required String albumId, required List userIds}) async { await _albumApiRepository.addUsers(albumId, userIds); return _repository.addUsers(albumId, userIds); diff --git a/mobile/lib/domain/services/search.service.dart b/mobile/lib/domain/services/search.service.dart index 052a2ca9da..6ccc5a97bf 100644 --- a/mobile/lib/domain/services/search.service.dart +++ b/mobile/lib/domain/services/search.service.dart @@ -83,10 +83,10 @@ extension on AssetResponseDto { extension on AssetTypeEnum { AssetType toAssetType() => switch (this) { - AssetTypeEnum.IMAGE => AssetType.image, - AssetTypeEnum.VIDEO => AssetType.video, - AssetTypeEnum.AUDIO => AssetType.audio, - AssetTypeEnum.OTHER => AssetType.other, - _ => throw Exception('Unknown AssetType value: $this'), - }; + AssetTypeEnum.IMAGE => AssetType.image, + AssetTypeEnum.VIDEO => AssetType.video, + AssetTypeEnum.AUDIO => AssetType.audio, + AssetTypeEnum.OTHER => AssetType.other, + _ => throw Exception('Unknown AssetType value: $this'), + }; } diff --git a/mobile/lib/domain/services/store.service.dart b/mobile/lib/domain/services/store.service.dart index bd839a18ec..dc845b70f1 100644 --- a/mobile/lib/domain/services/store.service.dart +++ b/mobile/lib/domain/services/store.service.dart @@ -24,16 +24,12 @@ class StoreService { } // TODO: Replace the implementation with the one from create after removing the typedef - static Future init({ - required IsarStoreRepository storeRepository, - }) async { + static Future init({required IsarStoreRepository storeRepository}) async { _instance ??= await create(storeRepository: storeRepository); return _instance!; } - static Future create({ - required IsarStoreRepository storeRepository, - }) async { + static Future create({required IsarStoreRepository storeRepository}) async { final instance = StoreService._(storeRepository: storeRepository); await instance._populateCache(); instance._storeUpdateSubscription = instance._listenForChange(); @@ -48,8 +44,8 @@ class StoreService { } StreamSubscription _listenForChange() => _storeRepository.watchAll().listen((event) { - _cache[event.key.id] = event.value; - }); + _cache[event.key.id] = event.value; + }); /// Disposes the store and cancels the subscription. To reuse the store call init() again void dispose() async { diff --git a/mobile/lib/domain/services/sync_stream.service.dart b/mobile/lib/domain/services/sync_stream.service.dart index a985008251..c21a9cade5 100644 --- a/mobile/lib/domain/services/sync_stream.service.dart +++ b/mobile/lib/domain/services/sync_stream.service.dart @@ -18,9 +18,9 @@ class SyncStreamService { required SyncApiRepository syncApiRepository, required SyncStreamRepository syncStreamRepository, bool Function()? cancelChecker, - }) : _syncApiRepository = syncApiRepository, - _syncStreamRepository = syncStreamRepository, - _cancelChecker = cancelChecker; + }) : _syncApiRepository = syncApiRepository, + _syncStreamRepository = syncStreamRepository, + _cancelChecker = cancelChecker; bool get isCancelled => _cancelChecker?.call() ?? false; @@ -34,9 +34,7 @@ class SyncStreamService { Future handleWsAssetUploadReadyV1Batch(List batchData) async { if (batchData.isEmpty) return; - _logger.info( - 'Processing batch of ${batchData.length} AssetUploadReadyV1 events', - ); + _logger.info('Processing batch of ${batchData.length} AssetUploadReadyV1 events'); final List assets = []; final List exifs = []; @@ -65,22 +63,12 @@ class SyncStreamService { } if (assets.isNotEmpty && exifs.isNotEmpty) { - await _syncStreamRepository.updateAssetsV1( - assets, - debugLabel: 'websocket-batch', - ); - await _syncStreamRepository.updateAssetsExifV1( - exifs, - debugLabel: 'websocket-batch', - ); + await _syncStreamRepository.updateAssetsV1(assets, debugLabel: 'websocket-batch'); + await _syncStreamRepository.updateAssetsExifV1(exifs, debugLabel: 'websocket-batch'); _logger.info('Successfully processed ${assets.length} assets in batch'); } } catch (error, stackTrace) { - _logger.severe( - "Error processing AssetUploadReadyV1 websocket batch events", - error, - stackTrace, - ); + _logger.severe("Error processing AssetUploadReadyV1 websocket batch events", error, stackTrace); } } @@ -114,10 +102,7 @@ class SyncStreamService { batch.clear(); } - Future _handleSyncData( - SyncEntityType type, - Iterable data, - ) async { + Future _handleSyncData(SyncEntityType type, Iterable data) async { _logger.fine("Processing sync data for $type of length ${data.length}"); switch (type) { case SyncEntityType.userV1: @@ -135,30 +120,15 @@ class SyncStreamService { case SyncEntityType.assetExifV1: return _syncStreamRepository.updateAssetsExifV1(data.cast()); case SyncEntityType.partnerAssetV1: - return _syncStreamRepository.updateAssetsV1( - data.cast(), - debugLabel: 'partner', - ); + return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'partner'); case SyncEntityType.partnerAssetBackfillV1: - return _syncStreamRepository.updateAssetsV1( - data.cast(), - debugLabel: 'partner backfill', - ); + return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'partner backfill'); case SyncEntityType.partnerAssetDeleteV1: - return _syncStreamRepository.deleteAssetsV1( - data.cast(), - debugLabel: "partner", - ); + return _syncStreamRepository.deleteAssetsV1(data.cast(), debugLabel: "partner"); case SyncEntityType.partnerAssetExifV1: - return _syncStreamRepository.updateAssetsExifV1( - data.cast(), - debugLabel: 'partner', - ); + return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'partner'); case SyncEntityType.partnerAssetExifBackfillV1: - return _syncStreamRepository.updateAssetsExifV1( - data.cast(), - debugLabel: 'partner backfill', - ); + return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'partner backfill'); case SyncEntityType.albumV1: return _syncStreamRepository.updateAlbumsV1(data.cast()); case SyncEntityType.albumDeleteV1: @@ -166,39 +136,21 @@ class SyncStreamService { case SyncEntityType.albumUserV1: return _syncStreamRepository.updateAlbumUsersV1(data.cast()); case SyncEntityType.albumUserBackfillV1: - return _syncStreamRepository.updateAlbumUsersV1( - data.cast(), - debugLabel: 'backfill', - ); + return _syncStreamRepository.updateAlbumUsersV1(data.cast(), debugLabel: 'backfill'); case SyncEntityType.albumUserDeleteV1: return _syncStreamRepository.deleteAlbumUsersV1(data.cast()); case SyncEntityType.albumAssetV1: - return _syncStreamRepository.updateAssetsV1( - data.cast(), - debugLabel: 'album', - ); + return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'album'); case SyncEntityType.albumAssetBackfillV1: - return _syncStreamRepository.updateAssetsV1( - data.cast(), - debugLabel: 'album backfill', - ); + return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'album backfill'); case SyncEntityType.albumAssetExifV1: - return _syncStreamRepository.updateAssetsExifV1( - data.cast(), - debugLabel: 'album', - ); + return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'album'); case SyncEntityType.albumAssetExifBackfillV1: - return _syncStreamRepository.updateAssetsExifV1( - data.cast(), - debugLabel: 'album backfill', - ); + return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'album backfill'); case SyncEntityType.albumToAssetV1: return _syncStreamRepository.updateAlbumToAssetsV1(data.cast()); case SyncEntityType.albumToAssetBackfillV1: - return _syncStreamRepository.updateAlbumToAssetsV1( - data.cast(), - debugLabel: 'backfill', - ); + return _syncStreamRepository.updateAlbumToAssetsV1(data.cast(), debugLabel: 'backfill'); case SyncEntityType.albumToAssetDeleteV1: return _syncStreamRepository.deleteAlbumToAssetsV1(data.cast()); // No-op. SyncAckV1 entities are checkpoints in the sync stream @@ -218,28 +170,15 @@ class SyncStreamService { case SyncEntityType.stackDeleteV1: return _syncStreamRepository.deleteStacksV1(data.cast()); case SyncEntityType.partnerStackV1: - return _syncStreamRepository.updateStacksV1( - data.cast(), - debugLabel: 'partner', - ); + return _syncStreamRepository.updateStacksV1(data.cast(), debugLabel: 'partner'); case SyncEntityType.partnerStackBackfillV1: - return _syncStreamRepository.updateStacksV1( - data.cast(), - debugLabel: 'partner backfill', - ); + return _syncStreamRepository.updateStacksV1(data.cast(), debugLabel: 'partner backfill'); case SyncEntityType.partnerStackDeleteV1: - return _syncStreamRepository.deleteStacksV1( - data.cast(), - debugLabel: 'partner', - ); + return _syncStreamRepository.deleteStacksV1(data.cast(), debugLabel: 'partner'); case SyncEntityType.userMetadataV1: - return _syncStreamRepository.updateUserMetadatasV1( - data.cast(), - ); + return _syncStreamRepository.updateUserMetadatasV1(data.cast()); case SyncEntityType.userMetadataDeleteV1: - return _syncStreamRepository.deleteUserMetadatasV1( - data.cast(), - ); + return _syncStreamRepository.deleteUserMetadatasV1(data.cast()); case SyncEntityType.personV1: return _syncStreamRepository.updatePeopleV1(data.cast()); case SyncEntityType.personDeleteV1: diff --git a/mobile/lib/domain/services/timeline.service.dart b/mobile/lib/domain/services/timeline.service.dart index 7e982558b7..7c22fb786d 100644 --- a/mobile/lib/domain/services/timeline.service.dart +++ b/mobile/lib/domain/services/timeline.service.dart @@ -11,27 +11,19 @@ import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/infrastructure/repositories/timeline.repository.dart'; import 'package:immich_mobile/utils/async_mutex.dart'; -typedef TimelineAssetSource = Future> Function( - int index, - int count, -); +typedef TimelineAssetSource = Future> Function(int index, int count); typedef TimelineBucketSource = Stream> Function(); -typedef TimelineQuery = ({ - TimelineAssetSource assetSource, - TimelineBucketSource bucketSource, -}); +typedef TimelineQuery = ({TimelineAssetSource assetSource, TimelineBucketSource bucketSource}); class TimelineFactory { final DriftTimelineRepository _timelineRepository; final SettingsService _settingsService; - const TimelineFactory({ - required DriftTimelineRepository timelineRepository, - required SettingsService settingsService, - }) : _timelineRepository = timelineRepository, - _settingsService = settingsService; + const TimelineFactory({required DriftTimelineRepository timelineRepository, required SettingsService settingsService}) + : _timelineRepository = timelineRepository, + _settingsService = settingsService; GroupAssetsBy get groupBy { final group = GroupAssetsBy.values[_settingsService.get(Setting.groupAssetsBy)]; @@ -75,17 +67,11 @@ class TimelineService { int _totalAssets = 0; int get totalAssets => _totalAssets; - TimelineService(TimelineQuery query) - : this._( - assetSource: query.assetSource, - bucketSource: query.bucketSource, - ); + TimelineService(TimelineQuery query) : this._(assetSource: query.assetSource, bucketSource: query.bucketSource); - TimelineService._({ - required TimelineAssetSource assetSource, - required TimelineBucketSource bucketSource, - }) : _assetSource = assetSource, - _bucketSource = bucketSource { + TimelineService._({required TimelineAssetSource assetSource, required TimelineBucketSource bucketSource}) + : _assetSource = assetSource, + _bucketSource = bucketSource { _bucketSubscription = _bucketSource().listen((buckets) { _mutex.run(() async { final totalAssets = buckets.fold(0, (acc, bucket) => acc + bucket.assetCount); @@ -103,10 +89,7 @@ class TimelineService { count = kTimelineAssetLoadBatchSize; } else { offset = _bufferOffset; - count = math.min( - _buffer.length, - totalAssets - _bufferOffset, - ); + count = math.min(_buffer.length, totalAssets - _bufferOffset); } _buffer = await _assetSource(offset, count); _bufferOffset = offset; @@ -134,10 +117,7 @@ class TimelineService { // make sure to load a meaningful amount of data (and not only the requested slice) // otherwise, each call to [loadAssets] would result in DB call trashing performance // fills small requests to [kTimelineAssetLoadBatchSize], adds some legroom into the opposite scroll direction for large requests - final len = math.max( - kTimelineAssetLoadBatchSize, - count + kTimelineAssetLoadOppositeSize, - ); + final len = math.max(kTimelineAssetLoadBatchSize, count + kTimelineAssetLoadOppositeSize); // when scrolling forward, start shortly before the requested offset // when scrolling backward, end shortly after the requested offset to guard against the user scrolling // in the other direction a tiny bit resulting in another required load from the DB diff --git a/mobile/lib/domain/services/user.service.dart b/mobile/lib/domain/services/user.service.dart index 14fed9fb93..3e948fe0f5 100644 --- a/mobile/lib/domain/services/user.service.dart +++ b/mobile/lib/domain/services/user.service.dart @@ -18,9 +18,9 @@ class UserService { required IsarUserRepository isarUserRepository, required UserApiRepository userApiRepository, required StoreService storeService, - }) : _isarUserRepository = isarUserRepository, - _userApiRepository = userApiRepository, - _storeService = storeService; + }) : _isarUserRepository = isarUserRepository, + _userApiRepository = userApiRepository, + _storeService = storeService; UserDto getMyUser() { return _storeService.get(StoreKey.currentUser); @@ -44,10 +44,7 @@ class UserService { Future createProfileImage(String name, Uint8List image) async { try { - final path = await _userApiRepository.createProfileImage( - name: name, - data: image, - ); + final path = await _userApiRepository.createProfileImage(name: name, data: image); final updatedUser = getMyUser().copyWith(profileImagePath: path); await _storeService.put(StoreKey.currentUser, updatedUser); await _isarUserRepository.update(updatedUser); diff --git a/mobile/lib/domain/utils/background_sync.dart b/mobile/lib/domain/utils/background_sync.dart index 1524c412f1..1944591c93 100644 --- a/mobile/lib/domain/utils/background_sync.dart +++ b/mobile/lib/domain/utils/background_sync.dart @@ -66,23 +66,21 @@ class BackgroundSyncManager { // We use a ternary operator to avoid [_deviceAlbumSyncTask] from being // captured by the closure passed to [runInIsolateGentle]. _deviceAlbumSyncTask = full - ? runInIsolateGentle( - computation: (ref) => ref.read(localSyncServiceProvider).sync(full: true), - ) - : runInIsolateGentle( - computation: (ref) => ref.read(localSyncServiceProvider).sync(full: false), - ); + ? runInIsolateGentle(computation: (ref) => ref.read(localSyncServiceProvider).sync(full: true)) + : runInIsolateGentle(computation: (ref) => ref.read(localSyncServiceProvider).sync(full: false)); - return _deviceAlbumSyncTask!.whenComplete(() { - _deviceAlbumSyncTask = null; - onLocalSyncComplete?.call(); - }).catchError((error) { - onLocalSyncError?.call(error.toString()); - _deviceAlbumSyncTask = null; - }); + return _deviceAlbumSyncTask! + .whenComplete(() { + _deviceAlbumSyncTask = null; + onLocalSyncComplete?.call(); + }) + .catchError((error) { + onLocalSyncError?.call(error.toString()); + _deviceAlbumSyncTask = null; + }); } -// No need to cancel the task, as it can also be run when the user logs out + // No need to cancel the task, as it can also be run when the user logs out Future hashAssets() { if (_hashTask != null) { return _hashTask!.future; @@ -90,17 +88,17 @@ class BackgroundSyncManager { onHashingStart?.call(); - _hashTask = runInIsolateGentle( - computation: (ref) => ref.read(hashServiceProvider).hashAssets(), - ); + _hashTask = runInIsolateGentle(computation: (ref) => ref.read(hashServiceProvider).hashAssets()); - return _hashTask!.whenComplete(() { - onHashingComplete?.call(); - _hashTask = null; - }).catchError((error) { - onHashingError?.call(error.toString()); - _hashTask = null; - }); + return _hashTask! + .whenComplete(() { + onHashingComplete?.call(); + _hashTask = null; + }) + .catchError((error) { + onHashingError?.call(error.toString()); + _hashTask = null; + }); } Future syncRemote() { @@ -110,16 +108,16 @@ class BackgroundSyncManager { onRemoteSyncStart?.call(); - _syncTask = runInIsolateGentle( - computation: (ref) => ref.read(syncStreamServiceProvider).sync(), - ); - return _syncTask!.whenComplete(() { - onRemoteSyncComplete?.call(); - _syncTask = null; - }).catchError((error) { - onRemoteSyncError?.call(error.toString()); - _syncTask = null; - }); + _syncTask = runInIsolateGentle(computation: (ref) => ref.read(syncStreamServiceProvider).sync()); + return _syncTask! + .whenComplete(() { + onRemoteSyncComplete?.call(); + _syncTask = null; + }) + .catchError((error) { + onRemoteSyncError?.call(error.toString()); + _syncTask = null; + }); } Future syncWebsocketBatch(List batchData) { @@ -133,9 +131,6 @@ class BackgroundSyncManager { } } -Cancelable _handleWsAssetUploadReadyV1Batch( - List batchData, -) => - runInIsolateGentle( - computation: (ref) => ref.read(syncStreamServiceProvider).handleWsAssetUploadReadyV1Batch(batchData), - ); +Cancelable _handleWsAssetUploadReadyV1Batch(List batchData) => runInIsolateGentle( + computation: (ref) => ref.read(syncStreamServiceProvider).handleWsAssetUploadReadyV1Batch(batchData), +); diff --git a/mobile/lib/domain/utils/event_stream.dart b/mobile/lib/domain/utils/event_stream.dart index 008ddef183..5967fdca50 100644 --- a/mobile/lib/domain/utils/event_stream.dart +++ b/mobile/lib/domain/utils/event_stream.dart @@ -28,12 +28,7 @@ class EventStream { void Function()? onDone, bool? cancelOnError, }) { - return where().listen( - onData, - onError: onError, - onDone: onDone, - cancelOnError: cancelOnError, - ); + return where().listen(onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError); } /// Closes the stream controller diff --git a/mobile/lib/entities/album.entity.dart b/mobile/lib/entities/album.entity.dart index a7cb612d6e..2ca0d50dcc 100644 --- a/mobile/lib/entities/album.entity.dart +++ b/mobile/lib/entities/album.entity.dart @@ -113,10 +113,7 @@ class Album { modifiedAt.isAtSameMomentAs(other.modifiedAt) && isAtSameMomentAs(startDate, other.startDate) && isAtSameMomentAs(endDate, other.endDate) && - isAtSameMomentAs( - lastModifiedAssetTimestamp, - other.lastModifiedAssetTimestamp, - ) && + isAtSameMomentAs(lastModifiedAssetTimestamp, other.lastModifiedAssetTimestamp) && shared == other.shared && activityEnabled == other.activityEnabled && owner.value == other.owner.value && @@ -169,9 +166,7 @@ class Album { a.thumbnail.value = await db.assets.where().remoteIdEqualTo(dto.albumThumbnailAssetId).findFirst(); } if (dto.albumUsers.isNotEmpty) { - final users = await db.users.getAllById( - dto.albumUsers.map((e) => e.user.id).toList(growable: false), - ); + final users = await db.users.getAllById(dto.albumUsers.map((e) => e.user.id).toList(growable: false)); a.sharedUsers.addAll(users.cast()); } if (dto.assets.isNotEmpty) { diff --git a/mobile/lib/entities/album.entity.g.dart b/mobile/lib/entities/album.entity.g.dart index 546101baca..e6ecde7f9a 100644 --- a/mobile/lib/entities/album.entity.g.dart +++ b/mobile/lib/entities/album.entity.g.dart @@ -42,31 +42,19 @@ const AlbumSchema = CollectionSchema( name: r'lastModifiedAssetTimestamp', type: IsarType.dateTime, ), - r'localId': PropertySchema( - id: 5, - name: r'localId', - type: IsarType.string, - ), + r'localId': PropertySchema(id: 5, name: r'localId', type: IsarType.string), r'modifiedAt': PropertySchema( id: 6, name: r'modifiedAt', type: IsarType.dateTime, ), - r'name': PropertySchema( - id: 7, - name: r'name', - type: IsarType.string, - ), + r'name': PropertySchema(id: 7, name: r'name', type: IsarType.string), r'remoteId': PropertySchema( id: 8, name: r'remoteId', type: IsarType.string, ), - r'shared': PropertySchema( - id: 9, - name: r'shared', - type: IsarType.bool, - ), + r'shared': PropertySchema(id: 9, name: r'shared', type: IsarType.bool), r'sortOrder': PropertySchema( id: 10, name: r'sortOrder', @@ -77,8 +65,9 @@ const AlbumSchema = CollectionSchema( id: 11, name: r'startDate', type: IsarType.dateTime, - ) + ), }, + estimateSize: _albumEstimateSize, serialize: _albumSerialize, deserialize: _albumDeserialize, @@ -95,7 +84,7 @@ const AlbumSchema = CollectionSchema( name: r'remoteId', type: IndexType.hash, caseSensitive: true, - ) + ), ], ), r'localId': IndexSchema( @@ -108,9 +97,9 @@ const AlbumSchema = CollectionSchema( name: r'localId', type: IndexType.hash, caseSensitive: true, - ) + ), ], - ) + ), }, links: { r'owner': LinkSchema( @@ -136,9 +125,10 @@ const AlbumSchema = CollectionSchema( name: r'assets', target: r'Asset', single: false, - ) + ), }, embeddedSchemas: {}, + getId: _albumGetId, getLinks: _albumGetLinks, attach: _albumAttach, @@ -212,7 +202,7 @@ Album _albumDeserialize( shared: reader.readBool(offsets[9]), sortOrder: _AlbumsortOrderValueEnumMap[reader.readByteOrNull(offsets[10])] ?? - SortOrder.desc, + SortOrder.desc, startDate: reader.readDateTimeOrNull(offsets[11]), ); object.id = id; @@ -248,7 +238,8 @@ P _albumDeserializeProp

( return (reader.readBool(offset)) as P; case 10: return (_AlbumsortOrderValueEnumMap[reader.readByteOrNull(offset)] ?? - SortOrder.desc) as P; + SortOrder.desc) + as P; case 11: return (reader.readDateTimeOrNull(offset)) as P; default: @@ -256,14 +247,8 @@ P _albumDeserializeProp

( } } -const _AlbumsortOrderEnumValueMap = { - 'asc': 0, - 'desc': 1, -}; -const _AlbumsortOrderValueEnumMap = { - 0: SortOrder.asc, - 1: SortOrder.desc, -}; +const _AlbumsortOrderEnumValueMap = {'asc': 0, 'desc': 1}; +const _AlbumsortOrderValueEnumMap = {0: SortOrder.asc, 1: SortOrder.desc}; Id _albumGetId(Album object) { return object.id; @@ -277,8 +262,12 @@ void _albumAttach(IsarCollection col, Id id, Album object) { object.id = id; object.owner.attach(col, col.isar.collection(), r'owner', id); object.thumbnail.attach(col, col.isar.collection(), r'thumbnail', id); - object.sharedUsers - .attach(col, col.isar.collection(), r'sharedUsers', id); + object.sharedUsers.attach( + col, + col.isar.collection(), + r'sharedUsers', + id, + ); object.assets.attach(col, col.isar.collection(), r'assets', id); } @@ -293,10 +282,7 @@ extension AlbumQueryWhereSort on QueryBuilder { extension AlbumQueryWhere on QueryBuilder { QueryBuilder idEqualTo(Id id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); + return query.addWhereClause(IdWhereClause.between(lower: id, upper: id)); }); } @@ -322,8 +308,10 @@ extension AlbumQueryWhere on QueryBuilder { }); } - QueryBuilder idGreaterThan(Id id, - {bool include = false}) { + QueryBuilder idGreaterThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: id, includeLower: include), @@ -331,8 +319,10 @@ extension AlbumQueryWhere on QueryBuilder { }); } - QueryBuilder idLessThan(Id id, - {bool include = false}) { + QueryBuilder idLessThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: id, includeUpper: include), @@ -347,141 +337,163 @@ extension AlbumQueryWhere on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder remoteIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'remoteId', - value: [null], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'remoteId', value: [null]), + ); }); } QueryBuilder remoteIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [null], - includeLower: false, - upper: [], - )); + return query.addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [null], + includeLower: false, + upper: [], + ), + ); }); } QueryBuilder remoteIdEqualTo( - String? remoteId) { + String? remoteId, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'remoteId', - value: [remoteId], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'remoteId', value: [remoteId]), + ); }); } QueryBuilder remoteIdNotEqualTo( - String? remoteId) { + String? remoteId, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [], - upper: [remoteId], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [remoteId], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [], + upper: [remoteId], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [remoteId], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [remoteId], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [], - upper: [remoteId], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [remoteId], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [], + upper: [remoteId], + includeUpper: false, + ), + ); } }); } QueryBuilder localIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'localId', - value: [null], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'localId', value: [null]), + ); }); } QueryBuilder localIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [null], - includeLower: false, - upper: [], - )); + return query.addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [null], + includeLower: false, + upper: [], + ), + ); }); } QueryBuilder localIdEqualTo( - String? localId) { + String? localId, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'localId', - value: [localId], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'localId', value: [localId]), + ); }); } QueryBuilder localIdNotEqualTo( - String? localId) { + String? localId, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [], - upper: [localId], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [localId], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [], + upper: [localId], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [localId], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [localId], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [], - upper: [localId], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [localId], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [], + upper: [localId], + includeUpper: false, + ), + ); } }); } @@ -489,22 +501,22 @@ extension AlbumQueryWhere on QueryBuilder { extension AlbumQueryFilter on QueryBuilder { QueryBuilder activityEnabledEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'activityEnabled', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'activityEnabled', value: value), + ); }); } QueryBuilder createdAtEqualTo( - DateTime value) { + DateTime value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'createdAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'createdAt', value: value), + ); }); } @@ -513,11 +525,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'createdAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'createdAt', + value: value, + ), + ); }); } @@ -526,11 +540,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'createdAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'createdAt', + value: value, + ), + ); }); } @@ -541,29 +557,31 @@ extension AlbumQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'createdAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'createdAt', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder descriptionIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'description', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'description'), + ); }); } QueryBuilder descriptionIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'description', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'description'), + ); }); } @@ -572,11 +590,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -586,12 +606,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -601,12 +623,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -618,14 +642,16 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'description', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'description', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -634,11 +660,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -647,79 +675,85 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder descriptionContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder descriptionMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'description', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'description', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder descriptionIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'description', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'description', value: ''), + ); }); } QueryBuilder descriptionIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'description', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'description', value: ''), + ); }); } QueryBuilder endDateIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'endDate', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'endDate'), + ); }); } QueryBuilder endDateIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'endDate', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'endDate'), + ); }); } QueryBuilder endDateEqualTo( - DateTime? value) { + DateTime? value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'endDate', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'endDate', value: value), + ); }); } @@ -728,11 +762,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'endDate', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'endDate', + value: value, + ), + ); }); } @@ -741,11 +777,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'endDate', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'endDate', + value: value, + ), + ); }); } @@ -756,22 +794,23 @@ extension AlbumQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'endDate', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'endDate', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder idEqualTo(Id value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: value), + ); }); } @@ -780,11 +819,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -793,11 +834,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -808,103 +851,112 @@ extension AlbumQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - lastModifiedAssetTimestampIsNull() { + lastModifiedAssetTimestampIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'lastModifiedAssetTimestamp', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'lastModifiedAssetTimestamp'), + ); }); } QueryBuilder - lastModifiedAssetTimestampIsNotNull() { + lastModifiedAssetTimestampIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'lastModifiedAssetTimestamp', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull( + property: r'lastModifiedAssetTimestamp', + ), + ); }); } QueryBuilder - lastModifiedAssetTimestampEqualTo(DateTime? value) { + lastModifiedAssetTimestampEqualTo(DateTime? value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'lastModifiedAssetTimestamp', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'lastModifiedAssetTimestamp', + value: value, + ), + ); }); } QueryBuilder - lastModifiedAssetTimestampGreaterThan( + lastModifiedAssetTimestampGreaterThan( DateTime? value, { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'lastModifiedAssetTimestamp', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'lastModifiedAssetTimestamp', + value: value, + ), + ); }); } QueryBuilder - lastModifiedAssetTimestampLessThan( - DateTime? value, { - bool include = false, - }) { + lastModifiedAssetTimestampLessThan(DateTime? value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'lastModifiedAssetTimestamp', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'lastModifiedAssetTimestamp', + value: value, + ), + ); }); } QueryBuilder - lastModifiedAssetTimestampBetween( + lastModifiedAssetTimestampBetween( DateTime? lower, DateTime? upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'lastModifiedAssetTimestamp', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'lastModifiedAssetTimestamp', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder localIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'localId', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'localId'), + ); }); } QueryBuilder localIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'localId', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'localId'), + ); }); } @@ -913,11 +965,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -927,12 +981,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -942,12 +998,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -959,14 +1017,16 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'localId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'localId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -975,11 +1035,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -988,63 +1050,69 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder localIdContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder localIdMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'localId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'localId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder localIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'localId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'localId', value: ''), + ); }); } QueryBuilder localIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'localId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'localId', value: ''), + ); }); } QueryBuilder modifiedAtEqualTo( - DateTime value) { + DateTime value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'modifiedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'modifiedAt', value: value), + ); }); } @@ -1053,11 +1121,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'modifiedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'modifiedAt', + value: value, + ), + ); }); } @@ -1066,11 +1136,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'modifiedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'modifiedAt', + value: value, + ), + ); }); } @@ -1081,13 +1153,15 @@ extension AlbumQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'modifiedAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'modifiedAt', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } @@ -1096,11 +1170,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1110,12 +1186,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1125,12 +1203,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1142,14 +1222,16 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'name', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'name', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1158,11 +1240,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1171,67 +1255,75 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder nameContains(String value, - {bool caseSensitive = true}) { + QueryBuilder nameContains( + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder nameMatches(String pattern, - {bool caseSensitive = true}) { + QueryBuilder nameMatches( + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'name', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'name', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder nameIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'name', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'name', value: ''), + ); }); } QueryBuilder nameIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'name', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'name', value: ''), + ); }); } QueryBuilder remoteIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'remoteId', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'remoteId'), + ); }); } QueryBuilder remoteIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'remoteId', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'remoteId'), + ); }); } @@ -1240,11 +1332,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1254,12 +1348,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1269,12 +1365,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1286,14 +1384,16 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'remoteId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'remoteId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1302,11 +1402,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1315,72 +1417,77 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder remoteIdContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder remoteIdMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'remoteId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'remoteId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder remoteIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'remoteId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'remoteId', value: ''), + ); }); } QueryBuilder remoteIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'remoteId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'remoteId', value: ''), + ); }); } QueryBuilder sharedEqualTo(bool value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'shared', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'shared', value: value), + ); }); } QueryBuilder sortOrderEqualTo( - SortOrder value) { + SortOrder value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'sortOrder', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'sortOrder', value: value), + ); }); } @@ -1389,11 +1496,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'sortOrder', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'sortOrder', + value: value, + ), + ); }); } @@ -1402,11 +1511,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'sortOrder', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'sortOrder', + value: value, + ), + ); }); } @@ -1417,39 +1528,41 @@ extension AlbumQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'sortOrder', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'sortOrder', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder startDateIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'startDate', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'startDate'), + ); }); } QueryBuilder startDateIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'startDate', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'startDate'), + ); }); } QueryBuilder startDateEqualTo( - DateTime? value) { + DateTime? value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'startDate', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'startDate', value: value), + ); }); } @@ -1458,11 +1571,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'startDate', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'startDate', + value: value, + ), + ); }); } @@ -1471,11 +1586,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'startDate', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'startDate', + value: value, + ), + ); }); } @@ -1486,13 +1603,15 @@ extension AlbumQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'startDate', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'startDate', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -1513,7 +1632,8 @@ extension AlbumQueryLinks on QueryBuilder { } QueryBuilder thumbnail( - FilterQuery q) { + FilterQuery q, + ) { return QueryBuilder.apply(this, (query) { return query.link(q, r'thumbnail'); }); @@ -1526,14 +1646,16 @@ extension AlbumQueryLinks on QueryBuilder { } QueryBuilder sharedUsers( - FilterQuery q) { + FilterQuery q, + ) { return QueryBuilder.apply(this, (query) { return query.link(q, r'sharedUsers'); }); } QueryBuilder sharedUsersLengthEqualTo( - int length) { + int length, + ) { return QueryBuilder.apply(this, (query) { return query.linkLength(r'sharedUsers', length, true, length, true); }); @@ -1561,10 +1683,7 @@ extension AlbumQueryLinks on QueryBuilder { } QueryBuilder - sharedUsersLengthGreaterThan( - int length, { - bool include = false, - }) { + sharedUsersLengthGreaterThan(int length, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.linkLength(r'sharedUsers', length, include, 999999, true); }); @@ -1578,19 +1697,26 @@ extension AlbumQueryLinks on QueryBuilder { }) { return QueryBuilder.apply(this, (query) { return query.linkLength( - r'sharedUsers', lower, includeLower, upper, includeUpper); + r'sharedUsers', + lower, + includeLower, + upper, + includeUpper, + ); }); } QueryBuilder assets( - FilterQuery q) { + FilterQuery q, + ) { return QueryBuilder.apply(this, (query) { return query.link(q, r'assets'); }); } QueryBuilder assetsLengthEqualTo( - int length) { + int length, + ) { return QueryBuilder.apply(this, (query) { return query.linkLength(r'assets', length, true, length, true); }); @@ -1634,7 +1760,12 @@ extension AlbumQueryLinks on QueryBuilder { }) { return QueryBuilder.apply(this, (query) { return query.linkLength( - r'assets', lower, includeLower, upper, includeUpper); + r'assets', + lower, + includeLower, + upper, + includeUpper, + ); }); } } @@ -1695,7 +1826,7 @@ extension AlbumQuerySortBy on QueryBuilder { } QueryBuilder - sortByLastModifiedAssetTimestampDesc() { + sortByLastModifiedAssetTimestampDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'lastModifiedAssetTimestamp', Sort.desc); }); @@ -1854,7 +1985,7 @@ extension AlbumQuerySortThenBy on QueryBuilder { } QueryBuilder - thenByLastModifiedAssetTimestampDesc() { + thenByLastModifiedAssetTimestampDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'lastModifiedAssetTimestamp', Sort.desc); }); @@ -1958,8 +2089,9 @@ extension AlbumQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByDescription( - {bool caseSensitive = true}) { + QueryBuilder distinctByDescription({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'description', caseSensitive: caseSensitive); }); @@ -1977,8 +2109,9 @@ extension AlbumQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByLocalId( - {bool caseSensitive = true}) { + QueryBuilder distinctByLocalId({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'localId', caseSensitive: caseSensitive); }); @@ -1990,15 +2123,17 @@ extension AlbumQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByName( - {bool caseSensitive = true}) { + QueryBuilder distinctByName({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'name', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByRemoteId( - {bool caseSensitive = true}) { + QueryBuilder distinctByRemoteId({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'remoteId', caseSensitive: caseSensitive); }); @@ -2055,7 +2190,7 @@ extension AlbumQueryProperty on QueryBuilder { } QueryBuilder - lastModifiedAssetTimestampProperty() { + lastModifiedAssetTimestampProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'lastModifiedAssetTimestamp'); }); diff --git a/mobile/lib/entities/android_device_asset.entity.g.dart b/mobile/lib/entities/android_device_asset.entity.g.dart index eaa7658565..9034709b8e 100644 --- a/mobile/lib/entities/android_device_asset.entity.g.dart +++ b/mobile/lib/entities/android_device_asset.entity.g.dart @@ -18,12 +18,9 @@ const AndroidDeviceAssetSchema = CollectionSchema( name: r'AndroidDeviceAsset', id: -6758387181232899335, properties: { - r'hash': PropertySchema( - id: 0, - name: r'hash', - type: IsarType.byteList, - ) + r'hash': PropertySchema(id: 0, name: r'hash', type: IsarType.byteList), }, + estimateSize: _androidDeviceAssetEstimateSize, serialize: _androidDeviceAssetSerialize, deserialize: _androidDeviceAssetDeserialize, @@ -40,12 +37,13 @@ const AndroidDeviceAssetSchema = CollectionSchema( name: r'hash', type: IndexType.hash, caseSensitive: false, - ) + ), ], - ) + ), }, links: {}, embeddedSchemas: {}, + getId: _androidDeviceAssetGetId, getLinks: _androidDeviceAssetGetLinks, attach: _androidDeviceAssetAttach, @@ -103,12 +101,16 @@ Id _androidDeviceAssetGetId(AndroidDeviceAsset object) { } List> _androidDeviceAssetGetLinks( - AndroidDeviceAsset object) { + AndroidDeviceAsset object, +) { return []; } void _androidDeviceAssetAttach( - IsarCollection col, Id id, AndroidDeviceAsset object) { + IsarCollection col, + Id id, + AndroidDeviceAsset object, +) { object.id = id; } @@ -124,17 +126,14 @@ extension AndroidDeviceAssetQueryWhereSort extension AndroidDeviceAssetQueryWhere on QueryBuilder { QueryBuilder - idEqualTo(Id id) { + idEqualTo(Id id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); + return query.addWhereClause(IdWhereClause.between(lower: id, upper: id)); }); } QueryBuilder - idNotEqualTo(Id id) { + idNotEqualTo(Id id) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query @@ -157,7 +156,7 @@ extension AndroidDeviceAssetQueryWhere } QueryBuilder - idGreaterThan(Id id, {bool include = false}) { + idGreaterThan(Id id, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: id, includeLower: include), @@ -166,7 +165,7 @@ extension AndroidDeviceAssetQueryWhere } QueryBuilder - idLessThan(Id id, {bool include = false}) { + idLessThan(Id id, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: id, includeUpper: include), @@ -175,63 +174,72 @@ extension AndroidDeviceAssetQueryWhere } QueryBuilder - idBetween( + idBetween( Id lowerId, Id upperId, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - hashEqualTo(List hash) { + hashEqualTo(List hash) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'hash', - value: [hash], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'hash', value: [hash]), + ); }); } QueryBuilder - hashNotEqualTo(List hash) { + hashNotEqualTo(List hash) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [], - upper: [hash], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [hash], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [], + upper: [hash], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [hash], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [hash], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [], - upper: [hash], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [hash], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [], + upper: [hash], + includeUpper: false, + ), + ); } }); } @@ -240,134 +248,97 @@ extension AndroidDeviceAssetQueryWhere extension AndroidDeviceAssetQueryFilter on QueryBuilder { QueryBuilder - hashElementEqualTo(int value) { + hashElementEqualTo(int value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'hash', value: value), + ); }); } QueryBuilder - hashElementGreaterThan( - int value, { - bool include = false, - }) { + hashElementGreaterThan(int value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'hash', + value: value, + ), + ); }); } QueryBuilder - hashElementLessThan( - int value, { - bool include = false, - }) { + hashElementLessThan(int value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'hash', + value: value, + ), + ); }); } QueryBuilder - hashElementBetween( + hashElementBetween( int lower, int upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'hash', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); - }); - } - - QueryBuilder - hashLengthEqualTo(int length) { - return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - length, - true, - length, - true, + return query.addFilterCondition( + FilterCondition.between( + property: r'hash', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), ); }); } QueryBuilder - hashIsEmpty() { + hashLengthEqualTo(int length) { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - true, - 0, - true, - ); + return query.listLength(r'hash', length, true, length, true); }); } QueryBuilder - hashIsNotEmpty() { + hashIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - false, - 999999, - true, - ); + return query.listLength(r'hash', 0, true, 0, true); }); } QueryBuilder - hashLengthLessThan( - int length, { - bool include = false, - }) { + hashIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - true, - length, - include, - ); + return query.listLength(r'hash', 0, false, 999999, true); }); } QueryBuilder - hashLengthGreaterThan( - int length, { - bool include = false, - }) { + hashLengthLessThan(int length, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - length, - include, - 999999, - true, - ); + return query.listLength(r'hash', 0, true, length, include); }); } QueryBuilder - hashLengthBetween( + hashLengthGreaterThan(int length, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.listLength(r'hash', length, include, 999999, true); + }); + } + + QueryBuilder + hashLengthBetween( int lower, int upper, { bool includeLower = true, @@ -385,58 +356,57 @@ extension AndroidDeviceAssetQueryFilter } QueryBuilder - idEqualTo(Id value) { + idEqualTo(Id value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: value), + ); }); } QueryBuilder - idGreaterThan( - Id value, { - bool include = false, - }) { + idGreaterThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + ), + ); }); } QueryBuilder - idLessThan( - Id value, { - bool include = false, - }) { + idLessThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + ), + ); }); } QueryBuilder - idBetween( + idBetween( Id lower, Id upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -453,14 +423,14 @@ extension AndroidDeviceAssetQuerySortBy extension AndroidDeviceAssetQuerySortThenBy on QueryBuilder { QueryBuilder - thenById() { + thenById() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'id', Sort.asc); }); } QueryBuilder - thenByIdDesc() { + thenByIdDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'id', Sort.desc); }); @@ -470,7 +440,7 @@ extension AndroidDeviceAssetQuerySortThenBy extension AndroidDeviceAssetQueryWhereDistinct on QueryBuilder { QueryBuilder - distinctByHash() { + distinctByHash() { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'hash'); }); diff --git a/mobile/lib/entities/asset.entity.dart b/mobile/lib/entities/asset.entity.dart index f5c577a060..0d549457a1 100644 --- a/mobile/lib/entities/asset.entity.dart +++ b/mobile/lib/entities/asset.entity.dart @@ -19,30 +19,30 @@ part 'asset.entity.g.dart'; @Collection(inheritance: false) class Asset { Asset.remote(AssetResponseDto remote) - : remoteId = remote.id, - checksum = remote.checksum, - fileCreatedAt = remote.fileCreatedAt, - fileModifiedAt = remote.fileModifiedAt, - updatedAt = remote.updatedAt, - durationInSeconds = remote.duration.toDuration()?.inSeconds ?? 0, - type = remote.type.toAssetType(), - fileName = remote.originalFileName, - height = remote.exifInfo?.exifImageHeight?.toInt(), - width = remote.exifInfo?.exifImageWidth?.toInt(), - livePhotoVideoId = remote.livePhotoVideoId, - ownerId = fastHash(remote.ownerId), - exifInfo = remote.exifInfo == null ? null : ExifDtoConverter.fromDto(remote.exifInfo!), - isFavorite = remote.isFavorite, - isArchived = remote.isArchived, - isTrashed = remote.isTrashed, - isOffline = remote.isOffline, - // workaround to nullify stackPrimaryAssetId for the parent asset until we refactor the mobile app - // stack handling to properly handle it - stackPrimaryAssetId = remote.stack?.primaryAssetId == remote.id ? null : remote.stack?.primaryAssetId, - stackCount = remote.stack?.assetCount ?? 0, - stackId = remote.stack?.id, - thumbhash = remote.thumbhash, - visibility = getVisibility(remote.visibility); + : remoteId = remote.id, + checksum = remote.checksum, + fileCreatedAt = remote.fileCreatedAt, + fileModifiedAt = remote.fileModifiedAt, + updatedAt = remote.updatedAt, + durationInSeconds = remote.duration.toDuration()?.inSeconds ?? 0, + type = remote.type.toAssetType(), + fileName = remote.originalFileName, + height = remote.exifInfo?.exifImageHeight?.toInt(), + width = remote.exifInfo?.exifImageWidth?.toInt(), + livePhotoVideoId = remote.livePhotoVideoId, + ownerId = fastHash(remote.ownerId), + exifInfo = remote.exifInfo == null ? null : ExifDtoConverter.fromDto(remote.exifInfo!), + isFavorite = remote.isFavorite, + isArchived = remote.isArchived, + isTrashed = remote.isTrashed, + isOffline = remote.isOffline, + // workaround to nullify stackPrimaryAssetId for the parent asset until we refactor the mobile app + // stack handling to properly handle it + stackPrimaryAssetId = remote.stack?.primaryAssetId == remote.id ? null : remote.stack?.primaryAssetId, + stackCount = remote.stack?.assetCount ?? 0, + stackId = remote.stack?.id, + thumbhash = remote.thumbhash, + visibility = getVisibility(remote.visibility); Asset({ this.id = Isar.autoIncrement, @@ -127,11 +127,7 @@ class Asset { @Index(unique: false, replace: false, type: IndexType.hash) String? localId; - @Index( - unique: true, - replace: false, - composite: [CompositeIndex("checksum", type: IndexType.hash)], - ) + @Index(unique: true, replace: false, composite: [CompositeIndex("checksum", type: IndexType.hash)]) int ownerId; DateTime fileCreatedAt; @@ -447,33 +443,32 @@ class Asset { int? stackCount, String? thumbhash, AssetVisibilityEnum? visibility, - }) => - Asset( - id: id ?? this.id, - checksum: checksum ?? this.checksum, - remoteId: remoteId ?? this.remoteId, - localId: localId ?? this.localId, - ownerId: ownerId ?? this.ownerId, - fileCreatedAt: fileCreatedAt ?? this.fileCreatedAt, - fileModifiedAt: fileModifiedAt ?? this.fileModifiedAt, - updatedAt: updatedAt ?? this.updatedAt, - durationInSeconds: durationInSeconds ?? this.durationInSeconds, - type: type ?? this.type, - width: width ?? this.width, - height: height ?? this.height, - fileName: fileName ?? this.fileName, - livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, - isFavorite: isFavorite ?? this.isFavorite, - isArchived: isArchived ?? this.isArchived, - isTrashed: isTrashed ?? this.isTrashed, - isOffline: isOffline ?? this.isOffline, - exifInfo: exifInfo ?? this.exifInfo, - stackId: stackId ?? this.stackId, - stackPrimaryAssetId: stackPrimaryAssetId ?? this.stackPrimaryAssetId, - stackCount: stackCount ?? this.stackCount, - thumbhash: thumbhash ?? this.thumbhash, - visibility: visibility ?? this.visibility, - ); + }) => Asset( + id: id ?? this.id, + checksum: checksum ?? this.checksum, + remoteId: remoteId ?? this.remoteId, + localId: localId ?? this.localId, + ownerId: ownerId ?? this.ownerId, + fileCreatedAt: fileCreatedAt ?? this.fileCreatedAt, + fileModifiedAt: fileModifiedAt ?? this.fileModifiedAt, + updatedAt: updatedAt ?? this.updatedAt, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + type: type ?? this.type, + width: width ?? this.width, + height: height ?? this.height, + fileName: fileName ?? this.fileName, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + isFavorite: isFavorite ?? this.isFavorite, + isArchived: isArchived ?? this.isArchived, + isTrashed: isTrashed ?? this.isTrashed, + isOffline: isOffline ?? this.isOffline, + exifInfo: exifInfo ?? this.exifInfo, + stackId: stackId ?? this.stackId, + stackPrimaryAssetId: stackPrimaryAssetId ?? this.stackPrimaryAssetId, + stackCount: stackCount ?? this.stackCount, + thumbhash: thumbhash ?? this.thumbhash, + visibility: visibility ?? this.visibility, + ); Future put(Isar db) async { await db.assets.put(this); @@ -494,10 +489,7 @@ class Asset { return compareByChecksum(a, b); } - static int compareByOwnerChecksumCreatedModified( - Asset a, - Asset b, - ) { + static int compareByOwnerChecksumCreatedModified(Asset a, Asset b) { final int ownerIdOrder = a.ownerId.compareTo(b.ownerId); if (ownerIdOrder != 0) return ownerIdOrder; final int checksumOrder = compareByChecksum(a, b); @@ -539,11 +531,11 @@ class Asset { } static getVisibility(AssetVisibility visibility) => switch (visibility) { - AssetVisibility.archive => AssetVisibilityEnum.archive, - AssetVisibility.hidden => AssetVisibilityEnum.hidden, - AssetVisibility.locked => AssetVisibilityEnum.locked, - AssetVisibility.timeline || _ => AssetVisibilityEnum.timeline, - }; + AssetVisibility.archive => AssetVisibilityEnum.archive, + AssetVisibility.hidden => AssetVisibilityEnum.hidden, + AssetVisibility.locked => AssetVisibilityEnum.locked, + AssetVisibility.timeline || _ => AssetVisibilityEnum.timeline, + }; } enum AssetType { @@ -556,21 +548,17 @@ enum AssetType { extension AssetTypeEnumHelper on AssetTypeEnum { AssetType toAssetType() => switch (this) { - AssetTypeEnum.IMAGE => AssetType.image, - AssetTypeEnum.VIDEO => AssetType.video, - AssetTypeEnum.AUDIO => AssetType.audio, - AssetTypeEnum.OTHER => AssetType.other, - _ => throw Exception(), - }; + AssetTypeEnum.IMAGE => AssetType.image, + AssetTypeEnum.VIDEO => AssetType.video, + AssetTypeEnum.AUDIO => AssetType.audio, + AssetTypeEnum.OTHER => AssetType.other, + _ => throw Exception(), + }; } /// Describes where the information of this asset came from: /// only from the local device, only from the remote server or merged from both -enum AssetState { - local, - remote, - merged, -} +enum AssetState { local, remote, merged } extension AssetsHelper on IsarCollection { Future deleteAllByRemoteId(Iterable ids) => ids.isEmpty ? Future.value(0) : remote(ids).deleteAll(); @@ -579,13 +567,9 @@ extension AssetsHelper on IsarCollection { Future> getAllByLocalId(Iterable ids) => ids.isEmpty ? Future.value([]) : local(ids).findAll(); Future getByRemoteId(String id) => where().remoteIdEqualTo(id).findFirst(); - QueryBuilder remote( - Iterable ids, - ) => + QueryBuilder remote(Iterable ids) => where().anyOf(ids, (q, String e) => q.remoteIdEqualTo(e)); - QueryBuilder local( - Iterable ids, - ) { + QueryBuilder local(Iterable ids) { return where().anyOf(ids, (q, String e) => q.localIdEqualTo(e)); } } diff --git a/mobile/lib/entities/asset.entity.g.dart b/mobile/lib/entities/asset.entity.g.dart index b558690813..be5b427d01 100644 --- a/mobile/lib/entities/asset.entity.g.dart +++ b/mobile/lib/entities/asset.entity.g.dart @@ -42,11 +42,7 @@ const AssetSchema = CollectionSchema( name: r'fileName', type: IsarType.string, ), - r'height': PropertySchema( - id: 5, - name: r'height', - type: IsarType.int, - ), + r'height': PropertySchema(id: 5, name: r'height', type: IsarType.int), r'isArchived': PropertySchema( id: 6, name: r'isArchived', @@ -72,16 +68,8 @@ const AssetSchema = CollectionSchema( name: r'livePhotoVideoId', type: IsarType.string, ), - r'localId': PropertySchema( - id: 11, - name: r'localId', - type: IsarType.string, - ), - r'ownerId': PropertySchema( - id: 12, - name: r'ownerId', - type: IsarType.long, - ), + r'localId': PropertySchema(id: 11, name: r'localId', type: IsarType.string), + r'ownerId': PropertySchema(id: 12, name: r'ownerId', type: IsarType.long), r'remoteId': PropertySchema( id: 13, name: r'remoteId', @@ -92,11 +80,7 @@ const AssetSchema = CollectionSchema( name: r'stackCount', type: IsarType.long, ), - r'stackId': PropertySchema( - id: 15, - name: r'stackId', - type: IsarType.string, - ), + r'stackId': PropertySchema(id: 15, name: r'stackId', type: IsarType.string), r'stackPrimaryAssetId': PropertySchema( id: 16, name: r'stackPrimaryAssetId', @@ -124,12 +108,9 @@ const AssetSchema = CollectionSchema( type: IsarType.byte, enumMap: _AssetvisibilityEnumValueMap, ), - r'width': PropertySchema( - id: 21, - name: r'width', - type: IsarType.int, - ) + r'width': PropertySchema(id: 21, name: r'width', type: IsarType.int), }, + estimateSize: _assetEstimateSize, serialize: _assetSerialize, deserialize: _assetDeserialize, @@ -146,7 +127,7 @@ const AssetSchema = CollectionSchema( name: r'remoteId', type: IndexType.hash, caseSensitive: true, - ) + ), ], ), r'localId': IndexSchema( @@ -159,7 +140,7 @@ const AssetSchema = CollectionSchema( name: r'localId', type: IndexType.hash, caseSensitive: true, - ) + ), ], ), r'ownerId_checksum': IndexSchema( @@ -177,12 +158,13 @@ const AssetSchema = CollectionSchema( name: r'checksum', type: IndexType.hash, caseSensitive: true, - ) + ), ], - ) + ), }, links: {}, embeddedSchemas: {}, + getId: _assetGetId, getLinks: _assetGetLinks, attach: _assetAttach, @@ -292,12 +274,13 @@ Asset _assetDeserialize( stackId: reader.readStringOrNull(offsets[15]), stackPrimaryAssetId: reader.readStringOrNull(offsets[16]), thumbhash: reader.readStringOrNull(offsets[17]), - type: _AssettypeValueEnumMap[reader.readByteOrNull(offsets[18])] ?? + type: + _AssettypeValueEnumMap[reader.readByteOrNull(offsets[18])] ?? AssetType.other, updatedAt: reader.readDateTime(offsets[19]), visibility: _AssetvisibilityValueEnumMap[reader.readByteOrNull(offsets[20])] ?? - AssetVisibilityEnum.timeline, + AssetVisibilityEnum.timeline, width: reader.readIntOrNull(offsets[21]), ); return object; @@ -348,12 +331,14 @@ P _assetDeserializeProp

( return (reader.readStringOrNull(offset)) as P; case 18: return (_AssettypeValueEnumMap[reader.readByteOrNull(offset)] ?? - AssetType.other) as P; + AssetType.other) + as P; case 19: return (reader.readDateTime(offset)) as P; case 20: return (_AssetvisibilityValueEnumMap[reader.readByteOrNull(offset)] ?? - AssetVisibilityEnum.timeline) as P; + AssetVisibilityEnum.timeline) + as P; case 21: return (reader.readIntOrNull(offset)) as P; default: @@ -361,12 +346,7 @@ P _assetDeserializeProp

( } } -const _AssettypeEnumValueMap = { - 'other': 0, - 'image': 1, - 'video': 2, - 'audio': 3, -}; +const _AssettypeEnumValueMap = {'other': 0, 'image': 1, 'video': 2, 'audio': 3}; const _AssettypeValueEnumMap = { 0: AssetType.other, 1: AssetType.image, @@ -416,10 +396,14 @@ extension AssetByIndex on IsarCollection { } Future> getAllByOwnerIdChecksum( - List ownerIdValues, List checksumValues) { + List ownerIdValues, + List checksumValues, + ) { final len = ownerIdValues.length; - assert(checksumValues.length == len, - 'All index values must have the same length'); + assert( + checksumValues.length == len, + 'All index values must have the same length', + ); final values = >[]; for (var i = 0; i < len; i++) { values.add([ownerIdValues[i], checksumValues[i]]); @@ -429,10 +413,14 @@ extension AssetByIndex on IsarCollection { } List getAllByOwnerIdChecksumSync( - List ownerIdValues, List checksumValues) { + List ownerIdValues, + List checksumValues, + ) { final len = ownerIdValues.length; - assert(checksumValues.length == len, - 'All index values must have the same length'); + assert( + checksumValues.length == len, + 'All index values must have the same length', + ); final values = >[]; for (var i = 0; i < len; i++) { values.add([ownerIdValues[i], checksumValues[i]]); @@ -442,10 +430,14 @@ extension AssetByIndex on IsarCollection { } Future deleteAllByOwnerIdChecksum( - List ownerIdValues, List checksumValues) { + List ownerIdValues, + List checksumValues, + ) { final len = ownerIdValues.length; - assert(checksumValues.length == len, - 'All index values must have the same length'); + assert( + checksumValues.length == len, + 'All index values must have the same length', + ); final values = >[]; for (var i = 0; i < len; i++) { values.add([ownerIdValues[i], checksumValues[i]]); @@ -455,10 +447,14 @@ extension AssetByIndex on IsarCollection { } int deleteAllByOwnerIdChecksumSync( - List ownerIdValues, List checksumValues) { + List ownerIdValues, + List checksumValues, + ) { final len = ownerIdValues.length; - assert(checksumValues.length == len, - 'All index values must have the same length'); + assert( + checksumValues.length == len, + 'All index values must have the same length', + ); final values = >[]; for (var i = 0; i < len; i++) { values.add([ownerIdValues[i], checksumValues[i]]); @@ -479,10 +475,15 @@ extension AssetByIndex on IsarCollection { return putAllByIndex(r'ownerId_checksum', objects); } - List putAllByOwnerIdChecksumSync(List objects, - {bool saveLinks = true}) { - return putAllByIndexSync(r'ownerId_checksum', objects, - saveLinks: saveLinks); + List putAllByOwnerIdChecksumSync( + List objects, { + bool saveLinks = true, + }) { + return putAllByIndexSync( + r'ownerId_checksum', + objects, + saveLinks: saveLinks, + ); } } @@ -497,10 +498,7 @@ extension AssetQueryWhereSort on QueryBuilder { extension AssetQueryWhere on QueryBuilder { QueryBuilder idEqualTo(Id id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); + return query.addWhereClause(IdWhereClause.between(lower: id, upper: id)); }); } @@ -526,8 +524,10 @@ extension AssetQueryWhere on QueryBuilder { }); } - QueryBuilder idGreaterThan(Id id, - {bool include = false}) { + QueryBuilder idGreaterThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: id, includeLower: include), @@ -535,8 +535,10 @@ extension AssetQueryWhere on QueryBuilder { }); } - QueryBuilder idLessThan(Id id, - {bool include = false}) { + QueryBuilder idLessThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: id, includeUpper: include), @@ -551,186 +553,220 @@ extension AssetQueryWhere on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder remoteIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'remoteId', - value: [null], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'remoteId', value: [null]), + ); }); } QueryBuilder remoteIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [null], - includeLower: false, - upper: [], - )); + return query.addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [null], + includeLower: false, + upper: [], + ), + ); }); } QueryBuilder remoteIdEqualTo( - String? remoteId) { + String? remoteId, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'remoteId', - value: [remoteId], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'remoteId', value: [remoteId]), + ); }); } QueryBuilder remoteIdNotEqualTo( - String? remoteId) { + String? remoteId, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [], - upper: [remoteId], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [remoteId], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [], + upper: [remoteId], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [remoteId], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [remoteId], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [], - upper: [remoteId], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [remoteId], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [], + upper: [remoteId], + includeUpper: false, + ), + ); } }); } QueryBuilder localIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'localId', - value: [null], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'localId', value: [null]), + ); }); } QueryBuilder localIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [null], - includeLower: false, - upper: [], - )); + return query.addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [null], + includeLower: false, + upper: [], + ), + ); }); } QueryBuilder localIdEqualTo( - String? localId) { + String? localId, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'localId', - value: [localId], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'localId', value: [localId]), + ); }); } QueryBuilder localIdNotEqualTo( - String? localId) { + String? localId, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [], - upper: [localId], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [localId], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [], + upper: [localId], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [localId], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [localId], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [], - upper: [localId], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [localId], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [], + upper: [localId], + includeUpper: false, + ), + ); } }); } QueryBuilder ownerIdEqualToAnyChecksum( - int ownerId) { + int ownerId, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'ownerId_checksum', - value: [ownerId], - )); + return query.addWhereClause( + IndexWhereClause.equalTo( + indexName: r'ownerId_checksum', + value: [ownerId], + ), + ); }); } QueryBuilder ownerIdNotEqualToAnyChecksum( - int ownerId) { + int ownerId, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [], - upper: [ownerId], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [ownerId], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [], + upper: [ownerId], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [ownerId], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [ownerId], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [], - upper: [ownerId], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [ownerId], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [], + upper: [ownerId], + includeUpper: false, + ), + ); } }); } @@ -740,12 +776,14 @@ extension AssetQueryWhere on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [ownerId], - includeLower: include, - upper: [], - )); + return query.addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [ownerId], + includeLower: include, + upper: [], + ), + ); }); } @@ -754,12 +792,14 @@ extension AssetQueryWhere on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [], - upper: [ownerId], - includeUpper: include, - )); + return query.addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [], + upper: [ownerId], + includeUpper: include, + ), + ); }); } @@ -770,57 +810,71 @@ extension AssetQueryWhere on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [lowerOwnerId], - includeLower: includeLower, - upper: [upperOwnerId], - includeUpper: includeUpper, - )); + return query.addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [lowerOwnerId], + includeLower: includeLower, + upper: [upperOwnerId], + includeUpper: includeUpper, + ), + ); }); } QueryBuilder ownerIdChecksumEqualTo( - int ownerId, String checksum) { + int ownerId, + String checksum, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'ownerId_checksum', - value: [ownerId, checksum], - )); + return query.addWhereClause( + IndexWhereClause.equalTo( + indexName: r'ownerId_checksum', + value: [ownerId, checksum], + ), + ); }); } QueryBuilder - ownerIdEqualToChecksumNotEqualTo(int ownerId, String checksum) { + ownerIdEqualToChecksumNotEqualTo(int ownerId, String checksum) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [ownerId], - upper: [ownerId, checksum], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [ownerId, checksum], - includeLower: false, - upper: [ownerId], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [ownerId], + upper: [ownerId, checksum], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [ownerId, checksum], + includeLower: false, + upper: [ownerId], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [ownerId, checksum], - includeLower: false, - upper: [ownerId], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [ownerId], - upper: [ownerId, checksum], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [ownerId, checksum], + includeLower: false, + upper: [ownerId], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [ownerId], + upper: [ownerId, checksum], + includeUpper: false, + ), + ); } }); } @@ -832,11 +886,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'checksum', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'checksum', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -846,12 +902,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'checksum', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'checksum', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -861,12 +919,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'checksum', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'checksum', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -878,14 +938,16 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'checksum', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'checksum', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -894,11 +956,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'checksum', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'checksum', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -907,77 +971,82 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'checksum', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'checksum', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder checksumContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'checksum', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'checksum', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder checksumMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'checksum', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'checksum', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder checksumIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'checksum', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'checksum', value: ''), + ); }); } QueryBuilder checksumIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'checksum', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'checksum', value: ''), + ); }); } QueryBuilder durationInSecondsEqualTo( - int value) { + int value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'durationInSeconds', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'durationInSeconds', value: value), + ); }); } QueryBuilder - durationInSecondsGreaterThan( - int value, { - bool include = false, - }) { + durationInSecondsGreaterThan(int value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'durationInSeconds', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'durationInSeconds', + value: value, + ), + ); }); } @@ -986,11 +1055,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'durationInSeconds', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'durationInSeconds', + value: value, + ), + ); }); } @@ -1001,23 +1072,25 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'durationInSeconds', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'durationInSeconds', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder fileCreatedAtEqualTo( - DateTime value) { + DateTime value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'fileCreatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'fileCreatedAt', value: value), + ); }); } @@ -1026,11 +1099,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'fileCreatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'fileCreatedAt', + value: value, + ), + ); }); } @@ -1039,11 +1114,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'fileCreatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'fileCreatedAt', + value: value, + ), + ); }); } @@ -1054,23 +1131,25 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'fileCreatedAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'fileCreatedAt', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder fileModifiedAtEqualTo( - DateTime value) { + DateTime value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'fileModifiedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'fileModifiedAt', value: value), + ); }); } @@ -1079,11 +1158,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'fileModifiedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'fileModifiedAt', + value: value, + ), + ); }); } @@ -1092,11 +1173,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'fileModifiedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'fileModifiedAt', + value: value, + ), + ); }); } @@ -1107,13 +1190,15 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'fileModifiedAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'fileModifiedAt', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } @@ -1122,11 +1207,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'fileName', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'fileName', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1136,12 +1223,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'fileName', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'fileName', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1151,12 +1240,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'fileName', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'fileName', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1168,14 +1259,16 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'fileName', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'fileName', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1184,11 +1277,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'fileName', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'fileName', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1197,78 +1292,83 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'fileName', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'fileName', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder fileNameContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'fileName', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'fileName', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder fileNameMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'fileName', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'fileName', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder fileNameIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'fileName', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'fileName', value: ''), + ); }); } QueryBuilder fileNameIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'fileName', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'fileName', value: ''), + ); }); } QueryBuilder heightIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'height', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'height'), + ); }); } QueryBuilder heightIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'height', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'height'), + ); }); } QueryBuilder heightEqualTo(int? value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'height', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'height', value: value), + ); }); } @@ -1277,11 +1377,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'height', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'height', + value: value, + ), + ); }); } @@ -1290,11 +1392,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'height', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'height', + value: value, + ), + ); }); } @@ -1305,22 +1409,23 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'height', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'height', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder idEqualTo(Id value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: value), + ); }); } @@ -1329,11 +1434,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -1342,11 +1449,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -1357,70 +1466,72 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder isArchivedEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isArchived', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isArchived', value: value), + ); }); } QueryBuilder isFavoriteEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isFavorite', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isFavorite', value: value), + ); }); } QueryBuilder isOfflineEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isOffline', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isOffline', value: value), + ); }); } QueryBuilder isTrashedEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isTrashed', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isTrashed', value: value), + ); }); } QueryBuilder livePhotoVideoIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'livePhotoVideoId', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'livePhotoVideoId'), + ); }); } QueryBuilder - livePhotoVideoIdIsNotNull() { + livePhotoVideoIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'livePhotoVideoId', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'livePhotoVideoId'), + ); }); } @@ -1429,11 +1540,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'livePhotoVideoId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'livePhotoVideoId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1443,12 +1556,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'livePhotoVideoId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'livePhotoVideoId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1458,12 +1573,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'livePhotoVideoId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'livePhotoVideoId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1475,14 +1592,16 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'livePhotoVideoId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'livePhotoVideoId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1491,11 +1610,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'livePhotoVideoId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'livePhotoVideoId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1504,70 +1625,76 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'livePhotoVideoId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'livePhotoVideoId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder livePhotoVideoIdContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'livePhotoVideoId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'livePhotoVideoId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder livePhotoVideoIdMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'livePhotoVideoId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'livePhotoVideoId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder livePhotoVideoIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'livePhotoVideoId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'livePhotoVideoId', value: ''), + ); }); } QueryBuilder - livePhotoVideoIdIsNotEmpty() { + livePhotoVideoIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'livePhotoVideoId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'livePhotoVideoId', value: ''), + ); }); } QueryBuilder localIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'localId', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'localId'), + ); }); } QueryBuilder localIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'localId', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'localId'), + ); }); } @@ -1576,11 +1703,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1590,12 +1719,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1605,12 +1736,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1622,14 +1755,16 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'localId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'localId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1638,11 +1773,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1651,62 +1788,67 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder localIdContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder localIdMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'localId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'localId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder localIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'localId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'localId', value: ''), + ); }); } QueryBuilder localIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'localId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'localId', value: ''), + ); }); } QueryBuilder ownerIdEqualTo(int value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'ownerId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'ownerId', value: value), + ); }); } @@ -1715,11 +1857,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'ownerId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'ownerId', + value: value, + ), + ); }); } @@ -1728,11 +1872,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'ownerId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'ownerId', + value: value, + ), + ); }); } @@ -1743,29 +1889,31 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'ownerId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'ownerId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder remoteIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'remoteId', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'remoteId'), + ); }); } QueryBuilder remoteIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'remoteId', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'remoteId'), + ); }); } @@ -1774,11 +1922,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1788,12 +1938,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1803,12 +1955,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1820,14 +1974,16 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'remoteId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'remoteId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1836,11 +1992,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1849,63 +2007,69 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder remoteIdContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder remoteIdMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'remoteId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'remoteId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder remoteIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'remoteId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'remoteId', value: ''), + ); }); } QueryBuilder remoteIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'remoteId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'remoteId', value: ''), + ); }); } QueryBuilder stackCountEqualTo( - int value) { + int value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'stackCount', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'stackCount', value: value), + ); }); } @@ -1914,11 +2078,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'stackCount', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'stackCount', + value: value, + ), + ); }); } @@ -1927,11 +2093,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'stackCount', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'stackCount', + value: value, + ), + ); }); } @@ -1942,29 +2110,31 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'stackCount', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'stackCount', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder stackIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'stackId', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'stackId'), + ); }); } QueryBuilder stackIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'stackId', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'stackId'), + ); }); } @@ -1973,11 +2143,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'stackId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'stackId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1987,12 +2159,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'stackId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'stackId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2002,12 +2176,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'stackId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'stackId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2019,14 +2195,16 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'stackId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'stackId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2035,11 +2213,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'stackId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'stackId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2048,71 +2228,77 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'stackId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'stackId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stackIdContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'stackId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'stackId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stackIdMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'stackId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'stackId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stackIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'stackId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'stackId', value: ''), + ); }); } QueryBuilder stackIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'stackId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'stackId', value: ''), + ); }); } QueryBuilder - stackPrimaryAssetIdIsNull() { + stackPrimaryAssetIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'stackPrimaryAssetId', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'stackPrimaryAssetId'), + ); }); } QueryBuilder - stackPrimaryAssetIdIsNotNull() { + stackPrimaryAssetIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'stackPrimaryAssetId', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'stackPrimaryAssetId'), + ); }); } @@ -2121,27 +2307,31 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'stackPrimaryAssetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'stackPrimaryAssetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - stackPrimaryAssetIdGreaterThan( + stackPrimaryAssetIdGreaterThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'stackPrimaryAssetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'stackPrimaryAssetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2151,12 +2341,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'stackPrimaryAssetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'stackPrimaryAssetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2168,28 +2360,29 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'stackPrimaryAssetId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'stackPrimaryAssetId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - stackPrimaryAssetIdStartsWith( - String value, { - bool caseSensitive = true, - }) { + stackPrimaryAssetIdStartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'stackPrimaryAssetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'stackPrimaryAssetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2198,71 +2391,80 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'stackPrimaryAssetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'stackPrimaryAssetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stackPrimaryAssetIdContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'stackPrimaryAssetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'stackPrimaryAssetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stackPrimaryAssetIdMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'stackPrimaryAssetId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'stackPrimaryAssetId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - stackPrimaryAssetIdIsEmpty() { + stackPrimaryAssetIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'stackPrimaryAssetId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'stackPrimaryAssetId', value: ''), + ); }); } QueryBuilder - stackPrimaryAssetIdIsNotEmpty() { + stackPrimaryAssetIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'stackPrimaryAssetId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + property: r'stackPrimaryAssetId', + value: '', + ), + ); }); } QueryBuilder thumbhashIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'thumbhash', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'thumbhash'), + ); }); } QueryBuilder thumbhashIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'thumbhash', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'thumbhash'), + ); }); } @@ -2271,11 +2473,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'thumbhash', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2285,12 +2489,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'thumbhash', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2300,12 +2506,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'thumbhash', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2317,14 +2525,16 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'thumbhash', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'thumbhash', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2333,11 +2543,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'thumbhash', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2346,63 +2558,69 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'thumbhash', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder thumbhashContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'thumbhash', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder thumbhashMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'thumbhash', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'thumbhash', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder thumbhashIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'thumbhash', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'thumbhash', value: ''), + ); }); } QueryBuilder thumbhashIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'thumbhash', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'thumbhash', value: ''), + ); }); } QueryBuilder typeEqualTo( - AssetType value) { + AssetType value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'type', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'type', value: value), + ); }); } @@ -2411,11 +2629,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'type', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'type', + value: value, + ), + ); }); } @@ -2424,11 +2644,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'type', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'type', + value: value, + ), + ); }); } @@ -2439,23 +2661,25 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'type', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'type', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder updatedAtEqualTo( - DateTime value) { + DateTime value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'updatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'updatedAt', value: value), + ); }); } @@ -2464,11 +2688,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'updatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'updatedAt', + value: value, + ), + ); }); } @@ -2477,11 +2703,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'updatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'updatedAt', + value: value, + ), + ); }); } @@ -2492,23 +2720,25 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'updatedAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'updatedAt', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder visibilityEqualTo( - AssetVisibilityEnum value) { + AssetVisibilityEnum value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'visibility', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'visibility', value: value), + ); }); } @@ -2517,11 +2747,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'visibility', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'visibility', + value: value, + ), + ); }); } @@ -2530,11 +2762,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'visibility', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'visibility', + value: value, + ), + ); }); } @@ -2545,38 +2779,39 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'visibility', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'visibility', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder widthIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'width', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'width'), + ); }); } QueryBuilder widthIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'width', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'width'), + ); }); } QueryBuilder widthEqualTo(int? value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'width', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'width', value: value), + ); }); } @@ -2585,11 +2820,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'width', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'width', + value: value, + ), + ); }); } @@ -2598,11 +2835,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'width', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'width', + value: value, + ), + ); }); } @@ -2613,13 +2852,15 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'width', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'width', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -3173,8 +3414,9 @@ extension AssetQuerySortThenBy on QueryBuilder { } extension AssetQueryWhereDistinct on QueryBuilder { - QueryBuilder distinctByChecksum( - {bool caseSensitive = true}) { + QueryBuilder distinctByChecksum({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'checksum', caseSensitive: caseSensitive); }); @@ -3198,8 +3440,9 @@ extension AssetQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByFileName( - {bool caseSensitive = true}) { + QueryBuilder distinctByFileName({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'fileName', caseSensitive: caseSensitive); }); @@ -3235,16 +3478,20 @@ extension AssetQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByLivePhotoVideoId( - {bool caseSensitive = true}) { + QueryBuilder distinctByLivePhotoVideoId({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'livePhotoVideoId', - caseSensitive: caseSensitive); + return query.addDistinctBy( + r'livePhotoVideoId', + caseSensitive: caseSensitive, + ); }); } - QueryBuilder distinctByLocalId( - {bool caseSensitive = true}) { + QueryBuilder distinctByLocalId({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'localId', caseSensitive: caseSensitive); }); @@ -3256,8 +3503,9 @@ extension AssetQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByRemoteId( - {bool caseSensitive = true}) { + QueryBuilder distinctByRemoteId({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'remoteId', caseSensitive: caseSensitive); }); @@ -3269,23 +3517,28 @@ extension AssetQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByStackId( - {bool caseSensitive = true}) { + QueryBuilder distinctByStackId({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'stackId', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByStackPrimaryAssetId( - {bool caseSensitive = true}) { + QueryBuilder distinctByStackPrimaryAssetId({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'stackPrimaryAssetId', - caseSensitive: caseSensitive); + return query.addDistinctBy( + r'stackPrimaryAssetId', + caseSensitive: caseSensitive, + ); }); } - QueryBuilder distinctByThumbhash( - {bool caseSensitive = true}) { + QueryBuilder distinctByThumbhash({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'thumbhash', caseSensitive: caseSensitive); }); @@ -3444,7 +3697,7 @@ extension AssetQueryProperty on QueryBuilder { } QueryBuilder - visibilityProperty() { + visibilityProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'visibility'); }); diff --git a/mobile/lib/entities/backup_album.entity.dart b/mobile/lib/entities/backup_album.entity.dart index 1e96c0452e..ad2a5d6718 100644 --- a/mobile/lib/entities/backup_album.entity.dart +++ b/mobile/lib/entities/backup_album.entity.dart @@ -14,21 +14,9 @@ class BackupAlbum { Id get isarId => fastHash(id); - BackupAlbum copyWith({ - String? id, - DateTime? lastBackup, - BackupSelection? selection, - }) { - return BackupAlbum( - id ?? this.id, - lastBackup ?? this.lastBackup, - selection ?? this.selection, - ); + BackupAlbum copyWith({String? id, DateTime? lastBackup, BackupSelection? selection}) { + return BackupAlbum(id ?? this.id, lastBackup ?? this.lastBackup, selection ?? this.selection); } } -enum BackupSelection { - none, - select, - exclude; -} +enum BackupSelection { none, select, exclude } diff --git a/mobile/lib/entities/backup_album.entity.g.dart b/mobile/lib/entities/backup_album.entity.g.dart index 23d00e43ca..ed98503119 100644 --- a/mobile/lib/entities/backup_album.entity.g.dart +++ b/mobile/lib/entities/backup_album.entity.g.dart @@ -17,11 +17,7 @@ const BackupAlbumSchema = CollectionSchema( name: r'BackupAlbum', id: 8308487201128361847, properties: { - r'id': PropertySchema( - id: 0, - name: r'id', - type: IsarType.string, - ), + r'id': PropertySchema(id: 0, name: r'id', type: IsarType.string), r'lastBackup': PropertySchema( id: 1, name: r'lastBackup', @@ -32,8 +28,9 @@ const BackupAlbumSchema = CollectionSchema( name: r'selection', type: IsarType.byte, enumMap: _BackupAlbumselectionEnumValueMap, - ) + ), }, + estimateSize: _backupAlbumEstimateSize, serialize: _backupAlbumSerialize, deserialize: _backupAlbumDeserialize, @@ -42,6 +39,7 @@ const BackupAlbumSchema = CollectionSchema( indexes: {}, links: {}, embeddedSchemas: {}, + getId: _backupAlbumGetId, getLinks: _backupAlbumGetLinks, attach: _backupAlbumAttach, @@ -96,9 +94,11 @@ P _backupAlbumDeserializeProp

( case 1: return (reader.readDateTime(offset)) as P; case 2: - return (_BackupAlbumselectionValueEnumMap[ - reader.readByteOrNull(offset)] ?? - BackupSelection.none) as P; + return (_BackupAlbumselectionValueEnumMap[reader.readByteOrNull( + offset, + )] ?? + BackupSelection.none) + as P; default: throw IsarError('Unknown property with id $propertyId'); } @@ -124,7 +124,10 @@ List> _backupAlbumGetLinks(BackupAlbum object) { } void _backupAlbumAttach( - IsarCollection col, Id id, BackupAlbum object) {} + IsarCollection col, + Id id, + BackupAlbum object, +) {} extension BackupAlbumQueryWhereSort on QueryBuilder { @@ -138,17 +141,18 @@ extension BackupAlbumQueryWhereSort extension BackupAlbumQueryWhere on QueryBuilder { QueryBuilder isarIdEqualTo( - Id isarId) { + Id isarId, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: isarId, - upper: isarId, - )); + return query.addWhereClause( + IdWhereClause.between(lower: isarId, upper: isarId), + ); }); } QueryBuilder isarIdNotEqualTo( - Id isarId) { + Id isarId, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query @@ -171,8 +175,9 @@ extension BackupAlbumQueryWhere } QueryBuilder isarIdGreaterThan( - Id isarId, - {bool include = false}) { + Id isarId, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: isarId, includeLower: include), @@ -181,8 +186,9 @@ extension BackupAlbumQueryWhere } QueryBuilder isarIdLessThan( - Id isarId, - {bool include = false}) { + Id isarId, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: isarId, includeUpper: include), @@ -197,12 +203,14 @@ extension BackupAlbumQueryWhere bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerIsarId, - includeLower: includeLower, - upper: upperIsarId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerIsarId, + includeLower: includeLower, + upper: upperIsarId, + includeUpper: includeUpper, + ), + ); }); } } @@ -214,11 +222,13 @@ extension BackupAlbumQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -228,12 +238,14 @@ extension BackupAlbumQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -243,12 +255,14 @@ extension BackupAlbumQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -260,14 +274,16 @@ extension BackupAlbumQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -276,11 +292,13 @@ extension BackupAlbumQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -289,77 +307,82 @@ extension BackupAlbumQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder idContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder idMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'id', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'id', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder idIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: ''), + ); }); } QueryBuilder idIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'id', value: ''), + ); }); } QueryBuilder isarIdEqualTo( - Id value) { + Id value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isarId', value: value), + ); }); } QueryBuilder - isarIdGreaterThan( - Id value, { - bool include = false, - }) { + isarIdGreaterThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } @@ -368,11 +391,13 @@ extension BackupAlbumQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } @@ -383,125 +408,125 @@ extension BackupAlbumQueryFilter bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'isarId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'isarId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - lastBackupEqualTo(DateTime value) { + lastBackupEqualTo(DateTime value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'lastBackup', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'lastBackup', value: value), + ); }); } QueryBuilder - lastBackupGreaterThan( - DateTime value, { - bool include = false, - }) { + lastBackupGreaterThan(DateTime value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'lastBackup', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'lastBackup', + value: value, + ), + ); }); } QueryBuilder - lastBackupLessThan( - DateTime value, { - bool include = false, - }) { + lastBackupLessThan(DateTime value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'lastBackup', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'lastBackup', + value: value, + ), + ); }); } QueryBuilder - lastBackupBetween( + lastBackupBetween( DateTime lower, DateTime upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'lastBackup', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'lastBackup', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - selectionEqualTo(BackupSelection value) { + selectionEqualTo(BackupSelection value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'selection', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'selection', value: value), + ); }); } QueryBuilder - selectionGreaterThan( - BackupSelection value, { - bool include = false, - }) { + selectionGreaterThan(BackupSelection value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'selection', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'selection', + value: value, + ), + ); }); } QueryBuilder - selectionLessThan( - BackupSelection value, { - bool include = false, - }) { + selectionLessThan(BackupSelection value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'selection', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'selection', + value: value, + ), + ); }); } QueryBuilder - selectionBetween( + selectionBetween( BackupSelection lower, BackupSelection upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'selection', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'selection', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -604,8 +629,9 @@ extension BackupAlbumQuerySortThenBy extension BackupAlbumQueryWhereDistinct on QueryBuilder { - QueryBuilder distinctById( - {bool caseSensitive = true}) { + QueryBuilder distinctById({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'id', caseSensitive: caseSensitive); }); @@ -645,7 +671,7 @@ extension BackupAlbumQueryProperty } QueryBuilder - selectionProperty() { + selectionProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'selection'); }); diff --git a/mobile/lib/entities/duplicated_asset.entity.g.dart b/mobile/lib/entities/duplicated_asset.entity.g.dart index 8965d47c97..6cf08ad9cc 100644 --- a/mobile/lib/entities/duplicated_asset.entity.g.dart +++ b/mobile/lib/entities/duplicated_asset.entity.g.dart @@ -17,12 +17,9 @@ const DuplicatedAssetSchema = CollectionSchema( name: r'DuplicatedAsset', id: -2679334728174694496, properties: { - r'id': PropertySchema( - id: 0, - name: r'id', - type: IsarType.string, - ) + r'id': PropertySchema(id: 0, name: r'id', type: IsarType.string), }, + estimateSize: _duplicatedAssetEstimateSize, serialize: _duplicatedAssetSerialize, deserialize: _duplicatedAssetDeserialize, @@ -31,6 +28,7 @@ const DuplicatedAssetSchema = CollectionSchema( indexes: {}, links: {}, embeddedSchemas: {}, + getId: _duplicatedAssetGetId, getLinks: _duplicatedAssetGetLinks, attach: _duplicatedAssetAttach, @@ -62,9 +60,7 @@ DuplicatedAsset _duplicatedAssetDeserialize( List offsets, Map> allOffsets, ) { - final object = DuplicatedAsset( - reader.readString(offsets[0]), - ); + final object = DuplicatedAsset(reader.readString(offsets[0])); return object; } @@ -91,7 +87,10 @@ List> _duplicatedAssetGetLinks(DuplicatedAsset object) { } void _duplicatedAssetAttach( - IsarCollection col, Id id, DuplicatedAsset object) {} + IsarCollection col, + Id id, + DuplicatedAsset object, +) {} extension DuplicatedAssetQueryWhereSort on QueryBuilder { @@ -105,17 +104,16 @@ extension DuplicatedAssetQueryWhereSort extension DuplicatedAssetQueryWhere on QueryBuilder { QueryBuilder - isarIdEqualTo(Id isarId) { + isarIdEqualTo(Id isarId) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: isarId, - upper: isarId, - )); + return query.addWhereClause( + IdWhereClause.between(lower: isarId, upper: isarId), + ); }); } QueryBuilder - isarIdNotEqualTo(Id isarId) { + isarIdNotEqualTo(Id isarId) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query @@ -138,7 +136,7 @@ extension DuplicatedAssetQueryWhere } QueryBuilder - isarIdGreaterThan(Id isarId, {bool include = false}) { + isarIdGreaterThan(Id isarId, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: isarId, includeLower: include), @@ -147,7 +145,7 @@ extension DuplicatedAssetQueryWhere } QueryBuilder - isarIdLessThan(Id isarId, {bool include = false}) { + isarIdLessThan(Id isarId, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: isarId, includeUpper: include), @@ -156,19 +154,21 @@ extension DuplicatedAssetQueryWhere } QueryBuilder - isarIdBetween( + isarIdBetween( Id lowerIsarId, Id upperIsarId, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerIsarId, - includeLower: includeLower, - upper: upperIsarId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerIsarId, + includeLower: includeLower, + upper: upperIsarId, + includeUpper: includeUpper, + ), + ); }); } } @@ -176,53 +176,52 @@ extension DuplicatedAssetQueryWhere extension DuplicatedAssetQueryFilter on QueryBuilder { QueryBuilder - idEqualTo( - String value, { - bool caseSensitive = true, - }) { + idEqualTo(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idGreaterThan( + idGreaterThan( String value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idLessThan( - String value, { - bool include = false, - bool caseSensitive = true, - }) { + idLessThan(String value, {bool include = false, bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idBetween( + idBetween( String lower, String upper, { bool includeLower = true, @@ -230,140 +229,141 @@ extension DuplicatedAssetQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idStartsWith( - String value, { - bool caseSensitive = true, - }) { + idStartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idEndsWith( - String value, { - bool caseSensitive = true, - }) { + idEndsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idContains(String value, {bool caseSensitive = true}) { + idContains(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idMatches(String pattern, {bool caseSensitive = true}) { + idMatches(String pattern, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'id', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'id', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idIsEmpty() { + idIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: ''), + ); }); } QueryBuilder - idIsNotEmpty() { + idIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'id', value: ''), + ); }); } QueryBuilder - isarIdEqualTo(Id value) { + isarIdEqualTo(Id value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isarId', value: value), + ); }); } QueryBuilder - isarIdGreaterThan( - Id value, { - bool include = false, - }) { + isarIdGreaterThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } QueryBuilder - isarIdLessThan( - Id value, { - bool include = false, - }) { + isarIdLessThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } QueryBuilder - isarIdBetween( + isarIdBetween( Id lower, Id upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'isarId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'isarId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -410,7 +410,7 @@ extension DuplicatedAssetQuerySortThenBy } QueryBuilder - thenByIsarIdDesc() { + thenByIsarIdDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'isarId', Sort.desc); }); @@ -419,8 +419,9 @@ extension DuplicatedAssetQuerySortThenBy extension DuplicatedAssetQueryWhereDistinct on QueryBuilder { - QueryBuilder distinctById( - {bool caseSensitive = true}) { + QueryBuilder distinctById({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'id', caseSensitive: caseSensitive); }); diff --git a/mobile/lib/entities/etag.entity.g.dart b/mobile/lib/entities/etag.entity.g.dart index afabca4aea..b1abba6bb7 100644 --- a/mobile/lib/entities/etag.entity.g.dart +++ b/mobile/lib/entities/etag.entity.g.dart @@ -22,17 +22,10 @@ const ETagSchema = CollectionSchema( name: r'assetCount', type: IsarType.long, ), - r'id': PropertySchema( - id: 1, - name: r'id', - type: IsarType.string, - ), - r'time': PropertySchema( - id: 2, - name: r'time', - type: IsarType.dateTime, - ) + r'id': PropertySchema(id: 1, name: r'id', type: IsarType.string), + r'time': PropertySchema(id: 2, name: r'time', type: IsarType.dateTime), }, + estimateSize: _eTagEstimateSize, serialize: _eTagSerialize, deserialize: _eTagDeserialize, @@ -49,12 +42,13 @@ const ETagSchema = CollectionSchema( name: r'id', type: IndexType.hash, caseSensitive: true, - ) + ), ], - ) + ), }, links: {}, embeddedSchemas: {}, + getId: _eTagGetId, getLinks: _eTagGetLinks, attach: _eTagAttach, @@ -189,10 +183,9 @@ extension ETagQueryWhereSort on QueryBuilder { extension ETagQueryWhere on QueryBuilder { QueryBuilder isarIdEqualTo(Id isarId) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: isarId, - upper: isarId, - )); + return query.addWhereClause( + IdWhereClause.between(lower: isarId, upper: isarId), + ); }); } @@ -218,8 +211,10 @@ extension ETagQueryWhere on QueryBuilder { }); } - QueryBuilder isarIdGreaterThan(Id isarId, - {bool include = false}) { + QueryBuilder isarIdGreaterThan( + Id isarId, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: isarId, includeLower: include), @@ -227,8 +222,10 @@ extension ETagQueryWhere on QueryBuilder { }); } - QueryBuilder isarIdLessThan(Id isarId, - {bool include = false}) { + QueryBuilder isarIdLessThan( + Id isarId, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: isarId, includeUpper: include), @@ -243,21 +240,22 @@ extension ETagQueryWhere on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerIsarId, - includeLower: includeLower, - upper: upperIsarId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerIsarId, + includeLower: includeLower, + upper: upperIsarId, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder idEqualTo(String id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'id', - value: [id], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'id', value: [id]), + ); }); } @@ -265,32 +263,40 @@ extension ETagQueryWhere on QueryBuilder { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [], - upper: [id], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [id], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [], + upper: [id], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [id], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [id], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [], - upper: [id], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [id], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [], + upper: [id], + includeUpper: false, + ), + ); } }); } @@ -299,27 +305,27 @@ extension ETagQueryWhere on QueryBuilder { extension ETagQueryFilter on QueryBuilder { QueryBuilder assetCountIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'assetCount', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'assetCount'), + ); }); } QueryBuilder assetCountIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'assetCount', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'assetCount'), + ); }); } QueryBuilder assetCountEqualTo( - int? value) { + int? value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'assetCount', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'assetCount', value: value), + ); }); } @@ -328,11 +334,13 @@ extension ETagQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'assetCount', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'assetCount', + value: value, + ), + ); }); } @@ -341,11 +349,13 @@ extension ETagQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'assetCount', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'assetCount', + value: value, + ), + ); }); } @@ -356,13 +366,15 @@ extension ETagQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'assetCount', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'assetCount', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } @@ -371,11 +383,13 @@ extension ETagQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -385,12 +399,14 @@ extension ETagQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -400,12 +416,14 @@ extension ETagQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -417,14 +435,16 @@ extension ETagQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -433,11 +453,13 @@ extension ETagQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -446,60 +468,67 @@ extension ETagQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder idContains(String value, - {bool caseSensitive = true}) { + QueryBuilder idContains( + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder idMatches(String pattern, - {bool caseSensitive = true}) { + QueryBuilder idMatches( + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'id', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'id', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder idIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: ''), + ); }); } QueryBuilder idIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'id', value: ''), + ); }); } QueryBuilder isarIdEqualTo(Id value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isarId', value: value), + ); }); } @@ -508,11 +537,13 @@ extension ETagQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } @@ -521,11 +552,13 @@ extension ETagQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } @@ -536,38 +569,39 @@ extension ETagQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'isarId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'isarId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder timeIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'time', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'time'), + ); }); } QueryBuilder timeIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'time', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'time'), + ); }); } QueryBuilder timeEqualTo(DateTime? value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'time', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'time', value: value), + ); }); } @@ -576,11 +610,13 @@ extension ETagQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'time', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'time', + value: value, + ), + ); }); } @@ -589,11 +625,13 @@ extension ETagQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'time', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'time', + value: value, + ), + ); }); } @@ -604,13 +642,15 @@ extension ETagQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'time', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'time', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -714,8 +754,9 @@ extension ETagQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctById( - {bool caseSensitive = true}) { + QueryBuilder distinctById({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'id', caseSensitive: caseSensitive); }); diff --git a/mobile/lib/entities/ios_device_asset.entity.g.dart b/mobile/lib/entities/ios_device_asset.entity.g.dart index ffed338c91..8d8fec945b 100644 --- a/mobile/lib/entities/ios_device_asset.entity.g.dart +++ b/mobile/lib/entities/ios_device_asset.entity.g.dart @@ -17,17 +17,10 @@ const IOSDeviceAssetSchema = CollectionSchema( name: r'IOSDeviceAsset', id: -1671546753821948030, properties: { - r'hash': PropertySchema( - id: 0, - name: r'hash', - type: IsarType.byteList, - ), - r'id': PropertySchema( - id: 1, - name: r'id', - type: IsarType.string, - ) + r'hash': PropertySchema(id: 0, name: r'hash', type: IsarType.byteList), + r'id': PropertySchema(id: 1, name: r'id', type: IsarType.string), }, + estimateSize: _iOSDeviceAssetEstimateSize, serialize: _iOSDeviceAssetSerialize, deserialize: _iOSDeviceAssetDeserialize, @@ -44,7 +37,7 @@ const IOSDeviceAssetSchema = CollectionSchema( name: r'id', type: IndexType.hash, caseSensitive: true, - ) + ), ], ), r'hash': IndexSchema( @@ -57,12 +50,13 @@ const IOSDeviceAssetSchema = CollectionSchema( name: r'hash', type: IndexType.hash, caseSensitive: false, - ) + ), ], - ) + ), }, links: {}, embeddedSchemas: {}, + getId: _iOSDeviceAssetGetId, getLinks: _iOSDeviceAssetGetLinks, attach: _iOSDeviceAssetAttach, @@ -128,7 +122,10 @@ List> _iOSDeviceAssetGetLinks(IOSDeviceAsset object) { } void _iOSDeviceAssetAttach( - IsarCollection col, Id id, IOSDeviceAsset object) {} + IsarCollection col, + Id id, + IOSDeviceAsset object, +) {} extension IOSDeviceAssetByIndex on IsarCollection { Future getById(String id) { @@ -179,8 +176,10 @@ extension IOSDeviceAssetByIndex on IsarCollection { return putAllByIndex(r'id', objects); } - List putAllByIdSync(List objects, - {bool saveLinks = true}) { + List putAllByIdSync( + List objects, { + bool saveLinks = true, + }) { return putAllByIndexSync(r'id', objects, saveLinks: saveLinks); } } @@ -197,17 +196,17 @@ extension IOSDeviceAssetQueryWhereSort extension IOSDeviceAssetQueryWhere on QueryBuilder { QueryBuilder isarIdEqualTo( - Id isarId) { + Id isarId, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: isarId, - upper: isarId, - )); + return query.addWhereClause( + IdWhereClause.between(lower: isarId, upper: isarId), + ); }); } QueryBuilder - isarIdNotEqualTo(Id isarId) { + isarIdNotEqualTo(Id isarId) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query @@ -230,7 +229,7 @@ extension IOSDeviceAssetQueryWhere } QueryBuilder - isarIdGreaterThan(Id isarId, {bool include = false}) { + isarIdGreaterThan(Id isarId, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: isarId, includeLower: include), @@ -239,7 +238,7 @@ extension IOSDeviceAssetQueryWhere } QueryBuilder - isarIdLessThan(Id isarId, {bool include = false}) { + isarIdLessThan(Id isarId, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: isarId, includeUpper: include), @@ -254,101 +253,120 @@ extension IOSDeviceAssetQueryWhere bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerIsarId, - includeLower: includeLower, - upper: upperIsarId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerIsarId, + includeLower: includeLower, + upper: upperIsarId, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder idEqualTo( - String id) { + String id, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'id', - value: [id], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'id', value: [id]), + ); }); } QueryBuilder idNotEqualTo( - String id) { + String id, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [], - upper: [id], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [id], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [], + upper: [id], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [id], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [id], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [], - upper: [id], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [id], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [], + upper: [id], + includeUpper: false, + ), + ); } }); } QueryBuilder hashEqualTo( - List hash) { + List hash, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'hash', - value: [hash], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'hash', value: [hash]), + ); }); } QueryBuilder - hashNotEqualTo(List hash) { + hashNotEqualTo(List hash) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [], - upper: [hash], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [hash], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [], + upper: [hash], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [hash], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [hash], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [], - upper: [hash], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [hash], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [], + upper: [hash], + includeUpper: false, + ), + ); } }); } @@ -357,134 +375,97 @@ extension IOSDeviceAssetQueryWhere extension IOSDeviceAssetQueryFilter on QueryBuilder { QueryBuilder - hashElementEqualTo(int value) { + hashElementEqualTo(int value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'hash', value: value), + ); }); } QueryBuilder - hashElementGreaterThan( - int value, { - bool include = false, - }) { + hashElementGreaterThan(int value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'hash', + value: value, + ), + ); }); } QueryBuilder - hashElementLessThan( - int value, { - bool include = false, - }) { + hashElementLessThan(int value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'hash', + value: value, + ), + ); }); } QueryBuilder - hashElementBetween( + hashElementBetween( int lower, int upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'hash', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); - }); - } - - QueryBuilder - hashLengthEqualTo(int length) { - return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - length, - true, - length, - true, + return query.addFilterCondition( + FilterCondition.between( + property: r'hash', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), ); }); } QueryBuilder - hashIsEmpty() { + hashLengthEqualTo(int length) { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - true, - 0, - true, - ); + return query.listLength(r'hash', length, true, length, true); }); } QueryBuilder - hashIsNotEmpty() { + hashIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - false, - 999999, - true, - ); + return query.listLength(r'hash', 0, true, 0, true); }); } QueryBuilder - hashLengthLessThan( - int length, { - bool include = false, - }) { + hashIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - true, - length, - include, - ); + return query.listLength(r'hash', 0, false, 999999, true); }); } QueryBuilder - hashLengthGreaterThan( - int length, { - bool include = false, - }) { + hashLengthLessThan(int length, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - length, - include, - 999999, - true, - ); + return query.listLength(r'hash', 0, true, length, include); }); } QueryBuilder - hashLengthBetween( + hashLengthGreaterThan(int length, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.listLength(r'hash', length, include, 999999, true); + }); + } + + QueryBuilder + hashLengthBetween( int lower, int upper, { bool includeLower = true, @@ -506,43 +487,45 @@ extension IOSDeviceAssetQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idGreaterThan( + idGreaterThan( String value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idLessThan( - String value, { - bool include = false, - bool caseSensitive = true, - }) { + idLessThan(String value, {bool include = false, bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -554,141 +537,143 @@ extension IOSDeviceAssetQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idStartsWith( - String value, { - bool caseSensitive = true, - }) { + idStartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idEndsWith( - String value, { - bool caseSensitive = true, - }) { + idEndsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idContains(String value, {bool caseSensitive = true}) { + idContains(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder idMatches( - String pattern, - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'id', - wildcard: pattern, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - idIsEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: '', - )); - }); - } - - QueryBuilder - idIsNotEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'id', - value: '', - )); - }); - } - - QueryBuilder - isarIdEqualTo(Id value) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isarId', - value: value, - )); - }); - } - - QueryBuilder - isarIdGreaterThan( - Id value, { - bool include = false, + String pattern, { + bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'id', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - isarIdLessThan( - Id value, { - bool include = false, - }) { + idIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: ''), + ); }); } QueryBuilder - isarIdBetween( + idIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'id', value: ''), + ); + }); + } + + QueryBuilder + isarIdEqualTo(Id value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isarId', value: value), + ); + }); + } + + QueryBuilder + isarIdGreaterThan(Id value, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'isarId', + value: value, + ), + ); + }); + } + + QueryBuilder + isarIdLessThan(Id value, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'isarId', + value: value, + ), + ); + }); + } + + QueryBuilder + isarIdBetween( Id lower, Id upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'isarId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'isarId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -735,7 +720,7 @@ extension IOSDeviceAssetQuerySortThenBy } QueryBuilder - thenByIsarIdDesc() { + thenByIsarIdDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'isarId', Sort.desc); }); @@ -750,8 +735,9 @@ extension IOSDeviceAssetQueryWhereDistinct }); } - QueryBuilder distinctById( - {bool caseSensitive = true}) { + QueryBuilder distinctById({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'id', caseSensitive: caseSensitive); }); diff --git a/mobile/lib/extensions/asset_extensions.dart b/mobile/lib/extensions/asset_extensions.dart index a5fa50983a..22d5d5030a 100644 --- a/mobile/lib/extensions/asset_extensions.dart +++ b/mobile/lib/extensions/asset_extensions.dart @@ -15,16 +15,10 @@ extension TZExtension on Asset { final location = getLocation(exifInfo!.timeZone!); dt = TZDateTime.from(dt, location); } on LocationNotFoundException { - RegExp re = RegExp( - r'^utc(?:([+-]\d{1,2})(?::(\d{2}))?)?$', - caseSensitive: false, - ); + RegExp re = RegExp(r'^utc(?:([+-]\d{1,2})(?::(\d{2}))?)?$', caseSensitive: false); final m = re.firstMatch(exifInfo!.timeZone!); if (m != null) { - final duration = Duration( - hours: int.parse(m.group(1) ?? '0'), - minutes: int.parse(m.group(2) ?? '0'), - ); + final duration = Duration(hours: int.parse(m.group(1) ?? '0'), minutes: int.parse(m.group(2) ?? '0')); dt = dt.add(duration); return (dt, duration); } diff --git a/mobile/lib/extensions/collection_extensions.dart b/mobile/lib/extensions/collection_extensions.dart index 95d2f74df8..541db7ccaf 100644 --- a/mobile/lib/extensions/collection_extensions.dart +++ b/mobile/lib/extensions/collection_extensions.dart @@ -6,10 +6,7 @@ import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/utils/hash.dart'; extension ListExtension on List { - List uniqueConsecutive({ - int Function(E a, E b)? compare, - void Function(E a, E b)? onDuplicate, - }) { + List uniqueConsecutive({int Function(E a, E b)? compare, void Function(E a, E b)? onDuplicate}) { compare ??= (E a, E b) => a == b ? 0 : 1; int i = 1, j = 1; for (; i < length; i++) { @@ -45,9 +42,7 @@ extension IntListExtension on Iterable { extension AssetListExtension on Iterable { /// Returns the assets that are already available in the Immich server - Iterable remoteOnly({ - void Function()? errorCallback, - }) { + Iterable remoteOnly({void Function()? errorCallback}) { final bool onlyRemote = every((e) => e.isRemote); if (!onlyRemote) { if (errorCallback != null) errorCallback(); @@ -58,10 +53,7 @@ extension AssetListExtension on Iterable { /// Returns the assets that are owned by the user passed to the [owner] param /// If [owner] is null, an empty list is returned - Iterable ownedOnly( - UserDto? owner, { - void Function()? errorCallback, - }) { + Iterable ownedOnly(UserDto? owner, {void Function()? errorCallback}) { if (owner == null) return []; final isarUserId = fastHash(owner.id); final bool onlyOwned = every((e) => e.ownerId == isarUserId); diff --git a/mobile/lib/extensions/datetime_extensions.dart b/mobile/lib/extensions/datetime_extensions.dart index 7e54980270..0bc95565a6 100644 --- a/mobile/lib/extensions/datetime_extensions.dart +++ b/mobile/lib/extensions/datetime_extensions.dart @@ -47,11 +47,7 @@ extension DateRangeFormatting on DateTime { /// - Date range of this year: "Mar 23-May 31" /// - Date range of other year: "Aug 28 - Sep 30, 2023" /// - Date range over multiple years: "Apr 17, 2021 - Apr 9, 2022" - static String formatDateRange( - DateTime startDate, - DateTime endDate, - Locale? locale, - ) { + static String formatDateRange(DateTime startDate, DateTime endDate, Locale? locale) { final now = DateTime.now(); final currentYear = now.year; final localeString = locale?.toString() ?? 'en_US'; diff --git a/mobile/lib/extensions/maplibrecontroller_extensions.dart b/mobile/lib/extensions/maplibrecontroller_extensions.dart index 42d5e2c1d0..e1f32a4d8c 100644 --- a/mobile/lib/extensions/maplibrecontroller_extensions.dart +++ b/mobile/lib/extensions/maplibrecontroller_extensions.dart @@ -13,9 +13,7 @@ extension MapMarkers on MapLibreMapController { Future addGeoJSONSourceForMarkers(List markers) async { return addSource( MapUtils.defaultSourceId, - GeojsonSourceProperties( - data: MapUtils.generateGeoJsonForMarkers(markers.toList()), - ), + GeojsonSourceProperties(data: MapUtils.generateGeoJsonForMarkers(markers.toList())), ); } @@ -73,23 +71,13 @@ extension MapMarkers on MapLibreMapController { try { final ByteData bytes = await rootBundle.load("assets/location-pin.png"); await addImage("mapMarker", bytes.buffer.asUint8List()); - return addSymbol( - SymbolOptions( - geometry: centre, - iconImage: "mapMarker", - iconSize: 0.15, - iconAnchor: "bottom", - ), - ); + return addSymbol(SymbolOptions(geometry: centre, iconImage: "mapMarker", iconSize: 0.15, iconAnchor: "bottom")); } finally { // no-op } } - Future getBoundsFromPoint( - Point point, - double distance, - ) async { + Future getBoundsFromPoint(Point point, double distance) async { final southWestPx = Point(point.x - distance, point.y + distance); final northEastPx = Point(point.x + distance, point.y - distance); diff --git a/mobile/lib/extensions/scroll_extensions.dart b/mobile/lib/extensions/scroll_extensions.dart index 2752d0b77a..169032ff5d 100644 --- a/mobile/lib/extensions/scroll_extensions.dart +++ b/mobile/lib/extensions/scroll_extensions.dart @@ -10,11 +10,7 @@ class FastScrollPhysics extends ScrollPhysics { } @override - SpringDescription get spring => const SpringDescription( - mass: 1, - stiffness: 402.49984375, - damping: 40, - ); + SpringDescription get spring => const SpringDescription(mass: 1, stiffness: 402.49984375, damping: 40); } class FastClampingScrollPhysics extends ClampingScrollPhysics { @@ -27,12 +23,12 @@ class FastClampingScrollPhysics extends ClampingScrollPhysics { @override SpringDescription get spring => const SpringDescription( - // When swiping between videos on Android, the placeholder of the first opened video - // can briefly be seen and cause a flicker effect if the video begins to initialize - // before the animation finishes - probably a bug in PhotoViewGallery's animation handling - // Making the animation faster is not just stylistic, but also helps to avoid this flicker - mass: 1, - stiffness: 1601.2499609375, - damping: 80, - ); + // When swiping between videos on Android, the placeholder of the first opened video + // can briefly be seen and cause a flicker effect if the video begins to initialize + // before the animation finishes - probably a bug in PhotoViewGallery's animation handling + // Making the animation faster is not just stylistic, but also helps to avoid this flicker + mass: 1, + stiffness: 1601.2499609375, + damping: 80, + ); } diff --git a/mobile/lib/extensions/string_extensions.dart b/mobile/lib/extensions/string_extensions.dart index 65660c04ef..6cd6e1e4b4 100644 --- a/mobile/lib/extensions/string_extensions.dart +++ b/mobile/lib/extensions/string_extensions.dart @@ -1,10 +1,6 @@ extension StringExtension on String { String capitalize() { - return split(" ") - .map( - (str) => str.isEmpty ? str : str[0].toUpperCase() + str.substring(1), - ) - .join(" "); + return split(" ").map((str) => str.isEmpty ? str : str[0].toUpperCase() + str.substring(1)).join(" "); } } diff --git a/mobile/lib/extensions/theme_extensions.dart b/mobile/lib/extensions/theme_extensions.dart index 6da8ac1fe6..332dc58fc0 100644 --- a/mobile/lib/extensions/theme_extensions.dart +++ b/mobile/lib/extensions/theme_extensions.dart @@ -7,16 +7,10 @@ extension ImmichColorSchemeExtensions on ColorScheme { extension ColorExtensions on Color { Color lighten({double amount = 0.1}) { - return Color.alphaBlend( - Colors.white.withValues(alpha: amount), - this, - ); + return Color.alphaBlend(Colors.white.withValues(alpha: amount), this); } Color darken({double amount = 0.1}) { - return Color.alphaBlend( - Colors.black.withValues(alpha: amount), - this, - ); + return Color.alphaBlend(Colors.black.withValues(alpha: amount), this); } } diff --git a/mobile/lib/extensions/translate_extensions.dart b/mobile/lib/extensions/translate_extensions.dart index d8a2810e79..cfd8c8cd1f 100644 --- a/mobile/lib/extensions/translate_extensions.dart +++ b/mobile/lib/extensions/translate_extensions.dart @@ -29,11 +29,7 @@ extension TextTranslateExtension on Text { } } -String _translateHelper( - BuildContext? context, - String key, [ - Map? args, -]) { +String _translateHelper(BuildContext? context, String key, [Map? args]) { if (key.isEmpty) { return ''; } diff --git a/mobile/lib/infrastructure/entities/asset_face.entity.drift.dart b/mobile/lib/infrastructure/entities/asset_face.entity.drift.dart index 140af60de1..092fcc5859 100644 --- a/mobile/lib/infrastructure/entities/asset_face.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/asset_face.entity.drift.dart @@ -11,88 +11,108 @@ import 'package:drift/internal/modular.dart' as i4; import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart' as i5; -typedef $$AssetFaceEntityTableCreateCompanionBuilder - = i1.AssetFaceEntityCompanion Function({ - required String id, - required String assetId, - i0.Value personId, - required int imageWidth, - required int imageHeight, - required int boundingBoxX1, - required int boundingBoxY1, - required int boundingBoxX2, - required int boundingBoxY2, - required String sourceType, -}); -typedef $$AssetFaceEntityTableUpdateCompanionBuilder - = i1.AssetFaceEntityCompanion Function({ - i0.Value id, - i0.Value assetId, - i0.Value personId, - i0.Value imageWidth, - i0.Value imageHeight, - i0.Value boundingBoxX1, - i0.Value boundingBoxY1, - i0.Value boundingBoxX2, - i0.Value boundingBoxY2, - i0.Value sourceType, -}); +typedef $$AssetFaceEntityTableCreateCompanionBuilder = + i1.AssetFaceEntityCompanion Function({ + required String id, + required String assetId, + i0.Value personId, + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }); +typedef $$AssetFaceEntityTableUpdateCompanionBuilder = + i1.AssetFaceEntityCompanion Function({ + i0.Value id, + i0.Value assetId, + i0.Value personId, + i0.Value imageWidth, + i0.Value imageHeight, + i0.Value boundingBoxX1, + i0.Value boundingBoxY1, + i0.Value boundingBoxX2, + i0.Value boundingBoxY2, + i0.Value sourceType, + }); -final class $$AssetFaceEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, i1.$AssetFaceEntityTable, i1.AssetFaceEntityData> { +final class $$AssetFaceEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$AssetFaceEntityTable, + i1.AssetFaceEntityData + > { $$AssetFaceEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i3.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('remote_asset_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet('asset_face_entity') .assetId, - i4.ReadDatabaseContainer(db) - .resultSet('remote_asset_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('remote_asset_entity').id, + ), + ); i3.$$RemoteAssetEntityTableProcessedTableManager get assetId { final $_column = $_itemColumn('asset_id')!; final manager = i3 .$$RemoteAssetEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('remote_asset_entity')) + ).resultSet('remote_asset_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_assetIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } static i5.$PersonEntityTable _personIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('person_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet('asset_face_entity') .personId, - i4.ReadDatabaseContainer(db) - .resultSet('person_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('person_entity').id, + ), + ); i5.$$PersonEntityTableProcessedTableManager? get personId { final $_column = $_itemColumn('person_id'); if ($_column == null) return null; final manager = i5 .$$PersonEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('person_entity')) + ).resultSet('person_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_personIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -106,75 +126,96 @@ class $$AssetFaceEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get imageWidth => $composableBuilder( - column: $table.imageWidth, builder: (column) => i0.ColumnFilters(column)); + column: $table.imageWidth, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get imageHeight => $composableBuilder( - column: $table.imageHeight, - builder: (column) => i0.ColumnFilters(column)); + column: $table.imageHeight, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get boundingBoxX1 => $composableBuilder( - column: $table.boundingBoxX1, - builder: (column) => i0.ColumnFilters(column)); + column: $table.boundingBoxX1, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get boundingBoxY1 => $composableBuilder( - column: $table.boundingBoxY1, - builder: (column) => i0.ColumnFilters(column)); + column: $table.boundingBoxY1, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get boundingBoxX2 => $composableBuilder( - column: $table.boundingBoxX2, - builder: (column) => i0.ColumnFilters(column)); + column: $table.boundingBoxX2, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get boundingBoxY2 => $composableBuilder( - column: $table.boundingBoxY2, - builder: (column) => i0.ColumnFilters(column)); + column: $table.boundingBoxY2, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get sourceType => $composableBuilder( - column: $table.sourceType, builder: (column) => i0.ColumnFilters(column)); + column: $table.sourceType, + builder: (column) => i0.ColumnFilters(column), + ); i3.$$RemoteAssetEntityTableFilterComposer get assetId { final i3.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$PersonEntityTableFilterComposer get personId { final i5.$$PersonEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.personId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('person_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$PersonEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('person_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.personId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('person_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$PersonEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('person_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -189,79 +230,97 @@ class $$AssetFaceEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get imageWidth => $composableBuilder( - column: $table.imageWidth, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.imageWidth, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get imageHeight => $composableBuilder( - column: $table.imageHeight, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.imageHeight, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get boundingBoxX1 => $composableBuilder( - column: $table.boundingBoxX1, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.boundingBoxX1, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get boundingBoxY1 => $composableBuilder( - column: $table.boundingBoxY1, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.boundingBoxY1, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get boundingBoxX2 => $composableBuilder( - column: $table.boundingBoxX2, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.boundingBoxX2, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get boundingBoxY2 => $composableBuilder( - column: $table.boundingBoxY2, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.boundingBoxY2, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get sourceType => $composableBuilder( - column: $table.sourceType, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.sourceType, + builder: (column) => i0.ColumnOrderings(column), + ); i3.$$RemoteAssetEntityTableOrderingComposer get assetId { final i3.$$RemoteAssetEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$PersonEntityTableOrderingComposer get personId { final i5.$$PersonEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.personId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('person_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$PersonEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('person_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.personId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('person_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$PersonEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('person_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -279,88 +338,116 @@ class $$AssetFaceEntityTableAnnotationComposer $composableBuilder(column: $table.id, builder: (column) => column); i0.GeneratedColumn get imageWidth => $composableBuilder( - column: $table.imageWidth, builder: (column) => column); + column: $table.imageWidth, + builder: (column) => column, + ); i0.GeneratedColumn get imageHeight => $composableBuilder( - column: $table.imageHeight, builder: (column) => column); + column: $table.imageHeight, + builder: (column) => column, + ); i0.GeneratedColumn get boundingBoxX1 => $composableBuilder( - column: $table.boundingBoxX1, builder: (column) => column); + column: $table.boundingBoxX1, + builder: (column) => column, + ); i0.GeneratedColumn get boundingBoxY1 => $composableBuilder( - column: $table.boundingBoxY1, builder: (column) => column); + column: $table.boundingBoxY1, + builder: (column) => column, + ); i0.GeneratedColumn get boundingBoxX2 => $composableBuilder( - column: $table.boundingBoxX2, builder: (column) => column); + column: $table.boundingBoxX2, + builder: (column) => column, + ); i0.GeneratedColumn get boundingBoxY2 => $composableBuilder( - column: $table.boundingBoxY2, builder: (column) => column); + column: $table.boundingBoxY2, + builder: (column) => column, + ); i0.GeneratedColumn get sourceType => $composableBuilder( - column: $table.sourceType, builder: (column) => column); + column: $table.sourceType, + builder: (column) => column, + ); i3.$$RemoteAssetEntityTableAnnotationComposer get assetId { final i3.$$RemoteAssetEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$PersonEntityTableAnnotationComposer get personId { final i5.$$PersonEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.personId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('person_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$PersonEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('person_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.personId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('person_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$PersonEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('person_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$AssetFaceEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$AssetFaceEntityTable, - i1.AssetFaceEntityData, - i1.$$AssetFaceEntityTableFilterComposer, - i1.$$AssetFaceEntityTableOrderingComposer, - i1.$$AssetFaceEntityTableAnnotationComposer, - $$AssetFaceEntityTableCreateCompanionBuilder, - $$AssetFaceEntityTableUpdateCompanionBuilder, - (i1.AssetFaceEntityData, i1.$$AssetFaceEntityTableReferences), - i1.AssetFaceEntityData, - i0.PrefetchHooks Function({bool assetId, bool personId})> { +class $$AssetFaceEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$AssetFaceEntityTable, + i1.AssetFaceEntityData, + i1.$$AssetFaceEntityTableFilterComposer, + i1.$$AssetFaceEntityTableOrderingComposer, + i1.$$AssetFaceEntityTableAnnotationComposer, + $$AssetFaceEntityTableCreateCompanionBuilder, + $$AssetFaceEntityTableUpdateCompanionBuilder, + (i1.AssetFaceEntityData, i1.$$AssetFaceEntityTableReferences), + i1.AssetFaceEntityData, + i0.PrefetchHooks Function({bool assetId, bool personId}) + > { $$AssetFaceEntityTableTableManager( - i0.GeneratedDatabase db, i1.$AssetFaceEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$AssetFaceEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -369,66 +456,69 @@ class $$AssetFaceEntityTableTableManager extends i0.RootTableManager< i1.$$AssetFaceEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1 .$$AssetFaceEntityTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - i0.Value id = const i0.Value.absent(), - i0.Value assetId = const i0.Value.absent(), - i0.Value personId = const i0.Value.absent(), - i0.Value imageWidth = const i0.Value.absent(), - i0.Value imageHeight = const i0.Value.absent(), - i0.Value boundingBoxX1 = const i0.Value.absent(), - i0.Value boundingBoxY1 = const i0.Value.absent(), - i0.Value boundingBoxX2 = const i0.Value.absent(), - i0.Value boundingBoxY2 = const i0.Value.absent(), - i0.Value sourceType = const i0.Value.absent(), - }) => - i1.AssetFaceEntityCompanion( - id: id, - assetId: assetId, - personId: personId, - imageWidth: imageWidth, - imageHeight: imageHeight, - boundingBoxX1: boundingBoxX1, - boundingBoxY1: boundingBoxY1, - boundingBoxX2: boundingBoxX2, - boundingBoxY2: boundingBoxY2, - sourceType: sourceType, - ), - createCompanionCallback: ({ - required String id, - required String assetId, - i0.Value personId = const i0.Value.absent(), - required int imageWidth, - required int imageHeight, - required int boundingBoxX1, - required int boundingBoxY1, - required int boundingBoxX2, - required int boundingBoxY2, - required String sourceType, - }) => - i1.AssetFaceEntityCompanion.insert( - id: id, - assetId: assetId, - personId: personId, - imageWidth: imageWidth, - imageHeight: imageHeight, - boundingBoxX1: boundingBoxX1, - boundingBoxY1: boundingBoxY1, - boundingBoxX2: boundingBoxX2, - boundingBoxY2: boundingBoxY2, - sourceType: sourceType, - ), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value assetId = const i0.Value.absent(), + i0.Value personId = const i0.Value.absent(), + i0.Value imageWidth = const i0.Value.absent(), + i0.Value imageHeight = const i0.Value.absent(), + i0.Value boundingBoxX1 = const i0.Value.absent(), + i0.Value boundingBoxY1 = const i0.Value.absent(), + i0.Value boundingBoxX2 = const i0.Value.absent(), + i0.Value boundingBoxY2 = const i0.Value.absent(), + i0.Value sourceType = const i0.Value.absent(), + }) => i1.AssetFaceEntityCompanion( + id: id, + assetId: assetId, + personId: personId, + imageWidth: imageWidth, + imageHeight: imageHeight, + boundingBoxX1: boundingBoxX1, + boundingBoxY1: boundingBoxY1, + boundingBoxX2: boundingBoxX2, + boundingBoxY2: boundingBoxY2, + sourceType: sourceType, + ), + createCompanionCallback: + ({ + required String id, + required String assetId, + i0.Value personId = const i0.Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) => i1.AssetFaceEntityCompanion.insert( + id: id, + assetId: assetId, + personId: personId, + imageWidth: imageWidth, + imageHeight: imageHeight, + boundingBoxX1: boundingBoxX1, + boundingBoxY1: boundingBoxY1, + boundingBoxX2: boundingBoxX2, + boundingBoxY2: boundingBoxY2, + sourceType: sourceType, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$AssetFaceEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$AssetFaceEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({assetId = false, personId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -439,52 +529,65 @@ class $$AssetFaceEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (assetId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.assetId, - referencedTable: - i1.$$AssetFaceEntityTableReferences._assetIdTable(db), - referencedColumn: i1.$$AssetFaceEntityTableReferences - ._assetIdTable(db) - .id, - ) as T; - } - if (personId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.personId, - referencedTable: - i1.$$AssetFaceEntityTableReferences._personIdTable(db), - referencedColumn: i1.$$AssetFaceEntityTableReferences - ._personIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (assetId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.assetId, + referencedTable: i1 + .$$AssetFaceEntityTableReferences + ._assetIdTable(db), + referencedColumn: i1 + .$$AssetFaceEntityTableReferences + ._assetIdTable(db) + .id, + ) + as T; + } + if (personId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.personId, + referencedTable: i1 + .$$AssetFaceEntityTableReferences + ._personIdTable(db), + referencedColumn: i1 + .$$AssetFaceEntityTableReferences + ._personIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$AssetFaceEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$AssetFaceEntityTable, - i1.AssetFaceEntityData, - i1.$$AssetFaceEntityTableFilterComposer, - i1.$$AssetFaceEntityTableOrderingComposer, - i1.$$AssetFaceEntityTableAnnotationComposer, - $$AssetFaceEntityTableCreateCompanionBuilder, - $$AssetFaceEntityTableUpdateCompanionBuilder, - (i1.AssetFaceEntityData, i1.$$AssetFaceEntityTableReferences), - i1.AssetFaceEntityData, - i0.PrefetchHooks Function({bool assetId, bool personId})>; +typedef $$AssetFaceEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$AssetFaceEntityTable, + i1.AssetFaceEntityData, + i1.$$AssetFaceEntityTableFilterComposer, + i1.$$AssetFaceEntityTableOrderingComposer, + i1.$$AssetFaceEntityTableAnnotationComposer, + $$AssetFaceEntityTableCreateCompanionBuilder, + $$AssetFaceEntityTableUpdateCompanionBuilder, + (i1.AssetFaceEntityData, i1.$$AssetFaceEntityTableReferences), + i1.AssetFaceEntityData, + i0.PrefetchHooks Function({bool assetId, bool personId}) + >; class $AssetFaceEntityTable extends i2.AssetFaceEntity with i0.TableInfo<$AssetFaceEntityTable, i1.AssetFaceEntityData> { @@ -495,81 +598,126 @@ class $AssetFaceEntityTable extends i2.AssetFaceEntity static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _assetIdMeta = - const i0.VerificationMeta('assetId'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _assetIdMeta = const i0.VerificationMeta( + 'assetId', + ); @override late final i0.GeneratedColumn assetId = i0.GeneratedColumn( - 'asset_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _personIdMeta = - const i0.VerificationMeta('personId'); + 'asset_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _personIdMeta = const i0.VerificationMeta( + 'personId', + ); @override late final i0.GeneratedColumn personId = i0.GeneratedColumn( - 'person_id', aliasedName, true, - type: i0.DriftSqlType.string, - requiredDuringInsert: false, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES person_entity (id) ON DELETE SET NULL')); - static const i0.VerificationMeta _imageWidthMeta = - const i0.VerificationMeta('imageWidth'); + 'person_id', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL', + ), + ); + static const i0.VerificationMeta _imageWidthMeta = const i0.VerificationMeta( + 'imageWidth', + ); @override late final i0.GeneratedColumn imageWidth = i0.GeneratedColumn( - 'image_width', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true); - static const i0.VerificationMeta _imageHeightMeta = - const i0.VerificationMeta('imageHeight'); + 'image_width', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _imageHeightMeta = const i0.VerificationMeta( + 'imageHeight', + ); @override late final i0.GeneratedColumn imageHeight = i0.GeneratedColumn( - 'image_height', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true); + 'image_height', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ); static const i0.VerificationMeta _boundingBoxX1Meta = const i0.VerificationMeta('boundingBoxX1'); @override late final i0.GeneratedColumn boundingBoxX1 = i0.GeneratedColumn( - 'bounding_box_x1', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true); + 'bounding_box_x1', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ); static const i0.VerificationMeta _boundingBoxY1Meta = const i0.VerificationMeta('boundingBoxY1'); @override late final i0.GeneratedColumn boundingBoxY1 = i0.GeneratedColumn( - 'bounding_box_y1', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true); + 'bounding_box_y1', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ); static const i0.VerificationMeta _boundingBoxX2Meta = const i0.VerificationMeta('boundingBoxX2'); @override late final i0.GeneratedColumn boundingBoxX2 = i0.GeneratedColumn( - 'bounding_box_x2', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true); + 'bounding_box_x2', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ); static const i0.VerificationMeta _boundingBoxY2Meta = const i0.VerificationMeta('boundingBoxY2'); @override late final i0.GeneratedColumn boundingBoxY2 = i0.GeneratedColumn( - 'bounding_box_y2', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true); - static const i0.VerificationMeta _sourceTypeMeta = - const i0.VerificationMeta('sourceType'); + 'bounding_box_y2', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _sourceTypeMeta = const i0.VerificationMeta( + 'sourceType', + ); @override late final i0.GeneratedColumn sourceType = i0.GeneratedColumn( - 'source_type', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); + 'source_type', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); @override List get $columns => [ - id, - assetId, - personId, - imageWidth, - imageHeight, - boundingBoxX1, - boundingBoxY1, - boundingBoxX2, - boundingBoxY2, - sourceType - ]; + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -577,8 +725,9 @@ class $AssetFaceEntityTable extends i2.AssetFaceEntity static const String $name = 'asset_face_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { @@ -587,68 +736,87 @@ class $AssetFaceEntityTable extends i2.AssetFaceEntity context.missing(_idMeta); } if (data.containsKey('asset_id')) { - context.handle(_assetIdMeta, - assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta)); + context.handle( + _assetIdMeta, + assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta), + ); } else if (isInserting) { context.missing(_assetIdMeta); } if (data.containsKey('person_id')) { - context.handle(_personIdMeta, - personId.isAcceptableOrUnknown(data['person_id']!, _personIdMeta)); + context.handle( + _personIdMeta, + personId.isAcceptableOrUnknown(data['person_id']!, _personIdMeta), + ); } if (data.containsKey('image_width')) { context.handle( - _imageWidthMeta, - imageWidth.isAcceptableOrUnknown( - data['image_width']!, _imageWidthMeta)); + _imageWidthMeta, + imageWidth.isAcceptableOrUnknown(data['image_width']!, _imageWidthMeta), + ); } else if (isInserting) { context.missing(_imageWidthMeta); } if (data.containsKey('image_height')) { context.handle( + _imageHeightMeta, + imageHeight.isAcceptableOrUnknown( + data['image_height']!, _imageHeightMeta, - imageHeight.isAcceptableOrUnknown( - data['image_height']!, _imageHeightMeta)); + ), + ); } else if (isInserting) { context.missing(_imageHeightMeta); } if (data.containsKey('bounding_box_x1')) { context.handle( + _boundingBoxX1Meta, + boundingBoxX1.isAcceptableOrUnknown( + data['bounding_box_x1']!, _boundingBoxX1Meta, - boundingBoxX1.isAcceptableOrUnknown( - data['bounding_box_x1']!, _boundingBoxX1Meta)); + ), + ); } else if (isInserting) { context.missing(_boundingBoxX1Meta); } if (data.containsKey('bounding_box_y1')) { context.handle( + _boundingBoxY1Meta, + boundingBoxY1.isAcceptableOrUnknown( + data['bounding_box_y1']!, _boundingBoxY1Meta, - boundingBoxY1.isAcceptableOrUnknown( - data['bounding_box_y1']!, _boundingBoxY1Meta)); + ), + ); } else if (isInserting) { context.missing(_boundingBoxY1Meta); } if (data.containsKey('bounding_box_x2')) { context.handle( + _boundingBoxX2Meta, + boundingBoxX2.isAcceptableOrUnknown( + data['bounding_box_x2']!, _boundingBoxX2Meta, - boundingBoxX2.isAcceptableOrUnknown( - data['bounding_box_x2']!, _boundingBoxX2Meta)); + ), + ); } else if (isInserting) { context.missing(_boundingBoxX2Meta); } if (data.containsKey('bounding_box_y2')) { context.handle( + _boundingBoxY2Meta, + boundingBoxY2.isAcceptableOrUnknown( + data['bounding_box_y2']!, _boundingBoxY2Meta, - boundingBoxY2.isAcceptableOrUnknown( - data['bounding_box_y2']!, _boundingBoxY2Meta)); + ), + ); } else if (isInserting) { context.missing(_boundingBoxY2Meta); } if (data.containsKey('source_type')) { context.handle( - _sourceTypeMeta, - sourceType.isAcceptableOrUnknown( - data['source_type']!, _sourceTypeMeta)); + _sourceTypeMeta, + sourceType.isAcceptableOrUnknown(data['source_type']!, _sourceTypeMeta), + ); } else if (isInserting) { context.missing(_sourceTypeMeta); } @@ -661,26 +829,46 @@ class $AssetFaceEntityTable extends i2.AssetFaceEntity i1.AssetFaceEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.AssetFaceEntityData( - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, - assetId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - personId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}person_id']), - imageWidth: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}image_width'])!, - imageHeight: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}image_height'])!, + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + assetId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + personId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}person_id'], + ), + imageWidth: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}image_width'], + )!, + imageHeight: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}image_height'], + )!, boundingBoxX1: attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, data['${effectivePrefix}bounding_box_x1'])!, + i0.DriftSqlType.int, + data['${effectivePrefix}bounding_box_x1'], + )!, boundingBoxY1: attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, data['${effectivePrefix}bounding_box_y1'])!, + i0.DriftSqlType.int, + data['${effectivePrefix}bounding_box_y1'], + )!, boundingBoxX2: attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, data['${effectivePrefix}bounding_box_x2'])!, + i0.DriftSqlType.int, + data['${effectivePrefix}bounding_box_x2'], + )!, boundingBoxY2: attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, data['${effectivePrefix}bounding_box_y2'])!, - sourceType: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}source_type'])!, + i0.DriftSqlType.int, + data['${effectivePrefix}bounding_box_y2'], + )!, + sourceType: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}source_type'], + )!, ); } @@ -707,17 +895,18 @@ class AssetFaceEntityData extends i0.DataClass final int boundingBoxX2; final int boundingBoxY2; final String sourceType; - const AssetFaceEntityData( - {required this.id, - required this.assetId, - this.personId, - required this.imageWidth, - required this.imageHeight, - required this.boundingBoxX1, - required this.boundingBoxY1, - required this.boundingBoxX2, - required this.boundingBoxY2, - required this.sourceType}); + const AssetFaceEntityData({ + required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -736,8 +925,10 @@ class AssetFaceEntityData extends i0.DataClass return map; } - factory AssetFaceEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory AssetFaceEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return AssetFaceEntityData( id: serializer.fromJson(json['id']), @@ -769,38 +960,40 @@ class AssetFaceEntityData extends i0.DataClass }; } - i1.AssetFaceEntityData copyWith( - {String? id, - String? assetId, - i0.Value personId = const i0.Value.absent(), - int? imageWidth, - int? imageHeight, - int? boundingBoxX1, - int? boundingBoxY1, - int? boundingBoxX2, - int? boundingBoxY2, - String? sourceType}) => - i1.AssetFaceEntityData( - id: id ?? this.id, - assetId: assetId ?? this.assetId, - personId: personId.present ? personId.value : this.personId, - imageWidth: imageWidth ?? this.imageWidth, - imageHeight: imageHeight ?? this.imageHeight, - boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, - boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, - boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, - boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, - sourceType: sourceType ?? this.sourceType, - ); + i1.AssetFaceEntityData copyWith({ + String? id, + String? assetId, + i0.Value personId = const i0.Value.absent(), + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType, + }) => i1.AssetFaceEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId.present ? personId.value : this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); AssetFaceEntityData copyWithCompanion(i1.AssetFaceEntityCompanion data) { return AssetFaceEntityData( id: data.id.present ? data.id.value : this.id, assetId: data.assetId.present ? data.assetId.value : this.assetId, personId: data.personId.present ? data.personId.value : this.personId, - imageWidth: - data.imageWidth.present ? data.imageWidth.value : this.imageWidth, - imageHeight: - data.imageHeight.present ? data.imageHeight.value : this.imageHeight, + imageWidth: data.imageWidth.present + ? data.imageWidth.value + : this.imageWidth, + imageHeight: data.imageHeight.present + ? data.imageHeight.value + : this.imageHeight, boundingBoxX1: data.boundingBoxX1.present ? data.boundingBoxX1.value : this.boundingBoxX1, @@ -813,8 +1006,9 @@ class AssetFaceEntityData extends i0.DataClass boundingBoxY2: data.boundingBoxY2.present ? data.boundingBoxY2.value : this.boundingBoxY2, - sourceType: - data.sourceType.present ? data.sourceType.value : this.sourceType, + sourceType: data.sourceType.present + ? data.sourceType.value + : this.sourceType, ); } @@ -837,16 +1031,17 @@ class AssetFaceEntityData extends i0.DataClass @override int get hashCode => Object.hash( - id, - assetId, - personId, - imageWidth, - imageHeight, - boundingBoxX1, - boundingBoxY1, - boundingBoxX2, - boundingBoxY2, - sourceType); + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -898,15 +1093,15 @@ class AssetFaceEntityCompanion required int boundingBoxX2, required int boundingBoxY2, required String sourceType, - }) : id = i0.Value(id), - assetId = i0.Value(assetId), - imageWidth = i0.Value(imageWidth), - imageHeight = i0.Value(imageHeight), - boundingBoxX1 = i0.Value(boundingBoxX1), - boundingBoxY1 = i0.Value(boundingBoxY1), - boundingBoxX2 = i0.Value(boundingBoxX2), - boundingBoxY2 = i0.Value(boundingBoxY2), - sourceType = i0.Value(sourceType); + }) : id = i0.Value(id), + assetId = i0.Value(assetId), + imageWidth = i0.Value(imageWidth), + imageHeight = i0.Value(imageHeight), + boundingBoxX1 = i0.Value(boundingBoxX1), + boundingBoxY1 = i0.Value(boundingBoxY1), + boundingBoxX2 = i0.Value(boundingBoxX2), + boundingBoxY2 = i0.Value(boundingBoxY2), + sourceType = i0.Value(sourceType); static i0.Insertable custom({ i0.Expression? id, i0.Expression? assetId, @@ -933,17 +1128,18 @@ class AssetFaceEntityCompanion }); } - i1.AssetFaceEntityCompanion copyWith( - {i0.Value? id, - i0.Value? assetId, - i0.Value? personId, - i0.Value? imageWidth, - i0.Value? imageHeight, - i0.Value? boundingBoxX1, - i0.Value? boundingBoxY1, - i0.Value? boundingBoxX2, - i0.Value? boundingBoxY2, - i0.Value? sourceType}) { + i1.AssetFaceEntityCompanion copyWith({ + i0.Value? id, + i0.Value? assetId, + i0.Value? personId, + i0.Value? imageWidth, + i0.Value? imageHeight, + i0.Value? boundingBoxX1, + i0.Value? boundingBoxY1, + i0.Value? boundingBoxX2, + i0.Value? boundingBoxY2, + i0.Value? sourceType, + }) { return i1.AssetFaceEntityCompanion( id: id ?? this.id, assetId: assetId ?? this.assetId, diff --git a/mobile/lib/infrastructure/entities/device_asset.entity.dart b/mobile/lib/infrastructure/entities/device_asset.entity.dart index d8bfb2aa45..e3e4a0d4f4 100644 --- a/mobile/lib/infrastructure/entities/device_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/device_asset.entity.dart @@ -16,21 +16,10 @@ class DeviceAssetEntity { final List hash; final DateTime modifiedTime; - const DeviceAssetEntity({ - required this.assetId, - required this.hash, - required this.modifiedTime, - }); + const DeviceAssetEntity({required this.assetId, required this.hash, required this.modifiedTime}); - DeviceAsset toModel() => DeviceAsset( - assetId: assetId, - hash: Uint8List.fromList(hash), - modifiedTime: modifiedTime, - ); + DeviceAsset toModel() => DeviceAsset(assetId: assetId, hash: Uint8List.fromList(hash), modifiedTime: modifiedTime); - static DeviceAssetEntity fromDto(DeviceAsset dto) => DeviceAssetEntity( - assetId: dto.assetId, - hash: dto.hash, - modifiedTime: dto.modifiedTime, - ); + static DeviceAssetEntity fromDto(DeviceAsset dto) => + DeviceAssetEntity(assetId: dto.assetId, hash: dto.hash, modifiedTime: dto.modifiedTime); } diff --git a/mobile/lib/infrastructure/entities/device_asset.entity.g.dart b/mobile/lib/infrastructure/entities/device_asset.entity.g.dart index a66f8288ef..87ae54ad40 100644 --- a/mobile/lib/infrastructure/entities/device_asset.entity.g.dart +++ b/mobile/lib/infrastructure/entities/device_asset.entity.g.dart @@ -17,22 +17,15 @@ const DeviceAssetEntitySchema = CollectionSchema( name: r'DeviceAssetEntity', id: 6967030785073446271, properties: { - r'assetId': PropertySchema( - id: 0, - name: r'assetId', - type: IsarType.string, - ), - r'hash': PropertySchema( - id: 1, - name: r'hash', - type: IsarType.byteList, - ), + r'assetId': PropertySchema(id: 0, name: r'assetId', type: IsarType.string), + r'hash': PropertySchema(id: 1, name: r'hash', type: IsarType.byteList), r'modifiedTime': PropertySchema( id: 2, name: r'modifiedTime', type: IsarType.dateTime, - ) + ), }, + estimateSize: _deviceAssetEntityEstimateSize, serialize: _deviceAssetEntitySerialize, deserialize: _deviceAssetEntityDeserialize, @@ -49,7 +42,7 @@ const DeviceAssetEntitySchema = CollectionSchema( name: r'assetId', type: IndexType.hash, caseSensitive: true, - ) + ), ], ), r'hash': IndexSchema( @@ -62,12 +55,13 @@ const DeviceAssetEntitySchema = CollectionSchema( name: r'hash', type: IndexType.hash, caseSensitive: false, - ) + ), ], - ) + ), }, links: {}, embeddedSchemas: {}, + getId: _deviceAssetEntityGetId, getLinks: _deviceAssetEntityGetLinks, attach: _deviceAssetEntityAttach, @@ -133,12 +127,16 @@ Id _deviceAssetEntityGetId(DeviceAssetEntity object) { } List> _deviceAssetEntityGetLinks( - DeviceAssetEntity object) { + DeviceAssetEntity object, +) { return []; } void _deviceAssetEntityAttach( - IsarCollection col, Id id, DeviceAssetEntity object) {} + IsarCollection col, + Id id, + DeviceAssetEntity object, +) {} extension DeviceAssetEntityByIndex on IsarCollection { Future getByAssetId(String assetId) { @@ -189,8 +187,10 @@ extension DeviceAssetEntityByIndex on IsarCollection { return putAllByIndex(r'assetId', objects); } - List putAllByAssetIdSync(List objects, - {bool saveLinks = true}) { + List putAllByAssetIdSync( + List objects, { + bool saveLinks = true, + }) { return putAllByIndexSync(r'assetId', objects, saveLinks: saveLinks); } } @@ -207,17 +207,14 @@ extension DeviceAssetEntityQueryWhereSort extension DeviceAssetEntityQueryWhere on QueryBuilder { QueryBuilder - idEqualTo(Id id) { + idEqualTo(Id id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); + return query.addWhereClause(IdWhereClause.between(lower: id, upper: id)); }); } QueryBuilder - idNotEqualTo(Id id) { + idNotEqualTo(Id id) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query @@ -240,7 +237,7 @@ extension DeviceAssetEntityQueryWhere } QueryBuilder - idGreaterThan(Id id, {bool include = false}) { + idGreaterThan(Id id, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: id, includeLower: include), @@ -249,7 +246,7 @@ extension DeviceAssetEntityQueryWhere } QueryBuilder - idLessThan(Id id, {bool include = false}) { + idLessThan(Id id, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: id, includeUpper: include), @@ -258,108 +255,124 @@ extension DeviceAssetEntityQueryWhere } QueryBuilder - idBetween( + idBetween( Id lowerId, Id upperId, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - assetIdEqualTo(String assetId) { + assetIdEqualTo(String assetId) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'assetId', - value: [assetId], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'assetId', value: [assetId]), + ); }); } QueryBuilder - assetIdNotEqualTo(String assetId) { + assetIdNotEqualTo(String assetId) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'assetId', - lower: [], - upper: [assetId], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'assetId', - lower: [assetId], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'assetId', + lower: [], + upper: [assetId], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'assetId', + lower: [assetId], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'assetId', - lower: [assetId], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'assetId', - lower: [], - upper: [assetId], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'assetId', + lower: [assetId], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'assetId', + lower: [], + upper: [assetId], + includeUpper: false, + ), + ); } }); } QueryBuilder - hashEqualTo(List hash) { + hashEqualTo(List hash) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'hash', - value: [hash], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'hash', value: [hash]), + ); }); } QueryBuilder - hashNotEqualTo(List hash) { + hashNotEqualTo(List hash) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [], - upper: [hash], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [hash], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [], + upper: [hash], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [hash], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [hash], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [], - upper: [hash], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [hash], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [], + upper: [hash], + includeUpper: false, + ), + ); } }); } @@ -368,53 +381,56 @@ extension DeviceAssetEntityQueryWhere extension DeviceAssetEntityQueryFilter on QueryBuilder { QueryBuilder - assetIdEqualTo( - String value, { - bool caseSensitive = true, - }) { + assetIdEqualTo(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'assetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'assetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdGreaterThan( + assetIdGreaterThan( String value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'assetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'assetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdLessThan( + assetIdLessThan( String value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'assetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'assetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdBetween( + assetIdBetween( String lower, String upper, { bool includeLower = true, @@ -422,216 +438,181 @@ extension DeviceAssetEntityQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'assetId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'assetId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdStartsWith( - String value, { - bool caseSensitive = true, - }) { + assetIdStartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'assetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'assetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdEndsWith( - String value, { - bool caseSensitive = true, - }) { + assetIdEndsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'assetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'assetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdContains(String value, {bool caseSensitive = true}) { + assetIdContains(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'assetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'assetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdMatches(String pattern, {bool caseSensitive = true}) { + assetIdMatches(String pattern, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'assetId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'assetId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdIsEmpty() { + assetIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'assetId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'assetId', value: ''), + ); }); } QueryBuilder - assetIdIsNotEmpty() { + assetIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'assetId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'assetId', value: ''), + ); }); } QueryBuilder - hashElementEqualTo(int value) { + hashElementEqualTo(int value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'hash', value: value), + ); }); } QueryBuilder - hashElementGreaterThan( - int value, { - bool include = false, - }) { + hashElementGreaterThan(int value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'hash', + value: value, + ), + ); }); } QueryBuilder - hashElementLessThan( - int value, { - bool include = false, - }) { + hashElementLessThan(int value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'hash', + value: value, + ), + ); }); } QueryBuilder - hashElementBetween( + hashElementBetween( int lower, int upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'hash', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); - }); - } - - QueryBuilder - hashLengthEqualTo(int length) { - return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - length, - true, - length, - true, + return query.addFilterCondition( + FilterCondition.between( + property: r'hash', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), ); }); } QueryBuilder - hashIsEmpty() { + hashLengthEqualTo(int length) { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - true, - 0, - true, - ); + return query.listLength(r'hash', length, true, length, true); }); } QueryBuilder - hashIsNotEmpty() { + hashIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - false, - 999999, - true, - ); + return query.listLength(r'hash', 0, true, 0, true); }); } QueryBuilder - hashLengthLessThan( - int length, { - bool include = false, - }) { + hashIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - true, - length, - include, - ); + return query.listLength(r'hash', 0, false, 999999, true); }); } QueryBuilder - hashLengthGreaterThan( - int length, { - bool include = false, - }) { + hashLengthLessThan(int length, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - length, - include, - 999999, - true, - ); + return query.listLength(r'hash', 0, true, length, include); }); } QueryBuilder - hashLengthBetween( + hashLengthGreaterThan(int length, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.listLength(r'hash', length, include, 999999, true); + }); + } + + QueryBuilder + hashLengthBetween( int lower, int upper, { bool includeLower = true, @@ -649,114 +630,112 @@ extension DeviceAssetEntityQueryFilter } QueryBuilder - idEqualTo(Id value) { + idEqualTo(Id value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: value), + ); }); } QueryBuilder - idGreaterThan( - Id value, { - bool include = false, - }) { + idGreaterThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + ), + ); }); } QueryBuilder - idLessThan( - Id value, { - bool include = false, - }) { + idLessThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + ), + ); }); } QueryBuilder - idBetween( + idBetween( Id lower, Id upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - modifiedTimeEqualTo(DateTime value) { + modifiedTimeEqualTo(DateTime value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'modifiedTime', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'modifiedTime', value: value), + ); }); } QueryBuilder - modifiedTimeGreaterThan( - DateTime value, { - bool include = false, - }) { + modifiedTimeGreaterThan(DateTime value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'modifiedTime', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'modifiedTime', + value: value, + ), + ); }); } QueryBuilder - modifiedTimeLessThan( - DateTime value, { - bool include = false, - }) { + modifiedTimeLessThan(DateTime value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'modifiedTime', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'modifiedTime', + value: value, + ), + ); }); } QueryBuilder - modifiedTimeBetween( + modifiedTimeBetween( DateTime lower, DateTime upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'modifiedTime', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'modifiedTime', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -770,28 +749,28 @@ extension DeviceAssetEntityQueryLinks extension DeviceAssetEntityQuerySortBy on QueryBuilder { QueryBuilder - sortByAssetId() { + sortByAssetId() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'assetId', Sort.asc); }); } QueryBuilder - sortByAssetIdDesc() { + sortByAssetIdDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'assetId', Sort.desc); }); } QueryBuilder - sortByModifiedTime() { + sortByModifiedTime() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'modifiedTime', Sort.asc); }); } QueryBuilder - sortByModifiedTimeDesc() { + sortByModifiedTimeDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'modifiedTime', Sort.desc); }); @@ -801,14 +780,14 @@ extension DeviceAssetEntityQuerySortBy extension DeviceAssetEntityQuerySortThenBy on QueryBuilder { QueryBuilder - thenByAssetId() { + thenByAssetId() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'assetId', Sort.asc); }); } QueryBuilder - thenByAssetIdDesc() { + thenByAssetIdDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'assetId', Sort.desc); }); @@ -821,21 +800,21 @@ extension DeviceAssetEntityQuerySortThenBy } QueryBuilder - thenByIdDesc() { + thenByIdDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'id', Sort.desc); }); } QueryBuilder - thenByModifiedTime() { + thenByModifiedTime() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'modifiedTime', Sort.asc); }); } QueryBuilder - thenByModifiedTimeDesc() { + thenByModifiedTimeDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'modifiedTime', Sort.desc); }); @@ -845,21 +824,21 @@ extension DeviceAssetEntityQuerySortThenBy extension DeviceAssetEntityQueryWhereDistinct on QueryBuilder { QueryBuilder - distinctByAssetId({bool caseSensitive = true}) { + distinctByAssetId({bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'assetId', caseSensitive: caseSensitive); }); } QueryBuilder - distinctByHash() { + distinctByHash() { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'hash'); }); } QueryBuilder - distinctByModifiedTime() { + distinctByModifiedTime() { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'modifiedTime'); }); @@ -887,7 +866,7 @@ extension DeviceAssetEntityQueryProperty } QueryBuilder - modifiedTimeProperty() { + modifiedTimeProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'modifiedTime'); }); diff --git a/mobile/lib/infrastructure/entities/exif.entity.dart b/mobile/lib/infrastructure/entities/exif.entity.dart index dae5486ab1..e8c7541349 100644 --- a/mobile/lib/infrastructure/entities/exif.entity.dart +++ b/mobile/lib/infrastructure/entities/exif.entity.dart @@ -52,47 +52,47 @@ class ExifInfo { }); static ExifInfo fromDto(domain.ExifInfo dto) => ExifInfo( - id: dto.assetId, - fileSize: dto.fileSize, - dateTimeOriginal: dto.dateTimeOriginal, - timeZone: dto.timeZone, - make: dto.make, - model: dto.model, - lens: dto.lens, - f: dto.f, - mm: dto.mm, - iso: dto.iso?.toInt(), - exposureSeconds: dto.exposureSeconds, - lat: dto.latitude, - long: dto.longitude, - city: dto.city, - state: dto.state, - country: dto.country, - description: dto.description, - orientation: dto.orientation, - ); + id: dto.assetId, + fileSize: dto.fileSize, + dateTimeOriginal: dto.dateTimeOriginal, + timeZone: dto.timeZone, + make: dto.make, + model: dto.model, + lens: dto.lens, + f: dto.f, + mm: dto.mm, + iso: dto.iso?.toInt(), + exposureSeconds: dto.exposureSeconds, + lat: dto.latitude, + long: dto.longitude, + city: dto.city, + state: dto.state, + country: dto.country, + description: dto.description, + orientation: dto.orientation, + ); domain.ExifInfo toDto() => domain.ExifInfo( - assetId: id, - fileSize: fileSize, - description: description, - orientation: orientation, - timeZone: timeZone, - dateTimeOriginal: dateTimeOriginal, - isFlipped: ExifDtoConverter.isOrientationFlipped(orientation), - latitude: lat, - longitude: long, - city: city, - state: state, - country: country, - make: make, - model: model, - lens: lens, - f: f, - mm: mm, - iso: iso?.toInt(), - exposureSeconds: exposureSeconds, - ); + assetId: id, + fileSize: fileSize, + description: description, + orientation: orientation, + timeZone: timeZone, + dateTimeOriginal: dateTimeOriginal, + isFlipped: ExifDtoConverter.isOrientationFlipped(orientation), + latitude: lat, + longitude: long, + city: city, + state: state, + country: country, + make: make, + model: model, + lens: lens, + f: f, + mm: mm, + iso: iso?.toInt(), + exposureSeconds: exposureSeconds, + ); } class RemoteExifEntity extends Table with DriftDefaultsMixin { @@ -148,24 +148,24 @@ class RemoteExifEntity extends Table with DriftDefaultsMixin { extension RemoteExifEntityDataDomainEx on RemoteExifEntityData { domain.ExifInfo toDto() => domain.ExifInfo( - fileSize: fileSize, - dateTimeOriginal: dateTimeOriginal, - timeZone: timeZone, - make: make, - model: model, - iso: iso, - city: city, - state: state, - country: country, - description: description, - orientation: orientation, - latitude: latitude, - longitude: longitude, - f: fNumber?.toDouble(), - mm: focalLength?.toDouble(), - lens: lens, - width: width?.toDouble(), - height: height?.toDouble(), - isFlipped: ExifDtoConverter.isOrientationFlipped(orientation), - ); + fileSize: fileSize, + dateTimeOriginal: dateTimeOriginal, + timeZone: timeZone, + make: make, + model: model, + iso: iso, + city: city, + state: state, + country: country, + description: description, + orientation: orientation, + latitude: latitude, + longitude: longitude, + f: fNumber?.toDouble(), + mm: focalLength?.toDouble(), + lens: lens, + width: width?.toDouble(), + height: height?.toDouble(), + isFlipped: ExifDtoConverter.isOrientationFlipped(orientation), + ); } diff --git a/mobile/lib/infrastructure/entities/exif.entity.drift.dart b/mobile/lib/infrastructure/entities/exif.entity.drift.dart index 10712948ea..d45d6e8ef6 100644 --- a/mobile/lib/infrastructure/entities/exif.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/exif.entity.drift.dart @@ -8,86 +8,100 @@ import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift. as i3; import 'package:drift/internal/modular.dart' as i4; -typedef $$RemoteExifEntityTableCreateCompanionBuilder - = i1.RemoteExifEntityCompanion Function({ - required String assetId, - i0.Value city, - i0.Value state, - i0.Value country, - i0.Value dateTimeOriginal, - i0.Value description, - i0.Value height, - i0.Value width, - i0.Value exposureTime, - i0.Value fNumber, - i0.Value fileSize, - i0.Value focalLength, - i0.Value latitude, - i0.Value longitude, - i0.Value iso, - i0.Value make, - i0.Value model, - i0.Value lens, - i0.Value orientation, - i0.Value timeZone, - i0.Value rating, - i0.Value projectionType, -}); -typedef $$RemoteExifEntityTableUpdateCompanionBuilder - = i1.RemoteExifEntityCompanion Function({ - i0.Value assetId, - i0.Value city, - i0.Value state, - i0.Value country, - i0.Value dateTimeOriginal, - i0.Value description, - i0.Value height, - i0.Value width, - i0.Value exposureTime, - i0.Value fNumber, - i0.Value fileSize, - i0.Value focalLength, - i0.Value latitude, - i0.Value longitude, - i0.Value iso, - i0.Value make, - i0.Value model, - i0.Value lens, - i0.Value orientation, - i0.Value timeZone, - i0.Value rating, - i0.Value projectionType, -}); +typedef $$RemoteExifEntityTableCreateCompanionBuilder = + i1.RemoteExifEntityCompanion Function({ + required String assetId, + i0.Value city, + i0.Value state, + i0.Value country, + i0.Value dateTimeOriginal, + i0.Value description, + i0.Value height, + i0.Value width, + i0.Value exposureTime, + i0.Value fNumber, + i0.Value fileSize, + i0.Value focalLength, + i0.Value latitude, + i0.Value longitude, + i0.Value iso, + i0.Value make, + i0.Value model, + i0.Value lens, + i0.Value orientation, + i0.Value timeZone, + i0.Value rating, + i0.Value projectionType, + }); +typedef $$RemoteExifEntityTableUpdateCompanionBuilder = + i1.RemoteExifEntityCompanion Function({ + i0.Value assetId, + i0.Value city, + i0.Value state, + i0.Value country, + i0.Value dateTimeOriginal, + i0.Value description, + i0.Value height, + i0.Value width, + i0.Value exposureTime, + i0.Value fNumber, + i0.Value fileSize, + i0.Value focalLength, + i0.Value latitude, + i0.Value longitude, + i0.Value iso, + i0.Value make, + i0.Value model, + i0.Value lens, + i0.Value orientation, + i0.Value timeZone, + i0.Value rating, + i0.Value projectionType, + }); -final class $$RemoteExifEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, i1.$RemoteExifEntityTable, i1.RemoteExifEntityData> { +final class $$RemoteExifEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$RemoteExifEntityTable, + i1.RemoteExifEntityData + > { $$RemoteExifEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i3.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('remote_asset_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet('remote_exif_entity') .assetId, - i4.ReadDatabaseContainer(db) - .resultSet('remote_asset_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('remote_asset_entity').id, + ), + ); i3.$$RemoteAssetEntityTableProcessedTableManager get assetId { final $_column = $_itemColumn('asset_id')!; final manager = i3 .$$RemoteAssetEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('remote_asset_entity')) + ).resultSet('remote_asset_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_assetIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -101,93 +115,134 @@ class $$RemoteExifEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get city => $composableBuilder( - column: $table.city, builder: (column) => i0.ColumnFilters(column)); + column: $table.city, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get state => $composableBuilder( - column: $table.state, builder: (column) => i0.ColumnFilters(column)); + column: $table.state, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get country => $composableBuilder( - column: $table.country, builder: (column) => i0.ColumnFilters(column)); + column: $table.country, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get dateTimeOriginal => $composableBuilder( - column: $table.dateTimeOriginal, - builder: (column) => i0.ColumnFilters(column)); + column: $table.dateTimeOriginal, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get description => $composableBuilder( - column: $table.description, - builder: (column) => i0.ColumnFilters(column)); + column: $table.description, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get height => $composableBuilder( - column: $table.height, builder: (column) => i0.ColumnFilters(column)); + column: $table.height, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get width => $composableBuilder( - column: $table.width, builder: (column) => i0.ColumnFilters(column)); + column: $table.width, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get exposureTime => $composableBuilder( - column: $table.exposureTime, - builder: (column) => i0.ColumnFilters(column)); + column: $table.exposureTime, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get fNumber => $composableBuilder( - column: $table.fNumber, builder: (column) => i0.ColumnFilters(column)); + column: $table.fNumber, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get fileSize => $composableBuilder( - column: $table.fileSize, builder: (column) => i0.ColumnFilters(column)); + column: $table.fileSize, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get focalLength => $composableBuilder( - column: $table.focalLength, - builder: (column) => i0.ColumnFilters(column)); + column: $table.focalLength, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get latitude => $composableBuilder( - column: $table.latitude, builder: (column) => i0.ColumnFilters(column)); + column: $table.latitude, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get longitude => $composableBuilder( - column: $table.longitude, builder: (column) => i0.ColumnFilters(column)); + column: $table.longitude, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get iso => $composableBuilder( - column: $table.iso, builder: (column) => i0.ColumnFilters(column)); + column: $table.iso, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get make => $composableBuilder( - column: $table.make, builder: (column) => i0.ColumnFilters(column)); + column: $table.make, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get model => $composableBuilder( - column: $table.model, builder: (column) => i0.ColumnFilters(column)); + column: $table.model, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get lens => $composableBuilder( - column: $table.lens, builder: (column) => i0.ColumnFilters(column)); + column: $table.lens, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get orientation => $composableBuilder( - column: $table.orientation, - builder: (column) => i0.ColumnFilters(column)); + column: $table.orientation, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get timeZone => $composableBuilder( - column: $table.timeZone, builder: (column) => i0.ColumnFilters(column)); + column: $table.timeZone, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get rating => $composableBuilder( - column: $table.rating, builder: (column) => i0.ColumnFilters(column)); + column: $table.rating, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get projectionType => $composableBuilder( - column: $table.projectionType, - builder: (column) => i0.ColumnFilters(column)); + column: $table.projectionType, + builder: (column) => i0.ColumnFilters(column), + ); i3.$$RemoteAssetEntityTableFilterComposer get assetId { final i3.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -202,96 +257,135 @@ class $$RemoteExifEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get city => $composableBuilder( - column: $table.city, builder: (column) => i0.ColumnOrderings(column)); + column: $table.city, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get state => $composableBuilder( - column: $table.state, builder: (column) => i0.ColumnOrderings(column)); + column: $table.state, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get country => $composableBuilder( - column: $table.country, builder: (column) => i0.ColumnOrderings(column)); + column: $table.country, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get dateTimeOriginal => $composableBuilder( - column: $table.dateTimeOriginal, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.dateTimeOriginal, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get description => $composableBuilder( - column: $table.description, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.description, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get height => $composableBuilder( - column: $table.height, builder: (column) => i0.ColumnOrderings(column)); + column: $table.height, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get width => $composableBuilder( - column: $table.width, builder: (column) => i0.ColumnOrderings(column)); + column: $table.width, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get exposureTime => $composableBuilder( - column: $table.exposureTime, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.exposureTime, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get fNumber => $composableBuilder( - column: $table.fNumber, builder: (column) => i0.ColumnOrderings(column)); + column: $table.fNumber, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get fileSize => $composableBuilder( - column: $table.fileSize, builder: (column) => i0.ColumnOrderings(column)); + column: $table.fileSize, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get focalLength => $composableBuilder( - column: $table.focalLength, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.focalLength, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get latitude => $composableBuilder( - column: $table.latitude, builder: (column) => i0.ColumnOrderings(column)); + column: $table.latitude, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get longitude => $composableBuilder( - column: $table.longitude, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.longitude, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get iso => $composableBuilder( - column: $table.iso, builder: (column) => i0.ColumnOrderings(column)); + column: $table.iso, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get make => $composableBuilder( - column: $table.make, builder: (column) => i0.ColumnOrderings(column)); + column: $table.make, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get model => $composableBuilder( - column: $table.model, builder: (column) => i0.ColumnOrderings(column)); + column: $table.model, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get lens => $composableBuilder( - column: $table.lens, builder: (column) => i0.ColumnOrderings(column)); + column: $table.lens, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get orientation => $composableBuilder( - column: $table.orientation, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.orientation, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get timeZone => $composableBuilder( - column: $table.timeZone, builder: (column) => i0.ColumnOrderings(column)); + column: $table.timeZone, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get rating => $composableBuilder( - column: $table.rating, builder: (column) => i0.ColumnOrderings(column)); + column: $table.rating, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get projectionType => $composableBuilder( - column: $table.projectionType, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.projectionType, + builder: (column) => i0.ColumnOrderings(column), + ); i3.$$RemoteAssetEntityTableOrderingComposer get assetId { final i3.$$RemoteAssetEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -315,10 +409,14 @@ class $$RemoteExifEntityTableAnnotationComposer $composableBuilder(column: $table.country, builder: (column) => column); i0.GeneratedColumn get dateTimeOriginal => $composableBuilder( - column: $table.dateTimeOriginal, builder: (column) => column); + column: $table.dateTimeOriginal, + builder: (column) => column, + ); i0.GeneratedColumn get description => $composableBuilder( - column: $table.description, builder: (column) => column); + column: $table.description, + builder: (column) => column, + ); i0.GeneratedColumn get height => $composableBuilder(column: $table.height, builder: (column) => column); @@ -327,7 +425,9 @@ class $$RemoteExifEntityTableAnnotationComposer $composableBuilder(column: $table.width, builder: (column) => column); i0.GeneratedColumn get exposureTime => $composableBuilder( - column: $table.exposureTime, builder: (column) => column); + column: $table.exposureTime, + builder: (column) => column, + ); i0.GeneratedColumn get fNumber => $composableBuilder(column: $table.fNumber, builder: (column) => column); @@ -336,7 +436,9 @@ class $$RemoteExifEntityTableAnnotationComposer $composableBuilder(column: $table.fileSize, builder: (column) => column); i0.GeneratedColumn get focalLength => $composableBuilder( - column: $table.focalLength, builder: (column) => column); + column: $table.focalLength, + builder: (column) => column, + ); i0.GeneratedColumn get latitude => $composableBuilder(column: $table.latitude, builder: (column) => column); @@ -357,7 +459,9 @@ class $$RemoteExifEntityTableAnnotationComposer $composableBuilder(column: $table.lens, builder: (column) => column); i0.GeneratedColumn get orientation => $composableBuilder( - column: $table.orientation, builder: (column) => column); + column: $table.orientation, + builder: (column) => column, + ); i0.GeneratedColumn get timeZone => $composableBuilder(column: $table.timeZone, builder: (column) => column); @@ -366,48 +470,59 @@ class $$RemoteExifEntityTableAnnotationComposer $composableBuilder(column: $table.rating, builder: (column) => column); i0.GeneratedColumn get projectionType => $composableBuilder( - column: $table.projectionType, builder: (column) => column); + column: $table.projectionType, + builder: (column) => column, + ); i3.$$RemoteAssetEntityTableAnnotationComposer get assetId { final i3.$$RemoteAssetEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$RemoteExifEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$RemoteExifEntityTable, - i1.RemoteExifEntityData, - i1.$$RemoteExifEntityTableFilterComposer, - i1.$$RemoteExifEntityTableOrderingComposer, - i1.$$RemoteExifEntityTableAnnotationComposer, - $$RemoteExifEntityTableCreateCompanionBuilder, - $$RemoteExifEntityTableUpdateCompanionBuilder, - (i1.RemoteExifEntityData, i1.$$RemoteExifEntityTableReferences), - i1.RemoteExifEntityData, - i0.PrefetchHooks Function({bool assetId})> { +class $$RemoteExifEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$RemoteExifEntityTable, + i1.RemoteExifEntityData, + i1.$$RemoteExifEntityTableFilterComposer, + i1.$$RemoteExifEntityTableOrderingComposer, + i1.$$RemoteExifEntityTableAnnotationComposer, + $$RemoteExifEntityTableCreateCompanionBuilder, + $$RemoteExifEntityTableUpdateCompanionBuilder, + (i1.RemoteExifEntityData, i1.$$RemoteExifEntityTableReferences), + i1.RemoteExifEntityData, + i0.PrefetchHooks Function({bool assetId}) + > { $$RemoteExifEntityTableTableManager( - i0.GeneratedDatabase db, i1.$RemoteExifEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$RemoteExifEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -416,115 +531,120 @@ class $$RemoteExifEntityTableTableManager extends i0.RootTableManager< .$$RemoteExifEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$RemoteExifEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value assetId = const i0.Value.absent(), - i0.Value city = const i0.Value.absent(), - i0.Value state = const i0.Value.absent(), - i0.Value country = const i0.Value.absent(), - i0.Value dateTimeOriginal = const i0.Value.absent(), - i0.Value description = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value width = const i0.Value.absent(), - i0.Value exposureTime = const i0.Value.absent(), - i0.Value fNumber = const i0.Value.absent(), - i0.Value fileSize = const i0.Value.absent(), - i0.Value focalLength = const i0.Value.absent(), - i0.Value latitude = const i0.Value.absent(), - i0.Value longitude = const i0.Value.absent(), - i0.Value iso = const i0.Value.absent(), - i0.Value make = const i0.Value.absent(), - i0.Value model = const i0.Value.absent(), - i0.Value lens = const i0.Value.absent(), - i0.Value orientation = const i0.Value.absent(), - i0.Value timeZone = const i0.Value.absent(), - i0.Value rating = const i0.Value.absent(), - i0.Value projectionType = const i0.Value.absent(), - }) => - i1.RemoteExifEntityCompanion( - assetId: assetId, - city: city, - state: state, - country: country, - dateTimeOriginal: dateTimeOriginal, - description: description, - height: height, - width: width, - exposureTime: exposureTime, - fNumber: fNumber, - fileSize: fileSize, - focalLength: focalLength, - latitude: latitude, - longitude: longitude, - iso: iso, - make: make, - model: model, - lens: lens, - orientation: orientation, - timeZone: timeZone, - rating: rating, - projectionType: projectionType, - ), - createCompanionCallback: ({ - required String assetId, - i0.Value city = const i0.Value.absent(), - i0.Value state = const i0.Value.absent(), - i0.Value country = const i0.Value.absent(), - i0.Value dateTimeOriginal = const i0.Value.absent(), - i0.Value description = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value width = const i0.Value.absent(), - i0.Value exposureTime = const i0.Value.absent(), - i0.Value fNumber = const i0.Value.absent(), - i0.Value fileSize = const i0.Value.absent(), - i0.Value focalLength = const i0.Value.absent(), - i0.Value latitude = const i0.Value.absent(), - i0.Value longitude = const i0.Value.absent(), - i0.Value iso = const i0.Value.absent(), - i0.Value make = const i0.Value.absent(), - i0.Value model = const i0.Value.absent(), - i0.Value lens = const i0.Value.absent(), - i0.Value orientation = const i0.Value.absent(), - i0.Value timeZone = const i0.Value.absent(), - i0.Value rating = const i0.Value.absent(), - i0.Value projectionType = const i0.Value.absent(), - }) => - i1.RemoteExifEntityCompanion.insert( - assetId: assetId, - city: city, - state: state, - country: country, - dateTimeOriginal: dateTimeOriginal, - description: description, - height: height, - width: width, - exposureTime: exposureTime, - fNumber: fNumber, - fileSize: fileSize, - focalLength: focalLength, - latitude: latitude, - longitude: longitude, - iso: iso, - make: make, - model: model, - lens: lens, - orientation: orientation, - timeZone: timeZone, - rating: rating, - projectionType: projectionType, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value assetId = const i0.Value.absent(), + i0.Value city = const i0.Value.absent(), + i0.Value state = const i0.Value.absent(), + i0.Value country = const i0.Value.absent(), + i0.Value dateTimeOriginal = const i0.Value.absent(), + i0.Value description = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value width = const i0.Value.absent(), + i0.Value exposureTime = const i0.Value.absent(), + i0.Value fNumber = const i0.Value.absent(), + i0.Value fileSize = const i0.Value.absent(), + i0.Value focalLength = const i0.Value.absent(), + i0.Value latitude = const i0.Value.absent(), + i0.Value longitude = const i0.Value.absent(), + i0.Value iso = const i0.Value.absent(), + i0.Value make = const i0.Value.absent(), + i0.Value model = const i0.Value.absent(), + i0.Value lens = const i0.Value.absent(), + i0.Value orientation = const i0.Value.absent(), + i0.Value timeZone = const i0.Value.absent(), + i0.Value rating = const i0.Value.absent(), + i0.Value projectionType = const i0.Value.absent(), + }) => i1.RemoteExifEntityCompanion( + assetId: assetId, + city: city, + state: state, + country: country, + dateTimeOriginal: dateTimeOriginal, + description: description, + height: height, + width: width, + exposureTime: exposureTime, + fNumber: fNumber, + fileSize: fileSize, + focalLength: focalLength, + latitude: latitude, + longitude: longitude, + iso: iso, + make: make, + model: model, + lens: lens, + orientation: orientation, + timeZone: timeZone, + rating: rating, + projectionType: projectionType, + ), + createCompanionCallback: + ({ + required String assetId, + i0.Value city = const i0.Value.absent(), + i0.Value state = const i0.Value.absent(), + i0.Value country = const i0.Value.absent(), + i0.Value dateTimeOriginal = const i0.Value.absent(), + i0.Value description = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value width = const i0.Value.absent(), + i0.Value exposureTime = const i0.Value.absent(), + i0.Value fNumber = const i0.Value.absent(), + i0.Value fileSize = const i0.Value.absent(), + i0.Value focalLength = const i0.Value.absent(), + i0.Value latitude = const i0.Value.absent(), + i0.Value longitude = const i0.Value.absent(), + i0.Value iso = const i0.Value.absent(), + i0.Value make = const i0.Value.absent(), + i0.Value model = const i0.Value.absent(), + i0.Value lens = const i0.Value.absent(), + i0.Value orientation = const i0.Value.absent(), + i0.Value timeZone = const i0.Value.absent(), + i0.Value rating = const i0.Value.absent(), + i0.Value projectionType = const i0.Value.absent(), + }) => i1.RemoteExifEntityCompanion.insert( + assetId: assetId, + city: city, + state: state, + country: country, + dateTimeOriginal: dateTimeOriginal, + description: description, + height: height, + width: width, + exposureTime: exposureTime, + fNumber: fNumber, + fileSize: fileSize, + focalLength: focalLength, + latitude: latitude, + longitude: longitude, + iso: iso, + make: make, + model: model, + lens: lens, + orientation: orientation, + timeZone: timeZone, + rating: rating, + projectionType: projectionType, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$RemoteExifEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$RemoteExifEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({assetId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -535,41 +655,50 @@ class $$RemoteExifEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (assetId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.assetId, - referencedTable: - i1.$$RemoteExifEntityTableReferences._assetIdTable(db), - referencedColumn: i1.$$RemoteExifEntityTableReferences - ._assetIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (assetId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.assetId, + referencedTable: i1 + .$$RemoteExifEntityTableReferences + ._assetIdTable(db), + referencedColumn: i1 + .$$RemoteExifEntityTableReferences + ._assetIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$RemoteExifEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$RemoteExifEntityTable, - i1.RemoteExifEntityData, - i1.$$RemoteExifEntityTableFilterComposer, - i1.$$RemoteExifEntityTableOrderingComposer, - i1.$$RemoteExifEntityTableAnnotationComposer, - $$RemoteExifEntityTableCreateCompanionBuilder, - $$RemoteExifEntityTableUpdateCompanionBuilder, - (i1.RemoteExifEntityData, i1.$$RemoteExifEntityTableReferences), - i1.RemoteExifEntityData, - i0.PrefetchHooks Function({bool assetId})>; +typedef $$RemoteExifEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$RemoteExifEntityTable, + i1.RemoteExifEntityData, + i1.$$RemoteExifEntityTableFilterComposer, + i1.$$RemoteExifEntityTableOrderingComposer, + i1.$$RemoteExifEntityTableAnnotationComposer, + $$RemoteExifEntityTableCreateCompanionBuilder, + $$RemoteExifEntityTableUpdateCompanionBuilder, + (i1.RemoteExifEntityData, i1.$$RemoteExifEntityTableReferences), + i1.RemoteExifEntityData, + i0.PrefetchHooks Function({bool assetId}) + >; class $RemoteExifEntityTable extends i2.RemoteExifEntity with i0.TableInfo<$RemoteExifEntityTable, i1.RemoteExifEntityData> { @@ -577,165 +706,277 @@ class $RemoteExifEntityTable extends i2.RemoteExifEntity final i0.GeneratedDatabase attachedDatabase; final String? _alias; $RemoteExifEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _assetIdMeta = - const i0.VerificationMeta('assetId'); + static const i0.VerificationMeta _assetIdMeta = const i0.VerificationMeta( + 'assetId', + ); @override late final i0.GeneratedColumn assetId = i0.GeneratedColumn( - 'asset_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _cityMeta = - const i0.VerificationMeta('city'); + 'asset_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _cityMeta = const i0.VerificationMeta( + 'city', + ); @override late final i0.GeneratedColumn city = i0.GeneratedColumn( - 'city', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _stateMeta = - const i0.VerificationMeta('state'); + 'city', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _stateMeta = const i0.VerificationMeta( + 'state', + ); @override late final i0.GeneratedColumn state = i0.GeneratedColumn( - 'state', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _countryMeta = - const i0.VerificationMeta('country'); + 'state', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _countryMeta = const i0.VerificationMeta( + 'country', + ); @override late final i0.GeneratedColumn country = i0.GeneratedColumn( - 'country', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); + 'country', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _dateTimeOriginalMeta = const i0.VerificationMeta('dateTimeOriginal'); @override late final i0.GeneratedColumn dateTimeOriginal = - i0.GeneratedColumn('date_time_original', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); - static const i0.VerificationMeta _descriptionMeta = - const i0.VerificationMeta('description'); + i0.GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _descriptionMeta = const i0.VerificationMeta( + 'description', + ); @override late final i0.GeneratedColumn description = - i0.GeneratedColumn('description', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _heightMeta = - const i0.VerificationMeta('height'); + i0.GeneratedColumn( + 'description', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _heightMeta = const i0.VerificationMeta( + 'height', + ); @override late final i0.GeneratedColumn height = i0.GeneratedColumn( - 'height', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); - static const i0.VerificationMeta _widthMeta = - const i0.VerificationMeta('width'); + 'height', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _widthMeta = const i0.VerificationMeta( + 'width', + ); @override late final i0.GeneratedColumn width = i0.GeneratedColumn( - 'width', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + 'width', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _exposureTimeMeta = const i0.VerificationMeta('exposureTime'); @override late final i0.GeneratedColumn exposureTime = - i0.GeneratedColumn('exposure_time', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _fNumberMeta = - const i0.VerificationMeta('fNumber'); + i0.GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _fNumberMeta = const i0.VerificationMeta( + 'fNumber', + ); @override late final i0.GeneratedColumn fNumber = i0.GeneratedColumn( - 'f_number', aliasedName, true, - type: i0.DriftSqlType.double, requiredDuringInsert: false); - static const i0.VerificationMeta _fileSizeMeta = - const i0.VerificationMeta('fileSize'); + 'f_number', + aliasedName, + true, + type: i0.DriftSqlType.double, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _fileSizeMeta = const i0.VerificationMeta( + 'fileSize', + ); @override late final i0.GeneratedColumn fileSize = i0.GeneratedColumn( - 'file_size', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); - static const i0.VerificationMeta _focalLengthMeta = - const i0.VerificationMeta('focalLength'); + 'file_size', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _focalLengthMeta = const i0.VerificationMeta( + 'focalLength', + ); @override late final i0.GeneratedColumn focalLength = - i0.GeneratedColumn('focal_length', aliasedName, true, - type: i0.DriftSqlType.double, requiredDuringInsert: false); - static const i0.VerificationMeta _latitudeMeta = - const i0.VerificationMeta('latitude'); + i0.GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: i0.DriftSqlType.double, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _latitudeMeta = const i0.VerificationMeta( + 'latitude', + ); @override late final i0.GeneratedColumn latitude = i0.GeneratedColumn( - 'latitude', aliasedName, true, - type: i0.DriftSqlType.double, requiredDuringInsert: false); - static const i0.VerificationMeta _longitudeMeta = - const i0.VerificationMeta('longitude'); + 'latitude', + aliasedName, + true, + type: i0.DriftSqlType.double, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _longitudeMeta = const i0.VerificationMeta( + 'longitude', + ); @override late final i0.GeneratedColumn longitude = i0.GeneratedColumn( - 'longitude', aliasedName, true, - type: i0.DriftSqlType.double, requiredDuringInsert: false); + 'longitude', + aliasedName, + true, + type: i0.DriftSqlType.double, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _isoMeta = const i0.VerificationMeta('iso'); @override late final i0.GeneratedColumn iso = i0.GeneratedColumn( - 'iso', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); - static const i0.VerificationMeta _makeMeta = - const i0.VerificationMeta('make'); + 'iso', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _makeMeta = const i0.VerificationMeta( + 'make', + ); @override late final i0.GeneratedColumn make = i0.GeneratedColumn( - 'make', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _modelMeta = - const i0.VerificationMeta('model'); + 'make', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _modelMeta = const i0.VerificationMeta( + 'model', + ); @override late final i0.GeneratedColumn model = i0.GeneratedColumn( - 'model', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _lensMeta = - const i0.VerificationMeta('lens'); + 'model', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _lensMeta = const i0.VerificationMeta( + 'lens', + ); @override late final i0.GeneratedColumn lens = i0.GeneratedColumn( - 'lens', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _orientationMeta = - const i0.VerificationMeta('orientation'); + 'lens', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _orientationMeta = const i0.VerificationMeta( + 'orientation', + ); @override late final i0.GeneratedColumn orientation = - i0.GeneratedColumn('orientation', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _timeZoneMeta = - const i0.VerificationMeta('timeZone'); + i0.GeneratedColumn( + 'orientation', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _timeZoneMeta = const i0.VerificationMeta( + 'timeZone', + ); @override late final i0.GeneratedColumn timeZone = i0.GeneratedColumn( - 'time_zone', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _ratingMeta = - const i0.VerificationMeta('rating'); + 'time_zone', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _ratingMeta = const i0.VerificationMeta( + 'rating', + ); @override late final i0.GeneratedColumn rating = i0.GeneratedColumn( - 'rating', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + 'rating', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _projectionTypeMeta = const i0.VerificationMeta('projectionType'); @override late final i0.GeneratedColumn projectionType = - i0.GeneratedColumn('projection_type', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); + i0.GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]; + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -743,111 +984,162 @@ class $RemoteExifEntityTable extends i2.RemoteExifEntity static const String $name = 'remote_exif_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('asset_id')) { - context.handle(_assetIdMeta, - assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta)); + context.handle( + _assetIdMeta, + assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta), + ); } else if (isInserting) { context.missing(_assetIdMeta); } if (data.containsKey('city')) { context.handle( - _cityMeta, city.isAcceptableOrUnknown(data['city']!, _cityMeta)); + _cityMeta, + city.isAcceptableOrUnknown(data['city']!, _cityMeta), + ); } if (data.containsKey('state')) { context.handle( - _stateMeta, state.isAcceptableOrUnknown(data['state']!, _stateMeta)); + _stateMeta, + state.isAcceptableOrUnknown(data['state']!, _stateMeta), + ); } if (data.containsKey('country')) { - context.handle(_countryMeta, - country.isAcceptableOrUnknown(data['country']!, _countryMeta)); + context.handle( + _countryMeta, + country.isAcceptableOrUnknown(data['country']!, _countryMeta), + ); } if (data.containsKey('date_time_original')) { context.handle( + _dateTimeOriginalMeta, + dateTimeOriginal.isAcceptableOrUnknown( + data['date_time_original']!, _dateTimeOriginalMeta, - dateTimeOriginal.isAcceptableOrUnknown( - data['date_time_original']!, _dateTimeOriginalMeta)); + ), + ); } if (data.containsKey('description')) { context.handle( + _descriptionMeta, + description.isAcceptableOrUnknown( + data['description']!, _descriptionMeta, - description.isAcceptableOrUnknown( - data['description']!, _descriptionMeta)); + ), + ); } if (data.containsKey('height')) { - context.handle(_heightMeta, - height.isAcceptableOrUnknown(data['height']!, _heightMeta)); + context.handle( + _heightMeta, + height.isAcceptableOrUnknown(data['height']!, _heightMeta), + ); } if (data.containsKey('width')) { context.handle( - _widthMeta, width.isAcceptableOrUnknown(data['width']!, _widthMeta)); + _widthMeta, + width.isAcceptableOrUnknown(data['width']!, _widthMeta), + ); } if (data.containsKey('exposure_time')) { context.handle( + _exposureTimeMeta, + exposureTime.isAcceptableOrUnknown( + data['exposure_time']!, _exposureTimeMeta, - exposureTime.isAcceptableOrUnknown( - data['exposure_time']!, _exposureTimeMeta)); + ), + ); } if (data.containsKey('f_number')) { - context.handle(_fNumberMeta, - fNumber.isAcceptableOrUnknown(data['f_number']!, _fNumberMeta)); + context.handle( + _fNumberMeta, + fNumber.isAcceptableOrUnknown(data['f_number']!, _fNumberMeta), + ); } if (data.containsKey('file_size')) { - context.handle(_fileSizeMeta, - fileSize.isAcceptableOrUnknown(data['file_size']!, _fileSizeMeta)); + context.handle( + _fileSizeMeta, + fileSize.isAcceptableOrUnknown(data['file_size']!, _fileSizeMeta), + ); } if (data.containsKey('focal_length')) { context.handle( + _focalLengthMeta, + focalLength.isAcceptableOrUnknown( + data['focal_length']!, _focalLengthMeta, - focalLength.isAcceptableOrUnknown( - data['focal_length']!, _focalLengthMeta)); + ), + ); } if (data.containsKey('latitude')) { - context.handle(_latitudeMeta, - latitude.isAcceptableOrUnknown(data['latitude']!, _latitudeMeta)); + context.handle( + _latitudeMeta, + latitude.isAcceptableOrUnknown(data['latitude']!, _latitudeMeta), + ); } if (data.containsKey('longitude')) { - context.handle(_longitudeMeta, - longitude.isAcceptableOrUnknown(data['longitude']!, _longitudeMeta)); + context.handle( + _longitudeMeta, + longitude.isAcceptableOrUnknown(data['longitude']!, _longitudeMeta), + ); } if (data.containsKey('iso')) { context.handle( - _isoMeta, iso.isAcceptableOrUnknown(data['iso']!, _isoMeta)); + _isoMeta, + iso.isAcceptableOrUnknown(data['iso']!, _isoMeta), + ); } if (data.containsKey('make')) { context.handle( - _makeMeta, make.isAcceptableOrUnknown(data['make']!, _makeMeta)); + _makeMeta, + make.isAcceptableOrUnknown(data['make']!, _makeMeta), + ); } if (data.containsKey('model')) { context.handle( - _modelMeta, model.isAcceptableOrUnknown(data['model']!, _modelMeta)); + _modelMeta, + model.isAcceptableOrUnknown(data['model']!, _modelMeta), + ); } if (data.containsKey('lens')) { context.handle( - _lensMeta, lens.isAcceptableOrUnknown(data['lens']!, _lensMeta)); + _lensMeta, + lens.isAcceptableOrUnknown(data['lens']!, _lensMeta), + ); } if (data.containsKey('orientation')) { context.handle( + _orientationMeta, + orientation.isAcceptableOrUnknown( + data['orientation']!, _orientationMeta, - orientation.isAcceptableOrUnknown( - data['orientation']!, _orientationMeta)); + ), + ); } if (data.containsKey('time_zone')) { - context.handle(_timeZoneMeta, - timeZone.isAcceptableOrUnknown(data['time_zone']!, _timeZoneMeta)); + context.handle( + _timeZoneMeta, + timeZone.isAcceptableOrUnknown(data['time_zone']!, _timeZoneMeta), + ); } if (data.containsKey('rating')) { - context.handle(_ratingMeta, - rating.isAcceptableOrUnknown(data['rating']!, _ratingMeta)); + context.handle( + _ratingMeta, + rating.isAcceptableOrUnknown(data['rating']!, _ratingMeta), + ); } if (data.containsKey('projection_type')) { context.handle( + _projectionTypeMeta, + projectionType.isAcceptableOrUnknown( + data['projection_type']!, _projectionTypeMeta, - projectionType.isAcceptableOrUnknown( - data['projection_type']!, _projectionTypeMeta)); + ), + ); } return context; } @@ -855,55 +1147,100 @@ class $RemoteExifEntityTable extends i2.RemoteExifEntity @override Set get $primaryKey => {assetId}; @override - i1.RemoteExifEntityData map(Map data, - {String? tablePrefix}) { + i1.RemoteExifEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.RemoteExifEntityData( - assetId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - city: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}city']), - state: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}state']), - country: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}country']), + assetId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}country'], + ), dateTimeOriginal: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, - data['${effectivePrefix}date_time_original']), - description: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}description']), - height: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}height']), - width: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}width']), + i0.DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}width'], + ), exposureTime: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}exposure_time']), - fNumber: attachedDatabase.typeMapping - .read(i0.DriftSqlType.double, data['${effectivePrefix}f_number']), - fileSize: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}file_size']), - focalLength: attachedDatabase.typeMapping - .read(i0.DriftSqlType.double, data['${effectivePrefix}focal_length']), - latitude: attachedDatabase.typeMapping - .read(i0.DriftSqlType.double, data['${effectivePrefix}latitude']), - longitude: attachedDatabase.typeMapping - .read(i0.DriftSqlType.double, data['${effectivePrefix}longitude']), - iso: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}iso']), - make: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}make']), - model: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}model']), - lens: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}lens']), - orientation: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}orientation']), - timeZone: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}time_zone']), - rating: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}rating']), + i0.DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + i0.DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + i0.DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + i0.DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + i0.DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}rating'], + ), projectionType: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}projection_type']), + i0.DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), ); } @@ -942,29 +1279,30 @@ class RemoteExifEntityData extends i0.DataClass final String? timeZone; final int? rating; final String? projectionType; - const RemoteExifEntityData( - {required this.assetId, - this.city, - this.state, - this.country, - this.dateTimeOriginal, - this.description, - this.height, - this.width, - this.exposureTime, - this.fNumber, - this.fileSize, - this.focalLength, - this.latitude, - this.longitude, - this.iso, - this.make, - this.model, - this.lens, - this.orientation, - this.timeZone, - this.rating, - this.projectionType}); + const RemoteExifEntityData({ + required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1035,16 +1373,19 @@ class RemoteExifEntityData extends i0.DataClass return map; } - factory RemoteExifEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory RemoteExifEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return RemoteExifEntityData( assetId: serializer.fromJson(json['assetId']), city: serializer.fromJson(json['city']), state: serializer.fromJson(json['state']), country: serializer.fromJson(json['country']), - dateTimeOriginal: - serializer.fromJson(json['dateTimeOriginal']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), description: serializer.fromJson(json['description']), height: serializer.fromJson(json['height']), width: serializer.fromJson(json['width']), @@ -1093,57 +1434,57 @@ class RemoteExifEntityData extends i0.DataClass }; } - i1.RemoteExifEntityData copyWith( - {String? assetId, - i0.Value city = const i0.Value.absent(), - i0.Value state = const i0.Value.absent(), - i0.Value country = const i0.Value.absent(), - i0.Value dateTimeOriginal = const i0.Value.absent(), - i0.Value description = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value width = const i0.Value.absent(), - i0.Value exposureTime = const i0.Value.absent(), - i0.Value fNumber = const i0.Value.absent(), - i0.Value fileSize = const i0.Value.absent(), - i0.Value focalLength = const i0.Value.absent(), - i0.Value latitude = const i0.Value.absent(), - i0.Value longitude = const i0.Value.absent(), - i0.Value iso = const i0.Value.absent(), - i0.Value make = const i0.Value.absent(), - i0.Value model = const i0.Value.absent(), - i0.Value lens = const i0.Value.absent(), - i0.Value orientation = const i0.Value.absent(), - i0.Value timeZone = const i0.Value.absent(), - i0.Value rating = const i0.Value.absent(), - i0.Value projectionType = const i0.Value.absent()}) => - i1.RemoteExifEntityData( - assetId: assetId ?? this.assetId, - city: city.present ? city.value : this.city, - state: state.present ? state.value : this.state, - country: country.present ? country.value : this.country, - dateTimeOriginal: dateTimeOriginal.present - ? dateTimeOriginal.value - : this.dateTimeOriginal, - description: description.present ? description.value : this.description, - height: height.present ? height.value : this.height, - width: width.present ? width.value : this.width, - exposureTime: - exposureTime.present ? exposureTime.value : this.exposureTime, - fNumber: fNumber.present ? fNumber.value : this.fNumber, - fileSize: fileSize.present ? fileSize.value : this.fileSize, - focalLength: focalLength.present ? focalLength.value : this.focalLength, - latitude: latitude.present ? latitude.value : this.latitude, - longitude: longitude.present ? longitude.value : this.longitude, - iso: iso.present ? iso.value : this.iso, - make: make.present ? make.value : this.make, - model: model.present ? model.value : this.model, - lens: lens.present ? lens.value : this.lens, - orientation: orientation.present ? orientation.value : this.orientation, - timeZone: timeZone.present ? timeZone.value : this.timeZone, - rating: rating.present ? rating.value : this.rating, - projectionType: - projectionType.present ? projectionType.value : this.projectionType, - ); + i1.RemoteExifEntityData copyWith({ + String? assetId, + i0.Value city = const i0.Value.absent(), + i0.Value state = const i0.Value.absent(), + i0.Value country = const i0.Value.absent(), + i0.Value dateTimeOriginal = const i0.Value.absent(), + i0.Value description = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value width = const i0.Value.absent(), + i0.Value exposureTime = const i0.Value.absent(), + i0.Value fNumber = const i0.Value.absent(), + i0.Value fileSize = const i0.Value.absent(), + i0.Value focalLength = const i0.Value.absent(), + i0.Value latitude = const i0.Value.absent(), + i0.Value longitude = const i0.Value.absent(), + i0.Value iso = const i0.Value.absent(), + i0.Value make = const i0.Value.absent(), + i0.Value model = const i0.Value.absent(), + i0.Value lens = const i0.Value.absent(), + i0.Value orientation = const i0.Value.absent(), + i0.Value timeZone = const i0.Value.absent(), + i0.Value rating = const i0.Value.absent(), + i0.Value projectionType = const i0.Value.absent(), + }) => i1.RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); RemoteExifEntityData copyWithCompanion(i1.RemoteExifEntityCompanion data) { return RemoteExifEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, @@ -1153,8 +1494,9 @@ class RemoteExifEntityData extends i0.DataClass dateTimeOriginal: data.dateTimeOriginal.present ? data.dateTimeOriginal.value : this.dateTimeOriginal, - description: - data.description.present ? data.description.value : this.description, + description: data.description.present + ? data.description.value + : this.description, height: data.height.present ? data.height.value : this.height, width: data.width.present ? data.width.value : this.width, exposureTime: data.exposureTime.present @@ -1162,16 +1504,18 @@ class RemoteExifEntityData extends i0.DataClass : this.exposureTime, fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, - focalLength: - data.focalLength.present ? data.focalLength.value : this.focalLength, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, latitude: data.latitude.present ? data.latitude.value : this.latitude, longitude: data.longitude.present ? data.longitude.value : this.longitude, iso: data.iso.present ? data.iso.value : this.iso, make: data.make.present ? data.make.value : this.make, model: data.model.present ? data.model.value : this.model, lens: data.lens.present ? data.lens.value : this.lens, - orientation: - data.orientation.present ? data.orientation.value : this.orientation, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, rating: data.rating.present ? data.rating.value : this.rating, projectionType: data.projectionType.present @@ -1211,29 +1555,29 @@ class RemoteExifEntityData extends i0.DataClass @override int get hashCode => Object.hashAll([ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]); + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); @override bool operator ==(Object other) => identical(this, other) || @@ -1384,29 +1728,30 @@ class RemoteExifEntityCompanion }); } - i1.RemoteExifEntityCompanion copyWith( - {i0.Value? assetId, - i0.Value? city, - i0.Value? state, - i0.Value? country, - i0.Value? dateTimeOriginal, - i0.Value? description, - i0.Value? height, - i0.Value? width, - i0.Value? exposureTime, - i0.Value? fNumber, - i0.Value? fileSize, - i0.Value? focalLength, - i0.Value? latitude, - i0.Value? longitude, - i0.Value? iso, - i0.Value? make, - i0.Value? model, - i0.Value? lens, - i0.Value? orientation, - i0.Value? timeZone, - i0.Value? rating, - i0.Value? projectionType}) { + i1.RemoteExifEntityCompanion copyWith({ + i0.Value? assetId, + i0.Value? city, + i0.Value? state, + i0.Value? country, + i0.Value? dateTimeOriginal, + i0.Value? description, + i0.Value? height, + i0.Value? width, + i0.Value? exposureTime, + i0.Value? fNumber, + i0.Value? fileSize, + i0.Value? focalLength, + i0.Value? latitude, + i0.Value? longitude, + i0.Value? iso, + i0.Value? make, + i0.Value? model, + i0.Value? lens, + i0.Value? orientation, + i0.Value? timeZone, + i0.Value? rating, + i0.Value? projectionType, + }) { return i1.RemoteExifEntityCompanion( assetId: assetId ?? this.assetId, city: city ?? this.city, diff --git a/mobile/lib/infrastructure/entities/exif.entity.g.dart b/mobile/lib/infrastructure/entities/exif.entity.g.dart index 989338abff..d2f9ebda27 100644 --- a/mobile/lib/infrastructure/entities/exif.entity.g.dart +++ b/mobile/lib/infrastructure/entities/exif.entity.g.dart @@ -17,16 +17,8 @@ const ExifInfoSchema = CollectionSchema( name: r'ExifInfo', id: -2409260054350835217, properties: { - r'city': PropertySchema( - id: 0, - name: r'city', - type: IsarType.string, - ), - r'country': PropertySchema( - id: 1, - name: r'country', - type: IsarType.string, - ), + r'city': PropertySchema(id: 0, name: r'city', type: IsarType.string), + r'country': PropertySchema(id: 1, name: r'country', type: IsarType.string), r'dateTimeOriginal': PropertySchema( id: 2, name: r'dateTimeOriginal', @@ -42,67 +34,28 @@ const ExifInfoSchema = CollectionSchema( name: r'exposureSeconds', type: IsarType.float, ), - r'f': PropertySchema( - id: 5, - name: r'f', - type: IsarType.float, - ), - r'fileSize': PropertySchema( - id: 6, - name: r'fileSize', - type: IsarType.long, - ), - r'iso': PropertySchema( - id: 7, - name: r'iso', - type: IsarType.int, - ), - r'lat': PropertySchema( - id: 8, - name: r'lat', - type: IsarType.float, - ), - r'lens': PropertySchema( - id: 9, - name: r'lens', - type: IsarType.string, - ), - r'long': PropertySchema( - id: 10, - name: r'long', - type: IsarType.float, - ), - r'make': PropertySchema( - id: 11, - name: r'make', - type: IsarType.string, - ), - r'mm': PropertySchema( - id: 12, - name: r'mm', - type: IsarType.float, - ), - r'model': PropertySchema( - id: 13, - name: r'model', - type: IsarType.string, - ), + r'f': PropertySchema(id: 5, name: r'f', type: IsarType.float), + r'fileSize': PropertySchema(id: 6, name: r'fileSize', type: IsarType.long), + r'iso': PropertySchema(id: 7, name: r'iso', type: IsarType.int), + r'lat': PropertySchema(id: 8, name: r'lat', type: IsarType.float), + r'lens': PropertySchema(id: 9, name: r'lens', type: IsarType.string), + r'long': PropertySchema(id: 10, name: r'long', type: IsarType.float), + r'make': PropertySchema(id: 11, name: r'make', type: IsarType.string), + r'mm': PropertySchema(id: 12, name: r'mm', type: IsarType.float), + r'model': PropertySchema(id: 13, name: r'model', type: IsarType.string), r'orientation': PropertySchema( id: 14, name: r'orientation', type: IsarType.string, ), - r'state': PropertySchema( - id: 15, - name: r'state', - type: IsarType.string, - ), + r'state': PropertySchema(id: 15, name: r'state', type: IsarType.string), r'timeZone': PropertySchema( id: 16, name: r'timeZone', type: IsarType.string, - ) + ), }, + estimateSize: _exifInfoEstimateSize, serialize: _exifInfoSerialize, deserialize: _exifInfoDeserialize, @@ -111,6 +64,7 @@ const ExifInfoSchema = CollectionSchema( indexes: {}, links: {}, embeddedSchemas: {}, + getId: _exifInfoGetId, getLinks: _exifInfoGetLinks, attach: _exifInfoAttach, @@ -301,10 +255,7 @@ extension ExifInfoQueryWhereSort on QueryBuilder { extension ExifInfoQueryWhere on QueryBuilder { QueryBuilder idEqualTo(Id id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); + return query.addWhereClause(IdWhereClause.between(lower: id, upper: id)); }); } @@ -330,8 +281,10 @@ extension ExifInfoQueryWhere on QueryBuilder { }); } - QueryBuilder idGreaterThan(Id id, - {bool include = false}) { + QueryBuilder idGreaterThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: id, includeLower: include), @@ -339,8 +292,10 @@ extension ExifInfoQueryWhere on QueryBuilder { }); } - QueryBuilder idLessThan(Id id, - {bool include = false}) { + QueryBuilder idLessThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: id, includeUpper: include), @@ -355,12 +310,14 @@ extension ExifInfoQueryWhere on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + ), + ); }); } } @@ -369,17 +326,17 @@ extension ExifInfoQueryFilter on QueryBuilder { QueryBuilder cityIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'city', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'city'), + ); }); } QueryBuilder cityIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'city', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'city'), + ); }); } @@ -388,11 +345,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'city', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'city', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -402,12 +361,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'city', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'city', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -417,12 +378,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'city', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'city', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -434,14 +397,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'city', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'city', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -450,11 +415,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'city', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'city', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -463,69 +430,75 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'city', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'city', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder cityContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'city', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'city', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder cityMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'city', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'city', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder cityIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'city', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'city', value: ''), + ); }); } QueryBuilder cityIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'city', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'city', value: ''), + ); }); } QueryBuilder countryIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'country', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'country'), + ); }); } QueryBuilder countryIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'country', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'country'), + ); }); } @@ -534,11 +507,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'country', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'country', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -548,12 +523,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'country', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'country', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -563,12 +540,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'country', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'country', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -580,14 +559,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'country', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'country', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -596,11 +577,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'country', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'country', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -609,144 +592,149 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'country', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'country', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder countryContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'country', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'country', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder countryMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'country', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'country', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder countryIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'country', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'country', value: ''), + ); }); } QueryBuilder countryIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'country', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'country', value: ''), + ); }); } QueryBuilder - dateTimeOriginalIsNull() { + dateTimeOriginalIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'dateTimeOriginal', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'dateTimeOriginal'), + ); }); } QueryBuilder - dateTimeOriginalIsNotNull() { + dateTimeOriginalIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'dateTimeOriginal', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'dateTimeOriginal'), + ); }); } QueryBuilder - dateTimeOriginalEqualTo(DateTime? value) { + dateTimeOriginalEqualTo(DateTime? value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'dateTimeOriginal', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'dateTimeOriginal', value: value), + ); }); } QueryBuilder - dateTimeOriginalGreaterThan( - DateTime? value, { - bool include = false, - }) { + dateTimeOriginalGreaterThan(DateTime? value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'dateTimeOriginal', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'dateTimeOriginal', + value: value, + ), + ); }); } QueryBuilder - dateTimeOriginalLessThan( - DateTime? value, { - bool include = false, - }) { + dateTimeOriginalLessThan(DateTime? value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'dateTimeOriginal', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'dateTimeOriginal', + value: value, + ), + ); }); } QueryBuilder - dateTimeOriginalBetween( + dateTimeOriginalBetween( DateTime? lower, DateTime? upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'dateTimeOriginal', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'dateTimeOriginal', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder descriptionIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'description', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'description'), + ); }); } QueryBuilder - descriptionIsNotNull() { + descriptionIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'description', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'description'), + ); }); } @@ -755,27 +743,31 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - descriptionGreaterThan( + descriptionGreaterThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -785,12 +777,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -802,14 +796,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'description', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'description', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -818,11 +814,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -831,123 +829,135 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder descriptionContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder descriptionMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'description', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'description', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder descriptionIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'description', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'description', value: ''), + ); }); } QueryBuilder - descriptionIsNotEmpty() { + descriptionIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'description', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'description', value: ''), + ); }); } QueryBuilder - exposureSecondsIsNull() { + exposureSecondsIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'exposureSeconds', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'exposureSeconds'), + ); }); } QueryBuilder - exposureSecondsIsNotNull() { + exposureSecondsIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'exposureSeconds', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'exposureSeconds'), + ); }); } QueryBuilder - exposureSecondsEqualTo( - double? value, { - double epsilon = Query.epsilon, - }) { + exposureSecondsEqualTo(double? value, {double epsilon = Query.epsilon}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'exposureSeconds', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'exposureSeconds', + value: value, + + epsilon: epsilon, + ), + ); }); } QueryBuilder - exposureSecondsGreaterThan( + exposureSecondsGreaterThan( double? value, { bool include = false, double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'exposureSeconds', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'exposureSeconds', + value: value, + + epsilon: epsilon, + ), + ); }); } QueryBuilder - exposureSecondsLessThan( + exposureSecondsLessThan( double? value, { bool include = false, double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'exposureSeconds', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'exposureSeconds', + value: value, + + epsilon: epsilon, + ), + ); }); } QueryBuilder - exposureSecondsBetween( + exposureSecondsBetween( double? lower, double? upper, { bool includeLower = true, @@ -955,30 +965,33 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'exposureSeconds', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'exposureSeconds', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + + epsilon: epsilon, + ), + ); }); } QueryBuilder fIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'f', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'f'), + ); }); } QueryBuilder fIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'f', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'f'), + ); }); } @@ -987,11 +1000,9 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'f', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'f', value: value, epsilon: epsilon), + ); }); } @@ -1001,12 +1012,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'f', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'f', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1016,12 +1030,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'f', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'f', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1033,40 +1050,43 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'f', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'f', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + + epsilon: epsilon, + ), + ); }); } QueryBuilder fileSizeIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'fileSize', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'fileSize'), + ); }); } QueryBuilder fileSizeIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'fileSize', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'fileSize'), + ); }); } QueryBuilder fileSizeEqualTo( - int? value) { + int? value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'fileSize', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'fileSize', value: value), + ); }); } @@ -1075,11 +1095,13 @@ extension ExifInfoQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'fileSize', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'fileSize', + value: value, + ), + ); }); } @@ -1088,11 +1110,13 @@ extension ExifInfoQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'fileSize', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'fileSize', + value: value, + ), + ); }); } @@ -1103,38 +1127,39 @@ extension ExifInfoQueryFilter bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'fileSize', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'fileSize', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder idIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'id', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'id'), + ); }); } QueryBuilder idIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'id', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'id'), + ); }); } QueryBuilder idEqualTo(Id? value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: value), + ); }); } @@ -1143,11 +1168,13 @@ extension ExifInfoQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -1156,11 +1183,13 @@ extension ExifInfoQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -1171,39 +1200,41 @@ extension ExifInfoQueryFilter bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder isoIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'iso', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'iso'), + ); }); } QueryBuilder isoIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'iso', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'iso'), + ); }); } QueryBuilder isoEqualTo( - int? value) { + int? value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'iso', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'iso', value: value), + ); }); } @@ -1212,11 +1243,13 @@ extension ExifInfoQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'iso', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'iso', + value: value, + ), + ); }); } @@ -1225,11 +1258,13 @@ extension ExifInfoQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'iso', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'iso', + value: value, + ), + ); }); } @@ -1240,29 +1275,31 @@ extension ExifInfoQueryFilter bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'iso', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'iso', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder latIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'lat', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'lat'), + ); }); } QueryBuilder latIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'lat', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'lat'), + ); }); } @@ -1271,11 +1308,14 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'lat', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'lat', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1285,12 +1325,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'lat', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'lat', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1300,12 +1343,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'lat', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'lat', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1317,30 +1363,33 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'lat', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'lat', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + + epsilon: epsilon, + ), + ); }); } QueryBuilder lensIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'lens', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'lens'), + ); }); } QueryBuilder lensIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'lens', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'lens'), + ); }); } @@ -1349,11 +1398,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'lens', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'lens', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1363,12 +1414,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'lens', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'lens', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1378,12 +1431,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'lens', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'lens', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1395,14 +1450,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'lens', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'lens', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1411,11 +1468,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'lens', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'lens', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1424,69 +1483,75 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'lens', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'lens', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder lensContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'lens', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'lens', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder lensMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'lens', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'lens', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder lensIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'lens', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'lens', value: ''), + ); }); } QueryBuilder lensIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'lens', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'lens', value: ''), + ); }); } QueryBuilder longIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'long', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'long'), + ); }); } QueryBuilder longIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'long', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'long'), + ); }); } @@ -1495,11 +1560,14 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'long', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'long', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1509,12 +1577,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'long', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'long', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1524,12 +1595,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'long', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'long', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1541,30 +1615,33 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'long', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'long', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + + epsilon: epsilon, + ), + ); }); } QueryBuilder makeIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'make', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'make'), + ); }); } QueryBuilder makeIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'make', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'make'), + ); }); } @@ -1573,11 +1650,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'make', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'make', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1587,12 +1666,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'make', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'make', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1602,12 +1683,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'make', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'make', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1619,14 +1702,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'make', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'make', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1635,11 +1720,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'make', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'make', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1648,69 +1735,75 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'make', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'make', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder makeContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'make', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'make', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder makeMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'make', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'make', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder makeIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'make', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'make', value: ''), + ); }); } QueryBuilder makeIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'make', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'make', value: ''), + ); }); } QueryBuilder mmIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'mm', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'mm'), + ); }); } QueryBuilder mmIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'mm', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'mm'), + ); }); } @@ -1719,11 +1812,14 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'mm', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'mm', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1733,12 +1829,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'mm', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'mm', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1748,12 +1847,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'mm', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'mm', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1765,30 +1867,33 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'mm', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'mm', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + + epsilon: epsilon, + ), + ); }); } QueryBuilder modelIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'model', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'model'), + ); }); } QueryBuilder modelIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'model', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'model'), + ); }); } @@ -1797,11 +1902,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'model', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'model', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1811,12 +1918,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'model', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'model', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1826,12 +1935,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'model', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'model', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1843,14 +1954,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'model', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'model', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1859,11 +1972,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'model', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'model', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1872,70 +1987,76 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'model', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'model', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder modelContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'model', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'model', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder modelMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'model', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'model', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder modelIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'model', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'model', value: ''), + ); }); } QueryBuilder modelIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'model', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'model', value: ''), + ); }); } QueryBuilder orientationIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'orientation', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'orientation'), + ); }); } QueryBuilder - orientationIsNotNull() { + orientationIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'orientation', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'orientation'), + ); }); } @@ -1944,27 +2065,31 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'orientation', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'orientation', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - orientationGreaterThan( + orientationGreaterThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'orientation', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'orientation', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1974,12 +2099,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'orientation', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'orientation', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1991,14 +2118,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'orientation', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'orientation', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2007,11 +2136,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'orientation', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'orientation', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2020,70 +2151,76 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'orientation', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'orientation', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder orientationContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'orientation', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'orientation', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder orientationMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'orientation', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'orientation', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder orientationIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'orientation', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'orientation', value: ''), + ); }); } QueryBuilder - orientationIsNotEmpty() { + orientationIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'orientation', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'orientation', value: ''), + ); }); } QueryBuilder stateIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'state', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'state'), + ); }); } QueryBuilder stateIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'state', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'state'), + ); }); } @@ -2092,11 +2229,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'state', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'state', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2106,12 +2245,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'state', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'state', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2121,12 +2262,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'state', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'state', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2138,14 +2281,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'state', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'state', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2154,11 +2299,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'state', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'state', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2167,69 +2314,75 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'state', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'state', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stateContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'state', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'state', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stateMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'state', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'state', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stateIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'state', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'state', value: ''), + ); }); } QueryBuilder stateIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'state', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'state', value: ''), + ); }); } QueryBuilder timeZoneIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'timeZone', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'timeZone'), + ); }); } QueryBuilder timeZoneIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'timeZone', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'timeZone'), + ); }); } @@ -2238,11 +2391,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'timeZone', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'timeZone', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2252,12 +2407,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'timeZone', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'timeZone', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2267,12 +2424,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'timeZone', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'timeZone', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2284,14 +2443,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'timeZone', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'timeZone', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2300,11 +2461,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'timeZone', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'timeZone', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2313,53 +2476,59 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'timeZone', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'timeZone', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder timeZoneContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'timeZone', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'timeZone', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder timeZoneMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'timeZone', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'timeZone', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder timeZoneIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'timeZone', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'timeZone', value: ''), + ); }); } QueryBuilder timeZoneIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'timeZone', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'timeZone', value: ''), + ); }); } } @@ -2797,15 +2966,17 @@ extension ExifInfoQuerySortThenBy extension ExifInfoQueryWhereDistinct on QueryBuilder { - QueryBuilder distinctByCity( - {bool caseSensitive = true}) { + QueryBuilder distinctByCity({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'city', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByCountry( - {bool caseSensitive = true}) { + QueryBuilder distinctByCountry({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'country', caseSensitive: caseSensitive); }); @@ -2817,8 +2988,9 @@ extension ExifInfoQueryWhereDistinct }); } - QueryBuilder distinctByDescription( - {bool caseSensitive = true}) { + QueryBuilder distinctByDescription({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'description', caseSensitive: caseSensitive); }); @@ -2854,8 +3026,9 @@ extension ExifInfoQueryWhereDistinct }); } - QueryBuilder distinctByLens( - {bool caseSensitive = true}) { + QueryBuilder distinctByLens({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'lens', caseSensitive: caseSensitive); }); @@ -2867,8 +3040,9 @@ extension ExifInfoQueryWhereDistinct }); } - QueryBuilder distinctByMake( - {bool caseSensitive = true}) { + QueryBuilder distinctByMake({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'make', caseSensitive: caseSensitive); }); @@ -2880,29 +3054,33 @@ extension ExifInfoQueryWhereDistinct }); } - QueryBuilder distinctByModel( - {bool caseSensitive = true}) { + QueryBuilder distinctByModel({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'model', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByOrientation( - {bool caseSensitive = true}) { + QueryBuilder distinctByOrientation({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'orientation', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByState( - {bool caseSensitive = true}) { + QueryBuilder distinctByState({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'state', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByTimeZone( - {bool caseSensitive = true}) { + QueryBuilder distinctByTimeZone({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'timeZone', caseSensitive: caseSensitive); }); @@ -2930,7 +3108,7 @@ extension ExifInfoQueryProperty } QueryBuilder - dateTimeOriginalProperty() { + dateTimeOriginalProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'dateTimeOriginal'); }); diff --git a/mobile/lib/infrastructure/entities/local_album.entity.drift.dart b/mobile/lib/infrastructure/entities/local_album.entity.drift.dart index 06f65e25d8..5be349c8e0 100644 --- a/mobile/lib/infrastructure/entities/local_album.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/local_album.entity.drift.dart @@ -8,24 +8,24 @@ import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart' as i3; import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4; -typedef $$LocalAlbumEntityTableCreateCompanionBuilder - = i1.LocalAlbumEntityCompanion Function({ - required String id, - required String name, - i0.Value updatedAt, - required i2.BackupSelection backupSelection, - i0.Value isIosSharedAlbum, - i0.Value marker_, -}); -typedef $$LocalAlbumEntityTableUpdateCompanionBuilder - = i1.LocalAlbumEntityCompanion Function({ - i0.Value id, - i0.Value name, - i0.Value updatedAt, - i0.Value backupSelection, - i0.Value isIosSharedAlbum, - i0.Value marker_, -}); +typedef $$LocalAlbumEntityTableCreateCompanionBuilder = + i1.LocalAlbumEntityCompanion Function({ + required String id, + required String name, + i0.Value updatedAt, + required i2.BackupSelection backupSelection, + i0.Value isIosSharedAlbum, + i0.Value marker_, + }); +typedef $$LocalAlbumEntityTableUpdateCompanionBuilder = + i1.LocalAlbumEntityCompanion Function({ + i0.Value id, + i0.Value name, + i0.Value updatedAt, + i0.Value backupSelection, + i0.Value isIosSharedAlbum, + i0.Value marker_, + }); class $$LocalAlbumEntityTableFilterComposer extends i0.Composer { @@ -37,25 +37,35 @@ class $$LocalAlbumEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnFilters(column)); + column: $table.name, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnWithTypeConverterFilters - get backupSelection => $composableBuilder( - column: $table.backupSelection, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + get backupSelection => $composableBuilder( + column: $table.backupSelection, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i0.ColumnFilters get isIosSharedAlbum => $composableBuilder( - column: $table.isIosSharedAlbum, - builder: (column) => i0.ColumnFilters(column)); + column: $table.isIosSharedAlbum, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get marker_ => $composableBuilder( - column: $table.marker_, builder: (column) => i0.ColumnFilters(column)); + column: $table.marker_, + builder: (column) => i0.ColumnFilters(column), + ); } class $$LocalAlbumEntityTableOrderingComposer @@ -68,25 +78,34 @@ class $$LocalAlbumEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnOrderings(column)); + column: $table.name, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get backupSelection => $composableBuilder( - column: $table.backupSelection, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.backupSelection, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isIosSharedAlbum => $composableBuilder( - column: $table.isIosSharedAlbum, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.isIosSharedAlbum, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get marker_ => $composableBuilder( - column: $table.marker_, builder: (column) => i0.ColumnOrderings(column)); + column: $table.marker_, + builder: (column) => i0.ColumnOrderings(column), + ); } class $$LocalAlbumEntityTableAnnotationComposer @@ -108,35 +127,47 @@ class $$LocalAlbumEntityTableAnnotationComposer $composableBuilder(column: $table.updatedAt, builder: (column) => column); i0.GeneratedColumnWithTypeConverter - get backupSelection => $composableBuilder( - column: $table.backupSelection, builder: (column) => column); + get backupSelection => $composableBuilder( + column: $table.backupSelection, + builder: (column) => column, + ); i0.GeneratedColumn get isIosSharedAlbum => $composableBuilder( - column: $table.isIosSharedAlbum, builder: (column) => column); + column: $table.isIosSharedAlbum, + builder: (column) => column, + ); i0.GeneratedColumn get marker_ => $composableBuilder(column: $table.marker_, builder: (column) => column); } -class $$LocalAlbumEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$LocalAlbumEntityTable, - i1.LocalAlbumEntityData, - i1.$$LocalAlbumEntityTableFilterComposer, - i1.$$LocalAlbumEntityTableOrderingComposer, - i1.$$LocalAlbumEntityTableAnnotationComposer, - $$LocalAlbumEntityTableCreateCompanionBuilder, - $$LocalAlbumEntityTableUpdateCompanionBuilder, - ( - i1.LocalAlbumEntityData, - i0.BaseReferences - ), - i1.LocalAlbumEntityData, - i0.PrefetchHooks Function()> { +class $$LocalAlbumEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$LocalAlbumEntityTable, + i1.LocalAlbumEntityData, + i1.$$LocalAlbumEntityTableFilterComposer, + i1.$$LocalAlbumEntityTableOrderingComposer, + i1.$$LocalAlbumEntityTableAnnotationComposer, + $$LocalAlbumEntityTableCreateCompanionBuilder, + $$LocalAlbumEntityTableUpdateCompanionBuilder, + ( + i1.LocalAlbumEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$LocalAlbumEntityTable, + i1.LocalAlbumEntityData + >, + ), + i1.LocalAlbumEntityData, + i0.PrefetchHooks Function() + > { $$LocalAlbumEntityTableTableManager( - i0.GeneratedDatabase db, i1.$LocalAlbumEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$LocalAlbumEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -145,63 +176,71 @@ class $$LocalAlbumEntityTableTableManager extends i0.RootTableManager< .$$LocalAlbumEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$LocalAlbumEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value id = const i0.Value.absent(), - i0.Value name = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value backupSelection = - const i0.Value.absent(), - i0.Value isIosSharedAlbum = const i0.Value.absent(), - i0.Value marker_ = const i0.Value.absent(), - }) => - i1.LocalAlbumEntityCompanion( - id: id, - name: name, - updatedAt: updatedAt, - backupSelection: backupSelection, - isIosSharedAlbum: isIosSharedAlbum, - marker_: marker_, - ), - createCompanionCallback: ({ - required String id, - required String name, - i0.Value updatedAt = const i0.Value.absent(), - required i2.BackupSelection backupSelection, - i0.Value isIosSharedAlbum = const i0.Value.absent(), - i0.Value marker_ = const i0.Value.absent(), - }) => - i1.LocalAlbumEntityCompanion.insert( - id: id, - name: name, - updatedAt: updatedAt, - backupSelection: backupSelection, - isIosSharedAlbum: isIosSharedAlbum, - marker_: marker_, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value name = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value backupSelection = + const i0.Value.absent(), + i0.Value isIosSharedAlbum = const i0.Value.absent(), + i0.Value marker_ = const i0.Value.absent(), + }) => i1.LocalAlbumEntityCompanion( + id: id, + name: name, + updatedAt: updatedAt, + backupSelection: backupSelection, + isIosSharedAlbum: isIosSharedAlbum, + marker_: marker_, + ), + createCompanionCallback: + ({ + required String id, + required String name, + i0.Value updatedAt = const i0.Value.absent(), + required i2.BackupSelection backupSelection, + i0.Value isIosSharedAlbum = const i0.Value.absent(), + i0.Value marker_ = const i0.Value.absent(), + }) => i1.LocalAlbumEntityCompanion.insert( + id: id, + name: name, + updatedAt: updatedAt, + backupSelection: backupSelection, + isIosSharedAlbum: isIosSharedAlbum, + marker_: marker_, + ), withReferenceMapper: (p0) => p0 .map((e) => (e.readTable(table), i0.BaseReferences(db, table, e))) .toList(), prefetchHooksCallback: null, - )); + ), + ); } -typedef $$LocalAlbumEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$LocalAlbumEntityTable, - i1.LocalAlbumEntityData, - i1.$$LocalAlbumEntityTableFilterComposer, - i1.$$LocalAlbumEntityTableOrderingComposer, - i1.$$LocalAlbumEntityTableAnnotationComposer, - $$LocalAlbumEntityTableCreateCompanionBuilder, - $$LocalAlbumEntityTableUpdateCompanionBuilder, - ( +typedef $$LocalAlbumEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$LocalAlbumEntityTable, i1.LocalAlbumEntityData, - i0.BaseReferences - ), - i1.LocalAlbumEntityData, - i0.PrefetchHooks Function()>; + i1.$$LocalAlbumEntityTableFilterComposer, + i1.$$LocalAlbumEntityTableOrderingComposer, + i1.$$LocalAlbumEntityTableAnnotationComposer, + $$LocalAlbumEntityTableCreateCompanionBuilder, + $$LocalAlbumEntityTableUpdateCompanionBuilder, + ( + i1.LocalAlbumEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$LocalAlbumEntityTable, + i1.LocalAlbumEntityData + >, + ), + i1.LocalAlbumEntityData, + i0.PrefetchHooks Function() + >; class $LocalAlbumEntityTable extends i3.LocalAlbumEntity with i0.TableInfo<$LocalAlbumEntityTable, i1.LocalAlbumEntityData> { @@ -212,51 +251,86 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _nameMeta = - const i0.VerificationMeta('name'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta( + 'name', + ); @override late final i0.GeneratedColumn name = i0.GeneratedColumn( - 'name', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + 'name', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); @override late final i0.GeneratedColumnWithTypeConverter - backupSelection = i0.GeneratedColumn( - 'backup_selection', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$LocalAlbumEntityTable.$converterbackupSelection); + backupSelection = + i0.GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter( + i1.$LocalAlbumEntityTable.$converterbackupSelection, + ); static const i0.VerificationMeta _isIosSharedAlbumMeta = const i0.VerificationMeta('isIosSharedAlbum'); @override late final i0.GeneratedColumn isIosSharedAlbum = - i0.GeneratedColumn('is_ios_shared_album', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_ios_shared_album" IN (0, 1))'), - defaultValue: const i4.Constant(false)); - static const i0.VerificationMeta _marker_Meta = - const i0.VerificationMeta('marker_'); + i0.GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const i4.Constant(false), + ); + static const i0.VerificationMeta _marker_Meta = const i0.VerificationMeta( + 'marker_', + ); @override late final i0.GeneratedColumn marker_ = i0.GeneratedColumn( - 'marker', aliasedName, true, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - i0.GeneratedColumn.constraintIsAlways('CHECK ("marker" IN (0, 1))')); + 'marker', + aliasedName, + true, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); @override - List get $columns => - [id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_]; + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -264,8 +338,9 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity static const String $name = 'local_album_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { @@ -275,23 +350,32 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity } if (data.containsKey('name')) { context.handle( - _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta)); + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); } else if (isInserting) { context.missing(_nameMeta); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('is_ios_shared_album')) { context.handle( + _isIosSharedAlbumMeta, + isIosSharedAlbum.isAcceptableOrUnknown( + data['is_ios_shared_album']!, _isIosSharedAlbumMeta, - isIosSharedAlbum.isAcceptableOrUnknown( - data['is_ios_shared_album']!, _isIosSharedAlbumMeta)); + ), + ); } if (data.containsKey('marker')) { - context.handle(_marker_Meta, - marker_.isAcceptableOrUnknown(data['marker']!, _marker_Meta)); + context.handle( + _marker_Meta, + marker_.isAcceptableOrUnknown(data['marker']!, _marker_Meta), + ); } return context; } @@ -299,23 +383,39 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity @override Set get $primaryKey => {id}; @override - i1.LocalAlbumEntityData map(Map data, - {String? tablePrefix}) { + i1.LocalAlbumEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.LocalAlbumEntityData( - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}name'], + )!, updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, backupSelection: i1.$LocalAlbumEntityTable.$converterbackupSelection - .fromSql(attachedDatabase.typeMapping.read(i0.DriftSqlType.int, - data['${effectivePrefix}backup_selection'])!), + .fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + ), isIosSharedAlbum: attachedDatabase.typeMapping.read( - i0.DriftSqlType.bool, data['${effectivePrefix}is_ios_shared_album'])!, - marker_: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}marker']), + i0.DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), ); } @@ -325,9 +425,9 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity } static i0.JsonTypeConverter2 - $converterbackupSelection = - const i0.EnumIndexConverter( - i2.BackupSelection.values); + $converterbackupSelection = const i0.EnumIndexConverter( + i2.BackupSelection.values, + ); @override bool get withoutRowId => true; @override @@ -342,13 +442,14 @@ class LocalAlbumEntityData extends i0.DataClass final i2.BackupSelection backupSelection; final bool isIosSharedAlbum; final bool? marker_; - const LocalAlbumEntityData( - {required this.id, - required this.name, - required this.updatedAt, - required this.backupSelection, - required this.isIosSharedAlbum, - this.marker_}); + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -356,9 +457,11 @@ class LocalAlbumEntityData extends i0.DataClass map['name'] = i0.Variable(name); map['updated_at'] = i0.Variable(updatedAt); { - map['backup_selection'] = i0.Variable(i1 - .$LocalAlbumEntityTable.$converterbackupSelection - .toSql(backupSelection)); + map['backup_selection'] = i0.Variable( + i1.$LocalAlbumEntityTable.$converterbackupSelection.toSql( + backupSelection, + ), + ); } map['is_ios_shared_album'] = i0.Variable(isIosSharedAlbum); if (!nullToAbsent || marker_ != null) { @@ -367,8 +470,10 @@ class LocalAlbumEntityData extends i0.DataClass return map; } - factory LocalAlbumEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory LocalAlbumEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return LocalAlbumEntityData( id: serializer.fromJson(json['id']), @@ -387,29 +492,31 @@ class LocalAlbumEntityData extends i0.DataClass 'id': serializer.toJson(id), 'name': serializer.toJson(name), 'updatedAt': serializer.toJson(updatedAt), - 'backupSelection': serializer.toJson(i1 - .$LocalAlbumEntityTable.$converterbackupSelection - .toJson(backupSelection)), + 'backupSelection': serializer.toJson( + i1.$LocalAlbumEntityTable.$converterbackupSelection.toJson( + backupSelection, + ), + ), 'isIosSharedAlbum': serializer.toJson(isIosSharedAlbum), 'marker_': serializer.toJson(marker_), }; } - i1.LocalAlbumEntityData copyWith( - {String? id, - String? name, - DateTime? updatedAt, - i2.BackupSelection? backupSelection, - bool? isIosSharedAlbum, - i0.Value marker_ = const i0.Value.absent()}) => - i1.LocalAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - updatedAt: updatedAt ?? this.updatedAt, - backupSelection: backupSelection ?? this.backupSelection, - isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, - marker_: marker_.present ? marker_.value : this.marker_, - ); + i1.LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + i2.BackupSelection? backupSelection, + bool? isIosSharedAlbum, + i0.Value marker_ = const i0.Value.absent(), + }) => i1.LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); LocalAlbumEntityData copyWithCompanion(i1.LocalAlbumEntityCompanion data) { return LocalAlbumEntityData( id: data.id.present ? data.id.value : this.id, @@ -440,7 +547,13 @@ class LocalAlbumEntityData extends i0.DataClass @override int get hashCode => Object.hash( - id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_); + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -476,9 +589,9 @@ class LocalAlbumEntityCompanion required i2.BackupSelection backupSelection, this.isIosSharedAlbum = const i0.Value.absent(), this.marker_ = const i0.Value.absent(), - }) : id = i0.Value(id), - name = i0.Value(name), - backupSelection = i0.Value(backupSelection); + }) : id = i0.Value(id), + name = i0.Value(name), + backupSelection = i0.Value(backupSelection); static i0.Insertable custom({ i0.Expression? id, i0.Expression? name, @@ -497,13 +610,14 @@ class LocalAlbumEntityCompanion }); } - i1.LocalAlbumEntityCompanion copyWith( - {i0.Value? id, - i0.Value? name, - i0.Value? updatedAt, - i0.Value? backupSelection, - i0.Value? isIosSharedAlbum, - i0.Value? marker_}) { + i1.LocalAlbumEntityCompanion copyWith({ + i0.Value? id, + i0.Value? name, + i0.Value? updatedAt, + i0.Value? backupSelection, + i0.Value? isIosSharedAlbum, + i0.Value? marker_, + }) { return i1.LocalAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -527,9 +641,11 @@ class LocalAlbumEntityCompanion map['updated_at'] = i0.Variable(updatedAt.value); } if (backupSelection.present) { - map['backup_selection'] = i0.Variable(i1 - .$LocalAlbumEntityTable.$converterbackupSelection - .toSql(backupSelection.value)); + map['backup_selection'] = i0.Variable( + i1.$LocalAlbumEntityTable.$converterbackupSelection.toSql( + backupSelection.value, + ), + ); } if (isIosSharedAlbum.present) { map['is_ios_shared_album'] = i0.Variable(isIosSharedAlbum.value); diff --git a/mobile/lib/infrastructure/entities/local_album_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/local_album_asset.entity.drift.dart index e8f94fa74b..78da361f62 100644 --- a/mobile/lib/infrastructure/entities/local_album_asset.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/local_album_asset.entity.drift.dart @@ -11,76 +11,96 @@ import 'package:drift/internal/modular.dart' as i4; import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart' as i5; -typedef $$LocalAlbumAssetEntityTableCreateCompanionBuilder - = i1.LocalAlbumAssetEntityCompanion Function({ - required String assetId, - required String albumId, -}); -typedef $$LocalAlbumAssetEntityTableUpdateCompanionBuilder - = i1.LocalAlbumAssetEntityCompanion Function({ - i0.Value assetId, - i0.Value albumId, -}); +typedef $$LocalAlbumAssetEntityTableCreateCompanionBuilder = + i1.LocalAlbumAssetEntityCompanion Function({ + required String assetId, + required String albumId, + }); +typedef $$LocalAlbumAssetEntityTableUpdateCompanionBuilder = + i1.LocalAlbumAssetEntityCompanion Function({ + i0.Value assetId, + i0.Value albumId, + }); -final class $$LocalAlbumAssetEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, - i1.$LocalAlbumAssetEntityTable, - i1.LocalAlbumAssetEntityData> { +final class $$LocalAlbumAssetEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$LocalAlbumAssetEntityTable, + i1.LocalAlbumAssetEntityData + > { $$LocalAlbumAssetEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i3.$LocalAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('local_asset_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet( - 'local_album_asset_entity') + 'local_album_asset_entity', + ) .assetId, - i4.ReadDatabaseContainer(db) - .resultSet('local_asset_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('local_asset_entity').id, + ), + ); i3.$$LocalAssetEntityTableProcessedTableManager get assetId { final $_column = $_itemColumn('asset_id')!; final manager = i3 .$$LocalAssetEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('local_asset_entity')) + ).resultSet('local_asset_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_assetIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } static i5.$LocalAlbumEntityTable _albumIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('local_album_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet( - 'local_album_asset_entity') + 'local_album_asset_entity', + ) .albumId, - i4.ReadDatabaseContainer(db) - .resultSet('local_album_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('local_album_entity').id, + ), + ); i5.$$LocalAlbumEntityTableProcessedTableManager get albumId { final $_column = $_itemColumn('album_id')!; final manager = i5 .$$LocalAlbumEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('local_album_entity')) + ).resultSet('local_album_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_albumIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -95,45 +115,55 @@ class $$LocalAlbumAssetEntityTableFilterComposer }); i3.$$LocalAssetEntityTableFilterComposer get assetId { final i3.$$LocalAssetEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('local_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$LocalAssetEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('local_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$LocalAssetEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$LocalAlbumEntityTableFilterComposer get albumId { final i5.$$LocalAlbumEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('local_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$LocalAlbumEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('local_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$LocalAlbumEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -150,48 +180,56 @@ class $$LocalAlbumAssetEntityTableOrderingComposer i3.$$LocalAssetEntityTableOrderingComposer get assetId { final i3.$$LocalAssetEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('local_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$LocalAssetEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'local_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$LocalAssetEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$LocalAlbumEntityTableOrderingComposer get albumId { final i5.$$LocalAlbumEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('local_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$LocalAlbumEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'local_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$LocalAlbumEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -208,106 +246,129 @@ class $$LocalAlbumAssetEntityTableAnnotationComposer i3.$$LocalAssetEntityTableAnnotationComposer get assetId { final i3.$$LocalAssetEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('local_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$LocalAssetEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'local_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$LocalAssetEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$LocalAlbumEntityTableAnnotationComposer get albumId { final i5.$$LocalAlbumEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('local_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$LocalAlbumEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'local_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$LocalAlbumEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$LocalAlbumAssetEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$LocalAlbumAssetEntityTable, - i1.LocalAlbumAssetEntityData, - i1.$$LocalAlbumAssetEntityTableFilterComposer, - i1.$$LocalAlbumAssetEntityTableOrderingComposer, - i1.$$LocalAlbumAssetEntityTableAnnotationComposer, - $$LocalAlbumAssetEntityTableCreateCompanionBuilder, - $$LocalAlbumAssetEntityTableUpdateCompanionBuilder, - (i1.LocalAlbumAssetEntityData, i1.$$LocalAlbumAssetEntityTableReferences), - i1.LocalAlbumAssetEntityData, - i0.PrefetchHooks Function({bool assetId, bool albumId})> { +class $$LocalAlbumAssetEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$LocalAlbumAssetEntityTable, + i1.LocalAlbumAssetEntityData, + i1.$$LocalAlbumAssetEntityTableFilterComposer, + i1.$$LocalAlbumAssetEntityTableOrderingComposer, + i1.$$LocalAlbumAssetEntityTableAnnotationComposer, + $$LocalAlbumAssetEntityTableCreateCompanionBuilder, + $$LocalAlbumAssetEntityTableUpdateCompanionBuilder, + ( + i1.LocalAlbumAssetEntityData, + i1.$$LocalAlbumAssetEntityTableReferences, + ), + i1.LocalAlbumAssetEntityData, + i0.PrefetchHooks Function({bool assetId, bool albumId}) + > { $$LocalAlbumAssetEntityTableTableManager( - i0.GeneratedDatabase db, i1.$LocalAlbumAssetEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$LocalAlbumAssetEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => i1.$$LocalAlbumAssetEntityTableFilterComposer( - $db: db, $table: table), + $db: db, + $table: table, + ), createOrderingComposer: () => i1.$$LocalAlbumAssetEntityTableOrderingComposer( - $db: db, $table: table), + $db: db, + $table: table, + ), createComputedFieldComposer: () => i1.$$LocalAlbumAssetEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value assetId = const i0.Value.absent(), - i0.Value albumId = const i0.Value.absent(), - }) => - i1.LocalAlbumAssetEntityCompanion( - assetId: assetId, - albumId: albumId, - ), - createCompanionCallback: ({ - required String assetId, - required String albumId, - }) => - i1.LocalAlbumAssetEntityCompanion.insert( - assetId: assetId, - albumId: albumId, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value assetId = const i0.Value.absent(), + i0.Value albumId = const i0.Value.absent(), + }) => i1.LocalAlbumAssetEntityCompanion( + assetId: assetId, + albumId: albumId, + ), + createCompanionCallback: + ({required String assetId, required String albumId}) => + i1.LocalAlbumAssetEntityCompanion.insert( + assetId: assetId, + albumId: albumId, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$LocalAlbumAssetEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$LocalAlbumAssetEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({assetId = false, albumId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -318,83 +379,104 @@ class $$LocalAlbumAssetEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (assetId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.assetId, - referencedTable: i1.$$LocalAlbumAssetEntityTableReferences - ._assetIdTable(db), - referencedColumn: i1.$$LocalAlbumAssetEntityTableReferences - ._assetIdTable(db) - .id, - ) as T; - } - if (albumId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.albumId, - referencedTable: i1.$$LocalAlbumAssetEntityTableReferences - ._albumIdTable(db), - referencedColumn: i1.$$LocalAlbumAssetEntityTableReferences - ._albumIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (assetId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.assetId, + referencedTable: i1 + .$$LocalAlbumAssetEntityTableReferences + ._assetIdTable(db), + referencedColumn: i1 + .$$LocalAlbumAssetEntityTableReferences + ._assetIdTable(db) + .id, + ) + as T; + } + if (albumId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.albumId, + referencedTable: i1 + .$$LocalAlbumAssetEntityTableReferences + ._albumIdTable(db), + referencedColumn: i1 + .$$LocalAlbumAssetEntityTableReferences + ._albumIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$LocalAlbumAssetEntityTableProcessedTableManager - = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$LocalAlbumAssetEntityTable, - i1.LocalAlbumAssetEntityData, - i1.$$LocalAlbumAssetEntityTableFilterComposer, - i1.$$LocalAlbumAssetEntityTableOrderingComposer, - i1.$$LocalAlbumAssetEntityTableAnnotationComposer, - $$LocalAlbumAssetEntityTableCreateCompanionBuilder, - $$LocalAlbumAssetEntityTableUpdateCompanionBuilder, - ( - i1.LocalAlbumAssetEntityData, - i1.$$LocalAlbumAssetEntityTableReferences - ), - i1.LocalAlbumAssetEntityData, - i0.PrefetchHooks Function({bool assetId, bool albumId})>; +typedef $$LocalAlbumAssetEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$LocalAlbumAssetEntityTable, + i1.LocalAlbumAssetEntityData, + i1.$$LocalAlbumAssetEntityTableFilterComposer, + i1.$$LocalAlbumAssetEntityTableOrderingComposer, + i1.$$LocalAlbumAssetEntityTableAnnotationComposer, + $$LocalAlbumAssetEntityTableCreateCompanionBuilder, + $$LocalAlbumAssetEntityTableUpdateCompanionBuilder, + (i1.LocalAlbumAssetEntityData, i1.$$LocalAlbumAssetEntityTableReferences), + i1.LocalAlbumAssetEntityData, + i0.PrefetchHooks Function({bool assetId, bool albumId}) + >; class $LocalAlbumAssetEntityTable extends i2.LocalAlbumAssetEntity with - i0 - .TableInfo<$LocalAlbumAssetEntityTable, i1.LocalAlbumAssetEntityData> { + i0.TableInfo< + $LocalAlbumAssetEntityTable, + i1.LocalAlbumAssetEntityData + > { @override final i0.GeneratedDatabase attachedDatabase; final String? _alias; $LocalAlbumAssetEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _assetIdMeta = - const i0.VerificationMeta('assetId'); + static const i0.VerificationMeta _assetIdMeta = const i0.VerificationMeta( + 'assetId', + ); @override late final i0.GeneratedColumn assetId = i0.GeneratedColumn( - 'asset_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES local_asset_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _albumIdMeta = - const i0.VerificationMeta('albumId'); + 'asset_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _albumIdMeta = const i0.VerificationMeta( + 'albumId', + ); @override late final i0.GeneratedColumn albumId = i0.GeneratedColumn( - 'album_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES local_album_entity (id) ON DELETE CASCADE')); + 'album_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -404,19 +486,24 @@ class $LocalAlbumAssetEntityTable extends i2.LocalAlbumAssetEntity static const String $name = 'local_album_asset_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('asset_id')) { - context.handle(_assetIdMeta, - assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta)); + context.handle( + _assetIdMeta, + assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta), + ); } else if (isInserting) { context.missing(_assetIdMeta); } if (data.containsKey('album_id')) { - context.handle(_albumIdMeta, - albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta)); + context.handle( + _albumIdMeta, + albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta), + ); } else if (isInserting) { context.missing(_albumIdMeta); } @@ -426,14 +513,20 @@ class $LocalAlbumAssetEntityTable extends i2.LocalAlbumAssetEntity @override Set get $primaryKey => {assetId, albumId}; @override - i1.LocalAlbumAssetEntityData map(Map data, - {String? tablePrefix}) { + i1.LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.LocalAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -452,8 +545,10 @@ class LocalAlbumAssetEntityData extends i0.DataClass implements i0.Insertable { final String assetId; final String albumId; - const LocalAlbumAssetEntityData( - {required this.assetId, required this.albumId}); + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -462,8 +557,10 @@ class LocalAlbumAssetEntityData extends i0.DataClass return map; } - factory LocalAlbumAssetEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return LocalAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -485,7 +582,8 @@ class LocalAlbumAssetEntityData extends i0.DataClass albumId: albumId ?? this.albumId, ); LocalAlbumAssetEntityData copyWithCompanion( - i1.LocalAlbumAssetEntityCompanion data) { + i1.LocalAlbumAssetEntityCompanion data, + ) { return LocalAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -522,8 +620,8 @@ class LocalAlbumAssetEntityCompanion LocalAlbumAssetEntityCompanion.insert({ required String assetId, required String albumId, - }) : assetId = i0.Value(assetId), - albumId = i0.Value(albumId); + }) : assetId = i0.Value(assetId), + albumId = i0.Value(albumId); static i0.Insertable custom({ i0.Expression? assetId, i0.Expression? albumId, @@ -534,8 +632,10 @@ class LocalAlbumAssetEntityCompanion }); } - i1.LocalAlbumAssetEntityCompanion copyWith( - {i0.Value? assetId, i0.Value? albumId}) { + i1.LocalAlbumAssetEntityCompanion copyWith({ + i0.Value? assetId, + i0.Value? albumId, + }) { return i1.LocalAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, diff --git a/mobile/lib/infrastructure/entities/local_asset.entity.dart b/mobile/lib/infrastructure/entities/local_asset.entity.dart index 204d5d6a80..e5519cfacf 100644 --- a/mobile/lib/infrastructure/entities/local_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/local_asset.entity.dart @@ -22,17 +22,17 @@ class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin { extension LocalAssetEntityDataDomainEx on LocalAssetEntityData { LocalAsset toDto() => LocalAsset( - id: id, - name: name, - checksum: checksum, - type: type, - createdAt: createdAt, - updatedAt: updatedAt, - durationInSeconds: durationInSeconds, - isFavorite: isFavorite, - height: height, - width: width, - remoteId: null, - orientation: orientation, - ); + id: id, + name: name, + checksum: checksum, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + durationInSeconds: durationInSeconds, + isFavorite: isFavorite, + height: height, + width: width, + remoteId: null, + orientation: orientation, + ); } diff --git a/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart index e9c5961aa5..329401d5db 100644 --- a/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart @@ -8,34 +8,34 @@ import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart' as i3; import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4; -typedef $$LocalAssetEntityTableCreateCompanionBuilder - = i1.LocalAssetEntityCompanion Function({ - required String name, - required i2.AssetType type, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value width, - i0.Value height, - i0.Value durationInSeconds, - required String id, - i0.Value checksum, - i0.Value isFavorite, - i0.Value orientation, -}); -typedef $$LocalAssetEntityTableUpdateCompanionBuilder - = i1.LocalAssetEntityCompanion Function({ - i0.Value name, - i0.Value type, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value width, - i0.Value height, - i0.Value durationInSeconds, - i0.Value id, - i0.Value checksum, - i0.Value isFavorite, - i0.Value orientation, -}); +typedef $$LocalAssetEntityTableCreateCompanionBuilder = + i1.LocalAssetEntityCompanion Function({ + required String name, + required i2.AssetType type, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value width, + i0.Value height, + i0.Value durationInSeconds, + required String id, + i0.Value checksum, + i0.Value isFavorite, + i0.Value orientation, + }); +typedef $$LocalAssetEntityTableUpdateCompanionBuilder = + i1.LocalAssetEntityCompanion Function({ + i0.Value name, + i0.Value type, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value width, + i0.Value height, + i0.Value durationInSeconds, + i0.Value id, + i0.Value checksum, + i0.Value isFavorite, + i0.Value orientation, + }); class $$LocalAssetEntityTableFilterComposer extends i0.Composer { @@ -47,41 +47,60 @@ class $$LocalAssetEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnFilters(column)); + column: $table.name, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnWithTypeConverterFilters get type => $composableBuilder( - column: $table.type, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + column: $table.type, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i0.ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get width => $composableBuilder( - column: $table.width, builder: (column) => i0.ColumnFilters(column)); + column: $table.width, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get height => $composableBuilder( - column: $table.height, builder: (column) => i0.ColumnFilters(column)); + column: $table.height, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get durationInSeconds => $composableBuilder( - column: $table.durationInSeconds, - builder: (column) => i0.ColumnFilters(column)); + column: $table.durationInSeconds, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get checksum => $composableBuilder( - column: $table.checksum, builder: (column) => i0.ColumnFilters(column)); + column: $table.checksum, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get isFavorite => $composableBuilder( - column: $table.isFavorite, builder: (column) => i0.ColumnFilters(column)); + column: $table.isFavorite, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get orientation => $composableBuilder( - column: $table.orientation, - builder: (column) => i0.ColumnFilters(column)); + column: $table.orientation, + builder: (column) => i0.ColumnFilters(column), + ); } class $$LocalAssetEntityTableOrderingComposer @@ -94,42 +113,59 @@ class $$LocalAssetEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnOrderings(column)); + column: $table.name, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get type => $composableBuilder( - column: $table.type, builder: (column) => i0.ColumnOrderings(column)); + column: $table.type, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get width => $composableBuilder( - column: $table.width, builder: (column) => i0.ColumnOrderings(column)); + column: $table.width, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get height => $composableBuilder( - column: $table.height, builder: (column) => i0.ColumnOrderings(column)); + column: $table.height, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get durationInSeconds => $composableBuilder( - column: $table.durationInSeconds, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.durationInSeconds, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get checksum => $composableBuilder( - column: $table.checksum, builder: (column) => i0.ColumnOrderings(column)); + column: $table.checksum, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isFavorite => $composableBuilder( - column: $table.isFavorite, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.isFavorite, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get orientation => $composableBuilder( - column: $table.orientation, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.orientation, + builder: (column) => i0.ColumnOrderings(column), + ); } class $$LocalAssetEntityTableAnnotationComposer @@ -160,7 +196,9 @@ class $$LocalAssetEntityTableAnnotationComposer $composableBuilder(column: $table.height, builder: (column) => column); i0.GeneratedColumn get durationInSeconds => $composableBuilder( - column: $table.durationInSeconds, builder: (column) => column); + column: $table.durationInSeconds, + builder: (column) => column, + ); i0.GeneratedColumn get id => $composableBuilder(column: $table.id, builder: (column) => column); @@ -169,31 +207,43 @@ class $$LocalAssetEntityTableAnnotationComposer $composableBuilder(column: $table.checksum, builder: (column) => column); i0.GeneratedColumn get isFavorite => $composableBuilder( - column: $table.isFavorite, builder: (column) => column); + column: $table.isFavorite, + builder: (column) => column, + ); i0.GeneratedColumn get orientation => $composableBuilder( - column: $table.orientation, builder: (column) => column); + column: $table.orientation, + builder: (column) => column, + ); } -class $$LocalAssetEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$LocalAssetEntityTable, - i1.LocalAssetEntityData, - i1.$$LocalAssetEntityTableFilterComposer, - i1.$$LocalAssetEntityTableOrderingComposer, - i1.$$LocalAssetEntityTableAnnotationComposer, - $$LocalAssetEntityTableCreateCompanionBuilder, - $$LocalAssetEntityTableUpdateCompanionBuilder, - ( - i1.LocalAssetEntityData, - i0.BaseReferences - ), - i1.LocalAssetEntityData, - i0.PrefetchHooks Function()> { +class $$LocalAssetEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$LocalAssetEntityTable, + i1.LocalAssetEntityData, + i1.$$LocalAssetEntityTableFilterComposer, + i1.$$LocalAssetEntityTableOrderingComposer, + i1.$$LocalAssetEntityTableAnnotationComposer, + $$LocalAssetEntityTableCreateCompanionBuilder, + $$LocalAssetEntityTableUpdateCompanionBuilder, + ( + i1.LocalAssetEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$LocalAssetEntityTable, + i1.LocalAssetEntityData + >, + ), + i1.LocalAssetEntityData, + i0.PrefetchHooks Function() + > { $$LocalAssetEntityTableTableManager( - i0.GeneratedDatabase db, i1.$LocalAssetEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$LocalAssetEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -202,84 +252,94 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager< .$$LocalAssetEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$LocalAssetEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value name = const i0.Value.absent(), - i0.Value type = const i0.Value.absent(), - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value width = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value durationInSeconds = const i0.Value.absent(), - i0.Value id = const i0.Value.absent(), - i0.Value checksum = const i0.Value.absent(), - i0.Value isFavorite = const i0.Value.absent(), - i0.Value orientation = const i0.Value.absent(), - }) => - i1.LocalAssetEntityCompanion( - name: name, - type: type, - createdAt: createdAt, - updatedAt: updatedAt, - width: width, - height: height, - durationInSeconds: durationInSeconds, - id: id, - checksum: checksum, - isFavorite: isFavorite, - orientation: orientation, - ), - createCompanionCallback: ({ - required String name, - required i2.AssetType type, - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value width = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value durationInSeconds = const i0.Value.absent(), - required String id, - i0.Value checksum = const i0.Value.absent(), - i0.Value isFavorite = const i0.Value.absent(), - i0.Value orientation = const i0.Value.absent(), - }) => - i1.LocalAssetEntityCompanion.insert( - name: name, - type: type, - createdAt: createdAt, - updatedAt: updatedAt, - width: width, - height: height, - durationInSeconds: durationInSeconds, - id: id, - checksum: checksum, - isFavorite: isFavorite, - orientation: orientation, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value name = const i0.Value.absent(), + i0.Value type = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value width = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value durationInSeconds = const i0.Value.absent(), + i0.Value id = const i0.Value.absent(), + i0.Value checksum = const i0.Value.absent(), + i0.Value isFavorite = const i0.Value.absent(), + i0.Value orientation = const i0.Value.absent(), + }) => i1.LocalAssetEntityCompanion( + name: name, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + width: width, + height: height, + durationInSeconds: durationInSeconds, + id: id, + checksum: checksum, + isFavorite: isFavorite, + orientation: orientation, + ), + createCompanionCallback: + ({ + required String name, + required i2.AssetType type, + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value width = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value durationInSeconds = const i0.Value.absent(), + required String id, + i0.Value checksum = const i0.Value.absent(), + i0.Value isFavorite = const i0.Value.absent(), + i0.Value orientation = const i0.Value.absent(), + }) => i1.LocalAssetEntityCompanion.insert( + name: name, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + width: width, + height: height, + durationInSeconds: durationInSeconds, + id: id, + checksum: checksum, + isFavorite: isFavorite, + orientation: orientation, + ), withReferenceMapper: (p0) => p0 .map((e) => (e.readTable(table), i0.BaseReferences(db, table, e))) .toList(), prefetchHooksCallback: null, - )); + ), + ); } -typedef $$LocalAssetEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$LocalAssetEntityTable, - i1.LocalAssetEntityData, - i1.$$LocalAssetEntityTableFilterComposer, - i1.$$LocalAssetEntityTableOrderingComposer, - i1.$$LocalAssetEntityTableAnnotationComposer, - $$LocalAssetEntityTableCreateCompanionBuilder, - $$LocalAssetEntityTableUpdateCompanionBuilder, - ( +typedef $$LocalAssetEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$LocalAssetEntityTable, i1.LocalAssetEntityData, - i0.BaseReferences - ), - i1.LocalAssetEntityData, - i0.PrefetchHooks Function()>; -i0.Index get idxLocalAssetChecksum => i0.Index('idx_local_asset_checksum', - 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); + i1.$$LocalAssetEntityTableFilterComposer, + i1.$$LocalAssetEntityTableOrderingComposer, + i1.$$LocalAssetEntityTableAnnotationComposer, + $$LocalAssetEntityTableCreateCompanionBuilder, + $$LocalAssetEntityTableUpdateCompanionBuilder, + ( + i1.LocalAssetEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$LocalAssetEntityTable, + i1.LocalAssetEntityData + >, + ), + i1.LocalAssetEntityData, + i0.PrefetchHooks Function() + >; +i0.Index get idxLocalAssetChecksum => i0.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', +); class $LocalAssetEntityTable extends i3.LocalAssetEntity with i0.TableInfo<$LocalAssetEntityTable, i1.LocalAssetEntityData> { @@ -287,95 +347,146 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity final i0.GeneratedDatabase attachedDatabase; final String? _alias; $LocalAssetEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _nameMeta = - const i0.VerificationMeta('name'); + static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta( + 'name', + ); @override late final i0.GeneratedColumn name = i0.GeneratedColumn( - 'name', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); @override late final i0.GeneratedColumnWithTypeConverter type = - i0.GeneratedColumn('type', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$LocalAssetEntityTable.$convertertype); - static const i0.VerificationMeta _createdAtMeta = - const i0.VerificationMeta('createdAt'); + i0.GeneratedColumn( + 'type', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter(i1.$LocalAssetEntityTable.$convertertype); + static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta( + 'createdAt', + ); @override late final i0.GeneratedColumn createdAt = - i0.GeneratedColumn('created_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + i0.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _widthMeta = - const i0.VerificationMeta('width'); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _widthMeta = const i0.VerificationMeta( + 'width', + ); @override late final i0.GeneratedColumn width = i0.GeneratedColumn( - 'width', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); - static const i0.VerificationMeta _heightMeta = - const i0.VerificationMeta('height'); + 'width', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _heightMeta = const i0.VerificationMeta( + 'height', + ); @override late final i0.GeneratedColumn height = i0.GeneratedColumn( - 'height', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + 'height', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _durationInSecondsMeta = const i0.VerificationMeta('durationInSeconds'); @override late final i0.GeneratedColumn durationInSeconds = - i0.GeneratedColumn('duration_in_seconds', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + i0.GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _checksumMeta = - const i0.VerificationMeta('checksum'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _checksumMeta = const i0.VerificationMeta( + 'checksum', + ); @override late final i0.GeneratedColumn checksum = i0.GeneratedColumn( - 'checksum', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _isFavoriteMeta = - const i0.VerificationMeta('isFavorite'); + 'checksum', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _isFavoriteMeta = const i0.VerificationMeta( + 'isFavorite', + ); @override late final i0.GeneratedColumn isFavorite = i0.GeneratedColumn( - 'is_favorite', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const i4.Constant(false)); - static const i0.VerificationMeta _orientationMeta = - const i0.VerificationMeta('orientation'); + 'is_favorite', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const i4.Constant(false), + ); + static const i0.VerificationMeta _orientationMeta = const i0.VerificationMeta( + 'orientation', + ); @override late final i0.GeneratedColumn orientation = i0.GeneratedColumn( - 'orientation', aliasedName, false, - type: i0.DriftSqlType.int, - requiredDuringInsert: false, - defaultValue: const i4.Constant(0)); + 'orientation', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const i4.Constant(0), + ); @override List get $columns => [ - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - orientation - ]; + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -383,37 +494,51 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity static const String $name = 'local_asset_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('name')) { context.handle( - _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta)); + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); } else if (isInserting) { context.missing(_nameMeta); } if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('width')) { context.handle( - _widthMeta, width.isAcceptableOrUnknown(data['width']!, _widthMeta)); + _widthMeta, + width.isAcceptableOrUnknown(data['width']!, _widthMeta), + ); } if (data.containsKey('height')) { - context.handle(_heightMeta, - height.isAcceptableOrUnknown(data['height']!, _heightMeta)); + context.handle( + _heightMeta, + height.isAcceptableOrUnknown(data['height']!, _heightMeta), + ); } if (data.containsKey('duration_in_seconds')) { context.handle( + _durationInSecondsMeta, + durationInSeconds.isAcceptableOrUnknown( + data['duration_in_seconds']!, _durationInSecondsMeta, - durationInSeconds.isAcceptableOrUnknown( - data['duration_in_seconds']!, _durationInSecondsMeta)); + ), + ); } if (data.containsKey('id')) { context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); @@ -421,20 +546,25 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity context.missing(_idMeta); } if (data.containsKey('checksum')) { - context.handle(_checksumMeta, - checksum.isAcceptableOrUnknown(data['checksum']!, _checksumMeta)); + context.handle( + _checksumMeta, + checksum.isAcceptableOrUnknown(data['checksum']!, _checksumMeta), + ); } if (data.containsKey('is_favorite')) { context.handle( - _isFavoriteMeta, - isFavorite.isAcceptableOrUnknown( - data['is_favorite']!, _isFavoriteMeta)); + _isFavoriteMeta, + isFavorite.isAcceptableOrUnknown(data['is_favorite']!, _isFavoriteMeta), + ); } if (data.containsKey('orientation')) { context.handle( + _orientationMeta, + orientation.isAcceptableOrUnknown( + data['orientation']!, _orientationMeta, - orientation.isAcceptableOrUnknown( - data['orientation']!, _orientationMeta)); + ), + ); } return context; } @@ -442,33 +572,58 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity @override Set get $primaryKey => {id}; @override - i1.LocalAssetEntityData map(Map data, - {String? tablePrefix}) { + i1.LocalAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.LocalAssetEntityData( - name: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, - type: i1.$LocalAssetEntityTable.$convertertype.fromSql(attachedDatabase - .typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}type'])!), + name: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: i1.$LocalAssetEntityTable.$convertertype.fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + ), createdAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}height']), + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}height'], + ), durationInSeconds: attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}checksum']), - isFavorite: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - orientation: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}orientation'])!, + i0.DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, ); } @@ -498,25 +653,27 @@ class LocalAssetEntityData extends i0.DataClass final String? checksum; final bool isFavorite; final int orientation; - const LocalAssetEntityData( - {required this.name, - required this.type, - required this.createdAt, - required this.updatedAt, - this.width, - this.height, - this.durationInSeconds, - required this.id, - this.checksum, - required this.isFavorite, - required this.orientation}); + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; map['name'] = i0.Variable(name); { map['type'] = i0.Variable( - i1.$LocalAssetEntityTable.$convertertype.toSql(type)); + i1.$LocalAssetEntityTable.$convertertype.toSql(type), + ); } map['created_at'] = i0.Variable(createdAt); map['updated_at'] = i0.Variable(updatedAt); @@ -538,13 +695,16 @@ class LocalAssetEntityData extends i0.DataClass return map; } - factory LocalAssetEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory LocalAssetEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return LocalAssetEntityData( name: serializer.fromJson(json['name']), - type: i1.$LocalAssetEntityTable.$convertertype - .fromJson(serializer.fromJson(json['type'])), + type: i1.$LocalAssetEntityTable.$convertertype.fromJson( + serializer.fromJson(json['type']), + ), createdAt: serializer.fromJson(json['createdAt']), updatedAt: serializer.fromJson(json['updatedAt']), width: serializer.fromJson(json['width']), @@ -561,8 +721,9 @@ class LocalAssetEntityData extends i0.DataClass serializer ??= i0.driftRuntimeOptions.defaultSerializer; return { 'name': serializer.toJson(name), - 'type': serializer - .toJson(i1.$LocalAssetEntityTable.$convertertype.toJson(type)), + 'type': serializer.toJson( + i1.$LocalAssetEntityTable.$convertertype.toJson(type), + ), 'createdAt': serializer.toJson(createdAt), 'updatedAt': serializer.toJson(updatedAt), 'width': serializer.toJson(width), @@ -575,33 +736,33 @@ class LocalAssetEntityData extends i0.DataClass }; } - i1.LocalAssetEntityData copyWith( - {String? name, - i2.AssetType? type, - DateTime? createdAt, - DateTime? updatedAt, - i0.Value width = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value durationInSeconds = const i0.Value.absent(), - String? id, - i0.Value checksum = const i0.Value.absent(), - bool? isFavorite, - int? orientation}) => - i1.LocalAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present - ? durationInSeconds.value - : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum.present ? checksum.value : this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - orientation: orientation ?? this.orientation, - ); + i1.LocalAssetEntityData copyWith({ + String? name, + i2.AssetType? type, + DateTime? createdAt, + DateTime? updatedAt, + i0.Value width = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value durationInSeconds = const i0.Value.absent(), + String? id, + i0.Value checksum = const i0.Value.absent(), + bool? isFavorite, + int? orientation, + }) => i1.LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); LocalAssetEntityData copyWithCompanion(i1.LocalAssetEntityCompanion data) { return LocalAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -615,10 +776,12 @@ class LocalAssetEntityData extends i0.DataClass : this.durationInSeconds, id: data.id.present ? data.id.value : this.id, checksum: data.checksum.present ? data.checksum.value : this.checksum, - isFavorite: - data.isFavorite.present ? data.isFavorite.value : this.isFavorite, - orientation: - data.orientation.present ? data.orientation.value : this.orientation, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, ); } @@ -641,8 +804,19 @@ class LocalAssetEntityData extends i0.DataClass } @override - int get hashCode => Object.hash(name, type, createdAt, updatedAt, width, - height, durationInSeconds, id, checksum, isFavorite, orientation); + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -698,9 +872,9 @@ class LocalAssetEntityCompanion this.checksum = const i0.Value.absent(), this.isFavorite = const i0.Value.absent(), this.orientation = const i0.Value.absent(), - }) : name = i0.Value(name), - type = i0.Value(type), - id = i0.Value(id); + }) : name = i0.Value(name), + type = i0.Value(type), + id = i0.Value(id); static i0.Insertable custom({ i0.Expression? name, i0.Expression? type, @@ -729,18 +903,19 @@ class LocalAssetEntityCompanion }); } - i1.LocalAssetEntityCompanion copyWith( - {i0.Value? name, - i0.Value? type, - i0.Value? createdAt, - i0.Value? updatedAt, - i0.Value? width, - i0.Value? height, - i0.Value? durationInSeconds, - i0.Value? id, - i0.Value? checksum, - i0.Value? isFavorite, - i0.Value? orientation}) { + i1.LocalAssetEntityCompanion copyWith({ + i0.Value? name, + i0.Value? type, + i0.Value? createdAt, + i0.Value? updatedAt, + i0.Value? width, + i0.Value? height, + i0.Value? durationInSeconds, + i0.Value? id, + i0.Value? checksum, + i0.Value? isFavorite, + i0.Value? orientation, + }) { return i1.LocalAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -764,7 +939,8 @@ class LocalAssetEntityCompanion } if (type.present) { map['type'] = i0.Variable( - i1.$LocalAssetEntityTable.$convertertype.toSql(type.value)); + i1.$LocalAssetEntityTable.$convertertype.toSql(type.value), + ); } if (createdAt.present) { map['created_at'] = i0.Variable(createdAt.value); diff --git a/mobile/lib/infrastructure/entities/log.entity.g.dart b/mobile/lib/infrastructure/entities/log.entity.g.dart index 9300cf15c5..02fa817a08 100644 --- a/mobile/lib/infrastructure/entities/log.entity.g.dart +++ b/mobile/lib/infrastructure/entities/log.entity.g.dart @@ -32,23 +32,16 @@ const LoggerMessageSchema = CollectionSchema( name: r'createdAt', type: IsarType.dateTime, ), - r'details': PropertySchema( - id: 3, - name: r'details', - type: IsarType.string, - ), + r'details': PropertySchema(id: 3, name: r'details', type: IsarType.string), r'level': PropertySchema( id: 4, name: r'level', type: IsarType.byte, enumMap: _LoggerMessagelevelEnumValueMap, ), - r'message': PropertySchema( - id: 5, - name: r'message', - type: IsarType.string, - ) + r'message': PropertySchema(id: 5, name: r'message', type: IsarType.string), }, + estimateSize: _loggerMessageEstimateSize, serialize: _loggerMessageSerialize, deserialize: _loggerMessageDeserialize, @@ -57,6 +50,7 @@ const LoggerMessageSchema = CollectionSchema( indexes: {}, links: {}, embeddedSchemas: {}, + getId: _loggerMessageGetId, getLinks: _loggerMessageGetLinks, attach: _loggerMessageAttach, @@ -116,7 +110,8 @@ LoggerMessage _loggerMessageDeserialize( context2: reader.readStringOrNull(offsets[1]), createdAt: reader.readDateTime(offsets[2]), details: reader.readStringOrNull(offsets[3]), - level: _LoggerMessagelevelValueEnumMap[reader.readByteOrNull(offsets[4])] ?? + level: + _LoggerMessagelevelValueEnumMap[reader.readByteOrNull(offsets[4])] ?? LogLevel.info, message: reader.readString(offsets[5]), ); @@ -140,7 +135,8 @@ P _loggerMessageDeserializeProp

( return (reader.readStringOrNull(offset)) as P; case 4: return (_LoggerMessagelevelValueEnumMap[reader.readByteOrNull(offset)] ?? - LogLevel.info) as P; + LogLevel.info) + as P; case 5: return (reader.readString(offset)) as P; default: @@ -182,7 +178,10 @@ List> _loggerMessageGetLinks(LoggerMessage object) { } void _loggerMessageAttach( - IsarCollection col, Id id, LoggerMessage object) {} + IsarCollection col, + Id id, + LoggerMessage object, +) {} extension LoggerMessageQueryWhereSort on QueryBuilder { @@ -196,17 +195,16 @@ extension LoggerMessageQueryWhereSort extension LoggerMessageQueryWhere on QueryBuilder { QueryBuilder idEqualTo( - Id id) { + Id id, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); + return query.addWhereClause(IdWhereClause.between(lower: id, upper: id)); }); } QueryBuilder idNotEqualTo( - Id id) { + Id id, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query @@ -229,8 +227,9 @@ extension LoggerMessageQueryWhere } QueryBuilder idGreaterThan( - Id id, - {bool include = false}) { + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: id, includeLower: include), @@ -239,8 +238,9 @@ extension LoggerMessageQueryWhere } QueryBuilder idLessThan( - Id id, - {bool include = false}) { + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: id, includeUpper: include), @@ -255,12 +255,14 @@ extension LoggerMessageQueryWhere bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + ), + ); }); } } @@ -268,71 +270,74 @@ extension LoggerMessageQueryWhere extension LoggerMessageQueryFilter on QueryBuilder { QueryBuilder - context1IsNull() { + context1IsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'context1', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'context1'), + ); }); } QueryBuilder - context1IsNotNull() { + context1IsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'context1', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'context1'), + ); }); } QueryBuilder - context1EqualTo( - String? value, { - bool caseSensitive = true, - }) { + context1EqualTo(String? value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'context1', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'context1', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context1GreaterThan( + context1GreaterThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'context1', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'context1', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context1LessThan( + context1LessThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'context1', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'context1', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context1Between( + context1Between( String? lower, String? upper, { bool includeLower = true, @@ -340,153 +345,158 @@ extension LoggerMessageQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'context1', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'context1', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context1StartsWith( - String value, { - bool caseSensitive = true, - }) { + context1StartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'context1', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'context1', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context1EndsWith( - String value, { - bool caseSensitive = true, - }) { + context1EndsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'context1', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'context1', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context1Contains(String value, {bool caseSensitive = true}) { + context1Contains(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'context1', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'context1', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context1Matches(String pattern, {bool caseSensitive = true}) { + context1Matches(String pattern, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'context1', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'context1', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context1IsEmpty() { + context1IsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'context1', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'context1', value: ''), + ); }); } QueryBuilder - context1IsNotEmpty() { + context1IsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'context1', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'context1', value: ''), + ); }); } QueryBuilder - context2IsNull() { + context2IsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'context2', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'context2'), + ); }); } QueryBuilder - context2IsNotNull() { + context2IsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'context2', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'context2'), + ); }); } QueryBuilder - context2EqualTo( - String? value, { - bool caseSensitive = true, - }) { + context2EqualTo(String? value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'context2', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'context2', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context2GreaterThan( + context2GreaterThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'context2', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'context2', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context2LessThan( + context2LessThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'context2', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'context2', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context2Between( + context2Between( String? lower, String? upper, { bool includeLower = true, @@ -494,209 +504,213 @@ extension LoggerMessageQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'context2', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'context2', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context2StartsWith( - String value, { - bool caseSensitive = true, - }) { + context2StartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'context2', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'context2', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context2EndsWith( - String value, { - bool caseSensitive = true, - }) { + context2EndsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'context2', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'context2', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context2Contains(String value, {bool caseSensitive = true}) { + context2Contains(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'context2', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'context2', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context2Matches(String pattern, {bool caseSensitive = true}) { + context2Matches(String pattern, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'context2', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'context2', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context2IsEmpty() { + context2IsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'context2', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'context2', value: ''), + ); }); } QueryBuilder - context2IsNotEmpty() { + context2IsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'context2', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'context2', value: ''), + ); }); } QueryBuilder - createdAtEqualTo(DateTime value) { + createdAtEqualTo(DateTime value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'createdAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'createdAt', value: value), + ); }); } QueryBuilder - createdAtGreaterThan( - DateTime value, { - bool include = false, - }) { + createdAtGreaterThan(DateTime value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'createdAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'createdAt', + value: value, + ), + ); }); } QueryBuilder - createdAtLessThan( - DateTime value, { - bool include = false, - }) { + createdAtLessThan(DateTime value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'createdAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'createdAt', + value: value, + ), + ); }); } QueryBuilder - createdAtBetween( + createdAtBetween( DateTime lower, DateTime upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'createdAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'createdAt', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - detailsIsNull() { + detailsIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'details', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'details'), + ); }); } QueryBuilder - detailsIsNotNull() { + detailsIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'details', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'details'), + ); }); } QueryBuilder - detailsEqualTo( - String? value, { - bool caseSensitive = true, - }) { + detailsEqualTo(String? value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'details', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'details', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - detailsGreaterThan( + detailsGreaterThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'details', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'details', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - detailsLessThan( + detailsLessThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'details', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'details', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - detailsBetween( + detailsBetween( String? lower, String? upper, { bool includeLower = true, @@ -704,108 +718,109 @@ extension LoggerMessageQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'details', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'details', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - detailsStartsWith( - String value, { - bool caseSensitive = true, - }) { + detailsStartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'details', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'details', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - detailsEndsWith( - String value, { - bool caseSensitive = true, - }) { + detailsEndsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'details', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'details', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - detailsContains(String value, {bool caseSensitive = true}) { + detailsContains(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'details', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'details', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - detailsMatches(String pattern, {bool caseSensitive = true}) { + detailsMatches(String pattern, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'details', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'details', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - detailsIsEmpty() { + detailsIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'details', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'details', value: ''), + ); }); } QueryBuilder - detailsIsNotEmpty() { + detailsIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'details', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'details', value: ''), + ); }); } QueryBuilder idEqualTo( - Id value) { + Id value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: value), + ); }); } QueryBuilder - idGreaterThan( - Id value, { - bool include = false, - }) { + idGreaterThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -814,11 +829,13 @@ extension LoggerMessageQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -829,120 +846,124 @@ extension LoggerMessageQueryFilter bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - levelEqualTo(LogLevel value) { + levelEqualTo(LogLevel value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'level', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'level', value: value), + ); }); } QueryBuilder - levelGreaterThan( - LogLevel value, { - bool include = false, - }) { + levelGreaterThan(LogLevel value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'level', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'level', + value: value, + ), + ); }); } QueryBuilder - levelLessThan( - LogLevel value, { - bool include = false, - }) { + levelLessThan(LogLevel value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'level', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'level', + value: value, + ), + ); }); } QueryBuilder - levelBetween( + levelBetween( LogLevel lower, LogLevel upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'level', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'level', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - messageEqualTo( - String value, { - bool caseSensitive = true, - }) { + messageEqualTo(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'message', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'message', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - messageGreaterThan( + messageGreaterThan( String value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'message', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'message', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - messageLessThan( + messageLessThan( String value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'message', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'message', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - messageBetween( + messageBetween( String lower, String upper, { bool includeLower = true, @@ -950,84 +971,86 @@ extension LoggerMessageQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'message', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'message', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - messageStartsWith( - String value, { - bool caseSensitive = true, - }) { + messageStartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'message', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'message', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - messageEndsWith( - String value, { - bool caseSensitive = true, - }) { + messageEndsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'message', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'message', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - messageContains(String value, {bool caseSensitive = true}) { + messageContains(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'message', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'message', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - messageMatches(String pattern, {bool caseSensitive = true}) { + messageMatches(String pattern, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'message', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'message', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - messageIsEmpty() { + messageIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'message', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'message', value: ''), + ); }); } QueryBuilder - messageIsNotEmpty() { + messageIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'message', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'message', value: ''), + ); }); } } @@ -1047,7 +1070,7 @@ extension LoggerMessageQuerySortBy } QueryBuilder - sortByContext1Desc() { + sortByContext1Desc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'context1', Sort.desc); }); @@ -1060,7 +1083,7 @@ extension LoggerMessageQuerySortBy } QueryBuilder - sortByContext2Desc() { + sortByContext2Desc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'context2', Sort.desc); }); @@ -1073,7 +1096,7 @@ extension LoggerMessageQuerySortBy } QueryBuilder - sortByCreatedAtDesc() { + sortByCreatedAtDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'createdAt', Sort.desc); }); @@ -1125,7 +1148,7 @@ extension LoggerMessageQuerySortThenBy } QueryBuilder - thenByContext1Desc() { + thenByContext1Desc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'context1', Sort.desc); }); @@ -1138,7 +1161,7 @@ extension LoggerMessageQuerySortThenBy } QueryBuilder - thenByContext2Desc() { + thenByContext2Desc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'context2', Sort.desc); }); @@ -1151,7 +1174,7 @@ extension LoggerMessageQuerySortThenBy } QueryBuilder - thenByCreatedAtDesc() { + thenByCreatedAtDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'createdAt', Sort.desc); }); @@ -1208,15 +1231,17 @@ extension LoggerMessageQuerySortThenBy extension LoggerMessageQueryWhereDistinct on QueryBuilder { - QueryBuilder distinctByContext1( - {bool caseSensitive = true}) { + QueryBuilder distinctByContext1({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'context1', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByContext2( - {bool caseSensitive = true}) { + QueryBuilder distinctByContext2({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'context2', caseSensitive: caseSensitive); }); @@ -1228,8 +1253,9 @@ extension LoggerMessageQueryWhereDistinct }); } - QueryBuilder distinctByDetails( - {bool caseSensitive = true}) { + QueryBuilder distinctByDetails({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'details', caseSensitive: caseSensitive); }); @@ -1241,8 +1267,9 @@ extension LoggerMessageQueryWhereDistinct }); } - QueryBuilder distinctByMessage( - {bool caseSensitive = true}) { + QueryBuilder distinctByMessage({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'message', caseSensitive: caseSensitive); }); diff --git a/mobile/lib/infrastructure/entities/memory.entity.drift.dart b/mobile/lib/infrastructure/entities/memory.entity.drift.dart index cb88651ba4..f5f18695a5 100644 --- a/mobile/lib/infrastructure/entities/memory.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/memory.entity.drift.dart @@ -10,65 +10,76 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i5; import 'package:drift/internal/modular.dart' as i6; -typedef $$MemoryEntityTableCreateCompanionBuilder = i1.MemoryEntityCompanion - Function({ - required String id, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value deletedAt, - required String ownerId, - required i2.MemoryTypeEnum type, - required String data, - i0.Value isSaved, - required DateTime memoryAt, - i0.Value seenAt, - i0.Value showAt, - i0.Value hideAt, -}); -typedef $$MemoryEntityTableUpdateCompanionBuilder = i1.MemoryEntityCompanion - Function({ - i0.Value id, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value deletedAt, - i0.Value ownerId, - i0.Value type, - i0.Value data, - i0.Value isSaved, - i0.Value memoryAt, - i0.Value seenAt, - i0.Value showAt, - i0.Value hideAt, -}); +typedef $$MemoryEntityTableCreateCompanionBuilder = + i1.MemoryEntityCompanion Function({ + required String id, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value deletedAt, + required String ownerId, + required i2.MemoryTypeEnum type, + required String data, + i0.Value isSaved, + required DateTime memoryAt, + i0.Value seenAt, + i0.Value showAt, + i0.Value hideAt, + }); +typedef $$MemoryEntityTableUpdateCompanionBuilder = + i1.MemoryEntityCompanion Function({ + i0.Value id, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value deletedAt, + i0.Value ownerId, + i0.Value type, + i0.Value data, + i0.Value isSaved, + i0.Value memoryAt, + i0.Value seenAt, + i0.Value showAt, + i0.Value hideAt, + }); -final class $$MemoryEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, i1.$MemoryEntityTable, i1.MemoryEntityData> { +final class $$MemoryEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$MemoryEntityTable, + i1.MemoryEntityData + > { $$MemoryEntityTableReferences(super.$_db, super.$_table, super.$_typedResult); static i5.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) => i6.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( - i6.ReadDatabaseContainer(db) - .resultSet('memory_entity') - .ownerId, - i6.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + .createAlias( + i0.$_aliasNameGenerator( + i6.ReadDatabaseContainer( + db, + ).resultSet('memory_entity').ownerId, + i6.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i5.$$UserEntityTableProcessedTableManager get ownerId { final $_column = $_itemColumn('owner_id')!; final manager = i5 .$$UserEntityTableTableManager( + $_db, + i6.ReadDatabaseContainer( $_db, - i6.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_ownerIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -82,59 +93,85 @@ class $$MemoryEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get deletedAt => $composableBuilder( - column: $table.deletedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.deletedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnWithTypeConverterFilters - get type => $composableBuilder( - column: $table.type, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + get type => $composableBuilder( + column: $table.type, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i0.ColumnFilters get data => $composableBuilder( - column: $table.data, builder: (column) => i0.ColumnFilters(column)); + column: $table.data, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get isSaved => $composableBuilder( - column: $table.isSaved, builder: (column) => i0.ColumnFilters(column)); + column: $table.isSaved, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get memoryAt => $composableBuilder( - column: $table.memoryAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.memoryAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get seenAt => $composableBuilder( - column: $table.seenAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.seenAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get showAt => $composableBuilder( - column: $table.showAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.showAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get hideAt => $composableBuilder( - column: $table.hideAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.hideAt, + builder: (column) => i0.ColumnFilters(column), + ); i5.$$UserEntityTableFilterComposer get ownerId { final i5.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableFilterComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableFilterComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -149,60 +186,84 @@ class $$MemoryEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get deletedAt => $composableBuilder( - column: $table.deletedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.deletedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get type => $composableBuilder( - column: $table.type, builder: (column) => i0.ColumnOrderings(column)); + column: $table.type, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get data => $composableBuilder( - column: $table.data, builder: (column) => i0.ColumnOrderings(column)); + column: $table.data, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isSaved => $composableBuilder( - column: $table.isSaved, builder: (column) => i0.ColumnOrderings(column)); + column: $table.isSaved, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get memoryAt => $composableBuilder( - column: $table.memoryAt, builder: (column) => i0.ColumnOrderings(column)); + column: $table.memoryAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get seenAt => $composableBuilder( - column: $table.seenAt, builder: (column) => i0.ColumnOrderings(column)); + column: $table.seenAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get showAt => $composableBuilder( - column: $table.showAt, builder: (column) => i0.ColumnOrderings(column)); + column: $table.showAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get hideAt => $composableBuilder( - column: $table.hideAt, builder: (column) => i0.ColumnOrderings(column)); + column: $table.hideAt, + builder: (column) => i0.ColumnOrderings(column), + ); i5.$$UserEntityTableOrderingComposer get ownerId { final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -251,42 +312,52 @@ class $$MemoryEntityTableAnnotationComposer i5.$$UserEntityTableAnnotationComposer get ownerId { final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$MemoryEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$MemoryEntityTable, - i1.MemoryEntityData, - i1.$$MemoryEntityTableFilterComposer, - i1.$$MemoryEntityTableOrderingComposer, - i1.$$MemoryEntityTableAnnotationComposer, - $$MemoryEntityTableCreateCompanionBuilder, - $$MemoryEntityTableUpdateCompanionBuilder, - (i1.MemoryEntityData, i1.$$MemoryEntityTableReferences), - i1.MemoryEntityData, - i0.PrefetchHooks Function({bool ownerId})> { +class $$MemoryEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$MemoryEntityTable, + i1.MemoryEntityData, + i1.$$MemoryEntityTableFilterComposer, + i1.$$MemoryEntityTableOrderingComposer, + i1.$$MemoryEntityTableAnnotationComposer, + $$MemoryEntityTableCreateCompanionBuilder, + $$MemoryEntityTableUpdateCompanionBuilder, + (i1.MemoryEntityData, i1.$$MemoryEntityTableReferences), + i1.MemoryEntityData, + i0.PrefetchHooks Function({bool ownerId}) + > { $$MemoryEntityTableTableManager( - i0.GeneratedDatabase db, i1.$MemoryEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$MemoryEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -295,74 +366,77 @@ class $$MemoryEntityTableTableManager extends i0.RootTableManager< i1.$$MemoryEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$MemoryEntityTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - i0.Value id = const i0.Value.absent(), - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value deletedAt = const i0.Value.absent(), - i0.Value ownerId = const i0.Value.absent(), - i0.Value type = const i0.Value.absent(), - i0.Value data = const i0.Value.absent(), - i0.Value isSaved = const i0.Value.absent(), - i0.Value memoryAt = const i0.Value.absent(), - i0.Value seenAt = const i0.Value.absent(), - i0.Value showAt = const i0.Value.absent(), - i0.Value hideAt = const i0.Value.absent(), - }) => - i1.MemoryEntityCompanion( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - deletedAt: deletedAt, - ownerId: ownerId, - type: type, - data: data, - isSaved: isSaved, - memoryAt: memoryAt, - seenAt: seenAt, - showAt: showAt, - hideAt: hideAt, - ), - createCompanionCallback: ({ - required String id, - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value deletedAt = const i0.Value.absent(), - required String ownerId, - required i2.MemoryTypeEnum type, - required String data, - i0.Value isSaved = const i0.Value.absent(), - required DateTime memoryAt, - i0.Value seenAt = const i0.Value.absent(), - i0.Value showAt = const i0.Value.absent(), - i0.Value hideAt = const i0.Value.absent(), - }) => - i1.MemoryEntityCompanion.insert( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - deletedAt: deletedAt, - ownerId: ownerId, - type: type, - data: data, - isSaved: isSaved, - memoryAt: memoryAt, - seenAt: seenAt, - showAt: showAt, - hideAt: hideAt, - ), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value deletedAt = const i0.Value.absent(), + i0.Value ownerId = const i0.Value.absent(), + i0.Value type = const i0.Value.absent(), + i0.Value data = const i0.Value.absent(), + i0.Value isSaved = const i0.Value.absent(), + i0.Value memoryAt = const i0.Value.absent(), + i0.Value seenAt = const i0.Value.absent(), + i0.Value showAt = const i0.Value.absent(), + i0.Value hideAt = const i0.Value.absent(), + }) => i1.MemoryEntityCompanion( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + deletedAt: deletedAt, + ownerId: ownerId, + type: type, + data: data, + isSaved: isSaved, + memoryAt: memoryAt, + seenAt: seenAt, + showAt: showAt, + hideAt: hideAt, + ), + createCompanionCallback: + ({ + required String id, + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value deletedAt = const i0.Value.absent(), + required String ownerId, + required i2.MemoryTypeEnum type, + required String data, + i0.Value isSaved = const i0.Value.absent(), + required DateTime memoryAt, + i0.Value seenAt = const i0.Value.absent(), + i0.Value showAt = const i0.Value.absent(), + i0.Value hideAt = const i0.Value.absent(), + }) => i1.MemoryEntityCompanion.insert( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + deletedAt: deletedAt, + ownerId: ownerId, + type: type, + data: data, + isSaved: isSaved, + memoryAt: memoryAt, + seenAt: seenAt, + showAt: showAt, + hideAt: hideAt, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$MemoryEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$MemoryEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({ownerId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -373,40 +447,50 @@ class $$MemoryEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (ownerId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.ownerId, - referencedTable: - i1.$$MemoryEntityTableReferences._ownerIdTable(db), - referencedColumn: - i1.$$MemoryEntityTableReferences._ownerIdTable(db).id, - ) as T; - } + dynamic + > + >(state) { + if (ownerId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.ownerId, + referencedTable: i1 + .$$MemoryEntityTableReferences + ._ownerIdTable(db), + referencedColumn: i1 + .$$MemoryEntityTableReferences + ._ownerIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$MemoryEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$MemoryEntityTable, - i1.MemoryEntityData, - i1.$$MemoryEntityTableFilterComposer, - i1.$$MemoryEntityTableOrderingComposer, - i1.$$MemoryEntityTableAnnotationComposer, - $$MemoryEntityTableCreateCompanionBuilder, - $$MemoryEntityTableUpdateCompanionBuilder, - (i1.MemoryEntityData, i1.$$MemoryEntityTableReferences), - i1.MemoryEntityData, - i0.PrefetchHooks Function({bool ownerId})>; +typedef $$MemoryEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$MemoryEntityTable, + i1.MemoryEntityData, + i1.$$MemoryEntityTableFilterComposer, + i1.$$MemoryEntityTableOrderingComposer, + i1.$$MemoryEntityTableAnnotationComposer, + $$MemoryEntityTableCreateCompanionBuilder, + $$MemoryEntityTableUpdateCompanionBuilder, + (i1.MemoryEntityData, i1.$$MemoryEntityTableReferences), + i1.MemoryEntityData, + i0.PrefetchHooks Function({bool ownerId}) + >; class $MemoryEntityTable extends i3.MemoryEntity with i0.TableInfo<$MemoryEntityTable, i1.MemoryEntityData> { @@ -417,100 +501,159 @@ class $MemoryEntityTable extends i3.MemoryEntity static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _createdAtMeta = - const i0.VerificationMeta('createdAt'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta( + 'createdAt', + ); @override late final i0.GeneratedColumn createdAt = - i0.GeneratedColumn('created_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + i0.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _deletedAtMeta = - const i0.VerificationMeta('deletedAt'); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _deletedAtMeta = const i0.VerificationMeta( + 'deletedAt', + ); @override late final i0.GeneratedColumn deletedAt = - i0.GeneratedColumn('deleted_at', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); - static const i0.VerificationMeta _ownerIdMeta = - const i0.VerificationMeta('ownerId'); + i0.GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _ownerIdMeta = const i0.VerificationMeta( + 'ownerId', + ); @override late final i0.GeneratedColumn ownerId = i0.GeneratedColumn( - 'owner_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); @override late final i0.GeneratedColumnWithTypeConverter type = - i0.GeneratedColumn('type', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$MemoryEntityTable.$convertertype); - static const i0.VerificationMeta _dataMeta = - const i0.VerificationMeta('data'); + i0.GeneratedColumn( + 'type', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter(i1.$MemoryEntityTable.$convertertype); + static const i0.VerificationMeta _dataMeta = const i0.VerificationMeta( + 'data', + ); @override late final i0.GeneratedColumn data = i0.GeneratedColumn( - 'data', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _isSavedMeta = - const i0.VerificationMeta('isSaved'); + 'data', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _isSavedMeta = const i0.VerificationMeta( + 'isSaved', + ); @override late final i0.GeneratedColumn isSaved = i0.GeneratedColumn( - 'is_saved', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - i0.GeneratedColumn.constraintIsAlways('CHECK ("is_saved" IN (0, 1))'), - defaultValue: const i4.Constant(false)); - static const i0.VerificationMeta _memoryAtMeta = - const i0.VerificationMeta('memoryAt'); + 'is_saved', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const i4.Constant(false), + ); + static const i0.VerificationMeta _memoryAtMeta = const i0.VerificationMeta( + 'memoryAt', + ); @override late final i0.GeneratedColumn memoryAt = - i0.GeneratedColumn('memory_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: true); - static const i0.VerificationMeta _seenAtMeta = - const i0.VerificationMeta('seenAt'); + i0.GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _seenAtMeta = const i0.VerificationMeta( + 'seenAt', + ); @override late final i0.GeneratedColumn seenAt = i0.GeneratedColumn( - 'seen_at', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); - static const i0.VerificationMeta _showAtMeta = - const i0.VerificationMeta('showAt'); + 'seen_at', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _showAtMeta = const i0.VerificationMeta( + 'showAt', + ); @override late final i0.GeneratedColumn showAt = i0.GeneratedColumn( - 'show_at', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); - static const i0.VerificationMeta _hideAtMeta = - const i0.VerificationMeta('hideAt'); + 'show_at', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _hideAtMeta = const i0.VerificationMeta( + 'hideAt', + ); @override late final i0.GeneratedColumn hideAt = i0.GeneratedColumn( - 'hide_at', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); + 'hide_at', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override List get $columns => [ - id, - createdAt, - updatedAt, - deletedAt, - ownerId, - type, - data, - isSaved, - memoryAt, - seenAt, - showAt, - hideAt - ]; + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -518,8 +661,9 @@ class $MemoryEntityTable extends i3.MemoryEntity static const String $name = 'memory_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { @@ -528,50 +672,70 @@ class $MemoryEntityTable extends i3.MemoryEntity context.missing(_idMeta); } if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('deleted_at')) { - context.handle(_deletedAtMeta, - deletedAt.isAcceptableOrUnknown(data['deleted_at']!, _deletedAtMeta)); + context.handle( + _deletedAtMeta, + deletedAt.isAcceptableOrUnknown(data['deleted_at']!, _deletedAtMeta), + ); } if (data.containsKey('owner_id')) { - context.handle(_ownerIdMeta, - ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta)); + context.handle( + _ownerIdMeta, + ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta), + ); } else if (isInserting) { context.missing(_ownerIdMeta); } if (data.containsKey('data')) { context.handle( - _dataMeta, this.data.isAcceptableOrUnknown(data['data']!, _dataMeta)); + _dataMeta, + this.data.isAcceptableOrUnknown(data['data']!, _dataMeta), + ); } else if (isInserting) { context.missing(_dataMeta); } if (data.containsKey('is_saved')) { - context.handle(_isSavedMeta, - isSaved.isAcceptableOrUnknown(data['is_saved']!, _isSavedMeta)); + context.handle( + _isSavedMeta, + isSaved.isAcceptableOrUnknown(data['is_saved']!, _isSavedMeta), + ); } if (data.containsKey('memory_at')) { - context.handle(_memoryAtMeta, - memoryAt.isAcceptableOrUnknown(data['memory_at']!, _memoryAtMeta)); + context.handle( + _memoryAtMeta, + memoryAt.isAcceptableOrUnknown(data['memory_at']!, _memoryAtMeta), + ); } else if (isInserting) { context.missing(_memoryAtMeta); } if (data.containsKey('seen_at')) { - context.handle(_seenAtMeta, - seenAt.isAcceptableOrUnknown(data['seen_at']!, _seenAtMeta)); + context.handle( + _seenAtMeta, + seenAt.isAcceptableOrUnknown(data['seen_at']!, _seenAtMeta), + ); } if (data.containsKey('show_at')) { - context.handle(_showAtMeta, - showAt.isAcceptableOrUnknown(data['show_at']!, _showAtMeta)); + context.handle( + _showAtMeta, + showAt.isAcceptableOrUnknown(data['show_at']!, _showAtMeta), + ); } if (data.containsKey('hide_at')) { - context.handle(_hideAtMeta, - hideAt.isAcceptableOrUnknown(data['hide_at']!, _hideAtMeta)); + context.handle( + _hideAtMeta, + hideAt.isAcceptableOrUnknown(data['hide_at']!, _hideAtMeta), + ); } return context; } @@ -582,31 +746,56 @@ class $MemoryEntityTable extends i3.MemoryEntity i1.MemoryEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.MemoryEntityData( - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, createdAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - deletedAt: attachedDatabase.typeMapping - .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), - ownerId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - type: i1.$MemoryEntityTable.$convertertype.fromSql(attachedDatabase - .typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}type'])!), - data: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}data'])!, - isSaved: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_saved'])!, - memoryAt: attachedDatabase.typeMapping - .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}memory_at'])!, - seenAt: attachedDatabase.typeMapping - .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}seen_at']), - showAt: attachedDatabase.typeMapping - .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}show_at']), - hideAt: attachedDatabase.typeMapping - .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}hide_at']), + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: i1.$MemoryEntityTable.$convertertype.fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + ), + data: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), ); } @@ -637,19 +826,20 @@ class MemoryEntityData extends i0.DataClass final DateTime? seenAt; final DateTime? showAt; final DateTime? hideAt; - const MemoryEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - this.deletedAt, - required this.ownerId, - required this.type, - required this.data, - required this.isSaved, - required this.memoryAt, - this.seenAt, - this.showAt, - this.hideAt}); + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -661,8 +851,9 @@ class MemoryEntityData extends i0.DataClass } map['owner_id'] = i0.Variable(ownerId); { - map['type'] = - i0.Variable(i1.$MemoryEntityTable.$convertertype.toSql(type)); + map['type'] = i0.Variable( + i1.$MemoryEntityTable.$convertertype.toSql(type), + ); } map['data'] = i0.Variable(data); map['is_saved'] = i0.Variable(isSaved); @@ -679,8 +870,10 @@ class MemoryEntityData extends i0.DataClass return map; } - factory MemoryEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory MemoryEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return MemoryEntityData( id: serializer.fromJson(json['id']), @@ -688,8 +881,9 @@ class MemoryEntityData extends i0.DataClass updatedAt: serializer.fromJson(json['updatedAt']), deletedAt: serializer.fromJson(json['deletedAt']), ownerId: serializer.fromJson(json['ownerId']), - type: i1.$MemoryEntityTable.$convertertype - .fromJson(serializer.fromJson(json['type'])), + type: i1.$MemoryEntityTable.$convertertype.fromJson( + serializer.fromJson(json['type']), + ), data: serializer.fromJson(json['data']), isSaved: serializer.fromJson(json['isSaved']), memoryAt: serializer.fromJson(json['memoryAt']), @@ -707,8 +901,9 @@ class MemoryEntityData extends i0.DataClass 'updatedAt': serializer.toJson(updatedAt), 'deletedAt': serializer.toJson(deletedAt), 'ownerId': serializer.toJson(ownerId), - 'type': serializer - .toJson(i1.$MemoryEntityTable.$convertertype.toJson(type)), + 'type': serializer.toJson( + i1.$MemoryEntityTable.$convertertype.toJson(type), + ), 'data': serializer.toJson(data), 'isSaved': serializer.toJson(isSaved), 'memoryAt': serializer.toJson(memoryAt), @@ -718,33 +913,33 @@ class MemoryEntityData extends i0.DataClass }; } - i1.MemoryEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - i0.Value deletedAt = const i0.Value.absent(), - String? ownerId, - i2.MemoryTypeEnum? type, - String? data, - bool? isSaved, - DateTime? memoryAt, - i0.Value seenAt = const i0.Value.absent(), - i0.Value showAt = const i0.Value.absent(), - i0.Value hideAt = const i0.Value.absent()}) => - i1.MemoryEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - ownerId: ownerId ?? this.ownerId, - type: type ?? this.type, - data: data ?? this.data, - isSaved: isSaved ?? this.isSaved, - memoryAt: memoryAt ?? this.memoryAt, - seenAt: seenAt.present ? seenAt.value : this.seenAt, - showAt: showAt.present ? showAt.value : this.showAt, - hideAt: hideAt.present ? hideAt.value : this.hideAt, - ); + i1.MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + i0.Value deletedAt = const i0.Value.absent(), + String? ownerId, + i2.MemoryTypeEnum? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + i0.Value seenAt = const i0.Value.absent(), + i0.Value showAt = const i0.Value.absent(), + i0.Value hideAt = const i0.Value.absent(), + }) => i1.MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); MemoryEntityData copyWithCompanion(i1.MemoryEntityCompanion data) { return MemoryEntityData( id: data.id.present ? data.id.value : this.id, @@ -782,8 +977,20 @@ class MemoryEntityData extends i0.DataClass } @override - int get hashCode => Object.hash(id, createdAt, updatedAt, deletedAt, ownerId, - type, data, isSaved, memoryAt, seenAt, showAt, hideAt); + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -842,11 +1049,11 @@ class MemoryEntityCompanion extends i0.UpdateCompanion { this.seenAt = const i0.Value.absent(), this.showAt = const i0.Value.absent(), this.hideAt = const i0.Value.absent(), - }) : id = i0.Value(id), - ownerId = i0.Value(ownerId), - type = i0.Value(type), - data = i0.Value(data), - memoryAt = i0.Value(memoryAt); + }) : id = i0.Value(id), + ownerId = i0.Value(ownerId), + type = i0.Value(type), + data = i0.Value(data), + memoryAt = i0.Value(memoryAt); static i0.Insertable custom({ i0.Expression? id, i0.Expression? createdAt, @@ -877,19 +1084,20 @@ class MemoryEntityCompanion extends i0.UpdateCompanion { }); } - i1.MemoryEntityCompanion copyWith( - {i0.Value? id, - i0.Value? createdAt, - i0.Value? updatedAt, - i0.Value? deletedAt, - i0.Value? ownerId, - i0.Value? type, - i0.Value? data, - i0.Value? isSaved, - i0.Value? memoryAt, - i0.Value? seenAt, - i0.Value? showAt, - i0.Value? hideAt}) { + i1.MemoryEntityCompanion copyWith({ + i0.Value? id, + i0.Value? createdAt, + i0.Value? updatedAt, + i0.Value? deletedAt, + i0.Value? ownerId, + i0.Value? type, + i0.Value? data, + i0.Value? isSaved, + i0.Value? memoryAt, + i0.Value? seenAt, + i0.Value? showAt, + i0.Value? hideAt, + }) { return i1.MemoryEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -926,7 +1134,8 @@ class MemoryEntityCompanion extends i0.UpdateCompanion { } if (type.present) { map['type'] = i0.Variable( - i1.$MemoryEntityTable.$convertertype.toSql(type.value)); + i1.$MemoryEntityTable.$convertertype.toSql(type.value), + ); } if (data.present) { map['data'] = i0.Variable(data.value); diff --git a/mobile/lib/infrastructure/entities/memory_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/memory_asset.entity.drift.dart index 9253e8bc05..eedeb85e5c 100644 --- a/mobile/lib/infrastructure/entities/memory_asset.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/memory_asset.entity.drift.dart @@ -11,74 +11,92 @@ import 'package:drift/internal/modular.dart' as i4; import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart' as i5; -typedef $$MemoryAssetEntityTableCreateCompanionBuilder - = i1.MemoryAssetEntityCompanion Function({ - required String assetId, - required String memoryId, -}); -typedef $$MemoryAssetEntityTableUpdateCompanionBuilder - = i1.MemoryAssetEntityCompanion Function({ - i0.Value assetId, - i0.Value memoryId, -}); +typedef $$MemoryAssetEntityTableCreateCompanionBuilder = + i1.MemoryAssetEntityCompanion Function({ + required String assetId, + required String memoryId, + }); +typedef $$MemoryAssetEntityTableUpdateCompanionBuilder = + i1.MemoryAssetEntityCompanion Function({ + i0.Value assetId, + i0.Value memoryId, + }); -final class $$MemoryAssetEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, - i1.$MemoryAssetEntityTable, - i1.MemoryAssetEntityData> { +final class $$MemoryAssetEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$MemoryAssetEntityTable, + i1.MemoryAssetEntityData + > { $$MemoryAssetEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i3.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('remote_asset_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet('memory_asset_entity') .assetId, - i4.ReadDatabaseContainer(db) - .resultSet('remote_asset_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('remote_asset_entity').id, + ), + ); i3.$$RemoteAssetEntityTableProcessedTableManager get assetId { final $_column = $_itemColumn('asset_id')!; final manager = i3 .$$RemoteAssetEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('remote_asset_entity')) + ).resultSet('remote_asset_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_assetIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } static i5.$MemoryEntityTable _memoryIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('memory_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet('memory_asset_entity') .memoryId, - i4.ReadDatabaseContainer(db) - .resultSet('memory_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('memory_entity').id, + ), + ); i5.$$MemoryEntityTableProcessedTableManager get memoryId { final $_column = $_itemColumn('memory_id')!; final manager = i5 .$$MemoryEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('memory_entity')) + ).resultSet('memory_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_memoryIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -93,45 +111,55 @@ class $$MemoryAssetEntityTableFilterComposer }); i3.$$RemoteAssetEntityTableFilterComposer get assetId { final i3.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$MemoryEntityTableFilterComposer get memoryId { final i5.$$MemoryEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.memoryId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('memory_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$MemoryEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('memory_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.memoryId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('memory_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$MemoryEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('memory_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -148,46 +176,55 @@ class $$MemoryAssetEntityTableOrderingComposer i3.$$RemoteAssetEntityTableOrderingComposer get assetId { final i3.$$RemoteAssetEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$MemoryEntityTableOrderingComposer get memoryId { final i5.$$MemoryEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.memoryId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('memory_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$MemoryEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('memory_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.memoryId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('memory_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$MemoryEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('memory_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -204,65 +241,79 @@ class $$MemoryAssetEntityTableAnnotationComposer i3.$$RemoteAssetEntityTableAnnotationComposer get assetId { final i3.$$RemoteAssetEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$MemoryEntityTableAnnotationComposer get memoryId { final i5.$$MemoryEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.memoryId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('memory_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$MemoryEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('memory_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.memoryId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('memory_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$MemoryEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('memory_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$MemoryAssetEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$MemoryAssetEntityTable, - i1.MemoryAssetEntityData, - i1.$$MemoryAssetEntityTableFilterComposer, - i1.$$MemoryAssetEntityTableOrderingComposer, - i1.$$MemoryAssetEntityTableAnnotationComposer, - $$MemoryAssetEntityTableCreateCompanionBuilder, - $$MemoryAssetEntityTableUpdateCompanionBuilder, - (i1.MemoryAssetEntityData, i1.$$MemoryAssetEntityTableReferences), - i1.MemoryAssetEntityData, - i0.PrefetchHooks Function({bool assetId, bool memoryId})> { +class $$MemoryAssetEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$MemoryAssetEntityTable, + i1.MemoryAssetEntityData, + i1.$$MemoryAssetEntityTableFilterComposer, + i1.$$MemoryAssetEntityTableOrderingComposer, + i1.$$MemoryAssetEntityTableAnnotationComposer, + $$MemoryAssetEntityTableCreateCompanionBuilder, + $$MemoryAssetEntityTableUpdateCompanionBuilder, + (i1.MemoryAssetEntityData, i1.$$MemoryAssetEntityTableReferences), + i1.MemoryAssetEntityData, + i0.PrefetchHooks Function({bool assetId, bool memoryId}) + > { $$MemoryAssetEntityTableTableManager( - i0.GeneratedDatabase db, i1.$MemoryAssetEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$MemoryAssetEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -271,35 +322,38 @@ class $$MemoryAssetEntityTableTableManager extends i0.RootTableManager< .$$MemoryAssetEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$MemoryAssetEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value assetId = const i0.Value.absent(), - i0.Value memoryId = const i0.Value.absent(), - }) => - i1.MemoryAssetEntityCompanion( - assetId: assetId, - memoryId: memoryId, - ), - createCompanionCallback: ({ - required String assetId, - required String memoryId, - }) => - i1.MemoryAssetEntityCompanion.insert( - assetId: assetId, - memoryId: memoryId, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value assetId = const i0.Value.absent(), + i0.Value memoryId = const i0.Value.absent(), + }) => i1.MemoryAssetEntityCompanion( + assetId: assetId, + memoryId: memoryId, + ), + createCompanionCallback: + ({required String assetId, required String memoryId}) => + i1.MemoryAssetEntityCompanion.insert( + assetId: assetId, + memoryId: memoryId, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$MemoryAssetEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$MemoryAssetEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({assetId = false, memoryId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -310,53 +364,65 @@ class $$MemoryAssetEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (assetId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.assetId, - referencedTable: - i1.$$MemoryAssetEntityTableReferences._assetIdTable(db), - referencedColumn: i1.$$MemoryAssetEntityTableReferences - ._assetIdTable(db) - .id, - ) as T; - } - if (memoryId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.memoryId, - referencedTable: i1.$$MemoryAssetEntityTableReferences - ._memoryIdTable(db), - referencedColumn: i1.$$MemoryAssetEntityTableReferences - ._memoryIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (assetId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.assetId, + referencedTable: i1 + .$$MemoryAssetEntityTableReferences + ._assetIdTable(db), + referencedColumn: i1 + .$$MemoryAssetEntityTableReferences + ._assetIdTable(db) + .id, + ) + as T; + } + if (memoryId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.memoryId, + referencedTable: i1 + .$$MemoryAssetEntityTableReferences + ._memoryIdTable(db), + referencedColumn: i1 + .$$MemoryAssetEntityTableReferences + ._memoryIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$MemoryAssetEntityTableProcessedTableManager - = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$MemoryAssetEntityTable, - i1.MemoryAssetEntityData, - i1.$$MemoryAssetEntityTableFilterComposer, - i1.$$MemoryAssetEntityTableOrderingComposer, - i1.$$MemoryAssetEntityTableAnnotationComposer, - $$MemoryAssetEntityTableCreateCompanionBuilder, - $$MemoryAssetEntityTableUpdateCompanionBuilder, - (i1.MemoryAssetEntityData, i1.$$MemoryAssetEntityTableReferences), - i1.MemoryAssetEntityData, - i0.PrefetchHooks Function({bool assetId, bool memoryId})>; +typedef $$MemoryAssetEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$MemoryAssetEntityTable, + i1.MemoryAssetEntityData, + i1.$$MemoryAssetEntityTableFilterComposer, + i1.$$MemoryAssetEntityTableOrderingComposer, + i1.$$MemoryAssetEntityTableAnnotationComposer, + $$MemoryAssetEntityTableCreateCompanionBuilder, + $$MemoryAssetEntityTableUpdateCompanionBuilder, + (i1.MemoryAssetEntityData, i1.$$MemoryAssetEntityTableReferences), + i1.MemoryAssetEntityData, + i0.PrefetchHooks Function({bool assetId, bool memoryId}) + >; class $MemoryAssetEntityTable extends i2.MemoryAssetEntity with i0.TableInfo<$MemoryAssetEntityTable, i1.MemoryAssetEntityData> { @@ -364,24 +430,34 @@ class $MemoryAssetEntityTable extends i2.MemoryAssetEntity final i0.GeneratedDatabase attachedDatabase; final String? _alias; $MemoryAssetEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _assetIdMeta = - const i0.VerificationMeta('assetId'); + static const i0.VerificationMeta _assetIdMeta = const i0.VerificationMeta( + 'assetId', + ); @override late final i0.GeneratedColumn assetId = i0.GeneratedColumn( - 'asset_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _memoryIdMeta = - const i0.VerificationMeta('memoryId'); + 'asset_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _memoryIdMeta = const i0.VerificationMeta( + 'memoryId', + ); @override late final i0.GeneratedColumn memoryId = i0.GeneratedColumn( - 'memory_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES memory_entity (id) ON DELETE CASCADE')); + 'memory_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, memoryId]; @override @@ -391,19 +467,24 @@ class $MemoryAssetEntityTable extends i2.MemoryAssetEntity static const String $name = 'memory_asset_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('asset_id')) { - context.handle(_assetIdMeta, - assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta)); + context.handle( + _assetIdMeta, + assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta), + ); } else if (isInserting) { context.missing(_assetIdMeta); } if (data.containsKey('memory_id')) { - context.handle(_memoryIdMeta, - memoryId.isAcceptableOrUnknown(data['memory_id']!, _memoryIdMeta)); + context.handle( + _memoryIdMeta, + memoryId.isAcceptableOrUnknown(data['memory_id']!, _memoryIdMeta), + ); } else if (isInserting) { context.missing(_memoryIdMeta); } @@ -413,14 +494,20 @@ class $MemoryAssetEntityTable extends i2.MemoryAssetEntity @override Set get $primaryKey => {assetId, memoryId}; @override - i1.MemoryAssetEntityData map(Map data, - {String? tablePrefix}) { + i1.MemoryAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.MemoryAssetEntityData( - assetId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - memoryId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}memory_id'])!, + assetId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, ); } @@ -448,8 +535,10 @@ class MemoryAssetEntityData extends i0.DataClass return map; } - factory MemoryAssetEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory MemoryAssetEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return MemoryAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -507,8 +596,8 @@ class MemoryAssetEntityCompanion MemoryAssetEntityCompanion.insert({ required String assetId, required String memoryId, - }) : assetId = i0.Value(assetId), - memoryId = i0.Value(memoryId); + }) : assetId = i0.Value(assetId), + memoryId = i0.Value(memoryId); static i0.Insertable custom({ i0.Expression? assetId, i0.Expression? memoryId, @@ -519,8 +608,10 @@ class MemoryAssetEntityCompanion }); } - i1.MemoryAssetEntityCompanion copyWith( - {i0.Value? assetId, i0.Value? memoryId}) { + i1.MemoryAssetEntityCompanion copyWith({ + i0.Value? assetId, + i0.Value? memoryId, + }) { return i1.MemoryAssetEntityCompanion( assetId: assetId ?? this.assetId, memoryId: memoryId ?? this.memoryId, diff --git a/mobile/lib/infrastructure/entities/merged_asset.drift.dart b/mobile/lib/infrastructure/entities/merged_asset.drift.dart index 7f56b25d4e..75f8de2de0 100644 --- a/mobile/lib/infrastructure/entities/merged_asset.drift.dart +++ b/mobile/lib/infrastructure/entities/merged_asset.drift.dart @@ -16,87 +16,100 @@ import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.d class MergedAssetDrift extends i1.ModularAccessor { MergedAssetDrift(i0.GeneratedDatabase db) : super(db); - i0.Selectable mergedAsset(List var1, - {required MergedAsset$limit limit}) { + i0.Selectable mergedAsset( + List var1, { + required MergedAsset$limit limit, + }) { var $arrayStartIndex = 1; final expandedvar1 = $expandVar($arrayStartIndex, var1.length); $arrayStartIndex += var1.length; - final generatedlimit = $write(limit(alias(this.localAssetEntity, 'lae')), - startIndex: $arrayStartIndex); + final generatedlimit = $write( + limit(alias(this.localAssetEntity, 'lae')), + startIndex: $arrayStartIndex, + ); $arrayStartIndex += generatedlimit.amountOfVariables; return customSelect( - 'SELECT rae.id AS remote_id, (SELECT lae.id FROM local_asset_entity AS lae WHERE lae.checksum = rae.checksum LIMIT 1) AS local_id, rae.name, rae.type, rae.created_at AS created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar1) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at AS created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) ORDER BY created_at DESC ${generatedlimit.sql}', - variables: [ - for (var $ in var1) i0.Variable($), - ...generatedlimit.introducedVariables - ], - readsFrom: { - remoteAssetEntity, - localAssetEntity, - stackEntity, - localAlbumAssetEntity, - localAlbumEntity, - ...generatedlimit.watchedTables, - }).map((i0.QueryRow row) => MergedAssetResult( - remoteId: row.readNullable('remote_id'), - localId: row.readNullable('local_id'), - name: row.read('name'), - type: i4.$RemoteAssetEntityTable.$convertertype - .fromSql(row.read('type')), - createdAt: row.read('created_at'), - updatedAt: row.read('updated_at'), - width: row.readNullable('width'), - height: row.readNullable('height'), - durationInSeconds: row.readNullable('duration_in_seconds'), - isFavorite: row.read('is_favorite'), - thumbHash: row.readNullable('thumb_hash'), - checksum: row.readNullable('checksum'), - ownerId: row.readNullable('owner_id'), - livePhotoVideoId: row.readNullable('live_photo_video_id'), - orientation: row.read('orientation'), - stackId: row.readNullable('stack_id'), - )); + 'SELECT rae.id AS remote_id, (SELECT lae.id FROM local_asset_entity AS lae WHERE lae.checksum = rae.checksum LIMIT 1) AS local_id, rae.name, rae.type, rae.created_at AS created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar1) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at AS created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) ORDER BY created_at DESC ${generatedlimit.sql}', + variables: [ + for (var $ in var1) i0.Variable($), + ...generatedlimit.introducedVariables, + ], + readsFrom: { + remoteAssetEntity, + localAssetEntity, + stackEntity, + localAlbumAssetEntity, + localAlbumEntity, + ...generatedlimit.watchedTables, + }, + ).map( + (i0.QueryRow row) => MergedAssetResult( + remoteId: row.readNullable('remote_id'), + localId: row.readNullable('local_id'), + name: row.read('name'), + type: i4.$RemoteAssetEntityTable.$convertertype.fromSql( + row.read('type'), + ), + createdAt: row.read('created_at'), + updatedAt: row.read('updated_at'), + width: row.readNullable('width'), + height: row.readNullable('height'), + durationInSeconds: row.readNullable('duration_in_seconds'), + isFavorite: row.read('is_favorite'), + thumbHash: row.readNullable('thumb_hash'), + checksum: row.readNullable('checksum'), + ownerId: row.readNullable('owner_id'), + livePhotoVideoId: row.readNullable('live_photo_video_id'), + orientation: row.read('orientation'), + stackId: row.readNullable('stack_id'), + ), + ); } - i0.Selectable mergedBucket(List var2, - {required int groupBy}) { + i0.Selectable mergedBucket( + List var2, { + required int groupBy, + }) { var $arrayStartIndex = 2; final expandedvar2 = $expandVar($arrayStartIndex, var2.length); $arrayStartIndex += var2.length; return customSelect( - 'SELECT COUNT(*) AS asset_count, CASE WHEN ?1 = 0 THEN STRFTIME(\'%Y-%m-%d\', created_at, \'localtime\') WHEN ?1 = 1 THEN STRFTIME(\'%Y-%m\', created_at, \'localtime\') END AS bucket_date FROM (SELECT rae.created_at FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar2) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT lae.created_at FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum LEFT JOIN local_album_asset_entity AS laa ON laa.asset_id = lae.id LEFT JOIN local_album_entity AS la ON la.id = laa.album_id WHERE rae.id IS NULL AND la.backup_selection = 0) GROUP BY bucket_date ORDER BY bucket_date DESC', - variables: [ - i0.Variable(groupBy), - for (var $ in var2) i0.Variable($) - ], - readsFrom: { - remoteAssetEntity, - stackEntity, - localAssetEntity, - localAlbumAssetEntity, - localAlbumEntity, - }).map((i0.QueryRow row) => MergedBucketResult( - assetCount: row.read('asset_count'), - bucketDate: row.read('bucket_date'), - )); + 'SELECT COUNT(*) AS asset_count, CASE WHEN ?1 = 0 THEN STRFTIME(\'%Y-%m-%d\', created_at, \'localtime\') WHEN ?1 = 1 THEN STRFTIME(\'%Y-%m\', created_at, \'localtime\') END AS bucket_date FROM (SELECT rae.created_at FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar2) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT lae.created_at FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum LEFT JOIN local_album_asset_entity AS laa ON laa.asset_id = lae.id LEFT JOIN local_album_entity AS la ON la.id = laa.album_id WHERE rae.id IS NULL AND la.backup_selection = 0) GROUP BY bucket_date ORDER BY bucket_date DESC', + variables: [ + i0.Variable(groupBy), + for (var $ in var2) i0.Variable($), + ], + readsFrom: { + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumAssetEntity, + localAlbumEntity, + }, + ).map( + (i0.QueryRow row) => MergedBucketResult( + assetCount: row.read('asset_count'), + bucketDate: row.read('bucket_date'), + ), + ); } - i4.$RemoteAssetEntityTable get remoteAssetEntity => - i1.ReadDatabaseContainer(attachedDatabase) - .resultSet('remote_asset_entity'); - i5.$StackEntityTable get stackEntity => - i1.ReadDatabaseContainer(attachedDatabase) - .resultSet('stack_entity'); - i3.$LocalAssetEntityTable get localAssetEntity => - i1.ReadDatabaseContainer(attachedDatabase) - .resultSet('local_asset_entity'); + i4.$RemoteAssetEntityTable get remoteAssetEntity => i1.ReadDatabaseContainer( + attachedDatabase, + ).resultSet('remote_asset_entity'); + i5.$StackEntityTable get stackEntity => i1.ReadDatabaseContainer( + attachedDatabase, + ).resultSet('stack_entity'); + i3.$LocalAssetEntityTable get localAssetEntity => i1.ReadDatabaseContainer( + attachedDatabase, + ).resultSet('local_asset_entity'); i6.$LocalAlbumAssetEntityTable get localAlbumAssetEntity => - i1.ReadDatabaseContainer(attachedDatabase) - .resultSet( - 'local_album_asset_entity'); - i7.$LocalAlbumEntityTable get localAlbumEntity => - i1.ReadDatabaseContainer(attachedDatabase) - .resultSet('local_album_entity'); + i1.ReadDatabaseContainer( + attachedDatabase, + ).resultSet('local_album_asset_entity'); + i7.$LocalAlbumEntityTable get localAlbumEntity => i1.ReadDatabaseContainer( + attachedDatabase, + ).resultSet('local_album_entity'); } class MergedAssetResult { @@ -141,8 +154,5 @@ typedef MergedAsset$limit = i0.Limit Function(i3.$LocalAssetEntityTable lae); class MergedBucketResult { final int assetCount; final String bucketDate; - MergedBucketResult({ - required this.assetCount, - required this.bucketDate, - }); + MergedBucketResult({required this.assetCount, required this.bucketDate}); } diff --git a/mobile/lib/infrastructure/entities/partner.entity.drift.dart b/mobile/lib/infrastructure/entities/partner.entity.drift.dart index 26a5dd2fe0..01ec72fe23 100644 --- a/mobile/lib/infrastructure/entities/partner.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/partner.entity.drift.dart @@ -10,74 +10,94 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i4; import 'package:drift/internal/modular.dart' as i5; -typedef $$PartnerEntityTableCreateCompanionBuilder = i1.PartnerEntityCompanion - Function({ - required String sharedById, - required String sharedWithId, - i0.Value inTimeline, -}); -typedef $$PartnerEntityTableUpdateCompanionBuilder = i1.PartnerEntityCompanion - Function({ - i0.Value sharedById, - i0.Value sharedWithId, - i0.Value inTimeline, -}); +typedef $$PartnerEntityTableCreateCompanionBuilder = + i1.PartnerEntityCompanion Function({ + required String sharedById, + required String sharedWithId, + i0.Value inTimeline, + }); +typedef $$PartnerEntityTableUpdateCompanionBuilder = + i1.PartnerEntityCompanion Function({ + i0.Value sharedById, + i0.Value sharedWithId, + i0.Value inTimeline, + }); -final class $$PartnerEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, i1.$PartnerEntityTable, i1.PartnerEntityData> { +final class $$PartnerEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$PartnerEntityTable, + i1.PartnerEntityData + > { $$PartnerEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i4.$UserEntityTable _sharedByIdTable(i0.GeneratedDatabase db) => i5.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( - i5.ReadDatabaseContainer(db) - .resultSet('partner_entity') - .sharedById, - i5.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + .createAlias( + i0.$_aliasNameGenerator( + i5.ReadDatabaseContainer( + db, + ).resultSet('partner_entity').sharedById, + i5.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i4.$$UserEntityTableProcessedTableManager get sharedById { final $_column = $_itemColumn('shared_by_id')!; final manager = i4 .$$UserEntityTableTableManager( + $_db, + i5.ReadDatabaseContainer( $_db, - i5.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_sharedByIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } static i4.$UserEntityTable _sharedWithIdTable(i0.GeneratedDatabase db) => i5.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i5.ReadDatabaseContainer(db) .resultSet('partner_entity') .sharedWithId, - i5.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + i5.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i4.$$UserEntityTableProcessedTableManager get sharedWithId { final $_column = $_itemColumn('shared_with_id')!; final manager = i4 .$$UserEntityTableTableManager( + $_db, + i5.ReadDatabaseContainer( $_db, - i5.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_sharedWithIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -91,49 +111,61 @@ class $$PartnerEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get inTimeline => $composableBuilder( - column: $table.inTimeline, builder: (column) => i0.ColumnFilters(column)); + column: $table.inTimeline, + builder: (column) => i0.ColumnFilters(column), + ); i4.$$UserEntityTableFilterComposer get sharedById { final i4.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.sharedById, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableFilterComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.sharedById, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableFilterComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i4.$$UserEntityTableFilterComposer get sharedWithId { final i4.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.sharedWithId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableFilterComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.sharedWithId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableFilterComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -148,50 +180,61 @@ class $$PartnerEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get inTimeline => $composableBuilder( - column: $table.inTimeline, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.inTimeline, + builder: (column) => i0.ColumnOrderings(column), + ); i4.$$UserEntityTableOrderingComposer get sharedById { final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.sharedById, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.sharedById, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i4.$$UserEntityTableOrderingComposer get sharedWithId { final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.sharedWithId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.sharedWithId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -206,68 +249,85 @@ class $$PartnerEntityTableAnnotationComposer super.$removeJoinBuilderFromRootComposer, }); i0.GeneratedColumn get inTimeline => $composableBuilder( - column: $table.inTimeline, builder: (column) => column); + column: $table.inTimeline, + builder: (column) => column, + ); i4.$$UserEntityTableAnnotationComposer get sharedById { final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.sharedById, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.sharedById, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i4.$$UserEntityTableAnnotationComposer get sharedWithId { final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.sharedWithId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.sharedWithId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$PartnerEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$PartnerEntityTable, - i1.PartnerEntityData, - i1.$$PartnerEntityTableFilterComposer, - i1.$$PartnerEntityTableOrderingComposer, - i1.$$PartnerEntityTableAnnotationComposer, - $$PartnerEntityTableCreateCompanionBuilder, - $$PartnerEntityTableUpdateCompanionBuilder, - (i1.PartnerEntityData, i1.$$PartnerEntityTableReferences), - i1.PartnerEntityData, - i0.PrefetchHooks Function({bool sharedById, bool sharedWithId})> { +class $$PartnerEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$PartnerEntityTable, + i1.PartnerEntityData, + i1.$$PartnerEntityTableFilterComposer, + i1.$$PartnerEntityTableOrderingComposer, + i1.$$PartnerEntityTableAnnotationComposer, + $$PartnerEntityTableCreateCompanionBuilder, + $$PartnerEntityTableUpdateCompanionBuilder, + (i1.PartnerEntityData, i1.$$PartnerEntityTableReferences), + i1.PartnerEntityData, + i0.PrefetchHooks Function({bool sharedById, bool sharedWithId}) + > { $$PartnerEntityTableTableManager( - i0.GeneratedDatabase db, i1.$PartnerEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$PartnerEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -276,38 +336,41 @@ class $$PartnerEntityTableTableManager extends i0.RootTableManager< i1.$$PartnerEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$PartnerEntityTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - i0.Value sharedById = const i0.Value.absent(), - i0.Value sharedWithId = const i0.Value.absent(), - i0.Value inTimeline = const i0.Value.absent(), - }) => - i1.PartnerEntityCompanion( - sharedById: sharedById, - sharedWithId: sharedWithId, - inTimeline: inTimeline, - ), - createCompanionCallback: ({ - required String sharedById, - required String sharedWithId, - i0.Value inTimeline = const i0.Value.absent(), - }) => - i1.PartnerEntityCompanion.insert( - sharedById: sharedById, - sharedWithId: sharedWithId, - inTimeline: inTimeline, - ), + updateCompanionCallback: + ({ + i0.Value sharedById = const i0.Value.absent(), + i0.Value sharedWithId = const i0.Value.absent(), + i0.Value inTimeline = const i0.Value.absent(), + }) => i1.PartnerEntityCompanion( + sharedById: sharedById, + sharedWithId: sharedWithId, + inTimeline: inTimeline, + ), + createCompanionCallback: + ({ + required String sharedById, + required String sharedWithId, + i0.Value inTimeline = const i0.Value.absent(), + }) => i1.PartnerEntityCompanion.insert( + sharedById: sharedById, + sharedWithId: sharedWithId, + inTimeline: inTimeline, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$PartnerEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$PartnerEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({sharedById = false, sharedWithId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -318,52 +381,65 @@ class $$PartnerEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (sharedById) { - state = state.withJoin( - currentTable: table, - currentColumn: table.sharedById, - referencedTable: - i1.$$PartnerEntityTableReferences._sharedByIdTable(db), - referencedColumn: i1.$$PartnerEntityTableReferences - ._sharedByIdTable(db) - .id, - ) as T; - } - if (sharedWithId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.sharedWithId, - referencedTable: i1.$$PartnerEntityTableReferences - ._sharedWithIdTable(db), - referencedColumn: i1.$$PartnerEntityTableReferences - ._sharedWithIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (sharedById) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.sharedById, + referencedTable: i1 + .$$PartnerEntityTableReferences + ._sharedByIdTable(db), + referencedColumn: i1 + .$$PartnerEntityTableReferences + ._sharedByIdTable(db) + .id, + ) + as T; + } + if (sharedWithId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.sharedWithId, + referencedTable: i1 + .$$PartnerEntityTableReferences + ._sharedWithIdTable(db), + referencedColumn: i1 + .$$PartnerEntityTableReferences + ._sharedWithIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$PartnerEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$PartnerEntityTable, - i1.PartnerEntityData, - i1.$$PartnerEntityTableFilterComposer, - i1.$$PartnerEntityTableOrderingComposer, - i1.$$PartnerEntityTableAnnotationComposer, - $$PartnerEntityTableCreateCompanionBuilder, - $$PartnerEntityTableUpdateCompanionBuilder, - (i1.PartnerEntityData, i1.$$PartnerEntityTableReferences), - i1.PartnerEntityData, - i0.PrefetchHooks Function({bool sharedById, bool sharedWithId})>; +typedef $$PartnerEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$PartnerEntityTable, + i1.PartnerEntityData, + i1.$$PartnerEntityTableFilterComposer, + i1.$$PartnerEntityTableOrderingComposer, + i1.$$PartnerEntityTableAnnotationComposer, + $$PartnerEntityTableCreateCompanionBuilder, + $$PartnerEntityTableUpdateCompanionBuilder, + (i1.PartnerEntityData, i1.$$PartnerEntityTableReferences), + i1.PartnerEntityData, + i0.PrefetchHooks Function({bool sharedById, bool sharedWithId}) + >; class $PartnerEntityTable extends i2.PartnerEntity with i0.TableInfo<$PartnerEntityTable, i1.PartnerEntityData> { @@ -371,37 +447,55 @@ class $PartnerEntityTable extends i2.PartnerEntity final i0.GeneratedDatabase attachedDatabase; final String? _alias; $PartnerEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _sharedByIdMeta = - const i0.VerificationMeta('sharedById'); + static const i0.VerificationMeta _sharedByIdMeta = const i0.VerificationMeta( + 'sharedById', + ); @override late final i0.GeneratedColumn sharedById = i0.GeneratedColumn( - 'shared_by_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'shared_by_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); static const i0.VerificationMeta _sharedWithIdMeta = const i0.VerificationMeta('sharedWithId'); @override late final i0.GeneratedColumn sharedWithId = - i0.GeneratedColumn('shared_with_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _inTimelineMeta = - const i0.VerificationMeta('inTimeline'); + i0.GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _inTimelineMeta = const i0.VerificationMeta( + 'inTimeline', + ); @override late final i0.GeneratedColumn inTimeline = i0.GeneratedColumn( - 'in_timeline', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'CHECK ("in_timeline" IN (0, 1))'), - defaultValue: const i3.Constant(false)); + 'in_timeline', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const i3.Constant(false), + ); @override - List get $columns => - [sharedById, sharedWithId, inTimeline]; + List get $columns => [ + sharedById, + sharedWithId, + inTimeline, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -409,31 +503,38 @@ class $PartnerEntityTable extends i2.PartnerEntity static const String $name = 'partner_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('shared_by_id')) { context.handle( + _sharedByIdMeta, + sharedById.isAcceptableOrUnknown( + data['shared_by_id']!, _sharedByIdMeta, - sharedById.isAcceptableOrUnknown( - data['shared_by_id']!, _sharedByIdMeta)); + ), + ); } else if (isInserting) { context.missing(_sharedByIdMeta); } if (data.containsKey('shared_with_id')) { context.handle( + _sharedWithIdMeta, + sharedWithId.isAcceptableOrUnknown( + data['shared_with_id']!, _sharedWithIdMeta, - sharedWithId.isAcceptableOrUnknown( - data['shared_with_id']!, _sharedWithIdMeta)); + ), + ); } else if (isInserting) { context.missing(_sharedWithIdMeta); } if (data.containsKey('in_timeline')) { context.handle( - _inTimelineMeta, - inTimeline.isAcceptableOrUnknown( - data['in_timeline']!, _inTimelineMeta)); + _inTimelineMeta, + inTimeline.isAcceptableOrUnknown(data['in_timeline']!, _inTimelineMeta), + ); } return context; } @@ -445,11 +546,17 @@ class $PartnerEntityTable extends i2.PartnerEntity final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.PartnerEntityData( sharedById: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}shared_by_id'])!, + i0.DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, sharedWithId: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}shared_with_id'])!, - inTimeline: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!, + i0.DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, ); } @@ -469,10 +576,11 @@ class PartnerEntityData extends i0.DataClass final String sharedById; final String sharedWithId; final bool inTimeline; - const PartnerEntityData( - {required this.sharedById, - required this.sharedWithId, - required this.inTimeline}); + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -482,8 +590,10 @@ class PartnerEntityData extends i0.DataClass return map; } - factory PartnerEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory PartnerEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return PartnerEntityData( sharedById: serializer.fromJson(json['sharedById']), @@ -501,22 +611,26 @@ class PartnerEntityData extends i0.DataClass }; } - i1.PartnerEntityData copyWith( - {String? sharedById, String? sharedWithId, bool? inTimeline}) => - i1.PartnerEntityData( - sharedById: sharedById ?? this.sharedById, - sharedWithId: sharedWithId ?? this.sharedWithId, - inTimeline: inTimeline ?? this.inTimeline, - ); + i1.PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => i1.PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); PartnerEntityData copyWithCompanion(i1.PartnerEntityCompanion data) { return PartnerEntityData( - sharedById: - data.sharedById.present ? data.sharedById.value : this.sharedById, + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, sharedWithId: data.sharedWithId.present ? data.sharedWithId.value : this.sharedWithId, - inTimeline: - data.inTimeline.present ? data.inTimeline.value : this.inTimeline, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, ); } @@ -554,8 +668,8 @@ class PartnerEntityCompanion extends i0.UpdateCompanion { required String sharedById, required String sharedWithId, this.inTimeline = const i0.Value.absent(), - }) : sharedById = i0.Value(sharedById), - sharedWithId = i0.Value(sharedWithId); + }) : sharedById = i0.Value(sharedById), + sharedWithId = i0.Value(sharedWithId); static i0.Insertable custom({ i0.Expression? sharedById, i0.Expression? sharedWithId, @@ -568,10 +682,11 @@ class PartnerEntityCompanion extends i0.UpdateCompanion { }); } - i1.PartnerEntityCompanion copyWith( - {i0.Value? sharedById, - i0.Value? sharedWithId, - i0.Value? inTimeline}) { + i1.PartnerEntityCompanion copyWith({ + i0.Value? sharedById, + i0.Value? sharedWithId, + i0.Value? inTimeline, + }) { return i1.PartnerEntityCompanion( sharedById: sharedById ?? this.sharedById, sharedWithId: sharedWithId ?? this.sharedWithId, diff --git a/mobile/lib/infrastructure/entities/person.entity.drift.dart b/mobile/lib/infrastructure/entities/person.entity.drift.dart index 70639adc2f..ffbd796f4b 100644 --- a/mobile/lib/infrastructure/entities/person.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/person.entity.drift.dart @@ -9,61 +9,72 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i4; import 'package:drift/internal/modular.dart' as i5; -typedef $$PersonEntityTableCreateCompanionBuilder = i1.PersonEntityCompanion - Function({ - required String id, - i0.Value createdAt, - i0.Value updatedAt, - required String ownerId, - required String name, - i0.Value faceAssetId, - required bool isFavorite, - required bool isHidden, - i0.Value color, - i0.Value birthDate, -}); -typedef $$PersonEntityTableUpdateCompanionBuilder = i1.PersonEntityCompanion - Function({ - i0.Value id, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value ownerId, - i0.Value name, - i0.Value faceAssetId, - i0.Value isFavorite, - i0.Value isHidden, - i0.Value color, - i0.Value birthDate, -}); +typedef $$PersonEntityTableCreateCompanionBuilder = + i1.PersonEntityCompanion Function({ + required String id, + i0.Value createdAt, + i0.Value updatedAt, + required String ownerId, + required String name, + i0.Value faceAssetId, + required bool isFavorite, + required bool isHidden, + i0.Value color, + i0.Value birthDate, + }); +typedef $$PersonEntityTableUpdateCompanionBuilder = + i1.PersonEntityCompanion Function({ + i0.Value id, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value ownerId, + i0.Value name, + i0.Value faceAssetId, + i0.Value isFavorite, + i0.Value isHidden, + i0.Value color, + i0.Value birthDate, + }); -final class $$PersonEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, i1.$PersonEntityTable, i1.PersonEntityData> { +final class $$PersonEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$PersonEntityTable, + i1.PersonEntityData + > { $$PersonEntityTableReferences(super.$_db, super.$_table, super.$_typedResult); static i4.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) => i5.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( - i5.ReadDatabaseContainer(db) - .resultSet('person_entity') - .ownerId, - i5.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + .createAlias( + i0.$_aliasNameGenerator( + i5.ReadDatabaseContainer( + db, + ).resultSet('person_entity').ownerId, + i5.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i4.$$UserEntityTableProcessedTableManager get ownerId { final $_column = $_itemColumn('owner_id')!; final manager = i4 .$$UserEntityTableTableManager( + $_db, + i5.ReadDatabaseContainer( $_db, - i5.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_ownerIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -77,52 +88,74 @@ class $$PersonEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnFilters(column)); + column: $table.name, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get faceAssetId => $composableBuilder( - column: $table.faceAssetId, - builder: (column) => i0.ColumnFilters(column)); + column: $table.faceAssetId, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get isFavorite => $composableBuilder( - column: $table.isFavorite, builder: (column) => i0.ColumnFilters(column)); + column: $table.isFavorite, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get isHidden => $composableBuilder( - column: $table.isHidden, builder: (column) => i0.ColumnFilters(column)); + column: $table.isHidden, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get color => $composableBuilder( - column: $table.color, builder: (column) => i0.ColumnFilters(column)); + column: $table.color, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get birthDate => $composableBuilder( - column: $table.birthDate, builder: (column) => i0.ColumnFilters(column)); + column: $table.birthDate, + builder: (column) => i0.ColumnFilters(column), + ); i4.$$UserEntityTableFilterComposer get ownerId { final i4.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableFilterComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableFilterComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -137,56 +170,74 @@ class $$PersonEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnOrderings(column)); + column: $table.name, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get faceAssetId => $composableBuilder( - column: $table.faceAssetId, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.faceAssetId, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isFavorite => $composableBuilder( - column: $table.isFavorite, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.isFavorite, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isHidden => $composableBuilder( - column: $table.isHidden, builder: (column) => i0.ColumnOrderings(column)); + column: $table.isHidden, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get color => $composableBuilder( - column: $table.color, builder: (column) => i0.ColumnOrderings(column)); + column: $table.color, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get birthDate => $composableBuilder( - column: $table.birthDate, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.birthDate, + builder: (column) => i0.ColumnOrderings(column), + ); i4.$$UserEntityTableOrderingComposer get ownerId { final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -213,10 +264,14 @@ class $$PersonEntityTableAnnotationComposer $composableBuilder(column: $table.name, builder: (column) => column); i0.GeneratedColumn get faceAssetId => $composableBuilder( - column: $table.faceAssetId, builder: (column) => column); + column: $table.faceAssetId, + builder: (column) => column, + ); i0.GeneratedColumn get isFavorite => $composableBuilder( - column: $table.isFavorite, builder: (column) => column); + column: $table.isFavorite, + builder: (column) => column, + ); i0.GeneratedColumn get isHidden => $composableBuilder(column: $table.isHidden, builder: (column) => column); @@ -229,42 +284,52 @@ class $$PersonEntityTableAnnotationComposer i4.$$UserEntityTableAnnotationComposer get ownerId { final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$PersonEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$PersonEntityTable, - i1.PersonEntityData, - i1.$$PersonEntityTableFilterComposer, - i1.$$PersonEntityTableOrderingComposer, - i1.$$PersonEntityTableAnnotationComposer, - $$PersonEntityTableCreateCompanionBuilder, - $$PersonEntityTableUpdateCompanionBuilder, - (i1.PersonEntityData, i1.$$PersonEntityTableReferences), - i1.PersonEntityData, - i0.PrefetchHooks Function({bool ownerId})> { +class $$PersonEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$PersonEntityTable, + i1.PersonEntityData, + i1.$$PersonEntityTableFilterComposer, + i1.$$PersonEntityTableOrderingComposer, + i1.$$PersonEntityTableAnnotationComposer, + $$PersonEntityTableCreateCompanionBuilder, + $$PersonEntityTableUpdateCompanionBuilder, + (i1.PersonEntityData, i1.$$PersonEntityTableReferences), + i1.PersonEntityData, + i0.PrefetchHooks Function({bool ownerId}) + > { $$PersonEntityTableTableManager( - i0.GeneratedDatabase db, i1.$PersonEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$PersonEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -273,66 +338,69 @@ class $$PersonEntityTableTableManager extends i0.RootTableManager< i1.$$PersonEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$PersonEntityTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - i0.Value id = const i0.Value.absent(), - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value ownerId = const i0.Value.absent(), - i0.Value name = const i0.Value.absent(), - i0.Value faceAssetId = const i0.Value.absent(), - i0.Value isFavorite = const i0.Value.absent(), - i0.Value isHidden = const i0.Value.absent(), - i0.Value color = const i0.Value.absent(), - i0.Value birthDate = const i0.Value.absent(), - }) => - i1.PersonEntityCompanion( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - name: name, - faceAssetId: faceAssetId, - isFavorite: isFavorite, - isHidden: isHidden, - color: color, - birthDate: birthDate, - ), - createCompanionCallback: ({ - required String id, - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - required String ownerId, - required String name, - i0.Value faceAssetId = const i0.Value.absent(), - required bool isFavorite, - required bool isHidden, - i0.Value color = const i0.Value.absent(), - i0.Value birthDate = const i0.Value.absent(), - }) => - i1.PersonEntityCompanion.insert( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - name: name, - faceAssetId: faceAssetId, - isFavorite: isFavorite, - isHidden: isHidden, - color: color, - birthDate: birthDate, - ), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value ownerId = const i0.Value.absent(), + i0.Value name = const i0.Value.absent(), + i0.Value faceAssetId = const i0.Value.absent(), + i0.Value isFavorite = const i0.Value.absent(), + i0.Value isHidden = const i0.Value.absent(), + i0.Value color = const i0.Value.absent(), + i0.Value birthDate = const i0.Value.absent(), + }) => i1.PersonEntityCompanion( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + name: name, + faceAssetId: faceAssetId, + isFavorite: isFavorite, + isHidden: isHidden, + color: color, + birthDate: birthDate, + ), + createCompanionCallback: + ({ + required String id, + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + required String ownerId, + required String name, + i0.Value faceAssetId = const i0.Value.absent(), + required bool isFavorite, + required bool isHidden, + i0.Value color = const i0.Value.absent(), + i0.Value birthDate = const i0.Value.absent(), + }) => i1.PersonEntityCompanion.insert( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + name: name, + faceAssetId: faceAssetId, + isFavorite: isFavorite, + isHidden: isHidden, + color: color, + birthDate: birthDate, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$PersonEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$PersonEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({ownerId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -343,40 +411,50 @@ class $$PersonEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (ownerId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.ownerId, - referencedTable: - i1.$$PersonEntityTableReferences._ownerIdTable(db), - referencedColumn: - i1.$$PersonEntityTableReferences._ownerIdTable(db).id, - ) as T; - } + dynamic + > + >(state) { + if (ownerId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.ownerId, + referencedTable: i1 + .$$PersonEntityTableReferences + ._ownerIdTable(db), + referencedColumn: i1 + .$$PersonEntityTableReferences + ._ownerIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$PersonEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$PersonEntityTable, - i1.PersonEntityData, - i1.$$PersonEntityTableFilterComposer, - i1.$$PersonEntityTableOrderingComposer, - i1.$$PersonEntityTableAnnotationComposer, - $$PersonEntityTableCreateCompanionBuilder, - $$PersonEntityTableUpdateCompanionBuilder, - (i1.PersonEntityData, i1.$$PersonEntityTableReferences), - i1.PersonEntityData, - i0.PrefetchHooks Function({bool ownerId})>; +typedef $$PersonEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$PersonEntityTable, + i1.PersonEntityData, + i1.$$PersonEntityTableFilterComposer, + i1.$$PersonEntityTableOrderingComposer, + i1.$$PersonEntityTableAnnotationComposer, + $$PersonEntityTableCreateCompanionBuilder, + $$PersonEntityTableUpdateCompanionBuilder, + (i1.PersonEntityData, i1.$$PersonEntityTableReferences), + i1.PersonEntityData, + i0.PrefetchHooks Function({bool ownerId}) + >; class $PersonEntityTable extends i2.PersonEntity with i0.TableInfo<$PersonEntityTable, i1.PersonEntityData> { @@ -387,88 +465,139 @@ class $PersonEntityTable extends i2.PersonEntity static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _createdAtMeta = - const i0.VerificationMeta('createdAt'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta( + 'createdAt', + ); @override late final i0.GeneratedColumn createdAt = - i0.GeneratedColumn('created_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i3.currentDateAndTime); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + i0.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i3.currentDateAndTime, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i3.currentDateAndTime); - static const i0.VerificationMeta _ownerIdMeta = - const i0.VerificationMeta('ownerId'); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i3.currentDateAndTime, + ); + static const i0.VerificationMeta _ownerIdMeta = const i0.VerificationMeta( + 'ownerId', + ); @override late final i0.GeneratedColumn ownerId = i0.GeneratedColumn( - 'owner_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _nameMeta = - const i0.VerificationMeta('name'); + 'owner_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta( + 'name', + ); @override late final i0.GeneratedColumn name = i0.GeneratedColumn( - 'name', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _faceAssetIdMeta = - const i0.VerificationMeta('faceAssetId'); + 'name', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _faceAssetIdMeta = const i0.VerificationMeta( + 'faceAssetId', + ); @override late final i0.GeneratedColumn faceAssetId = - i0.GeneratedColumn('face_asset_id', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _isFavoriteMeta = - const i0.VerificationMeta('isFavorite'); + i0.GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _isFavoriteMeta = const i0.VerificationMeta( + 'isFavorite', + ); @override late final i0.GeneratedColumn isFavorite = i0.GeneratedColumn( - 'is_favorite', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_favorite" IN (0, 1))')); - static const i0.VerificationMeta _isHiddenMeta = - const i0.VerificationMeta('isHidden'); + 'is_favorite', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + static const i0.VerificationMeta _isHiddenMeta = const i0.VerificationMeta( + 'isHidden', + ); @override late final i0.GeneratedColumn isHidden = i0.GeneratedColumn( - 'is_hidden', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_hidden" IN (0, 1))')); - static const i0.VerificationMeta _colorMeta = - const i0.VerificationMeta('color'); + 'is_hidden', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + static const i0.VerificationMeta _colorMeta = const i0.VerificationMeta( + 'color', + ); @override late final i0.GeneratedColumn color = i0.GeneratedColumn( - 'color', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _birthDateMeta = - const i0.VerificationMeta('birthDate'); + 'color', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _birthDateMeta = const i0.VerificationMeta( + 'birthDate', + ); @override late final i0.GeneratedColumn birthDate = - i0.GeneratedColumn('birth_date', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); + i0.GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override List get $columns => [ - id, - createdAt, - updatedAt, - ownerId, - name, - faceAssetId, - isFavorite, - isHidden, - color, - birthDate - ]; + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -476,8 +605,9 @@ class $PersonEntityTable extends i2.PersonEntity static const String $name = 'person_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { @@ -486,52 +616,69 @@ class $PersonEntityTable extends i2.PersonEntity context.missing(_idMeta); } if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('owner_id')) { - context.handle(_ownerIdMeta, - ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta)); + context.handle( + _ownerIdMeta, + ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta), + ); } else if (isInserting) { context.missing(_ownerIdMeta); } if (data.containsKey('name')) { context.handle( - _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta)); + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); } else if (isInserting) { context.missing(_nameMeta); } if (data.containsKey('face_asset_id')) { context.handle( + _faceAssetIdMeta, + faceAssetId.isAcceptableOrUnknown( + data['face_asset_id']!, _faceAssetIdMeta, - faceAssetId.isAcceptableOrUnknown( - data['face_asset_id']!, _faceAssetIdMeta)); + ), + ); } if (data.containsKey('is_favorite')) { context.handle( - _isFavoriteMeta, - isFavorite.isAcceptableOrUnknown( - data['is_favorite']!, _isFavoriteMeta)); + _isFavoriteMeta, + isFavorite.isAcceptableOrUnknown(data['is_favorite']!, _isFavoriteMeta), + ); } else if (isInserting) { context.missing(_isFavoriteMeta); } if (data.containsKey('is_hidden')) { - context.handle(_isHiddenMeta, - isHidden.isAcceptableOrUnknown(data['is_hidden']!, _isHiddenMeta)); + context.handle( + _isHiddenMeta, + isHidden.isAcceptableOrUnknown(data['is_hidden']!, _isHiddenMeta), + ); } else if (isInserting) { context.missing(_isHiddenMeta); } if (data.containsKey('color')) { context.handle( - _colorMeta, color.isAcceptableOrUnknown(data['color']!, _colorMeta)); + _colorMeta, + color.isAcceptableOrUnknown(data['color']!, _colorMeta), + ); } if (data.containsKey('birth_date')) { - context.handle(_birthDateMeta, - birthDate.isAcceptableOrUnknown(data['birth_date']!, _birthDateMeta)); + context.handle( + _birthDateMeta, + birthDate.isAcceptableOrUnknown(data['birth_date']!, _birthDateMeta), + ); } return context; } @@ -542,26 +689,46 @@ class $PersonEntityTable extends i2.PersonEntity i1.PersonEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.PersonEntityData( - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, createdAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - name: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}name'], + )!, faceAssetId: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}face_asset_id']), - isFavorite: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - isHidden: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_hidden'])!, - color: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}color']), - birthDate: attachedDatabase.typeMapping - .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}birth_date']), + i0.DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + isFavorite: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), ); } @@ -588,17 +755,18 @@ class PersonEntityData extends i0.DataClass final bool isHidden; final String? color; final DateTime? birthDate; - const PersonEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.name, - this.faceAssetId, - required this.isFavorite, - required this.isHidden, - this.color, - this.birthDate}); + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -621,8 +789,10 @@ class PersonEntityData extends i0.DataClass return map; } - factory PersonEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory PersonEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return PersonEntityData( id: serializer.fromJson(json['id']), @@ -654,29 +824,29 @@ class PersonEntityData extends i0.DataClass }; } - i1.PersonEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? name, - i0.Value faceAssetId = const i0.Value.absent(), - bool? isFavorite, - bool? isHidden, - i0.Value color = const i0.Value.absent(), - i0.Value birthDate = const i0.Value.absent()}) => - i1.PersonEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - name: name ?? this.name, - faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, - isFavorite: isFavorite ?? this.isFavorite, - isHidden: isHidden ?? this.isHidden, - color: color.present ? color.value : this.color, - birthDate: birthDate.present ? birthDate.value : this.birthDate, - ); + i1.PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + i0.Value faceAssetId = const i0.Value.absent(), + bool? isFavorite, + bool? isHidden, + i0.Value color = const i0.Value.absent(), + i0.Value birthDate = const i0.Value.absent(), + }) => i1.PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); PersonEntityData copyWithCompanion(i1.PersonEntityCompanion data) { return PersonEntityData( id: data.id.present ? data.id.value : this.id, @@ -684,10 +854,12 @@ class PersonEntityData extends i0.DataClass updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, name: data.name.present ? data.name.value : this.name, - faceAssetId: - data.faceAssetId.present ? data.faceAssetId.value : this.faceAssetId, - isFavorite: - data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, color: data.color.present ? data.color.value : this.color, birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, @@ -712,8 +884,18 @@ class PersonEntityData extends i0.DataClass } @override - int get hashCode => Object.hash(id, createdAt, updatedAt, ownerId, name, - faceAssetId, isFavorite, isHidden, color, birthDate); + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -764,11 +946,11 @@ class PersonEntityCompanion extends i0.UpdateCompanion { required bool isHidden, this.color = const i0.Value.absent(), this.birthDate = const i0.Value.absent(), - }) : id = i0.Value(id), - ownerId = i0.Value(ownerId), - name = i0.Value(name), - isFavorite = i0.Value(isFavorite), - isHidden = i0.Value(isHidden); + }) : id = i0.Value(id), + ownerId = i0.Value(ownerId), + name = i0.Value(name), + isFavorite = i0.Value(isFavorite), + isHidden = i0.Value(isHidden); static i0.Insertable custom({ i0.Expression? id, i0.Expression? createdAt, @@ -795,17 +977,18 @@ class PersonEntityCompanion extends i0.UpdateCompanion { }); } - i1.PersonEntityCompanion copyWith( - {i0.Value? id, - i0.Value? createdAt, - i0.Value? updatedAt, - i0.Value? ownerId, - i0.Value? name, - i0.Value? faceAssetId, - i0.Value? isFavorite, - i0.Value? isHidden, - i0.Value? color, - i0.Value? birthDate}) { + i1.PersonEntityCompanion copyWith({ + i0.Value? id, + i0.Value? createdAt, + i0.Value? updatedAt, + i0.Value? ownerId, + i0.Value? name, + i0.Value? faceAssetId, + i0.Value? isFavorite, + i0.Value? isHidden, + i0.Value? color, + i0.Value? birthDate, + }) { return i1.PersonEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, diff --git a/mobile/lib/infrastructure/entities/remote_album.entity.drift.dart b/mobile/lib/infrastructure/entities/remote_album.entity.drift.dart index bc13c8cb5c..30a6d0b535 100644 --- a/mobile/lib/infrastructure/entities/remote_album.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/remote_album.entity.drift.dart @@ -13,89 +13,107 @@ import 'package:drift/internal/modular.dart' as i6; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' as i7; -typedef $$RemoteAlbumEntityTableCreateCompanionBuilder - = i1.RemoteAlbumEntityCompanion Function({ - required String id, - required String name, - i0.Value description, - i0.Value createdAt, - i0.Value updatedAt, - required String ownerId, - i0.Value thumbnailAssetId, - i0.Value isActivityEnabled, - required i2.AlbumAssetOrder order, -}); -typedef $$RemoteAlbumEntityTableUpdateCompanionBuilder - = i1.RemoteAlbumEntityCompanion Function({ - i0.Value id, - i0.Value name, - i0.Value description, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value ownerId, - i0.Value thumbnailAssetId, - i0.Value isActivityEnabled, - i0.Value order, -}); +typedef $$RemoteAlbumEntityTableCreateCompanionBuilder = + i1.RemoteAlbumEntityCompanion Function({ + required String id, + required String name, + i0.Value description, + i0.Value createdAt, + i0.Value updatedAt, + required String ownerId, + i0.Value thumbnailAssetId, + i0.Value isActivityEnabled, + required i2.AlbumAssetOrder order, + }); +typedef $$RemoteAlbumEntityTableUpdateCompanionBuilder = + i1.RemoteAlbumEntityCompanion Function({ + i0.Value id, + i0.Value name, + i0.Value description, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value ownerId, + i0.Value thumbnailAssetId, + i0.Value isActivityEnabled, + i0.Value order, + }); -final class $$RemoteAlbumEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, - i1.$RemoteAlbumEntityTable, - i1.RemoteAlbumEntityData> { +final class $$RemoteAlbumEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$RemoteAlbumEntityTable, + i1.RemoteAlbumEntityData + > { $$RemoteAlbumEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i5.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) => i6.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i6.ReadDatabaseContainer(db) .resultSet('remote_album_entity') .ownerId, - i6.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + i6.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i5.$$UserEntityTableProcessedTableManager get ownerId { final $_column = $_itemColumn('owner_id')!; final manager = i5 .$$UserEntityTableTableManager( + $_db, + i6.ReadDatabaseContainer( $_db, - i6.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_ownerIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } static i7.$RemoteAssetEntityTable _thumbnailAssetIdTable( - i0.GeneratedDatabase db) => - i6.ReadDatabaseContainer(db) - .resultSet('remote_asset_entity') - .createAlias(i0.$_aliasNameGenerator( - i6.ReadDatabaseContainer(db) - .resultSet('remote_album_entity') - .thumbnailAssetId, - i6.ReadDatabaseContainer(db) - .resultSet('remote_asset_entity') - .id)); + i0.GeneratedDatabase db, + ) => i6.ReadDatabaseContainer(db) + .resultSet('remote_asset_entity') + .createAlias( + i0.$_aliasNameGenerator( + i6.ReadDatabaseContainer(db) + .resultSet('remote_album_entity') + .thumbnailAssetId, + i6.ReadDatabaseContainer( + db, + ).resultSet('remote_asset_entity').id, + ), + ); i7.$$RemoteAssetEntityTableProcessedTableManager? get thumbnailAssetId { final $_column = $_itemColumn('thumbnail_asset_id'); if ($_column == null) return null; final manager = i7 .$$RemoteAssetEntityTableTableManager( + $_db, + i6.ReadDatabaseContainer( $_db, - i6.ReadDatabaseContainer($_db) - .resultSet('remote_asset_entity')) + ).resultSet('remote_asset_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_thumbnailAssetIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -109,71 +127,92 @@ class $$RemoteAlbumEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnFilters(column)); + column: $table.name, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get description => $composableBuilder( - column: $table.description, - builder: (column) => i0.ColumnFilters(column)); + column: $table.description, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get isActivityEnabled => $composableBuilder( - column: $table.isActivityEnabled, - builder: (column) => i0.ColumnFilters(column)); + column: $table.isActivityEnabled, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnWithTypeConverterFilters - get order => $composableBuilder( - column: $table.order, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + get order => $composableBuilder( + column: $table.order, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i5.$$UserEntityTableFilterComposer get ownerId { final i5.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableFilterComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableFilterComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i7.$$RemoteAssetEntityTableFilterComposer get thumbnailAssetId { final i7.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.thumbnailAssetId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i7.$$RemoteAssetEntityTableFilterComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.thumbnailAssetId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i7.$$RemoteAssetEntityTableFilterComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -188,73 +227,92 @@ class $$RemoteAlbumEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnOrderings(column)); + column: $table.name, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get description => $composableBuilder( - column: $table.description, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.description, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isActivityEnabled => $composableBuilder( - column: $table.isActivityEnabled, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.isActivityEnabled, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get order => $composableBuilder( - column: $table.order, builder: (column) => i0.ColumnOrderings(column)); + column: $table.order, + builder: (column) => i0.ColumnOrderings(column), + ); i5.$$UserEntityTableOrderingComposer get ownerId { final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i7.$$RemoteAssetEntityTableOrderingComposer get thumbnailAssetId { final i7.$$RemoteAssetEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.thumbnailAssetId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i7.$$RemoteAssetEntityTableOrderingComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.thumbnailAssetId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i7.$$RemoteAssetEntityTableOrderingComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -275,7 +333,9 @@ class $$RemoteAlbumEntityTableAnnotationComposer $composableBuilder(column: $table.name, builder: (column) => column); i0.GeneratedColumn get description => $composableBuilder( - column: $table.description, builder: (column) => column); + column: $table.description, + builder: (column) => column, + ); i0.GeneratedColumn get createdAt => $composableBuilder(column: $table.createdAt, builder: (column) => column); @@ -284,73 +344,89 @@ class $$RemoteAlbumEntityTableAnnotationComposer $composableBuilder(column: $table.updatedAt, builder: (column) => column); i0.GeneratedColumn get isActivityEnabled => $composableBuilder( - column: $table.isActivityEnabled, builder: (column) => column); + column: $table.isActivityEnabled, + builder: (column) => column, + ); i0.GeneratedColumnWithTypeConverter get order => $composableBuilder(column: $table.order, builder: (column) => column); i5.$$UserEntityTableAnnotationComposer get ownerId { final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i7.$$RemoteAssetEntityTableAnnotationComposer get thumbnailAssetId { final i7.$$RemoteAssetEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.thumbnailAssetId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i7.$$RemoteAssetEntityTableAnnotationComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.thumbnailAssetId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i7.$$RemoteAssetEntityTableAnnotationComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$RemoteAlbumEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$RemoteAlbumEntityTable, - i1.RemoteAlbumEntityData, - i1.$$RemoteAlbumEntityTableFilterComposer, - i1.$$RemoteAlbumEntityTableOrderingComposer, - i1.$$RemoteAlbumEntityTableAnnotationComposer, - $$RemoteAlbumEntityTableCreateCompanionBuilder, - $$RemoteAlbumEntityTableUpdateCompanionBuilder, - (i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences), - i1.RemoteAlbumEntityData, - i0.PrefetchHooks Function({bool ownerId, bool thumbnailAssetId})> { +class $$RemoteAlbumEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$RemoteAlbumEntityTable, + i1.RemoteAlbumEntityData, + i1.$$RemoteAlbumEntityTableFilterComposer, + i1.$$RemoteAlbumEntityTableOrderingComposer, + i1.$$RemoteAlbumEntityTableAnnotationComposer, + $$RemoteAlbumEntityTableCreateCompanionBuilder, + $$RemoteAlbumEntityTableUpdateCompanionBuilder, + (i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences), + i1.RemoteAlbumEntityData, + i0.PrefetchHooks Function({bool ownerId, bool thumbnailAssetId}) + > { $$RemoteAlbumEntityTableTableManager( - i0.GeneratedDatabase db, i1.$RemoteAlbumEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$RemoteAlbumEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -359,63 +435,68 @@ class $$RemoteAlbumEntityTableTableManager extends i0.RootTableManager< .$$RemoteAlbumEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$RemoteAlbumEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value id = const i0.Value.absent(), - i0.Value name = const i0.Value.absent(), - i0.Value description = const i0.Value.absent(), - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value ownerId = const i0.Value.absent(), - i0.Value thumbnailAssetId = const i0.Value.absent(), - i0.Value isActivityEnabled = const i0.Value.absent(), - i0.Value order = const i0.Value.absent(), - }) => - i1.RemoteAlbumEntityCompanion( - id: id, - name: name, - description: description, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - thumbnailAssetId: thumbnailAssetId, - isActivityEnabled: isActivityEnabled, - order: order, - ), - createCompanionCallback: ({ - required String id, - required String name, - i0.Value description = const i0.Value.absent(), - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - required String ownerId, - i0.Value thumbnailAssetId = const i0.Value.absent(), - i0.Value isActivityEnabled = const i0.Value.absent(), - required i2.AlbumAssetOrder order, - }) => - i1.RemoteAlbumEntityCompanion.insert( - id: id, - name: name, - description: description, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - thumbnailAssetId: thumbnailAssetId, - isActivityEnabled: isActivityEnabled, - order: order, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value name = const i0.Value.absent(), + i0.Value description = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value ownerId = const i0.Value.absent(), + i0.Value thumbnailAssetId = const i0.Value.absent(), + i0.Value isActivityEnabled = const i0.Value.absent(), + i0.Value order = const i0.Value.absent(), + }) => i1.RemoteAlbumEntityCompanion( + id: id, + name: name, + description: description, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + thumbnailAssetId: thumbnailAssetId, + isActivityEnabled: isActivityEnabled, + order: order, + ), + createCompanionCallback: + ({ + required String id, + required String name, + i0.Value description = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + required String ownerId, + i0.Value thumbnailAssetId = const i0.Value.absent(), + i0.Value isActivityEnabled = const i0.Value.absent(), + required i2.AlbumAssetOrder order, + }) => i1.RemoteAlbumEntityCompanion.insert( + id: id, + name: name, + description: description, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + thumbnailAssetId: thumbnailAssetId, + isActivityEnabled: isActivityEnabled, + order: order, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$RemoteAlbumEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$RemoteAlbumEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({ownerId = false, thumbnailAssetId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -426,53 +507,65 @@ class $$RemoteAlbumEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (ownerId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.ownerId, - referencedTable: - i1.$$RemoteAlbumEntityTableReferences._ownerIdTable(db), - referencedColumn: i1.$$RemoteAlbumEntityTableReferences - ._ownerIdTable(db) - .id, - ) as T; - } - if (thumbnailAssetId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.thumbnailAssetId, - referencedTable: i1.$$RemoteAlbumEntityTableReferences - ._thumbnailAssetIdTable(db), - referencedColumn: i1.$$RemoteAlbumEntityTableReferences - ._thumbnailAssetIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (ownerId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.ownerId, + referencedTable: i1 + .$$RemoteAlbumEntityTableReferences + ._ownerIdTable(db), + referencedColumn: i1 + .$$RemoteAlbumEntityTableReferences + ._ownerIdTable(db) + .id, + ) + as T; + } + if (thumbnailAssetId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.thumbnailAssetId, + referencedTable: i1 + .$$RemoteAlbumEntityTableReferences + ._thumbnailAssetIdTable(db), + referencedColumn: i1 + .$$RemoteAlbumEntityTableReferences + ._thumbnailAssetIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$RemoteAlbumEntityTableProcessedTableManager - = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$RemoteAlbumEntityTable, - i1.RemoteAlbumEntityData, - i1.$$RemoteAlbumEntityTableFilterComposer, - i1.$$RemoteAlbumEntityTableOrderingComposer, - i1.$$RemoteAlbumEntityTableAnnotationComposer, - $$RemoteAlbumEntityTableCreateCompanionBuilder, - $$RemoteAlbumEntityTableUpdateCompanionBuilder, - (i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences), - i1.RemoteAlbumEntityData, - i0.PrefetchHooks Function({bool ownerId, bool thumbnailAssetId})>; +typedef $$RemoteAlbumEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$RemoteAlbumEntityTable, + i1.RemoteAlbumEntityData, + i1.$$RemoteAlbumEntityTableFilterComposer, + i1.$$RemoteAlbumEntityTableOrderingComposer, + i1.$$RemoteAlbumEntityTableAnnotationComposer, + $$RemoteAlbumEntityTableCreateCompanionBuilder, + $$RemoteAlbumEntityTableUpdateCompanionBuilder, + (i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences), + i1.RemoteAlbumEntityData, + i0.PrefetchHooks Function({bool ownerId, bool thumbnailAssetId}) + >; class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity with i0.TableInfo<$RemoteAlbumEntityTable, i1.RemoteAlbumEntityData> { @@ -483,84 +576,129 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _nameMeta = - const i0.VerificationMeta('name'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta( + 'name', + ); @override late final i0.GeneratedColumn name = i0.GeneratedColumn( - 'name', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _descriptionMeta = - const i0.VerificationMeta('description'); + 'name', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _descriptionMeta = const i0.VerificationMeta( + 'description', + ); @override late final i0.GeneratedColumn description = - i0.GeneratedColumn('description', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: false, - defaultValue: const i4.Constant('')); - static const i0.VerificationMeta _createdAtMeta = - const i0.VerificationMeta('createdAt'); + i0.GeneratedColumn( + 'description', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const i4.Constant(''), + ); + static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta( + 'createdAt', + ); @override late final i0.GeneratedColumn createdAt = - i0.GeneratedColumn('created_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + i0.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _ownerIdMeta = - const i0.VerificationMeta('ownerId'); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _ownerIdMeta = const i0.VerificationMeta( + 'ownerId', + ); @override late final i0.GeneratedColumn ownerId = i0.GeneratedColumn( - 'owner_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); static const i0.VerificationMeta _thumbnailAssetIdMeta = const i0.VerificationMeta('thumbnailAssetId'); @override late final i0.GeneratedColumn thumbnailAssetId = - i0.GeneratedColumn('thumbnail_asset_id', aliasedName, true, - type: i0.DriftSqlType.string, - requiredDuringInsert: false, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL')); + i0.GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); static const i0.VerificationMeta _isActivityEnabledMeta = const i0.VerificationMeta('isActivityEnabled'); @override late final i0.GeneratedColumn isActivityEnabled = - i0.GeneratedColumn('is_activity_enabled', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_activity_enabled" IN (0, 1))'), - defaultValue: const i4.Constant(true)); + i0.GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const i4.Constant(true), + ); @override late final i0.GeneratedColumnWithTypeConverter - order = i0.GeneratedColumn('order', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$RemoteAlbumEntityTable.$converterorder); + order = + i0.GeneratedColumn( + 'order', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter( + i1.$RemoteAlbumEntityTable.$converterorder, + ); @override List get $columns => [ - id, - name, - description, - createdAt, - updatedAt, - ownerId, - thumbnailAssetId, - isActivityEnabled, - order - ]; + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -568,8 +706,9 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity static const String $name = 'remote_album_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { @@ -579,41 +718,58 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity } if (data.containsKey('name')) { context.handle( - _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta)); + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); } else if (isInserting) { context.missing(_nameMeta); } if (data.containsKey('description')) { context.handle( + _descriptionMeta, + description.isAcceptableOrUnknown( + data['description']!, _descriptionMeta, - description.isAcceptableOrUnknown( - data['description']!, _descriptionMeta)); + ), + ); } if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('owner_id')) { - context.handle(_ownerIdMeta, - ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta)); + context.handle( + _ownerIdMeta, + ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta), + ); } else if (isInserting) { context.missing(_ownerIdMeta); } if (data.containsKey('thumbnail_asset_id')) { context.handle( + _thumbnailAssetIdMeta, + thumbnailAssetId.isAcceptableOrUnknown( + data['thumbnail_asset_id']!, _thumbnailAssetIdMeta, - thumbnailAssetId.isAcceptableOrUnknown( - data['thumbnail_asset_id']!, _thumbnailAssetIdMeta)); + ), + ); } if (data.containsKey('is_activity_enabled')) { context.handle( + _isActivityEnabledMeta, + isActivityEnabled.isAcceptableOrUnknown( + data['is_activity_enabled']!, _isActivityEnabledMeta, - isActivityEnabled.isAcceptableOrUnknown( - data['is_activity_enabled']!, _isActivityEnabledMeta)); + ), + ); } return context; } @@ -621,29 +777,50 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity @override Set get $primaryKey => {id}; @override - i1.RemoteAlbumEntityData map(Map data, - {String? tablePrefix}) { + i1.RemoteAlbumEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.RemoteAlbumEntityData( - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, - description: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}description'])!, + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}description'], + )!, createdAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, thumbnailAssetId: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}thumbnail_asset_id']), + i0.DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), isActivityEnabled: attachedDatabase.typeMapping.read( - i0.DriftSqlType.bool, data['${effectivePrefix}is_activity_enabled'])!, - order: i1.$RemoteAlbumEntityTable.$converterorder.fromSql(attachedDatabase - .typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}order'])!), + i0.DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: i1.$RemoteAlbumEntityTable.$converterorder.fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}order'], + )!, + ), ); } @@ -654,7 +831,8 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity static i0.JsonTypeConverter2 $converterorder = const i0.EnumIndexConverter( - i2.AlbumAssetOrder.values); + i2.AlbumAssetOrder.values, + ); @override bool get withoutRowId => true; @override @@ -672,16 +850,17 @@ class RemoteAlbumEntityData extends i0.DataClass final String? thumbnailAssetId; final bool isActivityEnabled; final i2.AlbumAssetOrder order; - const RemoteAlbumEntityData( - {required this.id, - required this.name, - required this.description, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - this.thumbnailAssetId, - required this.isActivityEnabled, - required this.order}); + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -697,13 +876,16 @@ class RemoteAlbumEntityData extends i0.DataClass map['is_activity_enabled'] = i0.Variable(isActivityEnabled); { map['order'] = i0.Variable( - i1.$RemoteAlbumEntityTable.$converterorder.toSql(order)); + i1.$RemoteAlbumEntityTable.$converterorder.toSql(order), + ); } return map; } - factory RemoteAlbumEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory RemoteAlbumEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return RemoteAlbumEntityData( id: serializer.fromJson(json['id']), @@ -714,8 +896,9 @@ class RemoteAlbumEntityData extends i0.DataClass ownerId: serializer.fromJson(json['ownerId']), thumbnailAssetId: serializer.fromJson(json['thumbnailAssetId']), isActivityEnabled: serializer.fromJson(json['isActivityEnabled']), - order: i1.$RemoteAlbumEntityTable.$converterorder - .fromJson(serializer.fromJson(json['order'])), + order: i1.$RemoteAlbumEntityTable.$converterorder.fromJson( + serializer.fromJson(json['order']), + ), ); } @override @@ -731,39 +914,41 @@ class RemoteAlbumEntityData extends i0.DataClass 'thumbnailAssetId': serializer.toJson(thumbnailAssetId), 'isActivityEnabled': serializer.toJson(isActivityEnabled), 'order': serializer.toJson( - i1.$RemoteAlbumEntityTable.$converterorder.toJson(order)), + i1.$RemoteAlbumEntityTable.$converterorder.toJson(order), + ), }; } - i1.RemoteAlbumEntityData copyWith( - {String? id, - String? name, - String? description, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - i0.Value thumbnailAssetId = const i0.Value.absent(), - bool? isActivityEnabled, - i2.AlbumAssetOrder? order}) => - i1.RemoteAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - description: description ?? this.description, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - thumbnailAssetId: thumbnailAssetId.present - ? thumbnailAssetId.value - : this.thumbnailAssetId, - isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, - order: order ?? this.order, - ); + i1.RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + i0.Value thumbnailAssetId = const i0.Value.absent(), + bool? isActivityEnabled, + i2.AlbumAssetOrder? order, + }) => i1.RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); RemoteAlbumEntityData copyWithCompanion(i1.RemoteAlbumEntityCompanion data) { return RemoteAlbumEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, - description: - data.description.present ? data.description.value : this.description, + description: data.description.present + ? data.description.value + : this.description, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, @@ -794,8 +979,17 @@ class RemoteAlbumEntityData extends i0.DataClass } @override - int get hashCode => Object.hash(id, name, description, createdAt, updatedAt, - ownerId, thumbnailAssetId, isActivityEnabled, order); + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -843,10 +1037,10 @@ class RemoteAlbumEntityCompanion this.thumbnailAssetId = const i0.Value.absent(), this.isActivityEnabled = const i0.Value.absent(), required i2.AlbumAssetOrder order, - }) : id = i0.Value(id), - name = i0.Value(name), - ownerId = i0.Value(ownerId), - order = i0.Value(order); + }) : id = i0.Value(id), + name = i0.Value(name), + ownerId = i0.Value(ownerId), + order = i0.Value(order); static i0.Insertable custom({ i0.Expression? id, i0.Expression? name, @@ -871,16 +1065,17 @@ class RemoteAlbumEntityCompanion }); } - i1.RemoteAlbumEntityCompanion copyWith( - {i0.Value? id, - i0.Value? name, - i0.Value? description, - i0.Value? createdAt, - i0.Value? updatedAt, - i0.Value? ownerId, - i0.Value? thumbnailAssetId, - i0.Value? isActivityEnabled, - i0.Value? order}) { + i1.RemoteAlbumEntityCompanion copyWith({ + i0.Value? id, + i0.Value? name, + i0.Value? description, + i0.Value? createdAt, + i0.Value? updatedAt, + i0.Value? ownerId, + i0.Value? thumbnailAssetId, + i0.Value? isActivityEnabled, + i0.Value? order, + }) { return i1.RemoteAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -923,7 +1118,8 @@ class RemoteAlbumEntityCompanion } if (order.present) { map['order'] = i0.Variable( - i1.$RemoteAlbumEntityTable.$converterorder.toSql(order.value)); + i1.$RemoteAlbumEntityTable.$converterorder.toSql(order.value), + ); } return map; } diff --git a/mobile/lib/infrastructure/entities/remote_album_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/remote_album_asset.entity.drift.dart index ab50607c96..adf22635c1 100644 --- a/mobile/lib/infrastructure/entities/remote_album_asset.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/remote_album_asset.entity.drift.dart @@ -11,76 +11,96 @@ import 'package:drift/internal/modular.dart' as i4; import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart' as i5; -typedef $$RemoteAlbumAssetEntityTableCreateCompanionBuilder - = i1.RemoteAlbumAssetEntityCompanion Function({ - required String assetId, - required String albumId, -}); -typedef $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder - = i1.RemoteAlbumAssetEntityCompanion Function({ - i0.Value assetId, - i0.Value albumId, -}); +typedef $$RemoteAlbumAssetEntityTableCreateCompanionBuilder = + i1.RemoteAlbumAssetEntityCompanion Function({ + required String assetId, + required String albumId, + }); +typedef $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder = + i1.RemoteAlbumAssetEntityCompanion Function({ + i0.Value assetId, + i0.Value albumId, + }); -final class $$RemoteAlbumAssetEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, - i1.$RemoteAlbumAssetEntityTable, - i1.RemoteAlbumAssetEntityData> { +final class $$RemoteAlbumAssetEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$RemoteAlbumAssetEntityTable, + i1.RemoteAlbumAssetEntityData + > { $$RemoteAlbumAssetEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i3.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('remote_asset_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet( - 'remote_album_asset_entity') + 'remote_album_asset_entity', + ) .assetId, - i4.ReadDatabaseContainer(db) - .resultSet('remote_asset_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('remote_asset_entity').id, + ), + ); i3.$$RemoteAssetEntityTableProcessedTableManager get assetId { final $_column = $_itemColumn('asset_id')!; final manager = i3 .$$RemoteAssetEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('remote_asset_entity')) + ).resultSet('remote_asset_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_assetIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } static i5.$RemoteAlbumEntityTable _albumIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('remote_album_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet( - 'remote_album_asset_entity') + 'remote_album_asset_entity', + ) .albumId, - i4.ReadDatabaseContainer(db) - .resultSet('remote_album_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('remote_album_entity').id, + ), + ); i5.$$RemoteAlbumEntityTableProcessedTableManager get albumId { final $_column = $_itemColumn('album_id')!; final manager = i5 .$$RemoteAlbumEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('remote_album_entity')) + ).resultSet('remote_album_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_albumIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -95,45 +115,55 @@ class $$RemoteAlbumAssetEntityTableFilterComposer }); i3.$$RemoteAssetEntityTableFilterComposer get assetId { final i3.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$RemoteAlbumEntityTableFilterComposer get albumId { final i5.$$RemoteAlbumEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$RemoteAlbumEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$RemoteAlbumEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -150,48 +180,56 @@ class $$RemoteAlbumAssetEntityTableOrderingComposer i3.$$RemoteAssetEntityTableOrderingComposer get assetId { final i3.$$RemoteAssetEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$RemoteAlbumEntityTableOrderingComposer get albumId { final i5.$$RemoteAlbumEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$RemoteAlbumEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$RemoteAlbumEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -208,106 +246,129 @@ class $$RemoteAlbumAssetEntityTableAnnotationComposer i3.$$RemoteAssetEntityTableAnnotationComposer get assetId { final i3.$$RemoteAssetEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$RemoteAlbumEntityTableAnnotationComposer get albumId { final i5.$$RemoteAlbumEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$RemoteAlbumEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$RemoteAlbumEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$RemoteAlbumAssetEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$RemoteAlbumAssetEntityTable, - i1.RemoteAlbumAssetEntityData, - i1.$$RemoteAlbumAssetEntityTableFilterComposer, - i1.$$RemoteAlbumAssetEntityTableOrderingComposer, - i1.$$RemoteAlbumAssetEntityTableAnnotationComposer, - $$RemoteAlbumAssetEntityTableCreateCompanionBuilder, - $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder, - (i1.RemoteAlbumAssetEntityData, i1.$$RemoteAlbumAssetEntityTableReferences), - i1.RemoteAlbumAssetEntityData, - i0.PrefetchHooks Function({bool assetId, bool albumId})> { +class $$RemoteAlbumAssetEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$RemoteAlbumAssetEntityTable, + i1.RemoteAlbumAssetEntityData, + i1.$$RemoteAlbumAssetEntityTableFilterComposer, + i1.$$RemoteAlbumAssetEntityTableOrderingComposer, + i1.$$RemoteAlbumAssetEntityTableAnnotationComposer, + $$RemoteAlbumAssetEntityTableCreateCompanionBuilder, + $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder, + ( + i1.RemoteAlbumAssetEntityData, + i1.$$RemoteAlbumAssetEntityTableReferences, + ), + i1.RemoteAlbumAssetEntityData, + i0.PrefetchHooks Function({bool assetId, bool albumId}) + > { $$RemoteAlbumAssetEntityTableTableManager( - i0.GeneratedDatabase db, i1.$RemoteAlbumAssetEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$RemoteAlbumAssetEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => i1.$$RemoteAlbumAssetEntityTableFilterComposer( - $db: db, $table: table), + $db: db, + $table: table, + ), createOrderingComposer: () => i1.$$RemoteAlbumAssetEntityTableOrderingComposer( - $db: db, $table: table), + $db: db, + $table: table, + ), createComputedFieldComposer: () => i1.$$RemoteAlbumAssetEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value assetId = const i0.Value.absent(), - i0.Value albumId = const i0.Value.absent(), - }) => - i1.RemoteAlbumAssetEntityCompanion( - assetId: assetId, - albumId: albumId, - ), - createCompanionCallback: ({ - required String assetId, - required String albumId, - }) => - i1.RemoteAlbumAssetEntityCompanion.insert( - assetId: assetId, - albumId: albumId, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value assetId = const i0.Value.absent(), + i0.Value albumId = const i0.Value.absent(), + }) => i1.RemoteAlbumAssetEntityCompanion( + assetId: assetId, + albumId: albumId, + ), + createCompanionCallback: + ({required String assetId, required String albumId}) => + i1.RemoteAlbumAssetEntityCompanion.insert( + assetId: assetId, + albumId: albumId, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$RemoteAlbumAssetEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$RemoteAlbumAssetEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({assetId = false, albumId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -318,83 +379,107 @@ class $$RemoteAlbumAssetEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (assetId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.assetId, - referencedTable: i1.$$RemoteAlbumAssetEntityTableReferences - ._assetIdTable(db), - referencedColumn: i1.$$RemoteAlbumAssetEntityTableReferences - ._assetIdTable(db) - .id, - ) as T; - } - if (albumId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.albumId, - referencedTable: i1.$$RemoteAlbumAssetEntityTableReferences - ._albumIdTable(db), - referencedColumn: i1.$$RemoteAlbumAssetEntityTableReferences - ._albumIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (assetId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.assetId, + referencedTable: i1 + .$$RemoteAlbumAssetEntityTableReferences + ._assetIdTable(db), + referencedColumn: i1 + .$$RemoteAlbumAssetEntityTableReferences + ._assetIdTable(db) + .id, + ) + as T; + } + if (albumId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.albumId, + referencedTable: i1 + .$$RemoteAlbumAssetEntityTableReferences + ._albumIdTable(db), + referencedColumn: i1 + .$$RemoteAlbumAssetEntityTableReferences + ._albumIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$RemoteAlbumAssetEntityTableProcessedTableManager - = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$RemoteAlbumAssetEntityTable, +typedef $$RemoteAlbumAssetEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$RemoteAlbumAssetEntityTable, + i1.RemoteAlbumAssetEntityData, + i1.$$RemoteAlbumAssetEntityTableFilterComposer, + i1.$$RemoteAlbumAssetEntityTableOrderingComposer, + i1.$$RemoteAlbumAssetEntityTableAnnotationComposer, + $$RemoteAlbumAssetEntityTableCreateCompanionBuilder, + $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder, + ( i1.RemoteAlbumAssetEntityData, - i1.$$RemoteAlbumAssetEntityTableFilterComposer, - i1.$$RemoteAlbumAssetEntityTableOrderingComposer, - i1.$$RemoteAlbumAssetEntityTableAnnotationComposer, - $$RemoteAlbumAssetEntityTableCreateCompanionBuilder, - $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder, - ( - i1.RemoteAlbumAssetEntityData, - i1.$$RemoteAlbumAssetEntityTableReferences - ), - i1.RemoteAlbumAssetEntityData, - i0.PrefetchHooks Function({bool assetId, bool albumId})>; + i1.$$RemoteAlbumAssetEntityTableReferences, + ), + i1.RemoteAlbumAssetEntityData, + i0.PrefetchHooks Function({bool assetId, bool albumId}) + >; class $RemoteAlbumAssetEntityTable extends i2.RemoteAlbumAssetEntity with - i0.TableInfo<$RemoteAlbumAssetEntityTable, - i1.RemoteAlbumAssetEntityData> { + i0.TableInfo< + $RemoteAlbumAssetEntityTable, + i1.RemoteAlbumAssetEntityData + > { @override final i0.GeneratedDatabase attachedDatabase; final String? _alias; $RemoteAlbumAssetEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _assetIdMeta = - const i0.VerificationMeta('assetId'); + static const i0.VerificationMeta _assetIdMeta = const i0.VerificationMeta( + 'assetId', + ); @override late final i0.GeneratedColumn assetId = i0.GeneratedColumn( - 'asset_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _albumIdMeta = - const i0.VerificationMeta('albumId'); + 'asset_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _albumIdMeta = const i0.VerificationMeta( + 'albumId', + ); @override late final i0.GeneratedColumn albumId = i0.GeneratedColumn( - 'album_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + 'album_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -404,19 +489,24 @@ class $RemoteAlbumAssetEntityTable extends i2.RemoteAlbumAssetEntity static const String $name = 'remote_album_asset_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('asset_id')) { - context.handle(_assetIdMeta, - assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta)); + context.handle( + _assetIdMeta, + assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta), + ); } else if (isInserting) { context.missing(_assetIdMeta); } if (data.containsKey('album_id')) { - context.handle(_albumIdMeta, - albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta)); + context.handle( + _albumIdMeta, + albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta), + ); } else if (isInserting) { context.missing(_albumIdMeta); } @@ -426,14 +516,20 @@ class $RemoteAlbumAssetEntityTable extends i2.RemoteAlbumAssetEntity @override Set get $primaryKey => {assetId, albumId}; @override - i1.RemoteAlbumAssetEntityData map(Map data, - {String? tablePrefix}) { + i1.RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.RemoteAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -452,8 +548,10 @@ class RemoteAlbumAssetEntityData extends i0.DataClass implements i0.Insertable { final String assetId; final String albumId; - const RemoteAlbumAssetEntityData( - {required this.assetId, required this.albumId}); + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -462,8 +560,10 @@ class RemoteAlbumAssetEntityData extends i0.DataClass return map; } - factory RemoteAlbumAssetEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return RemoteAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -485,7 +585,8 @@ class RemoteAlbumAssetEntityData extends i0.DataClass albumId: albumId ?? this.albumId, ); RemoteAlbumAssetEntityData copyWithCompanion( - i1.RemoteAlbumAssetEntityCompanion data) { + i1.RemoteAlbumAssetEntityCompanion data, + ) { return RemoteAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -522,8 +623,8 @@ class RemoteAlbumAssetEntityCompanion RemoteAlbumAssetEntityCompanion.insert({ required String assetId, required String albumId, - }) : assetId = i0.Value(assetId), - albumId = i0.Value(albumId); + }) : assetId = i0.Value(assetId), + albumId = i0.Value(albumId); static i0.Insertable custom({ i0.Expression? assetId, i0.Expression? albumId, @@ -534,8 +635,10 @@ class RemoteAlbumAssetEntityCompanion }); } - i1.RemoteAlbumAssetEntityCompanion copyWith( - {i0.Value? assetId, i0.Value? albumId}) { + i1.RemoteAlbumAssetEntityCompanion copyWith({ + i0.Value? assetId, + i0.Value? albumId, + }) { return i1.RemoteAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, diff --git a/mobile/lib/infrastructure/entities/remote_album_user.entity.drift.dart b/mobile/lib/infrastructure/entities/remote_album_user.entity.drift.dart index 7ec1151a8a..f8167ddf94 100644 --- a/mobile/lib/infrastructure/entities/remote_album_user.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/remote_album_user.entity.drift.dart @@ -12,78 +12,98 @@ import 'package:drift/internal/modular.dart' as i5; import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i6; -typedef $$RemoteAlbumUserEntityTableCreateCompanionBuilder - = i1.RemoteAlbumUserEntityCompanion Function({ - required String albumId, - required String userId, - required i2.AlbumUserRole role, -}); -typedef $$RemoteAlbumUserEntityTableUpdateCompanionBuilder - = i1.RemoteAlbumUserEntityCompanion Function({ - i0.Value albumId, - i0.Value userId, - i0.Value role, -}); +typedef $$RemoteAlbumUserEntityTableCreateCompanionBuilder = + i1.RemoteAlbumUserEntityCompanion Function({ + required String albumId, + required String userId, + required i2.AlbumUserRole role, + }); +typedef $$RemoteAlbumUserEntityTableUpdateCompanionBuilder = + i1.RemoteAlbumUserEntityCompanion Function({ + i0.Value albumId, + i0.Value userId, + i0.Value role, + }); -final class $$RemoteAlbumUserEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, - i1.$RemoteAlbumUserEntityTable, - i1.RemoteAlbumUserEntityData> { +final class $$RemoteAlbumUserEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$RemoteAlbumUserEntityTable, + i1.RemoteAlbumUserEntityData + > { $$RemoteAlbumUserEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i4.$RemoteAlbumEntityTable _albumIdTable(i0.GeneratedDatabase db) => i5.ReadDatabaseContainer(db) .resultSet('remote_album_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i5.ReadDatabaseContainer(db) .resultSet( - 'remote_album_user_entity') + 'remote_album_user_entity', + ) .albumId, - i5.ReadDatabaseContainer(db) - .resultSet('remote_album_entity') - .id)); + i5.ReadDatabaseContainer( + db, + ).resultSet('remote_album_entity').id, + ), + ); i4.$$RemoteAlbumEntityTableProcessedTableManager get albumId { final $_column = $_itemColumn('album_id')!; final manager = i4 .$$RemoteAlbumEntityTableTableManager( + $_db, + i5.ReadDatabaseContainer( $_db, - i5.ReadDatabaseContainer($_db) - .resultSet('remote_album_entity')) + ).resultSet('remote_album_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_albumIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } static i6.$UserEntityTable _userIdTable(i0.GeneratedDatabase db) => i5.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i5.ReadDatabaseContainer(db) .resultSet( - 'remote_album_user_entity') + 'remote_album_user_entity', + ) .userId, - i5.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + i5.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i6.$$UserEntityTableProcessedTableManager get userId { final $_column = $_itemColumn('user_id')!; final manager = i6 .$$UserEntityTableTableManager( + $_db, + i5.ReadDatabaseContainer( $_db, - i5.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_userIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -97,51 +117,62 @@ class $$RemoteAlbumUserEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnWithTypeConverterFilters - get role => $composableBuilder( - column: $table.role, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + get role => $composableBuilder( + column: $table.role, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i4.$$RemoteAlbumEntityTableFilterComposer get albumId { final i4.$$RemoteAlbumEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$RemoteAlbumEntityTableFilterComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$RemoteAlbumEntityTableFilterComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i6.$$UserEntityTableFilterComposer get userId { final i6.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.userId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i6.$$UserEntityTableFilterComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i6.$$UserEntityTableFilterComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -156,51 +187,62 @@ class $$RemoteAlbumUserEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get role => $composableBuilder( - column: $table.role, builder: (column) => i0.ColumnOrderings(column)); + column: $table.role, + builder: (column) => i0.ColumnOrderings(column), + ); i4.$$RemoteAlbumEntityTableOrderingComposer get albumId { final i4.$$RemoteAlbumEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$RemoteAlbumEntityTableOrderingComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet( - 'remote_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$RemoteAlbumEntityTableOrderingComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i6.$$UserEntityTableOrderingComposer get userId { final i6.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.userId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i6.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i6.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -220,108 +262,134 @@ class $$RemoteAlbumUserEntityTableAnnotationComposer i4.$$RemoteAlbumEntityTableAnnotationComposer get albumId { final i4.$$RemoteAlbumEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$RemoteAlbumEntityTableAnnotationComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet( - 'remote_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$RemoteAlbumEntityTableAnnotationComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i6.$$UserEntityTableAnnotationComposer get userId { final i6.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.userId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i6.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i6.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$RemoteAlbumUserEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$RemoteAlbumUserEntityTable, - i1.RemoteAlbumUserEntityData, - i1.$$RemoteAlbumUserEntityTableFilterComposer, - i1.$$RemoteAlbumUserEntityTableOrderingComposer, - i1.$$RemoteAlbumUserEntityTableAnnotationComposer, - $$RemoteAlbumUserEntityTableCreateCompanionBuilder, - $$RemoteAlbumUserEntityTableUpdateCompanionBuilder, - (i1.RemoteAlbumUserEntityData, i1.$$RemoteAlbumUserEntityTableReferences), - i1.RemoteAlbumUserEntityData, - i0.PrefetchHooks Function({bool albumId, bool userId})> { +class $$RemoteAlbumUserEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$RemoteAlbumUserEntityTable, + i1.RemoteAlbumUserEntityData, + i1.$$RemoteAlbumUserEntityTableFilterComposer, + i1.$$RemoteAlbumUserEntityTableOrderingComposer, + i1.$$RemoteAlbumUserEntityTableAnnotationComposer, + $$RemoteAlbumUserEntityTableCreateCompanionBuilder, + $$RemoteAlbumUserEntityTableUpdateCompanionBuilder, + ( + i1.RemoteAlbumUserEntityData, + i1.$$RemoteAlbumUserEntityTableReferences, + ), + i1.RemoteAlbumUserEntityData, + i0.PrefetchHooks Function({bool albumId, bool userId}) + > { $$RemoteAlbumUserEntityTableTableManager( - i0.GeneratedDatabase db, i1.$RemoteAlbumUserEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$RemoteAlbumUserEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => i1.$$RemoteAlbumUserEntityTableFilterComposer( - $db: db, $table: table), + $db: db, + $table: table, + ), createOrderingComposer: () => i1.$$RemoteAlbumUserEntityTableOrderingComposer( - $db: db, $table: table), + $db: db, + $table: table, + ), createComputedFieldComposer: () => i1.$$RemoteAlbumUserEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value albumId = const i0.Value.absent(), - i0.Value userId = const i0.Value.absent(), - i0.Value role = const i0.Value.absent(), - }) => - i1.RemoteAlbumUserEntityCompanion( - albumId: albumId, - userId: userId, - role: role, - ), - createCompanionCallback: ({ - required String albumId, - required String userId, - required i2.AlbumUserRole role, - }) => - i1.RemoteAlbumUserEntityCompanion.insert( - albumId: albumId, - userId: userId, - role: role, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value albumId = const i0.Value.absent(), + i0.Value userId = const i0.Value.absent(), + i0.Value role = const i0.Value.absent(), + }) => i1.RemoteAlbumUserEntityCompanion( + albumId: albumId, + userId: userId, + role: role, + ), + createCompanionCallback: + ({ + required String albumId, + required String userId, + required i2.AlbumUserRole role, + }) => i1.RemoteAlbumUserEntityCompanion.insert( + albumId: albumId, + userId: userId, + role: role, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$RemoteAlbumUserEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$RemoteAlbumUserEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({albumId = false, userId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -332,89 +400,115 @@ class $$RemoteAlbumUserEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (albumId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.albumId, - referencedTable: i1.$$RemoteAlbumUserEntityTableReferences - ._albumIdTable(db), - referencedColumn: i1.$$RemoteAlbumUserEntityTableReferences - ._albumIdTable(db) - .id, - ) as T; - } - if (userId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.userId, - referencedTable: i1.$$RemoteAlbumUserEntityTableReferences - ._userIdTable(db), - referencedColumn: i1.$$RemoteAlbumUserEntityTableReferences - ._userIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (albumId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.albumId, + referencedTable: i1 + .$$RemoteAlbumUserEntityTableReferences + ._albumIdTable(db), + referencedColumn: i1 + .$$RemoteAlbumUserEntityTableReferences + ._albumIdTable(db) + .id, + ) + as T; + } + if (userId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.userId, + referencedTable: i1 + .$$RemoteAlbumUserEntityTableReferences + ._userIdTable(db), + referencedColumn: i1 + .$$RemoteAlbumUserEntityTableReferences + ._userIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$RemoteAlbumUserEntityTableProcessedTableManager - = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$RemoteAlbumUserEntityTable, - i1.RemoteAlbumUserEntityData, - i1.$$RemoteAlbumUserEntityTableFilterComposer, - i1.$$RemoteAlbumUserEntityTableOrderingComposer, - i1.$$RemoteAlbumUserEntityTableAnnotationComposer, - $$RemoteAlbumUserEntityTableCreateCompanionBuilder, - $$RemoteAlbumUserEntityTableUpdateCompanionBuilder, - ( - i1.RemoteAlbumUserEntityData, - i1.$$RemoteAlbumUserEntityTableReferences - ), - i1.RemoteAlbumUserEntityData, - i0.PrefetchHooks Function({bool albumId, bool userId})>; +typedef $$RemoteAlbumUserEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$RemoteAlbumUserEntityTable, + i1.RemoteAlbumUserEntityData, + i1.$$RemoteAlbumUserEntityTableFilterComposer, + i1.$$RemoteAlbumUserEntityTableOrderingComposer, + i1.$$RemoteAlbumUserEntityTableAnnotationComposer, + $$RemoteAlbumUserEntityTableCreateCompanionBuilder, + $$RemoteAlbumUserEntityTableUpdateCompanionBuilder, + (i1.RemoteAlbumUserEntityData, i1.$$RemoteAlbumUserEntityTableReferences), + i1.RemoteAlbumUserEntityData, + i0.PrefetchHooks Function({bool albumId, bool userId}) + >; class $RemoteAlbumUserEntityTable extends i3.RemoteAlbumUserEntity with - i0 - .TableInfo<$RemoteAlbumUserEntityTable, i1.RemoteAlbumUserEntityData> { + i0.TableInfo< + $RemoteAlbumUserEntityTable, + i1.RemoteAlbumUserEntityData + > { @override final i0.GeneratedDatabase attachedDatabase; final String? _alias; $RemoteAlbumUserEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _albumIdMeta = - const i0.VerificationMeta('albumId'); + static const i0.VerificationMeta _albumIdMeta = const i0.VerificationMeta( + 'albumId', + ); @override late final i0.GeneratedColumn albumId = i0.GeneratedColumn( - 'album_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_album_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _userIdMeta = - const i0.VerificationMeta('userId'); + 'album_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _userIdMeta = const i0.VerificationMeta( + 'userId', + ); @override late final i0.GeneratedColumn userId = i0.GeneratedColumn( - 'user_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'user_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); @override late final i0.GeneratedColumnWithTypeConverter role = - i0.GeneratedColumn('role', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$RemoteAlbumUserEntityTable.$converterrole); + i0.GeneratedColumn( + 'role', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter( + i1.$RemoteAlbumUserEntityTable.$converterrole, + ); @override List get $columns => [albumId, userId, role]; @override @@ -424,19 +518,24 @@ class $RemoteAlbumUserEntityTable extends i3.RemoteAlbumUserEntity static const String $name = 'remote_album_user_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('album_id')) { - context.handle(_albumIdMeta, - albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta)); + context.handle( + _albumIdMeta, + albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta), + ); } else if (isInserting) { context.missing(_albumIdMeta); } if (data.containsKey('user_id')) { - context.handle(_userIdMeta, - userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta)); + context.handle( + _userIdMeta, + userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta), + ); } else if (isInserting) { context.missing(_userIdMeta); } @@ -446,17 +545,26 @@ class $RemoteAlbumUserEntityTable extends i3.RemoteAlbumUserEntity @override Set get $primaryKey => {albumId, userId}; @override - i1.RemoteAlbumUserEntityData map(Map data, - {String? tablePrefix}) { + i1.RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.RemoteAlbumUserEntityData( - albumId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}album_id'])!, - userId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}user_id'])!, + albumId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, role: i1.$RemoteAlbumUserEntityTable.$converterrole.fromSql( - attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}role'])!), + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}role'], + )!, + ), ); } @@ -478,8 +586,11 @@ class RemoteAlbumUserEntityData extends i0.DataClass final String albumId; final String userId; final i2.AlbumUserRole role; - const RemoteAlbumUserEntityData( - {required this.albumId, required this.userId, required this.role}); + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -487,19 +598,23 @@ class RemoteAlbumUserEntityData extends i0.DataClass map['user_id'] = i0.Variable(userId); { map['role'] = i0.Variable( - i1.$RemoteAlbumUserEntityTable.$converterrole.toSql(role)); + i1.$RemoteAlbumUserEntityTable.$converterrole.toSql(role), + ); } return map; } - factory RemoteAlbumUserEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return RemoteAlbumUserEntityData( albumId: serializer.fromJson(json['albumId']), userId: serializer.fromJson(json['userId']), - role: i1.$RemoteAlbumUserEntityTable.$converterrole - .fromJson(serializer.fromJson(json['role'])), + role: i1.$RemoteAlbumUserEntityTable.$converterrole.fromJson( + serializer.fromJson(json['role']), + ), ); } @override @@ -509,19 +624,23 @@ class RemoteAlbumUserEntityData extends i0.DataClass 'albumId': serializer.toJson(albumId), 'userId': serializer.toJson(userId), 'role': serializer.toJson( - i1.$RemoteAlbumUserEntityTable.$converterrole.toJson(role)), + i1.$RemoteAlbumUserEntityTable.$converterrole.toJson(role), + ), }; } - i1.RemoteAlbumUserEntityData copyWith( - {String? albumId, String? userId, i2.AlbumUserRole? role}) => - i1.RemoteAlbumUserEntityData( - albumId: albumId ?? this.albumId, - userId: userId ?? this.userId, - role: role ?? this.role, - ); + i1.RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + i2.AlbumUserRole? role, + }) => i1.RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); RemoteAlbumUserEntityData copyWithCompanion( - i1.RemoteAlbumUserEntityCompanion data) { + i1.RemoteAlbumUserEntityCompanion data, + ) { return RemoteAlbumUserEntityData( albumId: data.albumId.present ? data.albumId.value : this.albumId, userId: data.userId.present ? data.userId.value : this.userId, @@ -564,9 +683,9 @@ class RemoteAlbumUserEntityCompanion required String albumId, required String userId, required i2.AlbumUserRole role, - }) : albumId = i0.Value(albumId), - userId = i0.Value(userId), - role = i0.Value(role); + }) : albumId = i0.Value(albumId), + userId = i0.Value(userId), + role = i0.Value(role); static i0.Insertable custom({ i0.Expression? albumId, i0.Expression? userId, @@ -579,10 +698,11 @@ class RemoteAlbumUserEntityCompanion }); } - i1.RemoteAlbumUserEntityCompanion copyWith( - {i0.Value? albumId, - i0.Value? userId, - i0.Value? role}) { + i1.RemoteAlbumUserEntityCompanion copyWith({ + i0.Value? albumId, + i0.Value? userId, + i0.Value? role, + }) { return i1.RemoteAlbumUserEntityCompanion( albumId: albumId ?? this.albumId, userId: userId ?? this.userId, @@ -601,7 +721,8 @@ class RemoteAlbumUserEntityCompanion } if (role.present) { map['role'] = i0.Variable( - i1.$RemoteAlbumUserEntityTable.$converterrole.toSql(role.value)); + i1.$RemoteAlbumUserEntityTable.$converterrole.toSql(role.value), + ); } return map; } diff --git a/mobile/lib/infrastructure/entities/remote_asset.entity.dart b/mobile/lib/infrastructure/entities/remote_asset.entity.dart index cb14e2501d..7ee2e76ff6 100644 --- a/mobile/lib/infrastructure/entities/remote_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/remote_asset.entity.dart @@ -5,11 +5,7 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart'; import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; -@TableIndex( - name: 'UQ_remote_asset_owner_checksum', - columns: {#checksum, #ownerId}, - unique: true, -) +@TableIndex(name: 'UQ_remote_asset_owner_checksum', columns: {#checksum, #ownerId}, unique: true) @TableIndex(name: 'idx_remote_asset_checksum', columns: {#checksum}) class RemoteAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin { const RemoteAssetEntity(); @@ -40,21 +36,21 @@ class RemoteAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin extension RemoteAssetEntityDataDomainEx on RemoteAssetEntityData { RemoteAsset toDto() => RemoteAsset( - id: id, - name: name, - ownerId: ownerId, - checksum: checksum, - type: type, - createdAt: createdAt, - updatedAt: updatedAt, - durationInSeconds: durationInSeconds, - isFavorite: isFavorite, - height: height, - width: width, - thumbHash: thumbHash, - visibility: visibility, - livePhotoVideoId: livePhotoVideoId, - localId: null, - stackId: stackId, - ); + id: id, + name: name, + ownerId: ownerId, + checksum: checksum, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + durationInSeconds: durationInSeconds, + isFavorite: isFavorite, + height: height, + width: width, + thumbHash: thumbHash, + visibility: visibility, + livePhotoVideoId: livePhotoVideoId, + localId: null, + stackId: stackId, + ); } diff --git a/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart index 543ed65985..6bd416b259 100644 --- a/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart @@ -11,78 +11,90 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i5; import 'package:drift/internal/modular.dart' as i6; -typedef $$RemoteAssetEntityTableCreateCompanionBuilder - = i1.RemoteAssetEntityCompanion Function({ - required String name, - required i2.AssetType type, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value width, - i0.Value height, - i0.Value durationInSeconds, - required String id, - required String checksum, - i0.Value isFavorite, - required String ownerId, - i0.Value localDateTime, - i0.Value thumbHash, - i0.Value deletedAt, - i0.Value livePhotoVideoId, - required i2.AssetVisibility visibility, - i0.Value stackId, -}); -typedef $$RemoteAssetEntityTableUpdateCompanionBuilder - = i1.RemoteAssetEntityCompanion Function({ - i0.Value name, - i0.Value type, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value width, - i0.Value height, - i0.Value durationInSeconds, - i0.Value id, - i0.Value checksum, - i0.Value isFavorite, - i0.Value ownerId, - i0.Value localDateTime, - i0.Value thumbHash, - i0.Value deletedAt, - i0.Value livePhotoVideoId, - i0.Value visibility, - i0.Value stackId, -}); +typedef $$RemoteAssetEntityTableCreateCompanionBuilder = + i1.RemoteAssetEntityCompanion Function({ + required String name, + required i2.AssetType type, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value width, + i0.Value height, + i0.Value durationInSeconds, + required String id, + required String checksum, + i0.Value isFavorite, + required String ownerId, + i0.Value localDateTime, + i0.Value thumbHash, + i0.Value deletedAt, + i0.Value livePhotoVideoId, + required i2.AssetVisibility visibility, + i0.Value stackId, + }); +typedef $$RemoteAssetEntityTableUpdateCompanionBuilder = + i1.RemoteAssetEntityCompanion Function({ + i0.Value name, + i0.Value type, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value width, + i0.Value height, + i0.Value durationInSeconds, + i0.Value id, + i0.Value checksum, + i0.Value isFavorite, + i0.Value ownerId, + i0.Value localDateTime, + i0.Value thumbHash, + i0.Value deletedAt, + i0.Value livePhotoVideoId, + i0.Value visibility, + i0.Value stackId, + }); -final class $$RemoteAssetEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, - i1.$RemoteAssetEntityTable, - i1.RemoteAssetEntityData> { +final class $$RemoteAssetEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$RemoteAssetEntityTable, + i1.RemoteAssetEntityData + > { $$RemoteAssetEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i5.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) => i6.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i6.ReadDatabaseContainer(db) .resultSet('remote_asset_entity') .ownerId, - i6.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + i6.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i5.$$UserEntityTableProcessedTableManager get ownerId { final $_column = $_itemColumn('owner_id')!; final manager = i5 .$$UserEntityTableTableManager( + $_db, + i6.ReadDatabaseContainer( $_db, - i6.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_ownerIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -96,79 +108,111 @@ class $$RemoteAssetEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnFilters(column)); + column: $table.name, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnWithTypeConverterFilters get type => $composableBuilder( - column: $table.type, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + column: $table.type, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i0.ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get width => $composableBuilder( - column: $table.width, builder: (column) => i0.ColumnFilters(column)); + column: $table.width, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get height => $composableBuilder( - column: $table.height, builder: (column) => i0.ColumnFilters(column)); + column: $table.height, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get durationInSeconds => $composableBuilder( - column: $table.durationInSeconds, - builder: (column) => i0.ColumnFilters(column)); + column: $table.durationInSeconds, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get checksum => $composableBuilder( - column: $table.checksum, builder: (column) => i0.ColumnFilters(column)); + column: $table.checksum, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get isFavorite => $composableBuilder( - column: $table.isFavorite, builder: (column) => i0.ColumnFilters(column)); + column: $table.isFavorite, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get localDateTime => $composableBuilder( - column: $table.localDateTime, - builder: (column) => i0.ColumnFilters(column)); + column: $table.localDateTime, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get thumbHash => $composableBuilder( - column: $table.thumbHash, builder: (column) => i0.ColumnFilters(column)); + column: $table.thumbHash, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get deletedAt => $composableBuilder( - column: $table.deletedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.deletedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get livePhotoVideoId => $composableBuilder( - column: $table.livePhotoVideoId, - builder: (column) => i0.ColumnFilters(column)); + column: $table.livePhotoVideoId, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnWithTypeConverterFilters - get visibility => $composableBuilder( - column: $table.visibility, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + get visibility => $composableBuilder( + column: $table.visibility, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i0.ColumnFilters get stackId => $composableBuilder( - column: $table.stackId, builder: (column) => i0.ColumnFilters(column)); + column: $table.stackId, + builder: (column) => i0.ColumnFilters(column), + ); i5.$$UserEntityTableFilterComposer get ownerId { final i5.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableFilterComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableFilterComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -183,81 +227,109 @@ class $$RemoteAssetEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnOrderings(column)); + column: $table.name, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get type => $composableBuilder( - column: $table.type, builder: (column) => i0.ColumnOrderings(column)); + column: $table.type, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get width => $composableBuilder( - column: $table.width, builder: (column) => i0.ColumnOrderings(column)); + column: $table.width, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get height => $composableBuilder( - column: $table.height, builder: (column) => i0.ColumnOrderings(column)); + column: $table.height, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get durationInSeconds => $composableBuilder( - column: $table.durationInSeconds, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.durationInSeconds, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get checksum => $composableBuilder( - column: $table.checksum, builder: (column) => i0.ColumnOrderings(column)); + column: $table.checksum, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isFavorite => $composableBuilder( - column: $table.isFavorite, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.isFavorite, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get localDateTime => $composableBuilder( - column: $table.localDateTime, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.localDateTime, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get thumbHash => $composableBuilder( - column: $table.thumbHash, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.thumbHash, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get deletedAt => $composableBuilder( - column: $table.deletedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.deletedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get livePhotoVideoId => $composableBuilder( - column: $table.livePhotoVideoId, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.livePhotoVideoId, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get visibility => $composableBuilder( - column: $table.visibility, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.visibility, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get stackId => $composableBuilder( - column: $table.stackId, builder: (column) => i0.ColumnOrderings(column)); + column: $table.stackId, + builder: (column) => i0.ColumnOrderings(column), + ); i5.$$UserEntityTableOrderingComposer get ownerId { final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -290,7 +362,9 @@ class $$RemoteAssetEntityTableAnnotationComposer $composableBuilder(column: $table.height, builder: (column) => column); i0.GeneratedColumn get durationInSeconds => $composableBuilder( - column: $table.durationInSeconds, builder: (column) => column); + column: $table.durationInSeconds, + builder: (column) => column, + ); i0.GeneratedColumn get id => $composableBuilder(column: $table.id, builder: (column) => column); @@ -299,10 +373,14 @@ class $$RemoteAssetEntityTableAnnotationComposer $composableBuilder(column: $table.checksum, builder: (column) => column); i0.GeneratedColumn get isFavorite => $composableBuilder( - column: $table.isFavorite, builder: (column) => column); + column: $table.isFavorite, + builder: (column) => column, + ); i0.GeneratedColumn get localDateTime => $composableBuilder( - column: $table.localDateTime, builder: (column) => column); + column: $table.localDateTime, + builder: (column) => column, + ); i0.GeneratedColumn get thumbHash => $composableBuilder(column: $table.thumbHash, builder: (column) => column); @@ -311,53 +389,67 @@ class $$RemoteAssetEntityTableAnnotationComposer $composableBuilder(column: $table.deletedAt, builder: (column) => column); i0.GeneratedColumn get livePhotoVideoId => $composableBuilder( - column: $table.livePhotoVideoId, builder: (column) => column); + column: $table.livePhotoVideoId, + builder: (column) => column, + ); i0.GeneratedColumnWithTypeConverter get visibility => $composableBuilder( - column: $table.visibility, builder: (column) => column); + column: $table.visibility, + builder: (column) => column, + ); i0.GeneratedColumn get stackId => $composableBuilder(column: $table.stackId, builder: (column) => column); i5.$$UserEntityTableAnnotationComposer get ownerId { final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$RemoteAssetEntityTable, - i1.RemoteAssetEntityData, - i1.$$RemoteAssetEntityTableFilterComposer, - i1.$$RemoteAssetEntityTableOrderingComposer, - i1.$$RemoteAssetEntityTableAnnotationComposer, - $$RemoteAssetEntityTableCreateCompanionBuilder, - $$RemoteAssetEntityTableUpdateCompanionBuilder, - (i1.RemoteAssetEntityData, i1.$$RemoteAssetEntityTableReferences), - i1.RemoteAssetEntityData, - i0.PrefetchHooks Function({bool ownerId})> { +class $$RemoteAssetEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$RemoteAssetEntityTable, + i1.RemoteAssetEntityData, + i1.$$RemoteAssetEntityTableFilterComposer, + i1.$$RemoteAssetEntityTableOrderingComposer, + i1.$$RemoteAssetEntityTableAnnotationComposer, + $$RemoteAssetEntityTableCreateCompanionBuilder, + $$RemoteAssetEntityTableUpdateCompanionBuilder, + (i1.RemoteAssetEntityData, i1.$$RemoteAssetEntityTableReferences), + i1.RemoteAssetEntityData, + i0.PrefetchHooks Function({bool ownerId}) + > { $$RemoteAssetEntityTableTableManager( - i0.GeneratedDatabase db, i1.$RemoteAssetEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$RemoteAssetEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -366,95 +458,101 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager< .$$RemoteAssetEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$RemoteAssetEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value name = const i0.Value.absent(), - i0.Value type = const i0.Value.absent(), - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value width = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value durationInSeconds = const i0.Value.absent(), - i0.Value id = const i0.Value.absent(), - i0.Value checksum = const i0.Value.absent(), - i0.Value isFavorite = const i0.Value.absent(), - i0.Value ownerId = const i0.Value.absent(), - i0.Value localDateTime = const i0.Value.absent(), - i0.Value thumbHash = const i0.Value.absent(), - i0.Value deletedAt = const i0.Value.absent(), - i0.Value livePhotoVideoId = const i0.Value.absent(), - i0.Value visibility = const i0.Value.absent(), - i0.Value stackId = const i0.Value.absent(), - }) => - i1.RemoteAssetEntityCompanion( - name: name, - type: type, - createdAt: createdAt, - updatedAt: updatedAt, - width: width, - height: height, - durationInSeconds: durationInSeconds, - id: id, - checksum: checksum, - isFavorite: isFavorite, - ownerId: ownerId, - localDateTime: localDateTime, - thumbHash: thumbHash, - deletedAt: deletedAt, - livePhotoVideoId: livePhotoVideoId, - visibility: visibility, - stackId: stackId, - ), - createCompanionCallback: ({ - required String name, - required i2.AssetType type, - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value width = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value durationInSeconds = const i0.Value.absent(), - required String id, - required String checksum, - i0.Value isFavorite = const i0.Value.absent(), - required String ownerId, - i0.Value localDateTime = const i0.Value.absent(), - i0.Value thumbHash = const i0.Value.absent(), - i0.Value deletedAt = const i0.Value.absent(), - i0.Value livePhotoVideoId = const i0.Value.absent(), - required i2.AssetVisibility visibility, - i0.Value stackId = const i0.Value.absent(), - }) => - i1.RemoteAssetEntityCompanion.insert( - name: name, - type: type, - createdAt: createdAt, - updatedAt: updatedAt, - width: width, - height: height, - durationInSeconds: durationInSeconds, - id: id, - checksum: checksum, - isFavorite: isFavorite, - ownerId: ownerId, - localDateTime: localDateTime, - thumbHash: thumbHash, - deletedAt: deletedAt, - livePhotoVideoId: livePhotoVideoId, - visibility: visibility, - stackId: stackId, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value name = const i0.Value.absent(), + i0.Value type = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value width = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value durationInSeconds = const i0.Value.absent(), + i0.Value id = const i0.Value.absent(), + i0.Value checksum = const i0.Value.absent(), + i0.Value isFavorite = const i0.Value.absent(), + i0.Value ownerId = const i0.Value.absent(), + i0.Value localDateTime = const i0.Value.absent(), + i0.Value thumbHash = const i0.Value.absent(), + i0.Value deletedAt = const i0.Value.absent(), + i0.Value livePhotoVideoId = const i0.Value.absent(), + i0.Value visibility = + const i0.Value.absent(), + i0.Value stackId = const i0.Value.absent(), + }) => i1.RemoteAssetEntityCompanion( + name: name, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + width: width, + height: height, + durationInSeconds: durationInSeconds, + id: id, + checksum: checksum, + isFavorite: isFavorite, + ownerId: ownerId, + localDateTime: localDateTime, + thumbHash: thumbHash, + deletedAt: deletedAt, + livePhotoVideoId: livePhotoVideoId, + visibility: visibility, + stackId: stackId, + ), + createCompanionCallback: + ({ + required String name, + required i2.AssetType type, + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value width = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value durationInSeconds = const i0.Value.absent(), + required String id, + required String checksum, + i0.Value isFavorite = const i0.Value.absent(), + required String ownerId, + i0.Value localDateTime = const i0.Value.absent(), + i0.Value thumbHash = const i0.Value.absent(), + i0.Value deletedAt = const i0.Value.absent(), + i0.Value livePhotoVideoId = const i0.Value.absent(), + required i2.AssetVisibility visibility, + i0.Value stackId = const i0.Value.absent(), + }) => i1.RemoteAssetEntityCompanion.insert( + name: name, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + width: width, + height: height, + durationInSeconds: durationInSeconds, + id: id, + checksum: checksum, + isFavorite: isFavorite, + ownerId: ownerId, + localDateTime: localDateTime, + thumbHash: thumbHash, + deletedAt: deletedAt, + livePhotoVideoId: livePhotoVideoId, + visibility: visibility, + stackId: stackId, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$RemoteAssetEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$RemoteAssetEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({ownerId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -465,45 +563,54 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (ownerId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.ownerId, - referencedTable: - i1.$$RemoteAssetEntityTableReferences._ownerIdTable(db), - referencedColumn: i1.$$RemoteAssetEntityTableReferences - ._ownerIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (ownerId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.ownerId, + referencedTable: i1 + .$$RemoteAssetEntityTableReferences + ._ownerIdTable(db), + referencedColumn: i1 + .$$RemoteAssetEntityTableReferences + ._ownerIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$RemoteAssetEntityTableProcessedTableManager - = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$RemoteAssetEntityTable, - i1.RemoteAssetEntityData, - i1.$$RemoteAssetEntityTableFilterComposer, - i1.$$RemoteAssetEntityTableOrderingComposer, - i1.$$RemoteAssetEntityTableAnnotationComposer, - $$RemoteAssetEntityTableCreateCompanionBuilder, - $$RemoteAssetEntityTableUpdateCompanionBuilder, - (i1.RemoteAssetEntityData, i1.$$RemoteAssetEntityTableReferences), - i1.RemoteAssetEntityData, - i0.PrefetchHooks Function({bool ownerId})>; +typedef $$RemoteAssetEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$RemoteAssetEntityTable, + i1.RemoteAssetEntityData, + i1.$$RemoteAssetEntityTableFilterComposer, + i1.$$RemoteAssetEntityTableOrderingComposer, + i1.$$RemoteAssetEntityTableAnnotationComposer, + $$RemoteAssetEntityTableCreateCompanionBuilder, + $$RemoteAssetEntityTableUpdateCompanionBuilder, + (i1.RemoteAssetEntityData, i1.$$RemoteAssetEntityTableReferences), + i1.RemoteAssetEntityData, + i0.PrefetchHooks Function({bool ownerId}) + >; i0.Index get uQRemoteAssetOwnerChecksum => i0.Index( - 'UQ_remote_asset_owner_checksum', - 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', +); class $RemoteAssetEntityTable extends i3.RemoteAssetEntity with i0.TableInfo<$RemoteAssetEntityTable, i1.RemoteAssetEntityData> { @@ -511,138 +618,222 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity final i0.GeneratedDatabase attachedDatabase; final String? _alias; $RemoteAssetEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _nameMeta = - const i0.VerificationMeta('name'); + static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta( + 'name', + ); @override late final i0.GeneratedColumn name = i0.GeneratedColumn( - 'name', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); @override late final i0.GeneratedColumnWithTypeConverter type = - i0.GeneratedColumn('type', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$RemoteAssetEntityTable.$convertertype); - static const i0.VerificationMeta _createdAtMeta = - const i0.VerificationMeta('createdAt'); + i0.GeneratedColumn( + 'type', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter(i1.$RemoteAssetEntityTable.$convertertype); + static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta( + 'createdAt', + ); @override late final i0.GeneratedColumn createdAt = - i0.GeneratedColumn('created_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + i0.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _widthMeta = - const i0.VerificationMeta('width'); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _widthMeta = const i0.VerificationMeta( + 'width', + ); @override late final i0.GeneratedColumn width = i0.GeneratedColumn( - 'width', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); - static const i0.VerificationMeta _heightMeta = - const i0.VerificationMeta('height'); + 'width', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _heightMeta = const i0.VerificationMeta( + 'height', + ); @override late final i0.GeneratedColumn height = i0.GeneratedColumn( - 'height', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + 'height', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _durationInSecondsMeta = const i0.VerificationMeta('durationInSeconds'); @override late final i0.GeneratedColumn durationInSeconds = - i0.GeneratedColumn('duration_in_seconds', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + i0.GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _checksumMeta = - const i0.VerificationMeta('checksum'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _checksumMeta = const i0.VerificationMeta( + 'checksum', + ); @override late final i0.GeneratedColumn checksum = i0.GeneratedColumn( - 'checksum', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _isFavoriteMeta = - const i0.VerificationMeta('isFavorite'); + 'checksum', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _isFavoriteMeta = const i0.VerificationMeta( + 'isFavorite', + ); @override late final i0.GeneratedColumn isFavorite = i0.GeneratedColumn( - 'is_favorite', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const i4.Constant(false)); - static const i0.VerificationMeta _ownerIdMeta = - const i0.VerificationMeta('ownerId'); + 'is_favorite', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const i4.Constant(false), + ); + static const i0.VerificationMeta _ownerIdMeta = const i0.VerificationMeta( + 'ownerId', + ); @override late final i0.GeneratedColumn ownerId = i0.GeneratedColumn( - 'owner_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); static const i0.VerificationMeta _localDateTimeMeta = const i0.VerificationMeta('localDateTime'); @override late final i0.GeneratedColumn localDateTime = - i0.GeneratedColumn('local_date_time', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); - static const i0.VerificationMeta _thumbHashMeta = - const i0.VerificationMeta('thumbHash'); + i0.GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _thumbHashMeta = const i0.VerificationMeta( + 'thumbHash', + ); @override late final i0.GeneratedColumn thumbHash = i0.GeneratedColumn( - 'thumb_hash', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _deletedAtMeta = - const i0.VerificationMeta('deletedAt'); + 'thumb_hash', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _deletedAtMeta = const i0.VerificationMeta( + 'deletedAt', + ); @override late final i0.GeneratedColumn deletedAt = - i0.GeneratedColumn('deleted_at', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); + i0.GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _livePhotoVideoIdMeta = const i0.VerificationMeta('livePhotoVideoId'); @override late final i0.GeneratedColumn livePhotoVideoId = - i0.GeneratedColumn('live_photo_video_id', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); + i0.GeneratedColumn( + 'live_photo_video_id', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); @override late final i0.GeneratedColumnWithTypeConverter - visibility = i0.GeneratedColumn('visibility', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$RemoteAssetEntityTable.$convertervisibility); - static const i0.VerificationMeta _stackIdMeta = - const i0.VerificationMeta('stackId'); + visibility = + i0.GeneratedColumn( + 'visibility', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter( + i1.$RemoteAssetEntityTable.$convertervisibility, + ); + static const i0.VerificationMeta _stackIdMeta = const i0.VerificationMeta( + 'stackId', + ); @override late final i0.GeneratedColumn stackId = i0.GeneratedColumn( - 'stack_id', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); + 'stack_id', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - ownerId, - localDateTime, - thumbHash, - deletedAt, - livePhotoVideoId, - visibility, - stackId - ]; + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -650,37 +841,51 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity static const String $name = 'remote_asset_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('name')) { context.handle( - _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta)); + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); } else if (isInserting) { context.missing(_nameMeta); } if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('width')) { context.handle( - _widthMeta, width.isAcceptableOrUnknown(data['width']!, _widthMeta)); + _widthMeta, + width.isAcceptableOrUnknown(data['width']!, _widthMeta), + ); } if (data.containsKey('height')) { - context.handle(_heightMeta, - height.isAcceptableOrUnknown(data['height']!, _heightMeta)); + context.handle( + _heightMeta, + height.isAcceptableOrUnknown(data['height']!, _heightMeta), + ); } if (data.containsKey('duration_in_seconds')) { context.handle( + _durationInSecondsMeta, + durationInSeconds.isAcceptableOrUnknown( + data['duration_in_seconds']!, _durationInSecondsMeta, - durationInSeconds.isAcceptableOrUnknown( - data['duration_in_seconds']!, _durationInSecondsMeta)); + ), + ); } if (data.containsKey('id')) { context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); @@ -688,46 +893,62 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity context.missing(_idMeta); } if (data.containsKey('checksum')) { - context.handle(_checksumMeta, - checksum.isAcceptableOrUnknown(data['checksum']!, _checksumMeta)); + context.handle( + _checksumMeta, + checksum.isAcceptableOrUnknown(data['checksum']!, _checksumMeta), + ); } else if (isInserting) { context.missing(_checksumMeta); } if (data.containsKey('is_favorite')) { context.handle( - _isFavoriteMeta, - isFavorite.isAcceptableOrUnknown( - data['is_favorite']!, _isFavoriteMeta)); + _isFavoriteMeta, + isFavorite.isAcceptableOrUnknown(data['is_favorite']!, _isFavoriteMeta), + ); } if (data.containsKey('owner_id')) { - context.handle(_ownerIdMeta, - ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta)); + context.handle( + _ownerIdMeta, + ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta), + ); } else if (isInserting) { context.missing(_ownerIdMeta); } if (data.containsKey('local_date_time')) { context.handle( + _localDateTimeMeta, + localDateTime.isAcceptableOrUnknown( + data['local_date_time']!, _localDateTimeMeta, - localDateTime.isAcceptableOrUnknown( - data['local_date_time']!, _localDateTimeMeta)); + ), + ); } if (data.containsKey('thumb_hash')) { - context.handle(_thumbHashMeta, - thumbHash.isAcceptableOrUnknown(data['thumb_hash']!, _thumbHashMeta)); + context.handle( + _thumbHashMeta, + thumbHash.isAcceptableOrUnknown(data['thumb_hash']!, _thumbHashMeta), + ); } if (data.containsKey('deleted_at')) { - context.handle(_deletedAtMeta, - deletedAt.isAcceptableOrUnknown(data['deleted_at']!, _deletedAtMeta)); + context.handle( + _deletedAtMeta, + deletedAt.isAcceptableOrUnknown(data['deleted_at']!, _deletedAtMeta), + ); } if (data.containsKey('live_photo_video_id')) { context.handle( + _livePhotoVideoIdMeta, + livePhotoVideoId.isAcceptableOrUnknown( + data['live_photo_video_id']!, _livePhotoVideoIdMeta, - livePhotoVideoId.isAcceptableOrUnknown( - data['live_photo_video_id']!, _livePhotoVideoIdMeta)); + ), + ); } if (data.containsKey('stack_id')) { - context.handle(_stackIdMeta, - stackId.isAcceptableOrUnknown(data['stack_id']!, _stackIdMeta)); + context.handle( + _stackIdMeta, + stackId.isAcceptableOrUnknown(data['stack_id']!, _stackIdMeta), + ); } return context; } @@ -735,47 +956,84 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity @override Set get $primaryKey => {id}; @override - i1.RemoteAssetEntityData map(Map data, - {String? tablePrefix}) { + i1.RemoteAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.RemoteAssetEntityData( - name: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, - type: i1.$RemoteAssetEntityTable.$convertertype.fromSql(attachedDatabase - .typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}type'])!), + name: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: i1.$RemoteAssetEntityTable.$convertertype.fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + ), createdAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}height']), + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}height'], + ), durationInSeconds: attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}checksum'])!, - isFavorite: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - ownerId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + i0.DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, localDateTime: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}local_date_time']), - thumbHash: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}thumb_hash']), - deletedAt: attachedDatabase.typeMapping - .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), + i0.DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), livePhotoVideoId: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, - data['${effectivePrefix}live_photo_video_id']), + i0.DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), visibility: i1.$RemoteAssetEntityTable.$convertervisibility.fromSql( - attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, data['${effectivePrefix}visibility'])!), - stackId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}stack_id']), + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + ), + stackId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), ); } @@ -787,8 +1045,9 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity static i0.JsonTypeConverter2 $convertertype = const i0.EnumIndexConverter(i2.AssetType.values); static i0.JsonTypeConverter2 - $convertervisibility = const i0.EnumIndexConverter( - i2.AssetVisibility.values); + $convertervisibility = const i0.EnumIndexConverter( + i2.AssetVisibility.values, + ); @override bool get withoutRowId => true; @override @@ -814,31 +1073,33 @@ class RemoteAssetEntityData extends i0.DataClass final String? livePhotoVideoId; final i2.AssetVisibility visibility; final String? stackId; - const RemoteAssetEntityData( - {required this.name, - required this.type, - required this.createdAt, - required this.updatedAt, - this.width, - this.height, - this.durationInSeconds, - required this.id, - required this.checksum, - required this.isFavorite, - required this.ownerId, - this.localDateTime, - this.thumbHash, - this.deletedAt, - this.livePhotoVideoId, - required this.visibility, - this.stackId}); + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; map['name'] = i0.Variable(name); { map['type'] = i0.Variable( - i1.$RemoteAssetEntityTable.$convertertype.toSql(type)); + i1.$RemoteAssetEntityTable.$convertertype.toSql(type), + ); } map['created_at'] = i0.Variable(createdAt); map['updated_at'] = i0.Variable(updatedAt); @@ -869,7 +1130,8 @@ class RemoteAssetEntityData extends i0.DataClass } { map['visibility'] = i0.Variable( - i1.$RemoteAssetEntityTable.$convertervisibility.toSql(visibility)); + i1.$RemoteAssetEntityTable.$convertervisibility.toSql(visibility), + ); } if (!nullToAbsent || stackId != null) { map['stack_id'] = i0.Variable(stackId); @@ -877,13 +1139,16 @@ class RemoteAssetEntityData extends i0.DataClass return map; } - factory RemoteAssetEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory RemoteAssetEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return RemoteAssetEntityData( name: serializer.fromJson(json['name']), - type: i1.$RemoteAssetEntityTable.$convertertype - .fromJson(serializer.fromJson(json['type'])), + type: i1.$RemoteAssetEntityTable.$convertertype.fromJson( + serializer.fromJson(json['type']), + ), createdAt: serializer.fromJson(json['createdAt']), updatedAt: serializer.fromJson(json['updatedAt']), width: serializer.fromJson(json['width']), @@ -897,8 +1162,9 @@ class RemoteAssetEntityData extends i0.DataClass thumbHash: serializer.fromJson(json['thumbHash']), deletedAt: serializer.fromJson(json['deletedAt']), livePhotoVideoId: serializer.fromJson(json['livePhotoVideoId']), - visibility: i1.$RemoteAssetEntityTable.$convertervisibility - .fromJson(serializer.fromJson(json['visibility'])), + visibility: i1.$RemoteAssetEntityTable.$convertervisibility.fromJson( + serializer.fromJson(json['visibility']), + ), stackId: serializer.fromJson(json['stackId']), ); } @@ -907,8 +1173,9 @@ class RemoteAssetEntityData extends i0.DataClass serializer ??= i0.driftRuntimeOptions.defaultSerializer; return { 'name': serializer.toJson(name), - 'type': serializer - .toJson(i1.$RemoteAssetEntityTable.$convertertype.toJson(type)), + 'type': serializer.toJson( + i1.$RemoteAssetEntityTable.$convertertype.toJson(type), + ), 'createdAt': serializer.toJson(createdAt), 'updatedAt': serializer.toJson(updatedAt), 'width': serializer.toJson(width), @@ -923,53 +1190,55 @@ class RemoteAssetEntityData extends i0.DataClass 'deletedAt': serializer.toJson(deletedAt), 'livePhotoVideoId': serializer.toJson(livePhotoVideoId), 'visibility': serializer.toJson( - i1.$RemoteAssetEntityTable.$convertervisibility.toJson(visibility)), + i1.$RemoteAssetEntityTable.$convertervisibility.toJson(visibility), + ), 'stackId': serializer.toJson(stackId), }; } - i1.RemoteAssetEntityData copyWith( - {String? name, - i2.AssetType? type, - DateTime? createdAt, - DateTime? updatedAt, - i0.Value width = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value durationInSeconds = const i0.Value.absent(), - String? id, - String? checksum, - bool? isFavorite, - String? ownerId, - i0.Value localDateTime = const i0.Value.absent(), - i0.Value thumbHash = const i0.Value.absent(), - i0.Value deletedAt = const i0.Value.absent(), - i0.Value livePhotoVideoId = const i0.Value.absent(), - i2.AssetVisibility? visibility, - i0.Value stackId = const i0.Value.absent()}) => - i1.RemoteAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present - ? durationInSeconds.value - : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum ?? this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - ownerId: ownerId ?? this.ownerId, - localDateTime: - localDateTime.present ? localDateTime.value : this.localDateTime, - thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - livePhotoVideoId: livePhotoVideoId.present - ? livePhotoVideoId.value - : this.livePhotoVideoId, - visibility: visibility ?? this.visibility, - stackId: stackId.present ? stackId.value : this.stackId, - ); + i1.RemoteAssetEntityData copyWith({ + String? name, + i2.AssetType? type, + DateTime? createdAt, + DateTime? updatedAt, + i0.Value width = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value durationInSeconds = const i0.Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + i0.Value localDateTime = const i0.Value.absent(), + i0.Value thumbHash = const i0.Value.absent(), + i0.Value deletedAt = const i0.Value.absent(), + i0.Value livePhotoVideoId = const i0.Value.absent(), + i2.AssetVisibility? visibility, + i0.Value stackId = const i0.Value.absent(), + }) => i1.RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + ); RemoteAssetEntityData copyWithCompanion(i1.RemoteAssetEntityCompanion data) { return RemoteAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -983,8 +1252,9 @@ class RemoteAssetEntityData extends i0.DataClass : this.durationInSeconds, id: data.id.present ? data.id.value : this.id, checksum: data.checksum.present ? data.checksum.value : this.checksum, - isFavorite: - data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, localDateTime: data.localDateTime.present ? data.localDateTime.value @@ -994,8 +1264,9 @@ class RemoteAssetEntityData extends i0.DataClass livePhotoVideoId: data.livePhotoVideoId.present ? data.livePhotoVideoId.value : this.livePhotoVideoId, - visibility: - data.visibility.present ? data.visibility.value : this.visibility, + visibility: data.visibility.present + ? data.visibility.value + : this.visibility, stackId: data.stackId.present ? data.stackId.value : this.stackId, ); } @@ -1026,23 +1297,24 @@ class RemoteAssetEntityData extends i0.DataClass @override int get hashCode => Object.hash( - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - ownerId, - localDateTime, - thumbHash, - deletedAt, - livePhotoVideoId, - visibility, - stackId); + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -1122,12 +1394,12 @@ class RemoteAssetEntityCompanion this.livePhotoVideoId = const i0.Value.absent(), required i2.AssetVisibility visibility, this.stackId = const i0.Value.absent(), - }) : name = i0.Value(name), - type = i0.Value(type), - id = i0.Value(id), - checksum = i0.Value(checksum), - ownerId = i0.Value(ownerId), - visibility = i0.Value(visibility); + }) : name = i0.Value(name), + type = i0.Value(type), + id = i0.Value(id), + checksum = i0.Value(checksum), + ownerId = i0.Value(ownerId), + visibility = i0.Value(visibility); static i0.Insertable custom({ i0.Expression? name, i0.Expression? type, @@ -1168,24 +1440,25 @@ class RemoteAssetEntityCompanion }); } - i1.RemoteAssetEntityCompanion copyWith( - {i0.Value? name, - i0.Value? type, - i0.Value? createdAt, - i0.Value? updatedAt, - i0.Value? width, - i0.Value? height, - i0.Value? durationInSeconds, - i0.Value? id, - i0.Value? checksum, - i0.Value? isFavorite, - i0.Value? ownerId, - i0.Value? localDateTime, - i0.Value? thumbHash, - i0.Value? deletedAt, - i0.Value? livePhotoVideoId, - i0.Value? visibility, - i0.Value? stackId}) { + i1.RemoteAssetEntityCompanion copyWith({ + i0.Value? name, + i0.Value? type, + i0.Value? createdAt, + i0.Value? updatedAt, + i0.Value? width, + i0.Value? height, + i0.Value? durationInSeconds, + i0.Value? id, + i0.Value? checksum, + i0.Value? isFavorite, + i0.Value? ownerId, + i0.Value? localDateTime, + i0.Value? thumbHash, + i0.Value? deletedAt, + i0.Value? livePhotoVideoId, + i0.Value? visibility, + i0.Value? stackId, + }) { return i1.RemoteAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -1215,7 +1488,8 @@ class RemoteAssetEntityCompanion } if (type.present) { map['type'] = i0.Variable( - i1.$RemoteAssetEntityTable.$convertertype.toSql(type.value)); + i1.$RemoteAssetEntityTable.$convertertype.toSql(type.value), + ); } if (createdAt.present) { map['created_at'] = i0.Variable(createdAt.value); @@ -1257,9 +1531,9 @@ class RemoteAssetEntityCompanion map['live_photo_video_id'] = i0.Variable(livePhotoVideoId.value); } if (visibility.present) { - map['visibility'] = i0.Variable(i1 - .$RemoteAssetEntityTable.$convertervisibility - .toSql(visibility.value)); + map['visibility'] = i0.Variable( + i1.$RemoteAssetEntityTable.$convertervisibility.toSql(visibility.value), + ); } if (stackId.present) { map['stack_id'] = i0.Variable(stackId.value); @@ -1292,5 +1566,7 @@ class RemoteAssetEntityCompanion } } -i0.Index get idxRemoteAssetChecksum => i0.Index('idx_remote_asset_checksum', - 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); +i0.Index get idxRemoteAssetChecksum => i0.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', +); diff --git a/mobile/lib/infrastructure/entities/stack.entity.drift.dart b/mobile/lib/infrastructure/entities/stack.entity.drift.dart index df0390aea0..ff7a3c3444 100644 --- a/mobile/lib/infrastructure/entities/stack.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/stack.entity.drift.dart @@ -9,51 +9,62 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i4; import 'package:drift/internal/modular.dart' as i5; -typedef $$StackEntityTableCreateCompanionBuilder = i1.StackEntityCompanion - Function({ - required String id, - i0.Value createdAt, - i0.Value updatedAt, - required String ownerId, - required String primaryAssetId, -}); -typedef $$StackEntityTableUpdateCompanionBuilder = i1.StackEntityCompanion - Function({ - i0.Value id, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value ownerId, - i0.Value primaryAssetId, -}); +typedef $$StackEntityTableCreateCompanionBuilder = + i1.StackEntityCompanion Function({ + required String id, + i0.Value createdAt, + i0.Value updatedAt, + required String ownerId, + required String primaryAssetId, + }); +typedef $$StackEntityTableUpdateCompanionBuilder = + i1.StackEntityCompanion Function({ + i0.Value id, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value ownerId, + i0.Value primaryAssetId, + }); -final class $$StackEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, i1.$StackEntityTable, i1.StackEntityData> { +final class $$StackEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$StackEntityTable, + i1.StackEntityData + > { $$StackEntityTableReferences(super.$_db, super.$_table, super.$_typedResult); static i4.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) => i5.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( - i5.ReadDatabaseContainer(db) - .resultSet('stack_entity') - .ownerId, - i5.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + .createAlias( + i0.$_aliasNameGenerator( + i5.ReadDatabaseContainer( + db, + ).resultSet('stack_entity').ownerId, + i5.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i4.$$UserEntityTableProcessedTableManager get ownerId { final $_column = $_itemColumn('owner_id')!; final manager = i4 .$$UserEntityTableTableManager( + $_db, + i5.ReadDatabaseContainer( $_db, - i5.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_ownerIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -67,37 +78,49 @@ class $$StackEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get primaryAssetId => $composableBuilder( - column: $table.primaryAssetId, - builder: (column) => i0.ColumnFilters(column)); + column: $table.primaryAssetId, + builder: (column) => i0.ColumnFilters(column), + ); i4.$$UserEntityTableFilterComposer get ownerId { final i4.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableFilterComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableFilterComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -112,39 +135,49 @@ class $$StackEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get primaryAssetId => $composableBuilder( - column: $table.primaryAssetId, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.primaryAssetId, + builder: (column) => i0.ColumnOrderings(column), + ); i4.$$UserEntityTableOrderingComposer get ownerId { final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -168,46 +201,58 @@ class $$StackEntityTableAnnotationComposer $composableBuilder(column: $table.updatedAt, builder: (column) => column); i0.GeneratedColumn get primaryAssetId => $composableBuilder( - column: $table.primaryAssetId, builder: (column) => column); + column: $table.primaryAssetId, + builder: (column) => column, + ); i4.$$UserEntityTableAnnotationComposer get ownerId { final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$StackEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$StackEntityTable, - i1.StackEntityData, - i1.$$StackEntityTableFilterComposer, - i1.$$StackEntityTableOrderingComposer, - i1.$$StackEntityTableAnnotationComposer, - $$StackEntityTableCreateCompanionBuilder, - $$StackEntityTableUpdateCompanionBuilder, - (i1.StackEntityData, i1.$$StackEntityTableReferences), - i1.StackEntityData, - i0.PrefetchHooks Function({bool ownerId})> { +class $$StackEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$StackEntityTable, + i1.StackEntityData, + i1.$$StackEntityTableFilterComposer, + i1.$$StackEntityTableOrderingComposer, + i1.$$StackEntityTableAnnotationComposer, + $$StackEntityTableCreateCompanionBuilder, + $$StackEntityTableUpdateCompanionBuilder, + (i1.StackEntityData, i1.$$StackEntityTableReferences), + i1.StackEntityData, + i0.PrefetchHooks Function({bool ownerId}) + > { $$StackEntityTableTableManager( - i0.GeneratedDatabase db, i1.$StackEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$StackEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -216,46 +261,49 @@ class $$StackEntityTableTableManager extends i0.RootTableManager< i1.$$StackEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$StackEntityTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - i0.Value id = const i0.Value.absent(), - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value ownerId = const i0.Value.absent(), - i0.Value primaryAssetId = const i0.Value.absent(), - }) => - i1.StackEntityCompanion( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - primaryAssetId: primaryAssetId, - ), - createCompanionCallback: ({ - required String id, - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - required String ownerId, - required String primaryAssetId, - }) => - i1.StackEntityCompanion.insert( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - primaryAssetId: primaryAssetId, - ), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value ownerId = const i0.Value.absent(), + i0.Value primaryAssetId = const i0.Value.absent(), + }) => i1.StackEntityCompanion( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + primaryAssetId: primaryAssetId, + ), + createCompanionCallback: + ({ + required String id, + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + required String ownerId, + required String primaryAssetId, + }) => i1.StackEntityCompanion.insert( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + primaryAssetId: primaryAssetId, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$StackEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$StackEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({ownerId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -266,40 +314,49 @@ class $$StackEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (ownerId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.ownerId, - referencedTable: - i1.$$StackEntityTableReferences._ownerIdTable(db), - referencedColumn: - i1.$$StackEntityTableReferences._ownerIdTable(db).id, - ) as T; - } + dynamic + > + >(state) { + if (ownerId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.ownerId, + referencedTable: i1.$$StackEntityTableReferences + ._ownerIdTable(db), + referencedColumn: i1 + .$$StackEntityTableReferences + ._ownerIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$StackEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$StackEntityTable, - i1.StackEntityData, - i1.$$StackEntityTableFilterComposer, - i1.$$StackEntityTableOrderingComposer, - i1.$$StackEntityTableAnnotationComposer, - $$StackEntityTableCreateCompanionBuilder, - $$StackEntityTableUpdateCompanionBuilder, - (i1.StackEntityData, i1.$$StackEntityTableReferences), - i1.StackEntityData, - i0.PrefetchHooks Function({bool ownerId})>; +typedef $$StackEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$StackEntityTable, + i1.StackEntityData, + i1.$$StackEntityTableFilterComposer, + i1.$$StackEntityTableOrderingComposer, + i1.$$StackEntityTableAnnotationComposer, + $$StackEntityTableCreateCompanionBuilder, + $$StackEntityTableUpdateCompanionBuilder, + (i1.StackEntityData, i1.$$StackEntityTableReferences), + i1.StackEntityData, + i0.PrefetchHooks Function({bool ownerId}) + >; class $StackEntityTable extends i2.StackEntity with i0.TableInfo<$StackEntityTable, i1.StackEntityData> { @@ -310,42 +367,71 @@ class $StackEntityTable extends i2.StackEntity static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _createdAtMeta = - const i0.VerificationMeta('createdAt'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta( + 'createdAt', + ); @override late final i0.GeneratedColumn createdAt = - i0.GeneratedColumn('created_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i3.currentDateAndTime); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + i0.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i3.currentDateAndTime, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i3.currentDateAndTime); - static const i0.VerificationMeta _ownerIdMeta = - const i0.VerificationMeta('ownerId'); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i3.currentDateAndTime, + ); + static const i0.VerificationMeta _ownerIdMeta = const i0.VerificationMeta( + 'ownerId', + ); @override late final i0.GeneratedColumn ownerId = i0.GeneratedColumn( - 'owner_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); static const i0.VerificationMeta _primaryAssetIdMeta = const i0.VerificationMeta('primaryAssetId'); @override late final i0.GeneratedColumn primaryAssetId = - i0.GeneratedColumn('primary_asset_id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); + i0.GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); @override - List get $columns => - [id, createdAt, updatedAt, ownerId, primaryAssetId]; + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -353,8 +439,9 @@ class $StackEntityTable extends i2.StackEntity static const String $name = 'stack_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { @@ -363,24 +450,33 @@ class $StackEntityTable extends i2.StackEntity context.missing(_idMeta); } if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('owner_id')) { - context.handle(_ownerIdMeta, - ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta)); + context.handle( + _ownerIdMeta, + ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta), + ); } else if (isInserting) { context.missing(_ownerIdMeta); } if (data.containsKey('primary_asset_id')) { context.handle( + _primaryAssetIdMeta, + primaryAssetId.isAcceptableOrUnknown( + data['primary_asset_id']!, _primaryAssetIdMeta, - primaryAssetId.isAcceptableOrUnknown( - data['primary_asset_id']!, _primaryAssetIdMeta)); + ), + ); } else if (isInserting) { context.missing(_primaryAssetIdMeta); } @@ -393,16 +489,26 @@ class $StackEntityTable extends i2.StackEntity i1.StackEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.StackEntityData( - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, createdAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, primaryAssetId: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!, + i0.DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, ); } @@ -424,12 +530,13 @@ class StackEntityData extends i0.DataClass final DateTime updatedAt; final String ownerId; final String primaryAssetId; - const StackEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.primaryAssetId}); + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -441,8 +548,10 @@ class StackEntityData extends i0.DataClass return map; } - factory StackEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory StackEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return StackEntityData( id: serializer.fromJson(json['id']), @@ -464,19 +573,19 @@ class StackEntityData extends i0.DataClass }; } - i1.StackEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? primaryAssetId}) => - i1.StackEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - primaryAssetId: primaryAssetId ?? this.primaryAssetId, - ); + i1.StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => i1.StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); StackEntityData copyWithCompanion(i1.StackEntityCompanion data) { return StackEntityData( id: data.id.present ? data.id.value : this.id, @@ -534,9 +643,9 @@ class StackEntityCompanion extends i0.UpdateCompanion { this.updatedAt = const i0.Value.absent(), required String ownerId, required String primaryAssetId, - }) : id = i0.Value(id), - ownerId = i0.Value(ownerId), - primaryAssetId = i0.Value(primaryAssetId); + }) : id = i0.Value(id), + ownerId = i0.Value(ownerId), + primaryAssetId = i0.Value(primaryAssetId); static i0.Insertable custom({ i0.Expression? id, i0.Expression? createdAt, @@ -553,12 +662,13 @@ class StackEntityCompanion extends i0.UpdateCompanion { }); } - i1.StackEntityCompanion copyWith( - {i0.Value? id, - i0.Value? createdAt, - i0.Value? updatedAt, - i0.Value? ownerId, - i0.Value? primaryAssetId}) { + i1.StackEntityCompanion copyWith({ + i0.Value? id, + i0.Value? createdAt, + i0.Value? updatedAt, + i0.Value? ownerId, + i0.Value? primaryAssetId, + }) { return i1.StackEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, diff --git a/mobile/lib/infrastructure/entities/store.entity.g.dart b/mobile/lib/infrastructure/entities/store.entity.g.dart index b97b5b0a28..7da92cf778 100644 --- a/mobile/lib/infrastructure/entities/store.entity.g.dart +++ b/mobile/lib/infrastructure/entities/store.entity.g.dart @@ -17,17 +17,14 @@ const StoreValueSchema = CollectionSchema( name: r'StoreValue', id: 902899285492123510, properties: { - r'intValue': PropertySchema( - id: 0, - name: r'intValue', - type: IsarType.long, - ), + r'intValue': PropertySchema(id: 0, name: r'intValue', type: IsarType.long), r'strValue': PropertySchema( id: 1, name: r'strValue', type: IsarType.string, - ) + ), }, + estimateSize: _storeValueEstimateSize, serialize: _storeValueSerialize, deserialize: _storeValueDeserialize, @@ -36,6 +33,7 @@ const StoreValueSchema = CollectionSchema( indexes: {}, links: {}, embeddedSchemas: {}, + getId: _storeValueGetId, getLinks: _storeValueGetLinks, attach: _storeValueAttach, @@ -120,10 +118,7 @@ extension StoreValueQueryWhere on QueryBuilder { QueryBuilder idEqualTo(Id id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); + return query.addWhereClause(IdWhereClause.between(lower: id, upper: id)); }); } @@ -149,8 +144,10 @@ extension StoreValueQueryWhere }); } - QueryBuilder idGreaterThan(Id id, - {bool include = false}) { + QueryBuilder idGreaterThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: id, includeLower: include), @@ -158,8 +155,10 @@ extension StoreValueQueryWhere }); } - QueryBuilder idLessThan(Id id, - {bool include = false}) { + QueryBuilder idLessThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: id, includeUpper: include), @@ -174,12 +173,14 @@ extension StoreValueQueryWhere bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + ), + ); }); } } @@ -187,12 +188,12 @@ extension StoreValueQueryWhere extension StoreValueQueryFilter on QueryBuilder { QueryBuilder idEqualTo( - Id value) { + Id value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: value), + ); }); } @@ -201,11 +202,13 @@ extension StoreValueQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -214,11 +217,13 @@ extension StoreValueQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -229,54 +234,55 @@ extension StoreValueQueryFilter bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder intValueIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'intValue', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'intValue'), + ); }); } QueryBuilder - intValueIsNotNull() { + intValueIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'intValue', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'intValue'), + ); }); } QueryBuilder intValueEqualTo( - int? value) { + int? value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'intValue', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'intValue', value: value), + ); }); } QueryBuilder - intValueGreaterThan( - int? value, { - bool include = false, - }) { + intValueGreaterThan(int? value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'intValue', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'intValue', + value: value, + ), + ); }); } @@ -285,11 +291,13 @@ extension StoreValueQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'intValue', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'intValue', + value: value, + ), + ); }); } @@ -300,30 +308,32 @@ extension StoreValueQueryFilter bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'intValue', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'intValue', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder strValueIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'strValue', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'strValue'), + ); }); } QueryBuilder - strValueIsNotNull() { + strValueIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'strValue', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'strValue'), + ); }); } @@ -332,27 +342,31 @@ extension StoreValueQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'strValue', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - strValueGreaterThan( + strValueGreaterThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'strValue', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -362,12 +376,14 @@ extension StoreValueQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'strValue', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -379,28 +395,29 @@ extension StoreValueQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'strValue', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'strValue', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - strValueStartsWith( - String value, { - bool caseSensitive = true, - }) { + strValueStartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'strValue', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -409,55 +426,61 @@ extension StoreValueQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'strValue', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder strValueContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'strValue', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder strValueMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'strValue', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'strValue', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - strValueIsEmpty() { + strValueIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'strValue', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'strValue', value: ''), + ); }); } QueryBuilder - strValueIsNotEmpty() { + strValueIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'strValue', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'strValue', value: ''), + ); }); } } @@ -542,8 +565,9 @@ extension StoreValueQueryWhereDistinct }); } - QueryBuilder distinctByStrValue( - {bool caseSensitive = true}) { + QueryBuilder distinctByStrValue({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'strValue', caseSensitive: caseSensitive); }); diff --git a/mobile/lib/infrastructure/entities/user.entity.dart b/mobile/lib/infrastructure/entities/user.entity.dart index b0c1e6e866..ab5b9a5621 100644 --- a/mobile/lib/infrastructure/entities/user.entity.dart +++ b/mobile/lib/infrastructure/entities/user.entity.dart @@ -43,36 +43,36 @@ class User { }); static User fromDto(UserDto dto) => User( - id: dto.id, - updatedAt: dto.updatedAt, - email: dto.email, - name: dto.name, - isAdmin: dto.isAdmin, - isPartnerSharedBy: dto.isPartnerSharedBy, - isPartnerSharedWith: dto.isPartnerSharedWith, - profileImagePath: dto.profileImagePath ?? "", - avatarColor: dto.avatarColor, - memoryEnabled: dto.memoryEnabled, - inTimeline: dto.inTimeline, - quotaUsageInBytes: dto.quotaUsageInBytes, - quotaSizeInBytes: dto.quotaSizeInBytes, - ); + id: dto.id, + updatedAt: dto.updatedAt, + email: dto.email, + name: dto.name, + isAdmin: dto.isAdmin, + isPartnerSharedBy: dto.isPartnerSharedBy, + isPartnerSharedWith: dto.isPartnerSharedWith, + profileImagePath: dto.profileImagePath ?? "", + avatarColor: dto.avatarColor, + memoryEnabled: dto.memoryEnabled, + inTimeline: dto.inTimeline, + quotaUsageInBytes: dto.quotaUsageInBytes, + quotaSizeInBytes: dto.quotaSizeInBytes, + ); UserDto toDto() => UserDto( - id: id, - email: email, - name: name, - isAdmin: isAdmin, - updatedAt: updatedAt, - profileImagePath: profileImagePath.isEmpty ? null : profileImagePath, - avatarColor: avatarColor, - memoryEnabled: memoryEnabled, - inTimeline: inTimeline, - isPartnerSharedBy: isPartnerSharedBy, - isPartnerSharedWith: isPartnerSharedWith, - quotaUsageInBytes: quotaUsageInBytes, - quotaSizeInBytes: quotaSizeInBytes, - ); + id: id, + email: email, + name: name, + isAdmin: isAdmin, + updatedAt: updatedAt, + profileImagePath: profileImagePath.isEmpty ? null : profileImagePath, + avatarColor: avatarColor, + memoryEnabled: memoryEnabled, + inTimeline: inTimeline, + isPartnerSharedBy: isPartnerSharedBy, + isPartnerSharedWith: isPartnerSharedWith, + quotaUsageInBytes: quotaUsageInBytes, + quotaSizeInBytes: quotaSizeInBytes, + ); } class UserEntity extends Table with DriftDefaultsMixin { diff --git a/mobile/lib/infrastructure/entities/user.entity.drift.dart b/mobile/lib/infrastructure/entities/user.entity.drift.dart index 32be969518..2c3c8a1f9c 100644 --- a/mobile/lib/infrastructure/entities/user.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/user.entity.drift.dart @@ -6,28 +6,28 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as i2; import 'package:drift/src/runtime/query_builder/query_builder.dart' as i3; -typedef $$UserEntityTableCreateCompanionBuilder = i1.UserEntityCompanion - Function({ - required String id, - required String name, - i0.Value isAdmin, - required String email, - i0.Value profileImagePath, - i0.Value updatedAt, - i0.Value quotaSizeInBytes, - i0.Value quotaUsageInBytes, -}); -typedef $$UserEntityTableUpdateCompanionBuilder = i1.UserEntityCompanion - Function({ - i0.Value id, - i0.Value name, - i0.Value isAdmin, - i0.Value email, - i0.Value profileImagePath, - i0.Value updatedAt, - i0.Value quotaSizeInBytes, - i0.Value quotaUsageInBytes, -}); +typedef $$UserEntityTableCreateCompanionBuilder = + i1.UserEntityCompanion Function({ + required String id, + required String name, + i0.Value isAdmin, + required String email, + i0.Value profileImagePath, + i0.Value updatedAt, + i0.Value quotaSizeInBytes, + i0.Value quotaUsageInBytes, + }); +typedef $$UserEntityTableUpdateCompanionBuilder = + i1.UserEntityCompanion Function({ + i0.Value id, + i0.Value name, + i0.Value isAdmin, + i0.Value email, + i0.Value profileImagePath, + i0.Value updatedAt, + i0.Value quotaSizeInBytes, + i0.Value quotaUsageInBytes, + }); class $$UserEntityTableFilterComposer extends i0.Composer { @@ -39,31 +39,44 @@ class $$UserEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnFilters(column)); + column: $table.name, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get isAdmin => $composableBuilder( - column: $table.isAdmin, builder: (column) => i0.ColumnFilters(column)); + column: $table.isAdmin, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get email => $composableBuilder( - column: $table.email, builder: (column) => i0.ColumnFilters(column)); + column: $table.email, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get profileImagePath => $composableBuilder( - column: $table.profileImagePath, - builder: (column) => i0.ColumnFilters(column)); + column: $table.profileImagePath, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get quotaSizeInBytes => $composableBuilder( - column: $table.quotaSizeInBytes, - builder: (column) => i0.ColumnFilters(column)); + column: $table.quotaSizeInBytes, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get quotaUsageInBytes => $composableBuilder( - column: $table.quotaUsageInBytes, - builder: (column) => i0.ColumnFilters(column)); + column: $table.quotaUsageInBytes, + builder: (column) => i0.ColumnFilters(column), + ); } class $$UserEntityTableOrderingComposer @@ -76,32 +89,44 @@ class $$UserEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnOrderings(column)); + column: $table.name, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isAdmin => $composableBuilder( - column: $table.isAdmin, builder: (column) => i0.ColumnOrderings(column)); + column: $table.isAdmin, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get email => $composableBuilder( - column: $table.email, builder: (column) => i0.ColumnOrderings(column)); + column: $table.email, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get profileImagePath => $composableBuilder( - column: $table.profileImagePath, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.profileImagePath, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get quotaSizeInBytes => $composableBuilder( - column: $table.quotaSizeInBytes, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.quotaSizeInBytes, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get quotaUsageInBytes => $composableBuilder( - column: $table.quotaUsageInBytes, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.quotaUsageInBytes, + builder: (column) => i0.ColumnOrderings(column), + ); } class $$UserEntityTableAnnotationComposer @@ -126,37 +151,51 @@ class $$UserEntityTableAnnotationComposer $composableBuilder(column: $table.email, builder: (column) => column); i0.GeneratedColumn get profileImagePath => $composableBuilder( - column: $table.profileImagePath, builder: (column) => column); + column: $table.profileImagePath, + builder: (column) => column, + ); i0.GeneratedColumn get updatedAt => $composableBuilder(column: $table.updatedAt, builder: (column) => column); i0.GeneratedColumn get quotaSizeInBytes => $composableBuilder( - column: $table.quotaSizeInBytes, builder: (column) => column); + column: $table.quotaSizeInBytes, + builder: (column) => column, + ); i0.GeneratedColumn get quotaUsageInBytes => $composableBuilder( - column: $table.quotaUsageInBytes, builder: (column) => column); + column: $table.quotaUsageInBytes, + builder: (column) => column, + ); } -class $$UserEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$UserEntityTable, - i1.UserEntityData, - i1.$$UserEntityTableFilterComposer, - i1.$$UserEntityTableOrderingComposer, - i1.$$UserEntityTableAnnotationComposer, - $$UserEntityTableCreateCompanionBuilder, - $$UserEntityTableUpdateCompanionBuilder, - ( - i1.UserEntityData, - i0.BaseReferences - ), - i1.UserEntityData, - i0.PrefetchHooks Function()> { +class $$UserEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$UserEntityTable, + i1.UserEntityData, + i1.$$UserEntityTableFilterComposer, + i1.$$UserEntityTableOrderingComposer, + i1.$$UserEntityTableAnnotationComposer, + $$UserEntityTableCreateCompanionBuilder, + $$UserEntityTableUpdateCompanionBuilder, + ( + i1.UserEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$UserEntityTable, + i1.UserEntityData + >, + ), + i1.UserEntityData, + i0.PrefetchHooks Function() + > { $$UserEntityTableTableManager( - i0.GeneratedDatabase db, i1.$UserEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$UserEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -165,69 +204,75 @@ class $$UserEntityTableTableManager extends i0.RootTableManager< i1.$$UserEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$UserEntityTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - i0.Value id = const i0.Value.absent(), - i0.Value name = const i0.Value.absent(), - i0.Value isAdmin = const i0.Value.absent(), - i0.Value email = const i0.Value.absent(), - i0.Value profileImagePath = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value quotaSizeInBytes = const i0.Value.absent(), - i0.Value quotaUsageInBytes = const i0.Value.absent(), - }) => - i1.UserEntityCompanion( - id: id, - name: name, - isAdmin: isAdmin, - email: email, - profileImagePath: profileImagePath, - updatedAt: updatedAt, - quotaSizeInBytes: quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes, - ), - createCompanionCallback: ({ - required String id, - required String name, - i0.Value isAdmin = const i0.Value.absent(), - required String email, - i0.Value profileImagePath = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value quotaSizeInBytes = const i0.Value.absent(), - i0.Value quotaUsageInBytes = const i0.Value.absent(), - }) => - i1.UserEntityCompanion.insert( - id: id, - name: name, - isAdmin: isAdmin, - email: email, - profileImagePath: profileImagePath, - updatedAt: updatedAt, - quotaSizeInBytes: quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes, - ), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value name = const i0.Value.absent(), + i0.Value isAdmin = const i0.Value.absent(), + i0.Value email = const i0.Value.absent(), + i0.Value profileImagePath = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value quotaSizeInBytes = const i0.Value.absent(), + i0.Value quotaUsageInBytes = const i0.Value.absent(), + }) => i1.UserEntityCompanion( + id: id, + name: name, + isAdmin: isAdmin, + email: email, + profileImagePath: profileImagePath, + updatedAt: updatedAt, + quotaSizeInBytes: quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes, + ), + createCompanionCallback: + ({ + required String id, + required String name, + i0.Value isAdmin = const i0.Value.absent(), + required String email, + i0.Value profileImagePath = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value quotaSizeInBytes = const i0.Value.absent(), + i0.Value quotaUsageInBytes = const i0.Value.absent(), + }) => i1.UserEntityCompanion.insert( + id: id, + name: name, + isAdmin: isAdmin, + email: email, + profileImagePath: profileImagePath, + updatedAt: updatedAt, + quotaSizeInBytes: quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes, + ), withReferenceMapper: (p0) => p0 .map((e) => (e.readTable(table), i0.BaseReferences(db, table, e))) .toList(), prefetchHooksCallback: null, - )); + ), + ); } -typedef $$UserEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$UserEntityTable, - i1.UserEntityData, - i1.$$UserEntityTableFilterComposer, - i1.$$UserEntityTableOrderingComposer, - i1.$$UserEntityTableAnnotationComposer, - $$UserEntityTableCreateCompanionBuilder, - $$UserEntityTableUpdateCompanionBuilder, - ( +typedef $$UserEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$UserEntityTable, i1.UserEntityData, - i0.BaseReferences - ), - i1.UserEntityData, - i0.PrefetchHooks Function()>; + i1.$$UserEntityTableFilterComposer, + i1.$$UserEntityTableOrderingComposer, + i1.$$UserEntityTableAnnotationComposer, + $$UserEntityTableCreateCompanionBuilder, + $$UserEntityTableUpdateCompanionBuilder, + ( + i1.UserEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$UserEntityTable, + i1.UserEntityData + >, + ), + i1.UserEntityData, + i0.PrefetchHooks Function() + >; class $UserEntityTable extends i2.UserEntity with i0.TableInfo<$UserEntityTable, i1.UserEntityData> { @@ -238,69 +283,106 @@ class $UserEntityTable extends i2.UserEntity static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _nameMeta = - const i0.VerificationMeta('name'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta( + 'name', + ); @override late final i0.GeneratedColumn name = i0.GeneratedColumn( - 'name', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _isAdminMeta = - const i0.VerificationMeta('isAdmin'); + 'name', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _isAdminMeta = const i0.VerificationMeta( + 'isAdmin', + ); @override late final i0.GeneratedColumn isAdmin = i0.GeneratedColumn( - 'is_admin', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - i0.GeneratedColumn.constraintIsAlways('CHECK ("is_admin" IN (0, 1))'), - defaultValue: const i3.Constant(false)); - static const i0.VerificationMeta _emailMeta = - const i0.VerificationMeta('email'); + 'is_admin', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const i3.Constant(false), + ); + static const i0.VerificationMeta _emailMeta = const i0.VerificationMeta( + 'email', + ); @override late final i0.GeneratedColumn email = i0.GeneratedColumn( - 'email', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); + 'email', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); static const i0.VerificationMeta _profileImagePathMeta = const i0.VerificationMeta('profileImagePath'); @override late final i0.GeneratedColumn profileImagePath = - i0.GeneratedColumn('profile_image_path', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + i0.GeneratedColumn( + 'profile_image_path', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i3.currentDateAndTime); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i3.currentDateAndTime, + ); static const i0.VerificationMeta _quotaSizeInBytesMeta = const i0.VerificationMeta('quotaSizeInBytes'); @override late final i0.GeneratedColumn quotaSizeInBytes = i0.GeneratedColumn( - 'quota_size_in_bytes', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + 'quota_size_in_bytes', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _quotaUsageInBytesMeta = const i0.VerificationMeta('quotaUsageInBytes'); @override late final i0.GeneratedColumn quotaUsageInBytes = - i0.GeneratedColumn('quota_usage_in_bytes', aliasedName, false, - type: i0.DriftSqlType.int, - requiredDuringInsert: false, - defaultValue: const i3.Constant(0)); + i0.GeneratedColumn( + 'quota_usage_in_bytes', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const i3.Constant(0), + ); @override List get $columns => [ - id, - name, - isAdmin, - email, - profileImagePath, - updatedAt, - quotaSizeInBytes, - quotaUsageInBytes - ]; + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -308,8 +390,9 @@ class $UserEntityTable extends i2.UserEntity static const String $name = 'user_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { @@ -319,41 +402,58 @@ class $UserEntityTable extends i2.UserEntity } if (data.containsKey('name')) { context.handle( - _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta)); + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); } else if (isInserting) { context.missing(_nameMeta); } if (data.containsKey('is_admin')) { - context.handle(_isAdminMeta, - isAdmin.isAcceptableOrUnknown(data['is_admin']!, _isAdminMeta)); + context.handle( + _isAdminMeta, + isAdmin.isAcceptableOrUnknown(data['is_admin']!, _isAdminMeta), + ); } if (data.containsKey('email')) { context.handle( - _emailMeta, email.isAcceptableOrUnknown(data['email']!, _emailMeta)); + _emailMeta, + email.isAcceptableOrUnknown(data['email']!, _emailMeta), + ); } else if (isInserting) { context.missing(_emailMeta); } if (data.containsKey('profile_image_path')) { context.handle( + _profileImagePathMeta, + profileImagePath.isAcceptableOrUnknown( + data['profile_image_path']!, _profileImagePathMeta, - profileImagePath.isAcceptableOrUnknown( - data['profile_image_path']!, _profileImagePathMeta)); + ), + ); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('quota_size_in_bytes')) { context.handle( + _quotaSizeInBytesMeta, + quotaSizeInBytes.isAcceptableOrUnknown( + data['quota_size_in_bytes']!, _quotaSizeInBytesMeta, - quotaSizeInBytes.isAcceptableOrUnknown( - data['quota_size_in_bytes']!, _quotaSizeInBytesMeta)); + ), + ); } if (data.containsKey('quota_usage_in_bytes')) { context.handle( + _quotaUsageInBytesMeta, + quotaUsageInBytes.isAcceptableOrUnknown( + data['quota_usage_in_bytes']!, _quotaUsageInBytesMeta, - quotaUsageInBytes.isAcceptableOrUnknown( - data['quota_usage_in_bytes']!, _quotaUsageInBytesMeta)); + ), + ); } return context; } @@ -364,22 +464,38 @@ class $UserEntityTable extends i2.UserEntity i1.UserEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.UserEntityData( - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, - isAdmin: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_admin'])!, - email: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}email'])!, + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}email'], + )!, profileImagePath: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}profile_image_path']), + i0.DriftSqlType.string, + data['${effectivePrefix}profile_image_path'], + ), updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, quotaSizeInBytes: attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, data['${effectivePrefix}quota_size_in_bytes']), + i0.DriftSqlType.int, + data['${effectivePrefix}quota_size_in_bytes'], + ), quotaUsageInBytes: attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, data['${effectivePrefix}quota_usage_in_bytes'])!, + i0.DriftSqlType.int, + data['${effectivePrefix}quota_usage_in_bytes'], + )!, ); } @@ -404,15 +520,16 @@ class UserEntityData extends i0.DataClass final DateTime updatedAt; final int? quotaSizeInBytes; final int quotaUsageInBytes; - const UserEntityData( - {required this.id, - required this.name, - required this.isAdmin, - required this.email, - this.profileImagePath, - required this.updatedAt, - this.quotaSizeInBytes, - required this.quotaUsageInBytes}); + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + this.profileImagePath, + required this.updatedAt, + this.quotaSizeInBytes, + required this.quotaUsageInBytes, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -431,8 +548,10 @@ class UserEntityData extends i0.DataClass return map; } - factory UserEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory UserEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return UserEntityData( id: serializer.fromJson(json['id']), @@ -460,29 +579,29 @@ class UserEntityData extends i0.DataClass }; } - i1.UserEntityData copyWith( - {String? id, - String? name, - bool? isAdmin, - String? email, - i0.Value profileImagePath = const i0.Value.absent(), - DateTime? updatedAt, - i0.Value quotaSizeInBytes = const i0.Value.absent(), - int? quotaUsageInBytes}) => - i1.UserEntityData( - id: id ?? this.id, - name: name ?? this.name, - isAdmin: isAdmin ?? this.isAdmin, - email: email ?? this.email, - profileImagePath: profileImagePath.present - ? profileImagePath.value - : this.profileImagePath, - updatedAt: updatedAt ?? this.updatedAt, - quotaSizeInBytes: quotaSizeInBytes.present - ? quotaSizeInBytes.value - : this.quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, - ); + i1.UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + i0.Value profileImagePath = const i0.Value.absent(), + DateTime? updatedAt, + i0.Value quotaSizeInBytes = const i0.Value.absent(), + int? quotaUsageInBytes, + }) => i1.UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath.present + ? profileImagePath.value + : this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes.present + ? quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); UserEntityData copyWithCompanion(i1.UserEntityCompanion data) { return UserEntityData( id: data.id.present ? data.id.value : this.id, @@ -518,8 +637,16 @@ class UserEntityData extends i0.DataClass } @override - int get hashCode => Object.hash(id, name, isAdmin, email, profileImagePath, - updatedAt, quotaSizeInBytes, quotaUsageInBytes); + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -562,9 +689,9 @@ class UserEntityCompanion extends i0.UpdateCompanion { this.updatedAt = const i0.Value.absent(), this.quotaSizeInBytes = const i0.Value.absent(), this.quotaUsageInBytes = const i0.Value.absent(), - }) : id = i0.Value(id), - name = i0.Value(name), - email = i0.Value(email); + }) : id = i0.Value(id), + name = i0.Value(name), + email = i0.Value(email); static i0.Insertable custom({ i0.Expression? id, i0.Expression? name, @@ -587,15 +714,16 @@ class UserEntityCompanion extends i0.UpdateCompanion { }); } - i1.UserEntityCompanion copyWith( - {i0.Value? id, - i0.Value? name, - i0.Value? isAdmin, - i0.Value? email, - i0.Value? profileImagePath, - i0.Value? updatedAt, - i0.Value? quotaSizeInBytes, - i0.Value? quotaUsageInBytes}) { + i1.UserEntityCompanion copyWith({ + i0.Value? id, + i0.Value? name, + i0.Value? isAdmin, + i0.Value? email, + i0.Value? profileImagePath, + i0.Value? updatedAt, + i0.Value? quotaSizeInBytes, + i0.Value? quotaUsageInBytes, + }) { return i1.UserEntityCompanion( id: id ?? this.id, name: name ?? this.name, diff --git a/mobile/lib/infrastructure/entities/user.entity.g.dart b/mobile/lib/infrastructure/entities/user.entity.g.dart index 37a793b2c3..bb87051731 100644 --- a/mobile/lib/infrastructure/entities/user.entity.g.dart +++ b/mobile/lib/infrastructure/entities/user.entity.g.dart @@ -23,26 +23,14 @@ const UserSchema = CollectionSchema( type: IsarType.byte, enumMap: _UseravatarColorEnumValueMap, ), - r'email': PropertySchema( - id: 1, - name: r'email', - type: IsarType.string, - ), - r'id': PropertySchema( - id: 2, - name: r'id', - type: IsarType.string, - ), + r'email': PropertySchema(id: 1, name: r'email', type: IsarType.string), + r'id': PropertySchema(id: 2, name: r'id', type: IsarType.string), r'inTimeline': PropertySchema( id: 3, name: r'inTimeline', type: IsarType.bool, ), - r'isAdmin': PropertySchema( - id: 4, - name: r'isAdmin', - type: IsarType.bool, - ), + r'isAdmin': PropertySchema(id: 4, name: r'isAdmin', type: IsarType.bool), r'isPartnerSharedBy': PropertySchema( id: 5, name: r'isPartnerSharedBy', @@ -58,11 +46,7 @@ const UserSchema = CollectionSchema( name: r'memoryEnabled', type: IsarType.bool, ), - r'name': PropertySchema( - id: 8, - name: r'name', - type: IsarType.string, - ), + r'name': PropertySchema(id: 8, name: r'name', type: IsarType.string), r'profileImagePath': PropertySchema( id: 9, name: r'profileImagePath', @@ -82,8 +66,9 @@ const UserSchema = CollectionSchema( id: 12, name: r'updatedAt', type: IsarType.dateTime, - ) + ), }, + estimateSize: _userEstimateSize, serialize: _userSerialize, deserialize: _userDeserialize, @@ -100,12 +85,13 @@ const UserSchema = CollectionSchema( name: r'id', type: IndexType.hash, caseSensitive: true, - ) + ), ], - ) + ), }, links: {}, embeddedSchemas: {}, + getId: _userGetId, getLinks: _userGetLinks, attach: _userAttach, @@ -155,7 +141,7 @@ User _userDeserialize( final object = User( avatarColor: _UseravatarColorValueEnumMap[reader.readByteOrNull(offsets[0])] ?? - AvatarColor.primary, + AvatarColor.primary, email: reader.readString(offsets[1]), id: reader.readString(offsets[2]), inTimeline: reader.readBoolOrNull(offsets[3]) ?? false, @@ -181,7 +167,8 @@ P _userDeserializeProp

( switch (propertyId) { case 0: return (_UseravatarColorValueEnumMap[reader.readByteOrNull(offset)] ?? - AvatarColor.primary) as P; + AvatarColor.primary) + as P; case 1: return (reader.readString(offset)) as P; case 2: @@ -311,10 +298,9 @@ extension UserQueryWhereSort on QueryBuilder { extension UserQueryWhere on QueryBuilder { QueryBuilder isarIdEqualTo(Id isarId) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: isarId, - upper: isarId, - )); + return query.addWhereClause( + IdWhereClause.between(lower: isarId, upper: isarId), + ); }); } @@ -340,8 +326,10 @@ extension UserQueryWhere on QueryBuilder { }); } - QueryBuilder isarIdGreaterThan(Id isarId, - {bool include = false}) { + QueryBuilder isarIdGreaterThan( + Id isarId, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: isarId, includeLower: include), @@ -349,8 +337,10 @@ extension UserQueryWhere on QueryBuilder { }); } - QueryBuilder isarIdLessThan(Id isarId, - {bool include = false}) { + QueryBuilder isarIdLessThan( + Id isarId, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: isarId, includeUpper: include), @@ -365,21 +355,22 @@ extension UserQueryWhere on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerIsarId, - includeLower: includeLower, - upper: upperIsarId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerIsarId, + includeLower: includeLower, + upper: upperIsarId, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder idEqualTo(String id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'id', - value: [id], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'id', value: [id]), + ); }); } @@ -387,32 +378,40 @@ extension UserQueryWhere on QueryBuilder { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [], - upper: [id], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [id], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [], + upper: [id], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [id], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [id], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [], - upper: [id], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [id], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [], + upper: [id], + includeUpper: false, + ), + ); } }); } @@ -420,12 +419,12 @@ extension UserQueryWhere on QueryBuilder { extension UserQueryFilter on QueryBuilder { QueryBuilder avatarColorEqualTo( - AvatarColor value) { + AvatarColor value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'avatarColor', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'avatarColor', value: value), + ); }); } @@ -434,11 +433,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'avatarColor', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'avatarColor', + value: value, + ), + ); }); } @@ -447,11 +448,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'avatarColor', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'avatarColor', + value: value, + ), + ); }); } @@ -462,13 +465,15 @@ extension UserQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'avatarColor', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'avatarColor', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } @@ -477,11 +482,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'email', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'email', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -491,12 +498,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'email', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'email', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -506,12 +515,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'email', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'email', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -523,14 +534,16 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'email', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'email', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -539,11 +552,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'email', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'email', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -552,51 +567,59 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'email', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'email', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder emailContains(String value, - {bool caseSensitive = true}) { + QueryBuilder emailContains( + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'email', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'email', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder emailMatches(String pattern, - {bool caseSensitive = true}) { + QueryBuilder emailMatches( + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'email', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'email', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder emailIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'email', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'email', value: ''), + ); }); } QueryBuilder emailIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'email', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'email', value: ''), + ); }); } @@ -605,11 +628,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -619,12 +644,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -634,12 +661,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -651,14 +680,16 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -667,11 +698,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -680,99 +713,105 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder idContains(String value, - {bool caseSensitive = true}) { + QueryBuilder idContains( + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder idMatches(String pattern, - {bool caseSensitive = true}) { + QueryBuilder idMatches( + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'id', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'id', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder idIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: ''), + ); }); } QueryBuilder idIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'id', value: ''), + ); }); } QueryBuilder inTimelineEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'inTimeline', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'inTimeline', value: value), + ); }); } QueryBuilder isAdminEqualTo(bool value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isAdmin', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isAdmin', value: value), + ); }); } QueryBuilder isPartnerSharedByEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isPartnerSharedBy', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isPartnerSharedBy', value: value), + ); }); } QueryBuilder isPartnerSharedWithEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isPartnerSharedWith', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isPartnerSharedWith', value: value), + ); }); } QueryBuilder isarIdEqualTo(Id value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isarId', value: value), + ); }); } @@ -781,11 +820,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } @@ -794,11 +835,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } @@ -809,23 +852,25 @@ extension UserQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'isarId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'isarId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder memoryEnabledEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'memoryEnabled', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'memoryEnabled', value: value), + ); }); } @@ -834,11 +879,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -848,12 +895,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -863,12 +912,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -880,14 +931,16 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'name', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'name', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -896,11 +949,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -909,51 +964,59 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder nameContains(String value, - {bool caseSensitive = true}) { + QueryBuilder nameContains( + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder nameMatches(String pattern, - {bool caseSensitive = true}) { + QueryBuilder nameMatches( + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'name', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'name', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder nameIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'name', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'name', value: ''), + ); }); } QueryBuilder nameIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'name', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'name', value: ''), + ); }); } @@ -962,11 +1025,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'profileImagePath', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'profileImagePath', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -976,12 +1041,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'profileImagePath', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'profileImagePath', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -991,12 +1058,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'profileImagePath', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'profileImagePath', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1008,14 +1077,16 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'profileImagePath', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'profileImagePath', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1024,11 +1095,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'profileImagePath', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'profileImagePath', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1037,63 +1110,69 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'profileImagePath', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'profileImagePath', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder profileImagePathContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'profileImagePath', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'profileImagePath', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder profileImagePathMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'profileImagePath', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'profileImagePath', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder profileImagePathIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'profileImagePath', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'profileImagePath', value: ''), + ); }); } QueryBuilder profileImagePathIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'profileImagePath', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'profileImagePath', value: ''), + ); }); } QueryBuilder quotaSizeInBytesEqualTo( - int value) { + int value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'quotaSizeInBytes', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'quotaSizeInBytes', value: value), + ); }); } @@ -1102,11 +1181,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'quotaSizeInBytes', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'quotaSizeInBytes', + value: value, + ), + ); }); } @@ -1115,11 +1196,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'quotaSizeInBytes', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'quotaSizeInBytes', + value: value, + ), + ); }); } @@ -1130,23 +1213,25 @@ extension UserQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'quotaSizeInBytes', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'quotaSizeInBytes', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder quotaUsageInBytesEqualTo( - int value) { + int value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'quotaUsageInBytes', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'quotaUsageInBytes', value: value), + ); }); } @@ -1155,11 +1240,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'quotaUsageInBytes', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'quotaUsageInBytes', + value: value, + ), + ); }); } @@ -1168,11 +1255,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'quotaUsageInBytes', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'quotaUsageInBytes', + value: value, + ), + ); }); } @@ -1183,23 +1272,25 @@ extension UserQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'quotaUsageInBytes', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'quotaUsageInBytes', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder updatedAtEqualTo( - DateTime value) { + DateTime value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'updatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'updatedAt', value: value), + ); }); } @@ -1208,11 +1299,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'updatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'updatedAt', + value: value, + ), + ); }); } @@ -1221,11 +1314,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'updatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'updatedAt', + value: value, + ), + ); }); } @@ -1236,13 +1331,15 @@ extension UserQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'updatedAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'updatedAt', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -1586,15 +1683,17 @@ extension UserQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByEmail( - {bool caseSensitive = true}) { + QueryBuilder distinctByEmail({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'email', caseSensitive: caseSensitive); }); } - QueryBuilder distinctById( - {bool caseSensitive = true}) { + QueryBuilder distinctById({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'id', caseSensitive: caseSensitive); }); @@ -1630,18 +1729,22 @@ extension UserQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByName( - {bool caseSensitive = true}) { + QueryBuilder distinctByName({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'name', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByProfileImagePath( - {bool caseSensitive = true}) { + QueryBuilder distinctByProfileImagePath({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'profileImagePath', - caseSensitive: caseSensitive); + return query.addDistinctBy( + r'profileImagePath', + caseSensitive: caseSensitive, + ); }); } diff --git a/mobile/lib/infrastructure/entities/user_metadata.entity.drift.dart b/mobile/lib/infrastructure/entities/user_metadata.entity.drift.dart index a13ea5c04e..1e9dc8a890 100644 --- a/mobile/lib/infrastructure/entities/user_metadata.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/user_metadata.entity.drift.dart @@ -11,51 +11,64 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i5; import 'package:drift/internal/modular.dart' as i6; -typedef $$UserMetadataEntityTableCreateCompanionBuilder - = i1.UserMetadataEntityCompanion Function({ - required String userId, - required i2.UserMetadataKey key, - required Map value, -}); -typedef $$UserMetadataEntityTableUpdateCompanionBuilder - = i1.UserMetadataEntityCompanion Function({ - i0.Value userId, - i0.Value key, - i0.Value> value, -}); +typedef $$UserMetadataEntityTableCreateCompanionBuilder = + i1.UserMetadataEntityCompanion Function({ + required String userId, + required i2.UserMetadataKey key, + required Map value, + }); +typedef $$UserMetadataEntityTableUpdateCompanionBuilder = + i1.UserMetadataEntityCompanion Function({ + i0.Value userId, + i0.Value key, + i0.Value> value, + }); -final class $$UserMetadataEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, - i1.$UserMetadataEntityTable, - i1.UserMetadataEntityData> { +final class $$UserMetadataEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$UserMetadataEntityTable, + i1.UserMetadataEntityData + > { $$UserMetadataEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i5.$UserEntityTable _userIdTable(i0.GeneratedDatabase db) => i6.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i6.ReadDatabaseContainer(db) .resultSet( - 'user_metadata_entity') + 'user_metadata_entity', + ) .userId, - i6.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + i6.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i5.$$UserEntityTableProcessedTableManager get userId { final $_column = $_itemColumn('user_id')!; final manager = i5 .$$UserEntityTableTableManager( + $_db, + i6.ReadDatabaseContainer( $_db, - i6.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_userIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -69,35 +82,45 @@ class $$UserMetadataEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnWithTypeConverterFilters - get key => $composableBuilder( - column: $table.key, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + get key => $composableBuilder( + column: $table.key, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); - i0.ColumnWithTypeConverterFilters, Map, - i3.Uint8List> - get value => $composableBuilder( - column: $table.value, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + i0.ColumnWithTypeConverterFilters< + Map, + Map, + i3.Uint8List + > + get value => $composableBuilder( + column: $table.value, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i5.$$UserEntityTableFilterComposer get userId { final i5.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.userId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableFilterComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableFilterComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -112,30 +135,39 @@ class $$UserMetadataEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get key => $composableBuilder( - column: $table.key, builder: (column) => i0.ColumnOrderings(column)); + column: $table.key, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get value => $composableBuilder( - column: $table.value, builder: (column) => i0.ColumnOrderings(column)); + column: $table.value, + builder: (column) => i0.ColumnOrderings(column), + ); i5.$$UserEntityTableOrderingComposer get userId { final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.userId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -153,89 +185,106 @@ class $$UserMetadataEntityTableAnnotationComposer $composableBuilder(column: $table.key, builder: (column) => column); i0.GeneratedColumnWithTypeConverter, i3.Uint8List> - get value => - $composableBuilder(column: $table.value, builder: (column) => column); + get value => + $composableBuilder(column: $table.value, builder: (column) => column); i5.$$UserEntityTableAnnotationComposer get userId { final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.userId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$UserMetadataEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$UserMetadataEntityTable, - i1.UserMetadataEntityData, - i1.$$UserMetadataEntityTableFilterComposer, - i1.$$UserMetadataEntityTableOrderingComposer, - i1.$$UserMetadataEntityTableAnnotationComposer, - $$UserMetadataEntityTableCreateCompanionBuilder, - $$UserMetadataEntityTableUpdateCompanionBuilder, - (i1.UserMetadataEntityData, i1.$$UserMetadataEntityTableReferences), - i1.UserMetadataEntityData, - i0.PrefetchHooks Function({bool userId})> { +class $$UserMetadataEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$UserMetadataEntityTable, + i1.UserMetadataEntityData, + i1.$$UserMetadataEntityTableFilterComposer, + i1.$$UserMetadataEntityTableOrderingComposer, + i1.$$UserMetadataEntityTableAnnotationComposer, + $$UserMetadataEntityTableCreateCompanionBuilder, + $$UserMetadataEntityTableUpdateCompanionBuilder, + (i1.UserMetadataEntityData, i1.$$UserMetadataEntityTableReferences), + i1.UserMetadataEntityData, + i0.PrefetchHooks Function({bool userId}) + > { $$UserMetadataEntityTableTableManager( - i0.GeneratedDatabase db, i1.$UserMetadataEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$UserMetadataEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => i1 .$$UserMetadataEntityTableFilterComposer($db: db, $table: table), createOrderingComposer: () => i1.$$UserMetadataEntityTableOrderingComposer( - $db: db, $table: table), + $db: db, + $table: table, + ), createComputedFieldComposer: () => i1.$$UserMetadataEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value userId = const i0.Value.absent(), - i0.Value key = const i0.Value.absent(), - i0.Value> value = const i0.Value.absent(), - }) => - i1.UserMetadataEntityCompanion( - userId: userId, - key: key, - value: value, - ), - createCompanionCallback: ({ - required String userId, - required i2.UserMetadataKey key, - required Map value, - }) => - i1.UserMetadataEntityCompanion.insert( - userId: userId, - key: key, - value: value, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value userId = const i0.Value.absent(), + i0.Value key = const i0.Value.absent(), + i0.Value> value = const i0.Value.absent(), + }) => i1.UserMetadataEntityCompanion( + userId: userId, + key: key, + value: value, + ), + createCompanionCallback: + ({ + required String userId, + required i2.UserMetadataKey key, + required Map value, + }) => i1.UserMetadataEntityCompanion.insert( + userId: userId, + key: key, + value: value, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$UserMetadataEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$UserMetadataEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({userId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -246,42 +295,50 @@ class $$UserMetadataEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (userId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.userId, - referencedTable: - i1.$$UserMetadataEntityTableReferences._userIdTable(db), - referencedColumn: i1.$$UserMetadataEntityTableReferences - ._userIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (userId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.userId, + referencedTable: i1 + .$$UserMetadataEntityTableReferences + ._userIdTable(db), + referencedColumn: i1 + .$$UserMetadataEntityTableReferences + ._userIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$UserMetadataEntityTableProcessedTableManager - = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$UserMetadataEntityTable, - i1.UserMetadataEntityData, - i1.$$UserMetadataEntityTableFilterComposer, - i1.$$UserMetadataEntityTableOrderingComposer, - i1.$$UserMetadataEntityTableAnnotationComposer, - $$UserMetadataEntityTableCreateCompanionBuilder, - $$UserMetadataEntityTableUpdateCompanionBuilder, - (i1.UserMetadataEntityData, i1.$$UserMetadataEntityTableReferences), - i1.UserMetadataEntityData, - i0.PrefetchHooks Function({bool userId})>; +typedef $$UserMetadataEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$UserMetadataEntityTable, + i1.UserMetadataEntityData, + i1.$$UserMetadataEntityTableFilterComposer, + i1.$$UserMetadataEntityTableOrderingComposer, + i1.$$UserMetadataEntityTableAnnotationComposer, + $$UserMetadataEntityTableCreateCompanionBuilder, + $$UserMetadataEntityTableUpdateCompanionBuilder, + (i1.UserMetadataEntityData, i1.$$UserMetadataEntityTableReferences), + i1.UserMetadataEntityData, + i0.PrefetchHooks Function({bool userId}) + >; class $UserMetadataEntityTable extends i4.UserMetadataEntity with i0.TableInfo<$UserMetadataEntityTable, i1.UserMetadataEntityData> { @@ -289,28 +346,46 @@ class $UserMetadataEntityTable extends i4.UserMetadataEntity final i0.GeneratedDatabase attachedDatabase; final String? _alias; $UserMetadataEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _userIdMeta = - const i0.VerificationMeta('userId'); + static const i0.VerificationMeta _userIdMeta = const i0.VerificationMeta( + 'userId', + ); @override late final i0.GeneratedColumn userId = i0.GeneratedColumn( - 'user_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'user_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); @override late final i0.GeneratedColumnWithTypeConverter key = - i0.GeneratedColumn('key', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$UserMetadataEntityTable.$converterkey); + i0.GeneratedColumn( + 'key', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter( + i1.$UserMetadataEntityTable.$converterkey, + ); @override - late final i0 - .GeneratedColumnWithTypeConverter, i3.Uint8List> - value = i0.GeneratedColumn('value', aliasedName, false, - type: i0.DriftSqlType.blob, requiredDuringInsert: true) - .withConverter>( - i1.$UserMetadataEntityTable.$convertervalue); + late final i0.GeneratedColumnWithTypeConverter< + Map, + i3.Uint8List + > + value = + i0.GeneratedColumn( + 'value', + aliasedName, + false, + type: i0.DriftSqlType.blob, + requiredDuringInsert: true, + ).withConverter>( + i1.$UserMetadataEntityTable.$convertervalue, + ); @override List get $columns => [userId, key, value]; @override @@ -320,13 +395,16 @@ class $UserMetadataEntityTable extends i4.UserMetadataEntity static const String $name = 'user_metadata_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('user_id')) { - context.handle(_userIdMeta, - userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta)); + context.handle( + _userIdMeta, + userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta), + ); } else if (isInserting) { context.missing(_userIdMeta); } @@ -336,18 +414,28 @@ class $UserMetadataEntityTable extends i4.UserMetadataEntity @override Set get $primaryKey => {userId, key}; @override - i1.UserMetadataEntityData map(Map data, - {String? tablePrefix}) { + i1.UserMetadataEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.UserMetadataEntityData( - userId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}user_id'])!, - key: i1.$UserMetadataEntityTable.$converterkey.fromSql(attachedDatabase - .typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}key'])!), + userId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: i1.$UserMetadataEntityTable.$converterkey.fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + ), value: i1.$UserMetadataEntityTable.$convertervalue.fromSql( - attachedDatabase.typeMapping - .read(i0.DriftSqlType.blob, data['${effectivePrefix}value'])!), + attachedDatabase.typeMapping.read( + i0.DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, + ), ); } @@ -358,9 +446,10 @@ class $UserMetadataEntityTable extends i4.UserMetadataEntity static i0.JsonTypeConverter2 $converterkey = const i0.EnumIndexConverter( - i2.UserMetadataKey.values); + i2.UserMetadataKey.values, + ); static i0.JsonTypeConverter2, i3.Uint8List, Object?> - $convertervalue = i4.userMetadataConverter; + $convertervalue = i4.userMetadataConverter; @override bool get withoutRowId => true; @override @@ -372,32 +461,41 @@ class UserMetadataEntityData extends i0.DataClass final String userId; final i2.UserMetadataKey key; final Map value; - const UserMetadataEntityData( - {required this.userId, required this.key, required this.value}); + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; map['user_id'] = i0.Variable(userId); { map['key'] = i0.Variable( - i1.$UserMetadataEntityTable.$converterkey.toSql(key)); + i1.$UserMetadataEntityTable.$converterkey.toSql(key), + ); } { map['value'] = i0.Variable( - i1.$UserMetadataEntityTable.$convertervalue.toSql(value)); + i1.$UserMetadataEntityTable.$convertervalue.toSql(value), + ); } return map; } - factory UserMetadataEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory UserMetadataEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return UserMetadataEntityData( userId: serializer.fromJson(json['userId']), - key: i1.$UserMetadataEntityTable.$converterkey - .fromJson(serializer.fromJson(json['key'])), - value: i1.$UserMetadataEntityTable.$convertervalue - .fromJson(serializer.fromJson(json['value'])), + key: i1.$UserMetadataEntityTable.$converterkey.fromJson( + serializer.fromJson(json['key']), + ), + value: i1.$UserMetadataEntityTable.$convertervalue.fromJson( + serializer.fromJson(json['value']), + ), ); } @override @@ -405,24 +503,27 @@ class UserMetadataEntityData extends i0.DataClass serializer ??= i0.driftRuntimeOptions.defaultSerializer; return { 'userId': serializer.toJson(userId), - 'key': serializer - .toJson(i1.$UserMetadataEntityTable.$converterkey.toJson(key)), + 'key': serializer.toJson( + i1.$UserMetadataEntityTable.$converterkey.toJson(key), + ), 'value': serializer.toJson( - i1.$UserMetadataEntityTable.$convertervalue.toJson(value)), + i1.$UserMetadataEntityTable.$convertervalue.toJson(value), + ), }; } - i1.UserMetadataEntityData copyWith( - {String? userId, - i2.UserMetadataKey? key, - Map? value}) => - i1.UserMetadataEntityData( - userId: userId ?? this.userId, - key: key ?? this.key, - value: value ?? this.value, - ); + i1.UserMetadataEntityData copyWith({ + String? userId, + i2.UserMetadataKey? key, + Map? value, + }) => i1.UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); UserMetadataEntityData copyWithCompanion( - i1.UserMetadataEntityCompanion data) { + i1.UserMetadataEntityCompanion data, + ) { return UserMetadataEntityData( userId: data.userId.present ? data.userId.value : this.userId, key: data.key.present ? data.key.value : this.key, @@ -465,9 +566,9 @@ class UserMetadataEntityCompanion required String userId, required i2.UserMetadataKey key, required Map value, - }) : userId = i0.Value(userId), - key = i0.Value(key), - value = i0.Value(value); + }) : userId = i0.Value(userId), + key = i0.Value(key), + value = i0.Value(value); static i0.Insertable custom({ i0.Expression? userId, i0.Expression? key, @@ -480,10 +581,11 @@ class UserMetadataEntityCompanion }); } - i1.UserMetadataEntityCompanion copyWith( - {i0.Value? userId, - i0.Value? key, - i0.Value>? value}) { + i1.UserMetadataEntityCompanion copyWith({ + i0.Value? userId, + i0.Value? key, + i0.Value>? value, + }) { return i1.UserMetadataEntityCompanion( userId: userId ?? this.userId, key: key ?? this.key, @@ -499,11 +601,13 @@ class UserMetadataEntityCompanion } if (key.present) { map['key'] = i0.Variable( - i1.$UserMetadataEntityTable.$converterkey.toSql(key.value)); + i1.$UserMetadataEntityTable.$converterkey.toSql(key.value), + ); } if (value.present) { map['value'] = i0.Variable( - i1.$UserMetadataEntityTable.$convertervalue.toSql(value.value)); + i1.$UserMetadataEntityTable.$convertervalue.toSql(value.value), + ); } return map; } diff --git a/mobile/lib/infrastructure/repositories/asset_media.repository.dart b/mobile/lib/infrastructure/repositories/asset_media.repository.dart index e8bf9ace43..6c81c7ff7f 100644 --- a/mobile/lib/infrastructure/repositories/asset_media.repository.dart +++ b/mobile/lib/infrastructure/repositories/asset_media.repository.dart @@ -6,21 +6,13 @@ import 'package:photo_manager/photo_manager.dart'; class AssetMediaRepository { const AssetMediaRepository(); - Future getThumbnail( - String id, { - int quality = 80, - Size size = const Size.square(256), - }) => - AssetEntity( - id: id, - // The below fields are not used in thumbnailDataWithSize but are required - // to create an AssetEntity instance. It is faster to create a dummy AssetEntity - // instance than to fetch the asset from the device first. - typeInt: AssetType.image.index, - width: size.width.toInt(), - height: size.height.toInt(), - ).thumbnailDataWithSize( - ThumbnailSize(size.width.toInt(), size.height.toInt()), - quality: quality, - ); + Future getThumbnail(String id, {int quality = 80, Size size = const Size.square(256)}) => AssetEntity( + id: id, + // The below fields are not used in thumbnailDataWithSize but are required + // to create an AssetEntity instance. It is faster to create a dummy AssetEntity + // instance than to fetch the asset from the device first. + typeInt: AssetType.image.index, + width: size.width.toInt(), + height: size.height.toInt(), + ).thumbnailDataWithSize(ThumbnailSize(size.width.toInt(), size.height.toInt()), quality: quality); } diff --git a/mobile/lib/infrastructure/repositories/backup.repository.dart b/mobile/lib/infrastructure/repositories/backup.repository.dart index aaba90de5f..ce38ff9311 100644 --- a/mobile/lib/infrastructure/repositories/backup.repository.dart +++ b/mobile/lib/infrastructure/repositories/backup.repository.dart @@ -24,9 +24,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { useColumns: false, ), ]) - ..where( - _db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.excluded), - ); + ..where(_db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.excluded)); } Future getTotalCount() async { @@ -79,9 +77,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { Future getBackupCount(String userId) async { final query = _db.localAlbumAssetEntity.selectOnly(distinct: true) - ..addColumns( - [_db.localAlbumAssetEntity.assetId], - ) + ..addColumns([_db.localAlbumAssetEntity.assetId]) ..join([ innerJoin( _db.localAlbumEntity, @@ -112,9 +108,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { Future> getCandidates(String userId) async { final selectedAlbumIds = _db.localAlbumEntity.selectOnly(distinct: true) ..addColumns([_db.localAlbumEntity.id]) - ..where( - _db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.selected), - ); + ..where(_db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.selected)); final query = _db.localAssetEntity.select() ..where( @@ -138,11 +132,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { ) & lae.id.isNotInQuery(_getExcludedSubquery()), ) - ..orderBy( - [ - (localAsset) => OrderingTerm.desc(localAsset.createdAt), - ], - ); + ..orderBy([(localAsset) => OrderingTerm.desc(localAsset.createdAt)]); return query.map((localAsset) => localAsset.toDto()).get(); } diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index 15fce4d649..7f6374ed24 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -59,66 +59,58 @@ class IsarDatabaseRepository implements IDatabaseRepository { PersonEntity, AssetFaceEntity, ], - include: { - 'package:immich_mobile/infrastructure/entities/merged_asset.drift', - }, + include: {'package:immich_mobile/infrastructure/entities/merged_asset.drift'}, ) class Drift extends $Drift implements IDatabaseRepository { Drift([QueryExecutor? executor]) - : super( - executor ?? - driftDatabase( - name: 'immich', - native: const DriftNativeOptions(shareAcrossIsolates: true), - ), - ); + : super(executor ?? driftDatabase(name: 'immich', native: const DriftNativeOptions(shareAcrossIsolates: true))); @override int get schemaVersion => 4; @override MigrationStrategy get migration => MigrationStrategy( - onUpgrade: (m, from, to) async { - // Run migration steps without foreign keys and re-enable them later - await customStatement('PRAGMA foreign_keys = OFF'); + onUpgrade: (m, from, to) async { + // Run migration steps without foreign keys and re-enable them later + await customStatement('PRAGMA foreign_keys = OFF'); - await m.runMigrationSteps( - from: from, - to: to, - steps: migrationSteps( - from1To2: (m, v2) async { - for (final entity in v2.entities) { - await m.drop(entity); - await m.create(entity); - } - }, - from2To3: (m, v3) async { - // Removed foreign key constraint on stack.primaryAssetId - await m.alterTable(TableMigration(v3.stackEntity)); - }, - from3To4: (m, v4) async { - // Thumbnail path column got removed from person_entity - await m.alterTable(TableMigration(v4.personEntity)); - // asset_face_entity is added - await m.create(v4.assetFaceEntity); - }, - ), - ); - - if (kDebugMode) { - // Fail if the migration broke foreign keys - final wrongFKs = await customSelect('PRAGMA foreign_key_check').get(); - assert(wrongFKs.isEmpty, '${wrongFKs.map((e) => e.data)}'); - } - - await customStatement('PRAGMA foreign_keys = ON;'); - }, - beforeOpen: (details) async { - await customStatement('PRAGMA foreign_keys = ON'); - await customStatement('PRAGMA synchronous = NORMAL'); - await customStatement('PRAGMA journal_mode = WAL'); - }, + await m.runMigrationSteps( + from: from, + to: to, + steps: migrationSteps( + from1To2: (m, v2) async { + for (final entity in v2.entities) { + await m.drop(entity); + await m.create(entity); + } + }, + from2To3: (m, v3) async { + // Removed foreign key constraint on stack.primaryAssetId + await m.alterTable(TableMigration(v3.stackEntity)); + }, + from3To4: (m, v4) async { + // Thumbnail path column got removed from person_entity + await m.alterTable(TableMigration(v4.personEntity)); + // asset_face_entity is added + await m.create(v4.assetFaceEntity); + }, + ), ); + + if (kDebugMode) { + // Fail if the migration broke foreign keys + final wrongFKs = await customSelect('PRAGMA foreign_key_check').get(); + assert(wrongFKs.isEmpty, '${wrongFKs.map((e) => e.data)}'); + } + + await customStatement('PRAGMA foreign_keys = ON;'); + }, + beforeOpen: (details) async { + await customStatement('PRAGMA foreign_keys = ON'); + await customStatement('PRAGMA synchronous = NORMAL'); + await customStatement('PRAGMA journal_mode = WAL'); + }, + ); } class DriftDatabaseRepository implements IDatabaseRepository { diff --git a/mobile/lib/infrastructure/repositories/db.repository.drift.dart b/mobile/lib/infrastructure/repositories/db.repository.drift.dart index f5962f09ab..296b87900e 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.drift.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.drift.dart @@ -41,213 +41,232 @@ abstract class $Drift extends i0.GeneratedDatabase { $Drift(i0.QueryExecutor e) : super(e); $DriftManager get managers => $DriftManager(this); late final i1.$UserEntityTable userEntity = i1.$UserEntityTable(this); - late final i2.$RemoteAssetEntityTable remoteAssetEntity = - i2.$RemoteAssetEntityTable(this); + late final i2.$RemoteAssetEntityTable remoteAssetEntity = i2 + .$RemoteAssetEntityTable(this); late final i3.$StackEntityTable stackEntity = i3.$StackEntityTable(this); - late final i4.$LocalAssetEntityTable localAssetEntity = - i4.$LocalAssetEntityTable(this); - late final i5.$LocalAlbumEntityTable localAlbumEntity = - i5.$LocalAlbumEntityTable(this); - late final i6.$LocalAlbumAssetEntityTable localAlbumAssetEntity = - i6.$LocalAlbumAssetEntityTable(this); - late final i7.$UserMetadataEntityTable userMetadataEntity = - i7.$UserMetadataEntityTable(this); - late final i8.$PartnerEntityTable partnerEntity = - i8.$PartnerEntityTable(this); - late final i9.$RemoteExifEntityTable remoteExifEntity = - i9.$RemoteExifEntityTable(this); - late final i10.$RemoteAlbumEntityTable remoteAlbumEntity = - i10.$RemoteAlbumEntityTable(this); - late final i11.$RemoteAlbumAssetEntityTable remoteAlbumAssetEntity = - i11.$RemoteAlbumAssetEntityTable(this); - late final i12.$RemoteAlbumUserEntityTable remoteAlbumUserEntity = - i12.$RemoteAlbumUserEntityTable(this); + late final i4.$LocalAssetEntityTable localAssetEntity = i4 + .$LocalAssetEntityTable(this); + late final i5.$LocalAlbumEntityTable localAlbumEntity = i5 + .$LocalAlbumEntityTable(this); + late final i6.$LocalAlbumAssetEntityTable localAlbumAssetEntity = i6 + .$LocalAlbumAssetEntityTable(this); + late final i7.$UserMetadataEntityTable userMetadataEntity = i7 + .$UserMetadataEntityTable(this); + late final i8.$PartnerEntityTable partnerEntity = i8.$PartnerEntityTable( + this, + ); + late final i9.$RemoteExifEntityTable remoteExifEntity = i9 + .$RemoteExifEntityTable(this); + late final i10.$RemoteAlbumEntityTable remoteAlbumEntity = i10 + .$RemoteAlbumEntityTable(this); + late final i11.$RemoteAlbumAssetEntityTable remoteAlbumAssetEntity = i11 + .$RemoteAlbumAssetEntityTable(this); + late final i12.$RemoteAlbumUserEntityTable remoteAlbumUserEntity = i12 + .$RemoteAlbumUserEntityTable(this); late final i13.$MemoryEntityTable memoryEntity = i13.$MemoryEntityTable(this); - late final i14.$MemoryAssetEntityTable memoryAssetEntity = - i14.$MemoryAssetEntityTable(this); + late final i14.$MemoryAssetEntityTable memoryAssetEntity = i14 + .$MemoryAssetEntityTable(this); late final i15.$PersonEntityTable personEntity = i15.$PersonEntityTable(this); - late final i16.$AssetFaceEntityTable assetFaceEntity = - i16.$AssetFaceEntityTable(this); - i17.MergedAssetDrift get mergedAssetDrift => i18.ReadDatabaseContainer(this) - .accessor(i17.MergedAssetDrift.new); + late final i16.$AssetFaceEntityTable assetFaceEntity = i16 + .$AssetFaceEntityTable(this); + i17.MergedAssetDrift get mergedAssetDrift => i18.ReadDatabaseContainer( + this, + ).accessor(i17.MergedAssetDrift.new); @override Iterable> get allTables => allSchemaEntities.whereType>(); @override List get allSchemaEntities => [ - userEntity, - remoteAssetEntity, - stackEntity, - localAssetEntity, - localAlbumEntity, - localAlbumAssetEntity, - i4.idxLocalAssetChecksum, - i2.uQRemoteAssetOwnerChecksum, - i2.idxRemoteAssetChecksum, - userMetadataEntity, - partnerEntity, - remoteExifEntity, - remoteAlbumEntity, - remoteAlbumAssetEntity, - remoteAlbumUserEntity, - memoryEntity, - memoryAssetEntity, - personEntity, - assetFaceEntity - ]; + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + i4.idxLocalAssetChecksum, + i2.uQRemoteAssetOwnerChecksum, + i2.idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + ]; @override - i0.StreamQueryUpdateRules get streamUpdateRules => - const i0.StreamQueryUpdateRules( - [ - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_asset_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('stack_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('local_asset_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('local_album_asset_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('local_album_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('local_album_asset_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('user_metadata_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('partner_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('partner_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('remote_asset_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_exif_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_album_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('remote_asset_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_album_entity', kind: i0.UpdateKind.update), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('remote_asset_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_album_asset_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('remote_album_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_album_asset_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('remote_album_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_album_user_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_album_user_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('memory_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('remote_asset_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('memory_asset_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('memory_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('memory_asset_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('person_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('remote_asset_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('asset_face_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('person_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('asset_face_entity', kind: i0.UpdateKind.update), - ], - ), - ], - ); + i0.StreamQueryUpdateRules + get streamUpdateRules => const i0.StreamQueryUpdateRules([ + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [i0.TableUpdate('stack_entity', kind: i0.UpdateKind.delete)], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'local_asset_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('local_album_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'local_album_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('local_album_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('user_metadata_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [i0.TableUpdate('partner_entity', kind: i0.UpdateKind.delete)], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [i0.TableUpdate('partner_entity', kind: i0.UpdateKind.delete)], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'remote_asset_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_exif_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_album_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'remote_asset_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_album_entity', kind: i0.UpdateKind.update), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'remote_asset_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_album_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'remote_album_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_album_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'remote_album_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_album_user_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_album_user_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [i0.TableUpdate('memory_entity', kind: i0.UpdateKind.delete)], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'remote_asset_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('memory_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'memory_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('memory_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [i0.TableUpdate('person_entity', kind: i0.UpdateKind.delete)], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'remote_asset_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [i0.TableUpdate('asset_face_entity', kind: i0.UpdateKind.delete)], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'person_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [i0.TableUpdate('asset_face_entity', kind: i0.UpdateKind.update)], + ), + ]); @override i0.DriftDatabaseOptions get options => const i0.DriftDatabaseOptions(storeDateTimeAsText: true); @@ -278,7 +297,9 @@ class $DriftManager { i10.$$RemoteAlbumEntityTableTableManager(_db, _db.remoteAlbumEntity); i11.$$RemoteAlbumAssetEntityTableTableManager get remoteAlbumAssetEntity => i11.$$RemoteAlbumAssetEntityTableTableManager( - _db, _db.remoteAlbumAssetEntity); + _db, + _db.remoteAlbumAssetEntity, + ); i12.$$RemoteAlbumUserEntityTableTableManager get remoteAlbumUserEntity => i12 .$$RemoteAlbumUserEntityTableTableManager(_db, _db.remoteAlbumUserEntity); i13.$$MemoryEntityTableTableManager get memoryEntity => diff --git a/mobile/lib/infrastructure/repositories/db.repository.steps.dart b/mobile/lib/infrastructure/repositories/db.repository.steps.dart index 57c90f731d..5bf20780f4 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.steps.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.steps.dart @@ -29,696 +29,1181 @@ final class Schema2 extends i0.VersionedSchema { personEntity, ]; late final Shape0 userEntity = Shape0( - source: i0.VersionedTable( - entityName: 'user_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_1, - _column_2, - _column_3, - _column_4, - _column_5, - _column_6, - _column_7, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_4, + _column_5, + _column_6, + _column_7, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape1 remoteAssetEntity = Shape1( - source: i0.VersionedTable( - entityName: 'remote_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_1, - _column_8, - _column_9, - _column_5, - _column_10, - _column_11, - _column_12, - _column_0, - _column_13, - _column_14, - _column_15, - _column_16, - _column_17, - _column_18, - _column_19, - _column_20, - _column_21, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_13, + _column_14, + _column_15, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape2 localAssetEntity = Shape2( - source: i0.VersionedTable( - entityName: 'local_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_1, - _column_8, - _column_9, - _column_5, - _column_10, - _column_11, - _column_12, - _column_0, - _column_22, - _column_14, - _column_23, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'local_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_22, + _column_14, + _column_23, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape3 stackEntity = Shape3( - source: i0.VersionedTable( - entityName: 'stack_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_9, - _column_5, - _column_15, - _column_24, - ], - attachedDatabase: database, - ), - alias: null); - final i1.Index idxLocalAssetChecksum = - i1.Index('idx_local_asset_checksum', 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); - final i1.Index uQRemoteAssetOwnerChecksum = i1.Index('UQ_remote_asset_owner_checksum', - 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); - final i1.Index idxRemoteAssetChecksum = - i1.Index('idx_remote_asset_checksum', 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_0, _column_9, _column_5, _column_15, _column_24], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLocalAssetChecksum = i1.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + final i1.Index uQRemoteAssetOwnerChecksum = i1.Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + final i1.Index idxRemoteAssetChecksum = i1.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); late final Shape4 userMetadataEntity = Shape4( - source: i0.VersionedTable( - entityName: 'user_metadata_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(user_id, "key")', - ], - columns: [ - _column_25, - _column_26, - _column_27, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'user_metadata_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(user_id, "key")'], + columns: [_column_25, _column_26, _column_27], + attachedDatabase: database, + ), + alias: null, + ); late final Shape5 partnerEntity = Shape5( - source: i0.VersionedTable( - entityName: 'partner_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(shared_by_id, shared_with_id)', - ], - columns: [ - _column_28, - _column_29, - _column_30, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'partner_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(shared_by_id, shared_with_id)'], + columns: [_column_28, _column_29, _column_30], + attachedDatabase: database, + ), + alias: null, + ); late final Shape6 localAlbumEntity = Shape6( - source: i0.VersionedTable( - entityName: 'local_album_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_1, - _column_5, - _column_31, - _column_32, - _column_33, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'local_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_5, + _column_31, + _column_32, + _column_33, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape7 localAlbumAssetEntity = Shape7( - source: i0.VersionedTable( - entityName: 'local_album_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id, album_id)', - ], - columns: [ - _column_34, - _column_35, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_34, _column_35], + attachedDatabase: database, + ), + alias: null, + ); late final Shape8 remoteExifEntity = Shape8( - source: i0.VersionedTable( - entityName: 'remote_exif_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id)', - ], - columns: [ - _column_36, - _column_37, - _column_38, - _column_39, - _column_40, - _column_41, - _column_11, - _column_10, - _column_42, - _column_43, - _column_44, - _column_45, - _column_46, - _column_47, - _column_48, - _column_49, - _column_50, - _column_51, - _column_52, - _column_53, - _column_54, - _column_55, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_exif_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id)'], + columns: [ + _column_36, + _column_37, + _column_38, + _column_39, + _column_40, + _column_41, + _column_11, + _column_10, + _column_42, + _column_43, + _column_44, + _column_45, + _column_46, + _column_47, + _column_48, + _column_49, + _column_50, + _column_51, + _column_52, + _column_53, + _column_54, + _column_55, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape9 remoteAlbumEntity = Shape9( - source: i0.VersionedTable( - entityName: 'remote_album_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_1, - _column_56, - _column_9, - _column_5, - _column_15, - _column_57, - _column_58, - _column_59, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_56, + _column_9, + _column_5, + _column_15, + _column_57, + _column_58, + _column_59, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape7 remoteAlbumAssetEntity = Shape7( - source: i0.VersionedTable( - entityName: 'remote_album_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id, album_id)', - ], - columns: [ - _column_36, - _column_60, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_36, _column_60], + attachedDatabase: database, + ), + alias: null, + ); late final Shape10 remoteAlbumUserEntity = Shape10( - source: i0.VersionedTable( - entityName: 'remote_album_user_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(album_id, user_id)', - ], - columns: [ - _column_60, - _column_25, - _column_61, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_album_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(album_id, user_id)'], + columns: [_column_60, _column_25, _column_61], + attachedDatabase: database, + ), + alias: null, + ); late final Shape11 memoryEntity = Shape11( - source: i0.VersionedTable( - entityName: 'memory_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_9, - _column_5, - _column_18, - _column_15, - _column_8, - _column_62, - _column_63, - _column_64, - _column_65, - _column_66, - _column_67, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'memory_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_18, + _column_15, + _column_8, + _column_62, + _column_63, + _column_64, + _column_65, + _column_66, + _column_67, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape12 memoryAssetEntity = Shape12( - source: i0.VersionedTable( - entityName: 'memory_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id, memory_id)', - ], - columns: [ - _column_36, - _column_68, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'memory_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, memory_id)'], + columns: [_column_36, _column_68], + attachedDatabase: database, + ), + alias: null, + ); late final Shape13 personEntity = Shape13( - source: i0.VersionedTable( - entityName: 'person_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_9, - _column_5, - _column_15, - _column_1, - _column_69, - _column_70, - _column_71, - _column_72, - _column_73, - _column_74, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'person_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_1, + _column_69, + _column_70, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null, + ); } class Shape0 extends i0.VersionedTable { Shape0({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get id => columnsByName['id']! as i1.GeneratedColumn; - i1.GeneratedColumn get name => columnsByName['name']! as i1.GeneratedColumn; - i1.GeneratedColumn get isAdmin => columnsByName['is_admin']! as i1.GeneratedColumn; - i1.GeneratedColumn get email => columnsByName['email']! as i1.GeneratedColumn; - i1.GeneratedColumn get profileImagePath => columnsByName['profile_image_path']! as i1.GeneratedColumn; - i1.GeneratedColumn get updatedAt => columnsByName['updated_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get quotaSizeInBytes => columnsByName['quota_size_in_bytes']! as i1.GeneratedColumn; - i1.GeneratedColumn get quotaUsageInBytes => columnsByName['quota_usage_in_bytes']! as i1.GeneratedColumn; + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get isAdmin => + columnsByName['is_admin']! as i1.GeneratedColumn; + i1.GeneratedColumn get email => + columnsByName['email']! as i1.GeneratedColumn; + i1.GeneratedColumn get profileImagePath => + columnsByName['profile_image_path']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get quotaSizeInBytes => + columnsByName['quota_size_in_bytes']! as i1.GeneratedColumn; + i1.GeneratedColumn get quotaUsageInBytes => + columnsByName['quota_usage_in_bytes']! as i1.GeneratedColumn; } i1.GeneratedColumn _column_0(String aliasedName) => - i1.GeneratedColumn('id', aliasedName, false, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'id', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_1(String aliasedName) => - i1.GeneratedColumn('name', aliasedName, false, type: i1.DriftSqlType.string); -i1.GeneratedColumn _column_2(String aliasedName) => i1.GeneratedColumn('is_admin', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('CHECK ("is_admin" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + i1.GeneratedColumn( + 'name', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); +i1.GeneratedColumn _column_2(String aliasedName) => + i1.GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); i1.GeneratedColumn _column_3(String aliasedName) => - i1.GeneratedColumn('email', aliasedName, false, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'email', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_4(String aliasedName) => - i1.GeneratedColumn('profile_image_path', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'profile_image_path', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_5(String aliasedName) => - i1.GeneratedColumn('updated_at', aliasedName, false, - type: i1.DriftSqlType.dateTime, defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + i1.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i1.DriftSqlType.dateTime, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); i1.GeneratedColumn _column_6(String aliasedName) => - i1.GeneratedColumn('quota_size_in_bytes', aliasedName, true, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'quota_size_in_bytes', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_7(String aliasedName) => - i1.GeneratedColumn('quota_usage_in_bytes', aliasedName, false, - type: i1.DriftSqlType.int, defaultValue: const CustomExpression('0')); + i1.GeneratedColumn( + 'quota_usage_in_bytes', + aliasedName, + false, + type: i1.DriftSqlType.int, + defaultValue: const CustomExpression('0'), + ); class Shape1 extends i0.VersionedTable { Shape1({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get name => columnsByName['name']! as i1.GeneratedColumn; - i1.GeneratedColumn get type => columnsByName['type']! as i1.GeneratedColumn; - i1.GeneratedColumn get createdAt => columnsByName['created_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get updatedAt => columnsByName['updated_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get width => columnsByName['width']! as i1.GeneratedColumn; - i1.GeneratedColumn get height => columnsByName['height']! as i1.GeneratedColumn; - i1.GeneratedColumn get durationInSeconds => columnsByName['duration_in_seconds']! as i1.GeneratedColumn; - i1.GeneratedColumn get id => columnsByName['id']! as i1.GeneratedColumn; - i1.GeneratedColumn get checksum => columnsByName['checksum']! as i1.GeneratedColumn; - i1.GeneratedColumn get isFavorite => columnsByName['is_favorite']! as i1.GeneratedColumn; - i1.GeneratedColumn get ownerId => columnsByName['owner_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get localDateTime => columnsByName['local_date_time']! as i1.GeneratedColumn; - i1.GeneratedColumn get thumbHash => columnsByName['thumb_hash']! as i1.GeneratedColumn; - i1.GeneratedColumn get deletedAt => columnsByName['deleted_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get type => + columnsByName['type']! as i1.GeneratedColumn; + i1.GeneratedColumn get createdAt => + columnsByName['created_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get width => + columnsByName['width']! as i1.GeneratedColumn; + i1.GeneratedColumn get height => + columnsByName['height']! as i1.GeneratedColumn; + i1.GeneratedColumn get durationInSeconds => + columnsByName['duration_in_seconds']! as i1.GeneratedColumn; + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get checksum => + columnsByName['checksum']! as i1.GeneratedColumn; + i1.GeneratedColumn get isFavorite => + columnsByName['is_favorite']! as i1.GeneratedColumn; + i1.GeneratedColumn get ownerId => + columnsByName['owner_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get localDateTime => + columnsByName['local_date_time']! as i1.GeneratedColumn; + i1.GeneratedColumn get thumbHash => + columnsByName['thumb_hash']! as i1.GeneratedColumn; + i1.GeneratedColumn get deletedAt => + columnsByName['deleted_at']! as i1.GeneratedColumn; i1.GeneratedColumn get livePhotoVideoId => columnsByName['live_photo_video_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get visibility => columnsByName['visibility']! as i1.GeneratedColumn; - i1.GeneratedColumn get stackId => columnsByName['stack_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get visibility => + columnsByName['visibility']! as i1.GeneratedColumn; + i1.GeneratedColumn get stackId => + columnsByName['stack_id']! as i1.GeneratedColumn; } i1.GeneratedColumn _column_8(String aliasedName) => - i1.GeneratedColumn('type', aliasedName, false, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'type', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_9(String aliasedName) => - i1.GeneratedColumn('created_at', aliasedName, false, - type: i1.DriftSqlType.dateTime, defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + i1.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i1.DriftSqlType.dateTime, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); i1.GeneratedColumn _column_10(String aliasedName) => - i1.GeneratedColumn('width', aliasedName, true, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'width', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_11(String aliasedName) => - i1.GeneratedColumn('height', aliasedName, true, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'height', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_12(String aliasedName) => - i1.GeneratedColumn('duration_in_seconds', aliasedName, true, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_13(String aliasedName) => - i1.GeneratedColumn('checksum', aliasedName, false, type: i1.DriftSqlType.string); -i1.GeneratedColumn _column_14(String aliasedName) => i1.GeneratedColumn('is_favorite', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); -i1.GeneratedColumn _column_15(String aliasedName) => i1.GeneratedColumn('owner_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); + i1.GeneratedColumn( + 'checksum', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); +i1.GeneratedColumn _column_14(String aliasedName) => + i1.GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); +i1.GeneratedColumn _column_15(String aliasedName) => + i1.GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); i1.GeneratedColumn _column_16(String aliasedName) => - i1.GeneratedColumn('local_date_time', aliasedName, true, type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: i1.DriftSqlType.dateTime, + ); i1.GeneratedColumn _column_17(String aliasedName) => - i1.GeneratedColumn('thumb_hash', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_18(String aliasedName) => - i1.GeneratedColumn('deleted_at', aliasedName, true, type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: i1.DriftSqlType.dateTime, + ); i1.GeneratedColumn _column_19(String aliasedName) => - i1.GeneratedColumn('live_photo_video_id', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'live_photo_video_id', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_20(String aliasedName) => - i1.GeneratedColumn('visibility', aliasedName, false, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'visibility', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_21(String aliasedName) => - i1.GeneratedColumn('stack_id', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); class Shape2 extends i0.VersionedTable { Shape2({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get name => columnsByName['name']! as i1.GeneratedColumn; - i1.GeneratedColumn get type => columnsByName['type']! as i1.GeneratedColumn; - i1.GeneratedColumn get createdAt => columnsByName['created_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get updatedAt => columnsByName['updated_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get width => columnsByName['width']! as i1.GeneratedColumn; - i1.GeneratedColumn get height => columnsByName['height']! as i1.GeneratedColumn; - i1.GeneratedColumn get durationInSeconds => columnsByName['duration_in_seconds']! as i1.GeneratedColumn; - i1.GeneratedColumn get id => columnsByName['id']! as i1.GeneratedColumn; - i1.GeneratedColumn get checksum => columnsByName['checksum']! as i1.GeneratedColumn; - i1.GeneratedColumn get isFavorite => columnsByName['is_favorite']! as i1.GeneratedColumn; - i1.GeneratedColumn get orientation => columnsByName['orientation']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get type => + columnsByName['type']! as i1.GeneratedColumn; + i1.GeneratedColumn get createdAt => + columnsByName['created_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get width => + columnsByName['width']! as i1.GeneratedColumn; + i1.GeneratedColumn get height => + columnsByName['height']! as i1.GeneratedColumn; + i1.GeneratedColumn get durationInSeconds => + columnsByName['duration_in_seconds']! as i1.GeneratedColumn; + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get checksum => + columnsByName['checksum']! as i1.GeneratedColumn; + i1.GeneratedColumn get isFavorite => + columnsByName['is_favorite']! as i1.GeneratedColumn; + i1.GeneratedColumn get orientation => + columnsByName['orientation']! as i1.GeneratedColumn; } i1.GeneratedColumn _column_22(String aliasedName) => - i1.GeneratedColumn('checksum', aliasedName, true, type: i1.DriftSqlType.string); -i1.GeneratedColumn _column_23(String aliasedName) => i1.GeneratedColumn('orientation', aliasedName, false, - type: i1.DriftSqlType.int, defaultValue: const CustomExpression('0')); + i1.GeneratedColumn( + 'checksum', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); +i1.GeneratedColumn _column_23(String aliasedName) => + i1.GeneratedColumn( + 'orientation', + aliasedName, + false, + type: i1.DriftSqlType.int, + defaultValue: const CustomExpression('0'), + ); class Shape3 extends i0.VersionedTable { Shape3({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get id => columnsByName['id']! as i1.GeneratedColumn; - i1.GeneratedColumn get createdAt => columnsByName['created_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get updatedAt => columnsByName['updated_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get ownerId => columnsByName['owner_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get primaryAssetId => columnsByName['primary_asset_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get createdAt => + columnsByName['created_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get ownerId => + columnsByName['owner_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get primaryAssetId => + columnsByName['primary_asset_id']! as i1.GeneratedColumn; } i1.GeneratedColumn _column_24(String aliasedName) => - i1.GeneratedColumn('primary_asset_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id)')); + i1.GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id)', + ), + ); class Shape4 extends i0.VersionedTable { Shape4({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get userId => columnsByName['user_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get key => columnsByName['key']! as i1.GeneratedColumn; - i1.GeneratedColumn get value => columnsByName['value']! as i1.GeneratedColumn; + i1.GeneratedColumn get userId => + columnsByName['user_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get key => + columnsByName['key']! as i1.GeneratedColumn; + i1.GeneratedColumn get value => + columnsByName['value']! as i1.GeneratedColumn; } -i1.GeneratedColumn _column_25(String aliasedName) => i1.GeneratedColumn('user_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); +i1.GeneratedColumn _column_25(String aliasedName) => + i1.GeneratedColumn( + 'user_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); i1.GeneratedColumn _column_26(String aliasedName) => - i1.GeneratedColumn('key', aliasedName, false, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'key', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_27(String aliasedName) => - i1.GeneratedColumn('value', aliasedName, false, type: i1.DriftSqlType.blob); + i1.GeneratedColumn( + 'value', + aliasedName, + false, + type: i1.DriftSqlType.blob, + ); class Shape5 extends i0.VersionedTable { Shape5({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get sharedById => columnsByName['shared_by_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get sharedWithId => columnsByName['shared_with_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get inTimeline => columnsByName['in_timeline']! as i1.GeneratedColumn; + i1.GeneratedColumn get sharedById => + columnsByName['shared_by_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get sharedWithId => + columnsByName['shared_with_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get inTimeline => + columnsByName['in_timeline']! as i1.GeneratedColumn; } i1.GeneratedColumn _column_28(String aliasedName) => - i1.GeneratedColumn('shared_by_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); + i1.GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); i1.GeneratedColumn _column_29(String aliasedName) => - i1.GeneratedColumn('shared_with_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); -i1.GeneratedColumn _column_30(String aliasedName) => i1.GeneratedColumn('in_timeline', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('CHECK ("in_timeline" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + i1.GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); +i1.GeneratedColumn _column_30(String aliasedName) => + i1.GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); class Shape6 extends i0.VersionedTable { Shape6({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get id => columnsByName['id']! as i1.GeneratedColumn; - i1.GeneratedColumn get name => columnsByName['name']! as i1.GeneratedColumn; - i1.GeneratedColumn get updatedAt => columnsByName['updated_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get backupSelection => columnsByName['backup_selection']! as i1.GeneratedColumn; - i1.GeneratedColumn get isIosSharedAlbum => columnsByName['is_ios_shared_album']! as i1.GeneratedColumn; - i1.GeneratedColumn get marker_ => columnsByName['marker']! as i1.GeneratedColumn; + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get backupSelection => + columnsByName['backup_selection']! as i1.GeneratedColumn; + i1.GeneratedColumn get isIosSharedAlbum => + columnsByName['is_ios_shared_album']! as i1.GeneratedColumn; + i1.GeneratedColumn get marker_ => + columnsByName['marker']! as i1.GeneratedColumn; } i1.GeneratedColumn _column_31(String aliasedName) => - i1.GeneratedColumn('backup_selection', aliasedName, false, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_32(String aliasedName) => - i1.GeneratedColumn('is_ios_shared_album', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('CHECK ("is_ios_shared_album" IN (0, 1))'), - defaultValue: const CustomExpression('0')); -i1.GeneratedColumn _column_33(String aliasedName) => i1.GeneratedColumn('marker', aliasedName, true, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('CHECK ("marker" IN (0, 1))')); + i1.GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); +i1.GeneratedColumn _column_33(String aliasedName) => + i1.GeneratedColumn( + 'marker', + aliasedName, + true, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); class Shape7 extends i0.VersionedTable { Shape7({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get assetId => columnsByName['asset_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get albumId => columnsByName['album_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get assetId => + columnsByName['asset_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get albumId => + columnsByName['album_id']! as i1.GeneratedColumn; } -i1.GeneratedColumn _column_34(String aliasedName) => i1.GeneratedColumn('asset_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('REFERENCES local_asset_entity (id) ON DELETE CASCADE')); -i1.GeneratedColumn _column_35(String aliasedName) => i1.GeneratedColumn('album_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('REFERENCES local_album_entity (id) ON DELETE CASCADE')); +i1.GeneratedColumn _column_34(String aliasedName) => + i1.GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); +i1.GeneratedColumn _column_35(String aliasedName) => + i1.GeneratedColumn( + 'album_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); class Shape8 extends i0.VersionedTable { Shape8({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get assetId => columnsByName['asset_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get city => columnsByName['city']! as i1.GeneratedColumn; - i1.GeneratedColumn get state => columnsByName['state']! as i1.GeneratedColumn; - i1.GeneratedColumn get country => columnsByName['country']! as i1.GeneratedColumn; + i1.GeneratedColumn get assetId => + columnsByName['asset_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get city => + columnsByName['city']! as i1.GeneratedColumn; + i1.GeneratedColumn get state => + columnsByName['state']! as i1.GeneratedColumn; + i1.GeneratedColumn get country => + columnsByName['country']! as i1.GeneratedColumn; i1.GeneratedColumn get dateTimeOriginal => columnsByName['date_time_original']! as i1.GeneratedColumn; - i1.GeneratedColumn get description => columnsByName['description']! as i1.GeneratedColumn; - i1.GeneratedColumn get height => columnsByName['height']! as i1.GeneratedColumn; - i1.GeneratedColumn get width => columnsByName['width']! as i1.GeneratedColumn; - i1.GeneratedColumn get exposureTime => columnsByName['exposure_time']! as i1.GeneratedColumn; - i1.GeneratedColumn get fNumber => columnsByName['f_number']! as i1.GeneratedColumn; - i1.GeneratedColumn get fileSize => columnsByName['file_size']! as i1.GeneratedColumn; - i1.GeneratedColumn get focalLength => columnsByName['focal_length']! as i1.GeneratedColumn; - i1.GeneratedColumn get latitude => columnsByName['latitude']! as i1.GeneratedColumn; - i1.GeneratedColumn get longitude => columnsByName['longitude']! as i1.GeneratedColumn; - i1.GeneratedColumn get iso => columnsByName['iso']! as i1.GeneratedColumn; - i1.GeneratedColumn get make => columnsByName['make']! as i1.GeneratedColumn; - i1.GeneratedColumn get model => columnsByName['model']! as i1.GeneratedColumn; - i1.GeneratedColumn get lens => columnsByName['lens']! as i1.GeneratedColumn; - i1.GeneratedColumn get orientation => columnsByName['orientation']! as i1.GeneratedColumn; - i1.GeneratedColumn get timeZone => columnsByName['time_zone']! as i1.GeneratedColumn; - i1.GeneratedColumn get rating => columnsByName['rating']! as i1.GeneratedColumn; - i1.GeneratedColumn get projectionType => columnsByName['projection_type']! as i1.GeneratedColumn; + i1.GeneratedColumn get description => + columnsByName['description']! as i1.GeneratedColumn; + i1.GeneratedColumn get height => + columnsByName['height']! as i1.GeneratedColumn; + i1.GeneratedColumn get width => + columnsByName['width']! as i1.GeneratedColumn; + i1.GeneratedColumn get exposureTime => + columnsByName['exposure_time']! as i1.GeneratedColumn; + i1.GeneratedColumn get fNumber => + columnsByName['f_number']! as i1.GeneratedColumn; + i1.GeneratedColumn get fileSize => + columnsByName['file_size']! as i1.GeneratedColumn; + i1.GeneratedColumn get focalLength => + columnsByName['focal_length']! as i1.GeneratedColumn; + i1.GeneratedColumn get latitude => + columnsByName['latitude']! as i1.GeneratedColumn; + i1.GeneratedColumn get longitude => + columnsByName['longitude']! as i1.GeneratedColumn; + i1.GeneratedColumn get iso => + columnsByName['iso']! as i1.GeneratedColumn; + i1.GeneratedColumn get make => + columnsByName['make']! as i1.GeneratedColumn; + i1.GeneratedColumn get model => + columnsByName['model']! as i1.GeneratedColumn; + i1.GeneratedColumn get lens => + columnsByName['lens']! as i1.GeneratedColumn; + i1.GeneratedColumn get orientation => + columnsByName['orientation']! as i1.GeneratedColumn; + i1.GeneratedColumn get timeZone => + columnsByName['time_zone']! as i1.GeneratedColumn; + i1.GeneratedColumn get rating => + columnsByName['rating']! as i1.GeneratedColumn; + i1.GeneratedColumn get projectionType => + columnsByName['projection_type']! as i1.GeneratedColumn; } -i1.GeneratedColumn _column_36(String aliasedName) => i1.GeneratedColumn('asset_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); +i1.GeneratedColumn _column_36(String aliasedName) => + i1.GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); i1.GeneratedColumn _column_37(String aliasedName) => - i1.GeneratedColumn('city', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'city', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_38(String aliasedName) => - i1.GeneratedColumn('state', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'state', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_39(String aliasedName) => - i1.GeneratedColumn('country', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'country', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_40(String aliasedName) => - i1.GeneratedColumn('date_time_original', aliasedName, true, type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: i1.DriftSqlType.dateTime, + ); i1.GeneratedColumn _column_41(String aliasedName) => - i1.GeneratedColumn('description', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'description', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_42(String aliasedName) => - i1.GeneratedColumn('exposure_time', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_43(String aliasedName) => - i1.GeneratedColumn('f_number', aliasedName, true, type: i1.DriftSqlType.double); + i1.GeneratedColumn( + 'f_number', + aliasedName, + true, + type: i1.DriftSqlType.double, + ); i1.GeneratedColumn _column_44(String aliasedName) => - i1.GeneratedColumn('file_size', aliasedName, true, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'file_size', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_45(String aliasedName) => - i1.GeneratedColumn('focal_length', aliasedName, true, type: i1.DriftSqlType.double); + i1.GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: i1.DriftSqlType.double, + ); i1.GeneratedColumn _column_46(String aliasedName) => - i1.GeneratedColumn('latitude', aliasedName, true, type: i1.DriftSqlType.double); + i1.GeneratedColumn( + 'latitude', + aliasedName, + true, + type: i1.DriftSqlType.double, + ); i1.GeneratedColumn _column_47(String aliasedName) => - i1.GeneratedColumn('longitude', aliasedName, true, type: i1.DriftSqlType.double); + i1.GeneratedColumn( + 'longitude', + aliasedName, + true, + type: i1.DriftSqlType.double, + ); i1.GeneratedColumn _column_48(String aliasedName) => - i1.GeneratedColumn('iso', aliasedName, true, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'iso', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_49(String aliasedName) => - i1.GeneratedColumn('make', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'make', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_50(String aliasedName) => - i1.GeneratedColumn('model', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'model', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_51(String aliasedName) => - i1.GeneratedColumn('lens', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'lens', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_52(String aliasedName) => - i1.GeneratedColumn('orientation', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'orientation', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_53(String aliasedName) => - i1.GeneratedColumn('time_zone', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_54(String aliasedName) => - i1.GeneratedColumn('rating', aliasedName, true, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'rating', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_55(String aliasedName) => - i1.GeneratedColumn('projection_type', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); class Shape9 extends i0.VersionedTable { Shape9({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get id => columnsByName['id']! as i1.GeneratedColumn; - i1.GeneratedColumn get name => columnsByName['name']! as i1.GeneratedColumn; - i1.GeneratedColumn get description => columnsByName['description']! as i1.GeneratedColumn; - i1.GeneratedColumn get createdAt => columnsByName['created_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get updatedAt => columnsByName['updated_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get ownerId => columnsByName['owner_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get thumbnailAssetId => columnsByName['thumbnail_asset_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get isActivityEnabled => columnsByName['is_activity_enabled']! as i1.GeneratedColumn; - i1.GeneratedColumn get order => columnsByName['order']! as i1.GeneratedColumn; + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get description => + columnsByName['description']! as i1.GeneratedColumn; + i1.GeneratedColumn get createdAt => + columnsByName['created_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get ownerId => + columnsByName['owner_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get thumbnailAssetId => + columnsByName['thumbnail_asset_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get isActivityEnabled => + columnsByName['is_activity_enabled']! as i1.GeneratedColumn; + i1.GeneratedColumn get order => + columnsByName['order']! as i1.GeneratedColumn; } i1.GeneratedColumn _column_56(String aliasedName) => - i1.GeneratedColumn('description', aliasedName, false, - type: i1.DriftSqlType.string, defaultValue: const CustomExpression('\'\'')); + i1.GeneratedColumn( + 'description', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultValue: const CustomExpression('\'\''), + ); i1.GeneratedColumn _column_57(String aliasedName) => - i1.GeneratedColumn('thumbnail_asset_id', aliasedName, true, - type: i1.DriftSqlType.string, - defaultConstraints: - i1.GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE SET NULL')); + i1.GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); i1.GeneratedColumn _column_58(String aliasedName) => - i1.GeneratedColumn('is_activity_enabled', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('CHECK ("is_activity_enabled" IN (0, 1))'), - defaultValue: const CustomExpression('1')); + i1.GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); i1.GeneratedColumn _column_59(String aliasedName) => - i1.GeneratedColumn('order', aliasedName, false, type: i1.DriftSqlType.int); -i1.GeneratedColumn _column_60(String aliasedName) => i1.GeneratedColumn('album_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + i1.GeneratedColumn( + 'order', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); +i1.GeneratedColumn _column_60(String aliasedName) => + i1.GeneratedColumn( + 'album_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); class Shape10 extends i0.VersionedTable { Shape10({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get albumId => columnsByName['album_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get userId => columnsByName['user_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get role => columnsByName['role']! as i1.GeneratedColumn; + i1.GeneratedColumn get albumId => + columnsByName['album_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get userId => + columnsByName['user_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get role => + columnsByName['role']! as i1.GeneratedColumn; } i1.GeneratedColumn _column_61(String aliasedName) => - i1.GeneratedColumn('role', aliasedName, false, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'role', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); class Shape11 extends i0.VersionedTable { Shape11({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get id => columnsByName['id']! as i1.GeneratedColumn; - i1.GeneratedColumn get createdAt => columnsByName['created_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get updatedAt => columnsByName['updated_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get deletedAt => columnsByName['deleted_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get ownerId => columnsByName['owner_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get type => columnsByName['type']! as i1.GeneratedColumn; - i1.GeneratedColumn get data => columnsByName['data']! as i1.GeneratedColumn; - i1.GeneratedColumn get isSaved => columnsByName['is_saved']! as i1.GeneratedColumn; - i1.GeneratedColumn get memoryAt => columnsByName['memory_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get seenAt => columnsByName['seen_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get showAt => columnsByName['show_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get hideAt => columnsByName['hide_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get createdAt => + columnsByName['created_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get deletedAt => + columnsByName['deleted_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get ownerId => + columnsByName['owner_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get type => + columnsByName['type']! as i1.GeneratedColumn; + i1.GeneratedColumn get data => + columnsByName['data']! as i1.GeneratedColumn; + i1.GeneratedColumn get isSaved => + columnsByName['is_saved']! as i1.GeneratedColumn; + i1.GeneratedColumn get memoryAt => + columnsByName['memory_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get seenAt => + columnsByName['seen_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get showAt => + columnsByName['show_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get hideAt => + columnsByName['hide_at']! as i1.GeneratedColumn; } i1.GeneratedColumn _column_62(String aliasedName) => - i1.GeneratedColumn('data', aliasedName, false, type: i1.DriftSqlType.string); -i1.GeneratedColumn _column_63(String aliasedName) => i1.GeneratedColumn('is_saved', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('CHECK ("is_saved" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + i1.GeneratedColumn( + 'data', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); +i1.GeneratedColumn _column_63(String aliasedName) => + i1.GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); i1.GeneratedColumn _column_64(String aliasedName) => - i1.GeneratedColumn('memory_at', aliasedName, false, type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: i1.DriftSqlType.dateTime, + ); i1.GeneratedColumn _column_65(String aliasedName) => - i1.GeneratedColumn('seen_at', aliasedName, true, type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: i1.DriftSqlType.dateTime, + ); i1.GeneratedColumn _column_66(String aliasedName) => - i1.GeneratedColumn('show_at', aliasedName, true, type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'show_at', + aliasedName, + true, + type: i1.DriftSqlType.dateTime, + ); i1.GeneratedColumn _column_67(String aliasedName) => - i1.GeneratedColumn('hide_at', aliasedName, true, type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: i1.DriftSqlType.dateTime, + ); class Shape12 extends i0.VersionedTable { Shape12({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get assetId => columnsByName['asset_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get memoryId => columnsByName['memory_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get assetId => + columnsByName['asset_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get memoryId => + columnsByName['memory_id']! as i1.GeneratedColumn; } -i1.GeneratedColumn _column_68(String aliasedName) => i1.GeneratedColumn('memory_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('REFERENCES memory_entity (id) ON DELETE CASCADE')); +i1.GeneratedColumn _column_68(String aliasedName) => + i1.GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); class Shape13 extends i0.VersionedTable { Shape13({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get id => columnsByName['id']! as i1.GeneratedColumn; - i1.GeneratedColumn get createdAt => columnsByName['created_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get updatedAt => columnsByName['updated_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get ownerId => columnsByName['owner_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get name => columnsByName['name']! as i1.GeneratedColumn; - i1.GeneratedColumn get faceAssetId => columnsByName['face_asset_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get thumbnailPath => columnsByName['thumbnail_path']! as i1.GeneratedColumn; - i1.GeneratedColumn get isFavorite => columnsByName['is_favorite']! as i1.GeneratedColumn; - i1.GeneratedColumn get isHidden => columnsByName['is_hidden']! as i1.GeneratedColumn; - i1.GeneratedColumn get color => columnsByName['color']! as i1.GeneratedColumn; - i1.GeneratedColumn get birthDate => columnsByName['birth_date']! as i1.GeneratedColumn; + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get createdAt => + columnsByName['created_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get ownerId => + columnsByName['owner_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get faceAssetId => + columnsByName['face_asset_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get thumbnailPath => + columnsByName['thumbnail_path']! as i1.GeneratedColumn; + i1.GeneratedColumn get isFavorite => + columnsByName['is_favorite']! as i1.GeneratedColumn; + i1.GeneratedColumn get isHidden => + columnsByName['is_hidden']! as i1.GeneratedColumn; + i1.GeneratedColumn get color => + columnsByName['color']! as i1.GeneratedColumn; + i1.GeneratedColumn get birthDate => + columnsByName['birth_date']! as i1.GeneratedColumn; } i1.GeneratedColumn _column_69(String aliasedName) => - i1.GeneratedColumn('face_asset_id', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_70(String aliasedName) => - i1.GeneratedColumn('thumbnail_path', aliasedName, false, type: i1.DriftSqlType.string); -i1.GeneratedColumn _column_71(String aliasedName) => i1.GeneratedColumn('is_favorite', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))')); -i1.GeneratedColumn _column_72(String aliasedName) => i1.GeneratedColumn('is_hidden', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('CHECK ("is_hidden" IN (0, 1))')); + i1.GeneratedColumn( + 'thumbnail_path', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); +i1.GeneratedColumn _column_71(String aliasedName) => + i1.GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); +i1.GeneratedColumn _column_72(String aliasedName) => + i1.GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); i1.GeneratedColumn _column_73(String aliasedName) => - i1.GeneratedColumn('color', aliasedName, true, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'color', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_74(String aliasedName) => - i1.GeneratedColumn('birth_date', aliasedName, true, type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: i1.DriftSqlType.dateTime, + ); final class Schema3 extends i0.VersionedSchema { Schema3({required super.database}) : super(version: 3); @@ -744,326 +1229,295 @@ final class Schema3 extends i0.VersionedSchema { personEntity, ]; late final Shape0 userEntity = Shape0( - source: i0.VersionedTable( - entityName: 'user_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_1, - _column_2, - _column_3, - _column_4, - _column_5, - _column_6, - _column_7, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_4, + _column_5, + _column_6, + _column_7, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape1 remoteAssetEntity = Shape1( - source: i0.VersionedTable( - entityName: 'remote_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_1, - _column_8, - _column_9, - _column_5, - _column_10, - _column_11, - _column_12, - _column_0, - _column_13, - _column_14, - _column_15, - _column_16, - _column_17, - _column_18, - _column_19, - _column_20, - _column_21, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_13, + _column_14, + _column_15, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape2 localAssetEntity = Shape2( - source: i0.VersionedTable( - entityName: 'local_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_1, - _column_8, - _column_9, - _column_5, - _column_10, - _column_11, - _column_12, - _column_0, - _column_22, - _column_14, - _column_23, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'local_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_22, + _column_14, + _column_23, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape3 stackEntity = Shape3( - source: i0.VersionedTable( - entityName: 'stack_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_9, - _column_5, - _column_15, - _column_75, - ], - attachedDatabase: database, - ), - alias: null); - final i1.Index idxLocalAssetChecksum = - i1.Index('idx_local_asset_checksum', 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); - final i1.Index uQRemoteAssetOwnerChecksum = i1.Index('UQ_remote_asset_owner_checksum', - 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); - final i1.Index idxRemoteAssetChecksum = - i1.Index('idx_remote_asset_checksum', 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_0, _column_9, _column_5, _column_15, _column_75], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLocalAssetChecksum = i1.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + final i1.Index uQRemoteAssetOwnerChecksum = i1.Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + final i1.Index idxRemoteAssetChecksum = i1.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); late final Shape4 userMetadataEntity = Shape4( - source: i0.VersionedTable( - entityName: 'user_metadata_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(user_id, "key")', - ], - columns: [ - _column_25, - _column_26, - _column_27, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'user_metadata_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(user_id, "key")'], + columns: [_column_25, _column_26, _column_27], + attachedDatabase: database, + ), + alias: null, + ); late final Shape5 partnerEntity = Shape5( - source: i0.VersionedTable( - entityName: 'partner_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(shared_by_id, shared_with_id)', - ], - columns: [ - _column_28, - _column_29, - _column_30, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'partner_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(shared_by_id, shared_with_id)'], + columns: [_column_28, _column_29, _column_30], + attachedDatabase: database, + ), + alias: null, + ); late final Shape6 localAlbumEntity = Shape6( - source: i0.VersionedTable( - entityName: 'local_album_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_1, - _column_5, - _column_31, - _column_32, - _column_33, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'local_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_5, + _column_31, + _column_32, + _column_33, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape7 localAlbumAssetEntity = Shape7( - source: i0.VersionedTable( - entityName: 'local_album_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id, album_id)', - ], - columns: [ - _column_34, - _column_35, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_34, _column_35], + attachedDatabase: database, + ), + alias: null, + ); late final Shape8 remoteExifEntity = Shape8( - source: i0.VersionedTable( - entityName: 'remote_exif_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id)', - ], - columns: [ - _column_36, - _column_37, - _column_38, - _column_39, - _column_40, - _column_41, - _column_11, - _column_10, - _column_42, - _column_43, - _column_44, - _column_45, - _column_46, - _column_47, - _column_48, - _column_49, - _column_50, - _column_51, - _column_52, - _column_53, - _column_54, - _column_55, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_exif_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id)'], + columns: [ + _column_36, + _column_37, + _column_38, + _column_39, + _column_40, + _column_41, + _column_11, + _column_10, + _column_42, + _column_43, + _column_44, + _column_45, + _column_46, + _column_47, + _column_48, + _column_49, + _column_50, + _column_51, + _column_52, + _column_53, + _column_54, + _column_55, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape9 remoteAlbumEntity = Shape9( - source: i0.VersionedTable( - entityName: 'remote_album_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_1, - _column_56, - _column_9, - _column_5, - _column_15, - _column_57, - _column_58, - _column_59, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_56, + _column_9, + _column_5, + _column_15, + _column_57, + _column_58, + _column_59, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape7 remoteAlbumAssetEntity = Shape7( - source: i0.VersionedTable( - entityName: 'remote_album_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id, album_id)', - ], - columns: [ - _column_36, - _column_60, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_36, _column_60], + attachedDatabase: database, + ), + alias: null, + ); late final Shape10 remoteAlbumUserEntity = Shape10( - source: i0.VersionedTable( - entityName: 'remote_album_user_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(album_id, user_id)', - ], - columns: [ - _column_60, - _column_25, - _column_61, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_album_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(album_id, user_id)'], + columns: [_column_60, _column_25, _column_61], + attachedDatabase: database, + ), + alias: null, + ); late final Shape11 memoryEntity = Shape11( - source: i0.VersionedTable( - entityName: 'memory_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_9, - _column_5, - _column_18, - _column_15, - _column_8, - _column_62, - _column_63, - _column_64, - _column_65, - _column_66, - _column_67, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'memory_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_18, + _column_15, + _column_8, + _column_62, + _column_63, + _column_64, + _column_65, + _column_66, + _column_67, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape12 memoryAssetEntity = Shape12( - source: i0.VersionedTable( - entityName: 'memory_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id, memory_id)', - ], - columns: [ - _column_36, - _column_68, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'memory_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, memory_id)'], + columns: [_column_36, _column_68], + attachedDatabase: database, + ), + alias: null, + ); late final Shape13 personEntity = Shape13( - source: i0.VersionedTable( - entityName: 'person_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_9, - _column_5, - _column_15, - _column_1, - _column_69, - _column_70, - _column_71, - _column_72, - _column_73, - _column_74, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'person_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_1, + _column_69, + _column_70, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null, + ); } i1.GeneratedColumn _column_75(String aliasedName) => - i1.GeneratedColumn('primary_asset_id', aliasedName, false, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); final class Schema4 extends i0.VersionedSchema { Schema4({required super.database}) : super(version: 4); @@ -1090,391 +1544,416 @@ final class Schema4 extends i0.VersionedSchema { assetFaceEntity, ]; late final Shape0 userEntity = Shape0( - source: i0.VersionedTable( - entityName: 'user_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_1, - _column_2, - _column_3, - _column_4, - _column_5, - _column_6, - _column_7, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_4, + _column_5, + _column_6, + _column_7, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape1 remoteAssetEntity = Shape1( - source: i0.VersionedTable( - entityName: 'remote_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_1, - _column_8, - _column_9, - _column_5, - _column_10, - _column_11, - _column_12, - _column_0, - _column_13, - _column_14, - _column_15, - _column_16, - _column_17, - _column_18, - _column_19, - _column_20, - _column_21, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_13, + _column_14, + _column_15, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape3 stackEntity = Shape3( - source: i0.VersionedTable( - entityName: 'stack_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_9, - _column_5, - _column_15, - _column_75, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_0, _column_9, _column_5, _column_15, _column_75], + attachedDatabase: database, + ), + alias: null, + ); late final Shape2 localAssetEntity = Shape2( - source: i0.VersionedTable( - entityName: 'local_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_1, - _column_8, - _column_9, - _column_5, - _column_10, - _column_11, - _column_12, - _column_0, - _column_22, - _column_14, - _column_23, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'local_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_22, + _column_14, + _column_23, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape6 localAlbumEntity = Shape6( - source: i0.VersionedTable( - entityName: 'local_album_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_1, - _column_5, - _column_31, - _column_32, - _column_33, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'local_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_5, + _column_31, + _column_32, + _column_33, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape7 localAlbumAssetEntity = Shape7( - source: i0.VersionedTable( - entityName: 'local_album_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id, album_id)', - ], - columns: [ - _column_34, - _column_35, - ], - attachedDatabase: database, - ), - alias: null); - final i1.Index idxLocalAssetChecksum = - i1.Index('idx_local_asset_checksum', 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); - final i1.Index uQRemoteAssetOwnerChecksum = i1.Index('UQ_remote_asset_owner_checksum', - 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); - final i1.Index idxRemoteAssetChecksum = - i1.Index('idx_remote_asset_checksum', 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_34, _column_35], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLocalAssetChecksum = i1.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + final i1.Index uQRemoteAssetOwnerChecksum = i1.Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + final i1.Index idxRemoteAssetChecksum = i1.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); late final Shape4 userMetadataEntity = Shape4( - source: i0.VersionedTable( - entityName: 'user_metadata_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(user_id, "key")', - ], - columns: [ - _column_25, - _column_26, - _column_27, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'user_metadata_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(user_id, "key")'], + columns: [_column_25, _column_26, _column_27], + attachedDatabase: database, + ), + alias: null, + ); late final Shape5 partnerEntity = Shape5( - source: i0.VersionedTable( - entityName: 'partner_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(shared_by_id, shared_with_id)', - ], - columns: [ - _column_28, - _column_29, - _column_30, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'partner_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(shared_by_id, shared_with_id)'], + columns: [_column_28, _column_29, _column_30], + attachedDatabase: database, + ), + alias: null, + ); late final Shape8 remoteExifEntity = Shape8( - source: i0.VersionedTable( - entityName: 'remote_exif_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id)', - ], - columns: [ - _column_36, - _column_37, - _column_38, - _column_39, - _column_40, - _column_41, - _column_11, - _column_10, - _column_42, - _column_43, - _column_44, - _column_45, - _column_46, - _column_47, - _column_48, - _column_49, - _column_50, - _column_51, - _column_52, - _column_53, - _column_54, - _column_55, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_exif_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id)'], + columns: [ + _column_36, + _column_37, + _column_38, + _column_39, + _column_40, + _column_41, + _column_11, + _column_10, + _column_42, + _column_43, + _column_44, + _column_45, + _column_46, + _column_47, + _column_48, + _column_49, + _column_50, + _column_51, + _column_52, + _column_53, + _column_54, + _column_55, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape9 remoteAlbumEntity = Shape9( - source: i0.VersionedTable( - entityName: 'remote_album_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_1, - _column_56, - _column_9, - _column_5, - _column_15, - _column_57, - _column_58, - _column_59, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_56, + _column_9, + _column_5, + _column_15, + _column_57, + _column_58, + _column_59, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape7 remoteAlbumAssetEntity = Shape7( - source: i0.VersionedTable( - entityName: 'remote_album_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id, album_id)', - ], - columns: [ - _column_36, - _column_60, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_36, _column_60], + attachedDatabase: database, + ), + alias: null, + ); late final Shape10 remoteAlbumUserEntity = Shape10( - source: i0.VersionedTable( - entityName: 'remote_album_user_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(album_id, user_id)', - ], - columns: [ - _column_60, - _column_25, - _column_61, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_album_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(album_id, user_id)'], + columns: [_column_60, _column_25, _column_61], + attachedDatabase: database, + ), + alias: null, + ); late final Shape11 memoryEntity = Shape11( - source: i0.VersionedTable( - entityName: 'memory_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_9, - _column_5, - _column_18, - _column_15, - _column_8, - _column_62, - _column_63, - _column_64, - _column_65, - _column_66, - _column_67, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'memory_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_18, + _column_15, + _column_8, + _column_62, + _column_63, + _column_64, + _column_65, + _column_66, + _column_67, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape12 memoryAssetEntity = Shape12( - source: i0.VersionedTable( - entityName: 'memory_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id, memory_id)', - ], - columns: [ - _column_36, - _column_68, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'memory_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, memory_id)'], + columns: [_column_36, _column_68], + attachedDatabase: database, + ), + alias: null, + ); late final Shape14 personEntity = Shape14( - source: i0.VersionedTable( - entityName: 'person_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_9, - _column_5, - _column_15, - _column_1, - _column_69, - _column_71, - _column_72, - _column_73, - _column_74, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'person_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_1, + _column_69, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape15 assetFaceEntity = Shape15( - source: i0.VersionedTable( - entityName: 'asset_face_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_36, - _column_76, - _column_77, - _column_78, - _column_79, - _column_80, - _column_81, - _column_82, - _column_83, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'asset_face_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_36, + _column_76, + _column_77, + _column_78, + _column_79, + _column_80, + _column_81, + _column_82, + _column_83, + ], + attachedDatabase: database, + ), + alias: null, + ); } class Shape14 extends i0.VersionedTable { Shape14({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get id => columnsByName['id']! as i1.GeneratedColumn; - i1.GeneratedColumn get createdAt => columnsByName['created_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get updatedAt => columnsByName['updated_at']! as i1.GeneratedColumn; - i1.GeneratedColumn get ownerId => columnsByName['owner_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get name => columnsByName['name']! as i1.GeneratedColumn; - i1.GeneratedColumn get faceAssetId => columnsByName['face_asset_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get isFavorite => columnsByName['is_favorite']! as i1.GeneratedColumn; - i1.GeneratedColumn get isHidden => columnsByName['is_hidden']! as i1.GeneratedColumn; - i1.GeneratedColumn get color => columnsByName['color']! as i1.GeneratedColumn; - i1.GeneratedColumn get birthDate => columnsByName['birth_date']! as i1.GeneratedColumn; + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get createdAt => + columnsByName['created_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get ownerId => + columnsByName['owner_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get faceAssetId => + columnsByName['face_asset_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get isFavorite => + columnsByName['is_favorite']! as i1.GeneratedColumn; + i1.GeneratedColumn get isHidden => + columnsByName['is_hidden']! as i1.GeneratedColumn; + i1.GeneratedColumn get color => + columnsByName['color']! as i1.GeneratedColumn; + i1.GeneratedColumn get birthDate => + columnsByName['birth_date']! as i1.GeneratedColumn; } class Shape15 extends i0.VersionedTable { Shape15({required super.source, required super.alias}) : super.aliased(); - i1.GeneratedColumn get id => columnsByName['id']! as i1.GeneratedColumn; - i1.GeneratedColumn get assetId => columnsByName['asset_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get personId => columnsByName['person_id']! as i1.GeneratedColumn; - i1.GeneratedColumn get imageWidth => columnsByName['image_width']! as i1.GeneratedColumn; - i1.GeneratedColumn get imageHeight => columnsByName['image_height']! as i1.GeneratedColumn; - i1.GeneratedColumn get boundingBoxX1 => columnsByName['bounding_box_x1']! as i1.GeneratedColumn; - i1.GeneratedColumn get boundingBoxY1 => columnsByName['bounding_box_y1']! as i1.GeneratedColumn; - i1.GeneratedColumn get boundingBoxX2 => columnsByName['bounding_box_x2']! as i1.GeneratedColumn; - i1.GeneratedColumn get boundingBoxY2 => columnsByName['bounding_box_y2']! as i1.GeneratedColumn; - i1.GeneratedColumn get sourceType => columnsByName['source_type']! as i1.GeneratedColumn; + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get assetId => + columnsByName['asset_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get personId => + columnsByName['person_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get imageWidth => + columnsByName['image_width']! as i1.GeneratedColumn; + i1.GeneratedColumn get imageHeight => + columnsByName['image_height']! as i1.GeneratedColumn; + i1.GeneratedColumn get boundingBoxX1 => + columnsByName['bounding_box_x1']! as i1.GeneratedColumn; + i1.GeneratedColumn get boundingBoxY1 => + columnsByName['bounding_box_y1']! as i1.GeneratedColumn; + i1.GeneratedColumn get boundingBoxX2 => + columnsByName['bounding_box_x2']! as i1.GeneratedColumn; + i1.GeneratedColumn get boundingBoxY2 => + columnsByName['bounding_box_y2']! as i1.GeneratedColumn; + i1.GeneratedColumn get sourceType => + columnsByName['source_type']! as i1.GeneratedColumn; } -i1.GeneratedColumn _column_76(String aliasedName) => i1.GeneratedColumn('person_id', aliasedName, true, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways('REFERENCES person_entity (id) ON DELETE SET NULL')); +i1.GeneratedColumn _column_76(String aliasedName) => + i1.GeneratedColumn( + 'person_id', + aliasedName, + true, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL', + ), + ); i1.GeneratedColumn _column_77(String aliasedName) => - i1.GeneratedColumn('image_width', aliasedName, false, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'image_width', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_78(String aliasedName) => - i1.GeneratedColumn('image_height', aliasedName, false, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'image_height', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_79(String aliasedName) => - i1.GeneratedColumn('bounding_box_x1', aliasedName, false, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'bounding_box_x1', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_80(String aliasedName) => - i1.GeneratedColumn('bounding_box_y1', aliasedName, false, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'bounding_box_y1', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_81(String aliasedName) => - i1.GeneratedColumn('bounding_box_x2', aliasedName, false, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'bounding_box_x2', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_82(String aliasedName) => - i1.GeneratedColumn('bounding_box_y2', aliasedName, false, type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'bounding_box_y2', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_83(String aliasedName) => - i1.GeneratedColumn('source_type', aliasedName, false, type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'source_type', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); i0.MigrationStepWithVersion migrationSteps({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, required Future Function(i1.Migrator m, Schema3 schema) from2To3, @@ -1507,10 +1986,10 @@ i1.OnUpgrade stepByStep({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, required Future Function(i1.Migrator m, Schema3 schema) from2To3, required Future Function(i1.Migrator m, Schema4 schema) from3To4, -}) => - i0.VersionedSchema.stepByStepHelper( - step: migrationSteps( - from1To2: from1To2, - from2To3: from2To3, - from3To4: from3To4, - )); +}) => i0.VersionedSchema.stepByStepHelper( + step: migrationSteps( + from1To2: from1To2, + from2To3: from2To3, + from3To4: from3To4, + ), +); diff --git a/mobile/lib/infrastructure/repositories/exif.repository.dart b/mobile/lib/infrastructure/repositories/exif.repository.dart index 726e51b77d..0ede30680e 100644 --- a/mobile/lib/infrastructure/repositories/exif.repository.dart +++ b/mobile/lib/infrastructure/repositories/exif.repository.dart @@ -33,9 +33,7 @@ class IsarExifRepository extends IsarDatabaseRepository { Future> updateAll(List exifInfos) { return transaction(() async { - await _db.exifInfos.putAll( - exifInfos.map(entity.ExifInfo.fromDto).toList(), - ); + await _db.exifInfos.putAll(exifInfos.map(entity.ExifInfo.fromDto).toList()); return exifInfos; }); } diff --git a/mobile/lib/infrastructure/repositories/local_album.repository.dart b/mobile/lib/infrastructure/repositories/local_album.repository.dart index c1e5724b4c..feb25925f8 100644 --- a/mobile/lib/infrastructure/repositories/local_album.repository.dart +++ b/mobile/lib/infrastructure/repositories/local_album.repository.dart @@ -14,8 +14,8 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { final Drift _db; final Platform _platform; const DriftLocalAlbumRepository(this._db, {Platform? platform}) - : _platform = platform ?? const LocalPlatform(), - super(_db); + : _platform = platform ?? const LocalPlatform(), + super(_db); Future> getAll({Set sortBy = const {}}) { final assetCount = _db.localAlbumAssetEntity.assetId.count(); @@ -34,41 +34,32 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { if (sortBy.isNotEmpty) { final orderings = []; for (final sort in sortBy) { - orderings.add( - switch (sort) { - SortLocalAlbumsBy.id => OrderingTerm.asc(_db.localAlbumEntity.id), - SortLocalAlbumsBy.backupSelection => OrderingTerm.asc(_db.localAlbumEntity.backupSelection), - SortLocalAlbumsBy.isIosSharedAlbum => OrderingTerm.asc(_db.localAlbumEntity.isIosSharedAlbum), - SortLocalAlbumsBy.name => OrderingTerm.asc(_db.localAlbumEntity.name), - SortLocalAlbumsBy.assetCount => OrderingTerm.desc(assetCount), - }, - ); + orderings.add(switch (sort) { + SortLocalAlbumsBy.id => OrderingTerm.asc(_db.localAlbumEntity.id), + SortLocalAlbumsBy.backupSelection => OrderingTerm.asc(_db.localAlbumEntity.backupSelection), + SortLocalAlbumsBy.isIosSharedAlbum => OrderingTerm.asc(_db.localAlbumEntity.isIosSharedAlbum), + SortLocalAlbumsBy.name => OrderingTerm.asc(_db.localAlbumEntity.name), + SortLocalAlbumsBy.assetCount => OrderingTerm.desc(assetCount), + }); } query.orderBy(orderings); } - return query - .map( - (row) => row.readTable(_db.localAlbumEntity).toDto(assetCount: row.read(assetCount) ?? 0), - ) - .get(); + return query.map((row) => row.readTable(_db.localAlbumEntity).toDto(assetCount: row.read(assetCount) ?? 0)).get(); } Future delete(String albumId) => transaction(() async { - // Remove all assets that are only in this particular album - // We cannot remove all assets in the album because they might be in other albums in iOS - // That is not the case on Android since asset <-> album has one:one mapping - final assetsToDelete = _platform.isIOS ? await _getUniqueAssetsInAlbum(albumId) : await getAssetIds(albumId); - await _deleteAssets(assetsToDelete); + // Remove all assets that are only in this particular album + // We cannot remove all assets in the album because they might be in other albums in iOS + // That is not the case on Android since asset <-> album has one:one mapping + final assetsToDelete = _platform.isIOS ? await _getUniqueAssetsInAlbum(albumId) : await getAssetIds(albumId); + await _deleteAssets(assetsToDelete); - // All the other assets that are still associated will be unlinked automatically on-cascade - await _db.managers.localAlbumEntity.filter((a) => a.id.equals(albumId)).delete(); - }); + // All the other assets that are still associated will be unlinked automatically on-cascade + await _db.managers.localAlbumEntity.filter((a) => a.id.equals(albumId)).delete(); + }); - Future syncDeletes( - String albumId, - Iterable assetIdsToKeep, - ) async { + Future syncDeletes(String albumId, Iterable assetIdsToKeep) async { if (assetIdsToKeep.isEmpty) { return Future.value(); } @@ -77,12 +68,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { deleteSmt.where((localAsset) { final subQuery = _db.localAlbumAssetEntity.selectOnly() ..addColumns([_db.localAlbumAssetEntity.assetId]) - ..join([ - innerJoin( - _db.localAlbumEntity, - _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id), - ), - ]); + ..join([innerJoin(_db.localAlbumEntity, _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id))]); subQuery.where( _db.localAlbumEntity.id.equals(albumId) & _db.localAlbumAssetEntity.assetId.isNotIn(assetIdsToKeep), ); @@ -109,12 +95,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { if (toUpsert.isNotEmpty) { await _upsertAssets(toUpsert); await _db.localAlbumAssetEntity.insertAll( - toUpsert.map( - (a) => LocalAlbumAssetEntityCompanion.insert( - assetId: a.id, - albumId: localAlbum.id, - ), - ), + toUpsert.map((a) => LocalAlbumAssetEntityCompanion.insert(assetId: a.id, albumId: localAlbum.id)), mode: InsertMode.insertOrIgnore, ); } @@ -162,10 +143,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { final subQuery = _db.localAlbumAssetEntity.selectOnly() ..addColumns([_db.localAlbumAssetEntity.assetId]) ..join([ - innerJoin( - _db.localAlbumEntity, - _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id), - ), + innerJoin(_db.localAlbumEntity, _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id)), ]); subQuery.where(_db.localAlbumEntity.marker_.isNotNull()); return localAsset.id.isInQuery(subQuery); @@ -178,16 +156,12 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { } Future> getAssets(String albumId) { - final query = _db.localAlbumAssetEntity.select().join( - [ - innerJoin( - _db.localAssetEntity, - _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), - ), - ], - ) - ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) - ..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]); + final query = + _db.localAlbumAssetEntity.select().join([ + innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)), + ]) + ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) + ..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]); return query.map((row) => row.readTable(_db.localAssetEntity).toDto()).get(); } @@ -224,11 +198,8 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { batch.insertAll( _db.localAlbumAssetEntity, albumIds.cast().nonNulls.map( - (albumId) => LocalAlbumAssetEntityCompanion.insert( - assetId: assetId, - albumId: albumId, - ), - ), + (albumId) => LocalAlbumAssetEntityCompanion.insert(assetId: assetId, albumId: albumId), + ), onConflict: DoNothing(), ); }); @@ -237,18 +208,12 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { } Future> getAssetsToHash(String albumId) { - final query = _db.localAlbumAssetEntity.select().join( - [ - innerJoin( - _db.localAssetEntity, - _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), - ), - ], - ) - ..where( - _db.localAlbumAssetEntity.albumId.equals(albumId) & _db.localAssetEntity.checksum.isNull(), - ) - ..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]); + final query = + _db.localAlbumAssetEntity.select().join([ + innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)), + ]) + ..where(_db.localAlbumAssetEntity.albumId.equals(albumId) & _db.localAssetEntity.checksum.isNull()) + ..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]); return query.map((row) => row.readTable(_db.localAssetEntity).toDto()).get(); } @@ -275,10 +240,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { batch.insert<$LocalAssetEntityTable, LocalAssetEntityData>( _db.localAssetEntity, companion, - onConflict: DoUpdate( - (_) => companion, - where: (old) => old.updatedAt.isNotValue(asset.updatedAt), - ), + onConflict: DoUpdate((_) => companion, where: (old) => old.updatedAt.isNotValue(asset.updatedAt)), ); } }); @@ -351,15 +313,13 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { } Future getThumbnail(String albumId) async { - final query = _db.localAlbumAssetEntity.select().join([ - innerJoin( - _db.localAssetEntity, - _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), - ), - ]) - ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) - ..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]) - ..limit(1); + final query = + _db.localAlbumAssetEntity.select().join([ + innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)), + ]) + ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) + ..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]) + ..limit(1); final results = await query.map((row) => row.readTable(_db.localAssetEntity).toDto()).get(); diff --git a/mobile/lib/infrastructure/repositories/local_asset.repository.dart b/mobile/lib/infrastructure/repositories/local_asset.repository.dart index 17521e1cba..58adac30db 100644 --- a/mobile/lib/infrastructure/repositories/local_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/local_asset.repository.dart @@ -16,14 +16,11 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository { _db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum), useColumns: false, ), - ]) - ..where(_db.localAssetEntity.id.equals(id)); + ])..where(_db.localAssetEntity.id.equals(id)); return query.map((row) { final asset = row.readTable(_db.localAssetEntity).toDto(); - return asset.copyWith( - remoteId: row.read(_db.remoteAssetEntity.id), - ); + return asset.copyWith(remoteId: row.read(_db.remoteAssetEntity.id)); }).watchSingleOrNull(); } diff --git a/mobile/lib/infrastructure/repositories/memory.repository.dart b/mobile/lib/infrastructure/repositories/memory.repository.dart index 3663b75bf3..2a52faf2dd 100644 --- a/mobile/lib/infrastructure/repositories/memory.repository.dart +++ b/mobile/lib/infrastructure/repositories/memory.repository.dart @@ -13,30 +13,21 @@ class DriftMemoryRepository extends DriftDatabaseRepository { final now = DateTime.now(); final localUtc = DateTime.utc(now.year, now.month, now.day, 0, 0, 0); - final query = _db.select(_db.memoryEntity).join([ - leftOuterJoin( - _db.memoryAssetEntity, - _db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id), - ), - leftOuterJoin( - _db.remoteAssetEntity, - _db.remoteAssetEntity.id.equalsExp(_db.memoryAssetEntity.assetId) & - _db.remoteAssetEntity.deletedAt.isNull() & - _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline), - ), - ]) - ..where(_db.memoryEntity.ownerId.equals(ownerId)) - ..where(_db.memoryEntity.deletedAt.isNull()) - ..where( - _db.memoryEntity.showAt.isSmallerOrEqualValue(localUtc), - ) - ..where( - _db.memoryEntity.hideAt.isBiggerOrEqualValue(localUtc), - ) - ..orderBy([ - OrderingTerm.desc(_db.memoryEntity.memoryAt), - OrderingTerm.asc(_db.remoteAssetEntity.createdAt), - ]); + final query = + _db.select(_db.memoryEntity).join([ + leftOuterJoin(_db.memoryAssetEntity, _db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id)), + leftOuterJoin( + _db.remoteAssetEntity, + _db.remoteAssetEntity.id.equalsExp(_db.memoryAssetEntity.assetId) & + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline), + ), + ]) + ..where(_db.memoryEntity.ownerId.equals(ownerId)) + ..where(_db.memoryEntity.deletedAt.isNull()) + ..where(_db.memoryEntity.showAt.isSmallerOrEqualValue(localUtc)) + ..where(_db.memoryEntity.hideAt.isBiggerOrEqualValue(localUtc)) + ..orderBy([OrderingTerm.desc(_db.memoryEntity.memoryAt), OrderingTerm.asc(_db.remoteAssetEntity.createdAt)]); final rows = await query.get(); @@ -59,24 +50,19 @@ class DriftMemoryRepository extends DriftDatabaseRepository { } Future get(String memoryId) async { - final query = _db.select(_db.memoryEntity).join([ - leftOuterJoin( - _db.memoryAssetEntity, - _db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id), - ), - leftOuterJoin( - _db.remoteAssetEntity, - _db.remoteAssetEntity.id.equalsExp(_db.memoryAssetEntity.assetId) & - _db.remoteAssetEntity.deletedAt.isNull() & - _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline), - ), - ]) - ..where(_db.memoryEntity.id.equals(memoryId)) - ..where(_db.memoryEntity.deletedAt.isNull()) - ..orderBy([ - OrderingTerm.desc(_db.memoryEntity.memoryAt), - OrderingTerm.asc(_db.remoteAssetEntity.createdAt), - ]); + final query = + _db.select(_db.memoryEntity).join([ + leftOuterJoin(_db.memoryAssetEntity, _db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id)), + leftOuterJoin( + _db.remoteAssetEntity, + _db.remoteAssetEntity.id.equalsExp(_db.memoryAssetEntity.assetId) & + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline), + ), + ]) + ..where(_db.memoryEntity.id.equals(memoryId)) + ..where(_db.memoryEntity.deletedAt.isNull()) + ..orderBy([OrderingTerm.desc(_db.memoryEntity.memoryAt), OrderingTerm.asc(_db.remoteAssetEntity.createdAt)]); final rows = await query.get(); diff --git a/mobile/lib/infrastructure/repositories/partner.repository.dart b/mobile/lib/infrastructure/repositories/partner.repository.dart index 9e78b1b65a..b12061ad24 100644 --- a/mobile/lib/infrastructure/repositories/partner.repository.dart +++ b/mobile/lib/infrastructure/repositories/partner.repository.dart @@ -9,24 +9,13 @@ class DriftPartnerRepository extends DriftDatabaseRepository { Future> getPartners(String userId) { final query = _db.select(_db.partnerEntity).join([ - innerJoin( - _db.userEntity, - _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById), - ), - ]) - ..where( - _db.partnerEntity.sharedWithId.equals(userId), - ); + innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById)), + ])..where(_db.partnerEntity.sharedWithId.equals(userId)); return query.map((row) { final user = row.readTable(_db.userEntity); final partner = row.readTable(_db.partnerEntity); - return PartnerUserDto( - id: user.id, - email: user.email, - name: user.name, - inTimeline: partner.inTimeline, - ); + return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: partner.inTimeline); }).get(); } @@ -35,60 +24,33 @@ class DriftPartnerRepository extends DriftDatabaseRepository { final query = _db.select(_db.userEntity)..where((row) => row.id.equals(currentUserId).not()); return query.map((user) { - return PartnerUserDto( - id: user.id, - email: user.email, - name: user.name, - inTimeline: false, - ); + return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: false); }).get(); } // Get users who are sharing their photos WITH the current user Future> getSharedWith(String partnerId) { final query = _db.select(_db.partnerEntity).join([ - innerJoin( - _db.userEntity, - _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById), - ), - ]) - ..where( - _db.partnerEntity.sharedWithId.equals(partnerId), - ); + innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById)), + ])..where(_db.partnerEntity.sharedWithId.equals(partnerId)); return query.map((row) { final user = row.readTable(_db.userEntity); final partner = row.readTable(_db.partnerEntity); - return PartnerUserDto( - id: user.id, - email: user.email, - name: user.name, - inTimeline: partner.inTimeline, - ); + return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: partner.inTimeline); }).get(); } // Get users who the current user is sharing their photos TO Future> getSharedBy(String userId) { final query = _db.select(_db.partnerEntity).join([ - innerJoin( - _db.userEntity, - _db.userEntity.id.equalsExp(_db.partnerEntity.sharedWithId), - ), - ]) - ..where( - _db.partnerEntity.sharedById.equals(userId), - ); + innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedWithId)), + ])..where(_db.partnerEntity.sharedById.equals(userId)); return query.map((row) { final user = row.readTable(_db.userEntity); final partner = row.readTable(_db.partnerEntity); - return PartnerUserDto( - id: user.id, - email: user.email, - name: user.name, - inTimeline: partner.inTimeline, - ); + return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: partner.inTimeline); }).get(); } @@ -108,35 +70,24 @@ class DriftPartnerRepository extends DriftDatabaseRepository { Future getPartner(String partnerId, String userId) { final query = _db.select(_db.partnerEntity).join([ - innerJoin( - _db.userEntity, - _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById), - ), - ]) - ..where( - _db.partnerEntity.sharedById.equals(partnerId) & _db.partnerEntity.sharedWithId.equals(userId), - ); + innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById)), + ])..where(_db.partnerEntity.sharedById.equals(partnerId) & _db.partnerEntity.sharedWithId.equals(userId)); return query.map((row) { final user = row.readTable(_db.userEntity); final partner = row.readTable(_db.partnerEntity); - return PartnerUserDto( - id: user.id, - email: user.email, - name: user.name, - inTimeline: partner.inTimeline, - ); + return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: partner.inTimeline); }).getSingleOrNull(); } Future toggleShowInTimeline(PartnerUserDto partner, String userId) { return _db.partnerEntity.update().replace( - PartnerEntityCompanion( - sharedById: Value(partner.id), - sharedWithId: Value(userId), - inTimeline: Value(!partner.inTimeline), - ), - ); + PartnerEntityCompanion( + sharedById: Value(partner.id), + sharedWithId: Value(userId), + inTimeline: Value(!partner.inTimeline), + ), + ); } Future create(String partnerId, String userId) { @@ -150,8 +101,6 @@ class DriftPartnerRepository extends DriftDatabaseRepository { } Future delete(String partnerId, String userId) { - return _db.partnerEntity.deleteWhere( - (t) => t.sharedById.equals(userId) & t.sharedWithId.equals(partnerId), - ); + return _db.partnerEntity.deleteWhere((t) => t.sharedById.equals(userId) & t.sharedWithId.equals(partnerId)); } } diff --git a/mobile/lib/infrastructure/repositories/remote_album.repository.dart b/mobile/lib/infrastructure/repositories/remote_album.repository.dart index f60caceb47..79f3d78fd7 100644 --- a/mobile/lib/infrastructure/repositories/remote_album.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_album.repository.dart @@ -16,9 +16,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { final Drift _db; const DriftRemoteAlbumRepository(this._db) : super(_db); - Future> getAll({ - Set sortBy = const {SortRemoteAlbumsBy.updatedAt}, - }) { + Future> getAll({Set sortBy = const {SortRemoteAlbumsBy.updatedAt}}) { final assetCount = _db.remoteAlbumAssetEntity.assetId.count(); final query = _db.remoteAlbumEntity.select().join([ @@ -32,11 +30,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), useColumns: false, ), - leftOuterJoin( - _db.userEntity, - _db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId), - useColumns: false, - ), + leftOuterJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId), useColumns: false), ]); query ..where(_db.remoteAssetEntity.deletedAt.isNull()) @@ -47,22 +41,19 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { if (sortBy.isNotEmpty) { final orderings = []; for (final sort in sortBy) { - orderings.add( - switch (sort) { - SortRemoteAlbumsBy.id => OrderingTerm.asc(_db.remoteAlbumEntity.id), - SortRemoteAlbumsBy.updatedAt => OrderingTerm.desc(_db.remoteAlbumEntity.updatedAt), - }, - ); + orderings.add(switch (sort) { + SortRemoteAlbumsBy.id => OrderingTerm.asc(_db.remoteAlbumEntity.id), + SortRemoteAlbumsBy.updatedAt => OrderingTerm.desc(_db.remoteAlbumEntity.updatedAt), + }); } query.orderBy(orderings); } return query .map( - (row) => row.readTable(_db.remoteAlbumEntity).toDto( - assetCount: row.read(assetCount) ?? 0, - ownerName: row.read(_db.userEntity.name)!, - ), + (row) => row + .readTable(_db.remoteAlbumEntity) + .toDto(assetCount: row.read(assetCount) ?? 0, ownerName: row.read(_db.userEntity.name)!), ) .get(); } @@ -70,42 +61,39 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { Future get(String albumId) { final assetCount = _db.remoteAlbumAssetEntity.assetId.count(); - final query = _db.remoteAlbumEntity.select().join([ - leftOuterJoin( - _db.remoteAlbumAssetEntity, - _db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id), - useColumns: false, - ), - leftOuterJoin( - _db.remoteAssetEntity, - _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), - useColumns: false, - ), - leftOuterJoin( - _db.userEntity, - _db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId), - useColumns: false, - ), - ]) - ..where(_db.remoteAlbumEntity.id.equals(albumId) & _db.remoteAssetEntity.deletedAt.isNull()) - ..addColumns([assetCount]) - ..addColumns([_db.userEntity.name]) - ..groupBy([_db.remoteAlbumEntity.id]); + final query = + _db.remoteAlbumEntity.select().join([ + leftOuterJoin( + _db.remoteAlbumAssetEntity, + _db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id), + useColumns: false, + ), + leftOuterJoin( + _db.remoteAssetEntity, + _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), + useColumns: false, + ), + leftOuterJoin( + _db.userEntity, + _db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId), + useColumns: false, + ), + ]) + ..where(_db.remoteAlbumEntity.id.equals(albumId) & _db.remoteAssetEntity.deletedAt.isNull()) + ..addColumns([assetCount]) + ..addColumns([_db.userEntity.name]) + ..groupBy([_db.remoteAlbumEntity.id]); return query .map( - (row) => row.readTable(_db.remoteAlbumEntity).toDto( - assetCount: row.read(assetCount) ?? 0, - ownerName: row.read(_db.userEntity.name)!, - ), + (row) => row + .readTable(_db.remoteAlbumEntity) + .toDto(assetCount: row.read(assetCount) ?? 0, ownerName: row.read(_db.userEntity.name)!), ) .getSingleOrNull(); } - Future create( - RemoteAlbum album, - List assetIds, - ) async { + Future create(RemoteAlbum album, List assetIds) async { await _db.transaction(() async { final entity = RemoteAlbumEntityCompanion( id: Value(album.id), @@ -123,17 +111,11 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { if (assetIds.isNotEmpty) { final albumAssets = assetIds.map( - (assetId) => RemoteAlbumAssetEntityCompanion( - albumId: Value(album.id), - assetId: Value(assetId), - ), + (assetId) => RemoteAlbumAssetEntityCompanion(albumId: Value(album.id), assetId: Value(assetId)), ); await _db.batch((batch) { - batch.insertAll( - _db.remoteAlbumAssetEntity, - albumAssets, - ); + batch.insertAll(_db.remoteAlbumAssetEntity, albumAssets); }); } }); @@ -141,38 +123,30 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { Future update(RemoteAlbum album) async { await _db.remoteAlbumEntity.update().replace( - RemoteAlbumEntityCompanion( - id: Value(album.id), - name: Value(album.name), - ownerId: Value(album.ownerId), - createdAt: Value(album.createdAt), - updatedAt: Value(album.updatedAt), - description: Value(album.description), - thumbnailAssetId: Value(album.thumbnailAssetId), - isActivityEnabled: Value(album.isActivityEnabled), - order: Value(album.order), - ), - ); + RemoteAlbumEntityCompanion( + id: Value(album.id), + name: Value(album.name), + ownerId: Value(album.ownerId), + createdAt: Value(album.createdAt), + updatedAt: Value(album.updatedAt), + description: Value(album.description), + thumbnailAssetId: Value(album.thumbnailAssetId), + isActivityEnabled: Value(album.isActivityEnabled), + order: Value(album.order), + ), + ); } Future removeAssets(String albumId, List assetIds) { - return _db.remoteAlbumAssetEntity.deleteWhere( - (tbl) => tbl.albumId.equals(albumId) & tbl.assetId.isIn(assetIds), - ); + return _db.remoteAlbumAssetEntity.deleteWhere((tbl) => tbl.albumId.equals(albumId) & tbl.assetId.isIn(assetIds)); } FutureOr<(DateTime, DateTime)> getDateRange(String albumId) { final query = _db.remoteAlbumAssetEntity.selectOnly() ..where(_db.remoteAlbumAssetEntity.albumId.equals(albumId)) - ..addColumns([ - _db.remoteAssetEntity.createdAt.min(), - _db.remoteAssetEntity.createdAt.max(), - ]) + ..addColumns([_db.remoteAssetEntity.createdAt.min(), _db.remoteAssetEntity.createdAt.max()]) ..join([ - innerJoin( - _db.remoteAssetEntity, - _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), - ), + innerJoin(_db.remoteAssetEntity, _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId)), ]); return query.map((row) { @@ -183,8 +157,9 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { } Future> getSharedUsers(String albumId) async { - final albumUserRows = - await (_db.select(_db.remoteAlbumUserEntity)..where((row) => row.albumId.equals(albumId))).get(); + final albumUserRows = await (_db.select( + _db.remoteAlbumUserEntity, + )..where((row) => row.albumId.equals(albumId))).get(); if (albumUserRows.isEmpty) { return []; @@ -214,29 +189,19 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { Future> getAssets(String albumId) { final query = _db.remoteAlbumAssetEntity.select().join([ - innerJoin( - _db.remoteAssetEntity, - _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), - ), - ]) - ..where(_db.remoteAlbumAssetEntity.albumId.equals(albumId)); + innerJoin(_db.remoteAssetEntity, _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId)), + ])..where(_db.remoteAlbumAssetEntity.albumId.equals(albumId)); return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get(); } Future addAssets(String albumId, List assetIds) async { final albumAssets = assetIds.map( - (assetId) => RemoteAlbumAssetEntityCompanion( - albumId: Value(albumId), - assetId: Value(assetId), - ), + (assetId) => RemoteAlbumAssetEntityCompanion(albumId: Value(albumId), assetId: Value(assetId)), ); await _db.batch((batch) { - batch.insertAll( - _db.remoteAlbumAssetEntity, - albumAssets, - ); + batch.insertAll(_db.remoteAlbumAssetEntity, albumAssets); }); return assetIds.length; @@ -252,47 +217,41 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { ); return _db.batch((batch) { - batch.insertAll( - _db.remoteAlbumUserEntity, - albumUsers, - ); + batch.insertAll(_db.remoteAlbumUserEntity, albumUsers); }); } Future deleteAlbum(String albumId) async { return _db.transaction(() async { - await _db.remoteAlbumEntity.deleteWhere( - (table) => table.id.equals(albumId), - ); + await _db.remoteAlbumEntity.deleteWhere((table) => table.id.equals(albumId)); }); } Stream watchAlbum(String albumId) { - final query = _db.remoteAlbumEntity.select().join([ - leftOuterJoin( - _db.remoteAlbumAssetEntity, - _db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id), - useColumns: false, - ), - leftOuterJoin( - _db.remoteAssetEntity, - _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), - useColumns: false, - ), - leftOuterJoin( - _db.userEntity, - _db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId), - useColumns: false, - ), - ]) - ..where(_db.remoteAlbumEntity.id.equals(albumId)) - ..addColumns([_db.userEntity.name]) - ..groupBy([_db.remoteAlbumEntity.id]); + final query = + _db.remoteAlbumEntity.select().join([ + leftOuterJoin( + _db.remoteAlbumAssetEntity, + _db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id), + useColumns: false, + ), + leftOuterJoin( + _db.remoteAssetEntity, + _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), + useColumns: false, + ), + leftOuterJoin( + _db.userEntity, + _db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId), + useColumns: false, + ), + ]) + ..where(_db.remoteAlbumEntity.id.equals(albumId)) + ..addColumns([_db.userEntity.name]) + ..groupBy([_db.remoteAlbumEntity.id]); return query.map((row) { - final album = row.readTable(_db.remoteAlbumEntity).toDto( - ownerName: row.read(_db.userEntity.name)!, - ); + final album = row.readTable(_db.remoteAlbumEntity).toDto(ownerName: row.read(_db.userEntity.name)!); return album; }).watchSingleOrNull(); } diff --git a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart index 212b9b7f34..1ab62b3442 100644 --- a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart @@ -30,17 +30,16 @@ class RemoteAssetRepository extends DriftDatabaseRepository { } SingleOrNullSelectable _assetSelectable(String id) { - final query = _db.remoteAssetEntity.select().addColumns([ - _db.localAssetEntity.id, - ]).join([ - leftOuterJoin( - _db.localAssetEntity, - _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), - useColumns: false, - ), - ]) - ..where(_db.remoteAssetEntity.id.equals(id)) - ..limit(1); + final query = + _db.remoteAssetEntity.select().addColumns([_db.localAssetEntity.id]).join([ + leftOuterJoin( + _db.localAssetEntity, + _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), + useColumns: false, + ), + ]) + ..where(_db.remoteAssetEntity.id.equals(id)) + ..limit(1); return query.map((row) { final asset = row.readTable(_db.remoteAssetEntity).toDto(); @@ -57,17 +56,16 @@ class RemoteAssetRepository extends DriftDatabaseRepository { } Stream watchAsset(String id) { - final query = _db.remoteAssetEntity.select().addColumns([ - _db.localAssetEntity.id, - ]).join([ - leftOuterJoin( - _db.localAssetEntity, - _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), - useColumns: false, - ), - ]) - ..where(_db.remoteAssetEntity.id.equals(id)) - ..limit(1); + final query = + _db.remoteAssetEntity.select().addColumns([_db.localAssetEntity.id]).join([ + leftOuterJoin( + _db.localAssetEntity, + _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), + useColumns: false, + ), + ]) + ..where(_db.remoteAssetEntity.id.equals(id)) + ..limit(1); return query.map((row) { final asset = row.readTable(_db.remoteAssetEntity).toDto(); @@ -81,9 +79,7 @@ class RemoteAssetRepository extends DriftDatabaseRepository { } final query = _db.remoteAssetEntity.select() - ..where( - (row) => row.stackId.equals(asset.stackId!) & row.id.equals(asset.id).not(), - ) + ..where((row) => row.stackId.equals(asset.stackId!) & row.id.equals(asset.id).not()) ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]); return query.map((row) => row.toDto()).get(); @@ -102,24 +98,22 @@ class RemoteAssetRepository extends DriftDatabaseRepository { "asset", ); - final query = asset.selectOnly().join([ - innerJoin( - _db.remoteExifEntity, - _db.remoteExifEntity.assetId.equalsExp(asset.ref(_db.remoteAssetEntity.id)), - useColumns: false, - ), - ]) - ..addColumns([ - _db.remoteExifEntity.city, - _db.remoteExifEntity.assetId, - ]) - ..where( - _db.remoteExifEntity.city.isNotNull() & - asset.ref(_db.remoteAssetEntity.deletedAt).isNull() & - asset.ref(_db.remoteAssetEntity.visibility).equals(AssetVisibility.timeline.index), - ) - ..groupBy([_db.remoteExifEntity.city]) - ..orderBy([OrderingTerm.asc(_db.remoteExifEntity.city)]); + final query = + asset.selectOnly().join([ + innerJoin( + _db.remoteExifEntity, + _db.remoteExifEntity.assetId.equalsExp(asset.ref(_db.remoteAssetEntity.id)), + useColumns: false, + ), + ]) + ..addColumns([_db.remoteExifEntity.city, _db.remoteExifEntity.assetId]) + ..where( + _db.remoteExifEntity.city.isNotNull() & + asset.ref(_db.remoteAssetEntity.deletedAt).isNull() & + asset.ref(_db.remoteAssetEntity.visibility).equals(AssetVisibility.timeline.index), + ) + ..groupBy([_db.remoteExifEntity.city]) + ..orderBy([OrderingTerm.asc(_db.remoteExifEntity.city)]); return query.map((row) { final assetId = row.read(_db.remoteExifEntity.assetId); @@ -185,10 +179,7 @@ class RemoteAssetRepository extends DriftDatabaseRepository { for (final id in ids) { batch.update( _db.remoteExifEntity, - RemoteExifEntityCompanion( - latitude: Value(location.latitude), - longitude: Value(location.longitude), - ), + RemoteExifEntityCompanion(latitude: Value(location.latitude), longitude: Value(location.longitude)), where: (e) => e.assetId.equals(id), ); } @@ -205,23 +196,14 @@ class RemoteAssetRepository extends DriftDatabaseRepository { await _db.stackEntity.deleteWhere((row) => row.id.isIn(stackIds)); await _db.batch((batch) { - final companion = StackEntityCompanion( - ownerId: Value(userId), - primaryAssetId: Value(stack.primaryAssetId), - ); + final companion = StackEntityCompanion(ownerId: Value(userId), primaryAssetId: Value(stack.primaryAssetId)); - batch.insert( - _db.stackEntity, - companion.copyWith(id: Value(stack.id)), - onConflict: DoUpdate((_) => companion), - ); + batch.insert(_db.stackEntity, companion.copyWith(id: Value(stack.id)), onConflict: DoUpdate((_) => companion)); for (final assetId in stack.assetIds) { batch.update( _db.remoteAssetEntity, - RemoteAssetEntityCompanion( - stackId: Value(stack.id), - ), + RemoteAssetEntityCompanion(stackId: Value(stack.id)), where: (e) => e.id.equals(assetId), ); } diff --git a/mobile/lib/infrastructure/repositories/search_api.repository.dart b/mobile/lib/infrastructure/repositories/search_api.repository.dart index 441219a02f..129746120b 100644 --- a/mobile/lib/infrastructure/repositories/search_api.repository.dart +++ b/mobile/lib/infrastructure/repositories/search_api.repository.dart @@ -66,12 +66,5 @@ class SearchApiRepository extends ApiRepository { String? state, String? make, String? model, - }) => - _api.getSearchSuggestions( - type, - country: country, - state: state, - make: make, - model: model, - ); + }) => _api.getSearchSuggestions(type, country: country, state: state, make: make, model: model); } diff --git a/mobile/lib/infrastructure/repositories/stack.repository.dart b/mobile/lib/infrastructure/repositories/stack.repository.dart index cdac5fe4ad..28f7496f97 100644 --- a/mobile/lib/infrastructure/repositories/stack.repository.dart +++ b/mobile/lib/infrastructure/repositories/stack.repository.dart @@ -18,12 +18,6 @@ class DriftStackRepository extends DriftDatabaseRepository { extension on StackEntityData { Stack toDto() { - return Stack( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - primaryAssetId: primaryAssetId, - ); + return Stack(id: id, createdAt: createdAt, updatedAt: updatedAt, ownerId: ownerId, primaryAssetId: primaryAssetId); } } diff --git a/mobile/lib/infrastructure/repositories/store.repository.dart b/mobile/lib/infrastructure/repositories/store.repository.dart index 990c2ad65d..6467767aa2 100644 --- a/mobile/lib/infrastructure/repositories/store.repository.dart +++ b/mobile/lib/infrastructure/repositories/store.repository.dart @@ -23,11 +23,7 @@ class IsarStoreRepository extends IsarDatabaseRepository { .filter() .anyOf(validStoreKeys, (query, id) => query.idEqualTo(id)) .watch(fireImmediately: true) - .asyncExpand( - (entities) => Stream.fromFutures( - entities.map((e) async => _toUpdateEvent(e)), - ), - ); + .asyncExpand((entities) => Stream.fromFutures(entities.map((e) async => _toUpdateEvent(e)))); } Future delete(StoreKey key) async { @@ -68,14 +64,17 @@ class IsarStoreRepository extends IsarDatabaseRepository { return StoreDto(key, value); } - Future _toValue(StoreKey key, StoreValue entity) async => switch (key.type) { - const (int) => entity.intValue, - const (String) => entity.strValue, - const (bool) => entity.intValue == 1, - const (DateTime) => entity.intValue == null ? null : DateTime.fromMillisecondsSinceEpoch(entity.intValue!), - const (UserDto) => entity.strValue == null ? null : await IsarUserRepository(_db).getByUserId(entity.strValue!), - _ => null, - } as T?; + Future _toValue(StoreKey key, StoreValue entity) async => + switch (key.type) { + const (int) => entity.intValue, + const (String) => entity.strValue, + const (bool) => entity.intValue == 1, + const (DateTime) => entity.intValue == null ? null : DateTime.fromMillisecondsSinceEpoch(entity.intValue!), + const (UserDto) => + entity.strValue == null ? null : await IsarUserRepository(_db).getByUserId(entity.strValue!), + _ => null, + } + as T?; Future _fromValue(StoreKey key, T value) async { final (int? intValue, String? strValue) = switch (key.type) { @@ -83,13 +82,8 @@ class IsarStoreRepository extends IsarDatabaseRepository { const (String) => (null, value as String), const (bool) => ((value as bool) ? 1 : 0, null), const (DateTime) => ((value as DateTime).millisecondsSinceEpoch, null), - const (UserDto) => ( - null, - (await IsarUserRepository(_db).update(value as UserDto)).id, - ), - _ => throw UnsupportedError( - "Unsupported primitive type: ${key.type} for key: ${key.name}", - ), + const (UserDto) => (null, (await IsarUserRepository(_db).update(value as UserDto)).id), + _ => throw UnsupportedError("Unsupported primitive type: ${key.type} for key: ${key.name}"), }; return StoreValue(key.id, intValue: intValue, strValue: strValue); } diff --git a/mobile/lib/infrastructure/repositories/sync_api.repository.dart b/mobile/lib/infrastructure/repositories/sync_api.repository.dart index 6727f19c64..78b2a9d957 100644 --- a/mobile/lib/infrastructure/repositories/sync_api.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_api.repository.dart @@ -27,10 +27,7 @@ class SyncApiRepository { final client = httpClient ?? http.Client(); final endpoint = "${_api.apiClient.basePath}/sync/stream"; - final headers = { - 'Content-Type': 'application/json', - 'Accept': 'application/jsonlines+json', - }; + final headers = {'Content-Type': 'application/json', 'Accept': 'application/jsonlines+json'}; final headerParams = {}; await _api.applyToParams([], headerParams); @@ -78,10 +75,7 @@ class SyncApiRepository { if (response.statusCode != 200) { final errorBody = await response.stream.bytesToString(); - throw ApiException( - response.statusCode, - 'Failed to get sync stream: $errorBody', - ); + throw ApiException(response.statusCode, 'Failed to get sync stream: $errorBody'); } await for (final chunk in response.stream.transform(utf8.decoder)) { diff --git a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart index 54bc01cfa2..64b0661e16 100644 --- a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart @@ -42,16 +42,9 @@ class SyncStreamRepository extends DriftDatabaseRepository { try { await _db.batch((batch) { for (final user in data) { - final companion = UserEntityCompanion( - name: Value(user.name), - email: Value(user.email), - ); + final companion = UserEntityCompanion(name: Value(user.name), email: Value(user.email)); - batch.insert( - _db.userEntity, - companion.copyWith(id: Value(user.id)), - onConflict: DoUpdate((_) => companion), - ); + batch.insert(_db.userEntity, companion.copyWith(id: Value(user.id)), onConflict: DoUpdate((_) => companion)); } }); } catch (error, stack) { @@ -66,10 +59,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { for (final partner in data) { batch.delete( _db.partnerEntity, - PartnerEntityCompanion( - sharedById: Value(partner.sharedById), - sharedWithId: Value(partner.sharedWithId), - ), + PartnerEntityCompanion(sharedById: Value(partner.sharedById), sharedWithId: Value(partner.sharedWithId)), ); } }); @@ -87,10 +77,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { batch.insert( _db.partnerEntity, - companion.copyWith( - sharedById: Value(partner.sharedById), - sharedWithId: Value(partner.sharedWithId), - ), + companion.copyWith(sharedById: Value(partner.sharedById), sharedWithId: Value(partner.sharedWithId)), onConflict: DoUpdate((_) => companion), ); } @@ -101,24 +88,16 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future deleteAssetsV1( - Iterable data, { - String debugLabel = 'user', - }) async { + Future deleteAssetsV1(Iterable data, {String debugLabel = 'user'}) async { try { - await _db.remoteAssetEntity.deleteWhere( - (row) => row.id.isIn(data.map((e) => e.assetId)), - ); + await _db.remoteAssetEntity.deleteWhere((row) => row.id.isIn(data.map((e) => e.assetId))); } catch (error, stack) { _logger.severe('Error: deleteAssetsV1 - $debugLabel', error, stack); rethrow; } } - Future updateAssetsV1( - Iterable data, { - String debugLabel = 'user', - }) async { + Future updateAssetsV1(Iterable data, {String debugLabel = 'user'}) async { try { await _db.batch((batch) { for (final asset in data) { @@ -152,10 +131,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future updateAssetsExifV1( - Iterable data, { - String debugLabel = 'user', - }) async { + Future updateAssetsExifV1(Iterable data, {String debugLabel = 'user'}) async { try { await _db.batch((batch) { for (final exif in data) { @@ -191,20 +167,14 @@ class SyncStreamRepository extends DriftDatabaseRepository { } }); } catch (error, stack) { - _logger.severe( - 'Error: updateAssetsExifV1 - $debugLabel', - error, - stack, - ); + _logger.severe('Error: updateAssetsExifV1 - $debugLabel', error, stack); rethrow; } } Future deleteAlbumsV1(Iterable data) async { try { - await _db.remoteAlbumEntity.deleteWhere( - (row) => row.id.isIn(data.map((e) => e.albumId)), - ); + await _db.remoteAlbumEntity.deleteWhere((row) => row.id.isIn(data.map((e) => e.albumId))); } catch (error, stack) { _logger.severe('Error: deleteAlbumsV1', error, stack); rethrow; @@ -245,10 +215,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { for (final album in data) { batch.delete( _db.remoteAlbumUserEntity, - RemoteAlbumUserEntityCompanion( - albumId: Value(album.albumId), - userId: Value(album.userId), - ), + RemoteAlbumUserEntityCompanion(albumId: Value(album.albumId), userId: Value(album.userId)), ); } }); @@ -258,49 +225,32 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future updateAlbumUsersV1( - Iterable data, { - String debugLabel = 'user', - }) async { + Future updateAlbumUsersV1(Iterable data, {String debugLabel = 'user'}) async { try { await _db.batch((batch) { for (final album in data) { - final companion = RemoteAlbumUserEntityCompanion( - role: Value(album.role.toAlbumUserRole()), - ); + final companion = RemoteAlbumUserEntityCompanion(role: Value(album.role.toAlbumUserRole())); batch.insert( _db.remoteAlbumUserEntity, - companion.copyWith( - albumId: Value(album.albumId), - userId: Value(album.userId), - ), + companion.copyWith(albumId: Value(album.albumId), userId: Value(album.userId)), onConflict: DoUpdate((_) => companion), ); } }); } catch (error, stack) { - _logger.severe( - 'Error: updateAlbumUsersV1 - $debugLabel', - error, - stack, - ); + _logger.severe('Error: updateAlbumUsersV1 - $debugLabel', error, stack); rethrow; } } - Future deleteAlbumToAssetsV1( - Iterable data, - ) async { + Future deleteAlbumToAssetsV1(Iterable data) async { try { await _db.batch((batch) { for (final album in data) { batch.delete( _db.remoteAlbumAssetEntity, - RemoteAlbumAssetEntityCompanion( - albumId: Value(album.albumId), - assetId: Value(album.assetId), - ), + RemoteAlbumAssetEntityCompanion(albumId: Value(album.albumId), assetId: Value(album.assetId)), ); } }); @@ -310,10 +260,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future updateAlbumToAssetsV1( - Iterable data, { - String debugLabel = 'user', - }) async { + Future updateAlbumToAssetsV1(Iterable data, {String debugLabel = 'user'}) async { try { await _db.batch((batch) { for (final album in data) { @@ -322,19 +269,11 @@ class SyncStreamRepository extends DriftDatabaseRepository { assetId: Value(album.assetId), ); - batch.insert( - _db.remoteAlbumAssetEntity, - companion, - onConflict: DoNothing(), - ); + batch.insert(_db.remoteAlbumAssetEntity, companion, onConflict: DoNothing()); } }); } catch (error, stack) { - _logger.severe( - 'Error: updateAlbumToAssetsV1 - $debugLabel', - error, - stack, - ); + _logger.severe('Error: updateAlbumToAssetsV1 - $debugLabel', error, stack); rethrow; } } @@ -371,9 +310,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { Future deleteMemoriesV1(Iterable data) async { try { - await _db.memoryEntity.deleteWhere( - (row) => row.id.isIn(data.map((e) => e.memoryId)), - ); + await _db.memoryEntity.deleteWhere((row) => row.id.isIn(data.map((e) => e.memoryId))); } catch (error, stack) { _logger.severe('Error: deleteMemoriesV1', error, stack); rethrow; @@ -384,16 +321,9 @@ class SyncStreamRepository extends DriftDatabaseRepository { try { await _db.batch((batch) { for (final asset in data) { - final companion = MemoryAssetEntityCompanion( - memoryId: Value(asset.memoryId), - assetId: Value(asset.assetId), - ); + final companion = MemoryAssetEntityCompanion(memoryId: Value(asset.memoryId), assetId: Value(asset.assetId)); - batch.insert( - _db.memoryAssetEntity, - companion, - onConflict: DoNothing(), - ); + batch.insert(_db.memoryAssetEntity, companion, onConflict: DoNothing()); } }); } catch (error, stack) { @@ -402,18 +332,13 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future deleteMemoryAssetsV1( - Iterable data, - ) async { + Future deleteMemoryAssetsV1(Iterable data) async { try { await _db.batch((batch) { for (final asset in data) { batch.delete( _db.memoryAssetEntity, - MemoryAssetEntityCompanion( - memoryId: Value(asset.memoryId), - assetId: Value(asset.assetId), - ), + MemoryAssetEntityCompanion(memoryId: Value(asset.memoryId), assetId: Value(asset.assetId)), ); } }); @@ -423,10 +348,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future updateStacksV1( - Iterable data, { - String debugLabel = 'user', - }) async { + Future updateStacksV1(Iterable data, {String debugLabel = 'user'}) async { try { await _db.batch((batch) { for (final stack in data) { @@ -450,36 +372,24 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future deleteStacksV1( - Iterable data, { - String debugLabel = 'user', - }) async { + Future deleteStacksV1(Iterable data, {String debugLabel = 'user'}) async { try { - await _db.stackEntity.deleteWhere( - (row) => row.id.isIn(data.map((e) => e.stackId)), - ); + await _db.stackEntity.deleteWhere((row) => row.id.isIn(data.map((e) => e.stackId))); } catch (error, stack) { _logger.severe('Error: deleteStacksV1 - $debugLabel', error, stack); rethrow; } } - Future updateUserMetadatasV1( - Iterable data, - ) async { + Future updateUserMetadatasV1(Iterable data) async { try { await _db.batch((batch) { for (final userMetadata in data) { - final companion = UserMetadataEntityCompanion( - value: Value(userMetadata.value as Map), - ); + final companion = UserMetadataEntityCompanion(value: Value(userMetadata.value as Map)); batch.insert( _db.userMetadataEntity, - companion.copyWith( - userId: Value(userMetadata.userId), - key: Value(userMetadata.key.toUserMetadataKey()), - ), + companion.copyWith(userId: Value(userMetadata.userId), key: Value(userMetadata.key.toUserMetadataKey())), onConflict: DoUpdate((_) => companion), ); } @@ -490,9 +400,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future deleteUserMetadatasV1( - Iterable data, - ) async { + Future deleteUserMetadatasV1(Iterable data) async { try { await _db.batch((batch) { for (final userMetadata in data) { @@ -540,16 +448,11 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future deletePeopleV1( - Iterable data, - ) async { + Future deletePeopleV1(Iterable data) async { try { await _db.batch((batch) { for (final person in data) { - batch.deleteWhere( - _db.personEntity, - (row) => row.id.equals(person.personId), - ); + batch.deleteWhere(_db.personEntity, (row) => row.id.equals(person.personId)); } }); } catch (error, stack) { @@ -591,10 +494,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { try { await _db.batch((batch) { for (final assetFace in data) { - batch.deleteWhere( - _db.assetFaceEntity, - (row) => row.id.equals(assetFace.assetFaceId), - ); + batch.deleteWhere(_db.assetFaceEntity, (row) => row.id.equals(assetFace.assetFaceId)); } }); } catch (error, stack) { @@ -606,54 +506,54 @@ class SyncStreamRepository extends DriftDatabaseRepository { extension on AssetTypeEnum { AssetType toAssetType() => switch (this) { - AssetTypeEnum.IMAGE => AssetType.image, - AssetTypeEnum.VIDEO => AssetType.video, - AssetTypeEnum.AUDIO => AssetType.audio, - AssetTypeEnum.OTHER => AssetType.other, - _ => throw Exception('Unknown AssetType value: $this'), - }; + AssetTypeEnum.IMAGE => AssetType.image, + AssetTypeEnum.VIDEO => AssetType.video, + AssetTypeEnum.AUDIO => AssetType.audio, + AssetTypeEnum.OTHER => AssetType.other, + _ => throw Exception('Unknown AssetType value: $this'), + }; } extension on AssetOrder { AlbumAssetOrder toAlbumAssetOrder() => switch (this) { - AssetOrder.asc => AlbumAssetOrder.asc, - AssetOrder.desc => AlbumAssetOrder.desc, - _ => throw Exception('Unknown AssetOrder value: $this'), - }; + AssetOrder.asc => AlbumAssetOrder.asc, + AssetOrder.desc => AlbumAssetOrder.desc, + _ => throw Exception('Unknown AssetOrder value: $this'), + }; } extension on MemoryType { MemoryTypeEnum toMemoryType() => switch (this) { - MemoryType.onThisDay => MemoryTypeEnum.onThisDay, - _ => throw Exception('Unknown MemoryType value: $this'), - }; + MemoryType.onThisDay => MemoryTypeEnum.onThisDay, + _ => throw Exception('Unknown MemoryType value: $this'), + }; } extension on api.AlbumUserRole { AlbumUserRole toAlbumUserRole() => switch (this) { - api.AlbumUserRole.editor => AlbumUserRole.editor, - api.AlbumUserRole.viewer => AlbumUserRole.viewer, - _ => throw Exception('Unknown AlbumUserRole value: $this'), - }; + api.AlbumUserRole.editor => AlbumUserRole.editor, + api.AlbumUserRole.viewer => AlbumUserRole.viewer, + _ => throw Exception('Unknown AlbumUserRole value: $this'), + }; } extension on api.AssetVisibility { AssetVisibility toAssetVisibility() => switch (this) { - api.AssetVisibility.timeline => AssetVisibility.timeline, - api.AssetVisibility.hidden => AssetVisibility.hidden, - api.AssetVisibility.archive => AssetVisibility.archive, - api.AssetVisibility.locked => AssetVisibility.locked, - _ => throw Exception('Unknown AssetVisibility value: $this'), - }; + api.AssetVisibility.timeline => AssetVisibility.timeline, + api.AssetVisibility.hidden => AssetVisibility.hidden, + api.AssetVisibility.archive => AssetVisibility.archive, + api.AssetVisibility.locked => AssetVisibility.locked, + _ => throw Exception('Unknown AssetVisibility value: $this'), + }; } extension on api.UserMetadataKey { UserMetadataKey toUserMetadataKey() => switch (this) { - api.UserMetadataKey.onboarding => UserMetadataKey.onboarding, - api.UserMetadataKey.preferences => UserMetadataKey.preferences, - api.UserMetadataKey.license => UserMetadataKey.license, - _ => throw Exception('Unknown UserMetadataKey value: $this'), - }; + api.UserMetadataKey.onboarding => UserMetadataKey.onboarding, + api.UserMetadataKey.preferences => UserMetadataKey.preferences, + api.UserMetadataKey.license => UserMetadataKey.license, + _ => throw Exception('Unknown UserMetadataKey value: $this'), + }; } extension on String { diff --git a/mobile/lib/infrastructure/repositories/timeline.repository.dart b/mobile/lib/infrastructure/repositories/timeline.repository.dart index 772fb74f84..a2c14a363f 100644 --- a/mobile/lib/infrastructure/repositories/timeline.repository.dart +++ b/mobile/lib/infrastructure/repositories/timeline.repository.dart @@ -21,9 +21,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { Stream> watchTimelineUserIds(String userId) { final query = _db.partnerEntity.selectOnly() ..addColumns([_db.partnerEntity.sharedById]) - ..where( - _db.partnerEntity.inTimeline.equals(true) & _db.partnerEntity.sharedWithId.equals(userId), - ); + ..where(_db.partnerEntity.inTimeline.equals(true) & _db.partnerEntity.sharedWithId.equals(userId)); return query .map((row) => row.read(_db.partnerEntity.sharedById)!) @@ -33,25 +31,13 @@ class DriftTimelineRepository extends DriftDatabaseRepository { } TimelineQuery main(List userIds, GroupAssetsBy groupBy) => ( - bucketSource: () => _watchMainBucket( - userIds, - groupBy: groupBy, - ), - assetSource: (offset, count) => _getMainBucketAssets( - userIds, - offset: offset, - count: count, - ), - ); + bucketSource: () => _watchMainBucket(userIds, groupBy: groupBy), + assetSource: (offset, count) => _getMainBucketAssets(userIds, offset: offset, count: count), + ); - Stream> _watchMainBucket( - List userIds, { - GroupAssetsBy groupBy = GroupAssetsBy.day, - }) { + Stream> _watchMainBucket(List userIds, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { if (groupBy == GroupAssetsBy.none) { - throw UnsupportedError( - "GroupAssetsBy.none is not supported for watchMainBucket", - ); + throw UnsupportedError("GroupAssetsBy.none is not supported for watchMainBucket"); } return _db.mergedAssetDrift @@ -64,11 +50,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { .throttle(const Duration(seconds: 3), trailing: true); } - Future> _getMainBucketAssets( - List userIds, { - required int offset, - required int count, - }) { + Future> _getMainBucketAssets(List userIds, {required int offset, required int count}) { return _db.mergedAssetDrift .mergedAsset(userIds, limit: (_) => Limit(count, offset)) .map( @@ -109,21 +91,11 @@ class DriftTimelineRepository extends DriftDatabaseRepository { } TimelineQuery localAlbum(String albumId, GroupAssetsBy groupBy) => ( - bucketSource: () => _watchLocalAlbumBucket( - albumId, - groupBy: groupBy, - ), - assetSource: (offset, count) => _getLocalAlbumBucketAssets( - albumId, - offset: offset, - count: count, - ), - ); + bucketSource: () => _watchLocalAlbumBucket(albumId, groupBy: groupBy), + assetSource: (offset, count) => _getLocalAlbumBucketAssets(albumId, offset: offset, count: count), + ); - Stream> _watchLocalAlbumBucket( - String albumId, { - GroupAssetsBy groupBy = GroupAssetsBy.day, - }) { + Stream> _watchLocalAlbumBucket(String albumId, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { if (groupBy == GroupAssetsBy.none) { return _db.localAlbumAssetEntity .count(where: (row) => row.albumId.equals(albumId)) @@ -134,22 +106,23 @@ class DriftTimelineRepository extends DriftDatabaseRepository { final assetCountExp = _db.localAssetEntity.id.count(); final dateExp = _db.localAssetEntity.createdAt.dateFmt(groupBy); - final query = _db.localAssetEntity.selectOnly().join([ - innerJoin( - _db.localAlbumAssetEntity, - _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), - useColumns: false, - ), - leftOuterJoin( - _db.remoteAssetEntity, - _db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum), - useColumns: false, - ), - ]) - ..addColumns([assetCountExp, dateExp]) - ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) - ..groupBy([dateExp]) - ..orderBy([OrderingTerm.desc(dateExp)]); + final query = + _db.localAssetEntity.selectOnly().join([ + innerJoin( + _db.localAlbumAssetEntity, + _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), + useColumns: false, + ), + leftOuterJoin( + _db.remoteAssetEntity, + _db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum), + useColumns: false, + ), + ]) + ..addColumns([assetCountExp, dateExp]) + ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) + ..groupBy([dateExp]) + ..orderBy([OrderingTerm.desc(dateExp)]); return query.map((row) { final timeline = row.read(dateExp)!.dateFmt(groupBy); @@ -158,54 +131,37 @@ class DriftTimelineRepository extends DriftDatabaseRepository { }).watch(); } - Future> _getLocalAlbumBucketAssets( - String albumId, { - required int offset, - required int count, - }) { - final query = _db.localAssetEntity.select().join( - [ - innerJoin( - _db.localAlbumAssetEntity, - _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), - useColumns: false, - ), - leftOuterJoin( - _db.remoteAssetEntity, - _db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum), - useColumns: false, - ), - ], - ) - ..addColumns([_db.remoteAssetEntity.id]) - ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) - ..orderBy([OrderingTerm.desc(_db.localAssetEntity.createdAt)]) - ..limit(count, offset: offset); + Future> _getLocalAlbumBucketAssets(String albumId, {required int offset, required int count}) { + final query = + _db.localAssetEntity.select().join([ + innerJoin( + _db.localAlbumAssetEntity, + _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), + useColumns: false, + ), + leftOuterJoin( + _db.remoteAssetEntity, + _db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum), + useColumns: false, + ), + ]) + ..addColumns([_db.remoteAssetEntity.id]) + ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) + ..orderBy([OrderingTerm.desc(_db.localAssetEntity.createdAt)]) + ..limit(count, offset: offset); return query.map((row) { final asset = row.readTable(_db.localAssetEntity).toDto(); - return asset.copyWith( - remoteId: row.read(_db.remoteAssetEntity.id), - ); + return asset.copyWith(remoteId: row.read(_db.remoteAssetEntity.id)); }).get(); } TimelineQuery remoteAlbum(String albumId, GroupAssetsBy groupBy) => ( - bucketSource: () => _watchRemoteAlbumBucket( - albumId, - groupBy: groupBy, - ), - assetSource: (offset, count) => _getRemoteAlbumBucketAssets( - albumId, - offset: offset, - count: count, - ), - ); + bucketSource: () => _watchRemoteAlbumBucket(albumId, groupBy: groupBy), + assetSource: (offset, count) => _getRemoteAlbumBucketAssets(albumId, offset: offset, count: count), + ); - Stream> _watchRemoteAlbumBucket( - String albumId, { - GroupAssetsBy groupBy = GroupAssetsBy.day, - }) { + Stream> _watchRemoteAlbumBucket(String albumId, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { if (groupBy == GroupAssetsBy.none) { return _db.remoteAlbumAssetEntity .count(where: (row) => row.albumId.equals(albumId)) @@ -213,56 +169,53 @@ class DriftTimelineRepository extends DriftDatabaseRepository { .watch() .map((results) => results.isNotEmpty ? results.first : []) .handleError((error) { - return []; - }); + return []; + }); } - return (_db.remoteAlbumEntity.select()..where((row) => row.id.equals(albumId))).watch().switchMap((albums) { - if (albums.isEmpty) { - return Stream.value([]); - } + return (_db.remoteAlbumEntity.select()..where((row) => row.id.equals(albumId))) + .watch() + .switchMap((albums) { + if (albums.isEmpty) { + return Stream.value([]); + } - final album = albums.first; - final isAscending = album.order == AlbumAssetOrder.asc; - final assetCountExp = _db.remoteAssetEntity.id.count(); - final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy); + final album = albums.first; + final isAscending = album.order == AlbumAssetOrder.asc; + final assetCountExp = _db.remoteAssetEntity.id.count(); + final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy); - final query = _db.remoteAssetEntity.selectOnly() - ..addColumns([assetCountExp, dateExp]) - ..join([ - innerJoin( - _db.remoteAlbumAssetEntity, - _db.remoteAlbumAssetEntity.assetId.equalsExp(_db.remoteAssetEntity.id), - useColumns: false, - ), - ]) - ..where( - _db.remoteAssetEntity.deletedAt.isNull() & _db.remoteAlbumAssetEntity.albumId.equals(albumId), - ) - ..groupBy([dateExp]); + final query = _db.remoteAssetEntity.selectOnly() + ..addColumns([assetCountExp, dateExp]) + ..join([ + innerJoin( + _db.remoteAlbumAssetEntity, + _db.remoteAlbumAssetEntity.assetId.equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + ]) + ..where(_db.remoteAssetEntity.deletedAt.isNull() & _db.remoteAlbumAssetEntity.albumId.equals(albumId)) + ..groupBy([dateExp]); - if (isAscending) { - query.orderBy([OrderingTerm.asc(dateExp)]); - } else { - query.orderBy([OrderingTerm.desc(dateExp)]); - } + if (isAscending) { + query.orderBy([OrderingTerm.asc(dateExp)]); + } else { + query.orderBy([OrderingTerm.desc(dateExp)]); + } - return query.map((row) { - final timeline = row.read(dateExp)!.dateFmt(groupBy); - final assetCount = row.read(assetCountExp)!; - return TimeBucket(date: timeline, assetCount: assetCount); - }).watch(); - }).handleError((error) { - // If there's an error (e.g., album was deleted), return empty buckets - return []; - }); + return query.map((row) { + final timeline = row.read(dateExp)!.dateFmt(groupBy); + final assetCount = row.read(assetCountExp)!; + return TimeBucket(date: timeline, assetCount: assetCount); + }).watch(); + }) + .handleError((error) { + // If there's an error (e.g., album was deleted), return empty buckets + return []; + }); } - Future> _getRemoteAlbumBucketAssets( - String albumId, { - required int offset, - required int count, - }) async { + Future> _getRemoteAlbumBucketAssets(String albumId, {required int offset, required int count}) async { final albumData = await (_db.remoteAlbumEntity.select()..where((row) => row.id.equals(albumId))).getSingleOrNull(); // If album doesn't exist (was deleted), return empty list @@ -272,17 +225,13 @@ class DriftTimelineRepository extends DriftDatabaseRepository { final isAscending = albumData.order == AlbumAssetOrder.asc; - final query = _db.remoteAssetEntity.select().join( - [ - innerJoin( - _db.remoteAlbumAssetEntity, - _db.remoteAlbumAssetEntity.assetId.equalsExp(_db.remoteAssetEntity.id), - useColumns: false, - ), - ], - )..where( - _db.remoteAssetEntity.deletedAt.isNull() & _db.remoteAlbumAssetEntity.albumId.equals(albumId), - ); + final query = _db.remoteAssetEntity.select().join([ + innerJoin( + _db.remoteAlbumAssetEntity, + _db.remoteAlbumAssetEntity.assetId.equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + ])..where(_db.remoteAssetEntity.deletedAt.isNull() & _db.remoteAlbumAssetEntity.albumId.equals(albumId)); if (isAscending) { query.orderBy([OrderingTerm.asc(_db.remoteAssetEntity.createdAt)]); @@ -296,69 +245,57 @@ class DriftTimelineRepository extends DriftDatabaseRepository { } TimelineQuery fromAssets(List assets) => ( - bucketSource: () => Stream.value(_generateBuckets(assets.length)), - assetSource: (offset, count) => Future.value(assets.skip(offset).take(count).toList()), - ); + bucketSource: () => Stream.value(_generateBuckets(assets.length)), + assetSource: (offset, count) => Future.value(assets.skip(offset).take(count).toList()), + ); TimelineQuery remote(String ownerId, GroupAssetsBy groupBy) => _remoteQueryBuilder( - filter: (row) => - row.deletedAt.isNull() & row.visibility.equalsValue(AssetVisibility.timeline) & row.ownerId.equals(ownerId), - groupBy: groupBy, - ); + filter: (row) => + row.deletedAt.isNull() & row.visibility.equalsValue(AssetVisibility.timeline) & row.ownerId.equals(ownerId), + groupBy: groupBy, + ); TimelineQuery favorite(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder( - filter: (row) => row.deletedAt.isNull() & row.isFavorite.equals(true) & row.ownerId.equals(userId), - groupBy: groupBy, - ); + filter: (row) => row.deletedAt.isNull() & row.isFavorite.equals(true) & row.ownerId.equals(userId), + groupBy: groupBy, + ); TimelineQuery trash(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder( - filter: (row) => row.deletedAt.isNotNull() & row.ownerId.equals(userId), - groupBy: groupBy, - joinLocal: true, - ); + filter: (row) => row.deletedAt.isNotNull() & row.ownerId.equals(userId), + groupBy: groupBy, + joinLocal: true, + ); TimelineQuery archived(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder( - filter: (row) => - row.deletedAt.isNull() & row.ownerId.equals(userId) & row.visibility.equalsValue(AssetVisibility.archive), - groupBy: groupBy, - ); + filter: (row) => + row.deletedAt.isNull() & row.ownerId.equals(userId) & row.visibility.equalsValue(AssetVisibility.archive), + groupBy: groupBy, + ); TimelineQuery locked(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder( - filter: (row) => - row.deletedAt.isNull() & row.visibility.equalsValue(AssetVisibility.locked) & row.ownerId.equals(userId), - groupBy: groupBy, - ); + filter: (row) => + row.deletedAt.isNull() & row.visibility.equalsValue(AssetVisibility.locked) & row.ownerId.equals(userId), + groupBy: groupBy, + ); TimelineQuery video(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder( - filter: (row) => - row.deletedAt.isNull() & - row.type.equalsValue(AssetType.video) & - row.visibility.equalsValue(AssetVisibility.timeline) & - row.ownerId.equals(userId), - groupBy: groupBy, - ); + filter: (row) => + row.deletedAt.isNull() & + row.type.equalsValue(AssetType.video) & + row.visibility.equalsValue(AssetVisibility.timeline) & + row.ownerId.equals(userId), + groupBy: groupBy, + ); TimelineQuery place(String place, GroupAssetsBy groupBy) => ( - bucketSource: () => _watchPlaceBucket( - place, - groupBy: groupBy, - ), - assetSource: (offset, count) => _getPlaceBucketAssets( - place, - offset: offset, - count: count, - ), - ); + bucketSource: () => _watchPlaceBucket(place, groupBy: groupBy), + assetSource: (offset, count) => _getPlaceBucketAssets(place, offset: offset, count: count), + ); - Stream> _watchPlaceBucket( - String place, { - GroupAssetsBy groupBy = GroupAssetsBy.day, - }) { + Stream> _watchPlaceBucket(String place, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { if (groupBy == GroupAssetsBy.none) { // TODO: implement GroupAssetBy for place - throw UnsupportedError( - "GroupAssetsBy.none is not supported for watchPlaceBucket", - ); + throw UnsupportedError("GroupAssetsBy.none is not supported for watchPlaceBucket"); } final assetCountExp = _db.remoteAssetEntity.id.count(); @@ -388,27 +325,22 @@ class DriftTimelineRepository extends DriftDatabaseRepository { }).watch(); } - Future> _getPlaceBucketAssets( - String place, { - required int offset, - required int count, - }) { - final query = _db.remoteAssetEntity.select().join( - [ - innerJoin( - _db.remoteExifEntity, - _db.remoteExifEntity.assetId.equalsExp(_db.remoteAssetEntity.id), - useColumns: false, - ), - ], - ) - ..where( - _db.remoteAssetEntity.deletedAt.isNull() & - _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) & - _db.remoteExifEntity.city.equals(place), - ) - ..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)]) - ..limit(count, offset: offset); + Future> _getPlaceBucketAssets(String place, {required int offset, required int count}) { + final query = + _db.remoteAssetEntity.select().join([ + innerJoin( + _db.remoteExifEntity, + _db.remoteExifEntity.assetId.equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + ]) + ..where( + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) & + _db.remoteExifEntity.city.equals(place), + ) + ..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)]) + ..limit(count, offset: offset); return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get(); } @@ -419,12 +351,8 @@ class DriftTimelineRepository extends DriftDatabaseRepository { }) { return ( bucketSource: () => _watchRemoteBucket(filter: filter, groupBy: groupBy), - assetSource: (offset, count) => _getRemoteAssets( - filter: filter, - offset: offset, - count: count, - joinLocal: joinLocal, - ), + assetSource: (offset, count) => + _getRemoteAssets(filter: filter, offset: offset, count: count, joinLocal: joinLocal), ); } @@ -460,17 +388,18 @@ class DriftTimelineRepository extends DriftDatabaseRepository { bool joinLocal = false, }) { if (joinLocal) { - final query = _db.remoteAssetEntity.select().join([ - leftOuterJoin( - _db.localAssetEntity, - _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), - useColumns: false, - ), - ]) - ..addColumns([_db.localAssetEntity.id]) - ..where(filter(_db.remoteAssetEntity)) - ..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)]) - ..limit(count, offset: offset); + final query = + _db.remoteAssetEntity.select().join([ + leftOuterJoin( + _db.localAssetEntity, + _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), + useColumns: false, + ), + ]) + ..addColumns([_db.localAssetEntity.id]) + ..where(filter(_db.remoteAssetEntity)) + ..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)]) + ..limit(count, offset: offset); return query.map((row) { final asset = row.readTable(_db.remoteAssetEntity).toDto(); @@ -507,9 +436,7 @@ extension on Expression { return switch (groupBy) { GroupAssetsBy.day || GroupAssetsBy.auto => localTimeExp.date, GroupAssetsBy.month => localTimeExp.strftime("%Y-%m"), - GroupAssetsBy.none => throw ArgumentError( - "GroupAssetsBy.none is not supported for date formatting", - ), + GroupAssetsBy.none => throw ArgumentError("GroupAssetsBy.none is not supported for date formatting"), }; } } @@ -519,9 +446,7 @@ extension on String { final format = switch (groupBy) { GroupAssetsBy.day || GroupAssetsBy.auto => "y-M-d", GroupAssetsBy.month => "y-M", - GroupAssetsBy.none => throw ArgumentError( - "GroupAssetsBy.none is not supported for date formatting", - ), + GroupAssetsBy.none => throw ArgumentError("GroupAssetsBy.none is not supported for date formatting"), }; try { return DateFormat(format).parse(this); diff --git a/mobile/lib/infrastructure/repositories/user_api.repository.dart b/mobile/lib/infrastructure/repositories/user_api.repository.dart index 0ee3deb4b1..d21a1b71a6 100644 --- a/mobile/lib/infrastructure/repositories/user_api.repository.dart +++ b/mobile/lib/infrastructure/repositories/user_api.repository.dart @@ -17,15 +17,8 @@ class UserApiRepository extends ApiRepository { return UserConverter.fromAdminDto(adminDto, preferenceDto); } - Future createProfileImage({ - required String name, - required Uint8List data, - }) async { - final res = await checkNull( - _api.createProfileImage( - MultipartFile.fromBytes('file', data, filename: name), - ), - ); + Future createProfileImage({required String name, required Uint8List data}) async { + final res = await checkNull(_api.createProfileImage(MultipartFile.fromBytes('file', data, filename: name))); return res.profileImagePath; } diff --git a/mobile/lib/infrastructure/repositories/user_metadata.repository.dart b/mobile/lib/infrastructure/repositories/user_metadata.repository.dart index 81a4cd7945..7205c7f73a 100644 --- a/mobile/lib/infrastructure/repositories/user_metadata.repository.dart +++ b/mobile/lib/infrastructure/repositories/user_metadata.repository.dart @@ -18,20 +18,8 @@ class DriftUserMetadataRepository extends DriftDatabaseRepository { extension on UserMetadataEntityData { UserMetadata toDto() => switch (key) { - UserMetadataKey.onboarding => UserMetadata( - userId: userId, - key: key, - onboarding: Onboarding.fromMap(value), - ), - UserMetadataKey.preferences => UserMetadata( - userId: userId, - key: key, - preferences: Preferences.fromMap(value), - ), - UserMetadataKey.license => UserMetadata( - userId: userId, - key: key, - license: License.fromMap(value), - ), - }; + UserMetadataKey.onboarding => UserMetadata(userId: userId, key: key, onboarding: Onboarding.fromMap(value)), + UserMetadataKey.preferences => UserMetadata(userId: userId, key: key, preferences: Preferences.fromMap(value)), + UserMetadataKey.license => UserMetadata(userId: userId, key: key, license: License.fromMap(value)), + }; } diff --git a/mobile/lib/infrastructure/utils/user.converter.dart b/mobile/lib/infrastructure/utils/user.converter.dart index eb7b24737e..19958beabc 100644 --- a/mobile/lib/infrastructure/utils/user.converter.dart +++ b/mobile/lib/infrastructure/utils/user.converter.dart @@ -6,63 +6,59 @@ import 'package:openapi/api.dart'; abstract final class UserConverter { /// Base user dto used where the complete user object is not required static UserDto fromSimpleUserDto(UserResponseDto dto) => UserDto( - id: dto.id, - email: dto.email, - name: dto.name, - isAdmin: false, - updatedAt: DateTime.now(), - profileImagePath: dto.profileImagePath, - avatarColor: dto.avatarColor.toAvatarColor(), - ); + id: dto.id, + email: dto.email, + name: dto.name, + isAdmin: false, + updatedAt: DateTime.now(), + profileImagePath: dto.profileImagePath, + avatarColor: dto.avatarColor.toAvatarColor(), + ); - static UserDto fromAdminDto( - UserAdminResponseDto adminDto, [ - UserPreferencesResponseDto? preferenceDto, - ]) => - UserDto( - id: adminDto.id, - email: adminDto.email, - name: adminDto.name, - isAdmin: adminDto.isAdmin, - updatedAt: adminDto.updatedAt, - profileImagePath: adminDto.profileImagePath, - avatarColor: adminDto.avatarColor.toAvatarColor(), - memoryEnabled: preferenceDto?.memories.enabled ?? true, - inTimeline: false, - isPartnerSharedBy: false, - isPartnerSharedWith: false, - quotaUsageInBytes: adminDto.quotaUsageInBytes ?? 0, - quotaSizeInBytes: adminDto.quotaSizeInBytes ?? 0, - ); + static UserDto fromAdminDto(UserAdminResponseDto adminDto, [UserPreferencesResponseDto? preferenceDto]) => UserDto( + id: adminDto.id, + email: adminDto.email, + name: adminDto.name, + isAdmin: adminDto.isAdmin, + updatedAt: adminDto.updatedAt, + profileImagePath: adminDto.profileImagePath, + avatarColor: adminDto.avatarColor.toAvatarColor(), + memoryEnabled: preferenceDto?.memories.enabled ?? true, + inTimeline: false, + isPartnerSharedBy: false, + isPartnerSharedWith: false, + quotaUsageInBytes: adminDto.quotaUsageInBytes ?? 0, + quotaSizeInBytes: adminDto.quotaSizeInBytes ?? 0, + ); static UserDto fromPartnerDto(PartnerResponseDto dto) => UserDto( - id: dto.id, - email: dto.email, - name: dto.name, - isAdmin: false, - updatedAt: DateTime.now(), - profileImagePath: dto.profileImagePath, - avatarColor: dto.avatarColor.toAvatarColor(), - memoryEnabled: false, - inTimeline: dto.inTimeline ?? false, - isPartnerSharedBy: false, - isPartnerSharedWith: false, - quotaUsageInBytes: 0, - quotaSizeInBytes: 0, - ); + id: dto.id, + email: dto.email, + name: dto.name, + isAdmin: false, + updatedAt: DateTime.now(), + profileImagePath: dto.profileImagePath, + avatarColor: dto.avatarColor.toAvatarColor(), + memoryEnabled: false, + inTimeline: dto.inTimeline ?? false, + isPartnerSharedBy: false, + isPartnerSharedWith: false, + quotaUsageInBytes: 0, + quotaSizeInBytes: 0, + ); } extension on UserAvatarColor { AvatarColor toAvatarColor() => switch (this) { - UserAvatarColor.red => AvatarColor.red, - UserAvatarColor.green => AvatarColor.green, - UserAvatarColor.blue => AvatarColor.blue, - UserAvatarColor.purple => AvatarColor.purple, - UserAvatarColor.orange => AvatarColor.orange, - UserAvatarColor.pink => AvatarColor.pink, - UserAvatarColor.amber => AvatarColor.amber, - UserAvatarColor.yellow => AvatarColor.yellow, - UserAvatarColor.gray => AvatarColor.gray, - UserAvatarColor.primary || _ => AvatarColor.primary, - }; + UserAvatarColor.red => AvatarColor.red, + UserAvatarColor.green => AvatarColor.green, + UserAvatarColor.blue => AvatarColor.blue, + UserAvatarColor.purple => AvatarColor.purple, + UserAvatarColor.orange => AvatarColor.orange, + UserAvatarColor.pink => AvatarColor.pink, + UserAvatarColor.amber => AvatarColor.amber, + UserAvatarColor.yellow => AvatarColor.yellow, + UserAvatarColor.gray => AvatarColor.gray, + UserAvatarColor.primary || _ => AvatarColor.primary, + }; } diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index e52f298413..63220295ff 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -50,10 +50,7 @@ void main() async { runApp( ProviderScope( - overrides: [ - dbProvider.overrideWithValue(db), - isarProvider.overrideWithValue(db), - ], + overrides: [dbProvider.overrideWithValue(db), isarProvider.overrideWithValue(db)], child: const MainWidget(), ), ); @@ -100,23 +97,15 @@ Future initApp() async { globalConfig: (Config.holdingQueue, (1000, 1000, 1000)), ); - await FileDownloader().trackTasksInGroup( - kDownloadGroupLivePhoto, - markDownloadedComplete: false, - ); + await FileDownloader().trackTasksInGroup(kDownloadGroupLivePhoto, markDownloadedComplete: false); await FileDownloader().trackTasks(); - LicenseRegistry.addLicense( - () async* { - for (final license in nonPubLicenses.entries) { - yield LicenseEntryWithLineBreaks( - [license.key], - license.value, - ); - } - }, - ); + LicenseRegistry.addLicense(() async* { + for (final license in nonPubLicenses.entries) { + yield LicenseEntryWithLineBreaks([license.key], license.value); + } + }); } class ImmichApp extends ConsumerStatefulWidget { @@ -160,9 +149,7 @@ class ImmichAppState extends ConsumerState with WidgetsBindingObserve SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); // Sets the navigation bar color - SystemUiOverlayStyle overlayStyle = const SystemUiOverlayStyle( - systemNavigationBarColor: Colors.transparent, - ); + SystemUiOverlayStyle overlayStyle = const SystemUiOverlayStyle(systemNavigationBarColor: Colors.transparent); if (Platform.isAndroid) { // Android 8 does not support transparent app bars final info = await DeviceInfoPlugin().androidInfo; @@ -177,40 +164,22 @@ class ImmichAppState extends ConsumerState with WidgetsBindingObserve void _configureFileDownloaderNotifications() { FileDownloader().configureNotificationForGroup( kDownloadGroupImage, - running: TaskNotification( - 'downloading_media'.tr(), - '${'file_name'.tr()}: {filename}', - ), - complete: TaskNotification( - 'download_finished'.tr(), - '${'file_name'.tr()}: {filename}', - ), + running: TaskNotification('downloading_media'.tr(), '${'file_name'.tr()}: {filename}'), + complete: TaskNotification('download_finished'.tr(), '${'file_name'.tr()}: {filename}'), progressBar: true, ); FileDownloader().configureNotificationForGroup( kDownloadGroupVideo, - running: TaskNotification( - 'downloading_media'.tr(), - '${'file_name'.tr()}: {filename}', - ), - complete: TaskNotification( - 'download_finished'.tr(), - '${'file_name'.tr()}: {filename}', - ), + running: TaskNotification('downloading_media'.tr(), '${'file_name'.tr()}: {filename}'), + complete: TaskNotification('download_finished'.tr(), '${'file_name'.tr()}: {filename}'), progressBar: true, ); FileDownloader().configureNotificationForGroup( kManualUploadGroup, - running: TaskNotification( - 'uploading_media'.tr(), - '${'file_name'.tr()}: {displayName}', - ), - complete: TaskNotification( - 'upload_finished'.tr(), - '${'file_name'.tr()}: {displayName}', - ), + running: TaskNotification('uploading_media'.tr(), '${'file_name'.tr()}: {displayName}'), + complete: TaskNotification('upload_finished'.tr(), '${'file_name'.tr()}: {displayName}'), progressBar: true, ); } @@ -222,19 +191,13 @@ class ImmichAppState extends ConsumerState with WidgetsBindingObserve final isColdStart = currentRouteName == null || currentRouteName == SplashScreenRoute.name; if (deepLink.uri.scheme == "immich") { - final proposedRoute = await deepLinkHandler.handleScheme( - deepLink, - isColdStart, - ); + final proposedRoute = await deepLinkHandler.handleScheme(deepLink, isColdStart); return proposedRoute; } if (deepLink.uri.host == "my.immich.app") { - final proposedRoute = await deepLinkHandler.handleMyImmichApp( - deepLink, - isColdStart, - ); + final proposedRoute = await deepLinkHandler.handleMyImmichApp(deepLink, isColdStart); return proposedRoute; } @@ -275,9 +238,7 @@ class ImmichAppState extends ConsumerState with WidgetsBindingObserve final immichTheme = ref.watch(immichThemeProvider); return ProviderScope( - overrides: [ - localeProvider.overrideWithValue(context.locale), - ], + overrides: [localeProvider.overrideWithValue(context.locale)], child: MaterialApp.router( title: 'Immich', debugShowCheckedModeBanner: true, @@ -285,14 +246,8 @@ class ImmichAppState extends ConsumerState with WidgetsBindingObserve supportedLocales: context.supportedLocales, locale: context.locale, themeMode: ref.watch(immichThemeModeProvider), - darkTheme: getThemeData( - colorScheme: immichTheme.dark, - locale: context.locale, - ), - theme: getThemeData( - colorScheme: immichTheme.light, - locale: context.locale, - ), + darkTheme: getThemeData(colorScheme: immichTheme.dark, locale: context.locale), + theme: getThemeData(colorScheme: immichTheme.light, locale: context.locale), routerConfig: router.config( deepLinkBuilder: _deepLinkBuilder, navigatorObservers: () => [AppNavigationObserver(ref: ref), HeroController()], diff --git a/mobile/lib/models/albums/album_add_asset_response.model.dart b/mobile/lib/models/albums/album_add_asset_response.model.dart index fbc8a4d560..38dd989af5 100644 --- a/mobile/lib/models/albums/album_add_asset_response.model.dart +++ b/mobile/lib/models/albums/album_add_asset_response.model.dart @@ -7,15 +7,9 @@ class AlbumAddAssetsResponse { List alreadyInAlbum; int successfullyAdded; - AlbumAddAssetsResponse({ - required this.alreadyInAlbum, - required this.successfullyAdded, - }); + AlbumAddAssetsResponse({required this.alreadyInAlbum, required this.successfullyAdded}); - AlbumAddAssetsResponse copyWith({ - List? alreadyInAlbum, - int? successfullyAdded, - }) { + AlbumAddAssetsResponse copyWith({List? alreadyInAlbum, int? successfullyAdded}) { return AlbumAddAssetsResponse( alreadyInAlbum: alreadyInAlbum ?? this.alreadyInAlbum, successfullyAdded: successfullyAdded ?? this.successfullyAdded, @@ -23,10 +17,7 @@ class AlbumAddAssetsResponse { } Map toMap() { - return { - 'alreadyInAlbum': alreadyInAlbum, - 'successfullyAdded': successfullyAdded, - }; + return {'alreadyInAlbum': alreadyInAlbum, 'successfullyAdded': successfullyAdded}; } String toJson() => json.encode(toMap()); diff --git a/mobile/lib/models/albums/album_search.model.dart b/mobile/lib/models/albums/album_search.model.dart index ac4eedbff1..4f69e7e2e1 100644 --- a/mobile/lib/models/albums/album_search.model.dart +++ b/mobile/lib/models/albums/album_search.model.dart @@ -1,5 +1 @@ -enum QuickFilterMode { - all, - sharedWithMe, - myAlbums, -} +enum QuickFilterMode { all, sharedWithMe, myAlbums } diff --git a/mobile/lib/models/albums/album_viewer_page_state.model.dart b/mobile/lib/models/albums/album_viewer_page_state.model.dart index 823226b4a4..70427899ae 100644 --- a/mobile/lib/models/albums/album_viewer_page_state.model.dart +++ b/mobile/lib/models/albums/album_viewer_page_state.model.dart @@ -11,11 +11,7 @@ class AlbumViewerPageState { required this.editDescriptionText, }); - AlbumViewerPageState copyWith({ - bool? isEditAlbum, - String? editTitleText, - String? editDescriptionText, - }) { + AlbumViewerPageState copyWith({bool? isEditAlbum, String? editTitleText, String? editDescriptionText}) { return AlbumViewerPageState( isEditAlbum: isEditAlbum ?? this.isEditAlbum, editTitleText: editTitleText ?? this.editTitleText, diff --git a/mobile/lib/models/albums/asset_selection_page_result.model.dart b/mobile/lib/models/albums/asset_selection_page_result.model.dart index d921ac63ce..cc750f397f 100644 --- a/mobile/lib/models/albums/asset_selection_page_result.model.dart +++ b/mobile/lib/models/albums/asset_selection_page_result.model.dart @@ -4,9 +4,7 @@ import 'package:immich_mobile/entities/asset.entity.dart'; class AssetSelectionPageResult { final Set selectedAssets; - const AssetSelectionPageResult({ - required this.selectedAssets, - }); + const AssetSelectionPageResult({required this.selectedAssets}); @override bool operator ==(Object other) { if (identical(this, other)) return true; diff --git a/mobile/lib/models/asset_selection_state.dart b/mobile/lib/models/asset_selection_state.dart index c022ec3a3a..aded3064ce 100644 --- a/mobile/lib/models/asset_selection_state.dart +++ b/mobile/lib/models/asset_selection_state.dart @@ -13,12 +13,7 @@ class AssetSelectionState { this.selectedCount = 0, }); - AssetSelectionState copyWith({ - bool? hasRemote, - bool? hasLocal, - bool? hasMerged, - int? selectedCount, - }) { + AssetSelectionState copyWith({bool? hasRemote, bool? hasLocal, bool? hasMerged, int? selectedCount}) { return AssetSelectionState( hasRemote: hasRemote ?? this.hasRemote, hasLocal: hasLocal ?? this.hasLocal, @@ -28,10 +23,10 @@ class AssetSelectionState { } AssetSelectionState.fromSelection(Set selection) - : hasLocal = selection.any((e) => e.storage == AssetState.local), - hasMerged = selection.any((e) => e.storage == AssetState.merged), - hasRemote = selection.any((e) => e.storage == AssetState.remote), - selectedCount = selection.length; + : hasLocal = selection.any((e) => e.storage == AssetState.local), + hasMerged = selection.any((e) => e.storage == AssetState.merged), + hasRemote = selection.any((e) => e.storage == AssetState.remote), + selectedCount = selection.length; @override String toString() => diff --git a/mobile/lib/models/auth/auxilary_endpoint.model.dart b/mobile/lib/models/auth/auxilary_endpoint.model.dart index e876097d61..c7f472e111 100644 --- a/mobile/lib/models/auth/auxilary_endpoint.model.dart +++ b/mobile/lib/models/auth/auxilary_endpoint.model.dart @@ -5,19 +5,10 @@ class AuxilaryEndpoint { final String url; final AuxCheckStatus status; - const AuxilaryEndpoint({ - required this.url, - required this.status, - }); + const AuxilaryEndpoint({required this.url, required this.status}); - AuxilaryEndpoint copyWith({ - String? url, - AuxCheckStatus? status, - }) { - return AuxilaryEndpoint( - url: url ?? this.url, - status: status ?? this.status, - ); + AuxilaryEndpoint copyWith({String? url, AuxCheckStatus? status}) { + return AuxilaryEndpoint(url: url ?? this.url, status: status ?? this.status); } @override @@ -34,10 +25,7 @@ class AuxilaryEndpoint { int get hashCode => url.hashCode ^ status.hashCode; Map toMap() { - return { - 'url': url, - 'status': status.toMap(), - }; + return {'url': url, 'status': status.toMap()}; } factory AuxilaryEndpoint.fromMap(Map map) { @@ -55,9 +43,7 @@ class AuxilaryEndpoint { class AuxCheckStatus { final String name; - const AuxCheckStatus({ - required this.name, - }); + const AuxCheckStatus({required this.name}); const AuxCheckStatus._(this.name); static const loading = AuxCheckStatus._('loading'); @@ -75,24 +61,16 @@ class AuxCheckStatus { @override int get hashCode => name.hashCode; - AuxCheckStatus copyWith({ - String? name, - }) { - return AuxCheckStatus( - name: name ?? this.name, - ); + AuxCheckStatus copyWith({String? name}) { + return AuxCheckStatus(name: name ?? this.name); } Map toMap() { - return { - 'name': name, - }; + return {'name': name}; } factory AuxCheckStatus.fromMap(Map map) { - return AuxCheckStatus( - name: map['name'] as String, - ); + return AuxCheckStatus(name: map['name'] as String); } String toJson() => json.encode(toMap()); diff --git a/mobile/lib/models/auth/biometric_status.model.dart b/mobile/lib/models/auth/biometric_status.model.dart index 223b283279..ad2b06be04 100644 --- a/mobile/lib/models/auth/biometric_status.model.dart +++ b/mobile/lib/models/auth/biometric_status.model.dart @@ -5,18 +5,12 @@ class BiometricStatus { final List availableBiometrics; final bool canAuthenticate; - const BiometricStatus({ - required this.availableBiometrics, - required this.canAuthenticate, - }); + const BiometricStatus({required this.availableBiometrics, required this.canAuthenticate}); @override String toString() => 'BiometricStatus(availableBiometrics: $availableBiometrics, canAuthenticate: $canAuthenticate)'; - BiometricStatus copyWith({ - List? availableBiometrics, - bool? canAuthenticate, - }) { + BiometricStatus copyWith({List? availableBiometrics, bool? canAuthenticate}) { return BiometricStatus( availableBiometrics: availableBiometrics ?? this.availableBiometrics, canAuthenticate: canAuthenticate ?? this.canAuthenticate, diff --git a/mobile/lib/models/backup/available_album.model.dart b/mobile/lib/models/backup/available_album.model.dart index 96a19cf60b..502d0b66be 100644 --- a/mobile/lib/models/backup/available_album.model.dart +++ b/mobile/lib/models/backup/available_album.model.dart @@ -4,17 +4,9 @@ class AvailableAlbum { final Album album; final int assetCount; final DateTime? lastBackup; - const AvailableAlbum({ - required this.album, - required this.assetCount, - this.lastBackup, - }); + const AvailableAlbum({required this.album, required this.assetCount, this.lastBackup}); - AvailableAlbum copyWith({ - Album? album, - int? assetCount, - DateTime? lastBackup, - }) { + AvailableAlbum copyWith({Album? album, int? assetCount, DateTime? lastBackup}) { return AvailableAlbum( album: album ?? this.album, assetCount: assetCount ?? this.assetCount, diff --git a/mobile/lib/models/backup/backup_state.model.dart b/mobile/lib/models/backup/backup_state.model.dart index 39554736dd..635d925c3f 100644 --- a/mobile/lib/models/backup/backup_state.model.dart +++ b/mobile/lib/models/backup/backup_state.model.dart @@ -148,10 +148,7 @@ class BackUpState { collectionEquals(other.selectedBackupAlbums, selectedBackupAlbums) && collectionEquals(other.excludedBackupAlbums, excludedBackupAlbums) && collectionEquals(other.allUniqueAssets, allUniqueAssets) && - collectionEquals( - other.selectedAlbumsBackupAssetsIds, - selectedAlbumsBackupAssetsIds, - ) && + collectionEquals(other.selectedAlbumsBackupAssetsIds, selectedAlbumsBackupAssetsIds) && other.currentUploadAsset == currentUploadAsset; } diff --git a/mobile/lib/models/backup/success_upload_asset.model.dart b/mobile/lib/models/backup/success_upload_asset.model.dart index ca49450a31..da1e104ba3 100644 --- a/mobile/lib/models/backup/success_upload_asset.model.dart +++ b/mobile/lib/models/backup/success_upload_asset.model.dart @@ -5,17 +5,9 @@ class SuccessUploadAsset { final String remoteAssetId; final bool isDuplicate; - const SuccessUploadAsset({ - required this.candidate, - required this.remoteAssetId, - required this.isDuplicate, - }); + const SuccessUploadAsset({required this.candidate, required this.remoteAssetId, required this.isDuplicate}); - SuccessUploadAsset copyWith({ - BackupCandidate? candidate, - String? remoteAssetId, - bool? isDuplicate, - }) { + SuccessUploadAsset copyWith({BackupCandidate? candidate, String? remoteAssetId, bool? isDuplicate}) { return SuccessUploadAsset( candidate: candidate ?? this.candidate, remoteAssetId: remoteAssetId ?? this.remoteAssetId, diff --git a/mobile/lib/models/download/download_state.model.dart b/mobile/lib/models/download/download_state.model.dart index b2bd389bc2..82d4e31253 100644 --- a/mobile/lib/models/download/download_state.model.dart +++ b/mobile/lib/models/download/download_state.model.dart @@ -10,17 +10,9 @@ class DownloadInfo { // enum final TaskStatus status; - const DownloadInfo({ - required this.fileName, - required this.progress, - required this.status, - }); + const DownloadInfo({required this.fileName, required this.progress, required this.status}); - DownloadInfo copyWith({ - String? fileName, - double? progress, - TaskStatus? status, - }) { + DownloadInfo copyWith({String? fileName, double? progress, TaskStatus? status}) { return DownloadInfo( fileName: fileName ?? this.fileName, progress: progress ?? this.progress, @@ -29,11 +21,7 @@ class DownloadInfo { } Map toMap() { - return { - 'fileName': fileName, - 'progress': progress, - 'status': status.index, - }; + return {'fileName': fileName, 'progress': progress, 'status': status.index}; } factory DownloadInfo.fromMap(Map map) { @@ -67,17 +55,9 @@ class DownloadState { final TaskStatus downloadStatus; final Map taskProgress; final bool showProgress; - const DownloadState({ - required this.downloadStatus, - required this.taskProgress, - required this.showProgress, - }); + const DownloadState({required this.downloadStatus, required this.taskProgress, required this.showProgress}); - DownloadState copyWith({ - TaskStatus? downloadStatus, - Map? taskProgress, - bool? showProgress, - }) { + DownloadState copyWith({TaskStatus? downloadStatus, Map? taskProgress, bool? showProgress}) { return DownloadState( downloadStatus: downloadStatus ?? this.downloadStatus, taskProgress: taskProgress ?? this.taskProgress, diff --git a/mobile/lib/models/download/livephotos_medatada.model.dart b/mobile/lib/models/download/livephotos_medatada.model.dart index 9c0c7ae4e9..f77a1514ac 100644 --- a/mobile/lib/models/download/livephotos_medatada.model.dart +++ b/mobile/lib/models/download/livephotos_medatada.model.dart @@ -1,43 +1,25 @@ // ignore_for_file: public_member_api_docs, sort_constructors_first import 'dart:convert'; -enum LivePhotosPart { - video, - image, -} +enum LivePhotosPart { video, image } class LivePhotosMetadata { // enum LivePhotosPart part; String id; - LivePhotosMetadata({ - required this.part, - required this.id, - }); + LivePhotosMetadata({required this.part, required this.id}); - LivePhotosMetadata copyWith({ - LivePhotosPart? part, - String? id, - }) { - return LivePhotosMetadata( - part: part ?? this.part, - id: id ?? this.id, - ); + LivePhotosMetadata copyWith({LivePhotosPart? part, String? id}) { + return LivePhotosMetadata(part: part ?? this.part, id: id ?? this.id); } Map toMap() { - return { - 'part': part.index, - 'id': id, - }; + return {'part': part.index, 'id': id}; } factory LivePhotosMetadata.fromMap(Map map) { - return LivePhotosMetadata( - part: LivePhotosPart.values[map['part'] as int], - id: map['id'] as String, - ); + return LivePhotosMetadata(part: LivePhotosPart.values[map['part'] as int], id: map['id'] as String); } String toJson() => json.encode(toMap()); diff --git a/mobile/lib/models/folder/recursive_folder.model.dart b/mobile/lib/models/folder/recursive_folder.model.dart index 62ec670fed..33ac0f4cb4 100644 --- a/mobile/lib/models/folder/recursive_folder.model.dart +++ b/mobile/lib/models/folder/recursive_folder.model.dart @@ -3,9 +3,5 @@ import 'package:immich_mobile/models/folder/root_folder.model.dart'; class RecursiveFolder extends RootFolder { final String name; - const RecursiveFolder({ - required this.name, - required super.path, - required super.subfolders, - }); + const RecursiveFolder({required this.name, required super.path, required super.subfolders}); } diff --git a/mobile/lib/models/folder/root_folder.model.dart b/mobile/lib/models/folder/root_folder.model.dart index 567093ecd5..d4b791b915 100644 --- a/mobile/lib/models/folder/root_folder.model.dart +++ b/mobile/lib/models/folder/root_folder.model.dart @@ -4,8 +4,5 @@ class RootFolder { final List subfolders; final String path; - const RootFolder({ - required this.subfolders, - required this.path, - }); + const RootFolder({required this.subfolders, required this.path}); } diff --git a/mobile/lib/models/map/map_marker.model.dart b/mobile/lib/models/map/map_marker.model.dart index 781eae792f..0f425306ff 100644 --- a/mobile/lib/models/map/map_marker.model.dart +++ b/mobile/lib/models/map/map_marker.model.dart @@ -4,24 +4,13 @@ import 'package:openapi/api.dart'; class MapMarker { final LatLng latLng; final String assetRemoteId; - const MapMarker({ - required this.latLng, - required this.assetRemoteId, - }); + const MapMarker({required this.latLng, required this.assetRemoteId}); - MapMarker copyWith({ - LatLng? latLng, - String? assetRemoteId, - }) { - return MapMarker( - latLng: latLng ?? this.latLng, - assetRemoteId: assetRemoteId ?? this.assetRemoteId, - ); + MapMarker copyWith({LatLng? latLng, String? assetRemoteId}) { + return MapMarker(latLng: latLng ?? this.latLng, assetRemoteId: assetRemoteId ?? this.assetRemoteId); } - MapMarker.fromDto(MapMarkerResponseDto dto) - : latLng = LatLng(dto.lat, dto.lon), - assetRemoteId = dto.id; + MapMarker.fromDto(MapMarkerResponseDto dto) : latLng = LatLng(dto.lat, dto.lon), assetRemoteId = dto.id; @override String toString() => 'MapMarker(latLng: $latLng, assetRemoteId: $assetRemoteId)'; diff --git a/mobile/lib/models/memories/memory.model.dart b/mobile/lib/models/memories/memory.model.dart index fb85d70b9e..8a9db5d51b 100644 --- a/mobile/lib/models/memories/memory.model.dart +++ b/mobile/lib/models/memories/memory.model.dart @@ -7,19 +7,10 @@ import 'package:immich_mobile/entities/asset.entity.dart'; class Memory { final String title; final List assets; - const Memory({ - required this.title, - required this.assets, - }); + const Memory({required this.title, required this.assets}); - Memory copyWith({ - String? title, - List? assets, - }) { - return Memory( - title: title ?? this.title, - assets: assets ?? this.assets, - ); + Memory copyWith({String? title, List? assets}) { + return Memory(title: title ?? this.title, assets: assets ?? this.assets); } @override diff --git a/mobile/lib/models/search/search_curated_content.model.dart b/mobile/lib/models/search/search_curated_content.model.dart index 7ecb5af45c..6e4a083876 100644 --- a/mobile/lib/models/search/search_curated_content.model.dart +++ b/mobile/lib/models/search/search_curated_content.model.dart @@ -14,30 +14,14 @@ class SearchCuratedContent { /// The id to lookup the asset from the server final String id; - const SearchCuratedContent({ - required this.label, - required this.id, - this.subtitle, - }); + const SearchCuratedContent({required this.label, required this.id, this.subtitle}); - SearchCuratedContent copyWith({ - String? label, - String? subtitle, - String? id, - }) { - return SearchCuratedContent( - label: label ?? this.label, - subtitle: subtitle ?? this.subtitle, - id: id ?? this.id, - ); + SearchCuratedContent copyWith({String? label, String? subtitle, String? id}) { + return SearchCuratedContent(label: label ?? this.label, subtitle: subtitle ?? this.subtitle, id: id ?? this.id); } Map toMap() { - return { - 'label': label, - 'subtitle': subtitle, - 'id': id, - }; + return {'label': label, 'subtitle': subtitle, 'id': id}; } factory SearchCuratedContent.fromMap(Map map) { diff --git a/mobile/lib/models/search/search_filter.model.dart b/mobile/lib/models/search/search_filter.model.dart index 1c2167faac..7f27b4d333 100644 --- a/mobile/lib/models/search/search_filter.model.dart +++ b/mobile/lib/models/search/search_filter.model.dart @@ -8,30 +8,14 @@ class SearchLocationFilter { String? country; String? state; String? city; - SearchLocationFilter({ - this.country, - this.state, - this.city, - }); + SearchLocationFilter({this.country, this.state, this.city}); - SearchLocationFilter copyWith({ - String? country, - String? state, - String? city, - }) { - return SearchLocationFilter( - country: country ?? this.country, - state: state ?? this.state, - city: city ?? this.city, - ); + SearchLocationFilter copyWith({String? country, String? state, String? city}) { + return SearchLocationFilter(country: country ?? this.country, state: state ?? this.state, city: city ?? this.city); } Map toMap() { - return { - 'country': country, - 'state': state, - 'city': city, - }; + return {'country': country, 'state': state, 'city': city}; } factory SearchLocationFilter.fromMap(Map map) { @@ -64,26 +48,14 @@ class SearchLocationFilter { class SearchCameraFilter { String? make; String? model; - SearchCameraFilter({ - this.make, - this.model, - }); + SearchCameraFilter({this.make, this.model}); - SearchCameraFilter copyWith({ - String? make, - String? model, - }) { - return SearchCameraFilter( - make: make ?? this.make, - model: model ?? this.model, - ); + SearchCameraFilter copyWith({String? make, String? model}) { + return SearchCameraFilter(make: make ?? this.make, model: model ?? this.model); } Map toMap() { - return { - 'make': make, - 'model': model, - }; + return {'make': make, 'model': model}; } factory SearchCameraFilter.fromMap(Map map) { @@ -115,19 +87,10 @@ class SearchCameraFilter { class SearchDateFilter { DateTime? takenBefore; DateTime? takenAfter; - SearchDateFilter({ - this.takenBefore, - this.takenAfter, - }); + SearchDateFilter({this.takenBefore, this.takenAfter}); - SearchDateFilter copyWith({ - DateTime? takenBefore, - DateTime? takenAfter, - }) { - return SearchDateFilter( - takenBefore: takenBefore ?? this.takenBefore, - takenAfter: takenAfter ?? this.takenAfter, - ); + SearchDateFilter copyWith({DateTime? takenBefore, DateTime? takenAfter}) { + return SearchDateFilter(takenBefore: takenBefore ?? this.takenBefore, takenAfter: takenAfter ?? this.takenAfter); } Map toMap() { @@ -167,17 +130,9 @@ class SearchDisplayFilters { bool isNotInAlbum = false; bool isArchive = false; bool isFavorite = false; - SearchDisplayFilters({ - required this.isNotInAlbum, - required this.isArchive, - required this.isFavorite, - }); + SearchDisplayFilters({required this.isNotInAlbum, required this.isArchive, required this.isFavorite}); - SearchDisplayFilters copyWith({ - bool? isNotInAlbum, - bool? isArchive, - bool? isFavorite, - }) { + SearchDisplayFilters copyWith({bool? isNotInAlbum, bool? isArchive, bool? isFavorite}) { return SearchDisplayFilters( isNotInAlbum: isNotInAlbum ?? this.isNotInAlbum, isArchive: isArchive ?? this.isArchive, @@ -186,11 +141,7 @@ class SearchDisplayFilters { } Map toMap() { - return { - 'isNotInAlbum': isNotInAlbum, - 'isArchive': isArchive, - 'isFavorite': isFavorite, - }; + return {'isNotInAlbum': isNotInAlbum, 'isArchive': isArchive, 'isFavorite': isFavorite}; } factory SearchDisplayFilters.fromMap(Map map) { diff --git a/mobile/lib/models/search/search_result.model.dart b/mobile/lib/models/search/search_result.model.dart index 458a9b4abc..02553869bf 100644 --- a/mobile/lib/models/search/search_result.model.dart +++ b/mobile/lib/models/search/search_result.model.dart @@ -6,19 +6,10 @@ class SearchResult { final List assets; final int? nextPage; - const SearchResult({ - required this.assets, - this.nextPage, - }); + const SearchResult({required this.assets, this.nextPage}); - SearchResult copyWith({ - List? assets, - int? nextPage, - }) { - return SearchResult( - assets: assets ?? this.assets, - nextPage: nextPage ?? this.nextPage, - ); + SearchResult copyWith({List? assets, int? nextPage}) { + return SearchResult(assets: assets ?? this.assets, nextPage: nextPage ?? this.nextPage); } @override diff --git a/mobile/lib/models/server_info/server_config.model.dart b/mobile/lib/models/server_info/server_config.model.dart index 88c27443c4..37b98afadb 100644 --- a/mobile/lib/models/server_info/server_config.model.dart +++ b/mobile/lib/models/server_info/server_config.model.dart @@ -15,11 +15,7 @@ class ServerConfig { required this.mapLightStyleUrl, }); - ServerConfig copyWith({ - int? trashDays, - String? oauthButtonText, - String? externalDomain, - }) { + ServerConfig copyWith({int? trashDays, String? oauthButtonText, String? externalDomain}) { return ServerConfig( trashDays: trashDays ?? this.trashDays, oauthButtonText: oauthButtonText ?? this.oauthButtonText, @@ -34,11 +30,11 @@ class ServerConfig { 'ServerConfig(trashDays: $trashDays, oauthButtonText: $oauthButtonText, externalDomain: $externalDomain)'; ServerConfig.fromDto(ServerConfigDto dto) - : trashDays = dto.trashDays, - oauthButtonText = dto.oauthButtonText, - externalDomain = dto.externalDomain, - mapDarkStyleUrl = dto.mapDarkStyleUrl, - mapLightStyleUrl = dto.mapLightStyleUrl; + : trashDays = dto.trashDays, + oauthButtonText = dto.oauthButtonText, + externalDomain = dto.externalDomain, + mapDarkStyleUrl = dto.mapDarkStyleUrl, + mapLightStyleUrl = dto.mapLightStyleUrl; @override bool operator ==(covariant ServerConfig other) { diff --git a/mobile/lib/models/server_info/server_disk_info.model.dart b/mobile/lib/models/server_info/server_disk_info.model.dart index 8248097ca5..01042b9f6d 100644 --- a/mobile/lib/models/server_info/server_disk_info.model.dart +++ b/mobile/lib/models/server_info/server_disk_info.model.dart @@ -13,12 +13,7 @@ class ServerDiskInfo { required this.diskUsagePercentage, }); - ServerDiskInfo copyWith({ - String? diskAvailable, - String? diskSize, - String? diskUse, - double? diskUsagePercentage, - }) { + ServerDiskInfo copyWith({String? diskAvailable, String? diskSize, String? diskUse, double? diskUsagePercentage}) { return ServerDiskInfo( diskAvailable: diskAvailable ?? this.diskAvailable, diskSize: diskSize ?? this.diskSize, @@ -33,10 +28,10 @@ class ServerDiskInfo { } ServerDiskInfo.fromDto(ServerStorageResponseDto dto) - : diskAvailable = dto.diskAvailable, - diskSize = dto.diskSize, - diskUse = dto.diskUse, - diskUsagePercentage = dto.diskUsagePercentage; + : diskAvailable = dto.diskAvailable, + diskSize = dto.diskSize, + diskUse = dto.diskUse, + diskUsagePercentage = dto.diskUsagePercentage; @override bool operator ==(Object other) { diff --git a/mobile/lib/models/server_info/server_features.model.dart b/mobile/lib/models/server_info/server_features.model.dart index 7e537ebf34..20b9f29619 100644 --- a/mobile/lib/models/server_info/server_features.model.dart +++ b/mobile/lib/models/server_info/server_features.model.dart @@ -13,12 +13,7 @@ class ServerFeatures { required this.passwordLogin, }); - ServerFeatures copyWith({ - bool? trash, - bool? map, - bool? oauthEnabled, - bool? passwordLogin, - }) { + ServerFeatures copyWith({bool? trash, bool? map, bool? oauthEnabled, bool? passwordLogin}) { return ServerFeatures( trash: trash ?? this.trash, map: map ?? this.map, @@ -33,10 +28,10 @@ class ServerFeatures { } ServerFeatures.fromDto(ServerFeaturesDto dto) - : trash = dto.trash, - map = dto.map, - oauthEnabled = dto.oauth, - passwordLogin = dto.passwordLogin; + : trash = dto.trash, + map = dto.map, + oauthEnabled = dto.oauth, + passwordLogin = dto.passwordLogin; @override bool operator ==(covariant ServerFeatures other) { diff --git a/mobile/lib/models/server_info/server_version.model.dart b/mobile/lib/models/server_info/server_version.model.dart index bd71536622..2cb41b0415 100644 --- a/mobile/lib/models/server_info/server_version.model.dart +++ b/mobile/lib/models/server_info/server_version.model.dart @@ -5,22 +5,10 @@ class ServerVersion { final int minor; final int patch; - const ServerVersion({ - required this.major, - required this.minor, - required this.patch, - }); + const ServerVersion({required this.major, required this.minor, required this.patch}); - ServerVersion copyWith({ - int? major, - int? minor, - int? patch, - }) { - return ServerVersion( - major: major ?? this.major, - minor: minor ?? this.minor, - patch: patch ?? this.patch, - ); + ServerVersion copyWith({int? major, int? minor, int? patch}) { + return ServerVersion(major: major ?? this.major, minor: minor ?? this.minor, patch: patch ?? this.patch); } @override @@ -28,10 +16,7 @@ class ServerVersion { return 'ServerVersion(major: $major, minor: $minor, patch: $patch)'; } - ServerVersion.fromDto(ServerVersionResponseDto dto) - : major = dto.major, - minor = dto.minor, - patch = dto.patch_; + ServerVersion.fromDto(ServerVersionResponseDto dto) : major = dto.major, minor = dto.minor, patch = dto.patch_; @override bool operator ==(Object other) { diff --git a/mobile/lib/models/shared_link/shared_link.model.dart b/mobile/lib/models/shared_link/shared_link.model.dart index 135d191b20..57a1f441eb 100644 --- a/mobile/lib/models/shared_link/shared_link.model.dart +++ b/mobile/lib/models/shared_link/shared_link.model.dart @@ -58,23 +58,23 @@ class SharedLink { } SharedLink.fromDto(SharedLinkResponseDto dto) - : id = dto.id, - allowDownload = dto.allowDownload, - allowUpload = dto.allowUpload, - description = dto.description, - password = dto.password, - expiresAt = dto.expiresAt, - key = dto.key, - showMetadata = dto.showMetadata, - type = dto.type == SharedLinkType.ALBUM ? SharedLinkSource.album : SharedLinkSource.individual, - title = dto.type == SharedLinkType.ALBUM - ? dto.album?.albumName.toUpperCase() ?? "UNKNOWN SHARE" - : "INDIVIDUAL SHARE", - thumbAssetId = dto.type == SharedLinkType.ALBUM - ? dto.album?.albumThumbnailAssetId - : dto.assets.isNotEmpty - ? dto.assets[0].id - : null; + : id = dto.id, + allowDownload = dto.allowDownload, + allowUpload = dto.allowUpload, + description = dto.description, + password = dto.password, + expiresAt = dto.expiresAt, + key = dto.key, + showMetadata = dto.showMetadata, + type = dto.type == SharedLinkType.ALBUM ? SharedLinkSource.album : SharedLinkSource.individual, + title = dto.type == SharedLinkType.ALBUM + ? dto.album?.albumName.toUpperCase() ?? "UNKNOWN SHARE" + : "INDIVIDUAL SHARE", + thumbAssetId = dto.type == SharedLinkType.ALBUM + ? dto.album?.albumThumbnailAssetId + : dto.assets.isNotEmpty + ? dto.assets[0].id + : null; @override String toString() => diff --git a/mobile/lib/models/upload/share_intent_attachment.model.dart b/mobile/lib/models/upload/share_intent_attachment.model.dart index 61157f3674..ae05e4c492 100644 --- a/mobile/lib/models/upload/share_intent_attachment.model.dart +++ b/mobile/lib/models/upload/share_intent_attachment.model.dart @@ -5,21 +5,9 @@ import 'dart:io'; import 'package:immich_mobile/utils/bytes_units.dart'; import 'package:path/path.dart'; -enum ShareIntentAttachmentType { - image, - video, -} +enum ShareIntentAttachmentType { image, video } -enum UploadStatus { - enqueued, - running, - complete, - notFound, - failed, - canceled, - waitingToRetry, - paused, -} +enum UploadStatus { enqueued, running, complete, notFound, failed, canceled, waitingToRetry, paused } class ShareIntentAttachment { final String path; @@ -90,9 +78,8 @@ class ShareIntentAttachment { String toJson() => json.encode(toMap()); - factory ShareIntentAttachment.fromJson(String source) => ShareIntentAttachment.fromMap( - json.decode(source) as Map, - ); + factory ShareIntentAttachment.fromJson(String source) => + ShareIntentAttachment.fromMap(json.decode(source) as Map); @override String toString() { diff --git a/mobile/lib/pages/album/album_additional_shared_user_selection.page.dart b/mobile/lib/pages/album/album_additional_shared_user_selection.page.dart index c0fc8c9364..f40ac9ccae 100644 --- a/mobile/lib/pages/album/album_additional_shared_user_selection.page.dart +++ b/mobile/lib/pages/album/album_additional_shared_user_selection.page.dart @@ -14,10 +14,7 @@ import 'package:immich_mobile/widgets/common/user_circle_avatar.dart'; class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget { final Album album; - const AlbumAdditionalSharedUserSelectionPage({ - super.key, - required this.album, - }); + const AlbumAdditionalSharedUserSelectionPage({super.key, required this.album}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -30,17 +27,9 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget { buildTileIcon(UserDto user) { if (sharedUsersList.value.contains(user)) { - return CircleAvatar( - backgroundColor: context.primaryColor, - child: const Icon( - Icons.check_rounded, - size: 25, - ), - ); + return CircleAvatar(backgroundColor: context.primaryColor, child: const Icon(Icons.check_rounded, size: 25)); } else { - return UserCircleAvatar( - user: user, - ); + return UserCircleAvatar(user: user); } } @@ -53,31 +42,19 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 8.0), child: Chip( backgroundColor: context.primaryColor.withValues(alpha: 0.15), - label: Text( - user.name, - style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - ), - ), + label: Text(user.name, style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold)), ), ), ); } return ListView( children: [ - Wrap( - children: [...usersChip], - ), + Wrap(children: [...usersChip]), Padding( padding: const EdgeInsets.all(16.0), child: Text( 'suggestions'.tr(), - style: const TextStyle( - fontSize: 14, - color: Colors.grey, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(fontSize: 14, color: Colors.grey, fontWeight: FontWeight.bold), ), ), ListView.builder( @@ -87,31 +64,15 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget { return ListTile( leading: buildTileIcon(users[index]), dense: true, - title: Text( - users[index].name, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ), - subtitle: Text( - users[index].email, - style: const TextStyle( - fontSize: 12, - ), - ), + title: Text(users[index].name, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), + subtitle: Text(users[index].email, style: const TextStyle(fontSize: 12)), onTap: () { if (sharedUsersList.value.contains(users[index])) { sharedUsersList.value = sharedUsersList.value - .where( - (selectedUser) => selectedUser.id != users[index].id, - ) + .where((selectedUser) => selectedUser.id != users[index].id) .toSet(); } else { - sharedUsersList.value = { - ...sharedUsersList.value, - users[index], - }; + sharedUsersList.value = {...sharedUsersList.value, users[index]}; } }, ); @@ -124,9 +85,7 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( - title: const Text( - 'invite_to_album', - ).tr(), + title: const Text('invite_to_album').tr(), elevation: 0, centerTitle: false, leading: IconButton( @@ -138,19 +97,14 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget { actions: [ TextButton( onPressed: sharedUsersList.value.isEmpty ? null : addNewUsersHandler, - child: const Text( - "add", - style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ).tr(), + child: const Text("add", style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)).tr(), ), ], ), body: suggestedShareUsers.widgetWhen( onData: (users) { for (var sharedUsers in album.sharedUsers) { - users.removeWhere( - (u) => u.id == sharedUsers.id || u.id == album.ownerId, - ); + users.removeWhere((u) => u.id == sharedUsers.id || u.id == album.ownerId); } return buildUserList(users); diff --git a/mobile/lib/pages/album/album_asset_selection.page.dart b/mobile/lib/pages/album/album_asset_selection.page.dart index e72ecb0712..ccc4c44d43 100644 --- a/mobile/lib/pages/album/album_asset_selection.page.dart +++ b/mobile/lib/pages/album/album_asset_selection.page.dart @@ -13,11 +13,7 @@ import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart'; @RoutePage() class AlbumAssetSelectionPage extends HookConsumerWidget { - const AlbumAssetSelectionPage({ - super.key, - required this.existingAssets, - this.canDeselect = false, - }); + const AlbumAssetSelectionPage({super.key, required this.existingAssets, this.canDeselect = false}); final Set existingAssets; final bool canDeselect; @@ -52,10 +48,7 @@ class AlbumAssetSelectionPage extends HookConsumerWidget { }, ), title: selected.value.isEmpty - ? const Text( - 'add_photos', - style: TextStyle(fontSize: 18), - ).tr() + ? const Text('add_photos', style: TextStyle(fontSize: 18)).tr() : const Text( 'share_assets_selected', style: TextStyle(fontSize: 18), @@ -70,17 +63,12 @@ class AlbumAssetSelectionPage extends HookConsumerWidget { }, child: Text( canDeselect ? "done" : "add", - style: TextStyle( - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), + style: TextStyle(fontWeight: FontWeight.bold, color: context.primaryColor), ).tr(), ), ], ), - body: assetSelectionRenderList.widgetWhen( - onData: (data) => buildBody(data), - ), + body: assetSelectionRenderList.widgetWhen(onData: (data) => buildBody(data)), ); } } diff --git a/mobile/lib/pages/album/album_control_button.dart b/mobile/lib/pages/album/album_control_button.dart index c453ace618..578eb839a0 100644 --- a/mobile/lib/pages/album/album_control_button.dart +++ b/mobile/lib/pages/album/album_control_button.dart @@ -7,11 +7,7 @@ class AlbumControlButton extends ConsumerWidget { final void Function()? onAddPhotosPressed; final void Function()? onAddUsersPressed; - const AlbumControlButton({ - super.key, - this.onAddPhotosPressed, - this.onAddUsersPressed, - }); + const AlbumControlButton({super.key, this.onAddPhotosPressed, this.onAddUsersPressed}); @override Widget build(BuildContext context, WidgetRef ref) { diff --git a/mobile/lib/pages/album/album_date_range.dart b/mobile/lib/pages/album/album_date_range.dart index 57d808f226..dbfd9214f1 100644 --- a/mobile/lib/pages/album/album_date_range.dart +++ b/mobile/lib/pages/album/album_date_range.dart @@ -33,9 +33,7 @@ class AlbumDateRange extends ConsumerWidget { padding: const EdgeInsets.only(left: 16.0), child: Text( _getDateRangeText(startDate, endDate), - style: context.textTheme.labelLarge?.copyWith( - color: context.colorScheme.onSurfaceVariant, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.onSurfaceVariant), ), ); } @@ -46,8 +44,9 @@ class AlbumDateRange extends ConsumerWidget { return DateFormat.yMMMd().format(startDate); } - final String startDateText = - (startDate.year == endDate.year ? DateFormat.MMMd() : DateFormat.yMMMd()).format(startDate); + final String startDateText = (startDate.year == endDate.year ? DateFormat.MMMd() : DateFormat.yMMMd()).format( + startDate, + ); final String endDateText = DateFormat.yMMMd().format(endDate); return "$startDateText - $endDateText"; } diff --git a/mobile/lib/pages/album/album_description.dart b/mobile/lib/pages/album/album_description.dart index 37c5beb2c2..383367e8b7 100644 --- a/mobile/lib/pages/album/album_description.dart +++ b/mobile/lib/pages/album/album_description.dart @@ -36,10 +36,7 @@ class AlbumDescription extends ConsumerWidget { return Padding( padding: const EdgeInsets.only(left: 16, right: 8), - child: Text( - albumDescription ?? 'add_a_description'.tr(), - style: context.textTheme.bodyLarge, - ), + child: Text(albumDescription ?? 'add_a_description'.tr(), style: context.textTheme.bodyLarge), ); } } diff --git a/mobile/lib/pages/album/album_options.page.dart b/mobile/lib/pages/album/album_options.page.dart index 4c51093345..e659340f26 100644 --- a/mobile/lib/pages/album/album_options.page.dart +++ b/mobile/lib/pages/album/album_options.page.dart @@ -51,9 +51,7 @@ class AlbumOptionsPage extends HookConsumerWidget { final isSuccess = await ref.read(albumProvider.notifier).leaveAlbum(album); if (isSuccess) { - context.navigateTo( - const TabControllerRoute(children: [AlbumsRoute()]), - ); + context.navigateTo(const TabControllerRoute(children: [AlbumsRoute()])); } else { showErrorMessage(); } @@ -110,10 +108,7 @@ class AlbumOptionsPage extends HookConsumerWidget { return SafeArea( child: Padding( padding: const EdgeInsets.only(top: 24.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [...actions], - ), + child: Column(mainAxisSize: MainAxisSize.min, children: [...actions]), ), ); }, @@ -123,20 +118,9 @@ class AlbumOptionsPage extends HookConsumerWidget { buildOwnerInfo() { return ListTile( leading: owner != null ? UserCircleAvatar(user: owner.toDto()) : const SizedBox(), - title: Text( - album.owner.value?.name ?? "", - style: const TextStyle( - fontWeight: FontWeight.w500, - ), - ), - subtitle: Text( - album.owner.value?.email ?? "", - style: TextStyle(color: context.colorScheme.onSurfaceSecondary), - ), - trailing: Text( - "owner", - style: context.textTheme.labelLarge, - ).tr(), + title: Text(album.owner.value?.name ?? "", style: const TextStyle(fontWeight: FontWeight.w500)), + subtitle: Text(album.owner.value?.email ?? "", style: TextStyle(color: context.colorScheme.onSurfaceSecondary)), + trailing: Text("owner", style: context.textTheme.labelLarge).tr(), ); } @@ -148,22 +132,9 @@ class AlbumOptionsPage extends HookConsumerWidget { itemBuilder: (context, index) { final user = sharedUsers.value[index]; return ListTile( - leading: UserCircleAvatar( - user: user, - radius: 22, - ), - title: Text( - user.name, - style: const TextStyle( - fontWeight: FontWeight.w500, - ), - ), - subtitle: Text( - user.email, - style: TextStyle( - color: context.colorScheme.onSurfaceSecondary, - ), - ), + leading: UserCircleAvatar(user: user, radius: 22), + title: Text(user.name, style: const TextStyle(fontWeight: FontWeight.w500)), + subtitle: Text(user.email, style: TextStyle(color: context.colorScheme.onSurfaceSecondary)), trailing: userId == user.id || isOwner ? const Icon(Icons.more_horiz_rounded) : const SizedBox(), onTap: userId == user.id || isOwner ? () => handleUserClick(user) : null, ); @@ -206,9 +177,7 @@ class AlbumOptionsPage extends HookConsumerWidget { ).tr(), subtitle: Text( "let_others_respond", - style: context.textTheme.labelLarge?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.onSurfaceSecondary), ).tr(), ), buildSectionTitle("shared_album_section_people_title".tr()), diff --git a/mobile/lib/pages/album/album_shared_user_icons.dart b/mobile/lib/pages/album/album_shared_user_icons.dart index 723bb1e252..fe1823ec61 100644 --- a/mobile/lib/pages/album/album_shared_user_icons.dart +++ b/mobile/lib/pages/album/album_shared_user_icons.dart @@ -41,11 +41,7 @@ class AlbumSharedUserIcons extends HookConsumerWidget { itemBuilder: ((context, index) { return Padding( padding: const EdgeInsets.only(right: 8.0), - child: UserCircleAvatar( - user: sharedUsers.value[index], - radius: 18, - size: 36, - ), + child: UserCircleAvatar(user: sharedUsers.value[index], radius: 18, size: 36), ); }), itemCount: sharedUsers.value.length, diff --git a/mobile/lib/pages/album/album_shared_user_selection.page.dart b/mobile/lib/pages/album/album_shared_user_selection.page.dart index d5d963b206..562f02a2ab 100644 --- a/mobile/lib/pages/album/album_shared_user_selection.page.dart +++ b/mobile/lib/pages/album/album_shared_user_selection.page.dart @@ -25,10 +25,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { final suggestedShareUsers = ref.watch(otherUsersProvider); createSharedAlbum() async { - var newAlbum = await ref.watch(albumProvider.notifier).createAlbum( - ref.watch(albumTitleProvider), - assets, - ); + var newAlbum = await ref.watch(albumProvider.notifier).createAlbum(ref.watch(albumTitleProvider), assets); if (newAlbum != null) { ref.watch(albumTitleProvider.notifier).clearAlbumTitle(); @@ -40,9 +37,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { child: SnackBar( content: Text( 'select_user_for_sharing_page_err_album', - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ).tr(), ), ); @@ -50,17 +45,9 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { buildTileIcon(UserDto user) { if (sharedUsersList.value.contains(user)) { - return CircleAvatar( - backgroundColor: context.primaryColor, - child: const Icon( - Icons.check_rounded, - size: 25, - ), - ); + return CircleAvatar(backgroundColor: context.primaryColor, child: const Icon(Icons.check_rounded, size: 25)); } else { - return UserCircleAvatar( - user: user, - ); + return UserCircleAvatar(user: user); } } @@ -75,11 +62,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { backgroundColor: context.primaryColor.withValues(alpha: 0.15), label: Text( user.email, - style: const TextStyle( - fontSize: 12, - color: Colors.black87, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(fontSize: 12, color: Colors.black87, fontWeight: FontWeight.bold), ), ), ), @@ -87,18 +70,12 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { } return ListView( children: [ - Wrap( - children: [...usersChip], - ), + Wrap(children: [...usersChip]), Padding( padding: const EdgeInsets.all(16.0), child: const Text( 'suggestions', - style: TextStyle( - fontSize: 14, - color: Colors.grey, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 14, color: Colors.grey, fontWeight: FontWeight.bold), ).tr(), ), ListView.builder( @@ -107,25 +84,14 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { itemBuilder: ((context, index) { return ListTile( leading: buildTileIcon(users[index]), - title: Text( - users[index].email, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ), + title: Text(users[index].email, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), onTap: () { if (sharedUsersList.value.contains(users[index])) { sharedUsersList.value = sharedUsersList.value - .where( - (selectedUser) => selectedUser.id != users[index].id, - ) + .where((selectedUser) => selectedUser.id != users[index].id) .toSet(); } else { - sharedUsersList.value = { - ...sharedUsersList.value, - users[index], - }; + sharedUsersList.value = {...sharedUsersList.value, users[index]}; } }, ); @@ -138,10 +104,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( - title: Text( - 'invite_to_album', - style: TextStyle(color: context.primaryColor), - ).tr(), + title: Text('invite_to_album', style: TextStyle(color: context.primaryColor)).tr(), elevation: 0, centerTitle: false, leading: IconButton( @@ -152,9 +115,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { ), actions: [ TextButton( - style: TextButton.styleFrom( - foregroundColor: context.primaryColor, - ), + style: TextButton.styleFrom(foregroundColor: context.primaryColor), onPressed: sharedUsersList.value.isEmpty ? null : createSharedAlbum, child: const Text( "create_album", diff --git a/mobile/lib/pages/album/album_title.dart b/mobile/lib/pages/album/album_title.dart index ccea200f3a..6c7fc3faaa 100644 --- a/mobile/lib/pages/album/album_title.dart +++ b/mobile/lib/pages/album/album_title.dart @@ -19,32 +19,20 @@ class AlbumTitle extends ConsumerWidget { return const (false, false, ''); } - return ( - album.ownerId == userId, - album.isRemote, - album.name, - ); + return (album.ownerId == userId, album.isRemote, album.name); }), ); if (isOwner && isRemote) { return Padding( padding: const EdgeInsets.only(left: 8, right: 8), - child: AlbumViewerEditableTitle( - albumName: albumName, - titleFocusNode: titleFocusNode, - ), + child: AlbumViewerEditableTitle(albumName: albumName, titleFocusNode: titleFocusNode), ); } return Padding( padding: const EdgeInsets.only(left: 16, right: 8), - child: Text( - albumName, - style: context.textTheme.headlineLarge?.copyWith( - fontWeight: FontWeight.w700, - ), - ), + child: Text(albumName, style: context.textTheme.headlineLarge?.copyWith(fontWeight: FontWeight.w700)), ); } } diff --git a/mobile/lib/pages/album/album_viewer.dart b/mobile/lib/pages/album/album_viewer.dart index 6d9519f4ed..97853fb96a 100644 --- a/mobile/lib/pages/album/album_viewer.dart +++ b/mobile/lib/pages/album/album_viewer.dart @@ -65,10 +65,7 @@ class AlbumViewer extends HookConsumerWidget { /// If they exist, add to selected asset state to show they are already selected. void onAddPhotosPressed() async { AssetSelectionPageResult? returnPayload = await context.pushRoute( - AlbumAssetSelectionRoute( - existingAssets: album.assets, - canDeselect: false, - ), + AlbumAssetSelectionRoute(existingAssets: album.assets, canDeselect: false), ); if (returnPayload != null && returnPayload.selectedAssets.isNotEmpty) { @@ -98,9 +95,7 @@ class AlbumViewer extends HookConsumerWidget { onActivitiesPressed() { if (album.remoteId != null) { ref.read(currentAssetProvider.notifier).set(null); - context.pushRoute( - const ActivitiesRoute(), - ); + context.pushRoute(const ActivitiesRoute()); } } @@ -129,14 +124,8 @@ class AlbumViewer extends HookConsumerWidget { children: [ const SizedBox(height: 32), const AlbumDateRange(), - AlbumTitle( - key: const ValueKey("albumTitle"), - titleFocusNode: titleFocusNode, - ), - AlbumDescription( - key: const ValueKey("albumDescription"), - descriptionFocusNode: descriptionFocusNode, - ), + AlbumTitle(key: const ValueKey("albumTitle"), titleFocusNode: titleFocusNode), + AlbumDescription(key: const ValueKey("albumDescription"), descriptionFocusNode: descriptionFocusNode), const AlbumSharedUserIcons(), if (album.isRemote) Padding( diff --git a/mobile/lib/pages/album/album_viewer.page.dart b/mobile/lib/pages/album/album_viewer.page.dart index 146a93a0a6..c99dacd9b7 100644 --- a/mobile/lib/pages/album/album_viewer.page.dart +++ b/mobile/lib/pages/album/album_viewer.page.dart @@ -21,9 +21,7 @@ class AlbumViewerPage extends HookConsumerWidget { ref.listen(assetSelectionTimelineProvider, (_, __) {}); ref.listen(albumWatcher(albumId), (_, albumFuture) { - albumFuture.whenData( - (value) => ref.read(currentAlbumProvider.notifier).set(value), - ); + albumFuture.whenData((value) => ref.read(currentAlbumProvider.notifier).set(value)); }); return const Scaffold(body: AlbumViewer()); diff --git a/mobile/lib/pages/albums/albums.page.dart b/mobile/lib/pages/albums/albums.page.dart index aea4cfa2b8..5f155c2f0d 100644 --- a/mobile/lib/pages/albums/albums.page.dart +++ b/mobile/lib/pages/albums/albums.page.dart @@ -52,21 +52,18 @@ class AlbumsPage extends HookConsumerWidget { filterMode.value = mode; } - useEffect( - () { - searchController.addListener(() { + useEffect(() { + searchController.addListener(() { + onSearch(searchController.text, filterMode.value); + }); + + return () { + searchController.removeListener(() { onSearch(searchController.text, filterMode.value); }); - - return () { - searchController.removeListener(() { - onSearch(searchController.text, filterMode.value); - }); - debounceTimer.value?.cancel(); - }; - }, - [], - ); + debounceTimer.value?.cancel(); + }; + }, []); clearSearch() { filterMode.value = QuickFilterMode.all; @@ -79,13 +76,8 @@ class AlbumsPage extends HookConsumerWidget { showUploadButton: false, actions: [ IconButton( - icon: const Icon( - Icons.add_rounded, - size: 28, - ), - onPressed: () => context.pushRoute( - CreateAlbumRoute(), - ), + icon: const Icon(Icons.add_rounded, size: 28), + onPressed: () => context.pushRoute(CreateAlbumRoute()), ), ], ), @@ -100,13 +92,8 @@ class AlbumsPage extends HookConsumerWidget { children: [ Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(0), - width: 0, - ), - borderRadius: const BorderRadius.all( - Radius.circular(24), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(0), width: 0), + borderRadius: const BorderRadius.all(Radius.circular(24)), gradient: LinearGradient( colors: [ context.colorScheme.primary.withValues(alpha: 0.075), @@ -124,10 +111,7 @@ class AlbumsPage extends HookConsumerWidget { hintText: 'search_albums'.tr(), prefixIcon: const Icon(Icons.search_rounded), suffixIcon: searchController.text.isNotEmpty - ? IconButton( - icon: const Icon(Icons.clear_rounded), - onPressed: clearSearch, - ) + ? IconButton(icon: const Icon(Icons.clear_rounded), onPressed: clearSearch) : null, controller: searchController, onChanged: (_) => onSearch(searchController.text, filterMode.value), @@ -153,10 +137,7 @@ class AlbumsPage extends HookConsumerWidget { isSelected: filterMode.value == QuickFilterMode.sharedWithMe, onTap: () { changeFilter(QuickFilterMode.sharedWithMe); - onSearch( - searchController.text, - QuickFilterMode.sharedWithMe, - ); + onSearch(searchController.text, QuickFilterMode.sharedWithMe); }, ), QuickFilterButton( @@ -164,10 +145,7 @@ class AlbumsPage extends HookConsumerWidget { isSelected: filterMode.value == QuickFilterMode.myAlbums, onTap: () { changeFilter(QuickFilterMode.myAlbums); - onSearch( - searchController.text, - QuickFilterMode.myAlbums, - ); + onSearch(searchController.text, QuickFilterMode.myAlbums); }, ), ], @@ -177,10 +155,7 @@ class AlbumsPage extends HookConsumerWidget { children: [ const SortButton(), IconButton( - icon: Icon( - isGrid.value ? Icons.view_list_outlined : Icons.grid_view_outlined, - size: 24, - ), + icon: Icon(isGrid.value ? Icons.view_list_outlined : Icons.grid_view_outlined, size: 24), onPressed: toggleViewMode, ), ], @@ -201,9 +176,7 @@ class AlbumsPage extends HookConsumerWidget { itemBuilder: (context, index) { return AlbumThumbnailCard( album: sorted[index], - onTap: () => context.pushRoute( - AlbumViewerRoute(albumId: sorted[index].id), - ), + onTap: () => context.pushRoute(AlbumViewerRoute(albumId: sorted[index].id)), showOwner: true, ); }, @@ -221,44 +194,22 @@ class AlbumsPage extends HookConsumerWidget { sorted[index].name, maxLines: 2, overflow: TextOverflow.ellipsis, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), ), subtitle: sorted[index].ownerId != null ? Text( - '${'items_count'.t( - context: context, - args: { - 'count': sorted[index].assetCount, - }, - )} • ${sorted[index].ownerId != userId ? 'shared_by_user'.t( - context: context, - args: { - 'user': sorted[index].ownerName!, - }, - ) : 'owned'.t(context: context)}', + '${'items_count'.t(context: context, args: {'count': sorted[index].assetCount})} • ${sorted[index].ownerId != userId ? 'shared_by_user'.t(context: context, args: {'user': sorted[index].ownerName!}) : 'owned'.t(context: context)}', overflow: TextOverflow.ellipsis, style: context.textTheme.bodyMedium?.copyWith( color: context.colorScheme.onSurfaceSecondary, ), ) : null, - onTap: () => context.pushRoute( - AlbumViewerRoute(albumId: sorted[index].id), - ), - leadingPadding: const EdgeInsets.only( - right: 16, - ), + onTap: () => context.pushRoute(AlbumViewerRoute(albumId: sorted[index].id)), + leadingPadding: const EdgeInsets.only(right: 16), leading: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(15), - ), - child: ImmichThumbnail( - asset: sorted[index].thumbnail.value, - width: 80, - height: 80, - ), + borderRadius: const BorderRadius.all(Radius.circular(15)), + child: ImmichThumbnail(asset: sorted[index].thumbnail.value, width: 80, height: 80), ), // minVerticalPadding: 1, ), @@ -275,12 +226,7 @@ class AlbumsPage extends HookConsumerWidget { } class QuickFilterButton extends StatelessWidget { - const QuickFilterButton({ - super.key, - required this.isSelected, - required this.onTap, - required this.label, - }); + const QuickFilterButton({super.key, required this.isSelected, required this.onTap, required this.label}); final bool isSelected; final VoidCallback onTap; @@ -291,18 +237,11 @@ class QuickFilterButton extends StatelessWidget { return TextButton( onPressed: onTap, style: ButtonStyle( - backgroundColor: WidgetStateProperty.all( - isSelected ? context.colorScheme.primary : Colors.transparent, - ), + backgroundColor: WidgetStateProperty.all(isSelected ? context.colorScheme.primary : Colors.transparent), shape: WidgetStateProperty.all( RoundedRectangleBorder( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), - side: BorderSide( - color: context.colorScheme.onSurface.withAlpha(25), - width: 1, - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), + side: BorderSide(color: context.colorScheme.onSurface.withAlpha(25), width: 1), ), ), ), @@ -329,15 +268,9 @@ class SortButton extends ConsumerWidget { style: MenuStyle( elevation: const WidgetStatePropertyAll(1), shape: WidgetStateProperty.all( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(24), - ), - ), - ), - padding: const WidgetStatePropertyAll( - EdgeInsets.all(4), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), ), + padding: const WidgetStatePropertyAll(EdgeInsets.all(4)), ), consumeOutsideTap: true, menuChildren: AlbumSortMode.values @@ -345,16 +278,18 @@ class SortButton extends ConsumerWidget { (mode) => MenuItemButton( leadingIcon: albumSortOption == mode ? albumSortIsReverse - ? Icon( - Icons.keyboard_arrow_down, - color: - albumSortOption == mode ? context.colorScheme.onPrimary : context.colorScheme.onSurface, - ) - : Icon( - Icons.keyboard_arrow_up_rounded, - color: - albumSortOption == mode ? context.colorScheme.onPrimary : context.colorScheme.onSurface, - ) + ? Icon( + Icons.keyboard_arrow_down, + color: albumSortOption == mode + ? context.colorScheme.onPrimary + : context.colorScheme.onSurface, + ) + : Icon( + Icons.keyboard_arrow_up_rounded, + color: albumSortOption == mode + ? context.colorScheme.onPrimary + : context.colorScheme.onSurface, + ) : const Icon(Icons.abc, color: Colors.transparent), onPressed: () { final selected = albumSortOption == mode; @@ -366,18 +301,12 @@ class SortButton extends ConsumerWidget { } }, style: ButtonStyle( - padding: WidgetStateProperty.all( - const EdgeInsets.fromLTRB(16, 16, 32, 16), - ), + padding: WidgetStateProperty.all(const EdgeInsets.fromLTRB(16, 16, 32, 16)), backgroundColor: WidgetStateProperty.all( albumSortOption == mode ? context.colorScheme.primary : Colors.transparent, ), shape: WidgetStateProperty.all( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(24), - ), - ), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), ), ), child: Text( diff --git a/mobile/lib/pages/backup/album_preview.page.dart b/mobile/lib/pages/backup/album_preview.page.dart index 4cdc180973..def31afcd4 100644 --- a/mobile/lib/pages/backup/album_preview.page.dart +++ b/mobile/lib/pages/backup/album_preview.page.dart @@ -22,23 +22,17 @@ class AlbumPreviewPage extends HookConsumerWidget { assets.value = await ref.read(albumMediaRepositoryProvider).getAssets(album.localId!); } - useEffect( - () { - getAssetsInAlbum(); - return null; - }, - [], - ); + useEffect(() { + getAssetsInAlbum(); + return null; + }, []); return Scaffold( appBar: AppBar( elevation: 0, title: Column( children: [ - Text( - album.name, - style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ), + Text(album.name, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), Padding( padding: const EdgeInsets.only(top: 4.0), child: Text( @@ -52,10 +46,7 @@ class AlbumPreviewPage extends HookConsumerWidget { ), ], ), - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_new_rounded), - ), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_new_rounded)), ), body: GridView.builder( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( @@ -65,11 +56,7 @@ class AlbumPreviewPage extends HookConsumerWidget { ), itemCount: assets.value.length, itemBuilder: (context, index) { - return ImmichThumbnail( - asset: assets.value[index], - width: 100, - height: 100, - ); + return ImmichThumbnail(asset: assets.value[index], width: 100, height: 100); }, ), ); diff --git a/mobile/lib/pages/backup/backup_album_selection.page.dart b/mobile/lib/pages/backup/backup_album_selection.page.dart index 69e118cb78..d222211577 100644 --- a/mobile/lib/pages/backup/backup_album_selection.page.dart +++ b/mobile/lib/pages/backup/backup_album_selection.page.dart @@ -23,45 +23,29 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { final isDarkTheme = context.isDarkTheme; final albums = ref.watch(backupProvider).availableAlbums; - useEffect( - () { - ref.watch(backupProvider.notifier).getBackupInfo(); - return null; - }, - [], - ); + useEffect(() { + ref.watch(backupProvider.notifier).getBackupInfo(); + return null; + }, []); buildAlbumSelectionList() { if (albums.isEmpty) { - return const SliverToBoxAdapter( - child: Center( - child: CircularProgressIndicator(), - ), - ); + return const SliverToBoxAdapter(child: Center(child: CircularProgressIndicator())); } return SliverPadding( padding: const EdgeInsets.symmetric(vertical: 12.0), sliver: SliverList( - delegate: SliverChildBuilderDelegate( - ((context, index) { - return AlbumInfoListTile( - album: albums[index], - ); - }), - childCount: albums.length, - ), + delegate: SliverChildBuilderDelegate(((context, index) { + return AlbumInfoListTile(album: albums[index]); + }), childCount: albums.length), ), ); } buildAlbumSelectionGrid() { if (albums.isEmpty) { - return const SliverToBoxAdapter( - child: Center( - child: CircularProgressIndicator(), - ), - ); + return const SliverToBoxAdapter(child: Center(child: CircularProgressIndicator())); } return SliverPadding( @@ -74,9 +58,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { ), itemCount: albums.length, itemBuilder: ((context, index) { - return AlbumInfoCard( - album: albums[index], - ); + return AlbumInfoCard(album: albums[index]); }), ), ); @@ -101,10 +83,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { ), backgroundColor: context.primaryColor, deleteIconColor: isDarkTheme ? Colors.black : Colors.white, - deleteIcon: const Icon( - Icons.cancel_rounded, - size: 15, - ), + deleteIcon: const Icon(Icons.cancel_rounded, size: 15), onDeleted: removeSelection, ), ), @@ -125,18 +104,11 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { child: Chip( label: Text( album.name, - style: TextStyle( - fontSize: 12, - color: context.scaffoldBackgroundColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 12, color: context.scaffoldBackgroundColor, fontWeight: FontWeight.bold), ), backgroundColor: Colors.red[300], deleteIconColor: context.scaffoldBackgroundColor, - deleteIcon: const Icon( - Icons.cancel_rounded, - size: 15, - ), + deleteIcon: const Icon(Icons.cancel_rounded, size: 15), onDeleted: removeSelection, ), ), @@ -155,13 +127,8 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), - title: const Text( - "backup_album_selection_page_select_albums", - ).tr(), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), + title: const Text("backup_album_selection_page_select_albums").tr(), elevation: 0, ), body: CustomScrollView( @@ -172,25 +139,14 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( - padding: const EdgeInsets.symmetric( - vertical: 8.0, - horizontal: 16.0, - ), - child: Text( - "backup_album_selection_page_selection_info", - style: context.textTheme.titleSmall, - ).tr(), + padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), + child: Text("backup_album_selection_page_selection_info", style: context.textTheme.titleSmall).tr(), ), - // Selected Album Chips + // Selected Album Chips Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Wrap( - children: [ - ...buildSelectedAlbumNameChip(), - ...buildExcludedAlbumNameChip(), - ], - ), + child: Wrap(children: [...buildSelectedAlbumNameChip(), ...buildExcludedAlbumNameChip()]), ), SettingsSwitchListTile( @@ -198,21 +154,15 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { title: "sync_albums".tr(), subtitle: "sync_upload_album_setting_subtitle".tr(), contentPadding: const EdgeInsets.symmetric(horizontal: 16), - titleStyle: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.bold, - ), - subtitleStyle: context.textTheme.labelLarge?.copyWith( - color: context.colorScheme.primary, - ), + titleStyle: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.bold), + subtitleStyle: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.primary), onChanged: handleSyncAlbumToggle, ), ListTile( title: Text( "backup_album_selection_page_albums_device".tr( - namedArgs: { - 'count': ref.watch(backupProvider).availableAlbums.length.toString(), - }, + namedArgs: {'count': ref.watch(backupProvider).availableAlbums.length.toString()}, ), style: context.textTheme.titleSmall, ), @@ -220,46 +170,30 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { padding: const EdgeInsets.symmetric(vertical: 8.0), child: Text( "backup_album_selection_page_albums_tap", - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), ).tr(), ), trailing: IconButton( splashRadius: 16, - icon: Icon( - Icons.info, - size: 20, - color: context.primaryColor, - ), + icon: Icon(Icons.info, size: 20, color: context.primaryColor), onPressed: () { // show the dialog showDialog( context: context, builder: (BuildContext context) { return AlertDialog( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(10), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), elevation: 5, title: Text( 'backup_album_selection_page_selection_info', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: context.primaryColor), ).tr(), content: SingleChildScrollView( child: ListBody( children: [ const Text( 'backup_album_selection_page_assets_scatter', - style: TextStyle( - fontSize: 14, - ), + style: TextStyle(fontSize: 14), ).tr(), ], ), diff --git a/mobile/lib/pages/backup/backup_controller.page.dart b/mobile/lib/pages/backup/backup_controller.page.dart index 76a772884e..093ff952ae 100644 --- a/mobile/lib/pages/backup/backup_controller.page.dart +++ b/mobile/lib/pages/backup/backup_controller.page.dart @@ -31,52 +31,44 @@ class BackupControllerPage extends HookConsumerWidget { final didGetBackupInfo = useState(false); bool hasExclusiveAccess = backupState.backupProgress != BackUpProgressEnum.inBackground; - bool shouldBackup = backupState.allUniqueAssets.length - backupState.selectedAlbumsBackupAssetsIds.length == 0 || + bool shouldBackup = + backupState.allUniqueAssets.length - backupState.selectedAlbumsBackupAssetsIds.length == 0 || !hasExclusiveAccess ? false : true; - useEffect( - () { - // Update the background settings information just to make sure we - // have the latest, since the platform channel will not update - // automatically - if (Platform.isIOS) { - ref.watch(iOSBackgroundSettingsProvider.notifier).refresh(); - } + useEffect(() { + // Update the background settings information just to make sure we + // have the latest, since the platform channel will not update + // automatically + if (Platform.isIOS) { + ref.watch(iOSBackgroundSettingsProvider.notifier).refresh(); + } - ref.watch(websocketProvider.notifier).stopListenToEvent('on_upload_success'); + ref.watch(websocketProvider.notifier).stopListenToEvent('on_upload_success'); - return () { - WakelockPlus.disable(); - }; - }, - [], - ); + return () { + WakelockPlus.disable(); + }; + }, []); - useEffect( - () { - if (backupState.backupProgress == BackUpProgressEnum.idle && !didGetBackupInfo.value) { - ref.watch(backupProvider.notifier).getBackupInfo(); - didGetBackupInfo.value = true; - } - return null; - }, - [backupState.backupProgress], - ); + useEffect(() { + if (backupState.backupProgress == BackUpProgressEnum.idle && !didGetBackupInfo.value) { + ref.watch(backupProvider.notifier).getBackupInfo(); + didGetBackupInfo.value = true; + } + return null; + }, [backupState.backupProgress]); - useEffect( - () { - if (backupState.backupProgress == BackUpProgressEnum.inProgress) { - WakelockPlus.enable(); - } else { - WakelockPlus.disable(); - } + useEffect(() { + if (backupState.backupProgress == BackUpProgressEnum.inProgress) { + WakelockPlus.enable(); + } else { + WakelockPlus.disable(); + } - return null; - }, - [backupState.backupProgress], - ); + return null; + }, [backupState.backupProgress]); Widget buildSelectedAlbumName() { var text = "backup_controller_page_backup_selected".tr(); @@ -95,9 +87,7 @@ class BackupControllerPage extends HookConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Text( text.trim().substring(0, text.length - 2), - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), ), ); } else { @@ -105,9 +95,7 @@ class BackupControllerPage extends HookConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Text( "backup_controller_page_none_selected".tr(), - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), ), ); } @@ -126,9 +114,7 @@ class BackupControllerPage extends HookConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Text( text.trim().substring(0, text.length - 2), - style: context.textTheme.labelLarge?.copyWith( - color: Colors.red[300], - ), + style: context.textTheme.labelLarge?.copyWith(color: Colors.red[300]), ), ); } else { @@ -141,22 +127,14 @@ class BackupControllerPage extends HookConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Card( shape: RoundedRectangleBorder( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), - side: BorderSide( - color: context.colorScheme.outlineVariant, - width: 1, - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), + side: BorderSide(color: context.colorScheme.outlineVariant, width: 1), ), elevation: 0, borderOnForeground: false, child: ListTile( minVerticalPadding: 18, - title: Text( - "backup_controller_page_albums", - style: context.textTheme.titleMedium, - ).tr(), + title: Text("backup_controller_page_albums", style: context.textTheme.titleMedium).tr(), subtitle: Padding( padding: const EdgeInsets.only(top: 8.0), child: Column( @@ -164,9 +142,7 @@ class BackupControllerPage extends HookConsumerWidget { children: [ Text( "backup_controller_page_to_backup", - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ).tr(), buildSelectedAlbumName(), buildExcludedAlbumName(), @@ -181,12 +157,7 @@ class BackupControllerPage extends HookConsumerWidget { // waited until backup albums are stored in DB ref.read(albumProvider.notifier).refreshDeviceAlbums(); }, - child: const Text( - "select", - style: TextStyle( - fontWeight: FontWeight.bold, - ), - ).tr(), + child: const Text("select", style: TextStyle(fontWeight: FontWeight.bold)).tr(), ), ), ), @@ -202,11 +173,10 @@ class BackupControllerPage extends HookConsumerWidget { Widget buildBackupButton() { return Padding( - padding: const EdgeInsets.only( - top: 24, - ), + padding: const EdgeInsets.only(top: 24), child: Container( - child: backupState.backupProgress == BackUpProgressEnum.inProgress || + child: + backupState.backupProgress == BackUpProgressEnum.inProgress || backupState.backupProgress == BackUpProgressEnum.manualInProgress ? ElevatedButton( style: ElevatedButton.styleFrom( @@ -221,22 +191,13 @@ class BackupControllerPage extends HookConsumerWidget { ref.read(backupProvider.notifier).cancelBackup(); } }, - child: const Text( - "cancel", - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ).tr(), + child: const Text("cancel", style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)).tr(), ) : ElevatedButton( onPressed: shouldBackup ? startBackup : null, child: const Text( "backup_controller_page_start_backup", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ).tr(), ), ), @@ -246,36 +207,28 @@ class BackupControllerPage extends HookConsumerWidget { buildBackgroundBackupInfo() { return const ListTile( leading: Icon(Icons.info_outline_rounded), - title: Text( - "Background backup is currently running, cannot start manual backup", - ), + title: Text("Background backup is currently running, cannot start manual backup"), ); } buildLoadingIndicator() { return const Padding( padding: EdgeInsets.only(top: 42.0), - child: Center( - child: CircularProgressIndicator(), - ), + child: Center(child: CircularProgressIndicator()), ); } return Scaffold( appBar: AppBar( elevation: 0, - title: const Text( - "backup_controller_page_backup", - ).tr(), + title: const Text("backup_controller_page_backup").tr(), leading: IconButton( onPressed: () { ref.watch(websocketProvider.notifier).listenUploadEvent(); context.maybePop(true); }, splashRadius: 24, - icon: const Icon( - Icons.arrow_back_ios_rounded, - ), + icon: const Icon(Icons.arrow_back_ios_rounded), ), actions: [ Padding( @@ -283,9 +236,7 @@ class BackupControllerPage extends HookConsumerWidget { child: IconButton( onPressed: () => context.pushRoute(const BackupOptionsRoute()), splashRadius: 24, - icon: const Icon( - Icons.settings_outlined, - ), + icon: const Icon(Icons.settings_outlined), ), ), ], @@ -325,10 +276,7 @@ class BackupControllerPage extends HookConsumerWidget { if (!hasExclusiveAccess) buildBackgroundBackupInfo(), buildBackupButton(), ] - : [ - buildFolderSelectionTile(), - if (!didGetBackupInfo.value) buildLoadingIndicator(), - ], + : [buildFolderSelectionTile(), if (!didGetBackupInfo.value) buildLoadingIndicator()], ), ), ], diff --git a/mobile/lib/pages/backup/backup_options.page.dart b/mobile/lib/pages/backup/backup_options.page.dart index 29822cab15..846a32a742 100644 --- a/mobile/lib/pages/backup/backup_options.page.dart +++ b/mobile/lib/pages/backup/backup_options.page.dart @@ -15,9 +15,7 @@ class BackupOptionsPage extends StatelessWidget { leading: IconButton( onPressed: () => context.maybePop(true), splashRadius: 24, - icon: const Icon( - Icons.arrow_back_ios_rounded, - ), + icon: const Icon(Icons.arrow_back_ios_rounded), ), ), body: const BackupSettings(), diff --git a/mobile/lib/pages/backup/drift_backup.page.dart b/mobile/lib/pages/backup/drift_backup.page.dart index f691eb576b..6d31c75946 100644 --- a/mobile/lib/pages/backup/drift_backup.page.dart +++ b/mobile/lib/pages/backup/drift_backup.page.dart @@ -51,35 +51,25 @@ class _DriftBackupPageState extends ConsumerState { Widget build(BuildContext context) { final selectedAlbum = ref .watch(backupAlbumProvider) - .where( - (album) => album.backupSelection == BackupSelection.selected, - ) + .where((album) => album.backupSelection == BackupSelection.selected) .toList(); return Scaffold( appBar: AppBar( elevation: 0, - title: Text( - "backup_controller_page_backup".t(), - ), + title: Text("backup_controller_page_backup".t()), leading: IconButton( onPressed: () { context.maybePop(true); }, splashRadius: 24, - icon: const Icon( - Icons.arrow_back_ios_rounded, - ), + icon: const Icon(Icons.arrow_back_ios_rounded), ), ), body: Stack( children: [ Padding( - padding: const EdgeInsets.only( - left: 16.0, - right: 16, - bottom: 32, - ), + padding: const EdgeInsets.only(left: 16.0, right: 16, bottom: 32), child: ListView( children: [ const SizedBox(height: 8), @@ -89,15 +79,10 @@ class _DriftBackupPageState extends ConsumerState { const _BackupCard(), const _RemainderCard(), const Divider(), - BackupToggleButton( - onStart: () async => await startBackup(), - onStop: () async => await stopBackup(), - ), + BackupToggleButton(onStart: () async => await startBackup(), onStop: () async => await stopBackup()), TextButton.icon( icon: const Icon(Icons.info_outline_rounded), - onPressed: () => context.pushRoute( - const DriftUploadDetailRoute(), - ), + onPressed: () => context.pushRoute(const DriftUploadDetailRoute()), label: Text("view_details".t(context: context)), ), ], @@ -119,9 +104,7 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { String text = "backup_controller_page_backup_selected".tr(); final albums = ref .watch(backupAlbumProvider) - .where( - (album) => album.backupSelection == BackupSelection.selected, - ) + .where((album) => album.backupSelection == BackupSelection.selected) .toList(); if (albums.isNotEmpty) { @@ -137,9 +120,7 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Text( text.trim().substring(0, text.length - 2), - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), ), ); } else { @@ -147,9 +128,7 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Text( "backup_controller_page_none_selected".tr(), - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), ), ); } @@ -159,9 +138,7 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { String text = "backup_controller_page_excluded".tr(); final albums = ref .watch(backupAlbumProvider) - .where( - (album) => album.backupSelection == BackupSelection.excluded, - ) + .where((album) => album.backupSelection == BackupSelection.excluded) .toList(); if (albums.isNotEmpty) { @@ -173,9 +150,7 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Text( text.trim().substring(0, text.length - 2), - style: context.textTheme.labelLarge?.copyWith( - color: Colors.red[300], - ), + style: context.textTheme.labelLarge?.copyWith(color: Colors.red[300]), ), ); } else { @@ -186,19 +161,13 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { return Card( shape: RoundedRectangleBorder( borderRadius: const BorderRadius.all(Radius.circular(20)), - side: BorderSide( - color: context.colorScheme.outlineVariant, - width: 1, - ), + side: BorderSide(color: context.colorScheme.outlineVariant, width: 1), ), elevation: 0, borderOnForeground: false, child: ListTile( minVerticalPadding: 18, - title: Text( - "backup_controller_page_albums", - style: context.textTheme.titleMedium, - ).tr(), + title: Text("backup_controller_page_albums", style: context.textTheme.titleMedium).tr(), subtitle: Padding( padding: const EdgeInsets.only(top: 8.0), child: Column( @@ -206,9 +175,7 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { children: [ Text( "backup_controller_page_to_backup", - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ).tr(), buildSelectedAlbumName(), buildExcludedAlbumName(), @@ -224,12 +191,7 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { } ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id); }, - child: const Text( - "select", - style: TextStyle( - fontWeight: FontWeight.bold, - ), - ).tr(), + child: const Text("select", style: TextStyle(fontWeight: FontWeight.bold)).tr(), ), ), ); diff --git a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart index 3f12328a06..396b711d07 100644 --- a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart +++ b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart @@ -119,9 +119,7 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState setState(() => _searchQuery = value.trim()), ) - : const Text( - "backup_album_selection_page_select_albums", - ).t(context: context), + : const Text("backup_album_selection_page_select_albums").t(context: context), actions: [ if (!_isSearchMode) IconButton( @@ -151,27 +149,20 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState 600) { - return _AlbumSelectionGrid( - filteredAlbums: filteredAlbums, - searchQuery: _searchQuery, - ); + return _AlbumSelectionGrid(filteredAlbums: filteredAlbums, searchQuery: _searchQuery); } else { - return _AlbumSelectionList( - filteredAlbums: filteredAlbums, - searchQuery: _searchQuery, - ); + return _AlbumSelectionList(filteredAlbums: filteredAlbums, searchQuery: _searchQuery); } }, ), @@ -285,10 +250,7 @@ class _AlbumSelectionList extends StatelessWidget { final List filteredAlbums; final String searchQuery; - const _AlbumSelectionList({ - required this.filteredAlbums, - required this.searchQuery, - }); + const _AlbumSelectionList({required this.filteredAlbums, required this.searchQuery}); @override Widget build(BuildContext context) { @@ -304,24 +266,15 @@ class _AlbumSelectionList extends StatelessWidget { } if (filteredAlbums.isEmpty) { - return const SliverToBoxAdapter( - child: Center( - child: CircularProgressIndicator(), - ), - ); + return const SliverToBoxAdapter(child: Center(child: CircularProgressIndicator())); } return SliverPadding( padding: const EdgeInsets.symmetric(vertical: 12.0), sliver: SliverList( - delegate: SliverChildBuilderDelegate( - ((context, index) { - return DriftAlbumInfoListTile( - album: filteredAlbums[index], - ); - }), - childCount: filteredAlbums.length, - ), + delegate: SliverChildBuilderDelegate(((context, index) { + return DriftAlbumInfoListTile(album: filteredAlbums[index]); + }), childCount: filteredAlbums.length), ), ); } @@ -331,10 +284,7 @@ class _AlbumSelectionGrid extends StatelessWidget { final List filteredAlbums; final String searchQuery; - const _AlbumSelectionGrid({ - required this.filteredAlbums, - required this.searchQuery, - }); + const _AlbumSelectionGrid({required this.filteredAlbums, required this.searchQuery}); @override Widget build(BuildContext context) { @@ -350,11 +300,7 @@ class _AlbumSelectionGrid extends StatelessWidget { } if (filteredAlbums.isEmpty) { - return const SliverToBoxAdapter( - child: Center( - child: CircularProgressIndicator(), - ), - ); + return const SliverToBoxAdapter(child: Center(child: CircularProgressIndicator())); } return SliverPadding( @@ -367,9 +313,7 @@ class _AlbumSelectionGrid extends StatelessWidget { ), itemCount: filteredAlbums.length, itemBuilder: ((context, index) { - return DriftAlbumInfoListTile( - album: filteredAlbums[index], - ); + return DriftAlbumInfoListTile(album: filteredAlbums[index]); }), ), ); @@ -379,9 +323,7 @@ class _AlbumSelectionGrid extends StatelessWidget { class _SelectedAlbumNameChips extends ConsumerWidget { final List selectedBackupAlbums; - const _SelectedAlbumNameChips({ - required this.selectedBackupAlbums, - }); + const _SelectedAlbumNameChips({required this.selectedBackupAlbums}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -411,10 +353,7 @@ class _SelectedAlbumNameChips extends ConsumerWidget { ), backgroundColor: context.primaryColor, deleteIconColor: context.isDarkTheme ? Colors.black : Colors.white, - deleteIcon: const Icon( - Icons.cancel_rounded, - size: 15, - ), + deleteIcon: const Icon(Icons.cancel_rounded, size: 15), onDeleted: removeSelection, ), ), @@ -428,9 +367,7 @@ class _SelectedAlbumNameChips extends ConsumerWidget { class _ExcludedAlbumNameChips extends ConsumerWidget { final List excludedBackupAlbums; - const _ExcludedAlbumNameChips({ - required this.excludedBackupAlbums, - }); + const _ExcludedAlbumNameChips({required this.excludedBackupAlbums}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -452,18 +389,11 @@ class _ExcludedAlbumNameChips extends ConsumerWidget { child: Chip( label: Text( album.name, - style: TextStyle( - fontSize: 12, - color: context.scaffoldBackgroundColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 12, color: context.scaffoldBackgroundColor, fontWeight: FontWeight.bold), ), backgroundColor: Colors.red[300], deleteIconColor: context.scaffoldBackgroundColor, - deleteIcon: const Icon( - Icons.cancel_rounded, - size: 15, - ), + deleteIcon: const Icon(Icons.cancel_rounded, size: 15), onDeleted: removeSelection, ), ), @@ -478,10 +408,7 @@ class _SelectAllButton extends ConsumerWidget { final List filteredAlbums; final List selectedBackupAlbums; - const _SelectAllButton({ - required this.filteredAlbums, - required this.selectedBackupAlbums, - }); + const _SelectAllButton({required this.filteredAlbums, required this.selectedBackupAlbums}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -505,13 +432,9 @@ class _SelectAllButton extends ConsumerWidget { icon: const Icon(Icons.select_all), label: AnimatedSwitcher( duration: const Duration(milliseconds: 200), - child: Text( - "select_all".t(context: context), - ), - ), - style: ElevatedButton.styleFrom( - padding: const EdgeInsets.symmetric(vertical: 12.0), + child: Text("select_all".t(context: context)), ), + style: ElevatedButton.styleFrom(padding: const EdgeInsets.symmetric(vertical: 12.0)), ), ), const SizedBox(width: 8.0), @@ -528,9 +451,7 @@ class _SelectAllButton extends ConsumerWidget { : null, icon: const Icon(Icons.deselect), label: Text('deselect_all'.t(context: context)), - style: OutlinedButton.styleFrom( - padding: const EdgeInsets.symmetric(vertical: 12.0), - ), + style: OutlinedButton.styleFrom(padding: const EdgeInsets.symmetric(vertical: 12.0)), ), ), ], diff --git a/mobile/lib/pages/backup/drift_upload_detail.page.dart b/mobile/lib/pages/backup/drift_upload_detail.page.dart index 62d6341fd1..36dbe4e128 100644 --- a/mobile/lib/pages/backup/drift_upload_detail.page.dart +++ b/mobile/lib/pages/backup/drift_upload_detail.page.dart @@ -16,9 +16,7 @@ class DriftUploadDetailPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final uploadItems = ref.watch( - driftBackupProvider.select((state) => state.uploadItems), - ); + final uploadItems = ref.watch(driftBackupProvider.select((state) => state.uploadItems)); return Scaffold( appBar: AppBar( @@ -36,26 +34,18 @@ class DriftUploadDetailPage extends ConsumerWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon( - Icons.cloud_off_rounded, - size: 80, - color: context.colorScheme.onSurface.withValues(alpha: 0.3), - ), + Icon(Icons.cloud_off_rounded, size: 80, color: context.colorScheme.onSurface.withValues(alpha: 0.3)), const SizedBox(height: 16), Text( "no_uploads_in_progress".t(context: context), - style: context.textTheme.titleMedium?.copyWith( - color: context.colorScheme.onSurface.withValues(alpha: 0.6), - ), + style: context.textTheme.titleMedium?.copyWith(color: context.colorScheme.onSurface.withValues(alpha: 0.6)), ), ], ), ); } - Widget _buildUploadList( - Map uploadItems, - ) { + Widget _buildUploadList(Map uploadItems) { return ListView.separated( addAutomaticKeepAlives: true, padding: const EdgeInsets.all(16), @@ -68,10 +58,7 @@ class DriftUploadDetailPage extends ConsumerWidget { ); } - Widget _buildUploadCard( - BuildContext context, - DriftUploadStatus item, - ) { + Widget _buildUploadCard(BuildContext context, DriftUploadStatus item) { final isCompleted = item.progress >= 1.0; final double progressPercentage = (item.progress * 100).clamp(0, 100); @@ -79,19 +66,12 @@ class DriftUploadDetailPage extends ConsumerWidget { elevation: 0, color: item.isFailed != null ? context.colorScheme.errorContainer : context.colorScheme.surfaceContainer, shape: RoundedRectangleBorder( - borderRadius: const BorderRadius.all( - Radius.circular(16), - ), - side: BorderSide( - color: context.colorScheme.outline.withValues(alpha: 0.1), - width: 1, - ), + borderRadius: const BorderRadius.all(Radius.circular(16)), + side: BorderSide(color: context.colorScheme.outline.withValues(alpha: 0.1), width: 1), ), child: InkWell( onTap: () => _showFileDetailDialog(context, item), - borderRadius: const BorderRadius.all( - Radius.circular(16), - ), + borderRadius: const BorderRadius.all(Radius.circular(16)), child: Padding( padding: const EdgeInsets.all(16), child: Column( @@ -105,9 +85,7 @@ class DriftUploadDetailPage extends ConsumerWidget { children: [ Text( path.basename(item.filename), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), maxLines: 1, overflow: TextOverflow.ellipsis, ), @@ -166,18 +144,11 @@ class DriftUploadDetailPage extends ConsumerWidget { ), ), if (isCompleted) - Icon( - Icons.check_circle_rounded, - size: 28, - color: context.colorScheme.primary, - ) + Icon(Icons.check_circle_rounded, size: 28, color: context.colorScheme.primary) else Text( percentage.toStringAsFixed(0), - style: context.textTheme.labelSmall?.copyWith( - fontWeight: FontWeight.bold, - fontSize: 10, - ), + style: context.textTheme.labelSmall?.copyWith(fontWeight: FontWeight.bold, fontSize: 10), ), ], ), @@ -192,10 +163,7 @@ class DriftUploadDetailPage extends ConsumerWidget { ); } - Future _showFileDetailDialog( - BuildContext context, - DriftUploadStatus item, - ) async { + Future _showFileDetailDialog(BuildContext context, DriftUploadStatus item) async { showDialog( context: context, builder: (context) => FileDetailDialog(uploadStatus: item), @@ -206,10 +174,7 @@ class DriftUploadDetailPage extends ConsumerWidget { class FileDetailDialog extends ConsumerWidget { final DriftUploadStatus uploadStatus; - const FileDetailDialog({ - super.key, - required this.uploadStatus, - }); + const FileDetailDialog({super.key, required this.uploadStatus}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -217,29 +182,17 @@ class FileDetailDialog extends ConsumerWidget { insetPadding: const EdgeInsets.all(20), backgroundColor: context.colorScheme.surfaceContainerLow, shape: RoundedRectangleBorder( - borderRadius: const BorderRadius.all( - Radius.circular(16), - ), - side: BorderSide( - color: context.colorScheme.outline.withValues(alpha: 0.2), - width: 1, - ), + borderRadius: const BorderRadius.all(Radius.circular(16)), + side: BorderSide(color: context.colorScheme.outline.withValues(alpha: 0.2), width: 1), ), title: Row( children: [ - Icon( - Icons.info_outline, - color: context.primaryColor, - size: 24, - ), + Icon(Icons.info_outline, color: context.primaryColor, size: 24), const SizedBox(width: 8), Expanded( child: Text( "details".t(context: context), - style: context.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.w600, - color: context.primaryColor, - ), + style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor), ), ), ], @@ -250,10 +203,7 @@ class FileDetailDialog extends ConsumerWidget { future: _getAssetDetails(ref, uploadStatus.taskId), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { - return const SizedBox( - height: 200, - child: Center(child: CircularProgressIndicator()), - ); + return const SizedBox(height: 200, child: Center(child: CircularProgressIndicator())); } final asset = snapshot.data; @@ -270,18 +220,11 @@ class FileDetailDialog extends ConsumerWidget { width: 128, height: 128, decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.outline.withValues(alpha: 0.2), - width: 1, - ), + border: Border.all(color: context.colorScheme.outline.withValues(alpha: 0.2), width: 1), borderRadius: const BorderRadius.all(Radius.circular(12)), ), child: asset != null - ? Thumbnail( - asset: asset, - size: const Size(512, 512), - fit: BoxFit.cover, - ) + ? Thumbnail(asset: asset, size: const Size(512, 512), fit: BoxFit.cover) : null, ), ), @@ -289,44 +232,14 @@ class FileDetailDialog extends ConsumerWidget { const SizedBox(height: 24), if (asset != null) ...[ _buildInfoSection(context, [ - _buildInfoRow( - context, - "Filename", - path.basename(uploadStatus.filename), - ), - _buildInfoRow( - context, - "Local ID", - asset.id, - ), - _buildInfoRow( - context, - "File Size", - formatHumanReadableBytes(uploadStatus.fileSize, 2), - ), + _buildInfoRow(context, "Filename", path.basename(uploadStatus.filename)), + _buildInfoRow(context, "Local ID", asset.id), + _buildInfoRow(context, "File Size", formatHumanReadableBytes(uploadStatus.fileSize, 2)), if (asset.width != null) _buildInfoRow(context, "Width", "${asset.width}px"), - if (asset.height != null) - _buildInfoRow( - context, - "Height", - "${asset.height}px", - ), - _buildInfoRow( - context, - "Created At", - asset.createdAt.toString(), - ), - _buildInfoRow( - context, - "Updated At", - asset.updatedAt.toString(), - ), - if (asset.checksum != null) - _buildInfoRow( - context, - "Checksum", - asset.checksum!, - ), + if (asset.height != null) _buildInfoRow(context, "Height", "${asset.height}px"), + _buildInfoRow(context, "Created At", asset.createdAt.toString()), + _buildInfoRow(context, "Updated At", asset.updatedAt.toString()), + if (asset.checksum != null) _buildInfoRow(context, "Checksum", asset.checksum!), ]), ], ], @@ -340,39 +253,23 @@ class FileDetailDialog extends ConsumerWidget { onPressed: () => Navigator.of(context).pop(), child: Text( "close".t(), - style: TextStyle( - fontWeight: FontWeight.w600, - color: context.primaryColor, - ), + style: TextStyle(fontWeight: FontWeight.w600, color: context.primaryColor), ), ), ], ); } - Widget _buildInfoSection( - BuildContext context, - List children, - ) { + Widget _buildInfoSection(BuildContext context, List children) { return Container( width: double.infinity, padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: context.colorScheme.surfaceContainer, - borderRadius: const BorderRadius.all( - Radius.circular(12), - ), - border: Border.all( - color: context.colorScheme.outline.withValues(alpha: 0.1), - width: 1, - ), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ...children, - ], + borderRadius: const BorderRadius.all(Radius.circular(12)), + border: Border.all(color: context.colorScheme.outline.withValues(alpha: 0.1), width: 1), ), + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [...children]), ); } @@ -405,10 +302,7 @@ class FileDetailDialog extends ConsumerWidget { ); } - Future _getAssetDetails( - WidgetRef ref, - String localAssetId, - ) async { + Future _getAssetDetails(WidgetRef ref, String localAssetId) async { try { final repository = ref.read(localAssetRepository); return await repository.getById(localAssetId); diff --git a/mobile/lib/pages/backup/failed_backup_status.page.dart b/mobile/lib/pages/backup/failed_backup_status.page.dart index 8d0faf2d22..b533895cd7 100644 --- a/mobile/lib/pages/backup/failed_backup_status.page.dart +++ b/mobile/lib/pages/backup/failed_backup_status.page.dart @@ -25,9 +25,7 @@ class FailedBackupStatusPage extends HookConsumerWidget { context.maybePop(true); }, splashRadius: 24, - icon: const Icon( - Icons.arrow_back_ios_rounded, - ), + icon: const Icon(Icons.arrow_back_ios_rounded), ), ), body: ListView.builder( @@ -37,19 +35,13 @@ class FailedBackupStatusPage extends HookConsumerWidget { var errorAsset = errorBackupList.elementAt(index); return Padding( - padding: const EdgeInsets.symmetric( - horizontal: 12.0, - vertical: 4, - ), + padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 4), child: Card( shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all( Radius.circular(15), // if you need this ), - side: BorderSide( - color: Colors.black12, - width: 1, - ), + side: BorderSide(color: Colors.black12, width: 1), ), elevation: 0, child: Row( @@ -57,12 +49,7 @@ class FailedBackupStatusPage extends HookConsumerWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ ConstrainedBox( - constraints: const BoxConstraints( - minWidth: 100, - minHeight: 100, - maxWidth: 100, - maxHeight: 150, - ), + constraints: const BoxConstraints(minWidth: 100, minHeight: 100, maxWidth: 100, maxHeight: 150), child: ClipRRect( borderRadius: const BorderRadius.only( bottomLeft: Radius.circular(15), @@ -71,11 +58,7 @@ class FailedBackupStatusPage extends HookConsumerWidget { clipBehavior: Clip.hardEdge, child: Image( fit: BoxFit.cover, - image: ImmichLocalThumbnailProvider( - asset: errorAsset.asset, - height: 512, - width: 512, - ), + image: ImmichLocalThumbnailProvider(asset: errorAsset.asset, height: 512, width: 512), ), ), ), @@ -91,20 +74,14 @@ class FailedBackupStatusPage extends HookConsumerWidget { children: [ Text( DateFormat.yMMMMd().format( - DateTime.parse( - errorAsset.fileCreatedAt.toString(), - ).toLocal(), + DateTime.parse(errorAsset.fileCreatedAt.toString()).toLocal(), ), style: TextStyle( fontWeight: FontWeight.w600, color: context.isDarkTheme ? Colors.white70 : Colors.grey[800], ), ), - Icon( - Icons.error, - color: Colors.red.withAlpha(200), - size: 18, - ), + Icon(Icons.error, color: Colors.red.withAlpha(200), size: 18), ], ), Padding( @@ -113,10 +90,7 @@ class FailedBackupStatusPage extends HookConsumerWidget { errorAsset.fileName, maxLines: 1, overflow: TextOverflow.ellipsis, - style: TextStyle( - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), + style: TextStyle(fontWeight: FontWeight.bold, color: context.primaryColor), ), ), Text( diff --git a/mobile/lib/pages/common/activities.page.dart b/mobile/lib/pages/common/activities.page.dart index 203df2d503..1a1955af40 100644 --- a/mobile/lib/pages/common/activities.page.dart +++ b/mobile/lib/pages/common/activities.page.dart @@ -16,9 +16,7 @@ import 'package:immich_mobile/widgets/activities/dismissible_activity.dart'; @RoutePage() class ActivitiesPage extends HookConsumerWidget { - const ActivitiesPage({ - super.key, - }); + const ActivitiesPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -60,9 +58,7 @@ class ActivitiesPage extends HookConsumerWidget { itemBuilder: (context, index) { // Additional vertical gap after the last element if (index == data.length) { - return const SizedBox( - height: 80, - ); + return const SizedBox(height: 80); } final activity = data[index]; @@ -73,8 +69,9 @@ class ActivitiesPage extends HookConsumerWidget { child: DismissibleActivity( activity.id, ActivityTile(activity), - onDismiss: - canDelete ? (activityId) async => await activityNotifier.removeActivity(activity.id) : null, + onDismiss: canDelete + ? (activityId) async => await activityNotifier.removeActivity(activity.id) + : null, ), ); }, diff --git a/mobile/lib/pages/common/app_log.page.dart b/mobile/lib/pages/common/app_log.page.dart index 359a541de0..fe0c0ea442 100644 --- a/mobile/lib/pages/common/app_log.page.dart +++ b/mobile/lib/pages/common/app_log.page.dart @@ -12,17 +12,13 @@ import 'package:intl/intl.dart'; @RoutePage() class AppLogPage extends HookConsumerWidget { - const AppLogPage({ - super.key, - }); + const AppLogPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final immichLogger = LogService.I; final shouldReload = useState(false); - final logMessages = useFuture( - useMemoized(() => immichLogger.getMessages(), [shouldReload.value]), - ); + final logMessages = useFuture(useMemoized(() => immichLogger.getMessages(), [shouldReload.value])); Widget colorStatusIndicator(Color color) { return Column( @@ -31,38 +27,29 @@ class AppLogPage extends HookConsumerWidget { Container( width: 10, height: 10, - decoration: BoxDecoration( - color: color, - shape: BoxShape.circle, - ), + decoration: BoxDecoration(color: color, shape: BoxShape.circle), ), ], ); } Widget buildLeadingIcon(LogLevel level) => switch (level) { - LogLevel.info => colorStatusIndicator(context.primaryColor), - LogLevel.severe => colorStatusIndicator(Colors.redAccent), - LogLevel.warning => colorStatusIndicator(Colors.orangeAccent), - _ => colorStatusIndicator(Colors.grey), - }; + LogLevel.info => colorStatusIndicator(context.primaryColor), + LogLevel.severe => colorStatusIndicator(Colors.redAccent), + LogLevel.warning => colorStatusIndicator(Colors.orangeAccent), + _ => colorStatusIndicator(Colors.grey), + }; Color getTileColor(LogLevel level) => switch (level) { - LogLevel.info => Colors.transparent, - LogLevel.severe => Colors.redAccent.withValues(alpha: 0.25), - LogLevel.warning => Colors.orangeAccent.withValues(alpha: 0.25), - _ => context.primaryColor.withValues(alpha: 0.1), - }; + LogLevel.info => Colors.transparent, + LogLevel.severe => Colors.redAccent.withValues(alpha: 0.25), + LogLevel.warning => Colors.orangeAccent.withValues(alpha: 0.25), + _ => context.primaryColor.withValues(alpha: 0.1), + }; return Scaffold( appBar: AppBar( - title: const Text( - "Logs", - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16.0, - ), - ), + title: const Text("Logs", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16.0)), scrolledUnderElevation: 1, elevation: 2, actions: [ @@ -81,12 +68,7 @@ class AppLogPage extends HookConsumerWidget { Builder( builder: (BuildContext iconContext) { return IconButton( - icon: Icon( - Icons.share_rounded, - color: context.primaryColor, - semanticLabel: "Share logs", - size: 20.0, - ), + icon: Icon(Icons.share_rounded, color: context.primaryColor, semanticLabel: "Share logs", size: 20.0), onPressed: () { ImmichLogger.shareLogs(iconContext); }, @@ -98,10 +80,7 @@ class AppLogPage extends HookConsumerWidget { onPressed: () { context.maybePop(); }, - icon: const Icon( - Icons.arrow_back_ios_new_rounded, - size: 20.0, - ), + icon: const Icon(Icons.arrow_back_ios_new_rounded, size: 20.0), ), centerTitle: true, ), @@ -113,11 +92,7 @@ class AppLogPage extends HookConsumerWidget { itemBuilder: (context, index) { var logMessage = logMessages.data![index]; return ListTile( - onTap: () => context.pushRoute( - AppLogDetailRoute( - logMessage: logMessage, - ), - ), + onTap: () => context.pushRoute(AppLogDetailRoute(logMessage: logMessage)), trailing: const Icon(Icons.arrow_forward_ios_rounded), visualDensity: VisualDensity.compact, dense: true, @@ -125,18 +100,11 @@ class AppLogPage extends HookConsumerWidget { minLeadingWidth: 10, title: Text( truncateLogMessage(logMessage.message, 4), - style: TextStyle( - fontSize: 14.0, - color: context.colorScheme.onSurface, - fontFamily: "Inconsolata", - ), + style: TextStyle(fontSize: 14.0, color: context.colorScheme.onSurface, fontFamily: "Inconsolata"), ), subtitle: Text( "at ${DateFormat("HH:mm:ss.SSS").format(logMessage.createdAt)} in ${logMessage.logger}", - style: TextStyle( - fontSize: 12.0, - color: context.colorScheme.onSurfaceSecondary, - ), + style: TextStyle(fontSize: 12.0, color: context.colorScheme.onSurfaceSecondary), ), leading: buildLeadingIcon(logMessage.level), ); diff --git a/mobile/lib/pages/common/app_log_detail.page.dart b/mobile/lib/pages/common/app_log_detail.page.dart index d8647ca8e2..a9cf634faf 100644 --- a/mobile/lib/pages/common/app_log_detail.page.dart +++ b/mobile/lib/pages/common/app_log_detail.page.dart @@ -27,11 +27,7 @@ class AppLogDetailPage extends HookConsumerWidget { padding: const EdgeInsets.only(bottom: 8.0), child: Text( header, - style: TextStyle( - fontSize: 12.0, - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 12.0, color: context.primaryColor, fontWeight: FontWeight.bold), ), ), IconButton( @@ -41,38 +37,26 @@ class AppLogDetailPage extends HookConsumerWidget { SnackBar( content: Text( "Copied to clipboard", - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ), ), ); }); }, - icon: Icon( - Icons.copy, - size: 16.0, - color: context.primaryColor, - ), + icon: Icon(Icons.copy, size: 16.0, color: context.primaryColor), ), ], ), Container( decoration: BoxDecoration( color: context.colorScheme.surfaceContainerHigh, - borderRadius: const BorderRadius.all( - Radius.circular(15.0), - ), + borderRadius: const BorderRadius.all(Radius.circular(15.0)), ), child: Padding( padding: const EdgeInsets.all(8.0), child: SelectableText( text, - style: const TextStyle( - fontSize: 12.0, - fontWeight: FontWeight.bold, - fontFamily: "Inconsolata", - ), + style: const TextStyle(fontSize: 12.0, fontWeight: FontWeight.bold, fontFamily: "Inconsolata"), ), ), ), @@ -91,29 +75,19 @@ class AppLogDetailPage extends HookConsumerWidget { padding: const EdgeInsets.only(bottom: 8.0), child: Text( "FROM", - style: TextStyle( - fontSize: 12.0, - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 12.0, color: context.primaryColor, fontWeight: FontWeight.bold), ), ), Container( decoration: BoxDecoration( color: context.colorScheme.surfaceContainerHigh, - borderRadius: const BorderRadius.all( - Radius.circular(15.0), - ), + borderRadius: const BorderRadius.all(Radius.circular(15.0)), ), child: Padding( padding: const EdgeInsets.all(8.0), child: SelectableText( context1.toString(), - style: const TextStyle( - fontSize: 12.0, - fontWeight: FontWeight.bold, - fontFamily: "Inconsolata", - ), + style: const TextStyle(fontSize: 12.0, fontWeight: FontWeight.bold, fontFamily: "Inconsolata"), ), ), ), @@ -123,20 +97,14 @@ class AppLogDetailPage extends HookConsumerWidget { } return Scaffold( - appBar: AppBar( - title: const Text("Log Detail"), - ), + appBar: AppBar(title: const Text("Log Detail")), body: SafeArea( child: ListView( children: [ buildTextWithCopyButton("MESSAGE", logMessage.message), if (logMessage.error != null) buildTextWithCopyButton("DETAILS", logMessage.error.toString()), if (logMessage.logger != null) buildLogContext1(logMessage.logger.toString()), - if (logMessage.stack != null) - buildTextWithCopyButton( - "STACK TRACE", - logMessage.stack.toString(), - ), + if (logMessage.stack != null) buildTextWithCopyButton("STACK TRACE", logMessage.stack.toString()), ], ), ), diff --git a/mobile/lib/pages/common/change_experience.page.dart b/mobile/lib/pages/common/change_experience.page.dart index 21464e39fa..45392a38f6 100644 --- a/mobile/lib/pages/common/change_experience.page.dart +++ b/mobile/lib/pages/common/change_experience.page.dart @@ -49,11 +49,9 @@ class _ChangeExperiencePageState extends ConsumerState { // Cancel uploads await Store.put(StoreKey.backgroundBackup, false); - ref.read(backupProvider.notifier).configureBackgroundBackup( - enabled: false, - onBatteryInfo: () {}, - onError: (_) {}, - ); + ref + .read(backupProvider.notifier) + .configureBackgroundBackup(enabled: false, onBatteryInfo: () {}, onError: (_) {}); ref.read(backupProvider.notifier).setAutoBackup(false); ref.read(backupProvider.notifier).cancelBackup(); ref.read(manualUploadProvider.notifier).cancelBackup(); @@ -65,14 +63,8 @@ class _ChangeExperiencePageState extends ConsumerState { if (permission.isGranted) { await ref.read(backgroundSyncProvider).syncLocal(full: true); - await migrateDeviceAssetToSqlite( - ref.read(isarProvider), - ref.read(driftProvider), - ); - await migrateBackupAlbumsToSqlite( - ref.read(isarProvider), - ref.read(driftProvider), - ); + await migrateDeviceAssetToSqlite(ref.read(isarProvider), ref.read(driftProvider)); + await migrateBackupAlbumsToSqlite(ref.read(isarProvider), ref.read(driftProvider)); } } else { await ref.read(backgroundSyncProvider).cancel(); @@ -98,16 +90,8 @@ class _ChangeExperiencePageState extends ConsumerState { AnimatedSwitcher( duration: Durations.long4, child: hasMigrated - ? const Icon( - Icons.check_circle_rounded, - color: Colors.green, - size: 48.0, - ) - : const SizedBox( - width: 50.0, - height: 50.0, - child: CircularProgressIndicator(), - ), + ? const Icon(Icons.check_circle_rounded, color: Colors.green, size: 48.0) + : const SizedBox(width: 50.0, height: 50.0, child: CircularProgressIndicator()), ), const SizedBox(height: 16.0), Center( diff --git a/mobile/lib/pages/common/create_album.page.dart b/mobile/lib/pages/common/create_album.page.dart index 02e1ea18d4..5a0d4154f8 100644 --- a/mobile/lib/pages/common/create_album.page.dart +++ b/mobile/lib/pages/common/create_album.page.dart @@ -20,10 +20,7 @@ import 'package:immich_mobile/widgets/album/shared_album_thumbnail_image.dart'; class CreateAlbumPage extends HookConsumerWidget { final List? assets; - const CreateAlbumPage({ - super.key, - this.assets, - }); + const CreateAlbumPage({super.key, this.assets}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -32,9 +29,7 @@ class CreateAlbumPage extends HookConsumerWidget { final albumDescriptionTextFieldFocusNode = useFocusNode(); final isAlbumTitleTextFieldFocus = useState(false); final isAlbumTitleEmpty = useState(true); - final selectedAssets = useState>( - assets != null ? Set.from(assets!) : const {}, - ); + final selectedAssets = useState>(assets != null ? Set.from(assets!) : const {}); void onBackgroundTapped() { albumTitleTextFieldFocusNode.unfocus(); @@ -50,10 +45,7 @@ class CreateAlbumPage extends HookConsumerWidget { onSelectPhotosButtonPressed() async { AssetSelectionPageResult? selectedAsset = await context.pushRoute( - AlbumAssetSelectionRoute( - existingAssets: selectedAssets.value, - canDeselect: true, - ), + AlbumAssetSelectionRoute(existingAssets: selectedAssets.value, canDeselect: true), ); if (selectedAsset == null) { selectedAssets.value = const {}; @@ -64,10 +56,7 @@ class CreateAlbumPage extends HookConsumerWidget { buildTitleInputField() { return Padding( - padding: const EdgeInsets.only( - right: 10, - left: 10, - ), + padding: const EdgeInsets.only(right: 10, left: 10), child: AlbumTitleTextField( isAlbumTitleEmpty: isAlbumTitleEmpty, albumTitleTextFieldFocusNode: albumTitleTextFieldFocusNode, @@ -79,10 +68,7 @@ class CreateAlbumPage extends HookConsumerWidget { buildDescriptionInputField() { return Padding( - padding: const EdgeInsets.only( - right: 10, - left: 10, - ), + padding: const EdgeInsets.only(right: 10, left: 10), child: AlbumViewerEditableDescription( albumDescription: '', descriptionFocusNode: albumDescriptionTextFieldFocusNode, @@ -95,10 +81,7 @@ class CreateAlbumPage extends HookConsumerWidget { return SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.only(top: 200, left: 18), - child: Text( - 'create_shared_album_page_share_add_assets', - style: context.textTheme.labelLarge, - ).tr(), + child: Text('create_shared_album_page_share_add_assets', style: context.textTheme.labelLarge).tr(), ), ); } @@ -115,18 +98,11 @@ class CreateAlbumPage extends HookConsumerWidget { style: FilledButton.styleFrom( alignment: Alignment.centerLeft, padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 16), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(10), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), backgroundColor: context.colorScheme.surfaceContainerHigh, ), onPressed: onSelectPhotosButtonPressed, - icon: Icon( - Icons.add_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.add_rounded, color: context.primaryColor), label: Padding( padding: const EdgeInsets.only(left: 8.0), child: Text( @@ -174,17 +150,12 @@ class CreateAlbumPage extends HookConsumerWidget { crossAxisSpacing: 5.0, mainAxisSpacing: 5, ), - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - return GestureDetector( - onTap: onBackgroundTapped, - child: SharedAlbumThumbnailImage( - asset: selectedAssets.value.elementAt(index), - ), - ); - }, - childCount: selectedAssets.value.length, - ), + delegate: SliverChildBuilderDelegate((BuildContext context, int index) { + return GestureDetector( + onTap: onBackgroundTapped, + child: SharedAlbumThumbnailImage(asset: selectedAssets.value.elementAt(index)), + ); + }, childCount: selectedAssets.value.length), ), ); } @@ -194,10 +165,9 @@ class CreateAlbumPage extends HookConsumerWidget { Future createAlbum() async { onBackgroundTapped(); - var newAlbum = await ref.watch(albumProvider.notifier).createAlbum( - ref.read(albumTitleProvider), - selectedAssets.value, - ); + var newAlbum = await ref + .watch(albumProvider.notifier) + .createAlbum(ref.read(albumTitleProvider), selectedAssets.value); if (newAlbum != null) { ref.read(albumProvider.notifier).refreshRemoteAlbums(); @@ -220,9 +190,7 @@ class CreateAlbumPage extends HookConsumerWidget { }, icon: const Icon(Icons.close_rounded), ), - title: const Text( - 'create_album', - ).tr(), + title: const Text('create_album').tr(), actions: [ TextButton( onPressed: albumTitleController.text.isNotEmpty ? createAlbum : null, diff --git a/mobile/lib/pages/common/download_panel.dart b/mobile/lib/pages/common/download_panel.dart index 38212d5486..0775f5b4e4 100644 --- a/mobile/lib/pages/common/download_panel.dart +++ b/mobile/lib/pages/common/download_panel.dart @@ -6,22 +6,13 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/asset_viewer/download.provider.dart'; class DownloadPanel extends ConsumerWidget { - const DownloadPanel({ - super.key, - }); + const DownloadPanel({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - final showProgress = ref.watch( - downloadStateProvider.select((state) => state.showProgress), - ); + final showProgress = ref.watch(downloadStateProvider.select((state) => state.showProgress)); - final tasks = ref - .watch( - downloadStateProvider.select((state) => state.taskProgress), - ) - .entries - .toList(); + final tasks = ref.watch(downloadStateProvider.select((state) => state.taskProgress)).entries.toList(); onCancelDownload(String id) { ref.watch(downloadStateProvider.notifier).cancelDownload(id); @@ -74,47 +65,35 @@ class DownloadTaskTile extends StatelessWidget { final progressPercent = (progress * 100).round(); String getStatusText() => switch (status) { - TaskStatus.running => 'downloading'.tr(), - TaskStatus.complete => 'download_complete'.tr(), - TaskStatus.failed => 'download_failed'.tr(), - TaskStatus.canceled => 'download_canceled'.tr(), - TaskStatus.paused => 'download_paused'.tr(), - TaskStatus.enqueued => 'download_enqueue'.tr(), - TaskStatus.notFound => 'download_notfound'.tr(), - TaskStatus.waitingToRetry => 'download_waiting_to_retry'.tr(), - }; + TaskStatus.running => 'downloading'.tr(), + TaskStatus.complete => 'download_complete'.tr(), + TaskStatus.failed => 'download_failed'.tr(), + TaskStatus.canceled => 'download_canceled'.tr(), + TaskStatus.paused => 'download_paused'.tr(), + TaskStatus.enqueued => 'download_enqueue'.tr(), + TaskStatus.notFound => 'download_notfound'.tr(), + TaskStatus.waitingToRetry => 'download_waiting_to_retry'.tr(), + }; return SizedBox( key: const ValueKey('download_progress'), width: context.width - 32, child: Card( clipBehavior: Clip.antiAlias, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(16), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))), child: ListTile( minVerticalPadding: 18, leading: const Icon(Icons.video_file_outlined), - title: Text( - getStatusText(), - style: context.textTheme.labelLarge, - ), + title: Text(getStatusText(), style: context.textTheme.labelLarge), trailing: IconButton( icon: Icon(Icons.close, color: context.colorScheme.onError), onPressed: onCancelDownload, - style: ElevatedButton.styleFrom( - backgroundColor: context.colorScheme.error.withAlpha(200), - ), + style: ElevatedButton.styleFrom(backgroundColor: context.colorScheme.error.withAlpha(200)), ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - fileName, - style: context.textTheme.labelMedium, - ), + Text(fileName, style: context.textTheme.labelMedium), Row( children: [ Expanded( @@ -125,10 +104,7 @@ class DownloadTaskTile extends StatelessWidget { ), ), const SizedBox(width: 8), - Text( - '$progressPercent%', - style: context.textTheme.labelSmall, - ), + Text('$progressPercent%', style: context.textTheme.labelSmall), ], ), ], diff --git a/mobile/lib/pages/common/gallery_stacked_children.dart b/mobile/lib/pages/common/gallery_stacked_children.dart index eafc325049..7145bc2553 100644 --- a/mobile/lib/pages/common/gallery_stacked_children.dart +++ b/mobile/lib/pages/common/gallery_stacked_children.dart @@ -36,11 +36,7 @@ class GalleryStackedChildren extends HookConsumerWidget { shrinkWrap: true, scrollDirection: Axis.horizontal, itemCount: stackElements.length, - padding: const EdgeInsets.only( - left: 5, - right: 5, - bottom: 30, - ), + padding: const EdgeInsets.only(left: 5, right: 5, bottom: 30), itemBuilder: (context, index) { final currentAsset = stackElements.elementAt(index); final assetId = currentAsset.remoteId; @@ -63,9 +59,7 @@ class GalleryStackedChildren extends HookConsumerWidget { ? const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(6)), - border: Border.fromBorderSide( - BorderSide(color: Colors.white, width: 2), - ), + border: Border.fromBorderSide(BorderSide(color: Colors.white, width: 2)), ) : const BoxDecoration( color: Colors.white, diff --git a/mobile/lib/pages/common/gallery_viewer.page.dart b/mobile/lib/pages/common/gallery_viewer.page.dart index 05389018da..3c279dfcd2 100644 --- a/mobile/lib/pages/common/gallery_viewer.page.dart +++ b/mobile/lib/pages/common/gallery_viewer.page.dart @@ -87,11 +87,7 @@ class GalleryViewerPage extends HookConsumerWidget { if (index < totalAssets.value && index >= 0) { final asset = loadAsset(index); await precacheImage( - ImmichImage.imageProvider( - asset: asset, - width: context.width, - height: context.height, - ), + ImmichImage.imageProvider(asset: asset, width: context.width, height: context.height), context, onError: onError, ); @@ -103,23 +99,20 @@ class GalleryViewerPage extends HookConsumerWidget { } } - useEffect( - () { - if (ref.read(showControlsProvider)) { - SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); - } else { - SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive); - } + useEffect(() { + if (ref.read(showControlsProvider)) { + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); + } else { + SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive); + } - // Delay this a bit so we can finish loading the page - Timer(const Duration(milliseconds: 400), () { - precacheNextImage(currentIndex.value + 1); - }); + // Delay this a bit so we can finish loading the page + Timer(const Duration(milliseconds: 400), () { + precacheNextImage(currentIndex.value + 1); + }); - return null; - }, - const [], - ); + return null; + }, const []); useEffect(() { final asset = loadAsset(currentIndex.value); @@ -136,9 +129,7 @@ class GalleryViewerPage extends HookConsumerWidget { duration: const Duration(seconds: 1), content: Text( "local_asset_cast_failed".tr(), - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ), ), ); @@ -147,9 +138,7 @@ class GalleryViewerPage extends HookConsumerWidget { } } return null; - }, [ - ref.watch(castProvider).isCasting, - ]); + }, [ref.watch(castProvider).isCasting]); void showInfo() { final asset = ref.read(currentAssetProvider); @@ -157,9 +146,7 @@ class GalleryViewerPage extends HookConsumerWidget { return; } showModalBottomSheet( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(15.0)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15.0))), barrierColor: Colors.transparent, isScrollControlled: true, showDragHandle: true, @@ -174,20 +161,10 @@ class GalleryViewerPage extends HookConsumerWidget { expand: false, builder: (context, scrollController) { return Padding( - padding: EdgeInsets.only( - bottom: context.viewInsets.bottom, - ), - child: ref.watch(appSettingsServiceProvider).getSetting( - AppSettingsEnum.advancedTroubleshooting, - ) - ? AdvancedBottomSheet( - assetDetail: asset, - scrollController: scrollController, - ) - : DetailPanel( - asset: asset, - scrollController: scrollController, - ), + padding: EdgeInsets.only(bottom: context.viewInsets.bottom), + child: ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.advancedTroubleshooting) + ? AdvancedBottomSheet(assetDetail: asset, scrollController: scrollController) + : DetailPanel(asset: asset, scrollController: scrollController), ); }, ); @@ -258,10 +235,7 @@ class GalleryViewerPage extends HookConsumerWidget { tightMode: true, initialScale: PhotoViewComputedScale.contained * 0.99, minScale: PhotoViewComputedScale.contained * 0.99, - errorBuilder: (context, error, stackTrace) => ImmichImage( - asset, - fit: BoxFit.contain, - ), + errorBuilder: (context, error, stackTrace) => ImmichImage(asset, fit: BoxFit.contain), ); } @@ -283,11 +257,7 @@ class GalleryViewerPage extends HookConsumerWidget { asset: asset, image: Image( key: ValueKey(asset), - image: ImmichImage.imageProvider( - asset: asset, - width: context.width, - height: context.height, - ), + image: ImmichImage.imageProvider(asset: asset, width: context.width, height: context.height), fit: BoxFit.contain, height: context.height, width: context.width, @@ -342,17 +312,8 @@ class GalleryViewerPage extends HookConsumerWidget { child: Stack( fit: StackFit.expand, children: [ - BackdropFilter( - filter: ui.ImageFilter.blur( - sigmaX: 10, - sigmaY: 10, - ), - ), - ImmichThumbnail( - key: ValueKey(asset), - asset: asset, - fit: BoxFit.contain, - ), + BackdropFilter(filter: ui.ImageFilter.blur(sigmaX: 10, sigmaY: 10)), + ImmichThumbnail(key: ValueKey(asset), asset: asset, fit: BoxFit.contain), ], ), ); @@ -361,9 +322,9 @@ class GalleryViewerPage extends HookConsumerWidget { scrollPhysics: isZoomed.value ? const NeverScrollableScrollPhysics() // Don't allow paging while scrolled in : (Platform.isIOS - ? const FastScrollPhysics() // Use bouncing physics for iOS - : const FastClampingScrollPhysics() // Use heavy physics for Android - ), + ? const FastScrollPhysics() // Use bouncing physics for iOS + : const FastClampingScrollPhysics() // Use heavy physics for Android + ), itemCount: totalAssets.value, scrollDirection: Axis.horizontal, onPageChanged: (value, _) { @@ -401,9 +362,7 @@ class GalleryViewerPage extends HookConsumerWidget { duration: const Duration(seconds: 2), content: Text( "local_asset_cast_failed".tr(), - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ), ), ); @@ -416,10 +375,7 @@ class GalleryViewerPage extends HookConsumerWidget { top: 0, left: 0, right: 0, - child: GalleryAppBar( - key: const ValueKey('app-bar'), - showInfo: showInfo, - ), + child: GalleryAppBar(key: const ValueKey('app-bar'), showInfo: showInfo), ), Positioned( bottom: 0, diff --git a/mobile/lib/pages/common/headers_settings.page.dart b/mobile/lib/pages/common/headers_settings.page.dart index 0f4ab882c8..4cf683b4d9 100644 --- a/mobile/lib/pages/common/headers_settings.page.dart +++ b/mobile/lib/pages/common/headers_settings.page.dart @@ -79,10 +79,8 @@ class HeaderSettingsPage extends HookConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 16.0), itemCount: list.length, itemBuilder: (ctx, index) => list[index], - separatorBuilder: (context, index) => const Padding( - padding: EdgeInsets.only(bottom: 16.0, left: 8, right: 8), - child: Divider(), - ), + separatorBuilder: (context, index) => + const Padding(padding: EdgeInsets.only(bottom: 16.0, left: 8, right: 8), child: Divider()), ), ), ); @@ -109,12 +107,9 @@ class HeaderKeyValueSettings extends StatelessWidget { final SettingsHeader header; final Function() onRemove; - HeaderKeyValueSettings({ - super.key, - required this.header, - required this.onRemove, - }) : keyController = TextEditingController(text: header.key), - valueController = TextEditingController(text: header.value); + HeaderKeyValueSettings({super.key, required this.header, required this.onRemove}) + : keyController = TextEditingController(text: header.key), + valueController = TextEditingController(text: header.value); String? emptyFieldValidator(String? value) { if (value == null || value.isEmpty) { @@ -150,9 +145,7 @@ class HeaderKeyValueSettings extends StatelessWidget { Padding( padding: const EdgeInsets.only(left: 8), child: IconButton( - style: ElevatedButton.styleFrom( - padding: const EdgeInsets.symmetric(vertical: 12), - ), + style: ElevatedButton.styleFrom(padding: const EdgeInsets.symmetric(vertical: 12)), color: Colors.red[400], onPressed: onRemove, icon: const Icon(Icons.delete_outline), diff --git a/mobile/lib/pages/common/large_leading_tile.dart b/mobile/lib/pages/common/large_leading_tile.dart index d36e296429..4563834473 100644 --- a/mobile/lib/pages/common/large_leading_tile.dart +++ b/mobile/lib/pages/common/large_leading_tile.dart @@ -8,10 +8,7 @@ class LargeLeadingTile extends StatelessWidget { required this.onTap, required this.title, this.subtitle, - this.leadingPadding = const EdgeInsets.symmetric( - vertical: 8, - horizontal: 16.0, - ), + this.leadingPadding = const EdgeInsets.symmetric(vertical: 8, horizontal: 16.0), this.borderRadius = 20.0, this.trailing, this.selected = false, @@ -47,18 +44,12 @@ class LargeLeadingTile extends StatelessWidget { child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Padding( - padding: leadingPadding, - child: leading, - ), + Padding(padding: leadingPadding, child: leading), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox( - width: context.width * 0.6, - child: title, - ), + SizedBox(width: context.width * 0.6, child: title), subtitle ?? const SizedBox.shrink(), ], ), diff --git a/mobile/lib/pages/common/native_video_viewer.page.dart b/mobile/lib/pages/common/native_video_viewer.page.dart index 0dbaf6125c..d8b6db2276 100644 --- a/mobile/lib/pages/common/native_video_viewer.page.dart +++ b/mobile/lib/pages/common/native_video_viewer.page.dart @@ -78,17 +78,15 @@ class NativeVideoViewerPage extends HookConsumerWidget { throw Exception('No file found for the video'); } - final source = await VideoSource.init( - path: file.path, - type: VideoSourceType.file, - ); + final source = await VideoSource.init(path: file.path, type: VideoSourceType.file); return source; } // Use a network URL for the video player controller final serverEndpoint = Store.get(StoreKey.serverEndpoint); - final isOriginalVideo = - ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.loadOriginalVideo); + final isOriginalVideo = ref + .read(appSettingsServiceProvider) + .getSetting(AppSettingsEnum.loadOriginalVideo); final String postfixUrl = isOriginalVideo ? 'original' : 'video/playback'; final String videoUrl = asset.livePhotoVideoId != null ? '$serverEndpoint/assets/${asset.livePhotoVideoId}/$postfixUrl' @@ -101,30 +99,24 @@ class NativeVideoViewerPage extends HookConsumerWidget { ); return source; } catch (error) { - log.severe( - 'Error creating video source for asset ${asset.fileName}: $error', - ); + log.severe('Error creating video source for asset ${asset.fileName}: $error'); return null; } } final videoSource = useMemoized>(() => createSource()); final aspectRatio = useState(asset.aspectRatio); - useMemoized( - () async { - if (!context.mounted || aspectRatio.value != null) { - return null; - } + useMemoized(() async { + if (!context.mounted || aspectRatio.value != null) { + return null; + } - try { - aspectRatio.value = await ref.read(assetServiceProvider).getAspectRatio(asset); - } catch (error) { - log.severe( - 'Error getting aspect ratio for asset ${asset.fileName}: $error', - ); - } - }, - ); + try { + aspectRatio.value = await ref.read(assetServiceProvider).getAspectRatio(asset); + } catch (error) { + log.severe('Error getting aspect ratio for asset ${asset.fileName}: $error'); + } + }); void checkIfBuffering() { if (!context.mounted) { @@ -134,8 +126,9 @@ class NativeVideoViewerPage extends HookConsumerWidget { final videoPlayback = ref.read(videoPlaybackValueProvider); if ((isBuffering.value || videoPlayback.state == VideoPlaybackState.initializing) && videoPlayback.state != VideoPlaybackState.buffering) { - ref.read(videoPlaybackValueProvider.notifier).value = - videoPlayback.copyWith(state: VideoPlaybackState.buffering); + ref.read(videoPlaybackValueProvider.notifier).value = videoPlayback.copyWith( + state: VideoPlaybackState.buffering, + ); } } @@ -322,48 +315,42 @@ class NativeVideoViewerPage extends HookConsumerWidget { // This delay seems like a hacky way to resolve underlying bugs in video // playback, but other resolutions failed thus far Timer( - Platform.isIOS - ? Duration(milliseconds: 300 * playbackDelayFactor) - : imageToVideo - ? Duration(milliseconds: 200 * playbackDelayFactor) - : Duration(milliseconds: 400 * playbackDelayFactor), () { - if (!context.mounted) { - return; - } - - currentAsset.value = value; - if (currentAsset.value == asset) { - onPlaybackReady(); - } - }); - }); - - useEffect( - () { - // If opening a remote video from a hero animation, delay visibility to avoid a stutter - final timer = isVisible.value - ? null - : Timer( - const Duration(milliseconds: 300), - () => isVisible.value = true, - ); - - return () { - timer?.cancel(); - final playerController = controller.value; - if (playerController == null) { + Platform.isIOS + ? Duration(milliseconds: 300 * playbackDelayFactor) + : imageToVideo + ? Duration(milliseconds: 200 * playbackDelayFactor) + : Duration(milliseconds: 400 * playbackDelayFactor), + () { + if (!context.mounted) { return; } - removeListeners(playerController); - playerController.stop().catchError((error) { - log.fine('Error stopping video: $error'); - }); - WakelockPlus.disable(); - }; - }, - const [], - ); + currentAsset.value = value; + if (currentAsset.value == asset) { + onPlaybackReady(); + } + }, + ); + }); + + useEffect(() { + // If opening a remote video from a hero animation, delay visibility to avoid a stutter + final timer = isVisible.value ? null : Timer(const Duration(milliseconds: 300), () => isVisible.value = true); + + return () { + timer?.cancel(); + final playerController = controller.value; + if (playerController == null) { + return; + } + removeListeners(playerController); + playerController.stop().catchError((error) { + log.fine('Error stopping video: $error'); + }); + + WakelockPlus.disable(); + }; + }, const []); useOnAppLifecycleStateChange((_, state) async { if (state == AppLifecycleState.resumed && shouldPlayOnForeground.value) { @@ -393,12 +380,7 @@ class NativeVideoViewerPage extends HookConsumerWidget { child: AspectRatio( key: ValueKey(asset), aspectRatio: aspectRatio.value!, - child: isCurrent - ? NativeVideoPlayerView( - key: ValueKey(asset), - onViewReady: initController, - ) - : null, + child: isCurrent ? NativeVideoPlayerView(key: ValueKey(asset), onViewReady: initController) : null, ), ), ), diff --git a/mobile/lib/pages/common/settings.page.dart b/mobile/lib/pages/common/settings.page.dart index d18a7a1133..d7ecb7e582 100644 --- a/mobile/lib/pages/common/settings.page.dart +++ b/mobile/lib/pages/common/settings.page.dart @@ -18,67 +18,31 @@ import 'package:immich_mobile/widgets/settings/preference_settings/preference_se import 'package:immich_mobile/widgets/settings/settings_card.dart'; enum SettingSection { - beta( - 'beta_sync', - Icons.sync_outlined, - "beta_sync_subtitle", - ), - advanced( - 'advanced', - Icons.build_outlined, - "advanced_settings_tile_subtitle", - ), - assetViewer( - 'asset_viewer_settings_title', - Icons.image_outlined, - "asset_viewer_settings_subtitle", - ), - backup( - 'backup', - Icons.cloud_upload_outlined, - "backup_setting_subtitle", - ), - languages( - 'language', - Icons.language, - "setting_languages_subtitle", - ), - networking( - 'networking_settings', - Icons.wifi, - "networking_subtitle", - ), - notifications( - 'notifications', - Icons.notifications_none_rounded, - "setting_notifications_subtitle", - ), - preferences( - 'preferences_settings_title', - Icons.interests_outlined, - "preferences_settings_subtitle", - ), - timeline( - 'asset_list_settings_title', - Icons.auto_awesome_mosaic_outlined, - "asset_list_settings_subtitle", - ); + beta('beta_sync', Icons.sync_outlined, "beta_sync_subtitle"), + advanced('advanced', Icons.build_outlined, "advanced_settings_tile_subtitle"), + assetViewer('asset_viewer_settings_title', Icons.image_outlined, "asset_viewer_settings_subtitle"), + backup('backup', Icons.cloud_upload_outlined, "backup_setting_subtitle"), + languages('language', Icons.language, "setting_languages_subtitle"), + networking('networking_settings', Icons.wifi, "networking_subtitle"), + notifications('notifications', Icons.notifications_none_rounded, "setting_notifications_subtitle"), + preferences('preferences_settings_title', Icons.interests_outlined, "preferences_settings_subtitle"), + timeline('asset_list_settings_title', Icons.auto_awesome_mosaic_outlined, "asset_list_settings_subtitle"); final String title; final String subtitle; final IconData icon; Widget get widget => switch (this) { - SettingSection.beta => const _BetaLandscapeToggle(), - SettingSection.advanced => const AdvancedSettings(), - SettingSection.assetViewer => const AssetViewerSettings(), - SettingSection.backup => const BackupSettings(), - SettingSection.languages => const LanguageSettings(), - SettingSection.networking => const NetworkingSettings(), - SettingSection.notifications => const NotificationSetting(), - SettingSection.preferences => const PreferenceSetting(), - SettingSection.timeline => const AssetListSettings(), - }; + SettingSection.beta => const _BetaLandscapeToggle(), + SettingSection.advanced => const AdvancedSettings(), + SettingSection.assetViewer => const AssetViewerSettings(), + SettingSection.backup => const BackupSettings(), + SettingSection.languages => const LanguageSettings(), + SettingSection.networking => const NetworkingSettings(), + SettingSection.notifications => const NotificationSetting(), + SettingSection.preferences => const PreferenceSetting(), + SettingSection.timeline => const AssetListSettings(), + }; const SettingSection(this.title, this.icon, this.subtitle); } @@ -91,10 +55,7 @@ class SettingsPage extends StatelessWidget { Widget build(BuildContext context) { context.locale; return Scaffold( - appBar: AppBar( - centerTitle: false, - title: const Text('settings').tr(), - ), + appBar: AppBar(centerTitle: false, title: const Text('settings').tr()), body: context.isMobile ? const _MobileLayout() : const _TabletLayout(), ); } @@ -164,10 +125,7 @@ class _TabletLayout extends HookWidget { ), ), const VerticalDivider(width: 1), - Expanded( - flex: 4, - child: selectedSection.value.widget, - ), + Expanded(flex: 4, child: selectedSection.value.widget), ], ); } @@ -198,10 +156,7 @@ class SettingsSubPage extends StatelessWidget { Widget build(BuildContext context) { context.locale; return Scaffold( - appBar: AppBar( - centerTitle: false, - title: Text(section.title).tr(), - ), + appBar: AppBar(centerTitle: false, title: Text(section.title).tr()), body: section.widget, ); } diff --git a/mobile/lib/pages/common/splash_screen.page.dart b/mobile/lib/pages/common/splash_screen.page.dart index 47cd64f7f9..2bda4f90f9 100644 --- a/mobile/lib/pages/common/splash_screen.page.dart +++ b/mobile/lib/pages/common/splash_screen.page.dart @@ -43,31 +43,26 @@ class SplashScreenPageState extends ConsumerState { final accessToken = Store.tryGet(StoreKey.accessToken); if (accessToken != null && serverUrl != null && endpoint != null) { - ref.read(authProvider.notifier).saveAuthInfo(accessToken: accessToken).then( - (a) => { - log.info('Successfully updated auth info with access token: $accessToken'), - }, + ref + .read(authProvider.notifier) + .saveAuthInfo(accessToken: accessToken) + .then( + (a) => {log.info('Successfully updated auth info with access token: $accessToken')}, onError: (exception) => { - log.severe( - 'Failed to update auth info with access token: $accessToken', - ), + log.severe('Failed to update auth info with access token: $accessToken'), ref.read(authProvider.notifier).logout(), context.replaceRoute(const LoginRoute()), }, ); } else { - log.severe( - 'Missing crucial offline login info - Logging out completely', - ); + log.severe('Missing crucial offline login info - Logging out completely'); ref.read(authProvider.notifier).logout(); context.replaceRoute(const LoginRoute()); return; } if (context.router.current.name == SplashScreenRoute.name) { - context.replaceRoute( - Store.isBetaTimelineEnabled ? const TabShellRoute() : const TabControllerRoute(), - ); + context.replaceRoute(Store.isBetaTimelineEnabled ? const TabShellRoute() : const TabControllerRoute()); } if (Store.isBetaTimelineEnabled) { @@ -85,11 +80,7 @@ class SplashScreenPageState extends ConsumerState { Widget build(BuildContext context) { return const Scaffold( body: Center( - child: Image( - image: AssetImage('assets/immich-logo.png'), - width: 80, - filterQuality: FilterQuality.high, - ), + child: Image(image: AssetImage('assets/immich-logo.png'), width: 80, filterQuality: FilterQuality.high), ), ); } diff --git a/mobile/lib/pages/common/tab_controller.page.dart b/mobile/lib/pages/common/tab_controller.page.dart index 676b1db11b..ef637ba1c8 100644 --- a/mobile/lib/pages/common/tab_controller.page.dart +++ b/mobile/lib/pages/common/tab_controller.page.dart @@ -36,9 +36,7 @@ class TabControllerPage extends HookConsumerWidget { width: 20, child: CircularProgressIndicator( strokeWidth: 2, - valueColor: AlwaysStoppedAnimation( - context.primaryColor, - ), + valueColor: AlwaysStoppedAnimation(context.primaryColor), ), ), ), @@ -65,51 +63,31 @@ class TabControllerPage extends HookConsumerWidget { final navigationDestinations = [ NavigationDestination( label: 'photos'.tr(), - icon: const Icon( - Icons.photo_library_outlined, - ), + icon: const Icon(Icons.photo_library_outlined), selectedIcon: buildIcon( isProcessing: isRefreshingAssets, - icon: Icon( - Icons.photo_library, - color: context.primaryColor, - ), + icon: Icon(Icons.photo_library, color: context.primaryColor), ), ), NavigationDestination( label: 'search'.tr(), - icon: const Icon( - Icons.search_rounded, - ), - selectedIcon: Icon( - Icons.search, - color: context.primaryColor, - ), + icon: const Icon(Icons.search_rounded), + selectedIcon: Icon(Icons.search, color: context.primaryColor), ), NavigationDestination( label: 'albums'.tr(), - icon: const Icon( - Icons.photo_album_outlined, - ), + icon: const Icon(Icons.photo_album_outlined), selectedIcon: buildIcon( isProcessing: isRefreshingRemoteAlbums, - icon: Icon( - Icons.photo_album_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.photo_album_rounded, color: context.primaryColor), ), ), NavigationDestination( label: 'library'.tr(), - icon: const Icon( - Icons.space_dashboard_outlined, - ), + icon: const Icon(Icons.space_dashboard_outlined), selectedIcon: buildIcon( isProcessing: isRefreshingAssets, - icon: Icon( - Icons.space_dashboard_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.space_dashboard_rounded, color: context.primaryColor), ), ), ]; @@ -125,13 +103,7 @@ class TabControllerPage extends HookConsumerWidget { Widget navigationRail(TabsRouter tabsRouter) { return NavigationRail( destinations: navigationDestinations - .map( - (e) => NavigationRailDestination( - icon: e.icon, - label: Text(e.label), - selectedIcon: e.selectedIcon, - ), - ) + .map((e) => NavigationRailDestination(icon: e.icon, label: Text(e.label), selectedIcon: e.selectedIcon)) .toList(), onDestinationSelected: (index) => onNavigationSelected(tabsRouter, index), selectedIndex: tabsRouter.activeIndex, @@ -142,17 +114,9 @@ class TabControllerPage extends HookConsumerWidget { final multiselectEnabled = ref.watch(multiselectProvider); return AutoTabsRouter( - routes: [ - const PhotosRoute(), - SearchRoute(), - const AlbumsRoute(), - const LibraryRoute(), - ], + routes: [const PhotosRoute(), SearchRoute(), const AlbumsRoute(), const LibraryRoute()], duration: const Duration(milliseconds: 600), - transitionBuilder: (context, child, animation) => FadeTransition( - opacity: animation, - child: child, - ), + transitionBuilder: (context, child, animation) => FadeTransition(opacity: animation, child: child), builder: (context, child) { final tabsRouter = AutoTabsRouter.of(context); return PopScope( diff --git a/mobile/lib/pages/common/tab_shell.page.dart b/mobile/lib/pages/common/tab_shell.page.dart index 3961a8b14b..e06f7ca441 100644 --- a/mobile/lib/pages/common/tab_shell.page.dart +++ b/mobile/lib/pages/common/tab_shell.page.dart @@ -56,56 +56,30 @@ class _TabShellPageState extends ConsumerState { final navigationDestinations = [ NavigationDestination( label: 'photos'.tr(), - icon: const Icon( - Icons.photo_library_outlined, - ), - selectedIcon: Icon( - Icons.photo_library, - color: context.primaryColor, - ), + icon: const Icon(Icons.photo_library_outlined), + selectedIcon: Icon(Icons.photo_library, color: context.primaryColor), ), NavigationDestination( label: 'search'.tr(), - icon: const Icon( - Icons.search_rounded, - ), - selectedIcon: Icon( - Icons.search, - color: context.primaryColor, - ), + icon: const Icon(Icons.search_rounded), + selectedIcon: Icon(Icons.search, color: context.primaryColor), ), NavigationDestination( label: 'albums'.tr(), - icon: const Icon( - Icons.photo_album_outlined, - ), - selectedIcon: Icon( - Icons.photo_album_rounded, - color: context.primaryColor, - ), + icon: const Icon(Icons.photo_album_outlined), + selectedIcon: Icon(Icons.photo_album_rounded, color: context.primaryColor), ), NavigationDestination( label: 'library'.tr(), - icon: const Icon( - Icons.space_dashboard_outlined, - ), - selectedIcon: Icon( - Icons.space_dashboard_rounded, - color: context.primaryColor, - ), + icon: const Icon(Icons.space_dashboard_outlined), + selectedIcon: Icon(Icons.space_dashboard_rounded, color: context.primaryColor), ), ]; Widget navigationRail(TabsRouter tabsRouter) { return NavigationRail( destinations: navigationDestinations - .map( - (e) => NavigationRailDestination( - icon: e.icon, - label: Text(e.label), - selectedIcon: e.selectedIcon, - ), - ) + .map((e) => NavigationRailDestination(icon: e.icon, label: Text(e.label), selectedIcon: e.selectedIcon)) .toList(), onDestinationSelected: (index) => _onNavigationSelected(tabsRouter, index, ref), selectedIndex: tabsRouter.activeIndex, @@ -115,17 +89,9 @@ class _TabShellPageState extends ConsumerState { } return AutoTabsRouter( - routes: [ - const MainTimelineRoute(), - DriftSearchRoute(), - const DriftAlbumsRoute(), - const DriftLibraryRoute(), - ], + routes: [const MainTimelineRoute(), DriftSearchRoute(), const DriftAlbumsRoute(), const DriftLibraryRoute()], duration: const Duration(milliseconds: 600), - transitionBuilder: (context, child, animation) => FadeTransition( - opacity: animation, - child: child, - ), + transitionBuilder: (context, child, animation) => FadeTransition(opacity: animation, child: child), builder: (context, child) { final tabsRouter = AutoTabsRouter.of(context); return PopScope( @@ -142,10 +108,7 @@ class _TabShellPageState extends ConsumerState { ], ) : child, - bottomNavigationBar: _BottomNavigationBar( - tabsRouter: tabsRouter, - destinations: navigationDestinations, - ), + bottomNavigationBar: _BottomNavigationBar(tabsRouter: tabsRouter, destinations: navigationDestinations), ), ); }, @@ -175,10 +138,7 @@ void _onNavigationSelected(TabsRouter router, int index, WidgetRef ref) { } class _BottomNavigationBar extends ConsumerWidget { - const _BottomNavigationBar({ - required this.tabsRouter, - required this.destinations, - }); + const _BottomNavigationBar({required this.tabsRouter, required this.destinations}); final List destinations; final TabsRouter tabsRouter; diff --git a/mobile/lib/pages/editing/crop.page.dart b/mobile/lib/pages/editing/crop.page.dart index 97ef069e7b..35fd615800 100644 --- a/mobile/lib/pages/editing/crop.page.dart +++ b/mobile/lib/pages/editing/crop.page.dart @@ -32,20 +32,10 @@ class CropImagePage extends HookWidget { leading: CloseButton(color: context.primaryColor), actions: [ IconButton( - icon: Icon( - Icons.done_rounded, - color: context.primaryColor, - size: 24, - ), + icon: Icon(Icons.done_rounded, color: context.primaryColor, size: 24), onPressed: () async { final croppedImage = await cropController.croppedImage(); - context.pushRoute( - EditImageRoute( - asset: asset, - image: croppedImage, - isEdited: true, - ), - ); + context.pushRoute(EditImageRoute(asset: asset, image: croppedImage, isEdited: true)); }, ), ], @@ -60,11 +50,7 @@ class CropImagePage extends HookWidget { padding: const EdgeInsets.only(top: 20), width: constraints.maxWidth * 0.9, height: constraints.maxHeight * 0.6, - child: CropImage( - controller: cropController, - image: image, - gridColor: Colors.white, - ), + child: CropImage(controller: cropController, image: image, gridColor: Colors.white), ), Expanded( child: Container( @@ -81,28 +67,18 @@ class CropImagePage extends HookWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ Padding( - padding: const EdgeInsets.only( - left: 20, - right: 20, - bottom: 10, - ), + padding: const EdgeInsets.only(left: 20, right: 20, bottom: 10), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ IconButton( - icon: Icon( - Icons.rotate_left, - color: context.themeData.iconTheme.color, - ), + icon: Icon(Icons.rotate_left, color: context.themeData.iconTheme.color), onPressed: () { cropController.rotateLeft(); }, ), IconButton( - icon: Icon( - Icons.rotate_right, - color: context.themeData.iconTheme.color, - ), + icon: Icon(Icons.rotate_right, color: context.themeData.iconTheme.color), onPressed: () { cropController.rotateRight(); }, @@ -178,17 +154,14 @@ class _AspectRatioButton extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ IconButton( - icon: Icon( - switch (label) { - 'Free' => Icons.crop_free_rounded, - '1:1' => Icons.crop_square_rounded, - '16:9' => Icons.crop_16_9_rounded, - '3:2' => Icons.crop_3_2_rounded, - '7:5' => Icons.crop_7_5_rounded, - _ => Icons.crop_free_rounded, - }, - color: aspectRatio.value == ratio ? context.primaryColor : context.themeData.iconTheme.color, - ), + icon: Icon(switch (label) { + 'Free' => Icons.crop_free_rounded, + '1:1' => Icons.crop_square_rounded, + '16:9' => Icons.crop_16_9_rounded, + '3:2' => Icons.crop_3_2_rounded, + '7:5' => Icons.crop_7_5_rounded, + _ => Icons.crop_free_rounded, + }, color: aspectRatio.value == ratio ? context.primaryColor : context.themeData.iconTheme.color), onPressed: () { cropController.crop = const Rect.fromLTRB(0.1, 0.1, 0.9, 0.9); aspectRatio.value = ratio; diff --git a/mobile/lib/pages/editing/edit.page.dart b/mobile/lib/pages/editing/edit.page.dart index 70940e9552..c9ab014456 100644 --- a/mobile/lib/pages/editing/edit.page.dart +++ b/mobile/lib/pages/editing/edit.page.dart @@ -29,51 +29,34 @@ class EditImagePage extends ConsumerWidget { final Image image; final bool isEdited; - const EditImagePage({ - super.key, - required this.asset, - required this.image, - required this.isEdited, - }); + const EditImagePage({super.key, required this.asset, required this.image, required this.isEdited}); Future _imageToUint8List(Image image) async { final Completer completer = Completer(); - image.image.resolve(const ImageConfiguration()).addListener( - ImageStreamListener( - (ImageInfo info, bool _) { - info.image.toByteData(format: ImageByteFormat.png).then((byteData) { - if (byteData != null) { - completer.complete(byteData.buffer.asUint8List()); - } else { - completer.completeError('Failed to convert image to bytes'); - } - }); - }, - onError: (exception, stackTrace) => completer.completeError(exception), - ), + image.image + .resolve(const ImageConfiguration()) + .addListener( + ImageStreamListener((ImageInfo info, bool _) { + info.image.toByteData(format: ImageByteFormat.png).then((byteData) { + if (byteData != null) { + completer.complete(byteData.buffer.asUint8List()); + } else { + completer.completeError('Failed to convert image to bytes'); + } + }); + }, onError: (exception, stackTrace) => completer.completeError(exception)), ); return completer.future; } - Future _saveEditedImage( - BuildContext context, - Asset asset, - Image image, - WidgetRef ref, - ) async { + Future _saveEditedImage(BuildContext context, Asset asset, Image image, WidgetRef ref) async { try { final Uint8List imageData = await _imageToUint8List(image); - await ref.read(fileMediaRepositoryProvider).saveImage( - imageData, - title: "${p.withoutExtension(asset.fileName)}_edited.jpg", - ); + await ref + .read(fileMediaRepositoryProvider) + .saveImage(imageData, title: "${p.withoutExtension(asset.fileName)}_edited.jpg"); await ref.read(albumProvider.notifier).refreshDeviceAlbums(); context.navigator.popUntil((route) => route.isFirst); - ImmichToast.show( - durationInSecond: 3, - context: context, - msg: 'Image Saved!', - gravity: ToastGravity.CENTER, - ); + ImmichToast.show(durationInSecond: 3, context: context, msg: 'Image Saved!', gravity: ToastGravity.CENTER); } catch (e) { ImmichToast.show( durationInSecond: 6, @@ -91,37 +74,23 @@ class EditImagePage extends ConsumerWidget { title: Text("edit".tr()), backgroundColor: context.scaffoldBackgroundColor, leading: IconButton( - icon: Icon( - Icons.close_rounded, - color: context.primaryColor, - size: 24, - ), + icon: Icon(Icons.close_rounded, color: context.primaryColor, size: 24), onPressed: () => context.navigator.popUntil((route) => route.isFirst), ), actions: [ TextButton( onPressed: isEdited ? () => _saveEditedImage(context, asset, image, ref) : null, - child: Text( - "save_to_gallery".tr(), - style: TextStyle( - color: isEdited ? context.primaryColor : Colors.grey, - ), - ), + child: Text("save_to_gallery".tr(), style: TextStyle(color: isEdited ? context.primaryColor : Colors.grey)), ), ], ), backgroundColor: context.scaffoldBackgroundColor, body: Center( child: ConstrainedBox( - constraints: BoxConstraints( - maxHeight: context.height * 0.7, - maxWidth: context.width * 0.9, - ), + constraints: BoxConstraints(maxHeight: context.height * 0.7, maxWidth: context.width * 0.9), child: Container( decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(7), - ), + borderRadius: const BorderRadius.all(Radius.circular(7)), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.2), @@ -132,13 +101,8 @@ class EditImagePage extends ConsumerWidget { ], ), child: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(7), - ), - child: Image( - image: image.image, - fit: BoxFit.contain, - ), + borderRadius: const BorderRadius.all(Radius.circular(7)), + child: Image(image: image.image, fit: BoxFit.contain), ), ), ), @@ -148,9 +112,7 @@ class EditImagePage extends ConsumerWidget { margin: const EdgeInsets.only(bottom: 60, right: 10, left: 10, top: 10), decoration: BoxDecoration( color: context.scaffoldBackgroundColor, - borderRadius: const BorderRadius.all( - Radius.circular(30), - ), + borderRadius: const BorderRadius.all(Radius.circular(30)), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, @@ -159,15 +121,9 @@ class EditImagePage extends ConsumerWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( - icon: Icon( - Icons.crop_rotate_rounded, - color: context.themeData.iconTheme.color, - size: 25, - ), + icon: Icon(Icons.crop_rotate_rounded, color: context.themeData.iconTheme.color, size: 25), onPressed: () { - context.pushRoute( - CropImageRoute(asset: asset, image: image), - ); + context.pushRoute(CropImageRoute(asset: asset, image: image)); }, ), Text("crop".tr(), style: context.textTheme.displayMedium), @@ -177,18 +133,9 @@ class EditImagePage extends ConsumerWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( - icon: Icon( - Icons.filter, - color: context.themeData.iconTheme.color, - size: 25, - ), + icon: Icon(Icons.filter, color: context.themeData.iconTheme.color, size: 25), onPressed: () { - context.pushRoute( - FilterImageRoute( - asset: asset, - image: image, - ), - ); + context.pushRoute(FilterImageRoute(asset: asset, image: image)); }, ), Text("filter".tr(), style: context.textTheme.displayMedium), diff --git a/mobile/lib/pages/editing/filter.page.dart b/mobile/lib/pages/editing/filter.page.dart index 3dc8d28a94..6d41b4c5b8 100644 --- a/mobile/lib/pages/editing/filter.page.dart +++ b/mobile/lib/pages/editing/filter.page.dart @@ -18,21 +18,14 @@ class FilterImagePage extends HookWidget { final Image image; final Asset asset; - const FilterImagePage({ - super.key, - required this.image, - required this.asset, - }); + const FilterImagePage({super.key, required this.image, required this.asset}); @override Widget build(BuildContext context) { final colorFilter = useState(filters[0]); final selectedFilterIndex = useState(0); - Future createFilteredImage( - ui.Image inputImage, - ColorFilter filter, - ) { + Future createFilteredImage(ui.Image inputImage, ColorFilter filter) { final completer = Completer(); final size = Size(inputImage.width.toDouble(), inputImage.height.toDouble()); final recorder = ui.PictureRecorder(); @@ -55,11 +48,13 @@ class FilterImagePage extends HookWidget { Future applyFilterAndConvert(ColorFilter filter) async { final completer = Completer(); - image.image.resolve(ImageConfiguration.empty).addListener( - ImageStreamListener((ImageInfo info, bool _) { - completer.complete(info.image); - }), - ); + image.image + .resolve(ImageConfiguration.empty) + .addListener( + ImageStreamListener((ImageInfo info, bool _) { + completer.complete(info.image); + }), + ); final uiImage = await completer.future; final filteredUiImage = await createFilteredImage(uiImage, filter); @@ -76,20 +71,10 @@ class FilterImagePage extends HookWidget { leading: CloseButton(color: context.primaryColor), actions: [ IconButton( - icon: Icon( - Icons.done_rounded, - color: context.primaryColor, - size: 24, - ), + icon: Icon(Icons.done_rounded, color: context.primaryColor, size: 24), onPressed: () async { final filteredImage = await applyFilterAndConvert(colorFilter.value); - context.pushRoute( - EditImageRoute( - asset: asset, - image: filteredImage, - isEdited: true, - ), - ); + context.pushRoute(EditImageRoute(asset: asset, image: filteredImage, isEdited: true)); }, ), ], @@ -100,10 +85,7 @@ class FilterImagePage extends HookWidget { SizedBox( height: context.height * 0.7, child: Center( - child: ColorFiltered( - colorFilter: colorFilter.value, - child: image, - ), + child: ColorFiltered(colorFilter: colorFilter.value, child: image), ), ), SizedBox( @@ -156,21 +138,14 @@ class _FilterButton extends StatelessWidget { width: 80, height: 80, decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(10), - ), + borderRadius: const BorderRadius.all(Radius.circular(10)), border: isSelected ? Border.all(color: context.primaryColor, width: 3) : null, ), child: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(10), - ), + borderRadius: const BorderRadius.all(Radius.circular(10)), child: ColorFiltered( colorFilter: filter, - child: FittedBox( - fit: BoxFit.cover, - child: image, - ), + child: FittedBox(fit: BoxFit.cover, child: image), ), ), ), diff --git a/mobile/lib/pages/library/archive.page.dart b/mobile/lib/pages/library/archive.page.dart index 2b4aa64f3b..8ca1bb9752 100644 --- a/mobile/lib/pages/library/archive.page.dart +++ b/mobile/lib/pages/library/archive.page.dart @@ -16,15 +16,10 @@ class ArchivePage extends HookConsumerWidget { final archiveRenderList = ref.watch(archiveTimelineProvider); final count = archiveRenderList.value?.totalAssets.toString() ?? "?"; return AppBar( - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), centerTitle: true, automaticallyImplyLeading: false, - title: const Text( - 'archive_page_title', - ).tr(namedArgs: {'count': count}), + title: const Text('archive_page_title').tr(namedArgs: {'count': count}), ); } diff --git a/mobile/lib/pages/library/favorite.page.dart b/mobile/lib/pages/library/favorite.page.dart index 070693fe4a..649d7727d5 100644 --- a/mobile/lib/pages/library/favorite.page.dart +++ b/mobile/lib/pages/library/favorite.page.dart @@ -14,15 +14,10 @@ class FavoritesPage extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { AppBar buildAppBar() { return AppBar( - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), centerTitle: true, automaticallyImplyLeading: false, - title: const Text( - 'favorites', - ).tr(), + title: const Text('favorites').tr(), ); } diff --git a/mobile/lib/pages/library/folder/folder.page.dart b/mobile/lib/pages/library/folder/folder.page.dart index d089aace6e..2968bca18e 100644 --- a/mobile/lib/pages/library/folder/folder.page.dart +++ b/mobile/lib/pages/library/folder/folder.page.dart @@ -16,10 +16,7 @@ import 'package:immich_mobile/utils/bytes_units.dart'; import 'package:immich_mobile/widgets/asset_grid/thumbnail_image.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; -RecursiveFolder? _findFolderInStructure( - RootFolder rootFolder, - RecursiveFolder targetFolder, -) { +RecursiveFolder? _findFolderInStructure(RootFolder rootFolder, RecursiveFolder targetFolder) { for (final folder in rootFolder.subfolders) { if (targetFolder.path == '/' && folder.path.isEmpty && folder.name == targetFolder.name) { return folder; @@ -49,29 +46,23 @@ class FolderPage extends HookConsumerWidget { final currentFolder = useState(folder); final sortOrder = useState(SortOrder.asc); - useEffect( - () { - if (folder == null) { - ref.read(folderStructureProvider.notifier).fetchFolders(sortOrder.value); - } - return null; - }, - [], - ); + useEffect(() { + if (folder == null) { + ref.read(folderStructureProvider.notifier).fetchFolders(sortOrder.value); + } + return null; + }, []); // Update current folder when root structure changes - useEffect( - () { - if (folder != null && folderState.hasValue) { - final updatedFolder = _findFolderInStructure(folderState.value!, folder!); - if (updatedFolder != null) { - currentFolder.value = updatedFolder; - } + useEffect(() { + if (folder != null && folderState.hasValue) { + final updatedFolder = _findFolderInStructure(folderState.value!, folder!); + if (updatedFolder != null) { + currentFolder.value = updatedFolder; } - return null; - }, - [folderState], - ); + } + return null; + }, [folderState]); void onToggleSortOrder() { final newOrder = sortOrder.value == SortOrder.asc ? SortOrder.desc : SortOrder.asc; @@ -86,38 +77,19 @@ class FolderPage extends HookConsumerWidget { title: Text(currentFolder.value?.name ?? tr("folders")), elevation: 0, centerTitle: false, - actions: [ - IconButton( - icon: const Icon(Icons.swap_vert), - onPressed: onToggleSortOrder, - ), - ], + actions: [IconButton(icon: const Icon(Icons.swap_vert), onPressed: onToggleSortOrder)], ), body: folderState.when( data: (rootFolder) { if (folder == null) { - return FolderContent( - folder: rootFolder, - root: rootFolder, - sortOrder: sortOrder.value, - ); + return FolderContent(folder: rootFolder, root: rootFolder, sortOrder: sortOrder.value); } else { - return FolderContent( - folder: currentFolder.value!, - root: rootFolder, - sortOrder: sortOrder.value, - ); + return FolderContent(folder: currentFolder.value!, root: rootFolder, sortOrder: sortOrder.value); } }, - loading: () => const Center( - child: CircularProgressIndicator(), - ), + loading: () => const Center(child: CircularProgressIndicator()), error: (error, stack) { - ImmichToast.show( - context: context, - msg: "failed_to_load_folder".tr(), - toastType: ToastType.error, - ); + ImmichToast.show(context: context, msg: "failed_to_load_folder".tr(), toastType: ToastType.error); return Center(child: const Text("failed_to_load_folder").tr()); }, ), @@ -130,26 +102,18 @@ class FolderContent extends HookConsumerWidget { final RootFolder root; final SortOrder sortOrder; - const FolderContent({ - super.key, - this.folder, - required this.root, - this.sortOrder = SortOrder.asc, - }); + const FolderContent({super.key, this.folder, required this.root, this.sortOrder = SortOrder.asc}); @override Widget build(BuildContext context, WidgetRef ref) { final folderRenderlist = ref.watch(folderRenderListProvider(folder!)); // Initial asset fetch - useEffect( - () { - if (folder == null) return; - ref.read(folderRenderListProvider(folder!).notifier).fetchAssets(sortOrder); - return null; - }, - [folder], - ); + useEffect(() { + if (folder == null) return; + ref.read(folderRenderListProvider(folder!).notifier).fetchAssets(sortOrder); + return null; + }, [folder]); if (folder == null) { return Center(child: const Text("folder_not_found").tr()); @@ -182,18 +146,12 @@ class FolderContent extends HookConsumerWidget { if (folder!.subfolders.isNotEmpty) ...folder!.subfolders.map( (subfolder) => LargeLeadingTile( - leading: Icon( - Icons.folder, - color: context.primaryColor, - size: 48, - ), + leading: Icon(Icons.folder, color: context.primaryColor, size: 48), title: Text( subfolder.name, softWrap: false, overflow: TextOverflow.ellipsis, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), ), subtitle: subfolder.subfolders.isNotEmpty ? Text( @@ -212,23 +170,15 @@ class FolderContent extends HookConsumerWidget { onTap: () { ref.read(currentAssetProvider.notifier).set(asset); context.pushRoute( - GalleryViewerRoute( - renderList: list, - initialIndex: list.allAssets!.indexOf(asset), - ), + GalleryViewerRoute(renderList: list, initialIndex: list.allAssets!.indexOf(asset)), ); }, leading: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(15), - ), + borderRadius: const BorderRadius.all(Radius.circular(15)), child: SizedBox( width: 80, height: 80, - child: ThumbnailImage( - asset: asset, - showStorageIndicator: false, - ), + child: ThumbnailImage(asset: asset, showStorageIndicator: false), ), ), title: Text( @@ -236,30 +186,20 @@ class FolderContent extends HookConsumerWidget { maxLines: 2, softWrap: false, overflow: TextOverflow.ellipsis, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), ), subtitle: Text( "${asset.exifInfo?.fileSize != null ? formatBytes(asset.exifInfo?.fileSize ?? 0) : ""} • ${DateFormat.yMMMd().format(asset.fileCreatedAt)}", - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), ), ), ], ); }, - loading: () => const Center( - child: CircularProgressIndicator(), - ), + loading: () => const Center(child: CircularProgressIndicator()), error: (error, stack) { - ImmichToast.show( - context: context, - msg: "failed_to_load_assets".tr(), - toastType: ToastType.error, - ); + ImmichToast.show(context: context, msg: "failed_to_load_assets".tr(), toastType: ToastType.error); return Center(child: const Text("failed_to_load_assets").tr()); }, ), @@ -273,11 +213,7 @@ class FolderPath extends StatelessWidget { final RootFolder currentFolder; final RootFolder root; - const FolderPath({ - super.key, - required this.currentFolder, - required this.root, - }); + const FolderPath({super.key, required this.currentFolder, required this.root}); @override Widget build(BuildContext context) { diff --git a/mobile/lib/pages/library/library.page.dart b/mobile/lib/pages/library/library.page.dart index f51817d067..483427d2de 100644 --- a/mobile/lib/pages/library/library.page.dart +++ b/mobile/lib/pages/library/library.page.dart @@ -74,17 +74,11 @@ class LibraryPage extends ConsumerWidget { const Wrap( spacing: 8, runSpacing: 8, - children: [ - PeopleCollectionCard(), - PlacesCollectionCard(), - LocalAlbumsCollectionCard(), - ], + children: [PeopleCollectionCard(), PlacesCollectionCard(), LocalAlbumsCollectionCard()], ), const SizedBox(height: 12), const QuickAccessButtons(), - const SizedBox( - height: 32, - ), + const SizedBox(height: 32), ], ), ), @@ -100,13 +94,8 @@ class QuickAccessButtons extends ConsumerWidget { return Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(10), - width: 1, - ), - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(10), width: 1), + borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( colors: [ context.colorScheme.primary.withAlpha(10), @@ -130,41 +119,26 @@ class QuickAccessButtons extends ConsumerWidget { bottomRight: Radius.circular(partners.isEmpty ? 20 : 0), ), ), - leading: const Icon( - Icons.folder_outlined, - size: 26, - ), + leading: const Icon(Icons.folder_outlined, size: 26), title: Text( IntlKeys.folders.tr(), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), ), onTap: () => context.pushRoute(FolderRoute()), ), ListTile( - leading: const Icon( - Icons.lock_outline_rounded, - size: 26, - ), + leading: const Icon(Icons.lock_outline_rounded, size: 26), title: Text( IntlKeys.locked_folder.tr(), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), ), onTap: () => context.pushRoute(const LockedRoute()), ), ListTile( - leading: const Icon( - Icons.group_outlined, - size: 26, - ), + leading: const Icon(Icons.group_outlined, size: 26), title: Text( IntlKeys.partners.tr(), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), ), onTap: () => context.pushRoute(const PartnerRoute()), ), @@ -196,24 +170,13 @@ class PartnerList extends ConsumerWidget { bottomRight: Radius.circular(isLastItem ? 20 : 0), ), ), - contentPadding: const EdgeInsets.only( - left: 12.0, - right: 18.0, - ), + contentPadding: const EdgeInsets.only(left: 12.0, right: 18.0), leading: userAvatar(context, partner, radius: 16), title: const Text( "partner_list_user_photos", - style: TextStyle( - fontWeight: FontWeight.w500, - ), - ).tr( - namedArgs: { - 'user': partner.name, - }, - ), - onTap: () => context.pushRoute( - (PartnerDetailRoute(partner: partner)), - ), + style: TextStyle(fontWeight: FontWeight.w500), + ).tr(namedArgs: {'user': partner.name}), + onTap: () => context.pushRoute((PartnerDetailRoute(partner: partner))), ); }, ); @@ -241,22 +204,15 @@ class PeopleCollectionCard extends ConsumerWidget { height: size, width: size, decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withAlpha(30), - context.colorScheme.primary.withAlpha(25), - ], + colors: [context.colorScheme.primary.withAlpha(30), context.colorScheme.primary.withAlpha(25)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), child: people.widgetWhen( - onLoading: () => const Center( - child: CircularProgressIndicator(), - ), + onLoading: () => const Center(child: CircularProgressIndicator()), onData: (people) { return GridView.count( crossAxisCount: 2, @@ -308,9 +264,7 @@ class LocalAlbumsCollectionCard extends HookConsumerWidget { final size = context.width * widthFactor - 20.0; return GestureDetector( - onTap: () => context.pushRoute( - const LocalAlbumsRoute(), - ), + onTap: () => context.pushRoute(const LocalAlbumsRoute()), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -321,10 +275,7 @@ class LocalAlbumsCollectionCard extends HookConsumerWidget { decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withAlpha(30), - context.colorScheme.primary.withAlpha(25), - ], + colors: [context.colorScheme.primary.withAlpha(30), context.colorScheme.primary.withAlpha(25)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), @@ -336,10 +287,7 @@ class LocalAlbumsCollectionCard extends HookConsumerWidget { mainAxisSpacing: 8, physics: const NeverScrollableScrollPhysics(), children: albums.take(4).map((album) { - return AlbumThumbnailCard( - album: album, - showTitle: false, - ); + return AlbumThumbnailCard(album: album, showTitle: false); }).toList(), ), ), @@ -373,11 +321,7 @@ class PlacesCollectionCard extends StatelessWidget { final size = context.width * widthFactor - 20.0; return GestureDetector( - onTap: () => context.pushRoute( - PlacesCollectionRoute( - currentLocation: null, - ), - ), + onTap: () => context.pushRoute(PlacesCollectionRoute(currentLocation: null)), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -392,10 +336,7 @@ class PlacesCollectionCard extends StatelessWidget { child: IgnorePointer( child: MapThumbnail( zoom: 8, - centre: const LatLng( - 21.44950, - -157.91959, - ), + centre: const LatLng(21.44950, -157.91959), showAttribution: false, themeMode: context.isDarkTheme ? ThemeMode.dark : ThemeMode.light, ), @@ -425,12 +366,7 @@ class ActionButton extends StatelessWidget { final IconData icon; final String label; - const ActionButton({ - super.key, - required this.onPressed, - required this.icon, - required this.label, - }); + const ActionButton({super.key, required this.onPressed, required this.icon, required this.label}); @override Widget build(BuildContext context) { @@ -439,13 +375,7 @@ class ActionButton extends StatelessWidget { onPressed: onPressed, label: Padding( padding: const EdgeInsets.only(left: 4.0), - child: Text( - label, - style: TextStyle( - color: context.colorScheme.onSurface, - fontSize: 15, - ), - ), + child: Text(label, style: TextStyle(color: context.colorScheme.onSurface, fontSize: 15)), ), style: FilledButton.styleFrom( elevation: 0, @@ -454,16 +384,10 @@ class ActionButton extends StatelessWidget { alignment: Alignment.centerLeft, shape: RoundedRectangleBorder( borderRadius: const BorderRadius.all(Radius.circular(25)), - side: BorderSide( - color: context.colorScheme.onSurface.withAlpha(10), - width: 1, - ), + side: BorderSide(color: context.colorScheme.onSurface.withAlpha(10), width: 1), ), ), - icon: Icon( - icon, - color: context.primaryColor, - ), + icon: Icon(icon, color: context.primaryColor), ), ); } diff --git a/mobile/lib/pages/library/local_albums.page.dart b/mobile/lib/pages/library/local_albums.page.dart index 5c6091c76d..e52a8326df 100644 --- a/mobile/lib/pages/library/local_albums.page.dart +++ b/mobile/lib/pages/library/local_albums.page.dart @@ -18,9 +18,7 @@ class LocalAlbumsPage extends HookConsumerWidget { final albums = ref.watch(localAlbumsProvider); return Scaffold( - appBar: AppBar( - title: Text('on_this_device'.tr()), - ), + appBar: AppBar(title: Text('on_this_device'.tr())), body: ListView.builder( padding: const EdgeInsets.all(18.0), itemCount: albums.length, @@ -28,31 +26,18 @@ class LocalAlbumsPage extends HookConsumerWidget { return Padding( padding: const EdgeInsets.only(bottom: 8.0), child: LargeLeadingTile( - leadingPadding: const EdgeInsets.only( - right: 16, - ), + leadingPadding: const EdgeInsets.only(right: 16), leading: ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(15)), - child: ImmichThumbnail( - asset: albums[index].thumbnail.value, - width: 80, - height: 80, - ), + child: ImmichThumbnail(asset: albums[index].thumbnail.value, width: 80, height: 80), ), title: Text( albums[index].name, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), ), subtitle: Text( - 'items_count'.t( - context: context, - args: {'count': albums[index].assetCount}, - ), - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + 'items_count'.t(context: context, args: {'count': albums[index].assetCount}), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), onTap: () => context.pushRoute(AlbumViewerRoute(albumId: albums[index].id)), ), diff --git a/mobile/lib/pages/library/locked/locked.page.dart b/mobile/lib/pages/library/locked/locked.page.dart index eef12a7107..aea62e0051 100644 --- a/mobile/lib/pages/library/locked/locked.page.dart +++ b/mobile/lib/pages/library/locked/locked.page.dart @@ -19,29 +19,23 @@ class LockedPage extends HookConsumerWidget { final showOverlay = useState(false); final authProviderNotifier = ref.read(authProvider.notifier); // lock the page when it is destroyed - useEffect( - () { - return () { - authProviderNotifier.lockPinCode(); - }; - }, - [], - ); + useEffect(() { + return () { + authProviderNotifier.lockPinCode(); + }; + }, []); - useEffect( - () { - if (context.mounted) { - if (appLifeCycle == AppLifecycleState.resumed) { - showOverlay.value = false; - } else { - showOverlay.value = true; - } + useEffect(() { + if (context.mounted) { + if (appLifeCycle == AppLifecycleState.resumed) { + showOverlay.value = false; + } else { + showOverlay.value = true; } + } - return null; - }, - [appLifeCycle], - ); + return null; + }, [appLifeCycle]); return Scaffold( appBar: ref.watch(multiselectProvider) ? null : const LockPageAppBar(), @@ -51,12 +45,7 @@ class LockedPage extends HookConsumerWidget { renderListProvider: lockedTimelineProvider, topWidget: Padding( padding: const EdgeInsets.all(16.0), - child: Center( - child: Text( - 'no_locked_photos_message'.tr(), - style: context.textTheme.labelLarge, - ), - ), + child: Center(child: Text('no_locked_photos_message'.tr(), style: context.textTheme.labelLarge)), ), editEnabled: false, favoriteEnabled: false, @@ -84,9 +73,7 @@ class LockPageAppBar extends ConsumerWidget implements PreferredSizeWidget { ), centerTitle: true, automaticallyImplyLeading: false, - title: const Text( - 'locked_folder', - ).tr(), + title: const Text('locked_folder').tr(), ); } diff --git a/mobile/lib/pages/library/locked/pin_auth.page.dart b/mobile/lib/pages/library/locked/pin_auth.page.dart index abe2247927..36befa0016 100644 --- a/mobile/lib/pages/library/locked/pin_auth.page.dart +++ b/mobile/lib/pages/library/locked/pin_auth.page.dart @@ -23,18 +23,12 @@ class PinAuthPage extends HookConsumerWidget { final isBetaTimeline = Store.isBetaTimelineEnabled; Future registerBiometric(String pinCode) async { - final isRegistered = await ref.read(localAuthProvider.notifier).registerBiometric( - context, - pinCode, - ); + final isRegistered = await ref.read(localAuthProvider.notifier).registerBiometric(context, pinCode); if (isRegistered) { context.showSnackBar( SnackBar( - content: Text( - 'biometric_auth_enabled'.tr(), - style: context.textTheme.labelLarge, - ), + content: Text('biometric_auth_enabled'.tr(), style: context.textTheme.labelLarge), duration: const Duration(seconds: 3), backgroundColor: context.colorScheme.primaryContainer, ), @@ -79,20 +73,14 @@ class PinAuthPage extends HookConsumerWidget { } return Scaffold( - appBar: AppBar( - title: Text('locked_folder'.tr()), - ), + appBar: AppBar(title: Text('locked_folder'.tr())), body: ListView( shrinkWrap: true, children: [ Padding( padding: const EdgeInsets.only(top: 36.0), child: showPinRegistrationForm.value - ? Center( - child: PinRegistrationForm( - onDone: () => showPinRegistrationForm.value = false, - ), - ) + ? Center(child: PinRegistrationForm(onDone: () => showPinRegistrationForm.value = false)) : Column( children: [ Center( @@ -112,17 +100,11 @@ class PinAuthPage extends HookConsumerWidget { Padding( padding: const EdgeInsets.only(right: 16.0), child: TextButton.icon( - icon: const Icon( - Icons.fingerprint, - size: 28, - ), + icon: const Icon(Icons.fingerprint, size: 28), onPressed: enableBiometricAuth, label: Text( 'use_biometric'.tr(), - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - fontSize: 18, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor, fontSize: 18), ), ), ), diff --git a/mobile/lib/pages/library/partner/drift_partner.page.dart b/mobile/lib/pages/library/partner/drift_partner.page.dart index d65f2bc094..171fe0ea0d 100644 --- a/mobile/lib/pages/library/partner/drift_partner.page.dart +++ b/mobile/lib/pages/library/partner/drift_partner.page.dart @@ -22,10 +22,7 @@ class DriftPartnerPage extends HookConsumerWidget { addNewUsersHandler() async { final potentialPartners = potentialPartnersAsync.value; if (potentialPartners == null || potentialPartners.isEmpty) { - ImmichToast.show( - context: context, - msg: "partner_page_no_more_users".tr(), - ); + ImmichToast.show(context: context, msg: "partner_page_no_more_users".tr()); return; } @@ -77,18 +74,13 @@ class DriftPartnerPage extends HookConsumerWidget { centerTitle: false, actions: [ IconButton( - onPressed: potentialPartnersAsync.whenOrNull( - data: (data) => addNewUsersHandler, - ), + onPressed: potentialPartnersAsync.whenOrNull(data: (data) => addNewUsersHandler), icon: const Icon(Icons.person_add), tooltip: "add_partner".tr(), ), ], ), - body: _SharedToPartnerList( - onAddPartner: addNewUsersHandler, - onDeletePartner: onDeleteUser, - ), + body: _SharedToPartnerList(onAddPartner: addNewUsersHandler, onDeletePartner: onDeleteUser), ); } } @@ -97,10 +89,7 @@ class _SharedToPartnerList extends ConsumerWidget { final VoidCallback onAddPartner; final Function(PartnerUserDto partner) onDeletePartner; - const _SharedToPartnerList({ - required this.onAddPartner, - required this.onDeletePartner, - }); + const _SharedToPartnerList({required this.onAddPartner, required this.onDeletePartner}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -116,10 +105,7 @@ class _SharedToPartnerList extends ConsumerWidget { children: [ Padding( padding: const EdgeInsets.symmetric(vertical: 8), - child: const Text( - "partner_page_empty_message", - style: TextStyle(fontSize: 14), - ).tr(), + child: const Text("partner_page_empty_message", style: TextStyle(fontSize: 14)).tr(), ), Align( alignment: Alignment.center, @@ -142,18 +128,13 @@ class _SharedToPartnerList extends ConsumerWidget { leading: PartnerUserAvatar(partner: partner), title: Text(partner.name), subtitle: Text(partner.email), - trailing: IconButton( - icon: const Icon(Icons.person_remove), - onPressed: () => onDeletePartner(partner), - ), + trailing: IconButton(icon: const Icon(Icons.person_remove), onPressed: () => onDeletePartner(partner)), ); }, ); }, loading: () => const Center(child: CircularProgressIndicator()), - error: (error, stack) => Center( - child: Text("Error loading partners: $error"), - ), + error: (error, stack) => Center(child: Text("Error loading partners: $error")), ); } } diff --git a/mobile/lib/pages/library/partner/partner.page.dart b/mobile/lib/pages/library/partner/partner.page.dart index fb0dfe2ec3..eae4228a2d 100644 --- a/mobile/lib/pages/library/partner/partner.page.dart +++ b/mobile/lib/pages/library/partner/partner.page.dart @@ -22,10 +22,7 @@ class PartnerPage extends HookConsumerWidget { addNewUsersHandler() async { final users = availableUsers.value; if (users == null || users.isEmpty) { - ImmichToast.show( - context: context, - msg: "partner_page_no_more_users".tr(), - ); + ImmichToast.show(context: context, msg: "partner_page_no_more_users".tr()); return; } @@ -40,10 +37,7 @@ class PartnerPage extends HookConsumerWidget { onPressed: () => context.pop(u), child: Row( children: [ - Padding( - padding: const EdgeInsets.only(right: 8), - child: userAvatar(context, u), - ), + Padding(padding: const EdgeInsets.only(right: 8), child: userAvatar(context, u)), Text(u.name), ], ), @@ -57,11 +51,7 @@ class PartnerPage extends HookConsumerWidget { if (ok) { ref.invalidate(partnerSharedByProvider); } else { - ImmichToast.show( - context: context, - msg: "partner_page_partner_add_failed".tr(), - toastType: ToastType.error, - ); + ImmichToast.show(context: context, msg: "partner_page_partner_add_failed".tr(), toastType: ToastType.error); } } } @@ -87,9 +77,7 @@ class PartnerPage extends HookConsumerWidget { padding: const EdgeInsets.only(left: 16.0, top: 16.0), child: Text( "partner_page_shared_to_title", - style: context.textTheme.titleSmall?.copyWith( - color: context.colorScheme.onSurface.withAlpha(200), - ), + style: context.textTheme.titleSmall?.copyWith(color: context.colorScheme.onSurface.withAlpha(200)), ).tr(), ), if (users.isNotEmpty) @@ -99,10 +87,7 @@ class PartnerPage extends HookConsumerWidget { itemBuilder: ((context, index) { return ListTile( leading: userAvatar(context, users[index]), - title: Text( - users[index].email, - style: context.textTheme.bodyLarge, - ), + title: Text(users[index].email, style: context.textTheme.bodyLarge), trailing: IconButton( icon: const Icon(Icons.person_remove), onPressed: () => onDeleteUser(users[index]), @@ -118,17 +103,12 @@ class PartnerPage extends HookConsumerWidget { children: [ Padding( padding: const EdgeInsets.symmetric(vertical: 8), - child: const Text( - "partner_page_empty_message", - style: TextStyle(fontSize: 14), - ).tr(), + child: const Text("partner_page_empty_message", style: TextStyle(fontSize: 14)).tr(), ), Align( alignment: Alignment.center, child: ElevatedButton.icon( - onPressed: availableUsers.whenOrNull( - data: (data) => addNewUsersHandler, - ), + onPressed: availableUsers.whenOrNull(data: (data) => addNewUsersHandler), icon: const Icon(Icons.person_add), label: const Text("add_partner").tr(), ), diff --git a/mobile/lib/pages/library/partner/partner_detail.page.dart b/mobile/lib/pages/library/partner/partner_detail.page.dart index 78af3f0939..1f15dab6a3 100644 --- a/mobile/lib/pages/library/partner/partner_detail.page.dart +++ b/mobile/lib/pages/library/partner/partner_detail.page.dart @@ -22,24 +22,18 @@ class PartnerDetailPage extends HookConsumerWidget { final inTimeline = useState(partner.inTimeline); bool toggleInProcess = false; - useEffect( - () { - Future.microtask( - () async => { - await ref.read(assetProvider.notifier).getAllAsset(), - }, - ); - return null; - }, - [], - ); + useEffect(() { + Future.microtask(() async => {await ref.read(assetProvider.notifier).getAllAsset()}); + return null; + }, []); void toggleInTimeline() async { if (toggleInProcess) return; toggleInProcess = true; try { - final ok = - await ref.read(partnerSharedWithProvider.notifier).updatePartner(partner, inTimeline: !inTimeline.value); + final ok = await ref + .read(partnerSharedWithProvider.notifier) + .updatePartner(partner, inTimeline: !inTimeline.value); if (ok) { inTimeline.value = !inTimeline.value; final action = inTimeline.value ? "shown on" : "hidden from"; @@ -65,28 +59,16 @@ class PartnerDetailPage extends HookConsumerWidget { return Scaffold( appBar: ref.watch(multiselectProvider) ? null - : AppBar( - title: Text(partner.name), - elevation: 0, - centerTitle: false, - ), + : AppBar(title: Text(partner.name), elevation: 0, centerTitle: false), body: MultiselectGrid( topWidget: Padding( padding: const EdgeInsets.only(left: 8.0, right: 8.0, top: 16.0), child: Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(10), - width: 1, - ), - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(10), width: 1), + borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withAlpha(10), - context.colorScheme.primary.withAlpha(15), - ], + colors: [context.colorScheme.primary.withAlpha(10), context.colorScheme.primary.withAlpha(15)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), @@ -96,18 +78,13 @@ class PartnerDetailPage extends HookConsumerWidget { child: ListTile( title: Text( "Show in timeline", - style: context.textTheme.titleSmall?.copyWith( - color: context.colorScheme.primary, - ), + style: context.textTheme.titleSmall?.copyWith(color: context.colorScheme.primary), ), subtitle: Text( "Show photos and videos from this user in your timeline", style: context.textTheme.bodyMedium, ), - trailing: Switch( - value: inTimeline.value, - onChanged: (_) => toggleInTimeline(), - ), + trailing: Switch(value: inTimeline.value, onChanged: (_) => toggleInTimeline()), ), ), ), diff --git a/mobile/lib/pages/library/people/people_collection.page.dart b/mobile/lib/pages/library/people/people_collection.page.dart index 837553ac40..375d4d2a96 100644 --- a/mobile/lib/pages/library/people/people_collection.page.dart +++ b/mobile/lib/pages/library/people/people_collection.page.dart @@ -21,10 +21,7 @@ class PeopleCollectionPage extends HookConsumerWidget { final formFocus = useFocusNode(); final ValueNotifier search = useState(null); - showNameEditModel( - String personId, - String personName, - ) { + showNameEditModel(String personId, String personName) { return showDialog( context: context, useRootNavigator: false, @@ -84,22 +81,14 @@ class PeopleCollectionPage extends HookConsumerWidget { children: [ GestureDetector( onTap: () { - context.pushRoute( - PersonResultRoute( - personId: person.id, - personName: person.name, - ), - ); + context.pushRoute(PersonResultRoute(personId: person.id, personName: person.name)); }, child: Material( shape: const CircleBorder(side: BorderSide.none), elevation: 3, child: CircleAvatar( maxRadius: isTablet ? 120 / 2 : 96 / 2, - backgroundImage: NetworkImage( - getFaceThumbnailUrl(person.id), - headers: headers, - ), + backgroundImage: NetworkImage(getFaceThumbnailUrl(person.id), headers: headers), ), ), ), @@ -115,15 +104,11 @@ class PeopleCollectionPage extends HookConsumerWidget { ), ) : Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), + padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Text( person.name, overflow: TextOverflow.ellipsis, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), ), ), ), diff --git a/mobile/lib/pages/library/places/places_collection.page.dart b/mobile/lib/pages/library/places/places_collection.page.dart index 98bf372a96..73c38a109c 100644 --- a/mobile/lib/pages/library/places/places_collection.page.dart +++ b/mobile/lib/pages/library/places/places_collection.page.dart @@ -61,11 +61,7 @@ class PlacesCollectionPage extends HookConsumerWidget { child: MapThumbnail( onTap: (_, __) => context.pushRoute(MapRoute(initialLocation: currentLocation)), zoom: 8, - centre: currentLocation ?? - const LatLng( - 21.44950, - -157.91959, - ), + centre: currentLocation ?? const LatLng(21.44950, -157.91959), showAttribution: false, themeMode: context.isDarkTheme ? ThemeMode.dark : ThemeMode.light, ), @@ -113,16 +109,10 @@ class PlaceTile extends StatelessWidget { SearchRoute( prefilter: SearchFilter( people: {}, - location: SearchLocationFilter( - city: name, - ), + location: SearchLocationFilter(city: name), camera: SearchCameraFilter(), date: SearchDateFilter(), - display: SearchDisplayFilters( - isNotInAlbum: false, - isArchive: false, - isFavorite: false, - ), + display: SearchDisplayFilters(isNotInAlbum: false, isArchive: false, isFavorite: false), mediaType: AssetType.other, ), ), @@ -131,16 +121,9 @@ class PlaceTile extends StatelessWidget { return LargeLeadingTile( onTap: () => navigateToPlace(), - title: Text( - name, - style: context.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + title: Text(name, style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500)), leading: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), child: CachedNetworkImage( width: 80, height: 80, diff --git a/mobile/lib/pages/library/shared_link/shared_link.page.dart b/mobile/lib/pages/library/shared_link/shared_link.page.dart index 94af8f913b..66a77fb761 100644 --- a/mobile/lib/pages/library/shared_link/shared_link.page.dart +++ b/mobile/lib/pages/library/shared_link/shared_link.page.dart @@ -17,16 +17,13 @@ class SharedLinkPage extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final sharedLinks = ref.watch(sharedLinksStateProvider); - useEffect( - () { - ref.read(sharedLinksStateProvider.notifier).fetchLinks(); - return () { - if (!context.mounted) return; - ref.invalidate(sharedLinksStateProvider); - }; - }, - [], - ); + useEffect(() { + ref.read(sharedLinksStateProvider.notifier).fetchLinks(); + return () { + if (!context.mounted) return; + ref.invalidate(sharedLinksStateProvider); + }; + }, []); Widget buildNoShares() { return Column( @@ -36,30 +33,19 @@ class SharedLinkPage extends HookConsumerWidget { padding: const EdgeInsets.only(left: 16.0, top: 16.0), child: const Text( "shared_link_manage_links", - style: TextStyle( - fontSize: 14, - color: Colors.grey, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 14, color: Colors.grey, fontWeight: FontWeight.bold), ).tr(), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Padding( padding: const EdgeInsets.symmetric(vertical: 10), - child: const Text( - "you_dont_have_any_shared_links", - style: TextStyle(fontSize: 14), - ).tr(), + child: const Text("you_dont_have_any_shared_links", style: TextStyle(fontSize: 14)).tr(), ), ), Expanded( child: Center( - child: Icon( - Icons.link_off, - size: 100, - color: context.themeData.iconTheme.color?.withValues(alpha: 0.5), - ), + child: Icon(Icons.link_off, size: 100, color: context.themeData.iconTheme.color?.withValues(alpha: 0.5)), ), ), ], @@ -74,9 +60,7 @@ class SharedLinkPage extends HookConsumerWidget { padding: const EdgeInsets.only(left: 16.0, top: 16.0, bottom: 30.0), child: Text( "shared_link_manage_links", - style: context.textTheme.labelLarge?.copyWith( - color: context.textTheme.labelLarge?.color?.withAlpha(200), - ), + style: context.textTheme.labelLarge?.copyWith(color: context.textTheme.labelLarge?.color?.withAlpha(200)), ).tr(), ), Expanded( @@ -111,11 +95,7 @@ class SharedLinkPage extends HookConsumerWidget { } return Scaffold( - appBar: AppBar( - title: const Text("shared_link_app_bar_title").tr(), - elevation: 0, - centerTitle: false, - ), + appBar: AppBar(title: const Text("shared_link_app_bar_title").tr(), elevation: 0, centerTitle: false), body: SafeArea( child: sharedLinks.widgetWhen( onError: (error, stackTrace) => buildNoShares(), diff --git a/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart b/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart index c6db85a0fa..dcd503335b 100644 --- a/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart +++ b/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart @@ -19,12 +19,7 @@ class SharedLinkEditPage extends HookConsumerWidget { final List? assetsList; final String? albumId; - const SharedLinkEditPage({ - super.key, - this.existingLink, - this.assetsList, - this.albumId, - }); + const SharedLinkEditPage({super.key, this.existingLink, this.assetsList, this.albumId}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -46,20 +41,11 @@ class SharedLinkEditPage extends HookConsumerWidget { if (existingLink!.type == SharedLinkSource.album) { return Row( children: [ - const Text( - 'public_album', - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(), - const Text( - " | ", - style: TextStyle(fontWeight: FontWeight.bold), - ), + const Text('public_album', style: TextStyle(fontWeight: FontWeight.bold)).tr(), + const Text(" | ", style: TextStyle(fontWeight: FontWeight.bold)), Text( existingLink!.title, - style: TextStyle( - color: colorScheme.primary, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: colorScheme.primary, fontWeight: FontWeight.bold), ), ], ); @@ -68,21 +54,12 @@ class SharedLinkEditPage extends HookConsumerWidget { if (existingLink!.type == SharedLinkSource.individual) { return Row( children: [ - const Text( - 'shared_link_individual_shared', - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(), - const Text( - " | ", - style: TextStyle(fontWeight: FontWeight.bold), - ), + const Text('shared_link_individual_shared', style: TextStyle(fontWeight: FontWeight.bold)).tr(), + const Text(" | ", style: TextStyle(fontWeight: FontWeight.bold)), Expanded( child: Text( existingLink!.description ?? "--", - style: TextStyle( - color: colorScheme.primary, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: colorScheme.primary, fontWeight: FontWeight.bold), overflow: TextOverflow.ellipsis, ), ), @@ -91,10 +68,7 @@ class SharedLinkEditPage extends HookConsumerWidget { } } - return const Text( - "create_link_to_share_description", - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(); + return const Text("create_link_to_share_description", style: TextStyle(fontWeight: FontWeight.bold)).tr(); } Widget buildDescriptionField() { @@ -106,20 +80,12 @@ class SharedLinkEditPage extends HookConsumerWidget { autofocus: false, decoration: InputDecoration( labelText: 'description'.tr(), - labelStyle: TextStyle( - fontWeight: FontWeight.bold, - color: colorScheme.primary, - ), + labelStyle: TextStyle(fontWeight: FontWeight.bold, color: colorScheme.primary), floatingLabelBehavior: FloatingLabelBehavior.always, border: const OutlineInputBorder(), hintText: 'shared_link_edit_description_hint'.tr(), - hintStyle: const TextStyle( - fontWeight: FontWeight.normal, - fontSize: 14, - ), - disabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.grey.withValues(alpha: 0.5)), - ), + hintStyle: const TextStyle(fontWeight: FontWeight.normal, fontSize: 14), + disabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey.withValues(alpha: 0.5))), ), onTapOutside: (_) => descriptionFocusNode.unfocus(), ); @@ -132,20 +98,12 @@ class SharedLinkEditPage extends HookConsumerWidget { autofocus: false, decoration: InputDecoration( labelText: 'password'.tr(), - labelStyle: TextStyle( - fontWeight: FontWeight.bold, - color: colorScheme.primary, - ), + labelStyle: TextStyle(fontWeight: FontWeight.bold, color: colorScheme.primary), floatingLabelBehavior: FloatingLabelBehavior.always, border: const OutlineInputBorder(), hintText: 'shared_link_edit_password_hint'.tr(), - hintStyle: const TextStyle( - fontWeight: FontWeight.normal, - fontSize: 14, - ), - disabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.grey.withValues(alpha: 0.5)), - ), + hintStyle: const TextStyle(fontWeight: FontWeight.normal, fontSize: 14), + disabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey.withValues(alpha: 0.5))), ), ); } @@ -156,10 +114,7 @@ class SharedLinkEditPage extends HookConsumerWidget { onChanged: newShareLink.value.isEmpty ? (value) => showMetadata.value = value : null, activeColor: colorScheme.primary, dense: true, - title: Text( - "show_metadata", - style: themeData.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold), - ).tr(), + title: Text("show_metadata", style: themeData.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold)).tr(), ); } @@ -206,10 +161,7 @@ class SharedLinkEditPage extends HookConsumerWidget { return DropdownMenu( label: Text( "expire_after", - style: TextStyle( - fontWeight: FontWeight.bold, - color: colorScheme.primary, - ), + style: TextStyle(fontWeight: FontWeight.bold, color: colorScheme.primary), ).tr(), enableSearch: false, enableFilter: false, @@ -220,26 +172,17 @@ class SharedLinkEditPage extends HookConsumerWidget { expiryAfter.value = value!; }, dropdownMenuEntries: [ - DropdownMenuEntry( - value: 0, - label: "never".tr(), - ), + DropdownMenuEntry(value: 0, label: "never".tr()), DropdownMenuEntry( value: 30, label: "shared_link_edit_expire_after_option_minutes".tr(namedArgs: {'count': "30"}), ), - DropdownMenuEntry( - value: 60, - label: "shared_link_edit_expire_after_option_hour".tr(), - ), + DropdownMenuEntry(value: 60, label: "shared_link_edit_expire_after_option_hour".tr()), DropdownMenuEntry( value: 60 * 6, label: "shared_link_edit_expire_after_option_hours".tr(namedArgs: {'count': "6"}), ), - DropdownMenuEntry( - value: 60 * 24, - label: "shared_link_edit_expire_after_option_day".tr(), - ), + DropdownMenuEntry(value: 60 * 24, label: "shared_link_edit_expire_after_option_day".tr()), DropdownMenuEntry( value: 60 * 24 * 7, label: "shared_link_edit_expire_after_option_days".tr(namedArgs: {'count': "7"}), @@ -266,9 +209,7 @@ class SharedLinkEditPage extends HookConsumerWidget { SnackBar( content: Text( "shared_link_clipboard_copied_massage", - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ).tr(), duration: const Duration(seconds: 2), ), @@ -279,23 +220,14 @@ class SharedLinkEditPage extends HookConsumerWidget { Widget buildNewLinkField() { return Column( children: [ - const Padding( - padding: EdgeInsets.only( - top: 20, - bottom: 20, - ), - child: Divider(), - ), + const Padding(padding: EdgeInsets.only(top: 20, bottom: 20), child: Divider()), TextFormField( readOnly: true, initialValue: newShareLink.value, decoration: InputDecoration( border: const OutlineInputBorder(), enabledBorder: themeData.inputDecorationTheme.focusedBorder, - suffixIcon: IconButton( - onPressed: copyLinkToClipboard, - icon: const Icon(Icons.copy), - ), + suffixIcon: IconButton(onPressed: copyLinkToClipboard, icon: const Icon(Icons.copy)), ), ), Padding( @@ -306,13 +238,7 @@ class SharedLinkEditPage extends HookConsumerWidget { onPressed: () { context.maybePop(); }, - child: const Text( - "done", - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ).tr(), + child: const Text("done", style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)).tr(), ), ), ), @@ -325,7 +251,9 @@ class SharedLinkEditPage extends HookConsumerWidget { } Future handleNewLink() async { - final newLink = await ref.read(sharedLinkServiceProvider).createSharedLink( + final newLink = await ref + .read(sharedLinkServiceProvider) + .createSharedLink( albumId: albumId, assetIds: assetsList, showMeta: showMetadata.value, @@ -336,9 +264,7 @@ class SharedLinkEditPage extends HookConsumerWidget { expiresAt: expiryAfter.value == 0 ? null : calculateExpiry(), ); ref.invalidate(sharedLinksStateProvider); - final externalDomain = ref.read( - serverInfoProvider.select((s) => s.serverConfig.externalDomain), - ); + final externalDomain = ref.read(serverInfoProvider.select((s) => s.serverConfig.externalDomain)); var serverUrl = externalDomain.isNotEmpty ? externalDomain : getServerUrl(); if (serverUrl != null && !serverUrl.endsWith('/')) { serverUrl += '/'; @@ -390,7 +316,9 @@ class SharedLinkEditPage extends HookConsumerWidget { changeExpiry = true; } - await ref.read(sharedLinkServiceProvider).updateSharedLink( + await ref + .read(sharedLinkServiceProvider) + .updateSharedLink( existingLink!.id, showMeta: meta, allowDownload: download, @@ -406,9 +334,7 @@ class SharedLinkEditPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( - title: Text( - existingLink == null ? "create_link_to_share" : "edit_link", - ).tr(), + title: Text(existingLink == null ? "create_link_to_share" : "edit_link").tr(), elevation: 0, leading: const CloseButton(), centerTitle: false, @@ -416,32 +342,15 @@ class SharedLinkEditPage extends HookConsumerWidget { body: SafeArea( child: ListView( children: [ + Padding(padding: const EdgeInsets.all(padding), child: buildLinkTitle()), + Padding(padding: const EdgeInsets.all(padding), child: buildDescriptionField()), + Padding(padding: const EdgeInsets.all(padding), child: buildPasswordField()), Padding( - padding: const EdgeInsets.all(padding), - child: buildLinkTitle(), - ), - Padding( - padding: const EdgeInsets.all(padding), - child: buildDescriptionField(), - ), - Padding( - padding: const EdgeInsets.all(padding), - child: buildPasswordField(), - ), - Padding( - padding: const EdgeInsets.only( - left: padding, - right: padding, - bottom: padding, - ), + padding: const EdgeInsets.only(left: padding, right: padding, bottom: padding), child: buildShowMetaButton(), ), Padding( - padding: const EdgeInsets.only( - left: padding, - right: padding, - bottom: padding, - ), + padding: const EdgeInsets.only(left: padding, right: padding, bottom: padding), child: buildAllowDownloadButton(), ), Padding( @@ -450,48 +359,30 @@ class SharedLinkEditPage extends HookConsumerWidget { ), if (existingLink != null) Padding( - padding: const EdgeInsets.only( - left: padding, - right: padding, - bottom: padding, - ), + padding: const EdgeInsets.only(left: padding, right: padding, bottom: padding), child: buildEditExpiryButton(), ), Padding( - padding: const EdgeInsets.only( - left: padding, - right: padding, - bottom: padding, - ), + padding: const EdgeInsets.only(left: padding, right: padding, bottom: padding), child: buildExpiryAfterButton(), ), if (newShareLink.value.isEmpty) Align( alignment: Alignment.bottomRight, child: Padding( - padding: const EdgeInsets.only( - right: padding + 10, - bottom: padding, - ), + padding: const EdgeInsets.only(right: padding + 10, bottom: padding), child: ElevatedButton( onPressed: existingLink != null ? handleEditLink : handleNewLink, child: Text( existingLink != null ? "shared_link_edit_submit_button" : "create_link", - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), ).tr(), ), ), ), if (newShareLink.value.isNotEmpty) Padding( - padding: const EdgeInsets.only( - left: padding, - right: padding, - bottom: padding, - ), + padding: const EdgeInsets.only(left: padding, right: padding, bottom: padding), child: buildNewLinkField(), ), ], diff --git a/mobile/lib/pages/library/trash.page.dart b/mobile/lib/pages/library/trash.page.dart index 9ffe7ae704..2279998c2d 100644 --- a/mobile/lib/pages/library/trash.page.dart +++ b/mobile/lib/pages/library/trash.page.dart @@ -29,10 +29,7 @@ class TrashPage extends HookConsumerWidget { final selection = useState({}); final processing = useProcessingOverlay(); - void selectionListener( - bool multiselect, - Set selectedAssets, - ) { + void selectionListener(bool multiselect, Set selectedAssets) { selectionEnabledHook.value = multiselect; selection.value = selectedAssets; } @@ -43,11 +40,7 @@ class TrashPage extends HookConsumerWidget { processing.value = false; selectionEnabledHook.value = false; if (context.mounted) { - ImmichToast.show( - context: context, - msg: 'trash_emptied'.tr(), - gravity: ToastGravity.BOTTOM, - ); + ImmichToast.show(context: context, msg: 'trash_emptied'.tr(), gravity: ToastGravity.BOTTOM); } } @@ -88,10 +81,7 @@ class TrashPage extends HookConsumerWidget { handlePermanentDelete() async { await showDialog( context: context, - builder: (context) => DeleteDialog( - alert: "delete_dialog_alert_remote", - onDelete: () => onPermanentlyDelete(), - ), + builder: (context) => DeleteDialog(alert: "delete_dialog_alert_remote", onDelete: () => onPermanentlyDelete()), ); } @@ -138,8 +128,9 @@ class TrashPage extends HookConsumerWidget { selectionEnabledHook.value = false; selection.value = {}; }, - icon: - !selectionEnabledHook.value ? const Icon(Icons.arrow_back_ios_rounded) : const Icon(Icons.close_rounded), + icon: !selectionEnabledHook.value + ? const Icon(Icons.arrow_back_ios_rounded) + : const Icon(Icons.close_rounded), ), centerTitle: !selectionEnabledHook.value, automaticallyImplyLeading: false, @@ -149,14 +140,8 @@ class TrashPage extends HookConsumerWidget { PopupMenuButton( itemBuilder: (context) { return [ - PopupMenuItem( - value: () => selectionEnabledHook.value = true, - child: const Text('select').tr(), - ), - PopupMenuItem( - value: handleEmptyTrash, - child: const Text('empty_trash').tr(), - ), + PopupMenuItem(value: () => selectionEnabledHook.value = true, child: const Text('select').tr()), + PopupMenuItem(value: handleEmptyTrash, child: const Text('empty_trash').tr()), ]; }, onSelected: (fn) => fn(), @@ -177,40 +162,28 @@ class TrashPage extends HookConsumerWidget { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ TextButton.icon( - icon: Icon( - Icons.delete_forever, - color: Colors.red[400], - ), + icon: Icon(Icons.delete_forever, color: Colors.red[400]), label: Text( selection.value.isEmpty ? 'trash_page_delete_all'.tr() : 'delete'.tr(), - style: TextStyle( - fontSize: 14, - color: Colors.red[400], - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 14, color: Colors.red[400], fontWeight: FontWeight.bold), ), onPressed: processing.value ? null : selection.value.isEmpty - ? handleEmptyTrash - : handlePermanentDelete, + ? handleEmptyTrash + : handlePermanentDelete, ), TextButton.icon( - icon: const Icon( - Icons.history_rounded, - ), + icon: const Icon(Icons.history_rounded), label: Text( selection.value.isEmpty ? 'trash_page_restore_all'.tr() : 'restore'.tr(), - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), ), onPressed: processing.value ? null : selection.value.isEmpty - ? handleRestoreAll - : handleRestore, + ? handleRestoreAll + : handleRestore, ), ], ), @@ -227,9 +200,7 @@ class TrashPage extends HookConsumerWidget { ), body: trashRenderList.widgetWhen( onData: (data) => data.isEmpty - ? Center( - child: Text('trash_page_no_assets'.tr()), - ) + ? Center(child: Text('trash_page_no_assets'.tr())) : Stack( children: [ SafeArea( @@ -240,13 +211,8 @@ class TrashPage extends HookConsumerWidget { showMultiSelectIndicator: false, showStack: true, topWidget: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 24, - ), - child: const Text( - "trash_page_info", - ).tr(namedArgs: {"days": "$trashDays"}), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 24), + child: const Text("trash_page_info").tr(namedArgs: {"days": "$trashDays"}), ), ), ), diff --git a/mobile/lib/pages/login/change_password.page.dart b/mobile/lib/pages/login/change_password.page.dart index b05397ee38..248526df1b 100644 --- a/mobile/lib/pages/login/change_password.page.dart +++ b/mobile/lib/pages/login/change_password.page.dart @@ -9,8 +9,6 @@ class ChangePasswordPage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - return const Scaffold( - body: ChangePasswordForm(), - ); + return const Scaffold(body: ChangePasswordForm()); } } diff --git a/mobile/lib/pages/login/login.page.dart b/mobile/lib/pages/login/login.page.dart index 8045ae649f..e1d551900f 100644 --- a/mobile/lib/pages/login/login.page.dart +++ b/mobile/lib/pages/login/login.page.dart @@ -21,12 +21,10 @@ class LoginPage extends HookConsumerWidget { appVersion.value = packageInfo.version; } - useEffect( - () { - getAppInfo(); - return null; - }, - ); + useEffect(() { + getAppInfo(); + return null; + }); return Scaffold( body: LoginForm(), diff --git a/mobile/lib/pages/onboarding/permission_onboarding.page.dart b/mobile/lib/pages/onboarding/permission_onboarding.page.dart index 0233208585..52d4ac0125 100644 --- a/mobile/lib/pages/onboarding/permission_onboarding.page.dart +++ b/mobile/lib/pages/onboarding/permission_onboarding.page.dart @@ -26,24 +26,18 @@ class PermissionOnboardingPage extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - Text( - 'permission_onboarding_request', - style: context.textTheme.titleMedium, - textAlign: TextAlign.center, - ).tr(), + Text('permission_onboarding_request', style: context.textTheme.titleMedium, textAlign: TextAlign.center).tr(), const SizedBox(height: 18), ElevatedButton( onPressed: () => ref.read(galleryPermissionNotifier.notifier).requestGalleryPermission().then((permission) async { - if (permission.isGranted) { - // If permission is limited, we will show the limited - // permission page - goToBackup(); - } - }), - child: const Text( - 'continue', - ).tr(), + if (permission.isGranted) { + // If permission is limited, we will show the limited + // permission page + goToBackup(); + } + }), + child: const Text('continue').tr(), ), ], ); @@ -62,10 +56,7 @@ class PermissionOnboardingPage extends HookConsumerWidget { textAlign: TextAlign.center, ).tr(), const SizedBox(height: 18), - ElevatedButton( - onPressed: () => goToBackup(), - child: const Text('permission_onboarding_get_started').tr(), - ), + ElevatedButton(onPressed: () => goToBackup(), child: const Text('permission_onboarding_get_started').tr()), ], ); } @@ -78,11 +69,7 @@ class PermissionOnboardingPage extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - const Icon( - Icons.warning_outlined, - color: Colors.yellow, - size: 48, - ), + const Icon(Icons.warning_outlined, color: Colors.yellow, size: 48), const SizedBox(height: 8), Text( 'permission_onboarding_permission_limited', @@ -92,17 +79,10 @@ class PermissionOnboardingPage extends HookConsumerWidget { const SizedBox(height: 18), ElevatedButton( onPressed: () => openAppSettings(), - child: const Text( - 'permission_onboarding_go_to_settings', - ).tr(), + child: const Text('permission_onboarding_go_to_settings').tr(), ), const SizedBox(height: 8.0), - TextButton( - onPressed: () => goToBackup(), - child: const Text( - 'permission_onboarding_continue_anyway', - ).tr(), - ), + TextButton(onPressed: () => goToBackup(), child: const Text('permission_onboarding_continue_anyway').tr()), ], ); } @@ -112,11 +92,7 @@ class PermissionOnboardingPage extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - const Icon( - Icons.warning_outlined, - color: Colors.red, - size: 48, - ), + const Icon(Icons.warning_outlined, color: Colors.red, size: 48), const SizedBox(height: 8), Text( 'permission_onboarding_permission_denied', @@ -126,9 +102,7 @@ class PermissionOnboardingPage extends HookConsumerWidget { const SizedBox(height: 18), ElevatedButton( onPressed: () => openAppSettings(), - child: const Text( - 'permission_onboarding_go_to_settings', - ).tr(), + child: const Text('permission_onboarding_go_to_settings').tr(), ), ], ); @@ -138,7 +112,7 @@ class PermissionOnboardingPage extends HookConsumerWidget { PermissionStatus.limited => buildPermissionLimited(), PermissionStatus.denied => buildRequestPermission(), PermissionStatus.granted || PermissionStatus.provisional => buildPermissionGranted(), - PermissionStatus.restricted || PermissionStatus.permanentlyDenied => buildPermissionDenied() + PermissionStatus.restricted || PermissionStatus.permanentlyDenied => buildPermissionDenied(), }; return Scaffold( @@ -150,21 +124,13 @@ class PermissionOnboardingPage extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - const ImmichLogo( - heroTag: 'logo', - ), + const ImmichLogo(heroTag: 'logo'), const ImmichTitleText(), AnimatedSwitcher( duration: const Duration(milliseconds: 500), - child: Padding( - padding: const EdgeInsets.all(18.0), - child: child, - ), - ), - TextButton( - child: const Text('back').tr(), - onPressed: () => context.maybePop(), + child: Padding(padding: const EdgeInsets.all(18.0), child: child), ), + TextButton(child: const Text('back').tr(), onPressed: () => context.maybePop()), ], ), ), diff --git a/mobile/lib/pages/photos/memory.page.dart b/mobile/lib/pages/photos/memory.page.dart index 4826e19f89..20bd32a171 100644 --- a/mobile/lib/pages/photos/memory.page.dart +++ b/mobile/lib/pages/photos/memory.page.dart @@ -16,26 +16,19 @@ import 'package:immich_mobile/widgets/memories/memory_epilogue.dart'; import 'package:immich_mobile/widgets/memories/memory_progress_indicator.dart'; @RoutePage() - /// Expects [currentAssetProvider] to be set before navigating to this page class MemoryPage extends HookConsumerWidget { final List memories; final int memoryIndex; - const MemoryPage({ - required this.memories, - required this.memoryIndex, - super.key, - }); + const MemoryPage({required this.memories, required this.memoryIndex, super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final currentMemory = useState(memories[memoryIndex]); final currentAssetPage = useState(0); final currentMemoryIndex = useState(memoryIndex); - final assetProgress = useState( - "${currentAssetPage.value + 1}|${currentMemory.value.assets.length}", - ); + final assetProgress = useState("${currentAssetPage.value + 1}|${currentMemory.value.assets.length}"); const bgColor = Colors.black; final currentAsset = useState(null); @@ -55,19 +48,13 @@ class MemoryPage extends HookConsumerWidget { }); toNextMemory() { - memoryPageController.nextPage( - duration: const Duration(milliseconds: 500), - curve: Curves.easeIn, - ); + memoryPageController.nextPage(duration: const Duration(milliseconds: 500), curve: Curves.easeIn); } void toPreviousMemory() { if (currentMemoryIndex.value > 0) { // Move to the previous memory page - memoryPageController.previousPage( - duration: const Duration(milliseconds: 500), - curve: Curves.easeIn, - ); + memoryPageController.previousPage(duration: const Duration(milliseconds: 500), curve: Curves.easeIn); // Wait for the next frame to ensure the page is built SchedulerBinding.instance.addPostFrameCallback((_) { @@ -94,10 +81,7 @@ class MemoryPage extends HookConsumerWidget { // Go to the next asset PageController controller = memoryAssetPageControllers[currentMemoryIndex.value]; - controller.nextPage( - curve: Curves.easeInOut, - duration: const Duration(milliseconds: 500), - ); + controller.nextPage(curve: Curves.easeInOut, duration: const Duration(milliseconds: 500)); } else { // Go to the next memory since we are at the end of our assets toNextMemory(); @@ -109,10 +93,7 @@ class MemoryPage extends HookConsumerWidget { // Go to the previous asset PageController controller = memoryAssetPageControllers[currentMemoryIndex.value]; - controller.previousPage( - curve: Curves.easeInOut, - duration: const Duration(milliseconds: 500), - ); + controller.previousPage(curve: Curves.easeInOut, duration: const Duration(milliseconds: 500)); } else { // Go to the previous memory since we are at the end of our assets toPreviousMemory(); @@ -161,11 +142,7 @@ class MemoryPage extends HookConsumerWidget { // Precache the asset final size = MediaQuery.sizeOf(context); await precacheImage( - ImmichImage.imageProvider( - asset: asset, - width: size.width, - height: size.height, - ), + ImmichImage.imageProvider(asset: asset, width: size.width, height: size.height), context, size: size, ); @@ -219,9 +196,7 @@ class MemoryPage extends HookConsumerWidget { backgroundColor: bgColor, body: SafeArea( child: PageView.builder( - physics: const BouncingScrollPhysics( - parent: AlwaysScrollableScrollPhysics(), - ), + physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), scrollDirection: Axis.vertical, controller: memoryPageController, onPageChanged: (pageNumber) { @@ -252,12 +227,7 @@ class MemoryPage extends HookConsumerWidget { return Column( children: [ Padding( - padding: const EdgeInsets.only( - left: 24.0, - right: 24.0, - top: 8.0, - bottom: 2.0, - ), + padding: const EdgeInsets.only(left: 24.0, right: 24.0, top: 8.0, bottom: 2.0), child: AnimatedBuilder( animation: assetController, builder: (context, child) { @@ -277,9 +247,7 @@ class MemoryPage extends HookConsumerWidget { child: Stack( children: [ PageView.builder( - physics: const BouncingScrollPhysics( - parent: AlwaysScrollableScrollPhysics(), - ), + physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), controller: assetController, onPageChanged: onAssetChanged, scrollDirection: Axis.horizontal, @@ -290,11 +258,7 @@ class MemoryPage extends HookConsumerWidget { children: [ Container( color: Colors.black, - child: MemoryCard( - asset: asset, - title: memories[mIndex].title, - showTitle: index == 0, - ), + child: MemoryCard(asset: asset, title: memories[mIndex].title, showTitle: index == 0), ), Positioned.fill( child: Row( @@ -335,27 +299,19 @@ class MemoryPage extends HookConsumerWidget { // turn off full screen mode here // https://github.com/Milad-Akarie/auto_route_library/issues/1799 context.maybePop(); - SystemChrome.setEnabledSystemUIMode( - SystemUiMode.edgeToEdge, - ); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); }, shape: const CircleBorder(), color: Colors.white.withValues(alpha: 0.2), elevation: 0, - child: const Icon( - Icons.close_rounded, - color: Colors.white, - ), + child: const Icon(Icons.close_rounded, color: Colors.white), ), ), if (currentAsset.value != null && currentAsset.value!.isVideo) Positioned( bottom: 24, right: 32, - child: Icon( - Icons.videocam_outlined, - color: Colors.grey[200], - ), + child: Icon(Icons.videocam_outlined, color: Colors.grey[200]), ), ], ), diff --git a/mobile/lib/pages/photos/photos.page.dart b/mobile/lib/pages/photos/photos.page.dart index 2cc3c44e3a..957c32b224 100644 --- a/mobile/lib/pages/photos/photos.page.dart +++ b/mobile/lib/pages/photos/photos.page.dart @@ -29,17 +29,14 @@ class PhotosPage extends HookConsumerWidget { final tipOneOpacity = useState(0.0); final refreshCount = useState(0); - useEffect( - () { - ref.read(websocketProvider.notifier).connect(); - Future(() => ref.read(assetProvider.notifier).getAllAsset()); - Future(() => ref.read(albumProvider.notifier).refreshRemoteAlbums()); - ref.read(serverInfoProvider.notifier).getServerInfo(); + useEffect(() { + ref.read(websocketProvider.notifier).connect(); + Future(() => ref.read(assetProvider.notifier).getAllAsset()); + Future(() => ref.read(albumProvider.notifier).refreshRemoteAlbums()); + ref.read(serverInfoProvider.notifier).getServerInfo(); - return; - }, - [], - ); + return; + }, []); Widget buildLoadingIndicator() { Timer(const Duration(seconds: 2), () => tipOneOpacity.value = 1); @@ -53,9 +50,7 @@ class PhotosPage extends HookConsumerWidget { padding: const EdgeInsets.only(top: 16.0), child: Text( 'home_page_building_timeline', - style: context.textTheme.titleMedium?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.titleMedium?.copyWith(color: context.primaryColor), ).tr(), ), const SizedBox(height: 8), diff --git a/mobile/lib/pages/search/all_motion_videos.page.dart b/mobile/lib/pages/search/all_motion_videos.page.dart index 5d1081db33..60bb8a6cff 100644 --- a/mobile/lib/pages/search/all_motion_videos.page.dart +++ b/mobile/lib/pages/search/all_motion_videos.page.dart @@ -17,16 +17,9 @@ class AllMotionPhotosPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( title: const Text('search_page_motion_photos').tr(), - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), - ), - body: motionPhotos.widgetWhen( - onData: (assets) => ImmichAssetGrid( - assets: assets, - ), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), ), + body: motionPhotos.widgetWhen(onData: (assets) => ImmichAssetGrid(assets: assets)), ); } } diff --git a/mobile/lib/pages/search/all_people.page.dart b/mobile/lib/pages/search/all_people.page.dart index 6fcf07fbfa..b2814e6c13 100644 --- a/mobile/lib/pages/search/all_people.page.dart +++ b/mobile/lib/pages/search/all_people.page.dart @@ -17,13 +17,8 @@ class AllPeoplePage extends HookConsumerWidget { return Scaffold( appBar: AppBar( - title: const Text( - 'people', - ).tr(), - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), + title: const Text('people').tr(), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), ), body: curatedPeople.widgetWhen( onData: (people) => ExploreGrid( diff --git a/mobile/lib/pages/search/all_places.page.dart b/mobile/lib/pages/search/all_places.page.dart index 63908a5e2a..c92f87d3ac 100644 --- a/mobile/lib/pages/search/all_places.page.dart +++ b/mobile/lib/pages/search/all_places.page.dart @@ -17,19 +17,10 @@ class AllPlacesPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( - title: const Text( - 'places', - ).tr(), - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), - ), - body: places.widgetWhen( - onData: (data) => ExploreGrid( - curatedContent: data, - ), + title: const Text('places').tr(), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), ), + body: places.widgetWhen(onData: (data) => ExploreGrid(curatedContent: data)), ); } } diff --git a/mobile/lib/pages/search/all_videos.page.dart b/mobile/lib/pages/search/all_videos.page.dart index 6d123d9d7f..acad043a58 100644 --- a/mobile/lib/pages/search/all_videos.page.dart +++ b/mobile/lib/pages/search/all_videos.page.dart @@ -14,10 +14,7 @@ class AllVideosPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( title: const Text('videos').tr(), - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), ), body: MultiselectGrid(renderListProvider: allVideosTimelineProvider), ); diff --git a/mobile/lib/pages/search/map/map.page.dart b/mobile/lib/pages/search/map/map.page.dart index 62df4a50af..34522f0f04 100644 --- a/mobile/lib/pages/search/map/map.page.dart +++ b/mobile/lib/pages/search/map/map.page.dart @@ -62,17 +62,11 @@ class MapPage extends HookConsumerWidget { final bounds = await mapController.value!.getVisibleRegion(); final inBounds = markers.value - .where( - (m) => bounds.contains(LatLng(m.latLng.latitude, m.latLng.longitude)), - ) + .where((m) => bounds.contains(LatLng(m.latLng.latitude, m.latLng.longitude))) .toList(); // Notify bottom sheet to update asset grid only when there are new assets if (markersInBounds.value.length != inBounds.length) { - bottomSheetStreamController.add( - MapAssetsInBoundsUpdated( - inBounds.map((e) => e.assetRemoteId).toList(), - ), - ); + bottomSheetStreamController.add(MapAssetsInBoundsUpdated(inBounds.map((e) => e.assetRemoteId).toList())); } markersInBounds.value = inBounds; } @@ -80,9 +74,7 @@ class MapPage extends HookConsumerWidget { // removes all sources and layers and re-adds them with the updated markers Future reloadLayers() async { if (mapController.value != null) { - layerDebouncer.run( - () => mapController.value!.reloadAllLayersForMarkers(markers.value), - ); + layerDebouncer.run(() => mapController.value!.reloadAllLayersForMarkers(markers.value)); } } @@ -97,15 +89,12 @@ class MapPage extends HookConsumerWidget { } } - useEffect( - () { - final currentAssetLink = ref.read(currentAssetProvider.notifier).ref.keepAlive(); + useEffect(() { + final currentAssetLink = ref.read(currentAssetProvider.notifier).ref.keepAlive(); - loadMarkers(); - return currentAssetLink.close; - }, - [], - ); + loadMarkers(); + return currentAssetLink.close; + }, []); // Refetch markers when map state is changed ref.listen(mapStateNotifierProvider, (_, current) { @@ -121,16 +110,9 @@ class MapPage extends HookConsumerWidget { }); // updates the selected markers position based on the current map camera - Future updateAssetMarkerPosition( - MapMarker marker, { - bool shouldAnimate = true, - }) async { + Future updateAssetMarkerPosition(MapMarker marker, {bool shouldAnimate = true}) async { final assetPoint = await mapController.value!.toScreenLocation(marker.latLng); - selectedMarker.value = _AssetMarkerMeta( - point: assetPoint, - marker: marker, - shouldAnimate: shouldAnimate, - ); + selectedMarker.value = _AssetMarkerMeta(point: assetPoint, marker: marker, shouldAnimate: shouldAnimate); (assetPoint, marker, shouldAnimate); } @@ -160,10 +142,7 @@ class MapPage extends HookConsumerWidget { mapController.value = controller; controller.addListener(() { if (controller.isCameraMoving && selectedMarker.value != null) { - updateAssetMarkerPosition( - selectedMarker.value!.marker, - shouldAnimate: false, - ); + updateAssetMarkerPosition(selectedMarker.value!.marker, shouldAnimate: false); } }); } @@ -180,22 +159,13 @@ class MapPage extends HookConsumerWidget { } // Since we only have a single asset, we can just show GroupAssetBy.none - final renderList = await RenderList.fromAssets( - [asset], - GroupAssetsBy.none, - ); + final renderList = await RenderList.fromAssets([asset], GroupAssetsBy.none); ref.read(currentAssetProvider.notifier).set(asset); if (asset.isVideo) { ref.read(showControlsProvider.notifier).show = false; } - context.pushRoute( - GalleryViewerRoute( - initialIndex: 0, - heroOffset: 0, - renderList: renderList, - ), - ); + context.pushRoute(GalleryViewerRoute(initialIndex: 0, heroOffset: 0, renderList: renderList)); } /// BOTTOM SHEET CALLBACKS @@ -216,10 +186,7 @@ class MapPage extends HookConsumerWidget { if (mapController.value != null && assetMarker != null) { // Offset the latitude a little to show the marker just above the viewports center final offset = context.isMobile ? 0.02 : 0; - final latlng = LatLng( - assetMarker.latLng.latitude - offset, - assetMarker.latLng.longitude, - ); + final latlng = LatLng(assetMarker.latLng.latitude - offset, assetMarker.latLng.longitude); mapController.value!.animateCamera( CameraUpdate.newLatLngZoom(latlng, mapZoomToAssetLevel), duration: const Duration(milliseconds: 800), @@ -243,10 +210,7 @@ class MapPage extends HookConsumerWidget { if (mapController.value != null && location != null) { mapController.value!.animateCamera( - CameraUpdate.newLatLngZoom( - LatLng(location.latitude, location.longitude), - mapZoomToAssetLevel, - ), + CameraUpdate.newLatLngZoom(LatLng(location.latitude, location.longitude), mapZoomToAssetLevel), duration: const Duration(milliseconds: 800), ); } @@ -311,9 +275,7 @@ class MapPage extends HookConsumerWidget { bottom: context.padding.bottom + 16, child: ElevatedButton( onPressed: onZoomToLocation, - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.my_location), ), ), @@ -344,11 +306,7 @@ class _AssetMarkerMeta { final MapMarker marker; final bool shouldAnimate; - const _AssetMarkerMeta({ - required this.point, - required this.marker, - required this.shouldAnimate, - }); + const _AssetMarkerMeta({required this.point, required this.marker, required this.shouldAnimate}); @override String toString() => '_AssetMarkerMeta(point: $point, marker: $marker, shouldAnimate: $shouldAnimate)'; diff --git a/mobile/lib/pages/search/map/map_location_picker.page.dart b/mobile/lib/pages/search/map/map_location_picker.page.dart index 9b7bbd0cd8..0fe8b769f5 100644 --- a/mobile/lib/pages/search/map/map_location_picker.page.dart +++ b/mobile/lib/pages/search/map/map_location_picker.page.dart @@ -16,10 +16,7 @@ import 'package:immich_mobile/utils/map_utils.dart'; class MapLocationPickerPage extends HookConsumerWidget { final LatLng initialLatLng; - const MapLocationPickerPage({ - super.key, - this.initialLatLng = const LatLng(0, 0), - }); + const MapLocationPickerPage({super.key, this.initialLatLng = const LatLng(0, 0)}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -66,10 +63,7 @@ class MapLocationPickerPage extends HookConsumerWidget { onData: (style) => Container( clipBehavior: Clip.antiAliasWithSaveLayer, decoration: const BoxDecoration( - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(40), - bottomRight: Radius.circular(40), - ), + borderRadius: BorderRadius.only(bottomLeft: Radius.circular(40), bottomRight: Radius.circular(40)), ), child: MapLibreMap( initialCameraPosition: CameraPosition(target: initialLatLng, zoom: 12), @@ -108,9 +102,7 @@ class _AppBar extends StatelessWidget implements PreferredSizeWidget { alignment: Alignment.centerLeft, child: ElevatedButton( onPressed: onClose, - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.arrow_back_ios_new_rounded), ), ), @@ -126,11 +118,7 @@ class _BottomBar extends StatelessWidget { final Function() onUseLocation; final Function() onGetCurrentLocation; - const _BottomBar({ - required this.selectedLatLng, - required this.onUseLocation, - required this.onGetCurrentLocation, - }); + const _BottomBar({required this.selectedLatLng, required this.onUseLocation, required this.onGetCurrentLocation}); @override Widget build(BuildContext context) { @@ -149,9 +137,8 @@ class _BottomBar extends StatelessWidget { const SizedBox(width: 15), ValueListenableBuilder( valueListenable: selectedLatLng, - builder: (_, value, __) => Text( - "${value.latitude.toStringAsFixed(4)}, ${value.longitude.toStringAsFixed(4)}", - ), + builder: (_, value, __) => + Text("${value.latitude.toStringAsFixed(4)}, ${value.longitude.toStringAsFixed(4)}"), ), ], ), @@ -162,10 +149,7 @@ class _BottomBar extends StatelessWidget { onPressed: onUseLocation, child: const Text("map_location_picker_page_use_location").tr(), ), - ElevatedButton( - onPressed: onGetCurrentLocation, - child: const Icon(Icons.my_location), - ), + ElevatedButton(onPressed: onGetCurrentLocation, child: const Icon(Icons.my_location)), ], ), ], diff --git a/mobile/lib/pages/search/person_result.page.dart b/mobile/lib/pages/search/person_result.page.dart index 859c7e8a89..7d2e612d25 100644 --- a/mobile/lib/pages/search/person_result.page.dart +++ b/mobile/lib/pages/search/person_result.page.dart @@ -15,11 +15,7 @@ class PersonResultPage extends HookConsumerWidget { final String personId; final String personName; - const PersonResultPage({ - super.key, - required this.personId, - required this.personName, - }); + const PersonResultPage({super.key, required this.personId, required this.personName}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -30,10 +26,7 @@ class PersonResultPage extends HookConsumerWidget { context: context, useRootNavigator: false, builder: (BuildContext context) { - return PersonNameEditForm( - personId: personId, - personName: name.value, - ); + return PersonNameEditForm(personId: personId, personName: name.value); }, ).then((result) { if (result != null && result.success) { @@ -55,10 +48,7 @@ class PersonResultPage extends HookConsumerWidget { children: [ ListTile( leading: const Icon(Icons.edit_outlined), - title: const Text( - 'edit_name', - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(), + title: const Text('edit_name', style: TextStyle(fontWeight: FontWeight.bold)).tr(), onTap: showEditNameDialog, ), ], @@ -75,27 +65,13 @@ class PersonResultPage extends HookConsumerWidget { ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - 'add_a_name', - style: context.textTheme.titleMedium?.copyWith( - color: context.primaryColor, - ), - ).tr(), - Text( - 'find_them_fast', - style: context.textTheme.labelLarge, - ).tr(), + Text('add_a_name', style: context.textTheme.titleMedium?.copyWith(color: context.primaryColor)).tr(), + Text('find_them_fast', style: context.textTheme.labelLarge).tr(), ], ) : Column( crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - name.value, - style: context.textTheme.titleLarge, - overflow: TextOverflow.ellipsis, - ), - ], + children: [Text(name.value, style: context.textTheme.titleLarge, overflow: TextOverflow.ellipsis)], ), ); } @@ -103,16 +79,8 @@ class PersonResultPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( title: Text(name.value), - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), - actions: [ - IconButton( - onPressed: buildBottomSheet, - icon: const Icon(Icons.more_vert_rounded), - ), - ], + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), + actions: [IconButton(onPressed: buildBottomSheet, icon: const Icon(Icons.more_vert_rounded))], ), body: MultiselectGrid( renderListProvider: personAssetsProvider(personId), @@ -122,16 +90,10 @@ class PersonResultPage extends HookConsumerWidget { children: [ CircleAvatar( radius: 36, - backgroundImage: NetworkImage( - getFaceThumbnailUrl(personId), - headers: ApiService.getRequestHeaders(), - ), + backgroundImage: NetworkImage(getFaceThumbnailUrl(personId), headers: ApiService.getRequestHeaders()), ), Expanded( - child: Padding( - padding: const EdgeInsets.only(left: 16.0, right: 16.0), - child: buildTitleBlock(), - ), + child: Padding(padding: const EdgeInsets.only(left: 16.0, right: 16.0), child: buildTitleBlock()), ), ], ), diff --git a/mobile/lib/pages/search/recently_taken.page.dart b/mobile/lib/pages/search/recently_taken.page.dart index cc1eb7086e..988af2faf0 100644 --- a/mobile/lib/pages/search/recently_taken.page.dart +++ b/mobile/lib/pages/search/recently_taken.page.dart @@ -17,16 +17,9 @@ class RecentlyTakenPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( title: const Text('recently_taken_page_title').tr(), - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), - ), - body: recents.widgetWhen( - onData: (searchResponse) => ImmichAssetGrid( - assets: searchResponse, - ), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), ), + body: recents.widgetWhen(onData: (searchResponse) => ImmichAssetGrid(assets: searchResponse)), ); } } diff --git a/mobile/lib/pages/search/search.page.dart b/mobile/lib/pages/search/search.page.dart index d9a20e8b4d..97205e000c 100644 --- a/mobile/lib/pages/search/search.page.dart +++ b/mobile/lib/pages/search/search.page.dart @@ -41,12 +41,7 @@ class SearchPage extends HookConsumerWidget { location: prefilter?.location ?? SearchLocationFilter(), camera: prefilter?.camera ?? SearchCameraFilter(), date: prefilter?.date ?? SearchDateFilter(), - display: prefilter?.display ?? - SearchDisplayFilters( - isNotInAlbum: false, - isArchive: false, - isFavorite: false, - ), + display: prefilter?.display ?? SearchDisplayFilters(isNotInAlbum: false, isArchive: false, isFavorite: false), mediaType: prefilter?.mediaType ?? AssetType.other, language: "${context.locale.languageCode}-${context.locale.countryCode}", ), @@ -65,10 +60,7 @@ class SearchPage extends HookConsumerWidget { SnackBar searchInfoSnackBar(String message) { return SnackBar( - content: Text( - message, - style: context.textTheme.labelLarge, - ), + content: Text(message, style: context.textTheme.labelLarge), showCloseIcon: true, behavior: SnackBarBehavior.fixed, closeIconColor: context.colorScheme.onSurface, @@ -89,9 +81,7 @@ class SearchPage extends HookConsumerWidget { final hasResult = await ref.watch(paginatedSearchProvider.notifier).search(filter.value); if (!hasResult) { - context.showSnackBar( - searchInfoSnackBar('search_no_result'.tr()), - ); + context.showSnackBar(searchInfoSnackBar('search_no_result'.tr())); } previousFilter.value = filter.value; @@ -103,9 +93,7 @@ class SearchPage extends HookConsumerWidget { final hasResult = await ref.watch(paginatedSearchProvider.notifier).search(filter.value); if (!hasResult) { - context.showSnackBar( - searchInfoSnackBar('search_no_more_result'.tr()), - ); + context.showSnackBar(searchInfoSnackBar('search_no_more_result'.tr())); } isSearching.value = false; @@ -113,39 +101,26 @@ class SearchPage extends HookConsumerWidget { searchPrefilter() { if (prefilter != null) { - Future.delayed( - Duration.zero, - () { - search(); + Future.delayed(Duration.zero, () { + search(); - if (prefilter!.location.city != null) { - locationCurrentFilterWidget.value = Text( - prefilter!.location.city!, - style: context.textTheme.labelLarge, - ); - } - }, - ); + if (prefilter!.location.city != null) { + locationCurrentFilterWidget.value = Text(prefilter!.location.city!, style: context.textTheme.labelLarge); + } + }); } } - useEffect( - () { - Future.microtask( - () => ref.invalidate(paginatedSearchProvider), - ); - searchPrefilter(); + useEffect(() { + Future.microtask(() => ref.invalidate(paginatedSearchProvider)); + searchPrefilter(); - return null; - }, - [], - ); + return null; + }, []); showPeoplePicker() { handleOnSelect(Set value) { - filter.value = filter.value.copyWith( - people: value, - ); + filter.value = filter.value.copyWith(people: value); peopleCurrentFilterWidget.value = Text( value.map((e) => e.name != '' ? e.name : 'no_name'.tr()).join(', '), @@ -154,9 +129,7 @@ class SearchPage extends HookConsumerWidget { } handleClear() { - filter.value = filter.value.copyWith( - people: {}, - ); + filter.value = filter.value.copyWith(people: {}); peopleCurrentFilterWidget.value = null; search(); @@ -172,10 +145,7 @@ class SearchPage extends HookConsumerWidget { expanded: true, onSearch: search, onClear: handleClear, - child: PeoplePicker( - onSelect: handleOnSelect, - filter: filter.value.people, - ), + child: PeoplePicker(onSelect: handleOnSelect, filter: filter.value.people), ), ), ); @@ -184,11 +154,7 @@ class SearchPage extends HookConsumerWidget { showLocationPicker() { handleOnSelect(Map value) { filter.value = filter.value.copyWith( - location: SearchLocationFilter( - country: value['country'], - city: value['city'], - state: value['state'], - ), + location: SearchLocationFilter(country: value['country'], city: value['city'], state: value['state']), ); final locationText = []; @@ -204,16 +170,11 @@ class SearchPage extends HookConsumerWidget { locationText.add(value['city']!); } - locationCurrentFilterWidget.value = Text( - locationText.join(', '), - style: context.textTheme.labelLarge, - ); + locationCurrentFilterWidget.value = Text(locationText.join(', '), style: context.textTheme.labelLarge); } handleClear() { - filter.value = filter.value.copyWith( - location: SearchLocationFilter(), - ); + filter.value = filter.value.copyWith(location: SearchLocationFilter()); locationCurrentFilterWidget.value = null; search(); @@ -230,15 +191,10 @@ class SearchPage extends HookConsumerWidget { child: Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), child: Container( - padding: EdgeInsets.only( - bottom: context.viewInsets.bottom, - ), + padding: EdgeInsets.only(bottom: context.viewInsets.bottom), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: LocationPicker( - onSelected: handleOnSelect, - filter: filter.value.location, - ), + child: LocationPicker(onSelected: handleOnSelect, filter: filter.value.location), ), ), ), @@ -249,10 +205,7 @@ class SearchPage extends HookConsumerWidget { showCameraPicker() { handleOnSelect(Map value) { filter.value = filter.value.copyWith( - camera: SearchCameraFilter( - make: value['make'], - model: value['model'], - ), + camera: SearchCameraFilter(make: value['make'], model: value['model']), ); cameraCurrentFilterWidget.value = Text( @@ -262,9 +215,7 @@ class SearchPage extends HookConsumerWidget { } handleClear() { - filter.value = filter.value.copyWith( - camera: SearchCameraFilter(), - ); + filter.value = filter.value.copyWith(camera: SearchCameraFilter()); cameraCurrentFilterWidget.value = null; search(); @@ -280,10 +231,7 @@ class SearchPage extends HookConsumerWidget { onClear: handleClear, child: Padding( padding: const EdgeInsets.all(16.0), - child: CameraPicker( - onSelect: handleOnSelect, - filter: filter.value.camera, - ), + child: CameraPicker(onSelect: handleOnSelect, filter: filter.value.camera), ), ), ); @@ -315,9 +263,7 @@ class SearchPage extends HookConsumerWidget { ); if (date == null) { - filter.value = filter.value.copyWith( - date: SearchDateFilter(), - ); + filter.value = filter.value.copyWith(date: SearchDateFilter()); dateRangeCurrentFilterWidget.value = null; search(); @@ -327,13 +273,7 @@ class SearchPage extends HookConsumerWidget { filter.value = filter.value.copyWith( date: SearchDateFilter( takenAfter: date.start, - takenBefore: date.end.add( - const Duration( - hours: 23, - minutes: 59, - seconds: 59, - ), - ), + takenBefore: date.end.add(const Duration(hours: 23, minutes: 59, seconds: 59)), ), ); @@ -361,24 +301,20 @@ class SearchPage extends HookConsumerWidget { // MEDIA PICKER showMediaTypePicker() { handleOnSelected(AssetType assetType) { - filter.value = filter.value.copyWith( - mediaType: assetType, - ); + filter.value = filter.value.copyWith(mediaType: assetType); mediaTypeCurrentFilterWidget.value = Text( assetType == AssetType.image ? 'image'.tr() : assetType == AssetType.video - ? 'video'.tr() - : 'all'.tr(), + ? 'video'.tr() + : 'all'.tr(), style: context.textTheme.labelLarge, ); } handleClear() { - filter.value = filter.value.copyWith( - mediaType: AssetType.other, - ); + filter.value = filter.value.copyWith(mediaType: AssetType.other); mediaTypeCurrentFilterWidget.value = null; search(); @@ -390,10 +326,7 @@ class SearchPage extends HookConsumerWidget { title: 'search_filter_media_type_title'.tr(), onSearch: search, onClear: handleClear, - child: MediaTypePicker( - onSelect: handleOnSelected, - filter: filter.value.mediaType, - ), + child: MediaTypePicker(onSelect: handleOnSelected, filter: filter.value.mediaType), ), ); } @@ -405,31 +338,19 @@ class SearchPage extends HookConsumerWidget { value.forEach((key, value) { switch (key) { case DisplayOption.notInAlbum: - filter.value = filter.value.copyWith( - display: filter.value.display.copyWith( - isNotInAlbum: value, - ), - ); + filter.value = filter.value.copyWith(display: filter.value.display.copyWith(isNotInAlbum: value)); if (value) { filterText.add('search_filter_display_option_not_in_album'.tr()); } break; case DisplayOption.archive: - filter.value = filter.value.copyWith( - display: filter.value.display.copyWith( - isArchive: value, - ), - ); + filter.value = filter.value.copyWith(display: filter.value.display.copyWith(isArchive: value)); if (value) { filterText.add('archive'.tr()); } break; case DisplayOption.favorite: - filter.value = filter.value.copyWith( - display: filter.value.display.copyWith( - isFavorite: value, - ), - ); + filter.value = filter.value.copyWith(display: filter.value.display.copyWith(isFavorite: value)); if (value) { filterText.add('favorite'.tr()); } @@ -442,19 +363,12 @@ class SearchPage extends HookConsumerWidget { return; } - displayOptionCurrentFilterWidget.value = Text( - filterText.join(', '), - style: context.textTheme.labelLarge, - ); + displayOptionCurrentFilterWidget.value = Text(filterText.join(', '), style: context.textTheme.labelLarge); } handleClear() { filter.value = filter.value.copyWith( - display: SearchDisplayFilters( - isNotInAlbum: false, - isArchive: false, - isFavorite: false, - ), + display: SearchDisplayFilters(isNotInAlbum: false, isArchive: false, isFavorite: false), ); displayOptionCurrentFilterWidget.value = null; @@ -467,10 +381,7 @@ class SearchPage extends HookConsumerWidget { title: 'display_options'.tr(), onSearch: search, onClear: handleClear, - child: DisplayOptionPicker( - onSelect: handleOnSelect, - filter: filter.value.display, - ), + child: DisplayOptionPicker(onSelect: handleOnSelect, filter: filter.value.display), ), ); } @@ -478,27 +389,15 @@ class SearchPage extends HookConsumerWidget { handleTextSubmitted(String value) { switch (textSearchType.value) { case TextSearchType.context: - filter.value = filter.value.copyWith( - filename: '', - context: value, - description: '', - ); + filter.value = filter.value.copyWith(filename: '', context: value, description: ''); break; case TextSearchType.filename: - filter.value = filter.value.copyWith( - filename: value, - context: '', - description: '', - ); + filter.value = filter.value.copyWith(filename: value, context: '', description: ''); break; case TextSearchType.description: - filter.value = filter.value.copyWith( - filename: '', - context: '', - description: value, - ); + filter.value = filter.value.copyWith(filename: '', context: '', description: value); break; } @@ -506,10 +405,10 @@ class SearchPage extends HookConsumerWidget { } IconData getSearchPrefixIcon() => switch (textSearchType.value) { - TextSearchType.context => Icons.image_search_rounded, - TextSearchType.filename => Icons.abc_rounded, - TextSearchType.description => Icons.text_snippet_outlined, - }; + TextSearchType.context => Icons.image_search_rounded, + TextSearchType.filename => Icons.abc_rounded, + TextSearchType.description => Icons.text_snippet_outlined, + }; return Scaffold( resizeToAvoidBottomInset: false, @@ -522,21 +421,11 @@ class SearchPage extends HookConsumerWidget { style: MenuStyle( elevation: const WidgetStatePropertyAll(1), shape: WidgetStateProperty.all( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(24), - ), - ), - ), - padding: const WidgetStatePropertyAll( - EdgeInsets.all(4), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), ), + padding: const WidgetStatePropertyAll(EdgeInsets.all(4)), ), - builder: ( - BuildContext context, - MenuController controller, - Widget? child, - ) { + builder: (BuildContext context, MenuController controller, Widget? child) { return IconButton( onPressed: () { if (controller.isOpen) { @@ -610,13 +499,8 @@ class SearchPage extends HookConsumerWidget { ], title: Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(0), - width: 0, - ), - borderRadius: const BorderRadius.all( - Radius.circular(24), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(0), width: 0), + borderRadius: const BorderRadius.all(Radius.circular(24)), gradient: LinearGradient( colors: [ context.colorScheme.primary.withValues(alpha: 0.075), @@ -632,12 +516,7 @@ class SearchPage extends HookConsumerWidget { key: const Key('search_text_field'), controller: textSearchController, contentPadding: prefilter != null ? const EdgeInsets.only(left: 24) : const EdgeInsets.all(8), - prefixIcon: prefilter != null - ? null - : Icon( - getSearchPrefixIcon(), - color: context.colorScheme.primary, - ), + prefixIcon: prefilter != null ? null : Icon(getSearchPrefixIcon(), color: context.colorScheme.primary), onSubmitted: handleTextSubmitted, focusNode: ref.watch(searchInputFocusProvider), ), @@ -697,14 +576,9 @@ class SearchPage extends HookConsumerWidget { ), ), if (isSearching.value) - const Expanded( - child: Center(child: CircularProgressIndicator()), - ) + const Expanded(child: Center(child: CircularProgressIndicator())) else - SearchResultGrid( - onScrollEnd: loadMoreSearchResult, - isSearching: isSearching.value, - ), + SearchResultGrid(onScrollEnd: loadMoreSearchResult, isSearching: isSearching.value), ], ), ); @@ -715,11 +589,7 @@ class SearchResultGrid extends StatelessWidget { final VoidCallback onScrollEnd; final bool isSearching; - const SearchResultGrid({ - super.key, - required this.onScrollEnd, - this.isSearching = false, - }); + const SearchResultGrid({super.key, required this.onScrollEnd, this.isSearching = false}); @override Widget build(BuildContext context) { @@ -777,12 +647,7 @@ class SearchEmptyContent extends StatelessWidget { ), ), const SizedBox(height: 16), - Center( - child: Text( - 'search_page_search_photos_videos'.tr(), - style: context.textTheme.labelLarge, - ), - ), + Center(child: Text('search_page_search_photos_videos'.tr(), style: context.textTheme.labelLarge)), const SizedBox(height: 32), const QuickLinkList(), ], @@ -798,13 +663,8 @@ class QuickLinkList extends StatelessWidget { Widget build(BuildContext context) { return Container( decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), - border: Border.all( - color: context.colorScheme.outline.withAlpha(10), - width: 1, - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), + border: Border.all(color: context.colorScheme.outline.withAlpha(10), width: 1), gradient: LinearGradient( colors: [ context.colorScheme.primary.withAlpha(10), @@ -868,19 +728,9 @@ class QuickLink extends StatelessWidget { ); return ListTile( - shape: RoundedRectangleBorder( - borderRadius: borderRadius, - ), - leading: Icon( - icon, - size: 26, - ), - title: Text( - title, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + shape: RoundedRectangleBorder(borderRadius: borderRadius), + leading: Icon(icon, size: 26), + title: Text(title, style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500)), onTap: onTap, ); } diff --git a/mobile/lib/pages/settings/beta_sync_settings.page.dart b/mobile/lib/pages/settings/beta_sync_settings.page.dart index ba23ccf5eb..992557b7c6 100644 --- a/mobile/lib/pages/settings/beta_sync_settings.page.dart +++ b/mobile/lib/pages/settings/beta_sync_settings.page.dart @@ -5,9 +5,7 @@ import 'package:immich_mobile/widgets/settings/beta_sync_settings/beta_sync_sett @RoutePage() class BetaSyncSettingsPage extends StatelessWidget { - const BetaSyncSettingsPage({ - super.key, - }); + const BetaSyncSettingsPage({super.key}); @override Widget build(BuildContext context) { @@ -18,9 +16,7 @@ class BetaSyncSettingsPage extends StatelessWidget { leading: IconButton( onPressed: () => context.maybePop(true), splashRadius: 24, - icon: const Icon( - Icons.arrow_back_ios_rounded, - ), + icon: const Icon(Icons.arrow_back_ios_rounded), ), ), body: const BetaSyncSettings(), diff --git a/mobile/lib/pages/share_intent/share_intent.page.dart b/mobile/lib/pages/share_intent/share_intent.page.dart index 2b328b7ede..9d2dbe80c2 100644 --- a/mobile/lib/pages/share_intent/share_intent.page.dart +++ b/mobile/lib/pages/share_intent/share_intent.page.dart @@ -60,22 +60,16 @@ class ShareIntentPage extends HookConsumerWidget { appBar: AppBar( title: Column( children: [ - const Text('upload_to_immich').tr( - namedArgs: {'count': candidates.length.toString()}, - ), + const Text('upload_to_immich').tr(namedArgs: {'count': candidates.length.toString()}), Text( currentEndpoint, - style: context.textTheme.labelMedium?.copyWith( - color: context.colorScheme.onSurface.withAlpha(200), - ), + style: context.textTheme.labelMedium?.copyWith(color: context.colorScheme.onSurface.withAlpha(200)), ), ], ), leading: IconButton( onPressed: () { - context.navigateTo( - Store.isBetaTimelineEnabled ? const TabShellRoute() : const TabControllerRoute(), - ); + context.navigateTo(Store.isBetaTimelineEnabled ? const TabShellRoute() : const TabControllerRoute()); }, icon: const Icon(Icons.arrow_back), ), @@ -84,16 +78,10 @@ class ShareIntentPage extends HookConsumerWidget { itemCount: attachments.length, itemBuilder: (context, index) { final attachment = attachments[index]; - final target = candidates.firstWhere( - (element) => element.id == attachment.id, - orElse: () => attachment, - ); + final target = candidates.firstWhere((element) => element.id == attachment.id, orElse: () => attachment); return Padding( - padding: const EdgeInsets.symmetric( - vertical: 4.0, - horizontal: 16, - ), + padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 16), child: LargeLeadingTile( onTap: () => toggleSelection(attachment), disabled: isUploaded.value, @@ -103,21 +91,11 @@ class ShareIntentPage extends HookConsumerWidget { ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(16)), child: attachment.isImage - ? Image.file( - attachment.file, - width: 64, - height: 64, - fit: BoxFit.cover, - ) + ? Image.file(attachment.file, width: 64, height: 64, fit: BoxFit.cover) : const SizedBox( width: 64, height: 64, - child: Center( - child: Icon( - Icons.videocam, - color: Colors.white, - ), - ), + child: Center(child: Icon(Icons.videocam, color: Colors.white)), ), ), if (attachment.isImage) @@ -128,25 +106,13 @@ class ShareIntentPage extends HookConsumerWidget { Icons.image, color: Colors.white, size: 20, - shadows: [ - Shadow( - offset: Offset(0, 0), - blurRadius: 8.0, - color: Colors.black45, - ), - ], + shadows: [Shadow(offset: Offset(0, 0), blurRadius: 8.0, color: Colors.black45)], ), ), ], ), - title: Text( - attachment.fileName, - style: context.textTheme.titleSmall, - ), - subtitle: Text( - attachment.fileSize, - style: context.textTheme.labelLarge, - ), + title: Text(attachment.fileName, style: context.textTheme.titleSmall), + subtitle: Text(attachment.fileSize, style: context.textTheme.labelLarge), trailing: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: UploadStatusIcon( @@ -185,22 +151,14 @@ class UploadingText extends StatelessWidget { return element.status == UploadStatus.complete; }).length; - return const Text("shared_intent_upload_button_progress_text").tr( - namedArgs: { - 'current': uploadedCount.toString(), - 'total': candidates.length.toString(), - }, - ); + return const Text( + "shared_intent_upload_button_progress_text", + ).tr(namedArgs: {'current': uploadedCount.toString(), 'total': candidates.length.toString()}); } } class UploadStatusIcon extends StatelessWidget { - const UploadStatusIcon({ - super.key, - required this.status, - required this.selected, - this.progress = 0, - }); + const UploadStatusIcon({super.key, required this.status, required this.selected, this.progress = 0}); final UploadStatus status; final double progress; @@ -218,55 +176,42 @@ class UploadStatusIcon extends StatelessWidget { final statusIcon = switch (status) { UploadStatus.enqueued => Icon( - Icons.check_circle_rounded, - color: context.primaryColor, - semanticLabel: 'enqueued'.tr(), - ), + Icons.check_circle_rounded, + color: context.primaryColor, + semanticLabel: 'enqueued'.tr(), + ), UploadStatus.running => Stack( - alignment: AlignmentDirectional.center, - children: [ - SizedBox( - width: 40, - height: 40, - child: TweenAnimationBuilder( - tween: Tween(begin: 0.0, end: progress), - duration: const Duration(milliseconds: 500), - builder: (context, value, _) => CircularProgressIndicator( - backgroundColor: context.colorScheme.surfaceContainerLow, - strokeWidth: 3, - value: value, - semanticsLabel: 'uploading'.tr(), - ), + alignment: AlignmentDirectional.center, + children: [ + SizedBox( + width: 40, + height: 40, + child: TweenAnimationBuilder( + tween: Tween(begin: 0.0, end: progress), + duration: const Duration(milliseconds: 500), + builder: (context, value, _) => CircularProgressIndicator( + backgroundColor: context.colorScheme.surfaceContainerLow, + strokeWidth: 3, + value: value, + semanticsLabel: 'uploading'.tr(), ), ), - Text( - (progress * 100).toStringAsFixed(0), - style: context.textTheme.labelSmall?.copyWith( - fontWeight: FontWeight.bold, - ), - ), - ], - ), - UploadStatus.complete => Icon( - Icons.check_circle_rounded, - color: Colors.green, - semanticLabel: 'completed'.tr(), - ), - UploadStatus.notFound || UploadStatus.failed => Icon( - Icons.error_rounded, - color: Colors.red, - semanticLabel: 'failed'.tr(), - ), - UploadStatus.canceled => Icon( - Icons.cancel_rounded, - color: Colors.red, - semanticLabel: 'canceled'.tr(), - ), + ), + Text( + (progress * 100).toStringAsFixed(0), + style: context.textTheme.labelSmall?.copyWith(fontWeight: FontWeight.bold), + ), + ], + ), + UploadStatus.complete => Icon(Icons.check_circle_rounded, color: Colors.green, semanticLabel: 'completed'.tr()), + UploadStatus.notFound || + UploadStatus.failed => Icon(Icons.error_rounded, color: Colors.red, semanticLabel: 'failed'.tr()), + UploadStatus.canceled => Icon(Icons.cancel_rounded, color: Colors.red, semanticLabel: 'canceled'.tr()), UploadStatus.waitingToRetry || UploadStatus.paused => Icon( - Icons.pause_circle_rounded, - color: context.primaryColor, - semanticLabel: 'paused'.tr(), - ), + Icons.pause_circle_rounded, + color: context.primaryColor, + semanticLabel: 'paused'.tr(), + ), }; return statusIcon; diff --git a/mobile/lib/platform/native_sync_api.g.dart b/mobile/lib/platform/native_sync_api.g.dart index b4d1d91b69..67a320a96d 100644 --- a/mobile/lib/platform/native_sync_api.g.dart +++ b/mobile/lib/platform/native_sync_api.g.dart @@ -21,8 +21,10 @@ bool _deepEquals(Object? a, Object? b) { } if (a is Map && b is Map) { return a.length == b.length && - a.entries.every((MapEntry entry) => - (b as Map).containsKey(entry.key) && _deepEquals(entry.value, b[entry.key])); + a.entries.every( + (MapEntry entry) => + (b as Map).containsKey(entry.key) && _deepEquals(entry.value, b[entry.key]), + ); } return a == b; } @@ -59,17 +61,7 @@ class PlatformAsset { int orientation; List _toList() { - return [ - id, - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - orientation, - ]; + return [id, name, type, createdAt, updatedAt, width, height, durationInSeconds, orientation]; } Object encode() { @@ -128,13 +120,7 @@ class PlatformAlbum { int assetCount; List _toList() { - return [ - id, - name, - updatedAt, - isCloud, - assetCount, - ]; + return [id, name, updatedAt, isCloud, assetCount]; } Object encode() { @@ -170,12 +156,7 @@ class PlatformAlbum { } class SyncDelta { - SyncDelta({ - required this.hasChanges, - required this.updates, - required this.deletes, - required this.assetAlbums, - }); + SyncDelta({required this.hasChanges, required this.updates, required this.deletes, required this.assetAlbums}); bool hasChanges; @@ -186,12 +167,7 @@ class SyncDelta { Map> assetAlbums; List _toList() { - return [ - hasChanges, - updates, - deletes, - assetAlbums, - ]; + return [hasChanges, updates, deletes, assetAlbums]; } Object encode() { @@ -266,8 +242,8 @@ class NativeSyncApi { /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. NativeSyncApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) - : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); diff --git a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart index 439998065c..2ab1eeaaa9 100644 --- a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart +++ b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart @@ -34,23 +34,15 @@ final _features = [ final assets = await ref.read(remoteAssetRepositoryProvider).getSome(user.id); final selectedAssets = await ctx.pushRoute>( - DriftAssetSelectionTimelineRoute( - lockedSelectionAssets: assets.toSet(), - ), + DriftAssetSelectionTimelineRoute(lockedSelectionAssets: assets.toSet()), ); - DLog.log( - "Selected ${selectedAssets?.length ?? 0} assets", - ); + DLog.log("Selected ${selectedAssets?.length ?? 0} assets"); return Future.value(); }, ), - _Feature( - name: '', - icon: Icons.vertical_align_center_sharp, - onTap: (_, __) => Future.value(), - ), + _Feature(name: '', icon: Icons.vertical_align_center_sharp, onTap: (_, __) => Future.value()), _Feature( name: 'Sync Local', icon: Icons.photo_album_rounded, @@ -76,11 +68,7 @@ final _features = [ icon: Icons.save_rounded, onTap: (_, ref) => ref.read(driftProvider).customStatement("pragma wal_checkpoint(truncate)"), ), - _Feature( - name: '', - icon: Icons.vertical_align_center_sharp, - onTap: (_, __) => Future.value(), - ), + _Feature(name: '', icon: Icons.vertical_align_center_sharp, onTap: (_, __) => Future.value()), _Feature( name: 'Clear Delta Checkpoint', icon: Icons.delete_rounded, @@ -88,10 +76,7 @@ final _features = [ ), _Feature( name: 'Clear Local Data', - style: const TextStyle( - color: Colors.orange, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(color: Colors.orange, fontWeight: FontWeight.bold), icon: Icons.delete_forever_rounded, onTap: (_, ref) async { final db = ref.read(driftProvider); @@ -102,10 +87,7 @@ final _features = [ ), _Feature( name: 'Clear Remote Data', - style: const TextStyle( - color: Colors.orange, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(color: Colors.orange, fontWeight: FontWeight.bold), icon: Icons.delete_sweep_rounded, onTap: (_, ref) async { final db = ref.read(driftProvider); @@ -123,29 +105,20 @@ final _features = [ ), _Feature( name: 'Local Media Summary', - style: const TextStyle( - color: Colors.indigo, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(color: Colors.indigo, fontWeight: FontWeight.bold), icon: Icons.table_chart_rounded, onTap: (ctx, _) => ctx.pushRoute(const LocalMediaSummaryRoute()), ), _Feature( name: 'Remote Media Summary', - style: const TextStyle( - color: Colors.indigo, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(color: Colors.indigo, fontWeight: FontWeight.bold), icon: Icons.summarize_rounded, onTap: (ctx, _) => ctx.pushRoute(const RemoteMediaSummaryRoute()), ), _Feature( name: 'Reset Sqlite', icon: Icons.table_view_rounded, - style: const TextStyle( - color: Colors.red, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(color: Colors.red, fontWeight: FontWeight.bold), onTap: (_, ref) async { final drift = ref.read(driftProvider); // ignore: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member @@ -165,10 +138,7 @@ class FeatInDevPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: const Text('Features in Development'), - centerTitle: true, - ), + appBar: AppBar(title: const Text('Features in Development'), centerTitle: true), body: Column( children: [ Flexible( @@ -178,10 +148,7 @@ class FeatInDevPage extends StatelessWidget { final feat = _features[index]; return Consumer( builder: (ctx, ref, _) => ListTile( - title: Text( - feat.name, - style: feat.style, - ), + title: Text(feat.name, style: feat.style), trailing: Icon(feat.icon), visualDensity: VisualDensity.compact, onTap: () => unawaited(feat.onTap(ctx, ref)), @@ -200,12 +167,7 @@ class FeatInDevPage extends StatelessWidget { } class _Feature { - const _Feature({ - required this.name, - required this.icon, - required this.onTap, - this.style, - }); + const _Feature({required this.name, required this.icon, required this.onTap, this.style}); final String name; final IconData icon; @@ -244,18 +206,11 @@ class _DevLogs extends StatelessWidget { return ListTile( title: Text( logMessage.message, - style: TextStyle( - color: ctx.colorScheme.onSurface, - fontSize: 14.0, - overflow: TextOverflow.ellipsis, - ), + style: TextStyle(color: ctx.colorScheme.onSurface, fontSize: 14.0, overflow: TextOverflow.ellipsis), ), subtitle: Text( "at ${DateFormat("HH:mm:ss.SSS").format(logMessage.createdAt)}", - style: TextStyle( - color: ctx.colorScheme.onSurfaceSecondary, - fontSize: 12.0, - ), + style: TextStyle(color: ctx.colorScheme.onSurfaceSecondary, fontSize: 12.0), ), dense: true, visualDensity: VisualDensity.compact, diff --git a/mobile/lib/presentation/pages/dev/media_stat.page.dart b/mobile/lib/presentation/pages/dev/media_stat.page.dart index 70e93fd212..b48d8fc307 100644 --- a/mobile/lib/presentation/pages/dev/media_stat.page.dart +++ b/mobile/lib/presentation/pages/dev/media_stat.page.dart @@ -21,12 +21,7 @@ class _Summary extends StatelessWidget { final Future countFuture; final void Function()? onTap; - const _Summary({ - required this.name, - required this.countFuture, - this.leading, - this.onTap, - }); + const _Summary({required this.name, required this.countFuture, this.leading, this.onTap}); @override Widget build(BuildContext context) { @@ -40,31 +35,17 @@ class _Summary extends StatelessWidget { } else if (snapshot.hasError) { subtitle = const Icon(Icons.error_rounded); } else { - subtitle = Text( - '${snapshot.data ?? 0}', - style: ctx.textTheme.bodyLarge, - ); + subtitle = Text('${snapshot.data ?? 0}', style: ctx.textTheme.bodyLarge); } - return ListTile( - leading: leading, - title: Text(name), - trailing: subtitle, - onTap: onTap, - ); + return ListTile(leading: leading, title: Text(name), trailing: subtitle, onTap: onTap); }, ); } } final _localStats = [ - _Stat( - name: 'Local Assets', - load: (db) => db.managers.localAssetEntity.count(), - ), - _Stat( - name: 'Local Albums', - load: (db) => db.managers.localAlbumEntity.count(), - ), + _Stat(name: 'Local Assets', load: (db) => db.managers.localAssetEntity.count()), + _Stat(name: 'Local Albums', load: (db) => db.managers.localAlbumEntity.count()), ]; @RoutePage() @@ -97,10 +78,7 @@ class LocalMediaSummaryPage extends StatelessWidget { const Divider(), Padding( padding: const EdgeInsets.only(left: 15), - child: Text( - "Album summary", - style: ctx.textTheme.titleMedium, - ), + child: Text("Album summary", style: ctx.textTheme.titleMedium), ), ], ), @@ -117,15 +95,14 @@ class LocalMediaSummaryPage extends StatelessWidget { return SliverList.builder( itemBuilder: (_, index) { final album = albums[index]; - final countFuture = - db.managers.localAlbumAssetEntity.filter((f) => f.albumId.id.equals(album.id)).count(); + final countFuture = db.managers.localAlbumAssetEntity + .filter((f) => f.albumId.id.equals(album.id)) + .count(); return _Summary( leading: const Icon(Icons.photo_album_rounded), name: album.name, countFuture: countFuture, - onTap: () => context.router.push( - LocalTimelineRoute(album: album), - ), + onTap: () => context.router.push(LocalTimelineRoute(album: album)), ); }, itemCount: albums.length, @@ -141,38 +118,14 @@ class LocalMediaSummaryPage extends StatelessWidget { } final _remoteStats = [ - _Stat( - name: 'Remote Assets', - load: (db) => db.managers.remoteAssetEntity.count(), - ), - _Stat( - name: 'Exif Entities', - load: (db) => db.managers.remoteExifEntity.count(), - ), - _Stat( - name: 'Remote Albums', - load: (db) => db.managers.remoteAlbumEntity.count(), - ), - _Stat( - name: 'Memories', - load: (db) => db.managers.memoryEntity.count(), - ), - _Stat( - name: 'Memories Assets', - load: (db) => db.managers.memoryAssetEntity.count(), - ), - _Stat( - name: 'Stacks', - load: (db) => db.managers.stackEntity.count(), - ), - _Stat( - name: 'People', - load: (db) => db.managers.personEntity.count(), - ), - _Stat( - name: 'AssetFaces', - load: (db) => db.managers.assetFaceEntity.count(), - ), + _Stat(name: 'Remote Assets', load: (db) => db.managers.remoteAssetEntity.count()), + _Stat(name: 'Exif Entities', load: (db) => db.managers.remoteExifEntity.count()), + _Stat(name: 'Remote Albums', load: (db) => db.managers.remoteAlbumEntity.count()), + _Stat(name: 'Memories', load: (db) => db.managers.memoryEntity.count()), + _Stat(name: 'Memories Assets', load: (db) => db.managers.memoryAssetEntity.count()), + _Stat(name: 'Stacks', load: (db) => db.managers.stackEntity.count()), + _Stat(name: 'People', load: (db) => db.managers.personEntity.count()), + _Stat(name: 'AssetFaces', load: (db) => db.managers.assetFaceEntity.count()), ]; @RoutePage() @@ -205,10 +158,7 @@ class RemoteMediaSummaryPage extends StatelessWidget { const Divider(), Padding( padding: const EdgeInsets.only(left: 15), - child: Text( - "Album summary", - style: ctx.textTheme.titleMedium, - ), + child: Text("Album summary", style: ctx.textTheme.titleMedium), ), ], ), @@ -225,15 +175,14 @@ class RemoteMediaSummaryPage extends StatelessWidget { return SliverList.builder( itemBuilder: (_, index) { final album = albums[index]; - final countFuture = - db.managers.remoteAlbumAssetEntity.filter((f) => f.albumId.id.equals(album.id)).count(); + final countFuture = db.managers.remoteAlbumAssetEntity + .filter((f) => f.albumId.id.equals(album.id)) + .count(); return _Summary( leading: const Icon(Icons.photo_album_rounded), name: album.name, countFuture: countFuture, - onTap: () => context.router.push( - RemoteAlbumRoute(album: album), - ), + onTap: () => context.router.push(RemoteAlbumRoute(album: album)), ); }, itemCount: albums.length, diff --git a/mobile/lib/presentation/pages/drift_album.page.dart b/mobile/lib/presentation/pages/drift_album.page.dart index e22c63ac29..0835c741ad 100644 --- a/mobile/lib/presentation/pages/drift_album.page.dart +++ b/mobile/lib/presentation/pages/drift_album.page.dart @@ -35,13 +35,8 @@ class _DriftAlbumsPageState extends ConsumerState { pinned: true, actions: [ IconButton( - icon: const Icon( - Icons.add_rounded, - size: 28, - ), - onPressed: () => context.pushRoute( - const DriftCreateAlbumRoute(), - ), + icon: const Icon(Icons.add_rounded, size: 28), + onPressed: () => context.pushRoute(const DriftCreateAlbumRoute()), ), ], showUploadButton: false, @@ -49,9 +44,7 @@ class _DriftAlbumsPageState extends ConsumerState { AlbumSelector( onAlbumSelected: (album) { ref.read(currentRemoteAlbumProvider.notifier).setAlbum(album); - context.router.push( - RemoteAlbumRoute(album: album), - ); + context.router.push(RemoteAlbumRoute(album: album)); }, ), ], diff --git a/mobile/lib/presentation/pages/drift_archive.page.dart b/mobile/lib/presentation/pages/drift_archive.page.dart index 8a6f1607d0..ee8417bcad 100644 --- a/mobile/lib/presentation/pages/drift_archive.page.dart +++ b/mobile/lib/presentation/pages/drift_archive.page.dart @@ -16,18 +16,16 @@ class DriftArchivePage extends StatelessWidget { Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final user = ref.watch(currentUserProvider); - if (user == null) { - throw Exception('User must be logged in to access archive'); - } + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access archive'); + } - final timelineService = ref.watch(timelineFactoryProvider).archive(user.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + final timelineService = ref.watch(timelineFactoryProvider).archive(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: Timeline( appBar: MesmerizingSliverAppBar( diff --git a/mobile/lib/presentation/pages/drift_asset_selection_timeline.page.dart b/mobile/lib/presentation/pages/drift_asset_selection_timeline.page.dart index 0e5631082b..19f813cdb5 100644 --- a/mobile/lib/presentation/pages/drift_asset_selection_timeline.page.dart +++ b/mobile/lib/presentation/pages/drift_asset_selection_timeline.page.dart @@ -10,10 +10,7 @@ import 'package:immich_mobile/providers/user.provider.dart'; @RoutePage() class DriftAssetSelectionTimelinePage extends ConsumerWidget { final Set lockedSelectionAssets; - const DriftAssetSelectionTimelinePage({ - super.key, - this.lockedSelectionAssets = const {}, - }); + const DriftAssetSelectionTimelinePage({super.key, this.lockedSelectionAssets = const {}}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -21,27 +18,19 @@ class DriftAssetSelectionTimelinePage extends ConsumerWidget { overrides: [ multiSelectProvider.overrideWith( () => MultiSelectNotifier( - MultiSelectState( - selectedAssets: {}, - lockedSelectionAssets: lockedSelectionAssets, - forceEnable: true, - ), + MultiSelectState(selectedAssets: {}, lockedSelectionAssets: lockedSelectionAssets, forceEnable: true), ), ), - timelineServiceProvider.overrideWith( - (ref) { - final user = ref.watch(currentUserProvider); - if (user == null) { - throw Exception( - 'User must be logged in to access asset selection timeline', - ); - } + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access asset selection timeline'); + } - final timelineService = ref.watch(timelineFactoryProvider).remoteAssets(user.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + final timelineService = ref.watch(timelineFactoryProvider).remoteAssets(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: const Timeline(), ); diff --git a/mobile/lib/presentation/pages/drift_create_album.page.dart b/mobile/lib/presentation/pages/drift_create_album.page.dart index ba7157b15d..bac23b45c7 100644 --- a/mobile/lib/presentation/pages/drift_create_album.page.dart +++ b/mobile/lib/presentation/pages/drift_create_album.page.dart @@ -70,12 +70,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { Widget _buildContent() { if (selectedAssets.isEmpty) { - return SliverList( - delegate: SliverChildListDelegate([ - _buildEmptyState(), - _buildSelectPhotosButton(), - ]), - ); + return SliverList(delegate: SliverChildListDelegate([_buildEmptyState(), _buildSelectPhotosButton()])); } else { return _buildSelectedImageGrid(); } @@ -84,10 +79,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { Widget _buildEmptyState() { return Padding( padding: const EdgeInsets.only(top: 0, left: 18), - child: Text( - 'create_shared_album_page_share_add_assets', - style: context.textTheme.labelLarge, - ).t(), + child: Text('create_shared_album_page_share_add_assets', style: context.textTheme.labelLarge).t(), ); } @@ -97,27 +89,17 @@ class _DriftCreateAlbumPageState extends ConsumerState { child: FilledButton.icon( style: FilledButton.styleFrom( alignment: Alignment.centerLeft, - padding: const EdgeInsets.symmetric( - vertical: 24.0, - horizontal: 16.0, - ), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10.0)), - ), + padding: const EdgeInsets.symmetric(vertical: 24.0, horizontal: 16.0), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10.0))), backgroundColor: context.colorScheme.surfaceContainerHigh, ), onPressed: onSelectPhotos, icon: Icon(Icons.add_rounded, color: context.primaryColor), label: Padding( - padding: const EdgeInsets.only( - left: 8.0, - ), + padding: const EdgeInsets.only(left: 8.0), child: Text( 'create_shared_album_page_share_select_photos', - style: context.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.w600, - color: context.primaryColor, - ), + style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor), ).t(), ), ), @@ -133,16 +115,13 @@ class _DriftCreateAlbumPageState extends ConsumerState { crossAxisSpacing: 1.0, mainAxisSpacing: 1.0, ), - delegate: SliverChildBuilderDelegate( - (context, index) { - final asset = selectedAssets.elementAt(index); - return GestureDetector( - onTap: onBackgroundTapped, - child: Thumbnail(asset: asset), - ); - }, - childCount: selectedAssets.length, - ), + delegate: SliverChildBuilderDelegate((context, index) { + final asset = selectedAssets.elementAt(index); + return GestureDetector( + onTap: onBackgroundTapped, + child: Thumbnail(asset: asset), + ); + }, childCount: selectedAssets.length), ), ); } @@ -162,9 +141,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { Future onSelectPhotos() async { final assets = await context.pushRoute>( - DriftAssetSelectionTimelineRoute( - lockedSelectionAssets: selectedAssets, - ), + DriftAssetSelectionTimelineRoute(lockedSelectionAssets: selectedAssets), ); if (assets == null || assets.isEmpty) { @@ -183,16 +160,15 @@ class _DriftCreateAlbumPageState extends ConsumerState { if (title.isEmpty) { if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('create_album_title_required'.t()), - backgroundColor: context.colorScheme.error, - ), + SnackBar(content: Text('create_album_title_required'.t()), backgroundColor: context.colorScheme.error), ); } return; } - final album = await ref.watch(remoteAlbumProvider.notifier).createAlbum( + final album = await ref + .watch(remoteAlbumProvider.notifier) + .createAlbum( title: title, description: albumDescriptionController.text.trim(), assetIds: selectedAssets.map((asset) { @@ -203,18 +179,13 @@ class _DriftCreateAlbumPageState extends ConsumerState { if (album != null) { ref.read(currentRemoteAlbumProvider.notifier).setAlbum(album); - context.replaceRoute( - RemoteAlbumRoute(album: album), - ); + context.replaceRoute(RemoteAlbumRoute(album: album)); } } Widget buildTitleInputField() { return Padding( - padding: const EdgeInsets.only( - right: 10.0, - left: 10.0, - ), + padding: const EdgeInsets.only(right: 10.0, left: 10.0), child: _AlbumTitleTextField( focusNode: albumTitleTextFieldFocusNode, textController: albumTitleController, @@ -230,11 +201,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { Widget buildDescriptionInputField() { return Padding( - padding: const EdgeInsets.only( - right: 10.0, - left: 10.0, - top: 8, - ), + padding: const EdgeInsets.only(right: 10.0, left: 10.0, top: 8), child: _AlbumViewerEditableDescription( textController: albumDescriptionController, focusNode: albumDescriptionTextFieldFocusNode, @@ -244,11 +211,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { Widget buildControlButton() { return Padding( - padding: const EdgeInsets.only( - left: 12.0, - top: 8.0, - bottom: 8.0, - ), + padding: const EdgeInsets.only(left: 12.0, top: 8.0, bottom: 8.0), child: SizedBox( height: 42.0, child: ListView( @@ -272,10 +235,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { elevation: 0, centerTitle: false, backgroundColor: context.scaffoldBackgroundColor, - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.close_rounded), - ), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.close_rounded)), title: const Text('create_album').t(), actions: [ TextButton( @@ -292,12 +252,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { ), body: GestureDetector( onTap: onBackgroundTapped, - child: CustomScrollView( - slivers: [ - _buildSliverAppBar(), - _buildContent(), - ], - ), + child: CustomScrollView(slivers: [_buildSliverAppBar(), _buildContent()]), ), ); } @@ -341,11 +296,7 @@ class _AlbumTitleTextFieldState extends State<_AlbumTitleTextField> { Widget build(BuildContext context) { return TextField( focusNode: widget.focusNode, - style: TextStyle( - fontSize: 28.0, - color: context.colorScheme.onSurface, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 28.0, color: context.colorScheme.onSurface, fontWeight: FontWeight.bold), controller: widget.textController, onTap: () { if (widget.textController.text == 'create_album_page_untitled'.t(context: context)) { @@ -353,35 +304,23 @@ class _AlbumTitleTextFieldState extends State<_AlbumTitleTextField> { } }, decoration: InputDecoration( - contentPadding: const EdgeInsets.symmetric( - horizontal: 8.0, - vertical: 16.0, - ), + contentPadding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 16.0), suffixIcon: widget.textController.text.isNotEmpty && widget.isFocus ? IconButton( onPressed: () { widget.textController.clear(); }, - icon: Icon( - Icons.cancel_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.cancel_rounded, color: context.primaryColor), splashRadius: 10.0, ) : null, enabledBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.transparent), - borderRadius: BorderRadius.all( - Radius.circular(16.0), - ), + borderRadius: BorderRadius.all(Radius.circular(16.0)), ), focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: context.primaryColor.withValues(alpha: 0.3), - ), - borderRadius: const BorderRadius.all( - Radius.circular(16.0), - ), + borderSide: BorderSide(color: context.primaryColor.withValues(alpha: 0.3)), + borderRadius: const BorderRadius.all(Radius.circular(16.0)), ), hintText: 'add_a_title'.t(), hintStyle: context.themeData.inputDecorationTheme.hintStyle?.copyWith( @@ -398,10 +337,7 @@ class _AlbumTitleTextFieldState extends State<_AlbumTitleTextField> { } class _AlbumViewerEditableDescription extends StatefulWidget { - const _AlbumViewerEditableDescription({ - required this.textController, - required this.focusNode, - }); + const _AlbumViewerEditableDescription({required this.textController, required this.focusNode}); final TextEditingController textController; final FocusNode focusNode; @@ -448,37 +384,23 @@ class _AlbumViewerEditableDescriptionState extends State<_AlbumViewerEditableDes minLines: 1, controller: widget.textController, decoration: InputDecoration( - contentPadding: const EdgeInsets.symmetric( - horizontal: 12.0, - vertical: 16.0, - ), + contentPadding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 16.0), suffixIcon: widget.focusNode.hasFocus && widget.textController.text.isNotEmpty ? IconButton( onPressed: () { widget.textController.clear(); }, - icon: Icon( - Icons.cancel_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.cancel_rounded, color: context.primaryColor), splashRadius: 10.0, ) : null, enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: context.colorScheme.outline.withValues(alpha: 0.3), - ), - borderRadius: const BorderRadius.all( - Radius.circular(16.0), - ), + borderSide: BorderSide(color: context.colorScheme.outline.withValues(alpha: 0.3)), + borderRadius: const BorderRadius.all(Radius.circular(16.0)), ), focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: context.primaryColor.withValues(alpha: 0.3), - ), - borderRadius: const BorderRadius.all( - Radius.circular(16.0), - ), + borderSide: BorderSide(color: context.primaryColor.withValues(alpha: 0.3)), + borderRadius: const BorderRadius.all(Radius.circular(16.0)), ), hintStyle: context.themeData.inputDecorationTheme.hintStyle?.copyWith( fontSize: 16.0, diff --git a/mobile/lib/presentation/pages/drift_favorite.page.dart b/mobile/lib/presentation/pages/drift_favorite.page.dart index 5648fd7fac..2bc3f26363 100644 --- a/mobile/lib/presentation/pages/drift_favorite.page.dart +++ b/mobile/lib/presentation/pages/drift_favorite.page.dart @@ -16,18 +16,16 @@ class DriftFavoritePage extends StatelessWidget { Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final user = ref.watch(currentUserProvider); - if (user == null) { - throw Exception('User must be logged in to access favorite'); - } + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access favorite'); + } - final timelineService = ref.watch(timelineFactoryProvider).favorite(user.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + final timelineService = ref.watch(timelineFactoryProvider).favorite(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: Timeline( appBar: MesmerizingSliverAppBar( diff --git a/mobile/lib/presentation/pages/drift_library.page.dart b/mobile/lib/presentation/pages/drift_library.page.dart index 5cf47bc995..28fa6c6a16 100644 --- a/mobile/lib/presentation/pages/drift_library.page.dart +++ b/mobile/lib/presentation/pages/drift_library.page.dart @@ -27,12 +27,7 @@ class DriftLibraryPage extends ConsumerWidget { return const Scaffold( body: CustomScrollView( slivers: [ - ImmichSliverAppBar( - snap: false, - floating: false, - pinned: true, - showUploadButton: false, - ), + ImmichSliverAppBar(snap: false, floating: false, pinned: true, showUploadButton: false), _ActionButtonGrid(), _CollectionCards(), _QuickAccessButtonList(), @@ -47,9 +42,7 @@ class _ActionButtonGrid extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final isTrashEnable = ref.watch( - serverInfoProvider.select((state) => state.serverFeatures.trash), - ); + final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); return SliverPadding( padding: const EdgeInsets.only(left: 16, top: 16, right: 16, bottom: 12), @@ -97,11 +90,7 @@ class _ActionButtonGrid extends ConsumerWidget { } class _ActionButton extends StatelessWidget { - const _ActionButton({ - required this.icon, - required this.onTap, - required this.label, - }); + const _ActionButton({required this.icon, required this.onTap, required this.label}); final IconData icon; final VoidCallback onTap; @@ -114,13 +103,7 @@ class _ActionButton extends StatelessWidget { onPressed: onTap, label: Padding( padding: const EdgeInsets.only(left: 4.0), - child: Text( - label, - style: TextStyle( - color: context.colorScheme.onSurface, - fontSize: 15, - ), - ), + child: Text(label, style: TextStyle(color: context.colorScheme.onSurface, fontSize: 15)), ), style: FilledButton.styleFrom( elevation: 0, @@ -129,16 +112,10 @@ class _ActionButton extends StatelessWidget { alignment: Alignment.centerLeft, shape: RoundedRectangleBorder( borderRadius: const BorderRadius.all(Radius.circular(25)), - side: BorderSide( - color: context.colorScheme.onSurface.withAlpha(10), - width: 1, - ), + side: BorderSide(color: context.colorScheme.onSurface.withAlpha(10), width: 1), ), ), - icon: Icon( - icon, - color: context.primaryColor, - ), + icon: Icon(icon, color: context.primaryColor), ), ); } @@ -155,11 +132,7 @@ class _CollectionCards extends StatelessWidget { child: Wrap( spacing: 8, runSpacing: 8, - children: [ - _PeopleCollectionCard(), - _PlacesCollectionCard(), - _LocalAlbumsCollectionCard(), - ], + children: [_PeopleCollectionCard(), _PlacesCollectionCard(), _LocalAlbumsCollectionCard()], ), ), ); @@ -188,22 +161,15 @@ class _PeopleCollectionCard extends ConsumerWidget { height: size, width: size, decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withAlpha(30), - context.colorScheme.primary.withAlpha(25), - ], + colors: [context.colorScheme.primary.withAlpha(30), context.colorScheme.primary.withAlpha(25)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), child: people.widgetWhen( - onLoading: () => const Center( - child: CircularProgressIndicator(), - ), + onLoading: () => const Center(child: CircularProgressIndicator()), onData: (people) { return GridView.count( crossAxisCount: 2, @@ -253,9 +219,7 @@ class _PlacesCollectionCard extends StatelessWidget { final size = context.width * widthFactor - 20.0; return GestureDetector( - onTap: () => context.pushRoute( - DriftPlaceRoute(currentLocation: null), - ), + onTap: () => context.pushRoute(DriftPlaceRoute(currentLocation: null)), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -270,10 +234,7 @@ class _PlacesCollectionCard extends StatelessWidget { child: IgnorePointer( child: MapThumbnail( zoom: 8, - centre: const LatLng( - 21.44950, - -157.91959, - ), + centre: const LatLng(21.44950, -157.91959), showAttribution: false, themeMode: context.isDarkTheme ? ThemeMode.dark : ThemeMode.light, ), @@ -323,10 +284,7 @@ class _LocalAlbumsCollectionCard extends ConsumerWidget { decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withAlpha(30), - context.colorScheme.primary.withAlpha(25), - ], + colors: [context.colorScheme.primary.withAlpha(30), context.colorScheme.primary.withAlpha(25)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), @@ -340,24 +298,14 @@ class _LocalAlbumsCollectionCard extends ConsumerWidget { children: albums.when( data: (data) { return data.take(4).map((album) { - return LocalAlbumThumbnail( - albumId: album.id, - ); + return LocalAlbumThumbnail(albumId: album.id); }).toList(); }, error: (error, _) { - return [ - Center( - child: Text('Error: $error'), - ), - ]; + return [Center(child: Text('Error: $error'))]; }, loading: () { - return [ - const Center( - child: CircularProgressIndicator(), - ), - ]; + return [const Center(child: CircularProgressIndicator())]; }, ), ), @@ -394,13 +342,8 @@ class _QuickAccessButtonList extends ConsumerWidget { sliver: SliverToBoxAdapter( child: Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(10), - width: 1, - ), - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(10), width: 1), + borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( colors: [ context.colorScheme.primary.withAlpha(10), @@ -425,41 +368,26 @@ class _QuickAccessButtonList extends ConsumerWidget { bottomRight: Radius.circular(partners.isEmpty ? 20 : 0), ), ), - leading: const Icon( - Icons.folder_outlined, - size: 26, - ), + leading: const Icon(Icons.folder_outlined, size: 26), title: Text( 'folders'.t(context: context), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), ), onTap: () => context.pushRoute(FolderRoute()), ), ListTile( - leading: const Icon( - Icons.lock_outline_rounded, - size: 26, - ), + leading: const Icon(Icons.lock_outline_rounded, size: 26), title: Text( 'locked_folder'.t(context: context), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), ), onTap: () => context.pushRoute(const DriftLockedFolderRoute()), ), ListTile( - leading: const Icon( - Icons.group_outlined, - size: 26, - ), + leading: const Icon(Icons.group_outlined, size: 26), title: Text( 'partners'.t(context: context), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), ), onTap: () => context.pushRoute(const DriftPartnerRoute()), ), @@ -494,22 +422,13 @@ class _PartnerList extends StatelessWidget { bottomRight: Radius.circular(isLastItem ? 20 : 0), ), ), - contentPadding: const EdgeInsets.only( - left: 12.0, - right: 18.0, - ), - leading: PartnerUserAvatar( - partner: partner, - ), + contentPadding: const EdgeInsets.only(left: 12.0, right: 18.0), + leading: PartnerUserAvatar(partner: partner), title: const Text( "partner_list_user_photos", - style: TextStyle( - fontWeight: FontWeight.w500, - ), + style: TextStyle(fontWeight: FontWeight.w500), ).t(context: context, args: {'user': partner.name}), - onTap: () => context.pushRoute( - DriftPartnerDetailRoute(partner: partner), - ), + onTap: () => context.pushRoute(DriftPartnerDetailRoute(partner: partner)), ); }, ); diff --git a/mobile/lib/presentation/pages/drift_local_album.page.dart b/mobile/lib/presentation/pages/drift_local_album.page.dart index 7add23259e..536d9d82e2 100644 --- a/mobile/lib/presentation/pages/drift_local_album.page.dart +++ b/mobile/lib/presentation/pages/drift_local_album.page.dart @@ -16,14 +16,7 @@ class DriftLocalAlbumsPage extends StatelessWidget { @override Widget build(BuildContext context) { - return const Scaffold( - body: CustomScrollView( - slivers: [ - LocalAlbumsSliverAppBar(), - _AlbumList(), - ], - ), - ); + return const Scaffold(body: CustomScrollView(slivers: [LocalAlbumsSliverAppBar(), _AlbumList()])); } } @@ -37,10 +30,7 @@ class _AlbumList extends ConsumerWidget { return albums.when( loading: () => const SliverToBoxAdapter( child: Center( - child: Padding( - padding: EdgeInsets.all(20.0), - child: CircularProgressIndicator(), - ), + child: Padding(padding: EdgeInsets.all(20.0), child: CircularProgressIndicator()), ), ), error: (error, stack) => SliverToBoxAdapter( @@ -49,9 +39,7 @@ class _AlbumList extends ConsumerWidget { padding: const EdgeInsets.all(20.0), child: Text( 'Error loading albums: $error, stack: $stack', - style: TextStyle( - color: context.colorScheme.error, - ), + style: TextStyle(color: context.colorScheme.error), ), ), ), @@ -60,10 +48,7 @@ class _AlbumList extends ConsumerWidget { if (albums.isEmpty) { return const SliverToBoxAdapter( child: Center( - child: Padding( - padding: EdgeInsets.all(20.0), - child: Text('No albums found'), - ), + child: Padding(padding: EdgeInsets.all(20.0), child: Text('No albums found')), ), ); } @@ -77,30 +62,12 @@ class _AlbumList extends ConsumerWidget { return Padding( padding: const EdgeInsets.only(bottom: 8.0), child: LargeLeadingTile( - leadingPadding: const EdgeInsets.only( - right: 16, - ), - leading: SizedBox( - width: 80, - height: 80, - child: LocalAlbumThumbnail( - albumId: album.id, - ), - ), - title: Text( - album.name, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), - ), + leadingPadding: const EdgeInsets.only(right: 16), + leading: SizedBox(width: 80, height: 80, child: LocalAlbumThumbnail(albumId: album.id)), + title: Text(album.name, style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600)), subtitle: Text( - 'items_count'.t( - context: context, - args: {'count': album.assetCount}, - ), - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + 'items_count'.t(context: context, args: {'count': album.assetCount}), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), onTap: () => context.pushRoute(LocalTimelineRoute(album: album)), ), diff --git a/mobile/lib/presentation/pages/drift_locked_folder.page.dart b/mobile/lib/presentation/pages/drift_locked_folder.page.dart index 417e902de3..b19e8468ca 100644 --- a/mobile/lib/presentation/pages/drift_locked_folder.page.dart +++ b/mobile/lib/presentation/pages/drift_locked_folder.page.dart @@ -45,27 +45,23 @@ class _DriftLockedFolderPageState extends ConsumerState w Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final user = ref.watch(currentUserProvider); - if (user == null) { - throw Exception('User must be logged in to access locked folder'); - } + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access locked folder'); + } - final timelineService = ref.watch(timelineFactoryProvider).lockedFolder(user.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + final timelineService = ref.watch(timelineFactoryProvider).lockedFolder(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: _showOverlay ? const SizedBox() : PopScope( onPopInvokedWithResult: (didPop, _) => didPop ? ref.read(authProvider.notifier).lockPinCode() : null, child: Timeline( - appBar: MesmerizingSliverAppBar( - title: 'locked_folder'.t(context: context), - ), + appBar: MesmerizingSliverAppBar(title: 'locked_folder'.t(context: context)), bottomSheet: const LockedFolderBottomSheet(), ), ), diff --git a/mobile/lib/presentation/pages/drift_memory.page.dart b/mobile/lib/presentation/pages/drift_memory.page.dart index c42feff0ea..55e5d24ecb 100644 --- a/mobile/lib/presentation/pages/drift_memory.page.dart +++ b/mobile/lib/presentation/pages/drift_memory.page.dart @@ -22,20 +22,14 @@ class DriftMemoryPage extends HookConsumerWidget { final List memories; final int memoryIndex; - const DriftMemoryPage({ - required this.memories, - required this.memoryIndex, - super.key, - }); + const DriftMemoryPage({required this.memories, required this.memoryIndex, super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final currentMemory = useState(memories[memoryIndex]); final currentAssetPage = useState(0); final currentMemoryIndex = useState(memoryIndex); - final assetProgress = useState( - "${currentAssetPage.value + 1}|${currentMemory.value.assets.length}", - ); + final assetProgress = useState("${currentAssetPage.value + 1}|${currentMemory.value.assets.length}"); const bgColor = Colors.black; final currentAsset = useState(null); @@ -55,19 +49,13 @@ class DriftMemoryPage extends HookConsumerWidget { }); toNextMemory() { - memoryPageController.nextPage( - duration: const Duration(milliseconds: 500), - curve: Curves.easeIn, - ); + memoryPageController.nextPage(duration: const Duration(milliseconds: 500), curve: Curves.easeIn); } void toPreviousMemory() { if (currentMemoryIndex.value > 0) { // Move to the previous memory page - memoryPageController.previousPage( - duration: const Duration(milliseconds: 500), - curve: Curves.easeIn, - ); + memoryPageController.previousPage(duration: const Duration(milliseconds: 500), curve: Curves.easeIn); // Wait for the next frame to ensure the page is built SchedulerBinding.instance.addPostFrameCallback((_) { @@ -94,10 +82,7 @@ class DriftMemoryPage extends HookConsumerWidget { // Go to the next asset PageController controller = memoryAssetPageControllers[currentMemoryIndex.value]; - controller.nextPage( - curve: Curves.easeInOut, - duration: const Duration(milliseconds: 500), - ); + controller.nextPage(curve: Curves.easeInOut, duration: const Duration(milliseconds: 500)); } else { // Go to the next memory since we are at the end of our assets toNextMemory(); @@ -109,10 +94,7 @@ class DriftMemoryPage extends HookConsumerWidget { // Go to the previous asset PageController controller = memoryAssetPageControllers[currentMemoryIndex.value]; - controller.previousPage( - curve: Curves.easeInOut, - duration: const Duration(milliseconds: 500), - ); + controller.previousPage(curve: Curves.easeInOut, duration: const Duration(milliseconds: 500)); } else { // Go to the previous memory since we are at the end of our assets toPreviousMemory(); @@ -160,14 +142,7 @@ class DriftMemoryPage extends HookConsumerWidget { // Precache the asset final size = MediaQuery.sizeOf(context); - await precacheImage( - getFullImageProvider( - asset, - size: Size(size.width, size.height), - ), - context, - size: size, - ); + await precacheImage(getFullImageProvider(asset, size: Size(size.width, size.height)), context, size: size); } // Precache the next page right away if we are on the first page @@ -219,9 +194,7 @@ class DriftMemoryPage extends HookConsumerWidget { backgroundColor: bgColor, body: SafeArea( child: PageView.builder( - physics: const BouncingScrollPhysics( - parent: AlwaysScrollableScrollPhysics(), - ), + physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), scrollDirection: Axis.vertical, controller: memoryPageController, onPageChanged: (pageNumber) { @@ -249,23 +222,13 @@ class DriftMemoryPage extends HookConsumerWidget { } final yearsAgo = DateTime.now().year - memories[mIndex].data.year; - final title = 'years_ago'.t( - context: context, - args: { - 'years': yearsAgo.toString(), - }, - ); + final title = 'years_ago'.t(context: context, args: {'years': yearsAgo.toString()}); // Build horizontal page final assetController = memoryAssetPageControllers[mIndex]; return Column( children: [ Padding( - padding: const EdgeInsets.only( - left: 24.0, - right: 24.0, - top: 8.0, - bottom: 2.0, - ), + padding: const EdgeInsets.only(left: 24.0, right: 24.0, top: 8.0, bottom: 2.0), child: AnimatedBuilder( animation: assetController, builder: (context, child) { @@ -285,9 +248,7 @@ class DriftMemoryPage extends HookConsumerWidget { child: Stack( children: [ PageView.builder( - physics: const BouncingScrollPhysics( - parent: AlwaysScrollableScrollPhysics(), - ), + physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), controller: assetController, onPageChanged: onAssetChanged, scrollDirection: Axis.horizontal, @@ -298,11 +259,7 @@ class DriftMemoryPage extends HookConsumerWidget { children: [ Container( color: Colors.black, - child: DriftMemoryCard( - asset: asset, - title: title, - showTitle: index == 0, - ), + child: DriftMemoryCard(asset: asset, title: title, showTitle: index == 0), ), Positioned.fill( child: Row( @@ -343,35 +300,24 @@ class DriftMemoryPage extends HookConsumerWidget { // turn off full screen mode here // https://github.com/Milad-Akarie/auto_route_library/issues/1799 context.maybePop(); - SystemChrome.setEnabledSystemUIMode( - SystemUiMode.edgeToEdge, - ); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); }, shape: const CircleBorder(), color: Colors.white.withValues(alpha: 0.2), elevation: 0, - child: const Icon( - Icons.close_rounded, - color: Colors.white, - ), + child: const Icon(Icons.close_rounded, color: Colors.white), ), ), if (currentAsset.value != null && currentAsset.value!.isVideo) Positioned( bottom: 24, right: 32, - child: Icon( - Icons.videocam_outlined, - color: Colors.grey[200], - ), + child: Icon(Icons.videocam_outlined, color: Colors.grey[200]), ), ], ), ), - DriftMemoryBottomInfo( - memory: memories[mIndex], - title: title, - ), + DriftMemoryBottomInfo(memory: memories[mIndex], title: title), ], ); }, diff --git a/mobile/lib/presentation/pages/drift_partner_detail.page.dart b/mobile/lib/presentation/pages/drift_partner_detail.page.dart index 3d9d28aeab..95c5b008b3 100644 --- a/mobile/lib/presentation/pages/drift_partner_detail.page.dart +++ b/mobile/lib/presentation/pages/drift_partner_detail.page.dart @@ -15,28 +15,20 @@ import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart'; class DriftPartnerDetailPage extends StatelessWidget { final PartnerUserDto partner; - const DriftPartnerDetailPage({ - super.key, - required this.partner, - }); + const DriftPartnerDetailPage({super.key, required this.partner}); @override Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final timelineService = ref.watch(timelineFactoryProvider).remoteAssets(partner.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + timelineServiceProvider.overrideWith((ref) { + final timelineService = ref.watch(timelineFactoryProvider).remoteAssets(partner.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: Timeline( - appBar: MesmerizingSliverAppBar( - title: partner.name, - icon: Icons.person_outline, - ), + appBar: MesmerizingSliverAppBar(title: partner.name, icon: Icons.person_outline), topSliverWidget: _InfoBox(partner: partner), topSliverWidgetHeight: 110, bottomSheet: const PartnerDetailBottomSheet(), @@ -48,9 +40,7 @@ class DriftPartnerDetailPage extends StatelessWidget { class _InfoBox extends ConsumerStatefulWidget { final PartnerUserDto partner; - const _InfoBox({ - required this.partner, - }); + const _InfoBox({required this.partner}); @override ConsumerState<_InfoBox> createState() => _InfoBoxState(); @@ -72,10 +62,7 @@ class _InfoBoxState extends ConsumerState<_InfoBox> { } try { - await ref.read(partnerUsersProvider.notifier).toggleShowInTimeline( - widget.partner.id, - user.id, - ); + await ref.read(partnerUsersProvider.notifier).toggleShowInTimeline(widget.partner.id, user.id); setState(() { _inTimeline = !_inTimeline; @@ -101,18 +88,10 @@ class _InfoBoxState extends ConsumerState<_InfoBox> { padding: const EdgeInsets.only(left: 8.0, right: 8.0, top: 16.0), child: Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(10), - width: 1, - ), - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(10), width: 1), + borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withAlpha(10), - context.colorScheme.primary.withAlpha(15), - ], + colors: [context.colorScheme.primary.withAlpha(10), context.colorScheme.primary.withAlpha(15)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), @@ -122,18 +101,13 @@ class _InfoBoxState extends ConsumerState<_InfoBox> { child: ListTile( title: Text( "Show in timeline", - style: context.textTheme.titleSmall?.copyWith( - color: context.colorScheme.primary, - ), + style: context.textTheme.titleSmall?.copyWith(color: context.colorScheme.primary), ), subtitle: Text( "Show photos and videos from this user in your timeline", style: context.textTheme.bodyMedium, ), - trailing: Switch( - value: _inTimeline, - onChanged: (_) => _toggleInTimeline(), - ), + trailing: Switch(value: _inTimeline, onChanged: (_) => _toggleInTimeline()), ), ), ), diff --git a/mobile/lib/presentation/pages/drift_place.page.dart b/mobile/lib/presentation/pages/drift_place.page.dart index 969bfc70fd..e85bb90d54 100644 --- a/mobile/lib/presentation/pages/drift_place.page.dart +++ b/mobile/lib/presentation/pages/drift_place.page.dart @@ -52,9 +52,7 @@ class _PlaceSliverAppBar extends StatelessWidget { pinned: true, snap: false, backgroundColor: context.colorScheme.surfaceContainer, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(5)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), automaticallyImplyLeading: search.value == null, centerTitle: true, title: search.value != null @@ -98,20 +96,14 @@ class _Map extends StatelessWidget { child: MapThumbnail( onTap: (_, __) => context.pushRoute(MapRoute(initialLocation: currentLocation)), zoom: 8, - centre: currentLocation ?? - const LatLng( - 21.44950, - -157.91959, - ), + centre: currentLocation ?? const LatLng(21.44950, -157.91959), showAttribution: false, themeMode: context.isDarkTheme ? ThemeMode.dark : ThemeMode.light, ), ), ), ) - : const SliverToBoxAdapter( - child: SizedBox.shrink(), - ); + : const SliverToBoxAdapter(child: SizedBox.shrink()); } } @@ -127,10 +119,7 @@ class _PlaceList extends ConsumerWidget { return places.when( loading: () => const SliverToBoxAdapter( child: Center( - child: Padding( - padding: EdgeInsets.all(20.0), - child: CircularProgressIndicator(), - ), + child: Padding(padding: EdgeInsets.all(20.0), child: CircularProgressIndicator()), ), ), error: (error, stack) => SliverToBoxAdapter( @@ -139,9 +128,7 @@ class _PlaceList extends ConsumerWidget { padding: const EdgeInsets.all(20.0), child: Text( 'Error loading places: $error, stack: $stack', - style: TextStyle( - color: context.colorScheme.error, - ), + style: TextStyle(color: context.colorScheme.error), ), ), ), @@ -174,21 +161,10 @@ class _PlaceTile extends StatelessWidget { Widget build(BuildContext context) { return LargeLeadingTile( onTap: () => context.pushRoute(DriftPlaceDetailRoute(place: place.$1)), - title: Text( - place.$1, - style: context.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + title: Text(place.$1, style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500)), leading: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), - child: Thumbnail( - size: const Size(80, 80), - fit: BoxFit.cover, - remoteId: place.$2, - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), + child: Thumbnail(size: const Size(80, 80), fit: BoxFit.cover, remoteId: place.$2), ), ); } diff --git a/mobile/lib/presentation/pages/drift_place_detail.page.dart b/mobile/lib/presentation/pages/drift_place_detail.page.dart index d55725231f..0f50227945 100644 --- a/mobile/lib/presentation/pages/drift_place_detail.page.dart +++ b/mobile/lib/presentation/pages/drift_place_detail.page.dart @@ -9,28 +9,20 @@ import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart'; class DriftPlaceDetailPage extends StatelessWidget { final String place; - const DriftPlaceDetailPage({ - super.key, - required this.place, - }); + const DriftPlaceDetailPage({super.key, required this.place}); @override Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final timelineService = ref.watch(timelineFactoryProvider).place(place); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + timelineServiceProvider.overrideWith((ref) { + final timelineService = ref.watch(timelineFactoryProvider).place(place); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: Timeline( - appBar: MesmerizingSliverAppBar( - title: place, - icon: Icons.location_on, - ), + appBar: MesmerizingSliverAppBar(title: place, icon: Icons.location_on), ), ); } diff --git a/mobile/lib/presentation/pages/drift_recently_taken.page.dart b/mobile/lib/presentation/pages/drift_recently_taken.page.dart index c99c36bd24..ceb121b124 100644 --- a/mobile/lib/presentation/pages/drift_recently_taken.page.dart +++ b/mobile/lib/presentation/pages/drift_recently_taken.page.dart @@ -15,24 +15,18 @@ class DriftRecentlyTakenPage extends StatelessWidget { Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final user = ref.watch(currentUserProvider); - if (user == null) { - throw Exception( - 'User must be logged in to access recently taken', - ); - } + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access recently taken'); + } - final timelineService = ref.watch(timelineFactoryProvider).remoteAssets(user.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + final timelineService = ref.watch(timelineFactoryProvider).remoteAssets(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], - child: Timeline( - appBar: MesmerizingSliverAppBar(title: 'recently_taken'.t()), - ), + child: Timeline(appBar: MesmerizingSliverAppBar(title: 'recently_taken'.t())), ); } } diff --git a/mobile/lib/presentation/pages/drift_remote_album.page.dart b/mobile/lib/presentation/pages/drift_remote_album.page.dart index 4173e262bc..336050d5a9 100644 --- a/mobile/lib/presentation/pages/drift_remote_album.page.dart +++ b/mobile/lib/presentation/pages/drift_remote_album.page.dart @@ -21,10 +21,7 @@ import 'package:immich_mobile/widgets/common/remote_album_sliver_app_bar.dart'; class RemoteAlbumPage extends ConsumerStatefulWidget { final RemoteAlbum album; - const RemoteAlbumPage({ - super.key, - required this.album, - }); + const RemoteAlbumPage({super.key, required this.album}); @override ConsumerState createState() => _RemoteAlbumPageState(); @@ -40,16 +37,16 @@ class _RemoteAlbumPageState extends ConsumerState { final albumAssets = await ref.read(remoteAlbumProvider.notifier).getAssets(widget.album.id); final newAssets = await context.pushRoute>( - DriftAssetSelectionTimelineRoute( - lockedSelectionAssets: albumAssets.toSet(), - ), + DriftAssetSelectionTimelineRoute(lockedSelectionAssets: albumAssets.toSet()), ); if (newAssets == null || newAssets.isEmpty) { return; } - final added = await ref.read(remoteAlbumProvider.notifier).addAssets( + final added = await ref + .read(remoteAlbumProvider.notifier) + .addAssets( widget.album.id, newAssets.map((asset) { final remoteAsset = asset as RemoteAsset; @@ -60,21 +57,14 @@ class _RemoteAlbumPageState extends ConsumerState { if (added > 0) { ImmichToast.show( context: context, - msg: "assets_added_to_album_count".t( - context: context, - args: { - 'count': added.toString(), - }, - ), + msg: "assets_added_to_album_count".t(context: context, args: {'count': added.toString()}), toastType: ToastType.success, ); } } Future addUsers(BuildContext context) async { - final newUsers = await context.pushRoute>( - DriftUserSelectionRoute(album: widget.album), - ); + final newUsers = await context.pushRoute>(DriftUserSelectionRoute(album: widget.album)); if (newUsers == null || newUsers.isEmpty) { return; @@ -86,12 +76,7 @@ class _RemoteAlbumPageState extends ConsumerState { if (newUsers.isNotEmpty) { ImmichToast.show( context: context, - msg: "users_added_to_album_count".t( - context: context, - args: { - 'count': newUsers.length, - }, - ), + msg: "users_added_to_album_count".t(context: context, args: {'count': newUsers.length}), toastType: ToastType.success, ); } @@ -107,9 +92,7 @@ class _RemoteAlbumPageState extends ConsumerState { } Future toggleAlbumOrder() async { - await ref.read(remoteAlbumProvider.notifier).toggleAlbumOrder( - widget.album.id, - ); + await ref.read(remoteAlbumProvider.notifier).toggleAlbumOrder(widget.album.id); ref.invalidate(timelineServiceProvider); } @@ -123,16 +106,9 @@ class _RemoteAlbumPageState extends ConsumerState { content: Column( mainAxisSize: MainAxisSize.min, children: [ - Text( - 'album_delete_confirmation'.t( - context: context, - args: {'album': widget.album.name}, - ), - ), + Text('album_delete_confirmation'.t(context: context, args: {'album': widget.album.name})), const SizedBox(height: 8), - Text( - 'album_delete_confirmation_description'.t(context: context), - ), + Text('album_delete_confirmation_description'.t(context: context)), ], ), actions: [ @@ -142,9 +118,7 @@ class _RemoteAlbumPageState extends ConsumerState { ), TextButton( onPressed: () => Navigator.of(context).pop(true), - style: TextButton.styleFrom( - foregroundColor: Theme.of(context).colorScheme.error, - ), + style: TextButton.styleFrom(foregroundColor: Theme.of(context).colorScheme.error), child: Text('delete_album'.t(context: context)), ), ], @@ -230,13 +204,11 @@ class _RemoteAlbumPageState extends ConsumerState { Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final timelineService = ref.watch(timelineFactoryProvider).remoteAlbum(albumId: widget.album.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + timelineServiceProvider.overrideWith((ref) { + final timelineService = ref.watch(timelineFactoryProvider).remoteAlbum(albumId: widget.album.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: Timeline( appBar: RemoteAlbumSliverAppBar( @@ -245,9 +217,7 @@ class _RemoteAlbumPageState extends ConsumerState { onToggleAlbumOrder: () => toggleAlbumOrder(), onEditTitle: () => showEditTitleAndDescription(context), ), - bottomSheet: RemoteAlbumBottomSheet( - album: widget.album, - ), + bottomSheet: RemoteAlbumBottomSheet(album: widget.album), ), ); } @@ -257,18 +227,13 @@ class _EditAlbumData { final String name; final String? description; - const _EditAlbumData({ - required this.name, - this.description, - }); + const _EditAlbumData({required this.name, this.description}); } class _EditAlbumDialog extends ConsumerStatefulWidget { final RemoteAlbum album; - const _EditAlbumDialog({ - required this.album, - }); + const _EditAlbumDialog({required this.album}); @override ConsumerState<_EditAlbumDialog> createState() => _EditAlbumDialogState(); @@ -302,19 +267,14 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { final newTitle = titleController.text.trim(); final newDescription = descriptionController.text.trim(); - await ref.read(remoteAlbumProvider.notifier).updateAlbum( - widget.album.id, - name: newTitle, - description: newDescription.isEmpty ? null : newDescription, - ); + await ref + .read(remoteAlbumProvider.notifier) + .updateAlbum(widget.album.id, name: newTitle, description: newDescription.isEmpty ? null : newDescription); if (mounted) { - Navigator.of(context).pop( - _EditAlbumData( - name: newTitle, - description: newDescription.isEmpty ? null : newDescription, - ), - ); + Navigator.of( + context, + ).pop(_EditAlbumData(name: newTitle, description: newDescription.isEmpty ? null : newDescription)); } } catch (e) { if (mounted) { @@ -331,11 +291,7 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { Widget build(BuildContext context) { return Dialog( insetPadding: const EdgeInsets.all(24), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(16), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))), child: SingleChildScrollView( child: Container( padding: const EdgeInsets.all(16), @@ -348,16 +304,9 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { children: [ Row( children: [ - Icon( - Icons.edit_outlined, - color: context.colorScheme.primary, - size: 24, - ), + Icon(Icons.edit_outlined, color: context.colorScheme.primary, size: 24), const SizedBox(width: 12), - Text( - 'edit_album'.t(context: context), - style: context.textTheme.titleMedium, - ), + Text('edit_album'.t(context: context), style: context.textTheme.titleMedium), ], ), const SizedBox(height: 24), @@ -365,9 +314,7 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { // Album Name Text( 'album_name'.t(context: context).toUpperCase(), - style: context.textTheme.labelSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.labelSmall?.copyWith(fontWeight: FontWeight.w600), ), const SizedBox(height: 4), TextFormField( @@ -375,9 +322,7 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { maxLines: 1, textCapitalization: TextCapitalization.sentences, decoration: InputDecoration( - border: const OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(12)), - ), + border: const OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(12))), filled: true, fillColor: context.colorScheme.surface, ), @@ -394,9 +339,7 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { // Description Text( 'description'.t(context: context).toUpperCase(), - style: context.textTheme.labelSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.labelSmall?.copyWith(fontWeight: FontWeight.w600), ), const SizedBox(height: 4), TextFormField( @@ -404,11 +347,7 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { maxLines: 4, textCapitalization: TextCapitalization.sentences, decoration: InputDecoration( - border: const OutlineInputBorder( - borderRadius: BorderRadius.all( - Radius.circular(12), - ), - ), + border: const OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(12))), filled: true, fillColor: context.colorScheme.surface, ), diff --git a/mobile/lib/presentation/pages/drift_trash.page.dart b/mobile/lib/presentation/pages/drift_trash.page.dart index 61fc5e35f7..4d18d12d01 100644 --- a/mobile/lib/presentation/pages/drift_trash.page.dart +++ b/mobile/lib/presentation/pages/drift_trash.page.dart @@ -16,18 +16,16 @@ class DriftTrashPage extends StatelessWidget { Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final user = ref.watch(currentUserProvider); - if (user == null) { - throw Exception('User must be logged in to access trash'); - } + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access trash'); + } - final timelineService = ref.watch(timelineFactoryProvider).trash(user.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + final timelineService = ref.watch(timelineFactoryProvider).trash(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: Timeline( showStorageIndicator: true, @@ -42,18 +40,14 @@ class DriftTrashPage extends StatelessWidget { topSliverWidgetHeight: 24, topSliverWidget: Consumer( builder: (context, ref, child) { - final trashDays = ref.watch( - serverInfoProvider.select((v) => v.serverConfig.trashDays), - ); + final trashDays = ref.watch(serverInfoProvider.select((v) => v.serverConfig.trashDays)); return SliverPadding( padding: const EdgeInsets.all(16.0), sliver: SliverToBoxAdapter( child: SizedBox( height: 24.0, - child: const Text( - "trash_page_info", - ).t(context: context, args: {"days": "$trashDays"}), + child: const Text("trash_page_info").t(context: context, args: {"days": "$trashDays"}), ), ), ); diff --git a/mobile/lib/presentation/pages/drift_user_selection.page.dart b/mobile/lib/presentation/pages/drift_user_selection.page.dart index 5aaa438a11..e8835e7146 100644 --- a/mobile/lib/presentation/pages/drift_user_selection.page.dart +++ b/mobile/lib/presentation/pages/drift_user_selection.page.dart @@ -49,10 +49,7 @@ final driftUsersProvider = FutureProvider.autoDispose>((ref) async class DriftUserSelectionPage extends HookConsumerWidget { final RemoteAlbum album; - const DriftUserSelectionPage({ - super.key, - required this.album, - }); + const DriftUserSelectionPage({super.key, required this.album}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -65,17 +62,9 @@ class DriftUserSelectionPage extends HookConsumerWidget { buildTileIcon(UserDto user) { if (sharedUsersList.value.contains(user)) { - return CircleAvatar( - backgroundColor: context.primaryColor, - child: const Icon( - Icons.check_rounded, - size: 25, - ), - ); + return CircleAvatar(backgroundColor: context.primaryColor, child: const Icon(Icons.check_rounded, size: 25)); } else { - return UserCircleAvatar( - user: user, - ); + return UserCircleAvatar(user: user); } } @@ -88,31 +77,19 @@ class DriftUserSelectionPage extends HookConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 8.0), child: Chip( backgroundColor: context.primaryColor.withValues(alpha: 0.15), - label: Text( - user.name, - style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - ), - ), + label: Text(user.name, style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold)), ), ), ); } return ListView( children: [ - Wrap( - children: [...usersChip], - ), + Wrap(children: [...usersChip]), Padding( padding: const EdgeInsets.all(16.0), child: Text( 'suggestions'.tr(), - style: const TextStyle( - fontSize: 14, - color: Colors.grey, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(fontSize: 14, color: Colors.grey, fontWeight: FontWeight.bold), ), ), ListView.builder( @@ -122,31 +99,15 @@ class DriftUserSelectionPage extends HookConsumerWidget { return ListTile( leading: buildTileIcon(users[index]), dense: true, - title: Text( - users[index].name, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ), - subtitle: Text( - users[index].email, - style: const TextStyle( - fontSize: 12, - ), - ), + title: Text(users[index].name, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), + subtitle: Text(users[index].email, style: const TextStyle(fontSize: 12)), onTap: () { if (sharedUsersList.value.contains(users[index])) { sharedUsersList.value = sharedUsersList.value - .where( - (selectedUser) => selectedUser.id != users[index].id, - ) + .where((selectedUser) => selectedUser.id != users[index].id) .toSet(); } else { - sharedUsersList.value = { - ...sharedUsersList.value, - users[index], - }; + sharedUsersList.value = {...sharedUsersList.value, users[index]}; } }, ); @@ -159,9 +120,7 @@ class DriftUserSelectionPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( - title: const Text( - 'invite_to_album', - ).tr(), + title: const Text('invite_to_album').tr(), elevation: 0, centerTitle: false, leading: IconButton( @@ -173,10 +132,7 @@ class DriftUserSelectionPage extends HookConsumerWidget { actions: [ TextButton( onPressed: sharedUsersList.value.isEmpty ? null : addNewUsersHandler, - child: const Text( - "add", - style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ).tr(), + child: const Text("add", style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)).tr(), ), ], ), diff --git a/mobile/lib/presentation/pages/drift_video.page.dart b/mobile/lib/presentation/pages/drift_video.page.dart index 94c5620f9a..eef05acdce 100644 --- a/mobile/lib/presentation/pages/drift_video.page.dart +++ b/mobile/lib/presentation/pages/drift_video.page.dart @@ -15,22 +15,18 @@ class DriftVideoPage extends StatelessWidget { Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final user = ref.watch(currentUserProvider); - if (user == null) { - throw Exception('User must be logged in to video'); - } + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to video'); + } - final timelineService = ref.watch(timelineFactoryProvider).video(user.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + final timelineService = ref.watch(timelineFactoryProvider).video(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], - child: Timeline( - appBar: MesmerizingSliverAppBar(title: 'videos'.t()), - ), + child: Timeline(appBar: MesmerizingSliverAppBar(title: 'videos'.t())), ); } } diff --git a/mobile/lib/presentation/pages/local_timeline.page.dart b/mobile/lib/presentation/pages/local_timeline.page.dart index d322b5d9d2..67bc17cb37 100644 --- a/mobile/lib/presentation/pages/local_timeline.page.dart +++ b/mobile/lib/presentation/pages/local_timeline.page.dart @@ -17,13 +17,11 @@ class LocalTimelinePage extends StatelessWidget { Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final timelineService = ref.watch(timelineFactoryProvider).localAlbum(albumId: album.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + timelineServiceProvider.overrideWith((ref) { + final timelineService = ref.watch(timelineFactoryProvider).localAlbum(albumId: album.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: Timeline( appBar: MesmerizingSliverAppBar(title: album.name), diff --git a/mobile/lib/presentation/pages/search/drift_search.page.dart b/mobile/lib/presentation/pages/search/drift_search.page.dart index 868f1ff298..f61fad5484 100644 --- a/mobile/lib/presentation/pages/search/drift_search.page.dart +++ b/mobile/lib/presentation/pages/search/drift_search.page.dart @@ -44,12 +44,7 @@ class DriftSearchPage extends HookConsumerWidget { location: preFilter?.location ?? SearchLocationFilter(), camera: preFilter?.camera ?? SearchCameraFilter(), date: preFilter?.date ?? SearchDateFilter(), - display: preFilter?.display ?? - SearchDisplayFilters( - isNotInAlbum: false, - isArchive: false, - isFavorite: false, - ), + display: preFilter?.display ?? SearchDisplayFilters(isNotInAlbum: false, isArchive: false, isFavorite: false), mediaType: preFilter?.mediaType ?? AssetType.other, language: "${context.locale.languageCode}-${context.locale.countryCode}", ), @@ -68,10 +63,7 @@ class DriftSearchPage extends HookConsumerWidget { SnackBar searchInfoSnackBar(String message) { return SnackBar( - content: Text( - message, - style: context.textTheme.labelLarge, - ), + content: Text(message, style: context.textTheme.labelLarge), showCloseIcon: true, behavior: SnackBarBehavior.fixed, closeIconColor: context.colorScheme.onSurface, @@ -92,9 +84,7 @@ class DriftSearchPage extends HookConsumerWidget { final hasResult = await ref.watch(paginatedSearchProvider.notifier).search(filter.value); if (!hasResult) { - context.showSnackBar( - searchInfoSnackBar('search_no_result'.t(context: context)), - ); + context.showSnackBar(searchInfoSnackBar('search_no_result'.t(context: context))); } previousFilter.value = filter.value; @@ -106,9 +96,7 @@ class DriftSearchPage extends HookConsumerWidget { final hasResult = await ref.watch(paginatedSearchProvider.notifier).search(filter.value); if (!hasResult) { - context.showSnackBar( - searchInfoSnackBar('search_no_more_result'.t(context: context)), - ); + context.showSnackBar(searchInfoSnackBar('search_no_more_result'.t(context: context))); } isSearching.value = false; @@ -116,39 +104,26 @@ class DriftSearchPage extends HookConsumerWidget { searchPreFilter() { if (preFilter != null) { - Future.delayed( - Duration.zero, - () { - search(); + Future.delayed(Duration.zero, () { + search(); - if (preFilter!.location.city != null) { - locationCurrentFilterWidget.value = Text( - preFilter!.location.city!, - style: context.textTheme.labelLarge, - ); - } - }, - ); + if (preFilter!.location.city != null) { + locationCurrentFilterWidget.value = Text(preFilter!.location.city!, style: context.textTheme.labelLarge); + } + }); } } - useEffect( - () { - Future.microtask( - () => ref.invalidate(paginatedSearchProvider), - ); - searchPreFilter(); + useEffect(() { + Future.microtask(() => ref.invalidate(paginatedSearchProvider)); + searchPreFilter(); - return null; - }, - [], - ); + return null; + }, []); showPeoplePicker() { handleOnSelect(Set value) { - filter.value = filter.value.copyWith( - people: value, - ); + filter.value = filter.value.copyWith(people: value); peopleCurrentFilterWidget.value = Text( value.map((e) => e.name != '' ? e.name : 'no_name'.t(context: context)).join(', '), @@ -157,9 +132,7 @@ class DriftSearchPage extends HookConsumerWidget { } handleClear() { - filter.value = filter.value.copyWith( - people: {}, - ); + filter.value = filter.value.copyWith(people: {}); peopleCurrentFilterWidget.value = null; search(); @@ -175,10 +148,7 @@ class DriftSearchPage extends HookConsumerWidget { expanded: true, onSearch: search, onClear: handleClear, - child: PeoplePicker( - onSelect: handleOnSelect, - filter: filter.value.people, - ), + child: PeoplePicker(onSelect: handleOnSelect, filter: filter.value.people), ), ), ); @@ -187,11 +157,7 @@ class DriftSearchPage extends HookConsumerWidget { showLocationPicker() { handleOnSelect(Map value) { filter.value = filter.value.copyWith( - location: SearchLocationFilter( - country: value['country'], - city: value['city'], - state: value['state'], - ), + location: SearchLocationFilter(country: value['country'], city: value['city'], state: value['state']), ); final locationText = []; @@ -207,16 +173,11 @@ class DriftSearchPage extends HookConsumerWidget { locationText.add(value['city']!); } - locationCurrentFilterWidget.value = Text( - locationText.join(', '), - style: context.textTheme.labelLarge, - ); + locationCurrentFilterWidget.value = Text(locationText.join(', '), style: context.textTheme.labelLarge); } handleClear() { - filter.value = filter.value.copyWith( - location: SearchLocationFilter(), - ); + filter.value = filter.value.copyWith(location: SearchLocationFilter()); locationCurrentFilterWidget.value = null; search(); @@ -233,15 +194,10 @@ class DriftSearchPage extends HookConsumerWidget { child: Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), child: Container( - padding: EdgeInsets.only( - bottom: context.viewInsets.bottom, - ), + padding: EdgeInsets.only(bottom: context.viewInsets.bottom), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: LocationPicker( - onSelected: handleOnSelect, - filter: filter.value.location, - ), + child: LocationPicker(onSelected: handleOnSelect, filter: filter.value.location), ), ), ), @@ -252,10 +208,7 @@ class DriftSearchPage extends HookConsumerWidget { showCameraPicker() { handleOnSelect(Map value) { filter.value = filter.value.copyWith( - camera: SearchCameraFilter( - make: value['make'], - model: value['model'], - ), + camera: SearchCameraFilter(make: value['make'], model: value['model']), ); cameraCurrentFilterWidget.value = Text( @@ -265,9 +218,7 @@ class DriftSearchPage extends HookConsumerWidget { } handleClear() { - filter.value = filter.value.copyWith( - camera: SearchCameraFilter(), - ); + filter.value = filter.value.copyWith(camera: SearchCameraFilter()); cameraCurrentFilterWidget.value = null; search(); @@ -283,10 +234,7 @@ class DriftSearchPage extends HookConsumerWidget { onClear: handleClear, child: Padding( padding: const EdgeInsets.all(16.0), - child: CameraPicker( - onSelect: handleOnSelect, - filter: filter.value.camera, - ), + child: CameraPicker(onSelect: handleOnSelect, filter: filter.value.camera), ), ), ); @@ -318,9 +266,7 @@ class DriftSearchPage extends HookConsumerWidget { ); if (date == null) { - filter.value = filter.value.copyWith( - date: SearchDateFilter(), - ); + filter.value = filter.value.copyWith(date: SearchDateFilter()); dateRangeCurrentFilterWidget.value = null; search(); @@ -330,13 +276,7 @@ class DriftSearchPage extends HookConsumerWidget { filter.value = filter.value.copyWith( date: SearchDateFilter( takenAfter: date.start, - takenBefore: date.end.add( - const Duration( - hours: 23, - minutes: 59, - seconds: 59, - ), - ), + takenBefore: date.end.add(const Duration(hours: 23, minutes: 59, seconds: 59)), ), ); @@ -365,24 +305,20 @@ class DriftSearchPage extends HookConsumerWidget { // MEDIA PICKER showMediaTypePicker() { handleOnSelected(AssetType assetType) { - filter.value = filter.value.copyWith( - mediaType: assetType, - ); + filter.value = filter.value.copyWith(mediaType: assetType); mediaTypeCurrentFilterWidget.value = Text( assetType == AssetType.image ? 'image'.t(context: context) : assetType == AssetType.video - ? 'video'.t(context: context) - : 'all'.t(context: context), + ? 'video'.t(context: context) + : 'all'.t(context: context), style: context.textTheme.labelLarge, ); } handleClear() { - filter.value = filter.value.copyWith( - mediaType: AssetType.other, - ); + filter.value = filter.value.copyWith(mediaType: AssetType.other); mediaTypeCurrentFilterWidget.value = null; search(); @@ -394,10 +330,7 @@ class DriftSearchPage extends HookConsumerWidget { title: 'search_filter_media_type_title'.t(context: context), onSearch: search, onClear: handleClear, - child: MediaTypePicker( - onSelect: handleOnSelected, - filter: filter.value.mediaType, - ), + child: MediaTypePicker(onSelect: handleOnSelected, filter: filter.value.mediaType), ), ); } @@ -409,33 +342,19 @@ class DriftSearchPage extends HookConsumerWidget { value.forEach((key, value) { switch (key) { case DisplayOption.notInAlbum: - filter.value = filter.value.copyWith( - display: filter.value.display.copyWith( - isNotInAlbum: value, - ), - ); + filter.value = filter.value.copyWith(display: filter.value.display.copyWith(isNotInAlbum: value)); if (value) { - filterText.add( - 'search_filter_display_option_not_in_album'.t(context: context), - ); + filterText.add('search_filter_display_option_not_in_album'.t(context: context)); } break; case DisplayOption.archive: - filter.value = filter.value.copyWith( - display: filter.value.display.copyWith( - isArchive: value, - ), - ); + filter.value = filter.value.copyWith(display: filter.value.display.copyWith(isArchive: value)); if (value) { filterText.add('archive'.t(context: context)); } break; case DisplayOption.favorite: - filter.value = filter.value.copyWith( - display: filter.value.display.copyWith( - isFavorite: value, - ), - ); + filter.value = filter.value.copyWith(display: filter.value.display.copyWith(isFavorite: value)); if (value) { filterText.add('favorite'.t(context: context)); } @@ -448,19 +367,12 @@ class DriftSearchPage extends HookConsumerWidget { return; } - displayOptionCurrentFilterWidget.value = Text( - filterText.join(', '), - style: context.textTheme.labelLarge, - ); + displayOptionCurrentFilterWidget.value = Text(filterText.join(', '), style: context.textTheme.labelLarge); } handleClear() { filter.value = filter.value.copyWith( - display: SearchDisplayFilters( - isNotInAlbum: false, - isArchive: false, - isFavorite: false, - ), + display: SearchDisplayFilters(isNotInAlbum: false, isArchive: false, isFavorite: false), ); displayOptionCurrentFilterWidget.value = null; @@ -473,10 +385,7 @@ class DriftSearchPage extends HookConsumerWidget { title: 'display_options'.t(context: context), onSearch: search, onClear: handleClear, - child: DisplayOptionPicker( - onSelect: handleOnSelect, - filter: filter.value.display, - ), + child: DisplayOptionPicker(onSelect: handleOnSelect, filter: filter.value.display), ), ); } @@ -484,27 +393,15 @@ class DriftSearchPage extends HookConsumerWidget { handleTextSubmitted(String value) { switch (textSearchType.value) { case TextSearchType.context: - filter.value = filter.value.copyWith( - filename: '', - context: value, - description: '', - ); + filter.value = filter.value.copyWith(filename: '', context: value, description: ''); break; case TextSearchType.filename: - filter.value = filter.value.copyWith( - filename: value, - context: '', - description: '', - ); + filter.value = filter.value.copyWith(filename: value, context: '', description: ''); break; case TextSearchType.description: - filter.value = filter.value.copyWith( - filename: '', - context: '', - description: value, - ); + filter.value = filter.value.copyWith(filename: '', context: '', description: value); break; } @@ -512,10 +409,10 @@ class DriftSearchPage extends HookConsumerWidget { } IconData getSearchPrefixIcon() => switch (textSearchType.value) { - TextSearchType.context => Icons.image_search_rounded, - TextSearchType.filename => Icons.abc_rounded, - TextSearchType.description => Icons.text_snippet_outlined, - }; + TextSearchType.context => Icons.image_search_rounded, + TextSearchType.filename => Icons.abc_rounded, + TextSearchType.description => Icons.text_snippet_outlined, + }; return Scaffold( resizeToAvoidBottomInset: false, @@ -528,21 +425,11 @@ class DriftSearchPage extends HookConsumerWidget { style: MenuStyle( elevation: const WidgetStatePropertyAll(1), shape: WidgetStateProperty.all( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(24), - ), - ), - ), - padding: const WidgetStatePropertyAll( - EdgeInsets.all(4), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), ), + padding: const WidgetStatePropertyAll(EdgeInsets.all(4)), ), - builder: ( - BuildContext context, - MenuController controller, - Widget? child, - ) { + builder: (BuildContext context, MenuController controller, Widget? child) { return IconButton( onPressed: () { if (controller.isOpen) { @@ -616,13 +503,8 @@ class DriftSearchPage extends HookConsumerWidget { ], title: Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(0), - width: 0, - ), - borderRadius: const BorderRadius.all( - Radius.circular(24), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(0), width: 0), + borderRadius: const BorderRadius.all(Radius.circular(24)), gradient: LinearGradient( colors: [ context.colorScheme.primary.withValues(alpha: 0.075), @@ -638,12 +520,7 @@ class DriftSearchPage extends HookConsumerWidget { key: const Key('search_text_field'), controller: textSearchController, contentPadding: preFilter != null ? const EdgeInsets.only(left: 24) : const EdgeInsets.all(8), - prefixIcon: preFilter != null - ? null - : Icon( - getSearchPrefixIcon(), - color: context.colorScheme.primary, - ), + prefixIcon: preFilter != null ? null : Icon(getSearchPrefixIcon(), color: context.colorScheme.primary), onSubmitted: handleTextSubmitted, focusNode: ref.watch(searchInputFocusProvider), ), @@ -705,10 +582,7 @@ class DriftSearchPage extends HookConsumerWidget { ), ), if (isSearching.value) - const SliverFillRemaining( - hasScrollBody: false, - child: Center(child: CircularProgressIndicator()), - ) + const SliverFillRemaining(hasScrollBody: false, child: Center(child: CircularProgressIndicator())) else _SearchResultGrid(onScrollEnd: loadMoreSearchResult), ], @@ -747,19 +621,13 @@ class _SearchResultGrid extends ConsumerWidget { child: SliverFillRemaining( child: ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final timelineService = ref.watch(timelineFactoryProvider).fromAssets(searchResult.assets); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + timelineServiceProvider.overrideWith((ref) { + final timelineService = ref.watch(timelineFactoryProvider).fromAssets(searchResult.assets); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], - child: Timeline( - key: ValueKey(searchResult.totalAssets), - appBar: null, - groupBy: GroupAssetsBy.none, - ), + child: Timeline(key: ValueKey(searchResult.totalAssets), appBar: null, groupBy: GroupAssetsBy.none), ), ), ); @@ -784,16 +652,10 @@ class _SearchEmptyContent extends StatelessWidget { ), const SizedBox(height: 16), Center( - child: Text( - 'search_page_search_photos_videos'.t(context: context), - style: context.textTheme.labelLarge, - ), + child: Text('search_page_search_photos_videos'.t(context: context), style: context.textTheme.labelLarge), ), const SizedBox(height: 32), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 16), - child: _QuickLinkList(), - ), + const Padding(padding: EdgeInsets.symmetric(horizontal: 16), child: _QuickLinkList()), ], ), ); @@ -807,13 +669,8 @@ class _QuickLinkList extends StatelessWidget { Widget build(BuildContext context) { return Container( decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), - border: Border.all( - color: context.colorScheme.outline.withAlpha(10), - width: 1, - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), + border: Border.all(color: context.colorScheme.outline.withAlpha(10), width: 1), gradient: LinearGradient( colors: [ context.colorScheme.primary.withAlpha(10), @@ -876,19 +733,9 @@ class _QuickLink extends StatelessWidget { ); return ListTile( - shape: RoundedRectangleBorder( - borderRadius: borderRadius, - ), - leading: Icon( - icon, - size: 26, - ), - title: Text( - title, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + shape: RoundedRectangleBorder(borderRadius: borderRadius), + leading: Icon(icon, size: 26), + title: Text(title, style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500)), onTap: onTap, ); } diff --git a/mobile/lib/presentation/pages/search/paginated_search.provider.dart b/mobile/lib/presentation/pages/search/paginated_search.provider.dart index c93d002b95..718a241ba4 100644 --- a/mobile/lib/presentation/pages/search/paginated_search.provider.dart +++ b/mobile/lib/presentation/pages/search/paginated_search.provider.dart @@ -24,10 +24,7 @@ class PaginatedSearchNotifier extends StateNotifier { return false; } - state = SearchResult( - assets: [...state.assets, ...result.assets], - nextPage: result.nextPage, - ); + state = SearchResult(assets: [...state.assets, ...result.assets], nextPage: result.nextPage); return true; } diff --git a/mobile/lib/presentation/widgets/action_buttons/archive_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/archive_action_button.widget.dart index e461a9028e..d30ba07d0c 100644 --- a/mobile/lib/presentation/widgets/action_buttons/archive_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/archive_action_button.widget.dart @@ -27,10 +27,7 @@ class ArchiveActionButton extends ConsumerWidget { EventStream.shared.emit(const ViewerReloadAssetEvent()); } - final successMessage = 'archive_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'archive_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( diff --git a/mobile/lib/presentation/widgets/action_buttons/base_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/base_action_button.widget.dart index 9704c4b13b..5ec6c8bc54 100644 --- a/mobile/lib/presentation/widgets/action_buttons/base_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/base_action_button.widget.dart @@ -39,14 +39,10 @@ class BaseActionButton extends StatelessWidget { } return ConstrainedBox( - constraints: BoxConstraints( - maxWidth: maxWidth, - ), + constraints: BoxConstraints(maxWidth: maxWidth), child: MaterialButton( padding: const EdgeInsets.all(10), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(20)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20))), textColor: textColor, onPressed: onPressed, onLongPress: onLongPressed, @@ -59,10 +55,7 @@ class BaseActionButton extends StatelessWidget { const SizedBox(height: 8), Text( label, - style: const TextStyle( - fontSize: 14.0, - fontWeight: FontWeight.w400, - ), + style: const TextStyle(fontSize: 14.0, fontWeight: FontWeight.w400), maxLines: 3, textAlign: TextAlign.center, softWrap: true, diff --git a/mobile/lib/presentation/widgets/action_buttons/cast_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/cast_action_button.widget.dart index c80dbaaf2d..26b8ba6f47 100644 --- a/mobile/lib/presentation/widgets/action_buttons/cast_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/cast_action_button.widget.dart @@ -20,10 +20,7 @@ class CastActionButton extends ConsumerWidget { iconColor: isCasting ? context.primaryColor : null, // null = default color label: "cast".t(context: context), onPressed: () { - showDialog( - context: context, - builder: (context) => const CastDialog(), - ); + showDialog(context: context, builder: (context) => const CastDialog()); }, menuItem: menuItem, ); diff --git a/mobile/lib/presentation/widgets/action_buttons/delete_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/delete_action_button.widget.dart index f910a2a9e2..723700af55 100644 --- a/mobile/lib/presentation/widgets/action_buttons/delete_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/delete_action_button.widget.dart @@ -40,9 +40,7 @@ class DeleteActionButton extends ConsumerWidget { onPressed: () => Navigator.of(context).pop(true), child: Text( 'confirm'.t(context: context), - style: TextStyle( - color: context.colorScheme.error, - ), + style: TextStyle(color: context.colorScheme.error), ), ), ], @@ -58,10 +56,7 @@ class DeleteActionButton extends ConsumerWidget { EventStream.shared.emit(const ViewerReloadAssetEvent()); } - final successMessage = 'delete_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'delete_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( diff --git a/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart index 7a9465dfb6..cccdee9b3a 100644 --- a/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart @@ -33,10 +33,7 @@ class DeleteLocalActionButton extends ConsumerWidget { return; } - final successMessage = 'delete_local_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'delete_local_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( diff --git a/mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart index dafbdbc78e..cb0e7091c8 100644 --- a/mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart @@ -43,17 +43,10 @@ class DeleteTrashActionButton extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return TextButton.icon( - icon: Icon( - Icons.delete_forever, - color: Colors.red[400], - ), + icon: Icon(Icons.delete_forever, color: Colors.red[400]), label: Text( "delete".t(context: context), - style: TextStyle( - fontSize: 14, - color: Colors.red[400], - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 14, color: Colors.red[400], fontWeight: FontWeight.bold), ), onPressed: () => _onTap(context, ref), ); diff --git a/mobile/lib/presentation/widgets/action_buttons/edit_location_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/edit_location_action_button.widget.dart index fc642483be..1a8a1a5c39 100644 --- a/mobile/lib/presentation/widgets/action_buttons/edit_location_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/edit_location_action_button.widget.dart @@ -25,10 +25,7 @@ class EditLocationActionButton extends ConsumerWidget { ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'edit_location_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'edit_location_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( diff --git a/mobile/lib/presentation/widgets/action_buttons/favorite_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/favorite_action_button.widget.dart index c330a7bbb1..0aca5158ef 100644 --- a/mobile/lib/presentation/widgets/action_buttons/favorite_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/favorite_action_button.widget.dart @@ -12,11 +12,7 @@ class FavoriteActionButton extends ConsumerWidget { final ActionSource source; final bool menuItem; - const FavoriteActionButton({ - super.key, - required this.source, - this.menuItem = false, - }); + const FavoriteActionButton({super.key, required this.source, this.menuItem = false}); void _onTap(BuildContext context, WidgetRef ref) async { if (!context.mounted) { @@ -31,10 +27,7 @@ class FavoriteActionButton extends ConsumerWidget { ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'favorite_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'favorite_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( diff --git a/mobile/lib/presentation/widgets/action_buttons/remove_from_album_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/remove_from_album_action_button.widget.dart index 8857a1b2d9..a1f6f7e7d1 100644 --- a/mobile/lib/presentation/widgets/action_buttons/remove_from_album_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/remove_from_album_action_button.widget.dart @@ -12,11 +12,7 @@ class RemoveFromAlbumActionButton extends ConsumerWidget { final String albumId; final ActionSource source; - const RemoveFromAlbumActionButton({ - super.key, - required this.albumId, - required this.source, - }); + const RemoveFromAlbumActionButton({super.key, required this.albumId, required this.source}); void _onTap(BuildContext context, WidgetRef ref) async { if (!context.mounted) { diff --git a/mobile/lib/presentation/widgets/action_buttons/restore_trash_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/restore_trash_action_button.widget.dart index 7cdc28e1e8..e7928bd325 100644 --- a/mobile/lib/presentation/widgets/action_buttons/restore_trash_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/restore_trash_action_button.widget.dart @@ -20,10 +20,7 @@ class RestoreTrashActionButton extends ConsumerWidget { final result = await ref.read(actionProvider.notifier).restoreTrash(source); ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'assets_restored_count'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'assets_restored_count'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( @@ -38,16 +35,8 @@ class RestoreTrashActionButton extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return TextButton.icon( - icon: const Icon( - Icons.history_rounded, - ), - label: Text( - 'restore'.t(), - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ), + icon: const Icon(Icons.history_rounded), + label: Text('restore'.t(), style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), onPressed: () => _onTap(context, ref), ); } diff --git a/mobile/lib/presentation/widgets/action_buttons/stack_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/stack_action_button.widget.dart index d448c5ce86..22fccf5473 100644 --- a/mobile/lib/presentation/widgets/action_buttons/stack_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/stack_action_button.widget.dart @@ -27,10 +27,7 @@ class StackActionButton extends ConsumerWidget { final result = await ref.read(actionProvider.notifier).stack(user.id, source); ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'stack_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'stack_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( diff --git a/mobile/lib/presentation/widgets/action_buttons/trash_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/trash_action_button.widget.dart index d26bdfad04..df8f544601 100644 --- a/mobile/lib/presentation/widgets/action_buttons/trash_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/trash_action_button.widget.dart @@ -30,10 +30,7 @@ class TrashActionButton extends ConsumerWidget { EventStream.shared.emit(const ViewerReloadAssetEvent()); } - final successMessage = 'trash_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'trash_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( diff --git a/mobile/lib/presentation/widgets/action_buttons/unarchive_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/unarchive_action_button.widget.dart index d01a5cc47b..b457a1b4ca 100644 --- a/mobile/lib/presentation/widgets/action_buttons/unarchive_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/unarchive_action_button.widget.dart @@ -21,10 +21,7 @@ class UnArchiveActionButton extends ConsumerWidget { final result = await ref.read(actionProvider.notifier).unArchive(source); ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'unarchive_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'unarchive_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( diff --git a/mobile/lib/presentation/widgets/action_buttons/unfavorite_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/unfavorite_action_button.widget.dart index a45bdfb06a..7fdc5e81e8 100644 --- a/mobile/lib/presentation/widgets/action_buttons/unfavorite_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/unfavorite_action_button.widget.dart @@ -12,11 +12,7 @@ class UnFavoriteActionButton extends ConsumerWidget { final ActionSource source; final bool menuItem; - const UnFavoriteActionButton({ - super.key, - required this.source, - this.menuItem = false, - }); + const UnFavoriteActionButton({super.key, required this.source, this.menuItem = false}); void _onTap(BuildContext context, WidgetRef ref) async { if (!context.mounted) { @@ -31,10 +27,7 @@ class UnFavoriteActionButton extends ConsumerWidget { ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'unfavorite_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'unfavorite_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( diff --git a/mobile/lib/presentation/widgets/action_buttons/unstack_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/unstack_action_button.widget.dart index bf96e6ea41..ecc8a39c74 100644 --- a/mobile/lib/presentation/widgets/action_buttons/unstack_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/unstack_action_button.widget.dart @@ -21,10 +21,7 @@ class UnStackActionButton extends ConsumerWidget { final result = await ref.read(actionProvider.notifier).unStack(source); ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'unstack_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'unstack_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( diff --git a/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart index 9e2fc9b309..f037d365d8 100644 --- a/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart @@ -20,10 +20,7 @@ class UploadActionButton extends ConsumerWidget { final result = await ref.read(actionProvider.notifier).upload(source); - final successMessage = 'upload_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'upload_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( diff --git a/mobile/lib/presentation/widgets/album/album_selector.widget.dart b/mobile/lib/presentation/widgets/album/album_selector.widget.dart index 5d9378ecaf..cb6a38041d 100644 --- a/mobile/lib/presentation/widgets/album/album_selector.widget.dart +++ b/mobile/lib/presentation/widgets/album/album_selector.widget.dart @@ -27,10 +27,7 @@ typedef AlbumSelectorCallback = void Function(RemoteAlbum album); class AlbumSelector extends ConsumerStatefulWidget { final AlbumSelectorCallback onAlbumSelected; - const AlbumSelector({ - super.key, - required this.onAlbumSelected, - }); + const AlbumSelector({super.key, required this.onAlbumSelected}); @override ConsumerState createState() => _AlbumSelectorState(); @@ -113,21 +110,10 @@ class _AlbumSelectorState extends ConsumerState { onSearch: onSearch, searchController: searchController, ), - _QuickSortAndViewMode( - isGrid: isGrid, - onToggleViewMode: toggleViewMode, - ), + _QuickSortAndViewMode(isGrid: isGrid, onToggleViewMode: toggleViewMode), isGrid - ? _AlbumGrid( - albums: albums, - userId: userId, - onAlbumSelected: widget.onAlbumSelected, - ) - : _AlbumList( - albums: albums, - userId: userId, - onAlbumSelected: widget.onAlbumSelected, - ), + ? _AlbumGrid(albums: albums, userId: userId, onAlbumSelected: widget.onAlbumSelected) + : _AlbumList(albums: albums, userId: userId, onAlbumSelected: widget.onAlbumSelected), ], ); } @@ -151,18 +137,12 @@ class _SortButtonState extends ConsumerState<_SortButton> { setState(() { albumSortIsReverse = !albumSortIsReverse; }); - ref.read(remoteAlbumProvider.notifier).sortFilteredAlbums( - sortMode, - isReverse: albumSortIsReverse, - ); + ref.read(remoteAlbumProvider.notifier).sortFilteredAlbums(sortMode, isReverse: albumSortIsReverse); } else { setState(() { albumSortOption = sortMode; }); - ref.read(remoteAlbumProvider.notifier).sortFilteredAlbums( - sortMode, - isReverse: albumSortIsReverse, - ); + ref.read(remoteAlbumProvider.notifier).sortFilteredAlbums(sortMode, isReverse: albumSortIsReverse); } } @@ -172,15 +152,9 @@ class _SortButtonState extends ConsumerState<_SortButton> { style: MenuStyle( elevation: const WidgetStatePropertyAll(1), shape: WidgetStateProperty.all( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(24), - ), - ), - ), - padding: const WidgetStatePropertyAll( - EdgeInsets.all(4), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), ), + padding: const WidgetStatePropertyAll(EdgeInsets.all(4)), ), consumeOutsideTap: true, menuChildren: RemoteAlbumSortMode.values @@ -188,33 +162,27 @@ class _SortButtonState extends ConsumerState<_SortButton> { (sortMode) => MenuItemButton( leadingIcon: albumSortOption == sortMode ? albumSortIsReverse - ? Icon( - Icons.keyboard_arrow_down, - color: albumSortOption == sortMode - ? context.colorScheme.onPrimary - : context.colorScheme.onSurface, - ) - : Icon( - Icons.keyboard_arrow_up_rounded, - color: albumSortOption == sortMode - ? context.colorScheme.onPrimary - : context.colorScheme.onSurface, - ) + ? Icon( + Icons.keyboard_arrow_down, + color: albumSortOption == sortMode + ? context.colorScheme.onPrimary + : context.colorScheme.onSurface, + ) + : Icon( + Icons.keyboard_arrow_up_rounded, + color: albumSortOption == sortMode + ? context.colorScheme.onPrimary + : context.colorScheme.onSurface, + ) : const Icon(Icons.abc, color: Colors.transparent), onPressed: () => onMenuTapped(sortMode), style: ButtonStyle( - padding: WidgetStateProperty.all( - const EdgeInsets.fromLTRB(16, 16, 32, 16), - ), + padding: WidgetStateProperty.all(const EdgeInsets.fromLTRB(16, 16, 32, 16)), backgroundColor: WidgetStateProperty.all( albumSortOption == sortMode ? context.colorScheme.primary : Colors.transparent, ), shape: WidgetStateProperty.all( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(24), - ), - ), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), ), ), child: Text( @@ -243,12 +211,8 @@ class _SortButtonState extends ConsumerState<_SortButton> { Padding( padding: const EdgeInsets.only(right: 5), child: albumSortIsReverse - ? const Icon( - Icons.keyboard_arrow_down, - ) - : const Icon( - Icons.keyboard_arrow_up_rounded, - ), + ? const Icon(Icons.keyboard_arrow_down) + : const Icon(Icons.keyboard_arrow_up_rounded), ), Text( albumSortOption.key.t(context: context), @@ -287,13 +251,8 @@ class _SearchBar extends StatelessWidget { sliver: SliverToBoxAdapter( child: Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(0), - width: 0, - ), - borderRadius: const BorderRadius.all( - Radius.circular(24), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(0), width: 0), + borderRadius: const BorderRadius.all(Radius.circular(24)), gradient: LinearGradient( colors: [ context.colorScheme.primary.withValues(alpha: 0.075), @@ -311,10 +270,7 @@ class _SearchBar extends StatelessWidget { hintText: 'search_albums'.tr(), prefixIcon: const Icon(Icons.search_rounded), suffixIcon: searchController.text.isNotEmpty - ? IconButton( - icon: const Icon(Icons.clear_rounded), - onPressed: onClearSearch, - ) + ? IconButton(icon: const Icon(Icons.clear_rounded), onPressed: onClearSearch) : null, controller: searchController, onChanged: (_) => onSearch(searchController.text, filterMode), @@ -362,10 +318,7 @@ class _QuickFilterButtonRow extends StatelessWidget { isSelected: filterMode == QuickFilterMode.sharedWithMe, onTap: () { onChangeFilter(QuickFilterMode.sharedWithMe); - onSearch( - searchController.text, - QuickFilterMode.sharedWithMe, - ); + onSearch(searchController.text, QuickFilterMode.sharedWithMe); }, ), _QuickFilterButton( @@ -373,10 +326,7 @@ class _QuickFilterButtonRow extends StatelessWidget { isSelected: filterMode == QuickFilterMode.myAlbums, onTap: () { onChangeFilter(QuickFilterMode.myAlbums); - onSearch( - searchController.text, - QuickFilterMode.myAlbums, - ); + onSearch(searchController.text, QuickFilterMode.myAlbums); }, ), ], @@ -387,11 +337,7 @@ class _QuickFilterButtonRow extends StatelessWidget { } class _QuickFilterButton extends StatelessWidget { - const _QuickFilterButton({ - required this.isSelected, - required this.onTap, - required this.label, - }); + const _QuickFilterButton({required this.isSelected, required this.onTap, required this.label}); final bool isSelected; final VoidCallback onTap; @@ -402,18 +348,11 @@ class _QuickFilterButton extends StatelessWidget { return TextButton( onPressed: onTap, style: ButtonStyle( - backgroundColor: WidgetStateProperty.all( - isSelected ? context.colorScheme.primary : Colors.transparent, - ), + backgroundColor: WidgetStateProperty.all(isSelected ? context.colorScheme.primary : Colors.transparent), shape: WidgetStateProperty.all( RoundedRectangleBorder( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), - side: BorderSide( - color: context.colorScheme.onSurface.withAlpha(25), - width: 1, - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), + side: BorderSide(color: context.colorScheme.onSurface.withAlpha(25), width: 1), ), ), ), @@ -429,10 +368,7 @@ class _QuickFilterButton extends StatelessWidget { } class _QuickSortAndViewMode extends StatelessWidget { - const _QuickSortAndViewMode({ - required this.isGrid, - required this.onToggleViewMode, - }); + const _QuickSortAndViewMode({required this.isGrid, required this.onToggleViewMode}); final bool isGrid; final VoidCallback onToggleViewMode; @@ -447,10 +383,7 @@ class _QuickSortAndViewMode extends StatelessWidget { children: [ const _SortButton(), IconButton( - icon: Icon( - isGrid ? Icons.view_list_outlined : Icons.grid_view_outlined, - size: 24, - ), + icon: Icon(isGrid ? Icons.view_list_outlined : Icons.grid_view_outlined, size: 24), onPressed: onToggleViewMode, ), ], @@ -461,11 +394,7 @@ class _QuickSortAndViewMode extends StatelessWidget { } class _AlbumList extends ConsumerWidget { - const _AlbumList({ - required this.albums, - required this.userId, - required this.onAlbumSelected, - }); + const _AlbumList({required this.albums, required this.userId, required this.onAlbumSelected}); final List albums; final String? userId; @@ -476,10 +405,7 @@ class _AlbumList extends ConsumerWidget { if (albums.isEmpty) { return const SliverToBoxAdapter( child: Center( - child: Padding( - padding: EdgeInsets.all(20.0), - child: Text('No albums found'), - ), + child: Padding(padding: EdgeInsets.all(20.0), child: Text('No albums found')), ), ); } @@ -491,51 +417,25 @@ class _AlbumList extends ConsumerWidget { final album = albums[index]; return Padding( - padding: const EdgeInsets.only( - bottom: 8.0, - ), + padding: const EdgeInsets.only(bottom: 8.0), child: LargeLeadingTile( title: Text( album.name, maxLines: 2, overflow: TextOverflow.ellipsis, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), ), subtitle: Text( - '${'items_count'.t( - context: context, - args: { - 'count': album.assetCount, - }, - )} • ${album.ownerId != userId ? 'shared_by_user'.t( - context: context, - args: { - 'user': album.ownerName, - }, - ) : 'owned'.t(context: context)}', + '${'items_count'.t(context: context, args: {'count': album.assetCount})} • ${album.ownerId != userId ? 'shared_by_user'.t(context: context, args: {'user': album.ownerName}) : 'owned'.t(context: context)}', overflow: TextOverflow.ellipsis, - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), onTap: () => onAlbumSelected(album), - leadingPadding: const EdgeInsets.only( - right: 16, - ), + leadingPadding: const EdgeInsets.only(right: 16), leading: album.thumbnailAssetId != null ? ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(15), - ), - child: SizedBox( - width: 80, - height: 80, - child: Thumbnail( - remoteId: album.thumbnailAssetId, - ), - ), + borderRadius: const BorderRadius.all(Radius.circular(15)), + child: SizedBox(width: 80, height: 80, child: Thumbnail(remoteId: album.thumbnailAssetId)), ) : SizedBox( width: 80, @@ -544,16 +444,9 @@ class _AlbumList extends ConsumerWidget { decoration: BoxDecoration( color: context.colorScheme.surfaceContainer, borderRadius: const BorderRadius.all(Radius.circular(16)), - border: Border.all( - color: context.colorScheme.outline.withAlpha(50), - width: 1, - ), - ), - child: const Icon( - Icons.photo_album_rounded, - size: 24, - color: Colors.grey, + border: Border.all(color: context.colorScheme.outline.withAlpha(50), width: 1), ), + child: const Icon(Icons.photo_album_rounded, size: 24, color: Colors.grey), ), ), ), @@ -566,11 +459,7 @@ class _AlbumList extends ConsumerWidget { } class _AlbumGrid extends StatelessWidget { - const _AlbumGrid({ - required this.albums, - required this.userId, - required this.onAlbumSelected, - }); + const _AlbumGrid({required this.albums, required this.userId, required this.onAlbumSelected}); final List albums; final String? userId; @@ -581,10 +470,7 @@ class _AlbumGrid extends StatelessWidget { if (albums.isEmpty) { return const SliverToBoxAdapter( child: Center( - child: Padding( - padding: EdgeInsets.all(20.0), - child: Text('No albums found'), - ), + child: Padding(padding: EdgeInsets.all(20.0), child: Text('No albums found')), ), ); } @@ -598,28 +484,17 @@ class _AlbumGrid extends StatelessWidget { crossAxisSpacing: 4, childAspectRatio: .7, ), - delegate: SliverChildBuilderDelegate( - (context, index) { - final album = albums[index]; - return _GridAlbumCard( - album: album, - userId: userId, - onAlbumSelected: onAlbumSelected, - ); - }, - childCount: albums.length, - ), + delegate: SliverChildBuilderDelegate((context, index) { + final album = albums[index]; + return _GridAlbumCard(album: album, userId: userId, onAlbumSelected: onAlbumSelected); + }, childCount: albums.length), ), ); } } class _GridAlbumCard extends ConsumerWidget { - const _GridAlbumCard({ - required this.album, - required this.userId, - required this.onAlbumSelected, - }); + const _GridAlbumCard({required this.album, required this.userId, required this.onAlbumSelected}); final RemoteAlbum album; final String? userId; @@ -633,13 +508,8 @@ class _GridAlbumCard extends ConsumerWidget { elevation: 0, color: context.colorScheme.surfaceBright, shape: RoundedRectangleBorder( - borderRadius: const BorderRadius.all( - Radius.circular(16), - ), - side: BorderSide( - color: context.colorScheme.onSurface.withAlpha(25), - width: 1, - ), + borderRadius: const BorderRadius.all(Radius.circular(16)), + side: BorderSide(color: context.colorScheme.onSurface.withAlpha(25), width: 1), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -647,22 +517,14 @@ class _GridAlbumCard extends ConsumerWidget { Expanded( flex: 2, child: ClipRRect( - borderRadius: const BorderRadius.vertical( - top: Radius.circular(15), - ), + borderRadius: const BorderRadius.vertical(top: Radius.circular(15)), child: SizedBox( width: double.infinity, child: album.thumbnailAssetId != null - ? Thumbnail( - remoteId: album.thumbnailAssetId, - ) + ? Thumbnail(remoteId: album.thumbnailAssetId) : Container( color: context.colorScheme.surfaceContainerHighest, - child: const Icon( - Icons.photo_album_rounded, - size: 40, - color: Colors.grey, - ), + child: const Icon(Icons.photo_album_rounded, size: 40, color: Colors.grey), ), ), ), @@ -679,27 +541,13 @@ class _GridAlbumCard extends ConsumerWidget { album.name, maxLines: 2, overflow: TextOverflow.ellipsis, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), ), Text( - '${'items_count'.t( - context: context, - args: { - 'count': album.assetCount, - }, - )} • ${album.ownerId != userId ? 'shared_by_user'.t( - context: context, - args: { - 'user': album.ownerName, - }, - ) : 'owned'.t(context: context)}', + '${'items_count'.t(context: context, args: {'count': album.assetCount})} • ${album.ownerId != userId ? 'shared_by_user'.t(context: context, args: {'user': album.ownerName}) : 'owned'.t(context: context)}', maxLines: 1, overflow: TextOverflow.ellipsis, - style: context.textTheme.labelMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.labelMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), ], ), @@ -718,17 +566,15 @@ class AddToAlbumHeader extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { Future onCreateAlbum() async { - final newAlbum = await ref.read(remoteAlbumProvider.notifier).createAlbum( + final newAlbum = await ref + .read(remoteAlbumProvider.notifier) + .createAlbum( title: "Untitled Album", assetIds: ref.read(multiSelectProvider).selectedAssets.map((e) => (e as RemoteAsset).id).toList(), ); if (newAlbum == null) { - ImmichToast.show( - context: context, - toastType: ToastType.error, - msg: 'errors.failed_to_create_album'.tr(), - ); + ImmichToast.show(context: context, toastType: ToastType.error, msg: 'errors.failed_to_create_album'.tr()); return; } @@ -736,38 +582,23 @@ class AddToAlbumHeader extends ConsumerWidget { } return SliverPadding( - padding: const EdgeInsets.symmetric( - horizontal: 16, - ), + padding: const EdgeInsets.symmetric(horizontal: 16), sliver: SliverToBoxAdapter( child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - "add_to_album", - style: context.textTheme.titleSmall, - ).tr(), + Text("add_to_album", style: context.textTheme.titleSmall).tr(), TextButton.icon( style: TextButton.styleFrom( - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 4, - ), // remove internal padding + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), // remove internal padding minimumSize: const Size(0, 0), // allow shrinking tapTargetSize: MaterialTapTargetSize.shrinkWrap, // remove extra height ), onPressed: onCreateAlbum, - icon: Icon( - Icons.add, - color: context.primaryColor, - ), + icon: Icon(Icons.add, color: context.primaryColor), label: Text( "common_create_new_album", - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.bold, - fontSize: 14, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold, fontSize: 14), ).tr(), ), ], diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart index e78c8ea8ad..1eb3366e30 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart @@ -13,7 +13,5 @@ class StackChildrenNotifier extends AutoDisposeFamilyAsyncNotifier, BaseAsset?>( - StackChildrenNotifier.new, -); +final stackChildrenNotifier = AsyncNotifierProvider.autoDispose + .family, BaseAsset?>(StackChildrenNotifier.new); diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_stack.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_stack.widget.dart index 92f516157e..e5d1487d53 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_stack.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_stack.widget.dart @@ -11,9 +11,7 @@ class AssetStackRow extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - int opacity = ref.watch( - assetViewerProvider.select((state) => state.backgroundOpacity), - ); + int opacity = ref.watch(assetViewerProvider.select((state) => state.backgroundOpacity)); final showControls = ref.watch(assetViewerProvider.select((s) => s.showingControls)); if (!showControls) { @@ -27,11 +25,10 @@ class AssetStackRow extends ConsumerWidget { child: AnimatedOpacity( opacity: opacity / 255, duration: Durations.short2, - child: ref.watch(stackChildrenNotifier(asset)).when( - data: (state) => SizedBox.square( - dimension: 80, - child: _StackList(stack: state), - ), + child: ref + .watch(stackChildrenNotifier(asset)) + .when( + data: (state) => SizedBox.square(dimension: 80, child: _StackList(stack: state)), error: (_, __) => const SizedBox.shrink(), loading: () => const SizedBox.shrink(), ), @@ -49,11 +46,7 @@ class _StackList extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { return ListView.builder( scrollDirection: Axis.horizontal, - padding: const EdgeInsets.only( - left: 5, - right: 5, - bottom: 30, - ), + padding: const EdgeInsets.only(left: 5, right: 5, bottom: 30), itemCount: stack.length, itemBuilder: (ctx, index) { final asset = stack[index]; @@ -71,9 +64,7 @@ class _StackList extends ConsumerWidget { ? const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(6)), - border: Border.fromBorderSide( - BorderSide(color: Colors.white, width: 2), - ), + border: Border.fromBorderSide(BorderSide(color: Colors.white, width: 2)), ) : const BoxDecoration( color: Colors.white, @@ -87,10 +78,7 @@ class _StackList extends ConsumerWidget { children: [ Image( fit: BoxFit.cover, - image: getThumbnailImageProvider( - remoteId: asset.id, - size: const Size.square(60), - ), + image: getThumbnailImageProvider(remoteId: asset.id, size: const Size.square(60)), ), if (asset.isVideo) const Icon( @@ -98,11 +86,7 @@ class _StackList extends ConsumerWidget { color: Colors.white, size: 16, shadows: [ - Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - offset: Offset(0.0, 0.0), - ), + Shadow(blurRadius: 5.0, color: Color.fromRGBO(0, 0, 0, 0.6), offset: Offset(0.0, 0.0)), ], ), ], diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart index 8fbc28f072..c6b2360c9d 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart @@ -36,12 +36,7 @@ class AssetViewerPage extends StatelessWidget { final TimelineService timelineService; final int? heroOffset; - const AssetViewerPage({ - super.key, - required this.initialIndex, - required this.timelineService, - this.heroOffset, - }); + const AssetViewerPage({super.key, required this.initialIndex, required this.timelineService, this.heroOffset}); @override Widget build(BuildContext context) { @@ -59,12 +54,7 @@ class AssetViewer extends ConsumerStatefulWidget { final Platform? platform; final int? heroOffset; - const AssetViewer({ - super.key, - required this.initialIndex, - this.platform, - this.heroOffset, - }); + const AssetViewer({super.key, required this.initialIndex, this.platform, this.heroOffset}); @override ConsumerState createState() => _AssetViewerState(); @@ -162,11 +152,7 @@ class _AssetViewerState extends ConsumerState { context, onError: (_, __) {}, ), - precacheImage( - getFullImageProvider(asset, size: screenSize), - context, - onError: (_, __) {}, - ), + precacheImage(getFullImageProvider(asset, size: screenSize), context, onError: (_, __) {}), ]), ); } @@ -222,9 +208,7 @@ class _AssetViewerState extends ConsumerState { duration: const Duration(seconds: 2), content: Text( "local_asset_cast_failed".tr(), - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ), ), ); @@ -262,7 +246,8 @@ class _AssetViewerState extends ConsumerState { viewController = controller; dragDownPosition = details.localPosition; initialPhotoViewState = controller.value; - final isZoomed = scaleStateController.scaleState == PhotoViewScaleState.zoomedIn || + final isZoomed = + scaleStateController.scaleState == PhotoViewScaleState.zoomedIn || scaleStateController.scaleState == PhotoViewScaleState.covering; if (!showingBottomSheet && isZoomed) { blockGestures = true; @@ -350,10 +335,7 @@ class _AssetViewerState extends ConsumerState { final backgroundOpacity = (255 * (1.0 - (scaleReduction / dragRatio))).round(); - viewController?.updateMultiple( - position: initialPhotoViewState.position + delta, - scale: updatedScale, - ); + viewController?.updateMultiple(position: initialPhotoViewState.position + delta, scale: updatedScale); ref.read(assetViewerProvider.notifier).setOpacity(backgroundOpacity); } @@ -450,32 +432,21 @@ class _AssetViewerState extends ConsumerState { }); } - void _openBottomSheet( - BuildContext ctx, { - double extent = _kBottomSheetMinimumExtent, - }) { + void _openBottomSheet(BuildContext ctx, {double extent = _kBottomSheetMinimumExtent}) { ref.read(assetViewerProvider.notifier).setBottomSheet(true); initialScale = viewController?.scale; viewController?.updateMultiple(scale: _getScaleForBottomSheet); previousExtent = _kBottomSheetMinimumExtent; sheetCloseController = showBottomSheet( context: ctx, - sheetAnimationStyle: const AnimationStyle( - duration: Durations.short4, - reverseDuration: Durations.short2, - ), + sheetAnimationStyle: const AnimationStyle(duration: Durations.short4, reverseDuration: Durations.short2), constraints: const BoxConstraints(maxWidth: double.infinity), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(20.0)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(20.0))), backgroundColor: ctx.colorScheme.surfaceContainerLowest, builder: (_) { return NotificationListener( onNotification: _onNotification, - child: AssetDetailBottomSheet( - controller: bottomSheetController, - initialChildSize: extent, - ), + child: AssetDetailBottomSheet(controller: bottomSheetController, initialChildSize: extent), ); }, ); @@ -496,18 +467,10 @@ class _AssetViewerState extends ConsumerState { return; } isSnapping = true; - bottomSheetController.animateTo( - _kBottomSheetSnapExtent, - duration: Durations.short3, - curve: Curves.easeOut, - ); + bottomSheetController.animateTo(_kBottomSheetSnapExtent, duration: Durations.short3, curve: Curves.easeOut); } - Widget _placeholderBuilder( - BuildContext ctx, - ImageChunkEvent? progress, - int index, - ) { + Widget _placeholderBuilder(BuildContext ctx, ImageChunkEvent? progress, int index) { BaseAsset asset = ref.read(timelineServiceProvider).getAsset(index); final stackChildren = ref.read(stackChildrenNotifier(asset)).valueOrNull; if (stackChildren != null && stackChildren.isNotEmpty) { @@ -517,14 +480,7 @@ class _AssetViewerState extends ConsumerState { width: double.infinity, height: double.infinity, color: backgroundColor, - child: Thumbnail( - asset: asset, - fit: BoxFit.contain, - size: Size( - ctx.width, - ctx.height, - ), - ), + child: Thumbnail(asset: asset, fit: BoxFit.contain, size: Size(ctx.width, ctx.height)), ); } @@ -574,11 +530,7 @@ class _AssetViewerState extends ConsumerState { width: ctx.width, height: ctx.height, color: backgroundColor, - child: Thumbnail( - asset: asset, - fit: BoxFit.contain, - size: size, - ), + child: Thumbnail(asset: asset, fit: BoxFit.contain, size: size), ), ); } @@ -662,8 +614,7 @@ class _AssetViewerState extends ConsumerState { pageController: pageController, scrollPhysics: platform.isIOS ? const FastScrollPhysics() // Use bouncing physics for iOS - : const FastClampingScrollPhysics() // Use heavy physics for Android - , + : const FastClampingScrollPhysics(), // Use heavy physics for Android itemCount: totalAssets, onPageChanged: _onPageChanged, onPageBuild: _onPageBuild, @@ -678,10 +629,7 @@ class _AssetViewerState extends ConsumerState { mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const AssetStackRow(), - if (!isInLockedView) const ViewerBottomBar(), - ], + children: [const AssetStackRow(), if (!isInLockedView) const ViewerBottomBar()], ), ), ); diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart index 32d5249bdc..88513516eb 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart @@ -79,17 +79,11 @@ class AssetViewerStateNotifier extends AutoDisposeNotifier { } void setOpacity(int opacity) { - state = state.copyWith( - backgroundOpacity: opacity, - showingControls: opacity == 255 ? true : state.showingControls, - ); + state = state.copyWith(backgroundOpacity: opacity, showingControls: opacity == 255 ? true : state.showingControls); } void setBottomSheet(bool showing) { - state = state.copyWith( - showingBottomSheet: showing, - showingControls: showing ? true : state.showingControls, - ); + state = state.copyWith(showingBottomSheet: showing, showingControls: showing ? true : state.showingControls); if (showing) { ref.read(videoPlayerControlsProvider.notifier).pause(); } diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart index 8c04fd5a85..881ed4d150 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart @@ -25,12 +25,8 @@ class ViewerBottomBar extends ConsumerWidget { final user = ref.watch(currentUserProvider); final isOwner = asset is RemoteAsset && asset.ownerId == user?.id; - final isSheetOpen = ref.watch( - assetViewerProvider.select((s) => s.showingBottomSheet), - ); - int opacity = ref.watch( - assetViewerProvider.select((state) => state.backgroundOpacity), - ); + final isSheetOpen = ref.watch(assetViewerProvider.select((s) => s.showingBottomSheet)); + int opacity = ref.watch(assetViewerProvider.select((state) => state.backgroundOpacity)); final showControls = ref.watch(assetViewerProvider.select((s) => s.showingControls)); if (!showControls) { @@ -42,13 +38,8 @@ class ViewerBottomBar extends ConsumerWidget { if (asset.isLocalOnly) const UploadActionButton(source: ActionSource.viewer), if (asset.hasRemote && isOwner) const ArchiveActionButton(source: ActionSource.viewer), asset.isLocalOnly - ? const DeleteLocalActionButton( - source: ActionSource.viewer, - ) - : const DeleteActionButton( - source: ActionSource.viewer, - showConfirmation: true, - ), + ? const DeleteLocalActionButton(source: ActionSource.viewer) + : const DeleteActionButton(source: ActionSource.viewer, showConfirmation: true), ]; return IgnorePointer( @@ -64,9 +55,7 @@ class ViewerBottomBar extends ConsumerWidget { data: context.themeData.copyWith( iconTheme: const IconThemeData(size: 22, color: Colors.white), textTheme: context.themeData.textTheme.copyWith( - labelLarge: context.themeData.textTheme.labelLarge?.copyWith( - color: Colors.white, - ), + labelLarge: context.themeData.textTheme.labelLarge?.copyWith(color: Colors.white), ), ), child: Container( @@ -77,10 +66,7 @@ class ViewerBottomBar extends ConsumerWidget { mainAxisAlignment: MainAxisAlignment.end, children: [ if (asset.isVideo) const VideoControls(), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: actions, - ), + Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: actions), ], ), ), diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart index e24bb3d7c0..73ec6b456a 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart @@ -29,11 +29,7 @@ class AssetDetailBottomSheet extends ConsumerWidget { final DraggableScrollableController? controller; final double initialChildSize; - const AssetDetailBottomSheet({ - this.controller, - this.initialChildSize = 0.35, - super.key, - }); + const AssetDetailBottomSheet({this.controller, this.initialChildSize = 0.35, super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -42,9 +38,7 @@ class AssetDetailBottomSheet extends ConsumerWidget { return const SizedBox.shrink(); } - final isTrashEnable = ref.watch( - serverInfoProvider.select((state) => state.serverFeatures.trash), - ); + final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); final isInLockedView = ref.watch(inLockedViewProvider); @@ -58,9 +52,7 @@ class AssetDetailBottomSheet extends ConsumerWidget { ? const TrashActionButton(source: ActionSource.viewer) : const DeletePermanentActionButton(source: ActionSource.viewer), const DeleteActionButton(source: ActionSource.viewer), - const MoveToLockFolderActionButton( - source: ActionSource.viewer, - ), + const MoveToLockFolderActionButton(source: ActionSource.viewer), ], if (asset.storage == AssetState.local) ...[ const DeleteLocalActionButton(source: ActionSource.viewer), @@ -153,9 +145,7 @@ class _AssetDetailBottomSheet extends ConsumerWidget { // Asset Date and Time _SheetTile( title: _getDateTime(context, asset), - titleStyle: context.textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w600, - ), + titleStyle: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600), ), const SheetLocationDetails(), // Details header @@ -185,11 +175,7 @@ class _AssetDetailBottomSheet extends ConsumerWidget { _SheetTile( title: cameraTitle, titleStyle: context.textTheme.labelLarge, - leading: Icon( - Icons.camera_outlined, - size: 24, - color: context.textTheme.labelLarge?.color, - ), + leading: Icon(Icons.camera_outlined, size: 24, color: context.textTheme.labelLarge?.color), subtitle: _getCameraInfoSubtitle(exifInfo), subtitleStyle: context.textTheme.bodyMedium?.copyWith( color: context.textTheme.bodyMedium?.color?.withAlpha(155), @@ -207,13 +193,7 @@ class _SheetTile extends StatelessWidget { final TextStyle? titleStyle; final TextStyle? subtitleStyle; - const _SheetTile({ - required this.title, - this.titleStyle, - this.leading, - this.subtitle, - this.subtitleStyle, - }); + const _SheetTile({required this.title, this.titleStyle, this.leading, this.subtitle, this.subtitleStyle}); @override Widget build(BuildContext context) { diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart index f91dafb3ed..ab57ea4d8b 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart @@ -38,20 +38,13 @@ class _SheetLocationDetailsState extends ConsumerState { _mapController = controller; } - void _onExifChanged( - AsyncValue? previous, - AsyncValue current, - ) { + void _onExifChanged(AsyncValue? previous, AsyncValue current) { asset = ref.read(currentAssetNotifier); setState(() { exifInfo = current.valueOrNull; final hasCoordinates = exifInfo?.hasCoordinates ?? false; if (exifInfo != null && hasCoordinates) { - _mapController?.moveCamera( - CameraUpdate.newLatLng( - LatLng(exifInfo!.latitude!, exifInfo!.longitude!), - ), - ); + _mapController?.moveCamera(CameraUpdate.newLatLng(LatLng(exifInfo!.latitude!, exifInfo!.longitude!))); } }); } @@ -59,11 +52,7 @@ class _SheetLocationDetailsState extends ConsumerState { @override void initState() { super.initState(); - ref.listenManual( - currentAssetExifProvider, - _onExifChanged, - fireImmediately: true, - ); + ref.listenManual(currentAssetExifProvider, _onExifChanged, fireImmediately: true); } @override @@ -80,10 +69,7 @@ class _SheetLocationDetailsState extends ConsumerState { final coordinates = "${exifInfo!.latitude!.toStringAsFixed(4)}, ${exifInfo!.longitude!.toStringAsFixed(4)}"; return Padding( - padding: EdgeInsets.symmetric( - vertical: 16.0, - horizontal: context.isMobile ? 16.0 : 56.0, - ), + padding: EdgeInsets.symmetric(vertical: 16.0, horizontal: context.isMobile ? 16.0 : 56.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -97,25 +83,16 @@ class _SheetLocationDetailsState extends ConsumerState { ), ), ), - ExifMap( - exifInfo: exifInfo!, - markerId: remoteId, - onMapCreated: _onMapCreated, - ), + ExifMap(exifInfo: exifInfo!, markerId: remoteId, onMapCreated: _onMapCreated), const SizedBox(height: 15), if (locationName != null) Padding( padding: const EdgeInsets.only(bottom: 4.0), - child: Text( - locationName, - style: context.textTheme.labelLarge, - ), + child: Text(locationName, style: context.textTheme.labelLarge), ), Text( coordinates, - style: context.textTheme.labelMedium?.copyWith( - color: context.textTheme.labelMedium?.color?.withAlpha(150), - ), + style: context.textTheme.labelMedium?.copyWith(color: context.textTheme.labelMedium?.color?.withAlpha(150)), ), ], ), diff --git a/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart index f1163ad2fd..450012f7fa 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart @@ -36,25 +36,18 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget { final showViewInTimelineButton = previousRouteName != TabShellRoute.name && previousRouteName != null; final isShowingSheet = ref.watch(assetViewerProvider.select((state) => state.showingBottomSheet)); - int opacity = ref.watch( - assetViewerProvider.select((state) => state.backgroundOpacity), - ); + int opacity = ref.watch(assetViewerProvider.select((state) => state.backgroundOpacity)); final showControls = ref.watch(assetViewerProvider.select((s) => s.showingControls)); if (!showControls) { opacity = 0; } - final isCasting = ref.watch( - castProvider.select((c) => c.isCasting), - ); + final isCasting = ref.watch(castProvider.select((c) => c.isCasting)); final websocketConnected = ref.watch(websocketProvider.select((c) => c.isConnected)); final actions = [ - if (isCasting || (asset.hasRemote && websocketConnected)) - const CastActionButton( - menuItem: true, - ), + if (isCasting || (asset.hasRemote && websocketConnected)) const CastActionButton(menuItem: true), if (showViewInTimelineButton) IconButton( onPressed: () async { @@ -68,19 +61,13 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget { if (asset.hasRemote && isOwner && !asset.isFavorite) const FavoriteActionButton(source: ActionSource.viewer, menuItem: true), if (asset.hasRemote && isOwner && asset.isFavorite) - const UnFavoriteActionButton( - source: ActionSource.viewer, - menuItem: true, - ), + const UnFavoriteActionButton(source: ActionSource.viewer, menuItem: true), if (asset.isMotionPhoto) const MotionPhotoActionButton(menuItem: true), const _KebabMenu(), ]; final lockedViewActions = [ - if (isCasting || (asset.hasRemote && websocketConnected)) - const CastActionButton( - menuItem: true, - ), + if (isCasting || (asset.hasRemote && websocketConnected)) const CastActionButton(menuItem: true), const _KebabMenu(), ]; @@ -98,8 +85,8 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget { actions: isShowingSheet ? null : isInLockedView - ? lockedViewActions - : actions, + ? lockedViewActions + : actions, ), ), ); diff --git a/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart index f0d665b8ce..32510c2ca5 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart @@ -27,10 +27,7 @@ import 'package:logging/logging.dart'; import 'package:native_video_player/native_video_player.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; -bool _isCurrentAsset( - BaseAsset asset, - BaseAsset? currentAsset, -) { +bool _isCurrentAsset(BaseAsset asset, BaseAsset? currentAsset) { if (asset is RemoteAsset) { return switch (currentAsset) { RemoteAsset remoteAsset => remoteAsset.id == asset.id, @@ -98,10 +95,7 @@ class NativeVideoViewer extends HookConsumerWidget { throw Exception('No file found for the video'); } - final source = await VideoSource.init( - path: file.path, - type: VideoSourceType.file, - ); + final source = await VideoSource.init(path: file.path, type: VideoSourceType.file); return source; } @@ -122,31 +116,24 @@ class NativeVideoViewer extends HookConsumerWidget { ); return source; } catch (error) { - log.severe( - 'Error creating video source for asset ${asset.name}: $error', - ); + log.severe('Error creating video source for asset ${asset.name}: $error'); return null; } } final videoSource = useMemoized>(() => createSource()); final aspectRatio = useState(null); - useMemoized( - () async { - if (!context.mounted || aspectRatio.value != null) { - return null; - } + useMemoized(() async { + if (!context.mounted || aspectRatio.value != null) { + return null; + } - try { - aspectRatio.value = await ref.read(assetServiceProvider).getAspectRatio(asset); - } catch (error) { - log.severe( - 'Error getting aspect ratio for asset ${asset.name}: $error', - ); - } - }, - [asset.heroTag], - ); + try { + aspectRatio.value = await ref.read(assetServiceProvider).getAspectRatio(asset); + } catch (error) { + log.severe('Error getting aspect ratio for asset ${asset.name}: $error'); + } + }, [asset.heroTag]); void checkIfBuffering() { if (!context.mounted) { @@ -156,8 +143,9 @@ class NativeVideoViewer extends HookConsumerWidget { final videoPlayback = ref.read(videoPlaybackValueProvider); if ((isBuffering.value || videoPlayback.state == VideoPlaybackState.initializing) && videoPlayback.state != VideoPlaybackState.buffering) { - ref.read(videoPlaybackValueProvider.notifier).value = - videoPlayback.copyWith(state: VideoPlaybackState.buffering); + ref.read(videoPlaybackValueProvider.notifier).value = videoPlayback.copyWith( + state: VideoPlaybackState.buffering, + ); } } @@ -345,48 +333,42 @@ class NativeVideoViewer extends HookConsumerWidget { // This delay seems like a hacky way to resolve underlying bugs in video // playback, but other resolutions failed thus far Timer( - Platform.isIOS - ? Duration(milliseconds: 300 * playbackDelayFactor) - : imageToVideo - ? Duration(milliseconds: 200 * playbackDelayFactor) - : Duration(milliseconds: 400 * playbackDelayFactor), () { - if (!context.mounted) { - return; - } - - currentAsset.value = value; - if (currentAsset.value == asset) { - onPlaybackReady(); - } - }); - }); - - useEffect( - () { - // If opening a remote video from a hero animation, delay visibility to avoid a stutter - final timer = isVisible.value - ? null - : Timer( - const Duration(milliseconds: 300), - () => isVisible.value = true, - ); - - return () { - timer?.cancel(); - final playerController = controller.value; - if (playerController == null) { + Platform.isIOS + ? Duration(milliseconds: 300 * playbackDelayFactor) + : imageToVideo + ? Duration(milliseconds: 200 * playbackDelayFactor) + : Duration(milliseconds: 400 * playbackDelayFactor), + () { + if (!context.mounted) { return; } - removeListeners(playerController); - playerController.stop().catchError((error) { - log.fine('Error stopping video: $error'); - }); - WakelockPlus.disable(); - }; - }, - const [], - ); + currentAsset.value = value; + if (currentAsset.value == asset) { + onPlaybackReady(); + } + }, + ); + }); + + useEffect(() { + // If opening a remote video from a hero animation, delay visibility to avoid a stutter + final timer = isVisible.value ? null : Timer(const Duration(milliseconds: 300), () => isVisible.value = true); + + return () { + timer?.cancel(); + final playerController = controller.value; + if (playerController == null) { + return; + } + removeListeners(playerController); + playerController.stop().catchError((error) { + log.fine('Error stopping video: $error'); + }); + + WakelockPlus.disable(); + }; + }, const []); useOnAppLifecycleStateChange((_, state) async { if (state == AppLifecycleState.resumed && shouldPlayOnForeground.value) { @@ -416,12 +398,7 @@ class NativeVideoViewer extends HookConsumerWidget { child: AspectRatio( key: ValueKey(asset), aspectRatio: aspectRatio.value!, - child: isCurrent - ? NativeVideoPlayerView( - key: ValueKey(asset), - onViewReady: initController, - ) - : null, + child: isCurrent ? NativeVideoPlayerView(key: ValueKey(asset), onViewReady: initController) : null, ), ), ), diff --git a/mobile/lib/presentation/widgets/asset_viewer/video_viewer_controls.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/video_viewer_controls.widget.dart index 1fc01bb8e5..c1324b8ac0 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/video_viewer_controls.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/video_viewer_controls.widget.dart @@ -13,16 +13,11 @@ import 'package:immich_mobile/widgets/common/delayed_loading_indicator.dart'; class VideoViewerControls extends HookConsumerWidget { final Duration hideTimerDuration; - const VideoViewerControls({ - super.key, - this.hideTimerDuration = const Duration(seconds: 5), - }); + const VideoViewerControls({super.key, this.hideTimerDuration = const Duration(seconds: 5)}); @override Widget build(BuildContext context, WidgetRef ref) { - final assetIsVideo = ref.watch( - currentAssetNotifier.select((asset) => asset != null && asset.isVideo), - ); + final assetIsVideo = ref.watch(currentAssetNotifier.select((asset) => asset != null && asset.isVideo)); bool showControls = ref.watch(assetViewerProvider.select((s) => s.showingControls)); final showBottomSheet = ref.watch(assetViewerProvider.select((s) => s.showingBottomSheet)); if (showBottomSheet) { @@ -33,20 +28,17 @@ class VideoViewerControls extends HookConsumerWidget { final cast = ref.watch(castProvider); // A timer to hide the controls - final hideTimer = useTimer( - hideTimerDuration, - () { - if (!context.mounted) { - return; - } - final state = ref.read(videoPlaybackValueProvider).state; + final hideTimer = useTimer(hideTimerDuration, () { + if (!context.mounted) { + return; + } + final state = ref.read(videoPlaybackValueProvider).state; - // Do not hide on paused - if (state != VideoPlaybackState.paused && state != VideoPlaybackState.completed && assetIsVideo) { - ref.read(assetViewerProvider.notifier).setControls(false); - } - }, - ); + // Do not hide on paused + if (state != VideoPlaybackState.paused && state != VideoPlaybackState.completed && assetIsVideo) { + ref.read(assetViewerProvider.notifier).setControls(false); + } + }); final showBuffering = state == VideoPlaybackState.buffering && !cast.isCasting; /// Shows the controls and starts the timer to hide them @@ -97,11 +89,7 @@ class VideoViewerControls extends HookConsumerWidget { child: Stack( children: [ if (showBuffering) - const Center( - child: DelayedLoadingIndicator( - fadeInDuration: Duration(milliseconds: 400), - ), - ) + const Center(child: DelayedLoadingIndicator(fadeInDuration: Duration(milliseconds: 400))) else GestureDetector( onTap: () => ref.read(assetViewerProvider.notifier).setControls(false), diff --git a/mobile/lib/presentation/widgets/backup/backup_toggle_button.widget.dart b/mobile/lib/presentation/widgets/backup/backup_toggle_button.widget.dart index 520111070f..a74c169224 100644 --- a/mobile/lib/presentation/widgets/backup/backup_toggle_button.widget.dart +++ b/mobile/lib/presentation/widgets/backup/backup_toggle_button.widget.dart @@ -11,11 +11,7 @@ class BackupToggleButton extends ConsumerStatefulWidget { final VoidCallback onStart; final VoidCallback onStop; - const BackupToggleButton({ - super.key, - required this.onStart, - required this.onStop, - }); + const BackupToggleButton({super.key, required this.onStart, required this.onStop}); @override ConsumerState createState() => BackupToggleButtonState(); @@ -29,17 +25,12 @@ class BackupToggleButtonState extends ConsumerState with Sin @override void initState() { super.initState(); - _animationController = AnimationController( - duration: const Duration(seconds: 8), - vsync: this, - ); + _animationController = AnimationController(duration: const Duration(seconds: 8), vsync: this); - _gradientAnimation = Tween(begin: 0, end: 1).animate( - CurvedAnimation( - parent: _animationController, - curve: Curves.easeInOut, - ), - ); + _gradientAnimation = Tween( + begin: 0, + end: 1, + ).animate(CurvedAnimation(parent: _animationController, curve: Curves.easeInOut)); _isEnabled = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup); } @@ -66,21 +57,13 @@ class BackupToggleButtonState extends ConsumerState with Sin @override Widget build(BuildContext context) { - final enqueueCount = ref.watch( - driftBackupProvider.select((state) => state.enqueueCount), - ); + final enqueueCount = ref.watch(driftBackupProvider.select((state) => state.enqueueCount)); - final enqueueTotalCount = ref.watch( - driftBackupProvider.select((state) => state.enqueueTotalCount), - ); + final enqueueTotalCount = ref.watch(driftBackupProvider.select((state) => state.enqueueTotalCount)); - final isCanceling = ref.watch( - driftBackupProvider.select((state) => state.isCanceling), - ); + final isCanceling = ref.watch(driftBackupProvider.select((state) => state.isCanceling)); - final uploadTasks = ref.watch( - driftBackupProvider.select((state) => state.uploadItems), - ); + final uploadTasks = ref.watch(driftBackupProvider.select((state) => state.uploadItems)); final isUploading = uploadTasks.isNotEmpty; @@ -116,11 +99,7 @@ class BackupToggleButtonState extends ConsumerState with Sin end: Alignment.bottomRight, ), boxShadow: [ - BoxShadow( - color: context.primaryColor.withValues(alpha: 0.1), - blurRadius: 12, - offset: const Offset(0, 2), - ), + BoxShadow(color: context.primaryColor.withValues(alpha: 0.1), blurRadius: 12, offset: const Offset(0, 2)), ], ), child: Container( @@ -151,18 +130,8 @@ class BackupToggleButtonState extends ConsumerState with Sin ), ), child: isUploading - ? const SizedBox( - width: 24, - height: 24, - child: CircularProgressIndicator( - strokeWidth: 2, - ), - ) - : Icon( - Icons.cloud_upload_outlined, - color: context.primaryColor, - size: 24, - ), + ? const SizedBox(width: 24, height: 24, child: CircularProgressIndicator(strokeWidth: 2)) + : Icon(Icons.cloud_upload_outlined, color: context.primaryColor, size: 24), ), const SizedBox(width: 16), Expanded( @@ -185,10 +154,7 @@ class BackupToggleButtonState extends ConsumerState with Sin Text( "queue_status".t( context: context, - args: { - 'count': enqueueCount.toString(), - 'total': enqueueTotalCount.toString(), - }, + args: {'count': enqueueCount.toString(), 'total': enqueueTotalCount.toString()}, ), style: context.textTheme.labelLarge?.copyWith( color: context.colorScheme.onSurfaceSecondary, @@ -197,10 +163,7 @@ class BackupToggleButtonState extends ConsumerState with Sin if (isCanceling) Row( children: [ - Text( - "canceling".t(), - style: context.textTheme.labelLarge, - ), + Text("canceling".t(), style: context.textTheme.labelLarge), const SizedBox(width: 4), SizedBox( width: 18, @@ -215,10 +178,7 @@ class BackupToggleButtonState extends ConsumerState with Sin ], ), ), - Switch.adaptive( - value: _isEnabled, - onChanged: (value) => isCanceling ? null : _onToggle(value), - ), + Switch.adaptive(value: _isEnabled, onChanged: (value) => isCanceling ? null : _onToggle(value)), ], ), ), diff --git a/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart index 76243cf803..6485926996 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart @@ -24,9 +24,7 @@ class ArchiveBottomSheet extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final multiselect = ref.watch(multiSelectProvider); - final isTrashEnable = ref.watch( - serverInfoProvider.select((state) => state.serverFeatures.trash), - ); + final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); return BaseBottomSheet( initialChildSize: 0.25, @@ -41,14 +39,10 @@ class ArchiveBottomSheet extends ConsumerWidget { const DownloadActionButton(source: ActionSource.timeline), isTrashEnable ? const TrashActionButton(source: ActionSource.timeline) - : const DeletePermanentActionButton( - source: ActionSource.timeline, - ), + : const DeletePermanentActionButton(source: ActionSource.timeline), const EditDateTimeActionButton(), const EditLocationActionButton(source: ActionSource.timeline), - const MoveToLockFolderActionButton( - source: ActionSource.timeline, - ), + const MoveToLockFolderActionButton(source: ActionSource.timeline), const StackActionButton(source: ActionSource.timeline), ], if (multiselect.hasLocal) ...[ diff --git a/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart index b3e71567e0..a2c88d9fd7 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart @@ -73,9 +73,7 @@ class _BaseDraggableScrollableSheetState extends ConsumerState borderOnForeground: false, clipBehavior: Clip.antiAlias, elevation: 6.0, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(18)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(18))), margin: const EdgeInsets.symmetric(horizontal: 0), child: CustomScrollView( controller: scrollController, @@ -89,11 +87,7 @@ class _BaseDraggableScrollableSheetState extends ConsumerState if (widget.actions.isNotEmpty) SizedBox( height: 115, - child: ListView( - shrinkWrap: true, - scrollDirection: Axis.horizontal, - children: widget.actions, - ), + child: ListView(shrinkWrap: true, scrollDirection: Axis.horizontal, children: widget.actions), ), if (widget.actions.isNotEmpty) ...[ const Divider(indent: 16, endIndent: 16), diff --git a/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart index 3f3f933745..ec0fded6c3 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart @@ -24,9 +24,7 @@ class FavoriteBottomSheet extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final multiselect = ref.watch(multiSelectProvider); - final isTrashEnable = ref.watch( - serverInfoProvider.select((state) => state.serverFeatures.trash), - ); + final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); return BaseBottomSheet( initialChildSize: 0.25, @@ -41,14 +39,10 @@ class FavoriteBottomSheet extends ConsumerWidget { const DownloadActionButton(source: ActionSource.timeline), isTrashEnable ? const TrashActionButton(source: ActionSource.timeline) - : const DeletePermanentActionButton( - source: ActionSource.timeline, - ), + : const DeletePermanentActionButton(source: ActionSource.timeline), const EditDateTimeActionButton(), const EditLocationActionButton(source: ActionSource.timeline), - const MoveToLockFolderActionButton( - source: ActionSource.timeline, - ), + const MoveToLockFolderActionButton(source: ActionSource.timeline), const StackActionButton(source: ActionSource.timeline), ], if (multiselect.hasLocal) ...[ diff --git a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart index d338cfa833..3912aef15c 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart @@ -31,9 +31,7 @@ class GeneralBottomSheet extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final multiselect = ref.watch(multiSelectProvider); - final isTrashEnable = ref.watch( - serverInfoProvider.select((state) => state.serverFeatures.trash), - ); + final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); Future addAssetsToAlbum(RemoteAlbum album) async { final selectedAssets = multiselect.selectedAssets; @@ -41,24 +39,19 @@ class GeneralBottomSheet extends ConsumerWidget { return; } - final addedCount = await ref.read(remoteAlbumProvider.notifier).addAssets( - album.id, - selectedAssets.map((e) => (e as RemoteAsset).id).toList(), - ); + final addedCount = await ref + .read(remoteAlbumProvider.notifier) + .addAssets(album.id, selectedAssets.map((e) => (e as RemoteAsset).id).toList()); if (addedCount != selectedAssets.length) { ImmichToast.show( context: context, - msg: 'add_to_album_bottom_sheet_already_exists'.tr( - namedArgs: {"album": album.name}, - ), + msg: 'add_to_album_bottom_sheet_already_exists'.tr(namedArgs: {"album": album.name}), ); } else { ImmichToast.show( context: context, - msg: 'add_to_album_bottom_sheet_added'.tr( - namedArgs: {"album": album.name}, - ), + msg: 'add_to_album_bottom_sheet_added'.tr(namedArgs: {"album": album.name}), ); } @@ -78,18 +71,14 @@ class GeneralBottomSheet extends ConsumerWidget { const DownloadActionButton(source: ActionSource.timeline), isTrashEnable ? const TrashActionButton(source: ActionSource.timeline) - : const DeletePermanentActionButton( - source: ActionSource.timeline, - ), + : const DeletePermanentActionButton(source: ActionSource.timeline), const DeleteActionButton(source: ActionSource.timeline), if (multiselect.hasLocal || multiselect.hasMerged) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), ], const EditDateTimeActionButton(), const EditLocationActionButton(source: ActionSource.timeline), - const MoveToLockFolderActionButton( - source: ActionSource.timeline, - ), + const MoveToLockFolderActionButton(source: ActionSource.timeline), const StackActionButton(source: ActionSource.timeline), ], if (multiselect.hasLocal) ...[ @@ -99,9 +88,7 @@ class GeneralBottomSheet extends ConsumerWidget { ], slivers: [ const AddToAlbumHeader(), - AlbumSelector( - onAlbumSelected: addAssetsToAlbum, - ), + AlbumSelector(onAlbumSelected: addAssetsToAlbum), ], ); } diff --git a/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart index ff77c79906..9765b61684 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart @@ -27,9 +27,7 @@ class RemoteAlbumBottomSheet extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final multiselect = ref.watch(multiSelectProvider); - final isTrashEnable = ref.watch( - serverInfoProvider.select((state) => state.serverFeatures.trash), - ); + final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); return BaseBottomSheet( initialChildSize: 0.25, @@ -44,24 +42,17 @@ class RemoteAlbumBottomSheet extends ConsumerWidget { const DownloadActionButton(source: ActionSource.timeline), isTrashEnable ? const TrashActionButton(source: ActionSource.timeline) - : const DeletePermanentActionButton( - source: ActionSource.timeline, - ), + : const DeletePermanentActionButton(source: ActionSource.timeline), const EditDateTimeActionButton(), const EditLocationActionButton(source: ActionSource.timeline), - const MoveToLockFolderActionButton( - source: ActionSource.timeline, - ), + const MoveToLockFolderActionButton(source: ActionSource.timeline), const StackActionButton(source: ActionSource.timeline), ], if (multiselect.hasLocal) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), const UploadActionButton(source: ActionSource.timeline), ], - RemoveFromAlbumActionButton( - source: ActionSource.timeline, - albumId: album.id, - ), + RemoveFromAlbumActionButton(source: ActionSource.timeline, albumId: album.id), ], ); } diff --git a/mobile/lib/presentation/widgets/images/image_provider.dart b/mobile/lib/presentation/widgets/images/image_provider.dart index e4effd0804..d94480b434 100644 --- a/mobile/lib/presentation/widgets/images/image_provider.dart +++ b/mobile/lib/presentation/widgets/images/image_provider.dart @@ -5,20 +5,12 @@ import 'package:immich_mobile/domain/services/setting.service.dart'; import 'package:immich_mobile/presentation/widgets/images/local_image_provider.dart'; import 'package:immich_mobile/presentation/widgets/images/remote_image_provider.dart'; -ImageProvider getFullImageProvider( - BaseAsset asset, { - Size size = const Size(1080, 1920), -}) { +ImageProvider getFullImageProvider(BaseAsset asset, {Size size = const Size(1080, 1920)}) { // Create new provider and cache it final ImageProvider provider; if (_shouldUseLocalAsset(asset)) { final id = asset is LocalAsset ? asset.id : (asset as RemoteAsset).localId!; - provider = LocalFullImageProvider( - id: id, - name: asset.name, - size: size, - type: asset.type, - ); + provider = LocalFullImageProvider(id: id, name: asset.name, size: size, type: asset.type); } else { final String assetId; if (asset is LocalAsset && asset.hasRemote) { @@ -34,15 +26,8 @@ ImageProvider getFullImageProvider( return provider; } -ImageProvider getThumbnailImageProvider({ - BaseAsset? asset, - String? remoteId, - Size size = const Size.square(256), -}) { - assert( - asset != null || remoteId != null, - 'Either asset or remoteId must be provided', - ); +ImageProvider getThumbnailImageProvider({BaseAsset? asset, String? remoteId, Size size = const Size.square(256)}) { + assert(asset != null || remoteId != null, 'Either asset or remoteId must be provided'); if (remoteId != null) { return RemoteThumbProvider(assetId: remoteId); @@ -50,12 +35,7 @@ ImageProvider getThumbnailImageProvider({ if (_shouldUseLocalAsset(asset!)) { final id = asset is LocalAsset ? asset.id : (asset as RemoteAsset).localId!; - return LocalThumbProvider( - id: id, - updatedAt: asset.updatedAt, - name: asset.name, - size: size, - ); + return LocalThumbProvider(id: id, updatedAt: asset.updatedAt, name: asset.name, size: size); } final String assetId; diff --git a/mobile/lib/presentation/widgets/images/local_album_thumbnail.widget.dart b/mobile/lib/presentation/widgets/images/local_album_thumbnail.widget.dart index dcf0f28527..8b9ede4c6d 100644 --- a/mobile/lib/presentation/widgets/images/local_album_thumbnail.widget.dart +++ b/mobile/lib/presentation/widgets/images/local_album_thumbnail.widget.dart @@ -5,10 +5,7 @@ import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart' import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; class LocalAlbumThumbnail extends ConsumerWidget { - const LocalAlbumThumbnail({ - super.key, - required this.albumId, - }); + const LocalAlbumThumbnail({super.key, required this.albumId}); final String albumId; @override @@ -21,34 +18,21 @@ class LocalAlbumThumbnail extends ConsumerWidget { decoration: BoxDecoration( color: context.colorScheme.surfaceContainer, borderRadius: const BorderRadius.all(Radius.circular(16)), - border: Border.all( - color: context.colorScheme.outline.withAlpha(50), - width: 1, - ), - ), - child: Icon( - Icons.collections, - size: 24, - color: context.primaryColor, + border: Border.all(color: context.colorScheme.outline.withAlpha(50), width: 1), ), + child: Icon(Icons.collections, size: 24, color: context.primaryColor), ); } return ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(16)), - child: Thumbnail( - asset: data, - ), + child: Thumbnail(asset: data), ); }, error: (error, stack) { return const Icon(Icons.error, size: 24); }, - loading: () => const SizedBox( - width: 24, - height: 24, - child: Center(child: CircularProgressIndicator()), - ), + loading: () => const SizedBox(width: 24, height: 24, child: Center(child: CircularProgressIndicator())), ); } } diff --git a/mobile/lib/presentation/widgets/images/local_image_provider.dart b/mobile/lib/presentation/widgets/images/local_image_provider.dart index 41bc19ba57..350bcbb8fb 100644 --- a/mobile/lib/presentation/widgets/images/local_image_provider.dart +++ b/mobile/lib/presentation/widgets/images/local_image_provider.dart @@ -39,10 +39,7 @@ class LocalThumbProvider extends ImageProvider { } @override - ImageStreamCompleter loadImage( - LocalThumbProvider key, - ImageDecoderCallback decode, - ) { + ImageStreamCompleter loadImage(LocalThumbProvider key, ImageDecoderCallback decode) { final cache = cacheManager ?? ThumbnailImageCacheManager(); return MultiFrameImageStreamCompleter( codec: _codec(key, cache, decode), @@ -57,11 +54,7 @@ class LocalThumbProvider extends ImageProvider { ); } - Future _codec( - LocalThumbProvider key, - CacheManager cache, - ImageDecoderCallback decode, - ) async { + Future _codec(LocalThumbProvider key, CacheManager cache, ImageDecoderCallback decode) async { final cacheKey = '${key.id}-${key.updatedAt}-${key.size.width}x${key.size.height}'; final fileFromCache = await cache.getFileFromCache(cacheKey); @@ -75,9 +68,7 @@ class LocalThumbProvider extends ImageProvider { final thumbnailBytes = await _assetMediaRepository.getThumbnail(key.id, size: key.size); if (thumbnailBytes == null) { PaintingBinding.instance.imageCache.evict(key); - throw StateError( - "Loading thumb for local photo ${key.name} failed", - ); + throw StateError("Loading thumb for local photo ${key.name} failed"); } final buffer = await ImmutableBuffer.fromUint8List(thumbnailBytes); @@ -107,12 +98,7 @@ class LocalFullImageProvider extends ImageProvider { final Size size; final AssetType type; - const LocalFullImageProvider({ - required this.id, - required this.name, - required this.size, - required this.type, - }); + const LocalFullImageProvider({required this.id, required this.name, required this.size, required this.type}); @override Future obtainKey(ImageConfiguration configuration) { @@ -120,10 +106,7 @@ class LocalFullImageProvider extends ImageProvider { } @override - ImageStreamCompleter loadImage( - LocalFullImageProvider key, - ImageDecoderCallback decode, - ) { + ImageStreamCompleter loadImage(LocalFullImageProvider key, ImageDecoderCallback decode) { return MultiImageStreamCompleter( codec: _codec(key, decode), scale: 1.0, @@ -134,10 +117,7 @@ class LocalFullImageProvider extends ImageProvider { } // Streams in each stage of the image as we ask for it - Stream _codec( - LocalFullImageProvider key, - ImageDecoderCallback decode, - ) async* { + Stream _codec(LocalFullImageProvider key, ImageDecoderCallback decode) async* { try { switch (key.type) { case AssetType.image: @@ -156,16 +136,11 @@ class LocalFullImageProvider extends ImageProvider { } } catch (error, stack) { Logger('ImmichLocalImageProvider').severe('Error loading local image ${key.name}', error, stack); - throw const ImageLoadingException( - 'Could not load image from local storage', - ); + throw const ImageLoadingException('Could not load image from local storage'); } } - Future _getThumbnailCodec( - LocalFullImageProvider key, - ImageDecoderCallback decode, - ) async { + Future _getThumbnailCodec(LocalFullImageProvider key, ImageDecoderCallback decode) async { final thumbBytes = await _assetMediaRepository.getThumbnail(key.id, size: key.size); if (thumbBytes == null) { return null; @@ -174,10 +149,7 @@ class LocalFullImageProvider extends ImageProvider { return decode(buffer); } - Stream _decodeProgressive( - LocalFullImageProvider key, - ImageDecoderCallback decode, - ) async* { + Stream _decodeProgressive(LocalFullImageProvider key, ImageDecoderCallback decode) async* { final file = await _storageRepository.getFileForAsset(key.id); if (file == null) { throw StateError("Opening file for asset ${key.name} failed"); diff --git a/mobile/lib/presentation/widgets/images/remote_image_provider.dart b/mobile/lib/presentation/widgets/images/remote_image_provider.dart index 14d13a08d8..27f310f4f2 100644 --- a/mobile/lib/presentation/widgets/images/remote_image_provider.dart +++ b/mobile/lib/presentation/widgets/images/remote_image_provider.dart @@ -15,10 +15,7 @@ class RemoteThumbProvider extends ImageProvider { final String assetId; final CacheManager? cacheManager; - const RemoteThumbProvider({ - required this.assetId, - this.cacheManager, - }); + const RemoteThumbProvider({required this.assetId, this.cacheManager}); @override Future obtainKey(ImageConfiguration configuration) { @@ -26,10 +23,7 @@ class RemoteThumbProvider extends ImageProvider { } @override - ImageStreamCompleter loadImage( - RemoteThumbProvider key, - ImageDecoderCallback decode, - ) { + ImageStreamCompleter loadImage(RemoteThumbProvider key, ImageDecoderCallback decode) { final cache = cacheManager ?? RemoteImageCacheManager(); final chunkController = StreamController(); return MultiFrameImageStreamCompleter( @@ -49,9 +43,7 @@ class RemoteThumbProvider extends ImageProvider { ImageDecoderCallback decode, StreamController chunkController, ) async { - final preview = getThumbnailUrlForRemoteId( - key.assetId, - ); + final preview = getThumbnailUrlForRemoteId(key.assetId); return ImageLoader.loadImageFromCache( preview, @@ -79,10 +71,7 @@ class RemoteFullImageProvider extends ImageProvider { final String assetId; final CacheManager? cacheManager; - const RemoteFullImageProvider({ - required this.assetId, - this.cacheManager, - }); + const RemoteFullImageProvider({required this.assetId, this.cacheManager}); @override Future obtainKey(ImageConfiguration configuration) { @@ -90,10 +79,7 @@ class RemoteFullImageProvider extends ImageProvider { } @override - ImageStreamCompleter loadImage( - RemoteFullImageProvider key, - ImageDecoderCallback decode, - ) { + ImageStreamCompleter loadImage(RemoteFullImageProvider key, ImageDecoderCallback decode) { final cache = cacheManager ?? RemoteImageCacheManager(); final chunkEvents = StreamController(); return MultiImageStreamCompleter( diff --git a/mobile/lib/presentation/widgets/images/thumb_hash_provider.dart b/mobile/lib/presentation/widgets/images/thumb_hash_provider.dart index cd286a4cdf..8d292523d7 100644 --- a/mobile/lib/presentation/widgets/images/thumb_hash_provider.dart +++ b/mobile/lib/presentation/widgets/images/thumb_hash_provider.dart @@ -8,9 +8,7 @@ import 'package:thumbhash/thumbhash.dart'; class ThumbHashProvider extends ImageProvider { final String thumbHash; - const ThumbHashProvider({ - required this.thumbHash, - }); + const ThumbHashProvider({required this.thumbHash}); @override Future obtainKey(ImageConfiguration configuration) { @@ -18,20 +16,11 @@ class ThumbHashProvider extends ImageProvider { } @override - ImageStreamCompleter loadImage( - ThumbHashProvider key, - ImageDecoderCallback decode, - ) { - return MultiFrameImageStreamCompleter( - codec: _loadCodec(key, decode), - scale: 1.0, - ); + ImageStreamCompleter loadImage(ThumbHashProvider key, ImageDecoderCallback decode) { + return MultiFrameImageStreamCompleter(codec: _loadCodec(key, decode), scale: 1.0); } - Future _loadCodec( - ThumbHashProvider key, - ImageDecoderCallback decode, - ) async { + Future _loadCodec(ThumbHashProvider key, ImageDecoderCallback decode) async { final image = thumbHashToRGBA(base64Decode(key.thumbHash)); return decode(await ImmutableBuffer.fromUint8List(rgbaToBmp(image))); } diff --git a/mobile/lib/presentation/widgets/images/thumbnail.widget.dart b/mobile/lib/presentation/widgets/images/thumbnail.widget.dart index 80f6af617c..8335bd406b 100644 --- a/mobile/lib/presentation/widgets/images/thumbnail.widget.dart +++ b/mobile/lib/presentation/widgets/images/thumbnail.widget.dart @@ -8,16 +8,8 @@ import 'package:logging/logging.dart'; import 'package:octo_image/octo_image.dart'; class Thumbnail extends StatelessWidget { - const Thumbnail({ - this.asset, - this.remoteId, - this.size = const Size.square(256), - this.fit = BoxFit.cover, - super.key, - }) : assert( - asset != null || remoteId != null, - 'Either asset or remoteId must be provided', - ); + const Thumbnail({this.asset, this.remoteId, this.size = const Size.square(256), this.fit = BoxFit.cover, super.key}) + : assert(asset != null || remoteId != null, 'Either asset or remoteId must be provided'); final BaseAsset? asset; final String? remoteId; @@ -33,12 +25,7 @@ class Thumbnail extends StatelessWidget { image: provider, octoSet: OctoSet( placeholderBuilder: _blurHashPlaceholderBuilder(thumbHash, fit: fit), - errorBuilder: _blurHashErrorBuilder( - thumbHash, - provider: provider, - fit: fit, - asset: asset, - ), + errorBuilder: _blurHashErrorBuilder(thumbHash, provider: provider, fit: fit, asset: asset), ), fadeOutDuration: const Duration(milliseconds: 100), fadeInDuration: Duration.zero, @@ -50,10 +37,7 @@ class Thumbnail extends StatelessWidget { } } -OctoPlaceholderBuilder _blurHashPlaceholderBuilder( - String? thumbHash, { - BoxFit? fit, -}) { +OctoPlaceholderBuilder _blurHashPlaceholderBuilder(String? thumbHash, {BoxFit? fit}) { return (context) => thumbHash == null ? const ThumbnailPlaceholder() : FadeInPlaceholderImage( @@ -63,12 +47,7 @@ OctoPlaceholderBuilder _blurHashPlaceholderBuilder( ); } -OctoErrorBuilder _blurHashErrorBuilder( - String? blurhash, { - BaseAsset? asset, - ImageProvider? provider, - BoxFit? fit, -}) => +OctoErrorBuilder _blurHashErrorBuilder(String? blurhash, {BaseAsset? asset, ImageProvider? provider, BoxFit? fit}) => (context, e, s) { Logger("ImThumbnail").warning("Error loading thumbnail for ${asset?.name}", e, s); provider?.evict(); @@ -76,10 +55,7 @@ OctoErrorBuilder _blurHashErrorBuilder( alignment: Alignment.center, children: [ _blurHashPlaceholderBuilder(blurhash, fit: fit)(context), - const Opacity( - opacity: 0.75, - child: Icon(Icons.error_outline_rounded), - ), + const Opacity(opacity: 0.75, child: Icon(Icons.error_outline_rounded)), ], ); }; diff --git a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart index ce4e50cbd5..b9ef1ca45a 100644 --- a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart +++ b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart @@ -30,29 +30,25 @@ class ThumbnailTile extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final heroIndex = heroOffset ?? TabsRouterScope.of(context)?.controller.activeIndex ?? 0; - final assetContainerColor = - context.isDarkTheme ? context.primaryColor.darken(amount: 0.4) : context.primaryColor.lighten(amount: 0.75); + final assetContainerColor = context.isDarkTheme + ? context.primaryColor.darken(amount: 0.4) + : context.primaryColor.lighten(amount: 0.75); final isSelected = ref.watch( - multiSelectProvider.select( - (multiselect) => multiselect.selectedAssets.contains(asset), - ), + multiSelectProvider.select((multiselect) => multiselect.selectedAssets.contains(asset)), ); final borderStyle = lockSelection ? BoxDecoration( color: context.colorScheme.surfaceContainerHighest, - border: Border.all( - color: context.colorScheme.surfaceContainerHighest, - width: 6, - ), + border: Border.all(color: context.colorScheme.surfaceContainerHighest, width: 6), ) : isSelected - ? BoxDecoration( - color: assetContainerColor, - border: Border.all(color: assetContainerColor, width: 6), - ) - : const BoxDecoration(); + ? BoxDecoration( + color: assetContainerColor, + border: Border.all(color: assetContainerColor, width: 6), + ) + : const BoxDecoration(); final hasStack = asset is RemoteAsset && (asset as RemoteAsset).stackId != null; @@ -63,28 +59,22 @@ class ThumbnailTile extends ConsumerWidget { curve: Curves.decelerate, decoration: borderStyle, child: ClipRRect( - borderRadius: - isSelected || lockSelection ? const BorderRadius.all(Radius.circular(15.0)) : BorderRadius.zero, + borderRadius: isSelected || lockSelection + ? const BorderRadius.all(Radius.circular(15.0)) + : BorderRadius.zero, child: Stack( children: [ Positioned.fill( child: Hero( tag: '${asset.heroTag}_$heroIndex', - child: Thumbnail( - asset: asset, - fit: fit, - size: size, - ), + child: Thumbnail(asset: asset, fit: fit, size: size), ), ), if (hasStack) Align( alignment: Alignment.topRight, child: Padding( - padding: EdgeInsets.only( - right: 10.0, - top: asset.isVideo ? 24.0 : 6.0, - ), + padding: EdgeInsets.only(right: 10.0, top: asset.isVideo ? 24.0 : 6.0), child: const _TileOverlayIcon(Icons.burst_mode_rounded), ), ), @@ -99,26 +89,26 @@ class ThumbnailTile extends ConsumerWidget { if (showStorageIndicator) switch (asset.storage) { AssetState.local => const Align( - alignment: Alignment.bottomRight, - child: Padding( - padding: EdgeInsets.only(right: 10.0, bottom: 6.0), - child: _TileOverlayIcon(Icons.cloud_off_outlined), - ), + alignment: Alignment.bottomRight, + child: Padding( + padding: EdgeInsets.only(right: 10.0, bottom: 6.0), + child: _TileOverlayIcon(Icons.cloud_off_outlined), ), + ), AssetState.remote => const Align( - alignment: Alignment.bottomRight, - child: Padding( - padding: EdgeInsets.only(right: 10.0, bottom: 6.0), - child: _TileOverlayIcon(Icons.cloud_outlined), - ), + alignment: Alignment.bottomRight, + child: Padding( + padding: EdgeInsets.only(right: 10.0, bottom: 6.0), + child: _TileOverlayIcon(Icons.cloud_outlined), ), + ), AssetState.merged => const Align( - alignment: Alignment.bottomRight, - child: Padding( - padding: EdgeInsets.only(right: 10.0, bottom: 6.0), - child: _TileOverlayIcon(Icons.cloud_done_outlined), - ), + alignment: Alignment.bottomRight, + child: Padding( + padding: EdgeInsets.only(right: 10.0, bottom: 6.0), + child: _TileOverlayIcon(Icons.cloud_done_outlined), ), + ), }, if (asset.isFavorite) const Align( @@ -154,41 +144,22 @@ class _SelectionIndicator extends StatelessWidget { final bool isLocked; final Color? color; - const _SelectionIndicator({ - required this.isSelected, - required this.isLocked, - this.color, - }); + const _SelectionIndicator({required this.isSelected, required this.isLocked, this.color}); @override Widget build(BuildContext context) { if (isLocked) { return DecoratedBox( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: color, - ), - child: const Icon( - Icons.check_circle_rounded, - color: Colors.grey, - ), + decoration: BoxDecoration(shape: BoxShape.circle, color: color), + child: const Icon(Icons.check_circle_rounded, color: Colors.grey), ); } else if (isSelected) { return DecoratedBox( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: color, - ), - child: Icon( - Icons.check_circle_rounded, - color: context.primaryColor, - ), + decoration: BoxDecoration(shape: BoxShape.circle, color: color), + child: Icon(Icons.check_circle_rounded, color: context.primaryColor), ); } else { - return const Icon( - Icons.circle_outlined, - color: Colors.white, - ); + return const Icon(Icons.circle_outlined, color: Colors.white); } } } @@ -212,12 +183,7 @@ class _VideoIndicator extends StatelessWidget { color: Colors.white, fontSize: 12, fontWeight: FontWeight.bold, - shadows: [ - Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - ), - ], + shadows: [Shadow(blurRadius: 5.0, color: Color.fromRGBO(0, 0, 0, 0.6))], ), ), const _TileOverlayIcon(Icons.play_circle_outline_rounded), @@ -237,13 +203,7 @@ class _TileOverlayIcon extends StatelessWidget { icon, color: Colors.white, size: 16, - shadows: [ - const Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - offset: Offset(0.0, 0.0), - ), - ], + shadows: [const Shadow(blurRadius: 5.0, color: Color.fromRGBO(0, 0, 0, 0.6), offset: Offset(0.0, 0.0))], ); } } diff --git a/mobile/lib/presentation/widgets/memory/memory_bottom_info.widget.dart b/mobile/lib/presentation/widgets/memory/memory_bottom_info.widget.dart index 943b74288f..f067bc6bf3 100644 --- a/mobile/lib/presentation/widgets/memory/memory_bottom_info.widget.dart +++ b/mobile/lib/presentation/widgets/memory/memory_bottom_info.widget.dart @@ -11,11 +11,7 @@ import 'package:immich_mobile/routing/router.dart'; class DriftMemoryBottomInfo extends StatelessWidget { final DriftMemory memory; final String title; - const DriftMemoryBottomInfo({ - super.key, - required this.memory, - required this.title, - }); + const DriftMemoryBottomInfo({super.key, required this.memory, required this.title}); @override Widget build(BuildContext context) { @@ -23,47 +19,39 @@ class DriftMemoryBottomInfo extends StatelessWidget { final fileCreatedDate = memory.assets.first.createdAt; return Padding( padding: const EdgeInsets.all(16.0), - child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - title, - style: TextStyle( - color: Colors.grey[400], - fontSize: 13.0, - fontWeight: FontWeight.w500, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: TextStyle(color: Colors.grey[400], fontSize: 13.0, fontWeight: FontWeight.w500), ), - ), - Text( - df.format(fileCreatedDate), - style: const TextStyle( - color: Colors.white, - fontSize: 15.0, - fontWeight: FontWeight.w500, + Text( + df.format(fileCreatedDate), + style: const TextStyle(color: Colors.white, fontSize: 15.0, fontWeight: FontWeight.w500), ), - ), - ], - ), - Tooltip( - message: 'view_in_timeline'.tr(), - child: MaterialButton( - minWidth: 0, - onPressed: () async { - await context.maybePop(); - await context.navigateTo(const TabShellRoute(children: [MainTimelineRoute()])); - EventStream.shared.emit(ScrollToDateEvent(fileCreatedDate)); - }, - shape: const CircleBorder(), - color: Colors.white.withValues(alpha: 0.2), - elevation: 0, - child: const Icon( - Icons.open_in_new, - color: Colors.white, + ], + ), + Tooltip( + message: 'view_in_timeline'.tr(), + child: MaterialButton( + minWidth: 0, + onPressed: () async { + await context.maybePop(); + await context.navigateTo(const TabShellRoute(children: [MainTimelineRoute()])); + EventStream.shared.emit(ScrollToDateEvent(fileCreatedDate)); + }, + shape: const CircleBorder(), + color: Colors.white.withValues(alpha: 0.2), + elevation: 0, + child: const Icon(Icons.open_in_new, color: Colors.white), ), ), - ), - ]), + ], + ), ); } } diff --git a/mobile/lib/presentation/widgets/memory/memory_card.widget.dart b/mobile/lib/presentation/widgets/memory/memory_card.widget.dart index e69c848f45..eaed60b204 100644 --- a/mobile/lib/presentation/widgets/memory/memory_card.widget.dart +++ b/mobile/lib/presentation/widgets/memory/memory_card.widget.dart @@ -29,17 +29,12 @@ class DriftMemoryCard extends StatelessWidget { color: Colors.black, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(25.0)), - side: BorderSide( - color: Colors.black, - width: 1.0, - ), + side: BorderSide(color: Colors.black, width: 1.0), ), clipBehavior: Clip.hardEdge, child: Stack( children: [ - SizedBox.expand( - child: _BlurredBackdrop(asset: asset), - ), + SizedBox.expand(child: _BlurredBackdrop(asset: asset)), LayoutBuilder( builder: (context, constraints) { // Determine the fit using the aspect ratio @@ -55,11 +50,7 @@ class DriftMemoryCard extends StatelessWidget { } if (asset.isImage) { - return FullImage( - asset, - fit: fit, - size: const Size(double.infinity, double.infinity), - ); + return FullImage(asset, fit: fit, size: const Size(double.infinity, double.infinity)); } else { return SizedBox( width: context.width, @@ -69,11 +60,7 @@ class DriftMemoryCard extends StatelessWidget { asset: asset, showControls: false, playbackDelayFactor: 2, - image: FullImage( - asset, - size: Size(context.width, context.height), - fit: BoxFit.contain, - ), + image: FullImage(asset, size: Size(context.width, context.height), fit: BoxFit.contain), ), ); } @@ -85,10 +72,7 @@ class DriftMemoryCard extends StatelessWidget { bottom: 18.0, child: Text( title, - style: context.textTheme.headlineMedium?.copyWith( - color: Colors.white, - fontWeight: FontWeight.w500, - ), + style: context.textTheme.headlineMedium?.copyWith(color: Colors.white, fontWeight: FontWeight.w500), ), ), ], @@ -109,16 +93,9 @@ class _BlurredBackdrop extends HookWidget { // Use a nice cheap blur hash image decoration return Container( decoration: BoxDecoration( - image: DecorationImage( - image: MemoryImage( - blurhash, - ), - fit: BoxFit.cover, - ), - ), - child: Container( - color: Colors.black.withValues(alpha: 0.2), + image: DecorationImage(image: MemoryImage(blurhash), fit: BoxFit.cover), ), + child: Container(color: Colors.black.withValues(alpha: 0.2)), ); } else { // Fall back to using a more expensive image filtered @@ -129,16 +106,11 @@ class _BlurredBackdrop extends HookWidget { child: Container( decoration: BoxDecoration( image: DecorationImage( - image: getFullImageProvider( - asset, - size: Size(context.width, context.height), - ), + image: getFullImageProvider(asset, size: Size(context.width, context.height)), fit: BoxFit.cover, ), ), - child: Container( - color: Colors.black.withValues(alpha: 0.2), - ), + child: Container(color: Colors.black.withValues(alpha: 0.2)), ), ); } diff --git a/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart b/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart index 4863b60aad..e2bc59b4c7 100644 --- a/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart +++ b/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart @@ -17,17 +17,13 @@ class DriftMemoryLane extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return ConstrainedBox( - constraints: const BoxConstraints( - maxHeight: 200, - ), + constraints: const BoxConstraints(maxHeight: 200), child: CarouselView( itemExtent: 145.0, shrinkExtent: 1.0, elevation: 2, backgroundColor: Colors.black, - overlayColor: WidgetStateProperty.all( - Colors.white.withValues(alpha: 0.1), - ), + overlayColor: WidgetStateProperty.all(Colors.white.withValues(alpha: 0.1)), onTap: (index) { ref.read(hapticFeedbackProvider.notifier).heavyImpact(); @@ -40,12 +36,7 @@ class DriftMemoryLane extends ConsumerWidget { } } - context.pushRoute( - DriftMemoryRoute( - memories: memories, - memoryIndex: index, - ), - ); + context.pushRoute(DriftMemoryRoute(memories: memories, memoryIndex: index)); }, children: memories.map((memory) => DriftMemoryCard(memory: memory)).toList(), ), @@ -54,53 +45,33 @@ class DriftMemoryLane extends ConsumerWidget { } class DriftMemoryCard extends ConsumerWidget { - const DriftMemoryCard({ - super.key, - required this.memory, - }); + const DriftMemoryCard({super.key, required this.memory}); final DriftMemory memory; @override Widget build(BuildContext context, WidgetRef ref) { final yearsAgo = DateTime.now().year - memory.data.year; - final title = 'years_ago'.t( - context: context, - args: { - 'years': yearsAgo.toString(), - }, - ); + final title = 'years_ago'.t(context: context, args: {'years': yearsAgo.toString()}); return Center( child: Stack( children: [ ColorFiltered( - colorFilter: ColorFilter.mode( - Colors.black.withValues(alpha: 0.2), - BlendMode.darken, - ), + colorFilter: ColorFilter.mode(Colors.black.withValues(alpha: 0.2), BlendMode.darken), child: SizedBox( width: 205, height: 200, - child: Thumbnail( - remoteId: memory.assets[0].id, - fit: BoxFit.cover, - ), + child: Thumbnail(remoteId: memory.assets[0].id, fit: BoxFit.cover), ), ), Positioned( bottom: 16, left: 16, child: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 114, - ), + constraints: const BoxConstraints(maxWidth: 114), child: Text( title, - style: const TextStyle( - fontWeight: FontWeight.w600, - color: Colors.white, - fontSize: 15, - ), + style: const TextStyle(fontWeight: FontWeight.w600, color: Colors.white, fontSize: 15), ), ), ), diff --git a/mobile/lib/presentation/widgets/remote_album/drift_album_option.widget.dart b/mobile/lib/presentation/widgets/remote_album/drift_album_option.widget.dart index 81989b263a..c08348e2af 100644 --- a/mobile/lib/presentation/widgets/remote_album/drift_album_option.widget.dart +++ b/mobile/lib/presentation/widgets/remote_album/drift_album_option.widget.dart @@ -25,9 +25,7 @@ class DriftRemoteAlbumOption extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - TextStyle textStyle = Theme.of(context).textTheme.bodyLarge!.copyWith( - fontWeight: FontWeight.w600, - ); + TextStyle textStyle = Theme.of(context).textTheme.bodyLarge!.copyWith(fontWeight: FontWeight.w600); return SafeArea( child: Padding( @@ -38,72 +36,46 @@ class DriftRemoteAlbumOption extends ConsumerWidget { if (onEditAlbum != null) ListTile( leading: const Icon(Icons.edit), - title: Text( - 'edit_album'.t(context: context), - style: textStyle, - ), + title: Text('edit_album'.t(context: context), style: textStyle), onTap: onEditAlbum, ), if (onAddPhotos != null) ListTile( leading: const Icon(Icons.add_a_photo), - title: Text( - 'add_photos'.t(context: context), - style: textStyle, - ), + title: Text('add_photos'.t(context: context), style: textStyle), onTap: onAddPhotos, ), if (onAddUsers != null) ListTile( leading: const Icon(Icons.group_add), - title: Text( - 'album_viewer_page_share_add_users'.t(context: context), - style: textStyle, - ), + title: Text('album_viewer_page_share_add_users'.t(context: context), style: textStyle), onTap: onAddUsers, ), if (onLeaveAlbum != null) ListTile( leading: const Icon(Icons.person_remove_rounded), - title: Text( - 'leave_album'.t(context: context), - style: textStyle, - ), + title: Text('leave_album'.t(context: context), style: textStyle), onTap: onLeaveAlbum, ), if (onToggleAlbumOrder != null) ListTile( leading: const Icon(Icons.swap_vert_rounded), - title: Text( - 'change_display_order'.t(context: context), - style: textStyle, - ), + title: Text('change_display_order'.t(context: context), style: textStyle), onTap: onToggleAlbumOrder, ), if (onCreateSharedLink != null) ListTile( leading: const Icon(Icons.link), - title: Text( - 'create_shared_link'.t(context: context), - style: textStyle, - ), + title: Text('create_shared_link'.t(context: context), style: textStyle), onTap: onCreateSharedLink, ), if (onDeleteAlbum != null) ...[ - const Divider( - indent: 16, - endIndent: 16, - ), + const Divider(indent: 16, endIndent: 16), ListTile( - leading: Icon( - Icons.delete, - color: context.isDarkTheme ? Colors.red[400] : Colors.red[800], - ), + leading: Icon(Icons.delete, color: context.isDarkTheme ? Colors.red[400] : Colors.red[800]), title: Text( 'delete_album'.t(context: context), - style: textStyle.copyWith( - color: context.isDarkTheme ? Colors.red[400] : Colors.red[800], - ), + style: textStyle.copyWith(color: context.isDarkTheme ? Colors.red[400] : Colors.red[800]), ), onTap: onDeleteAlbum, ), diff --git a/mobile/lib/presentation/widgets/timeline/fixed/row.dart b/mobile/lib/presentation/widgets/timeline/fixed/row.dart index 24f3c97125..3fe3cea3c9 100644 --- a/mobile/lib/presentation/widgets/timeline/fixed/row.dart +++ b/mobile/lib/presentation/widgets/timeline/fixed/row.dart @@ -16,11 +16,7 @@ class FixedTimelineRow extends MultiChildRenderObjectWidget { @override RenderObject createRenderObject(BuildContext context) { - return RenderFixedRow( - dimension: dimension, - spacing: spacing, - textDirection: textDirection, - ); + return RenderFixedRow(dimension: dimension, spacing: spacing, textDirection: textDirection); } @override @@ -50,9 +46,9 @@ class RenderFixedRow extends RenderBox required double dimension, required double spacing, required TextDirection textDirection, - }) : _dimension = dimension, - _spacing = spacing, - _textDirection = textDirection { + }) : _dimension = dimension, + _spacing = spacing, + _textDirection = textDirection { addAll(children); } diff --git a/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart b/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart index 88d113dcde..ea5f55b35e 100644 --- a/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart +++ b/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart @@ -33,8 +33,8 @@ class FixedSegment extends Segment { required super.headerExtent, required super.spacing, required super.header, - }) : assert(tileHeight != 0), - mainAxisExtend = tileHeight + spacing; + }) : assert(tileHeight != 0), + mainAxisExtend = tileHeight + spacing; @override double indexToLayoutOffset(int index) { @@ -64,12 +64,7 @@ class FixedSegment extends Segment { final numberOfAssets = math.min(columnCount, assetCount - assetIndex); if (index == firstIndex) { - return TimelineHeader( - bucket: bucket, - header: header, - height: headerExtent, - assetOffset: firstAssetIndex, - ); + return TimelineHeader(bucket: bucket, header: header, height: headerExtent, assetOffset: firstAssetIndex); } return _FixedSegmentRow( @@ -104,10 +99,7 @@ class _FixedSegmentRow extends ConsumerWidget { } if (timelineService.hasRange(assetIndex, assetCount)) { - return _buildAssetRow( - context, - timelineService.getAssets(assetIndex, assetCount), - ); + return _buildAssetRow(context, timelineService.getAssets(assetIndex, assetCount)); } return FutureBuilder>( @@ -122,12 +114,7 @@ class _FixedSegmentRow extends ConsumerWidget { } Widget _buildPlaceholder(BuildContext context) { - return SegmentBuilder.buildPlaceholder( - context, - assetCount, - size: Size.square(tileHeight), - spacing: spacing, - ); + return SegmentBuilder.buildPlaceholder(context, assetCount, size: Size.square(tileHeight), spacing: spacing); } Widget _buildAssetRow(BuildContext context, List assets) { @@ -137,11 +124,7 @@ class _FixedSegmentRow extends ConsumerWidget { textDirection: Directionality.of(context), children: [ for (int i = 0; i < assets.length; i++) - _AssetTileWidget( - key: ValueKey(assets[i].heroTag), - asset: assets[i], - assetIndex: assetIndex + i, - ), + _AssetTileWidget(key: ValueKey(assets[i].heroTag), asset: assets[i], assetIndex: assetIndex + i), ], ); } @@ -151,19 +134,9 @@ class _AssetTileWidget extends ConsumerWidget { final BaseAsset asset; final int assetIndex; - const _AssetTileWidget({ - super.key, - required this.asset, - required this.assetIndex, - }); + const _AssetTileWidget({super.key, required this.asset, required this.assetIndex}); - Future _handleOnTap( - BuildContext ctx, - WidgetRef ref, - int assetIndex, - BaseAsset asset, - int? heroOffset, - ) async { + Future _handleOnTap(BuildContext ctx, WidgetRef ref, int assetIndex, BaseAsset asset, int? heroOffset) async { final multiSelectState = ref.read(multiSelectProvider); if (multiSelectState.forceEnable || multiSelectState.isEnabled) { @@ -192,11 +165,7 @@ class _AssetTileWidget extends ConsumerWidget { } bool _getLockSelectionStatus(WidgetRef ref) { - final lockSelectionAssets = ref.read( - multiSelectProvider.select( - (state) => state.lockedSelectionAssets, - ), - ); + final lockSelectionAssets = ref.read(multiSelectProvider.select((state) => state.lockedSelectionAssets)); if (lockSelectionAssets.isEmpty) { return false; @@ -210,9 +179,7 @@ class _AssetTileWidget extends ConsumerWidget { final heroOffset = TabsRouterScope.of(context)?.controller.activeIndex ?? 0; final lockSelection = _getLockSelectionStatus(ref); - final showStorageIndicator = ref.watch( - timelineArgsProvider.select((args) => args.showStorageIndicator), - ); + final showStorageIndicator = ref.watch(timelineArgsProvider.select((args) => args.showStorageIndicator)); return RepaintBoundary( child: GestureDetector( diff --git a/mobile/lib/presentation/widgets/timeline/fixed/segment_builder.dart b/mobile/lib/presentation/widgets/timeline/fixed/segment_builder.dart index 5e260a4e68..b65582f976 100644 --- a/mobile/lib/presentation/widgets/timeline/fixed/segment_builder.dart +++ b/mobile/lib/presentation/widgets/timeline/fixed/segment_builder.dart @@ -35,8 +35,7 @@ class FixedSegmentBuilder extends SegmentBuilder { final timelineHeader = switch (groupBy) { GroupAssetsBy.month => HeaderType.month, - GroupAssetsBy.day || - GroupAssetsBy.auto => + GroupAssetsBy.day || GroupAssetsBy.auto => bucket is TimeBucket && bucket.date.month != previousDate?.month ? HeaderType.monthAndDay : HeaderType.day, GroupAssetsBy.none => HeaderType.none, }; diff --git a/mobile/lib/presentation/widgets/timeline/header.widget.dart b/mobile/lib/presentation/widgets/timeline/header.widget.dart index da48acd7db..b8c6668a38 100644 --- a/mobile/lib/presentation/widgets/timeline/header.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/header.widget.dart @@ -47,11 +47,7 @@ class TimelineHeader extends StatelessWidget { final isDayHeader = header == HeaderType.day || header == HeaderType.monthAndDay; return Padding( - padding: EdgeInsets.only( - top: isMonthHeader ? 8.0 : 0.0, - left: 12.0, - right: 12.0, - ), + padding: EdgeInsets.only(top: isMonthHeader ? 8.0 : 0.0, left: 12.0, right: 12.0), child: SizedBox( height: height, child: Column( @@ -61,32 +57,17 @@ class TimelineHeader extends StatelessWidget { if (isMonthHeader) Row( children: [ - Text( - _formatMonth(context, date), - style: context.textTheme.labelLarge?.copyWith(fontSize: 24), - ), + Text(_formatMonth(context, date), style: context.textTheme.labelLarge?.copyWith(fontSize: 24)), const Spacer(), - if (header != HeaderType.monthAndDay) - _BulkSelectIconButton( - bucket: bucket, - assetOffset: assetOffset, - ), + if (header != HeaderType.monthAndDay) _BulkSelectIconButton(bucket: bucket, assetOffset: assetOffset), ], ), if (isDayHeader) Row( children: [ - Text( - _formatDay(context, date), - style: context.textTheme.labelLarge?.copyWith( - fontSize: 15, - ), - ), + Text(_formatDay(context, date), style: context.textTheme.labelLarge?.copyWith(fontSize: 15)), const Spacer(), - _BulkSelectIconButton( - bucket: bucket, - assetOffset: assetOffset, - ), + _BulkSelectIconButton(bucket: bucket, assetOffset: assetOffset), ], ), ], @@ -100,10 +81,7 @@ class _BulkSelectIconButton extends ConsumerWidget { final Bucket bucket; final int assetOffset; - const _BulkSelectIconButton({ - required this.bucket, - required this.assetOffset, - }); + const _BulkSelectIconButton({required this.bucket, required this.assetOffset}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -118,23 +96,12 @@ class _BulkSelectIconButton extends ConsumerWidget { return IconButton( onPressed: () { - ref.read(multiSelectProvider.notifier).toggleBucketSelection( - assetOffset, - bucket.assetCount, - ); + ref.read(multiSelectProvider.notifier).toggleBucketSelection(assetOffset, bucket.assetCount); ref.read(hapticFeedbackProvider.notifier).heavyImpact(); }, icon: isAllSelected - ? Icon( - Icons.check_circle_rounded, - size: 26, - color: context.primaryColor, - ) - : Icon( - Icons.check_circle_outline_rounded, - size: 26, - color: context.colorScheme.onSurfaceSecondary, - ), + ? Icon(Icons.check_circle_rounded, size: 26, color: context.primaryColor) + : Icon(Icons.check_circle_outline_rounded, size: 26, color: context.colorScheme.onSurfaceSecondary), ); } } diff --git a/mobile/lib/presentation/widgets/timeline/scrubber.widget.dart b/mobile/lib/presentation/widgets/timeline/scrubber.widget.dart index 6d23f169b7..be1d0f0873 100644 --- a/mobile/lib/presentation/widgets/timeline/scrubber.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/scrubber.widget.dart @@ -43,10 +43,7 @@ class Scrubber extends ConsumerStatefulWidget { ConsumerState createState() => ScrubberState(); } -List<_Segment> _buildSegments({ - required List layoutSegments, - required double timelineHeight, -}) { +List<_Segment> _buildSegments({required List layoutSegments, required double timelineHeight}) { const double offsetThreshold = 20.0; final segments = <_Segment>[]; @@ -66,14 +63,7 @@ List<_Segment> _buildSegments({ final showSegment = lastOffset + offsetThreshold <= startOffset && (lastDate == null || date.year != lastDate.year); - segments.add( - _Segment( - date: date, - startOffset: startOffset, - scrollLabel: label, - showSegment: showSegment, - ), - ); + segments.add(_Segment(date: date, startOffset: startOffset, scrollLabel: label, showSegment: showSegment)); lastDate = date; if (showSegment) { lastOffset = startOffset; @@ -109,27 +99,12 @@ class ScrubberState extends ConsumerState with TickerProviderStateMixi void initState() { super.initState(); _isDragging = false; - _segments = _buildSegments( - layoutSegments: widget.layoutSegments, - timelineHeight: _scrubberHeight, - ); - _thumbAnimationController = AnimationController( - vsync: this, - duration: kTimelineScrubberFadeInDuration, - ); - _thumbAnimation = CurvedAnimation( - parent: _thumbAnimationController, - curve: Curves.fastEaseInToSlowEaseOut, - ); - _labelAnimationController = AnimationController( - vsync: this, - duration: kTimelineScrubberFadeInDuration, - ); + _segments = _buildSegments(layoutSegments: widget.layoutSegments, timelineHeight: _scrubberHeight); + _thumbAnimationController = AnimationController(vsync: this, duration: kTimelineScrubberFadeInDuration); + _thumbAnimation = CurvedAnimation(parent: _thumbAnimationController, curve: Curves.fastEaseInToSlowEaseOut); + _labelAnimationController = AnimationController(vsync: this, duration: kTimelineScrubberFadeInDuration); - _labelAnimation = CurvedAnimation( - parent: _labelAnimationController, - curve: Curves.fastOutSlowIn, - ); + _labelAnimation = CurvedAnimation(parent: _labelAnimationController, curve: Curves.fastOutSlowIn); } @override @@ -143,10 +118,7 @@ class ScrubberState extends ConsumerState with TickerProviderStateMixi super.didUpdateWidget(oldWidget); if (oldWidget.layoutSegments.lastOrNull?.endOffset != widget.layoutSegments.lastOrNull?.endOffset) { - _segments = _buildSegments( - layoutSegments: widget.layoutSegments, - timelineHeight: _scrubberHeight, - ); + _segments = _buildSegments(layoutSegments: widget.layoutSegments, timelineHeight: _scrubberHeight); } } @@ -276,12 +248,10 @@ class ScrubberState extends ConsumerState with TickerProviderStateMixi } int _findLayoutSegmentIndex(_Segment segment) { - return widget.layoutSegments.indexWhere( - (layoutSegment) { - final bucket = layoutSegment.bucket as TimeBucket; - return bucket.date.year == segment.date.year && bucket.date.month == segment.date.month; - }, - ); + return widget.layoutSegments.indexWhere((layoutSegment) { + final bucket = layoutSegment.bucket as TimeBucket; + return bucket.date.year == segment.date.year && bucket.date.month == segment.date.month; + }); } void _scrollToLayoutSegment(int layoutSegmentIndex) { @@ -311,19 +281,13 @@ class ScrubberState extends ConsumerState with TickerProviderStateMixi if (_scrollController.hasClients == true) { // Cache to avoid multiple calls to [_currentOffset] final scrollOffset = _currentOffset; - final labelText = _segments - .lastWhereOrNull( - (segment) => segment.startOffset <= scrollOffset, - ) - ?.scrollLabel ?? + final labelText = + _segments.lastWhereOrNull((segment) => segment.startOffset <= scrollOffset)?.scrollLabel ?? _segments.firstOrNull?.scrollLabel; label = labelText != null ? Text( labelText, - style: ctx.textTheme.bodyLarge?.copyWith( - color: Colors.white, - fontWeight: FontWeight.bold, - ), + style: ctx.textTheme.bodyLarge?.copyWith(color: Colors.white, fontWeight: FontWeight.bold), ) : null; } @@ -351,11 +315,7 @@ class ScrubberState extends ConsumerState with TickerProviderStateMixi onVerticalDragStart: _onDragStart, onVerticalDragUpdate: _onDragUpdate, onVerticalDragEnd: _onDragEnd, - child: _Scrubber( - thumbAnimation: _thumbAnimation, - labelAnimation: _labelAnimation, - label: label, - ), + child: _Scrubber(thumbAnimation: _thumbAnimation, labelAnimation: _labelAnimation, label: label), ), ), ), @@ -370,12 +330,7 @@ class _SegmentsLayer extends StatelessWidget { final double topPadding; final bool isDragging; - const _SegmentsLayer({ - super.key, - required this.segments, - required this.topPadding, - required this.isDragging, - }); + const _SegmentsLayer({super.key, required this.segments, required this.topPadding, required this.isDragging}); @override Widget build(BuildContext context) { @@ -389,9 +344,7 @@ class _SegmentsLayer extends StatelessWidget { key: ValueKey('segment_${segment.date.millisecondsSinceEpoch}'), top: topPadding + segment.startOffset, end: 100, - child: RepaintBoundary( - child: _SegmentWidget(segment), - ), + child: RepaintBoundary(child: _SegmentWidget(segment)), ), ) .toList(), @@ -419,10 +372,7 @@ class _SegmentWidget extends StatelessWidget { alignment: Alignment.center, child: Text( _segment.date.year.toString(), - style: context.textTheme.labelMedium?.copyWith( - fontFamily: "OverpassMono", - fontWeight: FontWeight.w600, - ), + style: context.textTheme.labelMedium?.copyWith(fontFamily: "OverpassMono", fontWeight: FontWeight.w600), ), ), ), @@ -436,11 +386,7 @@ class _ScrollLabel extends StatelessWidget { final Color backgroundColor; final Animation animation; - const _ScrollLabel({ - required this.label, - required this.backgroundColor, - required this.animation, - }); + const _ScrollLabel({required this.label, required this.backgroundColor, required this.animation}); @override Widget build(BuildContext context) { @@ -471,16 +417,13 @@ class _Scrubber extends StatelessWidget { final Animation thumbAnimation; final Animation labelAnimation; - const _Scrubber({ - this.label, - required this.thumbAnimation, - required this.labelAnimation, - }); + const _Scrubber({this.label, required this.thumbAnimation, required this.labelAnimation}); @override Widget build(BuildContext context) { - final backgroundColor = - context.isDarkTheme ? context.colorScheme.primary.darken(amount: .5) : context.colorScheme.primary; + final backgroundColor = context.isDarkTheme + ? context.colorScheme.primary.darken(amount: .5) + : context.colorScheme.primary; return _SlideFadeTransition( animation: thumbAnimation, @@ -488,12 +431,7 @@ class _Scrubber extends StatelessWidget { mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.end, children: [ - if (label != null) - _ScrollLabel( - label: label!, - backgroundColor: backgroundColor, - animation: labelAnimation, - ), + if (label != null) _ScrollLabel(label: label!, backgroundColor: backgroundColor, animation: labelAnimation), _CircularThumb(backgroundColor), ], ), @@ -519,9 +457,7 @@ class _CircularThumb extends StatelessWidget { topRight: Radius.circular(4.0), bottomRight: Radius.circular(4.0), ), - child: Container( - constraints: BoxConstraints.tight(const Size(48.0 * 0.6, 48.0)), - ), + child: Container(constraints: BoxConstraints.tight(const Size(48.0 * 0.6, 48.0))), ), ); } @@ -543,14 +479,8 @@ class _ArrowPainter extends CustomPainter { final baseX = size.width / 2; final baseY = size.height / 2; - canvas.drawPath( - _trianglePath(Offset(baseX, baseY - 2.0), width, height, true), - paint, - ); - canvas.drawPath( - _trianglePath(Offset(baseX, baseY + 2.0), width, height, false), - paint, - ); + canvas.drawPath(_trianglePath(Offset(baseX, baseY - 2.0), width, height, true), paint); + canvas.drawPath(_trianglePath(Offset(baseX, baseY + 2.0), width, height, false), paint); } static Path _trianglePath(Offset o, double width, double height, bool isUp) { @@ -566,11 +496,9 @@ class _SlideFadeTransition extends StatelessWidget { final Animation _animation; final Widget _child; - const _SlideFadeTransition({ - required Animation animation, - required Widget child, - }) : _animation = animation, - _child = child; + const _SlideFadeTransition({required Animation animation, required Widget child}) + : _animation = animation, + _child = child; @override Widget build(BuildContext context) { @@ -578,14 +506,8 @@ class _SlideFadeTransition extends StatelessWidget { animation: _animation, builder: (context, child) => _animation.value == 0.0 ? const SizedBox() : child!, child: SlideTransition( - position: Tween( - begin: const Offset(0.3, 0.0), - end: const Offset(0.0, 0.0), - ).animate(_animation), - child: FadeTransition( - opacity: _animation, - child: _child, - ), + position: Tween(begin: const Offset(0.3, 0.0), end: const Offset(0.0, 0.0)).animate(_animation), + child: FadeTransition(opacity: _animation, child: _child), ), ); } @@ -597,19 +519,9 @@ class _Segment { final String scrollLabel; final bool showSegment; - const _Segment({ - required this.date, - required this.startOffset, - required this.scrollLabel, - this.showSegment = false, - }); + const _Segment({required this.date, required this.startOffset, required this.scrollLabel, this.showSegment = false}); - _Segment copyWith({ - DateTime? date, - double? startOffset, - String? scrollLabel, - bool? showSegment, - }) { + _Segment copyWith({DateTime? date, double? startOffset, String? scrollLabel, bool? showSegment}) { return _Segment( date: date ?? this.date, startOffset: startOffset ?? this.startOffset, diff --git a/mobile/lib/presentation/widgets/timeline/segment.model.dart b/mobile/lib/presentation/widgets/timeline/segment.model.dart index 6a20db4c98..bc5f974874 100644 --- a/mobile/lib/presentation/widgets/timeline/segment.model.dart +++ b/mobile/lib/presentation/widgets/timeline/segment.model.dart @@ -37,8 +37,8 @@ abstract class Segment { required this.headerExtent, required this.spacing, required this.header, - }) : gridIndex = firstIndex + 1, - gridOffset = startOffset + headerExtent + spacing; + }) : gridIndex = firstIndex + 1, + gridOffset = startOffset + headerExtent + spacing; bool containsIndex(int index) => firstIndex <= index && index <= lastIndex; diff --git a/mobile/lib/presentation/widgets/timeline/segment_builder.dart b/mobile/lib/presentation/widgets/timeline/segment_builder.dart index a746eab243..c80595a446 100644 --- a/mobile/lib/presentation/widgets/timeline/segment_builder.dart +++ b/mobile/lib/presentation/widgets/timeline/segment_builder.dart @@ -9,34 +9,26 @@ abstract class SegmentBuilder { final double spacing; final GroupAssetsBy groupBy; - const SegmentBuilder({ - required this.buckets, - this.spacing = kTimelineSpacing, - this.groupBy = GroupAssetsBy.day, - }); + const SegmentBuilder({required this.buckets, this.spacing = kTimelineSpacing, this.groupBy = GroupAssetsBy.day}); static double headerExtent(HeaderType header) => switch (header) { - HeaderType.month => kTimelineHeaderExtent, - HeaderType.day => kTimelineHeaderExtent * 0.90, - HeaderType.monthAndDay => kTimelineHeaderExtent * 1.6, - HeaderType.none => 0.0, - }; + HeaderType.month => kTimelineHeaderExtent, + HeaderType.day => kTimelineHeaderExtent * 0.90, + HeaderType.monthAndDay => kTimelineHeaderExtent * 1.6, + HeaderType.none => 0.0, + }; static Widget buildPlaceholder( BuildContext context, int count, { Size size = const Size.square(kTimelineFixedTileExtent), double spacing = kTimelineSpacing, - }) => - RepaintBoundary( - child: FixedTimelineRow( - dimension: size.height, - spacing: spacing, - textDirection: Directionality.of(context), - children: List.generate( - count, - (_) => ThumbnailPlaceholder(width: size.width, height: size.height), - ), - ), - ); + }) => RepaintBoundary( + child: FixedTimelineRow( + dimension: size.height, + spacing: spacing, + textDirection: Directionality.of(context), + children: List.generate(count, (_) => ThumbnailPlaceholder(width: size.width, height: size.height)), + ), + ); } diff --git a/mobile/lib/presentation/widgets/timeline/timeline.state.dart b/mobile/lib/presentation/widgets/timeline/timeline.state.dart index cdf79239d4..ad3ae3ccf6 100644 --- a/mobile/lib/presentation/widgets/timeline/timeline.state.dart +++ b/mobile/lib/presentation/widgets/timeline/timeline.state.dart @@ -54,10 +54,7 @@ class TimelineState { final bool isScrubbing; final bool isScrolling; - const TimelineState({ - this.isScrubbing = false, - this.isScrolling = false, - }); + const TimelineState({this.isScrubbing = false, this.isScrolling = false}); bool get isInteracting => isScrubbing || isScrolling; @@ -70,10 +67,7 @@ class TimelineState { int get hashCode => isScrubbing.hashCode ^ isScrolling.hashCode; TimelineState copyWith({bool? isScrubbing, bool? isScrolling}) { - return TimelineState( - isScrubbing: isScrubbing ?? this.isScrubbing, - isScrolling: isScrolling ?? this.isScrolling, - ); + return TimelineState(isScrubbing: isScrubbing ?? this.isScrubbing, isScrolling: isScrolling ?? this.isScrolling); } } @@ -89,38 +83,30 @@ class TimelineStateNotifier extends Notifier { } @override - TimelineState build() => const TimelineState( - isScrubbing: false, - isScrolling: false, - ); + TimelineState build() => const TimelineState(isScrubbing: false, isScrolling: false); } // This provider watches the buckets from the timeline service & args and serves the segments. // It should be used only after the timeline service and timeline args provider is overridden -final timelineSegmentProvider = StreamProvider.autoDispose>( - (ref) async* { - final args = ref.watch(timelineArgsProvider); - final columnCount = args.columnCount; - final spacing = args.spacing; - final availableTileWidth = args.maxWidth - (spacing * (columnCount - 1)); - final tileExtent = math.max(0, availableTileWidth) / columnCount; +final timelineSegmentProvider = StreamProvider.autoDispose>((ref) async* { + final args = ref.watch(timelineArgsProvider); + final columnCount = args.columnCount; + final spacing = args.spacing; + final availableTileWidth = args.maxWidth - (spacing * (columnCount - 1)); + final tileExtent = math.max(0, availableTileWidth) / columnCount; - final groupBy = args.groupBy ?? GroupAssetsBy.values[ref.watch(settingsProvider).get(Setting.groupAssetsBy)]; + final groupBy = args.groupBy ?? GroupAssetsBy.values[ref.watch(settingsProvider).get(Setting.groupAssetsBy)]; - final timelineService = ref.watch(timelineServiceProvider); - yield* timelineService.watchBuckets().map((buckets) { - return FixedSegmentBuilder( - buckets: buckets, - tileHeight: tileExtent, - columnCount: columnCount, - spacing: spacing, - groupBy: groupBy, - ).generate(); - }); - }, - dependencies: [timelineServiceProvider, timelineArgsProvider], -); + final timelineService = ref.watch(timelineServiceProvider); + yield* timelineService.watchBuckets().map((buckets) { + return FixedSegmentBuilder( + buckets: buckets, + tileHeight: tileExtent, + columnCount: columnCount, + spacing: spacing, + groupBy: groupBy, + ).generate(); + }); +}, dependencies: [timelineServiceProvider, timelineArgsProvider]); -final timelineStateProvider = NotifierProvider( - TimelineStateNotifier.new, -); +final timelineStateProvider = NotifierProvider(TimelineStateNotifier.new); diff --git a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart index 59cc018b28..26799580a2 100644 --- a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart @@ -29,11 +29,7 @@ class Timeline extends StatelessWidget { this.topSliverWidgetHeight, this.showStorageIndicator = false, this.withStack = false, - this.appBar = const ImmichSliverAppBar( - floating: true, - pinned: false, - snap: false, - ), + this.appBar = const ImmichSliverAppBar(floating: true, pinned: false, snap: false), this.bottomSheet = const GeneralBottomSheet(), this.groupBy, }); @@ -57,9 +53,7 @@ class Timeline extends StatelessWidget { (ref) => TimelineArgs( maxWidth: constraints.maxWidth, maxHeight: constraints.maxHeight, - columnCount: ref.watch( - settingsProvider.select((s) => s.get(Setting.tilesPerRow)), - ), + columnCount: ref.watch(settingsProvider.select((s) => s.get(Setting.tilesPerRow))), showStorageIndicator: showStorageIndicator, withStack: withStack, groupBy: groupBy, @@ -79,12 +73,7 @@ class Timeline extends StatelessWidget { } class _SliverTimeline extends ConsumerStatefulWidget { - const _SliverTimeline({ - this.topSliverWidget, - this.topSliverWidgetHeight, - this.appBar, - this.bottomSheet, - }); + const _SliverTimeline({this.topSliverWidget, this.topSliverWidgetHeight, this.appBar, this.bottomSheet}); final Widget? topSliverWidget; final double? topSliverWidgetHeight; @@ -108,11 +97,7 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { void _onEvent(Event event) { switch (event) { case ScrollToTopEvent(): - _scrollController.animateTo( - 0, - duration: const Duration(milliseconds: 250), - curve: Curves.easeInOut, - ); + _scrollController.animateTo(0, duration: const Duration(milliseconds: 250), curve: Curves.easeInOut); case ScrollToDateEvent scrollToDateEvent: _scrollToDate(scrollToDateEvent.date); case TimelineReloadEvent(): @@ -143,7 +128,8 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { }); // If exact date not found, try to find the closest month - final fallbackSegment = targetSegment ?? + final fallbackSegment = + targetSegment ?? segments.firstWhereOrNull((segment) { if (segment.bucket is TimeBucket) { final segmentDate = (segment.bucket as TimeBucket).date; @@ -168,9 +154,7 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { Widget build(BuildContext _) { final asyncSegments = ref.watch(timelineSegmentProvider); final maxHeight = ref.watch(timelineArgsProvider.select((args) => args.maxHeight)); - final isSelectionMode = ref.watch( - multiSelectProvider.select((s) => s.forceEnable), - ); + final isSelectionMode = ref.watch(multiSelectProvider.select((s) => s.forceEnable)); return asyncSegments.widgetWhen( onData: (segments) { @@ -211,42 +195,26 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { addRepaintBoundaries: false, ), ), - const SliverPadding( - padding: EdgeInsets.only( - bottom: scrubberBottomPadding, - ), - ), + const SliverPadding(padding: EdgeInsets.only(bottom: scrubberBottomPadding)), ], ), ), if (!isSelectionMode) ...[ Consumer( builder: (_, consumerRef, child) { - final isMultiSelectEnabled = consumerRef.watch( - multiSelectProvider.select( - (s) => s.isEnabled, - ), - ); + final isMultiSelectEnabled = consumerRef.watch(multiSelectProvider.select((s) => s.isEnabled)); if (isMultiSelectEnabled) { return child!; } return const SizedBox.shrink(); }, - child: const Positioned( - top: 60, - left: 25, - child: _MultiSelectStatusButton(), - ), + child: const Positioned(top: 60, left: 25, child: _MultiSelectStatusButton()), ), if (widget.bottomSheet != null) Consumer( builder: (_, consumerRef, child) { - final isMultiSelectEnabled = consumerRef.watch( - multiSelectProvider.select( - (s) => s.isEnabled, - ), - ); + final isMultiSelectEnabled = consumerRef.watch(multiSelectProvider.select((s) => s.isEnabled)); if (isMultiSelectEnabled) { return child!; @@ -267,22 +235,14 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { class _SliverSegmentedList extends SliverMultiBoxAdaptorWidget { final List _segments; - const _SliverSegmentedList({ - required List segments, - required super.delegate, - }) : _segments = segments; + const _SliverSegmentedList({required List segments, required super.delegate}) : _segments = segments; @override - _RenderSliverTimelineBoxAdaptor createRenderObject(BuildContext context) => _RenderSliverTimelineBoxAdaptor( - childManager: context as SliverMultiBoxAdaptorElement, - segments: _segments, - ); + _RenderSliverTimelineBoxAdaptor createRenderObject(BuildContext context) => + _RenderSliverTimelineBoxAdaptor(childManager: context as SliverMultiBoxAdaptorElement, segments: _segments); @override - void updateRenderObject( - BuildContext context, - _RenderSliverTimelineBoxAdaptor renderObject, - ) { + void updateRenderObject(BuildContext context, _RenderSliverTimelineBoxAdaptor renderObject) { renderObject.segments = _segments; } } @@ -299,10 +259,8 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { markNeedsLayout(); } - _RenderSliverTimelineBoxAdaptor({ - required super.childManager, - required List segments, - }) : _segments = segments; + _RenderSliverTimelineBoxAdaptor({required super.childManager, required List segments}) + : _segments = segments; int getMinChildIndexForScrollOffset(double offset) => _segments.findByOffset(offset)?.getMinChildIndexForScrollOffset(offset) ?? 0; @@ -335,16 +293,18 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { final int firstRequiredChildIndex = getMinChildIndexForScrollOffset(scrollOffset); // Find the index of the last child that should be visible or in the trailing cache area. - final int? lastRequiredChildIndex = - targetScrollOffset.isFinite ? getMaxChildIndexForScrollOffset(targetScrollOffset) : null; + final int? lastRequiredChildIndex = targetScrollOffset.isFinite + ? getMaxChildIndexForScrollOffset(targetScrollOffset) + : null; // Remove children that are no longer visible or within the cache area. if (firstChild == null) { collectGarbage(0, 0); } else { final int leadingChildrenToRemove = calculateLeadingGarbage(firstIndex: firstRequiredChildIndex); - final int trailingChildrenToRemove = - lastRequiredChildIndex == null ? 0 : calculateTrailingGarbage(lastIndex: lastRequiredChildIndex); + final int trailingChildrenToRemove = lastRequiredChildIndex == null + ? 0 + : calculateTrailingGarbage(lastIndex: lastRequiredChildIndex); collectGarbage(leadingChildrenToRemove, trailingChildrenToRemove); } @@ -352,10 +312,7 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { // try to add the first child needed for the current scroll offset. if (firstChild == null) { final double firstChildLayoutOffset = indexToLayoutOffset(firstRequiredChildIndex); - final bool childAdded = addInitialChild( - index: firstRequiredChildIndex, - layoutOffset: firstChildLayoutOffset, - ); + final bool childAdded = addInitialChild(index: firstRequiredChildIndex, layoutOffset: firstChildLayoutOffset); if (!childAdded) { // There are either no children, or we are past the end of all our children. @@ -408,16 +365,15 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { // until we reach the [lastRequiredChildIndex] or run out of children. double calculatedMaxScrollOffset = double.infinity; - for (int currentIndex = indexOf(mostRecentlyLaidOutChild!) + 1; - lastRequiredChildIndex == null || currentIndex <= lastRequiredChildIndex; - ++currentIndex) { + for ( + int currentIndex = indexOf(mostRecentlyLaidOutChild!) + 1; + lastRequiredChildIndex == null || currentIndex <= lastRequiredChildIndex; + ++currentIndex + ) { RenderBox? child = childAfter(mostRecentlyLaidOutChild!); if (child == null || indexOf(child) != currentIndex) { - child = insertAndLayoutChild( - childConstraints, - after: mostRecentlyLaidOutChild, - ); + child = insertAndLayoutChild(childConstraints, after: mostRecentlyLaidOutChild); if (child == null) { final Segment? segment = _segments.findByIndex(currentIndex) ?? _segments.lastOrNull; calculatedMaxScrollOffset = segment?.indexToLayoutOffset(currentIndex) ?? computeMaxScrollOffset(); @@ -443,30 +399,18 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { ); assert(debugAssertChildListIsNonEmptyAndContiguous()); assert(indexOf(firstChild!) == firstRequiredChildIndex); - assert( - lastRequiredChildIndex == null || lastLaidOutChildIndex <= lastRequiredChildIndex, - ); + assert(lastRequiredChildIndex == null || lastLaidOutChildIndex <= lastRequiredChildIndex); - calculatedMaxScrollOffset = math.min( - calculatedMaxScrollOffset, - estimateMaxScrollOffset(), - ); + calculatedMaxScrollOffset = math.min(calculatedMaxScrollOffset, estimateMaxScrollOffset()); - final double paintExtent = calculatePaintOffset( - constraints, - from: leadingScrollOffset, - to: trailingScrollOffset, - ); + final double paintExtent = calculatePaintOffset(constraints, from: leadingScrollOffset, to: trailingScrollOffset); - final double cacheExtent = calculateCacheOffset( - constraints, - from: leadingScrollOffset, - to: trailingScrollOffset, - ); + final double cacheExtent = calculateCacheOffset(constraints, from: leadingScrollOffset, to: trailingScrollOffset); final double targetEndScrollOffsetForPaint = constraints.scrollOffset + constraints.remainingPaintExtent; - final int? targetLastIndexForPaint = - targetEndScrollOffsetForPaint.isFinite ? getMaxChildIndexForScrollOffset(targetEndScrollOffsetForPaint) : null; + final int? targetLastIndexForPaint = targetEndScrollOffsetForPaint.isFinite + ? getMaxChildIndexForScrollOffset(targetEndScrollOffsetForPaint) + : null; final maxPaintExtent = math.max(paintExtent, calculatedMaxScrollOffset); @@ -477,7 +421,8 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { // Indicates if there's content scrolled off-screen. // This is true if the last child needed for painting is actually laid out, // or if the first child is partially visible. - hasVisualOverflow: (targetLastIndexForPaint != null && lastLaidOutChildIndex >= targetLastIndexForPaint) || + hasVisualOverflow: + (targetLastIndexForPaint != null && lastLaidOutChildIndex >= targetLastIndexForPaint) || constraints.scrollOffset > 0.0, cacheExtent: cacheExtent, ); @@ -500,16 +445,10 @@ class _MultiSelectStatusButton extends ConsumerWidget { final selectCount = ref.watch(multiSelectProvider.select((s) => s.selectedAssets.length)); return ElevatedButton.icon( onPressed: () => ref.read(multiSelectProvider.notifier).reset(), - icon: Icon( - Icons.close_rounded, - color: context.colorScheme.onPrimary, - ), + icon: Icon(Icons.close_rounded, color: context.colorScheme.onPrimary), label: Text( selectCount.toString(), - style: context.textTheme.titleMedium?.copyWith( - height: 2.5, - color: context.colorScheme.onPrimary, - ), + style: context.textTheme.titleMedium?.copyWith(height: 2.5, color: context.colorScheme.onPrimary), ), ); } diff --git a/mobile/lib/providers/activity.provider.dart b/mobile/lib/providers/activity.provider.dart index 38b8caaed9..a867a5a281 100644 --- a/mobile/lib/providers/activity.provider.dart +++ b/mobile/lib/providers/activity.provider.dart @@ -36,12 +36,9 @@ class AlbumActivity extends _$AlbumActivity { } Future addComment(String comment) async { - final activity = await ref.watch(activityServiceProvider).addActivity( - albumId, - ActivityType.comment, - assetId: assetId, - comment: comment, - ); + final activity = await ref + .watch(activityServiceProvider) + .addActivity(albumId, ActivityType.comment, assetId: assetId, comment: comment); if (activity.hasValue) { final activities = state.valueOrNull ?? []; diff --git a/mobile/lib/providers/activity.provider.g.dart b/mobile/lib/providers/activity.provider.g.dart index af574b991a..dc927795f8 100644 --- a/mobile/lib/providers/activity.provider.g.dart +++ b/mobile/lib/providers/activity.provider.g.dart @@ -34,10 +34,7 @@ abstract class _$AlbumActivity late final String albumId; late final String? assetId; - FutureOr> build( - String albumId, [ - String? assetId, - ]); + FutureOr> build(String albumId, [String? assetId]); } /// Maintains the current list of all activities for @@ -58,24 +55,15 @@ class AlbumActivityFamily extends Family>> { /// Maintains the current list of all activities for /// /// Copied from [AlbumActivity]. - AlbumActivityProvider call( - String albumId, [ - String? assetId, - ]) { - return AlbumActivityProvider( - albumId, - assetId, - ); + AlbumActivityProvider call(String albumId, [String? assetId]) { + return AlbumActivityProvider(albumId, assetId); } @override AlbumActivityProvider getProviderOverride( covariant AlbumActivityProvider provider, ) { - return call( - provider.albumId, - provider.assetId, - ); + return call(provider.albumId, provider.assetId); } static const Iterable? _dependencies = null; @@ -96,30 +84,28 @@ class AlbumActivityFamily extends Family>> { /// Maintains the current list of all activities for /// /// Copied from [AlbumActivity]. -class AlbumActivityProvider extends AutoDisposeAsyncNotifierProviderImpl< - AlbumActivity, List> { +class AlbumActivityProvider + extends + AutoDisposeAsyncNotifierProviderImpl> { /// Maintains the current list of all activities for /// /// Copied from [AlbumActivity]. - AlbumActivityProvider( - String albumId, [ - String? assetId, - ]) : this._internal( - () => AlbumActivity() - ..albumId = albumId - ..assetId = assetId, - from: albumActivityProvider, - name: r'albumActivityProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$albumActivityHash, - dependencies: AlbumActivityFamily._dependencies, - allTransitiveDependencies: - AlbumActivityFamily._allTransitiveDependencies, - albumId: albumId, - assetId: assetId, - ); + AlbumActivityProvider(String albumId, [String? assetId]) + : this._internal( + () => AlbumActivity() + ..albumId = albumId + ..assetId = assetId, + from: albumActivityProvider, + name: r'albumActivityProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$albumActivityHash, + dependencies: AlbumActivityFamily._dependencies, + allTransitiveDependencies: + AlbumActivityFamily._allTransitiveDependencies, + albumId: albumId, + assetId: assetId, + ); AlbumActivityProvider._internal( super._createNotifier, { @@ -136,13 +122,8 @@ class AlbumActivityProvider extends AutoDisposeAsyncNotifierProviderImpl< final String? assetId; @override - FutureOr> runNotifierBuild( - covariant AlbumActivity notifier, - ) { - return notifier.build( - albumId, - assetId, - ); + FutureOr> runNotifierBuild(covariant AlbumActivity notifier) { + return notifier.build(albumId, assetId); } @override @@ -166,7 +147,7 @@ class AlbumActivityProvider extends AutoDisposeAsyncNotifierProviderImpl< @override AutoDisposeAsyncNotifierProviderElement> - createElement() { + createElement() { return _AlbumActivityProviderElement(this); } @@ -198,8 +179,9 @@ mixin AlbumActivityRef on AutoDisposeAsyncNotifierProviderRef> { } class _AlbumActivityProviderElement - extends AutoDisposeAsyncNotifierProviderElement> with AlbumActivityRef { + extends + AutoDisposeAsyncNotifierProviderElement> + with AlbumActivityRef { _AlbumActivityProviderElement(super.provider); @override @@ -207,5 +189,6 @@ class _AlbumActivityProviderElement @override String? get assetId => (origin as AlbumActivityProvider).assetId; } + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/mobile/lib/providers/activity_statistics.provider.g.dart b/mobile/lib/providers/activity_statistics.provider.g.dart index d2de32c0aa..83d887f6dc 100644 --- a/mobile/lib/providers/activity_statistics.provider.g.dart +++ b/mobile/lib/providers/activity_statistics.provider.g.dart @@ -34,10 +34,7 @@ abstract class _$ActivityStatistics extends BuildlessAutoDisposeNotifier { late final String albumId; late final String? assetId; - int build( - String albumId, [ - String? assetId, - ]); + int build(String albumId, [String? assetId]); } /// Maintains the current number of comments by @@ -58,24 +55,15 @@ class ActivityStatisticsFamily extends Family { /// Maintains the current number of comments by /// /// Copied from [ActivityStatistics]. - ActivityStatisticsProvider call( - String albumId, [ - String? assetId, - ]) { - return ActivityStatisticsProvider( - albumId, - assetId, - ); + ActivityStatisticsProvider call(String albumId, [String? assetId]) { + return ActivityStatisticsProvider(albumId, assetId); } @override ActivityStatisticsProvider getProviderOverride( covariant ActivityStatisticsProvider provider, ) { - return call( - provider.albumId, - provider.assetId, - ); + return call(provider.albumId, provider.assetId); } static const Iterable? _dependencies = null; @@ -101,25 +89,22 @@ class ActivityStatisticsProvider /// Maintains the current number of comments by /// /// Copied from [ActivityStatistics]. - ActivityStatisticsProvider( - String albumId, [ - String? assetId, - ]) : this._internal( - () => ActivityStatistics() - ..albumId = albumId - ..assetId = assetId, - from: activityStatisticsProvider, - name: r'activityStatisticsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$activityStatisticsHash, - dependencies: ActivityStatisticsFamily._dependencies, - allTransitiveDependencies: - ActivityStatisticsFamily._allTransitiveDependencies, - albumId: albumId, - assetId: assetId, - ); + ActivityStatisticsProvider(String albumId, [String? assetId]) + : this._internal( + () => ActivityStatistics() + ..albumId = albumId + ..assetId = assetId, + from: activityStatisticsProvider, + name: r'activityStatisticsProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$activityStatisticsHash, + dependencies: ActivityStatisticsFamily._dependencies, + allTransitiveDependencies: + ActivityStatisticsFamily._allTransitiveDependencies, + albumId: albumId, + assetId: assetId, + ); ActivityStatisticsProvider._internal( super._createNotifier, { @@ -136,13 +121,8 @@ class ActivityStatisticsProvider final String? assetId; @override - int runNotifierBuild( - covariant ActivityStatistics notifier, - ) { - return notifier.build( - albumId, - assetId, - ); + int runNotifierBuild(covariant ActivityStatistics notifier) { + return notifier.build(albumId, assetId); } @override @@ -206,5 +186,6 @@ class _ActivityStatisticsProviderElement @override String? get assetId => (origin as ActivityStatisticsProvider).assetId; } + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/mobile/lib/providers/album/album.provider.dart b/mobile/lib/providers/album/album.provider.dart index ae565d20d8..35634d77c8 100644 --- a/mobile/lib/providers/album/album.provider.dart +++ b/mobile/lib/providers/album/album.provider.dart @@ -35,31 +35,15 @@ class AlbumNotifier extends StateNotifier> { Future deleteAlbum(Album album) => albumService.deleteAlbum(album); - Future createAlbum( - String albumTitle, - Set assets, - ) => - albumService.createAlbum(albumTitle, assets, []); + Future createAlbum(String albumTitle, Set assets) => albumService.createAlbum(albumTitle, assets, []); - Future getAlbumByName( - String albumName, { - bool? remote, - bool? shared, - bool? owner, - }) => - albumService.getAlbumByName( - albumName, - remote: remote, - shared: shared, - owner: owner, - ); + Future getAlbumByName(String albumName, {bool? remote, bool? shared, bool? owner}) => + albumService.getAlbumByName(albumName, remote: remote, shared: shared, owner: owner); /// Create an album on the server with the same name as the selected album for backup /// First this will check if the album already exists on the server with name /// If it does not exist, it will create the album on the server - Future createSyncAlbum( - String albumName, - ) async { + Future createSyncAlbum(String albumName) async { final album = await getAlbumByName(albumName, remote: true, owner: true); if (album != null) { return; @@ -105,10 +89,7 @@ class AlbumNotifier extends StateNotifier> { return await albumService.removeAsset(album, assets); } - Future setActivitystatus( - Album album, - bool enabled, - ) { + Future setActivitystatus(Album album, bool enabled) { return albumService.setActivityStatus(album, enabled); } @@ -126,10 +107,7 @@ class AlbumNotifier extends StateNotifier> { } final albumProvider = StateNotifierProvider.autoDispose>((ref) { - return AlbumNotifier( - ref.watch(albumServiceProvider), - ref, - ); + return AlbumNotifier(ref.watch(albumServiceProvider), ref); }); final albumWatcher = StreamProvider.autoDispose.family((ref, id) async* { diff --git a/mobile/lib/providers/album/album_sort_by_options.provider.dart b/mobile/lib/providers/album/album_sort_by_options.provider.dart index 6e1669faef..3dd09f1282 100644 --- a/mobile/lib/providers/album/album_sort_by_options.provider.dart +++ b/mobile/lib/providers/album/album_sort_by_options.provider.dart @@ -75,22 +75,10 @@ class _AlbumSortHandlers { enum AlbumSortMode { title(1, "library_page_sort_title", _AlbumSortHandlers.title), assetCount(4, "library_page_sort_asset_count", _AlbumSortHandlers.assetCount), - lastModified( - 3, - "library_page_sort_last_modified", - _AlbumSortHandlers.lastModified, - ), + lastModified(3, "library_page_sort_last_modified", _AlbumSortHandlers.lastModified), created(0, "library_page_sort_created", _AlbumSortHandlers.created), - mostRecent( - 2, - "sort_recent", - _AlbumSortHandlers.mostRecent, - ), - mostOldest( - 5, - "sort_oldest", - _AlbumSortHandlers.mostOldest, - ); + mostRecent(2, "sort_recent", _AlbumSortHandlers.mostRecent), + mostOldest(5, "sort_oldest", _AlbumSortHandlers.mostOldest); final int storeIndex; final String label; @@ -104,18 +92,12 @@ class AlbumSortByOptions extends _$AlbumSortByOptions { @override AlbumSortMode build() { final sortOpt = ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.selectedAlbumSortOrder); - return AlbumSortMode.values.firstWhere( - (e) => e.storeIndex == sortOpt, - orElse: () => AlbumSortMode.title, - ); + return AlbumSortMode.values.firstWhere((e) => e.storeIndex == sortOpt, orElse: () => AlbumSortMode.title); } void changeSortMode(AlbumSortMode sortOption) { state = sortOption; - ref.watch(appSettingsServiceProvider).setSetting( - AppSettingsEnum.selectedAlbumSortOrder, - sortOption.storeIndex, - ); + ref.watch(appSettingsServiceProvider).setSetting(AppSettingsEnum.selectedAlbumSortOrder, sortOption.storeIndex); } } diff --git a/mobile/lib/providers/album/album_sort_by_options.provider.g.dart b/mobile/lib/providers/album/album_sort_by_options.provider.g.dart index ba20e7eb66..750329c9d5 100644 --- a/mobile/lib/providers/album/album_sort_by_options.provider.g.dart +++ b/mobile/lib/providers/album/album_sort_by_options.provider.g.dart @@ -13,14 +13,14 @@ String _$albumSortByOptionsHash() => @ProviderFor(AlbumSortByOptions) final albumSortByOptionsProvider = AutoDisposeNotifierProvider.internal( - AlbumSortByOptions.new, - name: r'albumSortByOptionsProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$albumSortByOptionsHash, - dependencies: null, - allTransitiveDependencies: null, -); + AlbumSortByOptions.new, + name: r'albumSortByOptionsProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$albumSortByOptionsHash, + dependencies: null, + allTransitiveDependencies: null, + ); typedef _$AlbumSortByOptions = AutoDisposeNotifier; String _$albumSortOrderHash() => r'573dea45b4519e69386fc7104c72522e35713440'; @@ -29,14 +29,14 @@ String _$albumSortOrderHash() => r'573dea45b4519e69386fc7104c72522e35713440'; @ProviderFor(AlbumSortOrder) final albumSortOrderProvider = AutoDisposeNotifierProvider.internal( - AlbumSortOrder.new, - name: r'albumSortOrderProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$albumSortOrderHash, - dependencies: null, - allTransitiveDependencies: null, -); + AlbumSortOrder.new, + name: r'albumSortOrderProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$albumSortOrderHash, + dependencies: null, + allTransitiveDependencies: null, + ); typedef _$AlbumSortOrder = AutoDisposeNotifier; // ignore_for_file: type=lint diff --git a/mobile/lib/providers/album/album_title.provider.dart b/mobile/lib/providers/album/album_title.provider.dart index 126b3499a3..bf812a01d8 100644 --- a/mobile/lib/providers/album/album_title.provider.dart +++ b/mobile/lib/providers/album/album_title.provider.dart @@ -12,6 +12,4 @@ class AlbumTitleNotifier extends StateNotifier { } } -final albumTitleProvider = StateNotifierProvider( - (ref) => AlbumTitleNotifier(), -); +final albumTitleProvider = StateNotifierProvider((ref) => AlbumTitleNotifier()); diff --git a/mobile/lib/providers/album/album_viewer.provider.dart b/mobile/lib/providers/album/album_viewer.provider.dart index afae154cd7..f4ce047464 100644 --- a/mobile/lib/providers/album/album_viewer.provider.dart +++ b/mobile/lib/providers/album/album_viewer.provider.dart @@ -5,13 +5,7 @@ import 'package:immich_mobile/services/album.service.dart'; class AlbumViewerNotifier extends StateNotifier { AlbumViewerNotifier(this.ref) - : super( - const AlbumViewerPageState( - editTitleText: "", - isEditAlbum: false, - editDescriptionText: "", - ), - ); + : super(const AlbumViewerPageState(editTitleText: "", isEditAlbum: false, editDescriptionText: "")); final Ref ref; @@ -40,17 +34,10 @@ class AlbumViewerNotifier extends StateNotifier { } void resetState() { - state = state.copyWith( - editTitleText: "", - isEditAlbum: false, - editDescriptionText: "", - ); + state = state.copyWith(editTitleText: "", isEditAlbum: false, editDescriptionText: ""); } - Future changeAlbumTitle( - Album album, - String newAlbumTitle, - ) async { + Future changeAlbumTitle(Album album, String newAlbumTitle) async { AlbumService service = ref.watch(albumServiceProvider); bool isSuccess = await service.changeTitleAlbum(album, newAlbumTitle); @@ -65,16 +52,10 @@ class AlbumViewerNotifier extends StateNotifier { return false; } - Future changeAlbumDescription( - Album album, - String newAlbumDescription, - ) async { + Future changeAlbumDescription(Album album, String newAlbumDescription) async { AlbumService service = ref.watch(albumServiceProvider); - bool isSuccess = await service.changeDescriptionAlbum( - album, - newAlbumDescription, - ); + bool isSuccess = await service.changeDescriptionAlbum(album, newAlbumDescription); if (isSuccess) { state = state.copyWith(editDescriptionText: "", isEditAlbum: false); diff --git a/mobile/lib/providers/album/current_album.provider.g.dart b/mobile/lib/providers/album/current_album.provider.g.dart index 60ebe3e333..b6d079231f 100644 --- a/mobile/lib/providers/album/current_album.provider.g.dart +++ b/mobile/lib/providers/album/current_album.provider.g.dart @@ -12,13 +12,14 @@ String _$currentAlbumHash() => r'61f00273d6b69da45add1532cc3d3a076ee55110'; @ProviderFor(CurrentAlbum) final currentAlbumProvider = AutoDisposeNotifierProvider.internal( - CurrentAlbum.new, - name: r'currentAlbumProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$currentAlbumHash, - dependencies: null, - allTransitiveDependencies: null, -); + CurrentAlbum.new, + name: r'currentAlbumProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$currentAlbumHash, + dependencies: null, + allTransitiveDependencies: null, + ); typedef _$CurrentAlbum = AutoDisposeNotifier; // ignore_for_file: type=lint diff --git a/mobile/lib/providers/api.provider.g.dart b/mobile/lib/providers/api.provider.g.dart index 8a6f514ce6..ee1781c24c 100644 --- a/mobile/lib/providers/api.provider.g.dart +++ b/mobile/lib/providers/api.provider.g.dart @@ -13,8 +13,9 @@ String _$apiServiceHash() => r'187a7de59b064fab1104c23717f18ce0ae3e426c'; final apiServiceProvider = Provider.internal( apiService, name: r'apiServiceProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$apiServiceHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$apiServiceHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/mobile/lib/providers/app_life_cycle.provider.dart b/mobile/lib/providers/app_life_cycle.provider.dart index 07778ed2ed..31342bf23e 100644 --- a/mobile/lib/providers/app_life_cycle.provider.dart +++ b/mobile/lib/providers/app_life_cycle.provider.dart @@ -27,14 +27,7 @@ import 'package:isar/isar.dart'; import 'package:logging/logging.dart'; import 'package:permission_handler/permission_handler.dart'; -enum AppLifeCycleEnum { - active, - inactive, - paused, - resumed, - detached, - hidden, -} +enum AppLifeCycleEnum { active, inactive, paused, resumed, detached, hidden } class AppLifeCycleNotifier extends StateNotifier { final Ref _ref; @@ -93,15 +86,13 @@ class AppLifeCycleNotifier extends StateNotifier { // Ensure proper cleanup before starting new background tasks try { await Future.wait([ - backgroundManager.syncLocal().then( - (_) { - Logger("AppLifeCycleNotifier").fine("Hashing assets after syncLocal"); - // Check if app is still active before hashing - if (state == AppLifeCycleEnum.resumed) { - backgroundManager.hashAssets(); - } - }, - ), + backgroundManager.syncLocal().then((_) { + Logger("AppLifeCycleNotifier").fine("Hashing assets after syncLocal"); + // Check if app is still active before hashing + if (state == AppLifeCycleEnum.resumed) { + backgroundManager.hashAssets(); + } + }), backgroundManager.syncRemote(), ]).then((_) async { final isEnableBackup = _ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup); @@ -116,11 +107,7 @@ class AppLifeCycleNotifier extends StateNotifier { } }); } catch (e, stackTrace) { - Logger("AppLifeCycleNotifier").severe( - "Error during background sync", - e, - stackTrace, - ); + Logger("AppLifeCycleNotifier").severe("Error during background sync", e, stackTrace); } } diff --git a/mobile/lib/providers/asset.provider.dart b/mobile/lib/providers/asset.provider.dart index 5098dd2af4..9c8b28e6cf 100644 --- a/mobile/lib/providers/asset.provider.dart +++ b/mobile/lib/providers/asset.provider.dart @@ -68,9 +68,7 @@ class AssetNotifier extends StateNotifier { } final bool newRemote = await _assetService.refreshRemoteAssets(); final bool newLocal = await _albumService.refreshDeviceAlbums(); - debugPrint( - "changedUsers: $changedUsers, newRemote: $newRemote, newLocal: $newLocal", - ); + debugPrint("changedUsers: $changedUsers, newRemote: $newRemote, newLocal: $newLocal"); if (newRemote) { _ref.invalidate(memoryFutureProvider); } @@ -122,17 +120,11 @@ class AssetNotifier extends StateNotifier { /// Delete remote asset only /// /// Default behavior is trashing the asset - Future deleteRemoteAssets( - Iterable deleteAssets, { - bool shouldDeletePermanently = false, - }) async { + Future deleteRemoteAssets(Iterable deleteAssets, {bool shouldDeletePermanently = false}) async { _deleteInProgress = true; state = true; try { - await _assetService.deleteRemoteAssets( - deleteAssets, - shouldDeletePermanently: shouldDeletePermanently, - ); + await _assetService.deleteRemoteAssets(deleteAssets, shouldDeletePermanently: shouldDeletePermanently); return true; } catch (error) { log.severe("Failed to delete remote assets", error); @@ -143,17 +135,11 @@ class AssetNotifier extends StateNotifier { } } - Future deleteAssets( - Iterable deleteAssets, { - bool force = false, - }) async { + Future deleteAssets(Iterable deleteAssets, {bool force = false}) async { _deleteInProgress = true; state = true; try { - await _assetService.deleteAssets( - deleteAssets, - shouldDeletePermanently: force, - ); + await _assetService.deleteAssets(deleteAssets, shouldDeletePermanently: force); return true; } catch (error) { log.severe("Failed to delete assets", error); @@ -174,10 +160,7 @@ class AssetNotifier extends StateNotifier { return _assetService.changeArchiveStatus(assets, status); } - Future setLockedView( - List selection, - AssetVisibilityEnum visibility, - ) { + Future setLockedView(List selection, AssetVisibilityEnum visibility) { return _assetService.setVisibility(selection, visibility); } } diff --git a/mobile/lib/providers/asset_viewer/asset_people.provider.g.dart b/mobile/lib/providers/asset_viewer/asset_people.provider.g.dart index ebe8a14186..031a70e0d9 100644 --- a/mobile/lib/providers/asset_viewer/asset_people.provider.g.dart +++ b/mobile/lib/providers/asset_viewer/asset_people.provider.g.dart @@ -30,13 +30,12 @@ class _SystemHash { } } -abstract class _$AssetPeopleNotifier extends BuildlessAutoDisposeAsyncNotifier< - List> { +abstract class _$AssetPeopleNotifier + extends + BuildlessAutoDisposeAsyncNotifier> { late final Asset asset; - FutureOr> build( - Asset asset, - ); + FutureOr> build(Asset asset); } /// Maintains the list of people for an asset. @@ -58,21 +57,15 @@ class AssetPeopleNotifierFamily /// Maintains the list of people for an asset. /// /// Copied from [AssetPeopleNotifier]. - AssetPeopleNotifierProvider call( - Asset asset, - ) { - return AssetPeopleNotifierProvider( - asset, - ); + AssetPeopleNotifierProvider call(Asset asset) { + return AssetPeopleNotifierProvider(asset); } @override AssetPeopleNotifierProvider getProviderOverride( covariant AssetPeopleNotifierProvider provider, ) { - return call( - provider.asset, - ); + return call(provider.asset); } static const Iterable? _dependencies = null; @@ -93,26 +86,28 @@ class AssetPeopleNotifierFamily /// Maintains the list of people for an asset. /// /// Copied from [AssetPeopleNotifier]. -class AssetPeopleNotifierProvider extends AutoDisposeAsyncNotifierProviderImpl< - AssetPeopleNotifier, List> { +class AssetPeopleNotifierProvider + extends + AutoDisposeAsyncNotifierProviderImpl< + AssetPeopleNotifier, + List + > { /// Maintains the list of people for an asset. /// /// Copied from [AssetPeopleNotifier]. - AssetPeopleNotifierProvider( - Asset asset, - ) : this._internal( - () => AssetPeopleNotifier()..asset = asset, - from: assetPeopleNotifierProvider, - name: r'assetPeopleNotifierProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$assetPeopleNotifierHash, - dependencies: AssetPeopleNotifierFamily._dependencies, - allTransitiveDependencies: - AssetPeopleNotifierFamily._allTransitiveDependencies, - asset: asset, - ); + AssetPeopleNotifierProvider(Asset asset) + : this._internal( + () => AssetPeopleNotifier()..asset = asset, + from: assetPeopleNotifierProvider, + name: r'assetPeopleNotifierProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$assetPeopleNotifierHash, + dependencies: AssetPeopleNotifierFamily._dependencies, + allTransitiveDependencies: + AssetPeopleNotifierFamily._allTransitiveDependencies, + asset: asset, + ); AssetPeopleNotifierProvider._internal( super._createNotifier, { @@ -130,9 +125,7 @@ class AssetPeopleNotifierProvider extends AutoDisposeAsyncNotifierProviderImpl< FutureOr> runNotifierBuild( covariant AssetPeopleNotifier notifier, ) { - return notifier.build( - asset, - ); + return notifier.build(asset); } @override @@ -152,8 +145,11 @@ class AssetPeopleNotifierProvider extends AutoDisposeAsyncNotifierProviderImpl< } @override - AutoDisposeAsyncNotifierProviderElement> createElement() { + AutoDisposeAsyncNotifierProviderElement< + AssetPeopleNotifier, + List + > + createElement() { return _AssetPeopleNotifierProviderElement(this); } @@ -180,12 +176,17 @@ mixin AssetPeopleNotifierRef } class _AssetPeopleNotifierProviderElement - extends AutoDisposeAsyncNotifierProviderElement> with AssetPeopleNotifierRef { + extends + AutoDisposeAsyncNotifierProviderElement< + AssetPeopleNotifier, + List + > + with AssetPeopleNotifierRef { _AssetPeopleNotifierProviderElement(super.provider); @override Asset get asset => (origin as AssetPeopleNotifierProvider).asset; } + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/mobile/lib/providers/asset_viewer/current_asset.provider.g.dart b/mobile/lib/providers/asset_viewer/current_asset.provider.g.dart index 53b02c2ace..e0d8d47d3a 100644 --- a/mobile/lib/providers/asset_viewer/current_asset.provider.g.dart +++ b/mobile/lib/providers/asset_viewer/current_asset.provider.g.dart @@ -12,13 +12,14 @@ String _$currentAssetHash() => r'2def10ea594152c984ae2974d687ab6856d7bdd0'; @ProviderFor(CurrentAsset) final currentAssetProvider = AutoDisposeNotifierProvider.internal( - CurrentAsset.new, - name: r'currentAssetProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$currentAssetHash, - dependencies: null, - allTransitiveDependencies: null, -); + CurrentAsset.new, + name: r'currentAssetProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$currentAssetHash, + dependencies: null, + allTransitiveDependencies: null, + ); typedef _$CurrentAsset = AutoDisposeNotifier; // ignore_for_file: type=lint diff --git a/mobile/lib/providers/asset_viewer/download.provider.dart b/mobile/lib/providers/asset_viewer/download.provider.dart index b35a4546bb..36b935abe7 100644 --- a/mobile/lib/providers/asset_viewer/download.provider.dart +++ b/mobile/lib/providers/asset_viewer/download.provider.dart @@ -18,17 +18,14 @@ class DownloadStateNotifier extends StateNotifier { final ShareService _shareService; final AlbumService _albumService; - DownloadStateNotifier( - this._downloadService, - this._shareService, - this._albumService, - ) : super( - const DownloadState( - downloadStatus: TaskStatus.complete, - showProgress: false, - taskProgress: {}, - ), - ) { + DownloadStateNotifier(this._downloadService, this._shareService, this._albumService) + : super( + const DownloadState( + downloadStatus: TaskStatus.complete, + showProgress: false, + taskProgress: {}, + ), + ) { _downloadService.onImageDownloadStatus = _downloadImageCallback; _downloadService.onVideoDownloadStatus = _downloadVideoCallback; _downloadService.onLivePhotoDownloadStatus = _downloadLivePhotoCallback; @@ -131,9 +128,7 @@ class DownloadStateNotifier extends StateNotifier { ); if (state.taskProgress.isEmpty) { - state = state.copyWith( - showProgress: false, - ); + state = state.copyWith(showProgress: false); } _albumService.refreshDeviceAlbums(); }); @@ -159,9 +154,7 @@ class DownloadStateNotifier extends StateNotifier { } if (state.taskProgress.isEmpty) { - state = state.copyWith( - showProgress: false, - ); + state = state.copyWith(showProgress: false); } } @@ -169,19 +162,17 @@ class DownloadStateNotifier extends StateNotifier { showDialog( context: context, builder: (BuildContext buildContext) { - _shareService.shareAsset(asset, context).then( - (bool status) { - if (!status) { - ImmichToast.show( - context: context, - msg: 'image_viewer_page_state_provider_share_error'.tr(), - toastType: ToastType.error, - gravity: ToastGravity.BOTTOM, - ); - } - buildContext.pop(); - }, - ); + _shareService.shareAsset(asset, context).then((bool status) { + if (!status) { + ImmichToast.show( + context: context, + msg: 'image_viewer_page_state_provider_share_error'.tr(), + toastType: ToastType.error, + gravity: ToastGravity.BOTTOM, + ); + } + buildContext.pop(); + }); return const ShareDialog(); }, barrierDismissible: false, @@ -192,8 +183,8 @@ class DownloadStateNotifier extends StateNotifier { final downloadStateProvider = StateNotifierProvider( ((ref) => DownloadStateNotifier( - ref.watch(downloadServiceProvider), - ref.watch(shareServiceProvider), - ref.watch(albumServiceProvider), - )), + ref.watch(downloadServiceProvider), + ref.watch(shareServiceProvider), + ref.watch(albumServiceProvider), + )), ); diff --git a/mobile/lib/providers/asset_viewer/share_intent_upload.provider.dart b/mobile/lib/providers/asset_viewer/share_intent_upload.provider.dart index 97730b5935..7b2ab5b27a 100644 --- a/mobile/lib/providers/asset_viewer/share_intent_upload.provider.dart +++ b/mobile/lib/providers/asset_viewer/share_intent_upload.provider.dart @@ -15,10 +15,10 @@ import 'package:path/path.dart'; final shareIntentUploadProvider = StateNotifierProvider>( ((ref) => ShareIntentUploadStateNotifier( - ref.watch(appRouterProvider), - ref.watch(uploadServiceProvider), - ref.watch(shareIntentServiceProvider), - )), + ref.watch(appRouterProvider), + ref.watch(uploadServiceProvider), + ref.watch(shareIntentServiceProvider), + )), ); class ShareIntentUploadStateNotifier extends StateNotifier> { @@ -26,11 +26,7 @@ class ShareIntentUploadStateNotifier extends StateNotifier UploadStatus.running, TaskStatus.paused => UploadStatus.paused, TaskStatus.notFound => UploadStatus.notFound, - TaskStatus.waitingToRetry => UploadStatus.waitingToRetry + TaskStatus.waitingToRetry => UploadStatus.waitingToRetry, }; state = [ @@ -106,19 +102,12 @@ class ShareIntentUploadStateNotifier extends StateNotifier upload(File file) async { - final task = await _buildUploadTask( - hash(file.path).toString(), - file, - ); + final task = await _buildUploadTask(hash(file.path).toString(), file); _uploadService.enqueueTasks([task]); } - Future _buildUploadTask( - String id, - File file, { - Map? fields, - }) async { + Future _buildUploadTask(String id, File file, {Map? fields}) async { final serverEndpoint = Store.get(StoreKey.serverEndpoint); final url = Uri.parse('$serverEndpoint/assets').toString(); final headers = ApiService.getRequestHeaders(); diff --git a/mobile/lib/providers/asset_viewer/video_player_controls_provider.dart b/mobile/lib/providers/asset_viewer/video_player_controls_provider.dart index a758d97ad8..3cfc2e2f6f 100644 --- a/mobile/lib/providers/asset_viewer/video_player_controls_provider.dart +++ b/mobile/lib/providers/asset_viewer/video_player_controls_provider.dart @@ -2,11 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart'; class VideoPlaybackControls { - const VideoPlaybackControls({ - required this.position, - required this.pause, - this.restarted = false, - }); + const VideoPlaybackControls({required this.position, required this.pause, this.restarted = false}); final double position; final bool pause; @@ -67,9 +63,9 @@ class VideoPlayerControls extends StateNotifier { void restart() { state = const VideoPlaybackControls(position: 0, pause: false, restarted: true); - ref.read(videoPlaybackValueProvider.notifier).value = ref.read(videoPlaybackValueProvider.notifier).value.copyWith( - state: VideoPlaybackState.playing, - position: Duration.zero, - ); + ref.read(videoPlaybackValueProvider.notifier).value = ref + .read(videoPlaybackValueProvider.notifier) + .value + .copyWith(state: VideoPlaybackState.playing, position: Duration.zero); } } diff --git a/mobile/lib/providers/asset_viewer/video_player_value_provider.dart b/mobile/lib/providers/asset_viewer/video_player_value_provider.dart index 55a381b02b..c478ddd6f5 100644 --- a/mobile/lib/providers/asset_viewer/video_player_value_provider.dart +++ b/mobile/lib/providers/asset_viewer/video_player_value_provider.dart @@ -1,13 +1,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:native_video_player/native_video_player.dart'; -enum VideoPlaybackState { - initializing, - paused, - playing, - buffering, - completed, -} +enum VideoPlaybackState { initializing, paused, playing, buffering, completed } class VideoPlaybackValue { /// The current position of the video @@ -22,16 +16,9 @@ class VideoPlaybackValue { /// The volume of the video final double volume; - const VideoPlaybackValue({ - required this.position, - required this.duration, - required this.state, - required this.volume, - }); + const VideoPlaybackValue({required this.position, required this.duration, required this.state, required this.volume}); - factory VideoPlaybackValue.fromNativeController( - NativeVideoPlayerController controller, - ) { + factory VideoPlaybackValue.fromNativeController(NativeVideoPlayerController controller) { final playbackInfo = controller.playbackInfo; final videoInfo = controller.videoInfo; @@ -53,12 +40,7 @@ class VideoPlaybackValue { ); } - VideoPlaybackValue copyWith({ - Duration? position, - Duration? duration, - VideoPlaybackState? state, - double? volume, - }) { + VideoPlaybackValue copyWith({Duration? position, Duration? duration, VideoPlaybackState? state, double? volume}) { return VideoPlaybackValue( position: position ?? this.position, duration: duration ?? this.duration, @@ -92,22 +74,12 @@ class VideoPlaybackValueState extends StateNotifier { set position(Duration value) { if (state.position == value) return; - state = VideoPlaybackValue( - position: value, - duration: state.duration, - state: state.state, - volume: state.volume, - ); + state = VideoPlaybackValue(position: value, duration: state.duration, state: state.state, volume: state.volume); } set status(VideoPlaybackState value) { if (state.state == value) return; - state = VideoPlaybackValue( - position: state.position, - duration: state.duration, - state: value, - volume: state.volume, - ); + state = VideoPlaybackValue(position: state.position, duration: state.duration, state: value, volume: state.volume); } void reset() { diff --git a/mobile/lib/providers/auth.provider.dart b/mobile/lib/providers/auth.provider.dart index b918eeb215..fc3e08472b 100644 --- a/mobile/lib/providers/auth.provider.dart +++ b/mobile/lib/providers/auth.provider.dart @@ -49,16 +49,16 @@ class AuthNotifier extends StateNotifier { this._secureStorageService, this._widgetService, ) : super( - const AuthState( - deviceId: "", - userId: "", - userEmail: "", - name: '', - profileImagePath: '', - isAdmin: false, - isAuthenticated: false, - ), - ); + const AuthState( + deviceId: "", + userId: "", + userEmail: "", + name: '', + profileImagePath: '', + isAdmin: false, + isAuthenticated: false, + ), + ); Future validateServerUrl(String url) { return _authService.validateServerUrl(url); @@ -118,15 +118,10 @@ class AuthNotifier extends StateNotifier { } } - Future saveAuthInfo({ - required String accessToken, - }) async { + Future saveAuthInfo({required String accessToken}) async { await _apiService.setAccessToken(accessToken); - await _widgetService.writeCredentials( - Store.get(StoreKey.serverEndpoint), - accessToken, - ); + await _widgetService.writeCredentials(Store.get(StoreKey.serverEndpoint), accessToken); // Get the deviceid from the store if it exists, otherwise generate a new one String deviceId = Store.tryGet(StoreKey.deviceId) ?? await FlutterUdid.consistentUdid; @@ -150,21 +145,12 @@ class AuthNotifier extends StateNotifier { _log.severe("Unauthorized access, token likely expired. Logging out."); return false; } - _log.severe( - "Error getting user information from the server [API EXCEPTION]", - stackTrace, - ); + _log.severe("Error getting user information from the server [API EXCEPTION]", stackTrace); } catch (error, stackTrace) { - _log.severe( - "Error getting user information from the server [CATCH ALL]", - error, - stackTrace, - ); + _log.severe("Error getting user information from the server [CATCH ALL]", error, stackTrace); if (kDebugMode) { - debugPrint( - "Error getting user information from the server [CATCH ALL] $error $stackTrace", - ); + debugPrint("Error getting user information from the server [CATCH ALL] $error $stackTrace"); } } diff --git a/mobile/lib/providers/backup/backup.provider.dart b/mobile/lib/providers/backup/backup.provider.dart index 69290bcfc5..76cb383465 100644 --- a/mobile/lib/providers/backup/backup.provider.dart +++ b/mobile/lib/providers/backup/backup.provider.dart @@ -60,43 +60,38 @@ class BackupNotifier extends StateNotifier { this._backupAlbumService, this.ref, ) : super( - BackUpState( - backupProgress: BackUpProgressEnum.idle, - allAssetsInDatabase: const [], - progressInPercentage: 0, - progressInFileSize: "0 B / 0 B", - progressInFileSpeed: 0, - progressInFileSpeeds: const [], - progressInFileSpeedUpdateTime: DateTime.now(), - progressInFileSpeedUpdateSentBytes: 0, - cancelToken: CancellationToken(), - autoBackup: Store.get(StoreKey.autoBackup, false), - backgroundBackup: Store.get(StoreKey.backgroundBackup, false), - backupRequireWifi: Store.get(StoreKey.backupRequireWifi, true), - backupRequireCharging: Store.get(StoreKey.backupRequireCharging, false), - backupTriggerDelay: Store.get(StoreKey.backupTriggerDelay, 5000), - serverInfo: const ServerDiskInfo( - diskAvailable: "0", - diskSize: "0", - diskUse: "0", - diskUsagePercentage: 0, - ), - availableAlbums: const [], - selectedBackupAlbums: const {}, - excludedBackupAlbums: const {}, - allUniqueAssets: const {}, - selectedAlbumsBackupAssetsIds: const {}, - currentUploadAsset: CurrentUploadAsset( - id: '...', - fileCreatedAt: DateTime.parse('2020-10-04'), - fileName: '...', - fileType: '...', - fileSize: 0, - iCloudAsset: false, - ), - iCloudDownloadProgress: 0.0, + BackUpState( + backupProgress: BackUpProgressEnum.idle, + allAssetsInDatabase: const [], + progressInPercentage: 0, + progressInFileSize: "0 B / 0 B", + progressInFileSpeed: 0, + progressInFileSpeeds: const [], + progressInFileSpeedUpdateTime: DateTime.now(), + progressInFileSpeedUpdateSentBytes: 0, + cancelToken: CancellationToken(), + autoBackup: Store.get(StoreKey.autoBackup, false), + backgroundBackup: Store.get(StoreKey.backgroundBackup, false), + backupRequireWifi: Store.get(StoreKey.backupRequireWifi, true), + backupRequireCharging: Store.get(StoreKey.backupRequireCharging, false), + backupTriggerDelay: Store.get(StoreKey.backupTriggerDelay, 5000), + serverInfo: const ServerDiskInfo(diskAvailable: "0", diskSize: "0", diskUse: "0", diskUsagePercentage: 0), + availableAlbums: const [], + selectedBackupAlbums: const {}, + excludedBackupAlbums: const {}, + allUniqueAssets: const {}, + selectedAlbumsBackupAssetsIds: const {}, + currentUploadAsset: CurrentUploadAsset( + id: '...', + fileCreatedAt: DateTime.parse('2020-10-04'), + fileName: '...', + fileType: '...', + fileSize: 0, + iCloudAsset: false, ), - ); + iCloudDownloadProgress: 0.0, + ), + ); final log = Logger('BackupNotifier'); final BackupService _backupService; @@ -153,11 +148,7 @@ class BackupNotifier extends StateNotifier { // disable any backup cancelBackup(); setAutoBackup(false); - configureBackgroundBackup( - enabled: false, - onError: (msg) {}, - onBatteryInfo: () {}, - ); + configureBackgroundBackup(enabled: false, onError: (msg) {}, onBatteryInfo: () {}); } return _updateBackupAssetCount(); } @@ -175,9 +166,7 @@ class BackupNotifier extends StateNotifier { required void Function(String msg) onError, required void Function() onBatteryInfo, }) async { - assert( - enabled != null || requireWifi != null || requireCharging != null || triggerDelay != null, - ); + assert(enabled != null || requireWifi != null || requireCharging != null || triggerDelay != null); final bool wasEnabled = state.backgroundBackup; final bool wasWifi = state.backupRequireWifi; final bool wasCharging = state.backupRequireCharging; @@ -197,7 +186,8 @@ class BackupNotifier extends StateNotifier { } success &= await _backgroundService.enableService(immediate: true); } - success &= success && + success &= + success && await _backgroundService.configureService( requireUnmetered: state.backupRequireWifi, requireCharging: state.backupRequireCharging, @@ -206,10 +196,7 @@ class BackupNotifier extends StateNotifier { ); if (success) { await Store.put(StoreKey.backupRequireWifi, state.backupRequireWifi); - await Store.put( - StoreKey.backupRequireCharging, - state.backupRequireCharging, - ); + await Store.put(StoreKey.backupRequireCharging, state.backupRequireCharging); await Store.put(StoreKey.backupTriggerDelay, state.backupTriggerDelay); await Store.put(StoreKey.backgroundBackup, state.backgroundBackup); } else { @@ -296,14 +283,9 @@ class BackupNotifier extends StateNotifier { } } - state = state.copyWith( - selectedBackupAlbums: selectedAlbums, - excludedBackupAlbums: excludedAlbums, - ); + state = state.copyWith(selectedBackupAlbums: selectedAlbums, excludedBackupAlbums: excludedAlbums); - log.info( - "_getBackupAlbumsInfo: Found ${availableAlbums.length} available albums", - ); + log.info("_getBackupAlbumsInfo: Found ${availableAlbums.length} available albums"); debugPrint("_getBackupAlbumsInfo takes ${stopwatch.elapsedMilliseconds}ms"); } @@ -333,21 +315,14 @@ class BackupNotifier extends StateNotifier { for (final asset in assets) { List albumNames = [album.name]; - final existingAsset = assetsFromSelectedAlbums.firstWhereOrNull( - (a) => a.asset.localId == asset.localId, - ); + final existingAsset = assetsFromSelectedAlbums.firstWhereOrNull((a) => a.asset.localId == asset.localId); if (existingAsset != null) { albumNames.addAll(existingAsset.albumNames); assetsFromSelectedAlbums.remove(existingAsset); } - assetsFromSelectedAlbums.add( - BackupCandidate( - asset: asset, - albumNames: albumNames, - ), - ); + assetsFromSelectedAlbums.add(BackupCandidate(asset: asset, albumNames: albumNames)); } } @@ -361,9 +336,7 @@ class BackupNotifier extends StateNotifier { final assets = await ref.read(albumMediaRepositoryProvider).getAssets(album.album.localId!); for (final asset in assets) { - assetsFromExcludedAlbums.add( - BackupCandidate(asset: asset, albumNames: [album.name]), - ); + assetsFromExcludedAlbums.add(BackupCandidate(asset: asset, albumNames: [album.name])); } } @@ -381,9 +354,7 @@ class BackupNotifier extends StateNotifier { selectedAlbumsBackupAssets.removeWhere((assetId) => !allAssetsInDatabase.contains(assetId)); // Remove duplicated asset from all unique assets - allUniqueAssets.removeWhere( - (candidate) => duplicatedAssetIds.contains(candidate.asset.localId), - ); + allUniqueAssets.removeWhere((candidate) => duplicatedAssetIds.contains(candidate.asset.localId)); if (allUniqueAssets.isEmpty) { log.info("No assets are selected for back up"); @@ -509,9 +480,7 @@ class BackupNotifier extends StateNotifier { } void setAvailableAlbums(availableAlbums) { - state = state.copyWith( - availableAlbums: availableAlbums, - ); + state = state.copyWith(availableAlbums: availableAlbums); } void _onBackupError(ErrorUploadAsset errorAssetInfo) { @@ -541,28 +510,20 @@ class BackupNotifier extends StateNotifier { if (result.isDuplicate) { state = state.copyWith( allUniqueAssets: state.allUniqueAssets - .where( - (candidate) => candidate.asset.localId != result.candidate.asset.localId, - ) + .where((candidate) => candidate.asset.localId != result.candidate.asset.localId) .toSet(), ); } else { state = state.copyWith( - selectedAlbumsBackupAssetsIds: { - ...state.selectedAlbumsBackupAssetsIds, - result.candidate.asset.localId!, - }, - allAssetsInDatabase: [ - ...state.allAssetsInDatabase, - result.candidate.asset.localId!, - ], + selectedAlbumsBackupAssetsIds: {...state.selectedAlbumsBackupAssetsIds, result.candidate.asset.localId!}, + allAssetsInDatabase: [...state.allAssetsInDatabase, result.candidate.asset.localId!], ); } if (state.allUniqueAssets.length - state.selectedAlbumsBackupAssetsIds.length == 0) { - final latestAssetBackup = state.allUniqueAssets.map((candidate) => candidate.asset.fileModifiedAt).reduce( - (v, e) => e.isAfter(v) ? e : v, - ); + final latestAssetBackup = state.allUniqueAssets + .map((candidate) => candidate.asset.fileModifiedAt) + .reduce((v, e) => e.isAfter(v) ? e : v); state = state.copyWith( selectedBackupAlbums: state.selectedBackupAlbums.map((e) => e.copyWith(lastBackup: latestAssetBackup)).toSet(), excludedBackupAlbums: state.excludedBackupAlbums.map((e) => e.copyWith(lastBackup: latestAssetBackup)).toSet(), @@ -594,9 +555,7 @@ class BackupNotifier extends StateNotifier { } if (duration.inSeconds > 0) { - lastUploadSpeeds.add( - ((sent - lastSentBytes) / duration.inSeconds).abs().roundToDouble(), - ); + lastUploadSpeeds.add(((sent - lastSentBytes) / duration.inSeconds).abs().roundToDouble()); lastUploadSpeed = lastUploadSpeeds.average.abs().roundToDouble(); lastUpdateTime = now; @@ -618,9 +577,7 @@ class BackupNotifier extends StateNotifier { // Update server info if (diskInfo != null) { - state = state.copyWith( - serverInfo: diskInfo, - ); + state = state.copyWith(serverInfo: diskInfo); } } @@ -665,17 +622,11 @@ class BackupNotifier extends StateNotifier { Set selectedAlbums = state.selectedBackupAlbums; Set excludedAlbums = state.excludedBackupAlbums; if (selectedAlbums.isNotEmpty) { - selectedAlbums = _updateAlbumsBackupTime( - selectedAlbums, - selectedBackupAlbums, - ); + selectedAlbums = _updateAlbumsBackupTime(selectedAlbums, selectedBackupAlbums); } if (excludedAlbums.isNotEmpty) { - excludedAlbums = _updateAlbumsBackupTime( - excludedAlbums, - excludedBackupAlbums, - ); + excludedAlbums = _updateAlbumsBackupTime(excludedAlbums, excludedBackupAlbums); } final BackUpProgressEnum previous = state.backupProgress; state = state.copyWith( @@ -692,32 +643,21 @@ class BackupNotifier extends StateNotifier { return _resumeBackup(); } - Set _updateAlbumsBackupTime( - Set albums, - List backupAlbums, - ) { + Set _updateAlbumsBackupTime(Set albums, List backupAlbums) { Set result = {}; for (BackupAlbum ba in backupAlbums) { try { AvailableAlbum a = albums.firstWhere((e) => e.id == ba.id); result.add(a.copyWith(lastBackup: ba.lastBackup)); } on StateError { - log.severe( - "[_updateAlbumBackupTime] failed to find album in state", - "State Error", - StackTrace.current, - ); + log.severe("[_updateAlbumBackupTime] failed to find album in state", "State Error", StackTrace.current); } } return result; } Future notifyBackgroundServiceCanRun() async { - const allowedStates = [ - AppLifeCycleEnum.inactive, - AppLifeCycleEnum.paused, - AppLifeCycleEnum.detached, - ]; + const allowedStates = [AppLifeCycleEnum.inactive, AppLifeCycleEnum.paused, AppLifeCycleEnum.detached]; if (allowedStates.contains(ref.read(appStateProvider.notifier).state)) { _backgroundService.releaseLock(); } diff --git a/mobile/lib/providers/backup/backup_album.provider.dart b/mobile/lib/providers/backup/backup_album.provider.dart index 1e5d424926..f81f905c2f 100644 --- a/mobile/lib/providers/backup/backup_album.provider.dart +++ b/mobile/lib/providers/backup/backup_album.provider.dart @@ -5,9 +5,7 @@ import 'package:immich_mobile/infrastructure/repositories/local_album.repository import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; final backupAlbumProvider = StateNotifierProvider>( - (ref) => BackupAlbumNotifier( - ref.watch(localAlbumServiceProvider), - ), + (ref) => BackupAlbumNotifier(ref.watch(localAlbumServiceProvider)), ); class BackupAlbumNotifier extends StateNotifier> { diff --git a/mobile/lib/providers/backup/backup_verification.provider.dart b/mobile/lib/providers/backup/backup_verification.provider.dart index c075d81d86..da4253576b 100644 --- a/mobile/lib/providers/backup/backup_verification.provider.dart +++ b/mobile/lib/providers/backup/backup_verification.provider.dart @@ -64,7 +64,8 @@ class BackupVerification extends _$BackupVerification { onOk: () => _performDeletion(context, toDelete), title: "Corrupt backups!", ok: "Delete", - content: "Found ${toDelete.length} (max $limit at once) corrupt asset backups. " + content: + "Found ${toDelete.length} (max $limit at once) corrupt asset backups. " "Run the check again to find more.\n" "Do you want to delete the corrupt asset backups now?", ), @@ -77,23 +78,18 @@ class BackupVerification extends _$BackupVerification { } } - Future _performDeletion( - BuildContext context, - List assets, - ) async { + Future _performDeletion(BuildContext context, List assets) async { try { state = true; if (context.mounted) { - ImmichToast.show( - context: context, - msg: "Deleting ${assets.length} assets on the server...", - ); + ImmichToast.show(context: context, msg: "Deleting ${assets.length} assets on the server..."); } await ref.read(assetProvider.notifier).deleteAssets(assets, force: true); if (context.mounted) { ImmichToast.show( context: context, - msg: "Deleted ${assets.length} assets on the server. " + msg: + "Deleted ${assets.length} assets on the server. " "You can now start a manual backup", toastType: ToastType.success, ); diff --git a/mobile/lib/providers/backup/backup_verification.provider.g.dart b/mobile/lib/providers/backup/backup_verification.provider.g.dart index bae3ec366b..727e06a12c 100644 --- a/mobile/lib/providers/backup/backup_verification.provider.g.dart +++ b/mobile/lib/providers/backup/backup_verification.provider.g.dart @@ -13,14 +13,14 @@ String _$backupVerificationHash() => @ProviderFor(BackupVerification) final backupVerificationProvider = AutoDisposeNotifierProvider.internal( - BackupVerification.new, - name: r'backupVerificationProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$backupVerificationHash, - dependencies: null, - allTransitiveDependencies: null, -); + BackupVerification.new, + name: r'backupVerificationProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$backupVerificationHash, + dependencies: null, + allTransitiveDependencies: null, + ); typedef _$BackupVerification = AutoDisposeNotifier; // ignore_for_file: type=lint diff --git a/mobile/lib/providers/backup/drift_backup.provider.dart b/mobile/lib/providers/backup/drift_backup.provider.dart index d352869651..aca10e60a7 100644 --- a/mobile/lib/providers/backup/drift_backup.provider.dart +++ b/mobile/lib/providers/backup/drift_backup.provider.dart @@ -14,19 +14,10 @@ class EnqueueStatus { final int enqueueCount; final int totalCount; - const EnqueueStatus({ - required this.enqueueCount, - required this.totalCount, - }); + const EnqueueStatus({required this.enqueueCount, required this.totalCount}); - EnqueueStatus copyWith({ - int? enqueueCount, - int? totalCount, - }) { - return EnqueueStatus( - enqueueCount: enqueueCount ?? this.enqueueCount, - totalCount: totalCount ?? this.totalCount, - ); + EnqueueStatus copyWith({int? enqueueCount, int? totalCount}) { + return EnqueueStatus(enqueueCount: enqueueCount ?? this.enqueueCount, totalCount: totalCount ?? this.totalCount); } @override @@ -197,25 +188,22 @@ class DriftBackupState { } final driftBackupProvider = StateNotifierProvider((ref) { - return ExpBackupNotifier( - ref.watch(uploadServiceProvider), - ); + return ExpBackupNotifier(ref.watch(uploadServiceProvider)); }); class ExpBackupNotifier extends StateNotifier { - ExpBackupNotifier( - this._uploadService, - ) : super( - const DriftBackupState( - totalCount: 0, - backupCount: 0, - remainderCount: 0, - enqueueCount: 0, - enqueueTotalCount: 0, - isCanceling: false, - uploadItems: {}, - ), - ) { + ExpBackupNotifier(this._uploadService) + : super( + const DriftBackupState( + totalCount: 0, + backupCount: 0, + remainderCount: 0, + enqueueCount: 0, + enqueueTotalCount: 0, + isCanceling: false, + uploadItems: {}, + ), + ) { { _uploadService.taskStatusStream.listen(_handleTaskStatusUpdate); _uploadService.taskProgressStream.listen(_handleTaskProgressUpdate); @@ -241,10 +229,7 @@ class ExpBackupNotifier extends StateNotifier { switch (update.status) { case TaskStatus.complete: if (update.task.group == kBackupGroup) { - state = state.copyWith( - backupCount: state.backupCount + 1, - remainderCount: state.remainderCount - 1, - ); + state = state.copyWith(backupCount: state.backupCount + 1, remainderCount: state.remainderCount - 1); } // Remove the completed task from the upload items @@ -260,14 +245,7 @@ class ExpBackupNotifier extends StateNotifier { return; } - state = state.copyWith( - uploadItems: { - ...state.uploadItems, - taskId: currentItem.copyWith( - isFailed: true, - ), - }, - ); + state = state.copyWith(uploadItems: {...state.uploadItems, taskId: currentItem.copyWith(isFailed: true)}); break; case TaskStatus.canceled: @@ -299,9 +277,7 @@ class ExpBackupNotifier extends StateNotifier { fileSize: update.expectedFileSize, networkSpeedAsString: update.networkSpeedAsString, ) - : currentItem.copyWith( - progress: progress, - ), + : currentItem.copyWith(progress: progress), }, ); @@ -329,11 +305,7 @@ class ExpBackupNotifier extends StateNotifier { _uploadService.getBackupRemainderCount(userId), ]); - state = state.copyWith( - totalCount: totalCount, - backupCount: backupCount, - remainderCount: remainderCount, - ); + state = state.copyWith(totalCount: totalCount, backupCount: backupCount, remainderCount: remainderCount); } Future startBackup(String userId) { @@ -341,34 +313,22 @@ class ExpBackupNotifier extends StateNotifier { } void _updateEnqueueCount(EnqueueStatus status) { - state = state.copyWith( - enqueueCount: status.enqueueCount, - enqueueTotalCount: status.totalCount, - ); + state = state.copyWith(enqueueCount: status.enqueueCount, enqueueTotalCount: status.totalCount); } Future cancel() async { debugPrint("Canceling backup tasks..."); - state = state.copyWith( - enqueueCount: 0, - enqueueTotalCount: 0, - isCanceling: true, - ); + state = state.copyWith(enqueueCount: 0, enqueueTotalCount: 0, isCanceling: true); final activeTaskCount = await _uploadService.cancelBackup(); if (activeTaskCount > 0) { - debugPrint( - "$activeTaskCount tasks left, continuing to cancel...", - ); + debugPrint("$activeTaskCount tasks left, continuing to cancel..."); await cancel(); } else { debugPrint("All tasks canceled successfully."); // Clear all upload items when cancellation is complete - state = state.copyWith( - isCanceling: false, - uploadItems: {}, - ); + state = state.copyWith(isCanceling: false, uploadItems: {}); } } diff --git a/mobile/lib/providers/backup/manual_upload.provider.dart b/mobile/lib/providers/backup/manual_upload.provider.dart index b234a4ffe2..1aea7f64cb 100644 --- a/mobile/lib/providers/backup/manual_upload.provider.dart +++ b/mobile/lib/providers/backup/manual_upload.provider.dart @@ -56,44 +56,43 @@ class ManualUploadNotifier extends StateNotifier { this._backupAlbumService, this.ref, ) : super( - ManualUploadState( - progressInPercentage: 0, - progressInFileSize: "0 B / 0 B", - progressInFileSpeed: 0, - progressInFileSpeeds: const [], - progressInFileSpeedUpdateTime: DateTime.now(), - progressInFileSpeedUpdateSentBytes: 0, - cancelToken: CancellationToken(), - currentUploadAsset: CurrentUploadAsset( - id: '...', - fileCreatedAt: DateTime.parse('2020-10-04'), - fileName: '...', - fileType: '...', - ), - totalAssetsToUpload: 0, - successfulUploads: 0, - currentAssetIndex: 0, - showDetailedNotification: false, + ManualUploadState( + progressInPercentage: 0, + progressInFileSize: "0 B / 0 B", + progressInFileSpeed: 0, + progressInFileSpeeds: const [], + progressInFileSpeedUpdateTime: DateTime.now(), + progressInFileSpeedUpdateSentBytes: 0, + cancelToken: CancellationToken(), + currentUploadAsset: CurrentUploadAsset( + id: '...', + fileCreatedAt: DateTime.parse('2020-10-04'), + fileName: '...', + fileType: '...', ), - ); + totalAssetsToUpload: 0, + successfulUploads: 0, + currentAssetIndex: 0, + showDetailedNotification: false, + ), + ); String _lastPrintedDetailContent = ''; String? _lastPrintedDetailTitle; static const notifyInterval = Duration(milliseconds: 500); late final ThrottleProgressUpdate _throttledNotifiy = ThrottleProgressUpdate(_updateProgress, notifyInterval); - late final ThrottleProgressUpdate _throttledDetailNotify = - ThrottleProgressUpdate(_updateDetailProgress, notifyInterval); + late final ThrottleProgressUpdate _throttledDetailNotify = ThrottleProgressUpdate( + _updateDetailProgress, + notifyInterval, + ); void _updateProgress(String? title, int progress, int total) { // Guard against throttling calling this method after the upload is done if (_backupProvider.backupProgress == BackUpProgressEnum.manualInProgress) { _localNotificationService.showOrUpdateManualUploadStatus( "backup_background_service_in_progress_notification".tr(), - formatAssetBackupProgress( - state.currentAssetIndex, - state.totalAssetsToUpload, - ), + formatAssetBackupProgress(state.currentAssetIndex, state.totalAssetsToUpload), maxProgress: state.totalAssetsToUpload, progress: state.currentAssetIndex, showActions: true, @@ -146,9 +145,7 @@ class ManualUploadNotifier extends StateNotifier { } if (duration.inSeconds > 0) { - lastUploadSpeeds.add( - ((sent - lastSentBytes) / duration.inSeconds).abs().roundToDouble(), - ); + lastUploadSpeeds.add(((sent - lastSentBytes) / duration.inSeconds).abs().roundToDouble()); lastUploadSpeed = lastUploadSpeeds.average.abs().roundToDouble(); lastUpdateTime = now; @@ -165,23 +162,22 @@ class ManualUploadNotifier extends StateNotifier { ); if (state.showDetailedNotification) { - final title = "backup_background_service_current_upload_notification" - .tr(namedArgs: {'filename': state.currentUploadAsset.fileName}); + final title = "backup_background_service_current_upload_notification".tr( + namedArgs: {'filename': state.currentUploadAsset.fileName}, + ); _throttledDetailNotify(title: title, progress: sent, total: total); } } void _onSetCurrentBackupAsset(CurrentUploadAsset currentUploadAsset) { - state = state.copyWith( - currentUploadAsset: currentUploadAsset, - currentAssetIndex: state.currentAssetIndex + 1, - ); + state = state.copyWith(currentUploadAsset: currentUploadAsset, currentAssetIndex: state.currentAssetIndex + 1); if (state.totalAssetsToUpload > 1) { _throttledNotifiy(); } if (state.showDetailedNotification) { - _throttledDetailNotify.title = "backup_background_service_current_upload_notification" - .tr(namedArgs: {'filename': currentUploadAsset.fileName}); + _throttledDetailNotify.title = "backup_background_service_current_upload_notification".tr( + namedArgs: {'filename': currentUploadAsset.fileName}, + ); _throttledDetailNotify.progress = 0; _throttledDetailNotify.total = 0; } @@ -216,10 +212,7 @@ class ManualUploadNotifier extends StateNotifier { // Extrack candidate from allAssetsFromDevice final uploadAssets = candidates.where( (candidate) => - allAssetsFromDevice.firstWhereOrNull( - (asset) => asset.localId == candidate.asset.localId, - ) != - null, + allAssetsFromDevice.firstWhereOrNull((asset) => asset.localId == candidate.asset.localId) != null, ); if (uploadAssets.isEmpty) { @@ -251,14 +244,15 @@ class ManualUploadNotifier extends StateNotifier { } // Show detailed asset if enabled in settings or if a single asset is uploaded - bool showDetailedNotification = ref.read(appSettingsServiceProvider).getSetting( - AppSettingsEnum.backgroundBackupSingleProgress, - ) || + bool showDetailedNotification = + ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.backgroundBackupSingleProgress) || state.totalAssetsToUpload == 1; state = state.copyWith(showDetailedNotification: showDetailedNotification); final pmProgressHandler = Platform.isIOS ? PMProgressHandler() : null; - final bool ok = await ref.read(backupServiceProvider).backupAsset( + final bool ok = await ref + .read(backupServiceProvider) + .backupAsset( uploadAssets, state.cancelToken, pmProgressHandler: pmProgressHandler, @@ -269,9 +263,7 @@ class ManualUploadNotifier extends StateNotifier { ); // Close detailed notification - await _localNotificationService.closeNotification( - LocalNotificationService.manualUploadDetailedNotificationID, - ); + await _localNotificationService.closeNotification(LocalNotificationService.manualUploadDetailedNotificationID); _log.info( '[_startUpload] Manual Upload Completed - success: ${state.successfulUploads},' @@ -310,9 +302,7 @@ class ManualUploadNotifier extends StateNotifier { } finally { _backupProvider.updateBackupProgress(BackUpProgressEnum.idle); _handleAppInActivity(); - await _localNotificationService.closeNotification( - LocalNotificationService.manualUploadDetailedNotificationID, - ); + await _localNotificationService.closeNotification(LocalNotificationService.manualUploadDetailedNotificationID); await _backupProvider.notifyBackgroundServiceCanRun(); } return !hasErrors; @@ -345,10 +335,7 @@ class ManualUploadNotifier extends StateNotifier { ); } - Future uploadAssets( - BuildContext context, - Iterable allManualUploads, - ) async { + Future uploadAssets(BuildContext context, Iterable allManualUploads) async { // assumes the background service is currently running and // waits until it has stopped to start the backup. final bool hasLock = await ref.read(backgroundServiceProvider).acquireLock(); diff --git a/mobile/lib/providers/cast.provider.dart b/mobile/lib/providers/cast.provider.dart index 11cdcd54c5..75a2a35fb6 100644 --- a/mobile/lib/providers/cast.provider.dart +++ b/mobile/lib/providers/cast.provider.dart @@ -15,15 +15,15 @@ class CastNotifier extends StateNotifier { List<(String, CastDestinationType, dynamic)> discovered = List.empty(); CastNotifier(this._gCastService) - : super( - const CastManagerState( - isCasting: false, - currentTime: Duration.zero, - duration: Duration.zero, - receiverName: '', - castState: CastState.idle, - ), - ) { + : super( + const CastManagerState( + isCasting: false, + currentTime: Duration.zero, + duration: Duration.zero, + receiverName: '', + castState: CastState.idle, + ), + ) { _gCastService.onConnectionState = _onConnectionState; _gCastService.onCurrentTime = _onCurrentTime; _gCastService.onDuration = _onDuration; @@ -65,8 +65,8 @@ class CastNotifier extends StateNotifier { type: asset.type == old_asset_entity.AssetType.image ? AssetType.image : asset.type == old_asset_entity.AssetType.video - ? AssetType.video - : AssetType.other, + ? AssetType.video + : AssetType.other, createdAt: asset.fileCreatedAt, updatedAt: asset.updatedAt, ); diff --git a/mobile/lib/providers/folder.provider.dart b/mobile/lib/providers/folder.provider.dart index 7f89679734..696d7e19fd 100644 --- a/mobile/lib/providers/folder.provider.dart +++ b/mobile/lib/providers/folder.provider.dart @@ -23,9 +23,7 @@ class FolderStructureNotifier extends StateNotifier> { } final folderStructureProvider = StateNotifierProvider>((ref) { - return FolderStructureNotifier( - ref.watch(folderServiceProvider), - ); + return FolderStructureNotifier(ref.watch(folderServiceProvider)); }); class FolderRenderListNotifier extends StateNotifier> { @@ -49,8 +47,5 @@ class FolderRenderListNotifier extends StateNotifier> { final folderRenderListProvider = StateNotifierProvider.family, RootFolder>((ref, folder) { - return FolderRenderListNotifier( - ref.watch(folderServiceProvider), - folder, - ); -}); + return FolderRenderListNotifier(ref.watch(folderServiceProvider), folder); + }); diff --git a/mobile/lib/providers/gallery_permission.provider.dart b/mobile/lib/providers/gallery_permission.provider.dart index 3d8a6da941..6e4fc69926 100644 --- a/mobile/lib/providers/gallery_permission.provider.dart +++ b/mobile/lib/providers/gallery_permission.provider.dart @@ -5,8 +5,9 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:permission_handler/permission_handler.dart'; class GalleryPermissionNotifier extends StateNotifier { - GalleryPermissionNotifier() : super(PermissionStatus.denied) // Denied is the initial state - { + GalleryPermissionNotifier() + : super(PermissionStatus.denied) // Denied is the initial state + { // Sets the initial state getGalleryPermissionStatus(); } diff --git a/mobile/lib/providers/image/cache/image_loader.dart b/mobile/lib/providers/image/cache/image_loader.dart index f88d54e4f1..50530f7cdf 100644 --- a/mobile/lib/providers/image/cache/image_loader.dart +++ b/mobile/lib/providers/image/cache/image_loader.dart @@ -19,20 +19,13 @@ class ImageLoader { }) async { final headers = ApiService.getRequestHeaders(); - final stream = cache.getFileStream( - uri, - withProgress: chunkEvents != null, - headers: headers, - ); + final stream = cache.getFileStream(uri, withProgress: chunkEvents != null, headers: headers); await for (final result in stream) { if (result is DownloadProgress) { // We are downloading the file, so update the [chunkEvents] chunkEvents?.add( - ImageChunkEvent( - cumulativeBytesLoaded: result.downloaded, - expectedTotalBytes: result.totalSize, - ), + ImageChunkEvent(cumulativeBytesLoaded: result.downloaded, expectedTotalBytes: result.totalSize), ); } else if (result is FileInfo) { // We have the file diff --git a/mobile/lib/providers/image/cache/remote_image_cache_manager.dart b/mobile/lib/providers/image/cache/remote_image_cache_manager.dart index da20f46c62..b9e2880c04 100644 --- a/mobile/lib/providers/image/cache/remote_image_cache_manager.dart +++ b/mobile/lib/providers/image/cache/remote_image_cache_manager.dart @@ -9,12 +9,5 @@ class RemoteImageCacheManager extends CacheManager { return _instance; } - RemoteImageCacheManager._() - : super( - Config( - key, - maxNrOfCacheObjects: 500, - stalePeriod: const Duration(days: 30), - ), - ); + RemoteImageCacheManager._() : super(Config(key, maxNrOfCacheObjects: 500, stalePeriod: const Duration(days: 30))); } diff --git a/mobile/lib/providers/image/cache/thumbnail_image_cache_manager.dart b/mobile/lib/providers/image/cache/thumbnail_image_cache_manager.dart index f8d4cda3e6..bfea36eef6 100644 --- a/mobile/lib/providers/image/cache/thumbnail_image_cache_manager.dart +++ b/mobile/lib/providers/image/cache/thumbnail_image_cache_manager.dart @@ -9,12 +9,5 @@ class ThumbnailImageCacheManager extends CacheManager { return _instance; } - ThumbnailImageCacheManager._() - : super( - Config( - key, - maxNrOfCacheObjects: 5000, - stalePeriod: const Duration(days: 30), - ), - ); + ThumbnailImageCacheManager._() : super(Config(key, maxNrOfCacheObjects: 5000, stalePeriod: const Duration(days: 30))); } diff --git a/mobile/lib/providers/image/immich_local_image_provider.dart b/mobile/lib/providers/image/immich_local_image_provider.dart index 4c77ee4b56..8c46c52906 100644 --- a/mobile/lib/providers/image/immich_local_image_provider.dart +++ b/mobile/lib/providers/image/immich_local_image_provider.dart @@ -18,11 +18,8 @@ class ImmichLocalImageProvider extends ImageProvider { final double height; final Logger log = Logger('ImmichLocalImageProvider'); - ImmichLocalImageProvider({ - required this.asset, - required this.width, - required this.height, - }) : assert(asset.local != null, 'Only usable when asset.local is set'); + ImmichLocalImageProvider({required this.asset, required this.width, required this.height}) + : assert(asset.local != null, 'Only usable when asset.local is set'); /// Converts an [ImageProvider]'s settings plus an [ImageConfiguration] to a key /// that describes the precise image to load. @@ -32,10 +29,7 @@ class ImmichLocalImageProvider extends ImageProvider { } @override - ImageStreamCompleter loadImage( - ImmichLocalImageProvider key, - ImageDecoderCallback decode, - ) { + ImageStreamCompleter loadImage(ImmichLocalImageProvider key, ImageDecoderCallback decode) { final chunkEvents = StreamController(); return MultiImageStreamCompleter( codec: _codec(key.asset, decode, chunkEvents), diff --git a/mobile/lib/providers/image/immich_local_thumbnail_provider.dart b/mobile/lib/providers/image/immich_local_thumbnail_provider.dart index de69115444..5edb0fc79e 100644 --- a/mobile/lib/providers/image/immich_local_thumbnail_provider.dart +++ b/mobile/lib/providers/image/immich_local_thumbnail_provider.dart @@ -32,17 +32,12 @@ class ImmichLocalThumbnailProvider extends ImageProvider obtainKey( - ImageConfiguration configuration, - ) { + Future obtainKey(ImageConfiguration configuration) { return SynchronousFuture(this); } @override - ImageStreamCompleter loadImage( - ImmichLocalThumbnailProvider key, - ImageDecoderCallback decode, - ) { + ImageStreamCompleter loadImage(ImmichLocalThumbnailProvider key, ImageDecoderCallback decode) { final cache = cacheManager ?? ThumbnailImageCacheManager(); return MultiImageStreamCompleter( codec: _codec(key.asset, cache, decode), @@ -54,11 +49,7 @@ class ImmichLocalThumbnailProvider extends ImageProvider _codec( - Asset assetData, - CacheManager cache, - ImageDecoderCallback decode, - ) async* { + Stream _codec(Asset assetData, CacheManager cache, ImageDecoderCallback decode) async* { final cacheKey = '$userId${assetData.localId}${assetData.checksum}$width$height'; final fileFromCache = await cache.getFileFromCache(cacheKey); if (fileFromCache != null) { @@ -72,14 +63,9 @@ class ImmichLocalThumbnailProvider extends ImageProvider /// The image cache manager final CacheManager? cacheManager; - const ImmichRemoteImageProvider({ - required this.assetId, - this.cacheManager, - }); + const ImmichRemoteImageProvider({required this.assetId, this.cacheManager}); /// Converts an [ImageProvider]'s settings plus an [ImageConfiguration] to a key /// that describes the precise image to load. @override - Future obtainKey( - ImageConfiguration configuration, - ) { + Future obtainKey(ImageConfiguration configuration) { return SynchronousFuture(this); } @override - ImageStreamCompleter loadImage( - ImmichRemoteImageProvider key, - ImageDecoderCallback decode, - ) { + ImageStreamCompleter loadImage(ImmichRemoteImageProvider key, ImageDecoderCallback decode) { final cache = cacheManager ?? RemoteImageCacheManager(); final chunkEvents = StreamController(); return MultiImageStreamCompleter( @@ -51,10 +43,7 @@ class ImmichRemoteImageProvider extends ImageProvider } /// Whether to show the original file or load a compressed version - bool get _useOriginal => Store.get( - AppSettingsEnum.loadOriginal.storeKey, - AppSettingsEnum.loadOriginal.defaultValue, - ); + bool get _useOriginal => Store.get(AppSettingsEnum.loadOriginal.storeKey, AppSettingsEnum.loadOriginal.defaultValue); // Streams in each stage of the image as we ask for it Stream _codec( @@ -64,28 +53,15 @@ class ImmichRemoteImageProvider extends ImageProvider StreamController chunkEvents, ) async* { // Load the higher resolution version of the image - final url = getThumbnailUrlForRemoteId( - key.assetId, - type: api.AssetMediaSize.preview, - ); - final codec = await ImageLoader.loadImageFromCache( - url, - cache: cache, - decode: decode, - chunkEvents: chunkEvents, - ); + final url = getThumbnailUrlForRemoteId(key.assetId, type: api.AssetMediaSize.preview); + final codec = await ImageLoader.loadImageFromCache(url, cache: cache, decode: decode, chunkEvents: chunkEvents); yield codec; // Load the final remote image if (_useOriginal) { // Load the original image final url = getOriginalUrlForRemoteId(key.assetId); - final codec = await ImageLoader.loadImageFromCache( - url, - cache: cache, - decode: decode, - chunkEvents: chunkEvents, - ); + final codec = await ImageLoader.loadImageFromCache(url, cache: cache, decode: decode, chunkEvents: chunkEvents); yield codec; } await chunkEvents.close(); diff --git a/mobile/lib/providers/image/immich_remote_thumbnail_provider.dart b/mobile/lib/providers/image/immich_remote_thumbnail_provider.dart index cb2a6270b4..08ee4325e8 100644 --- a/mobile/lib/providers/image/immich_remote_thumbnail_provider.dart +++ b/mobile/lib/providers/image/immich_remote_thumbnail_provider.dart @@ -23,51 +23,27 @@ class ImmichRemoteThumbnailProvider extends ImageProvider obtainKey( - ImageConfiguration configuration, - ) { + Future obtainKey(ImageConfiguration configuration) { return SynchronousFuture(this); } @override - ImageStreamCompleter loadImage( - ImmichRemoteThumbnailProvider key, - ImageDecoderCallback decode, - ) { + ImageStreamCompleter loadImage(ImmichRemoteThumbnailProvider key, ImageDecoderCallback decode) { final cache = cacheManager ?? ThumbnailImageCacheManager(); - return MultiImageStreamCompleter( - codec: _codec(key, cache, decode), - scale: 1.0, - ); + return MultiImageStreamCompleter(codec: _codec(key, cache, decode), scale: 1.0); } // Streams in each stage of the image as we ask for it - Stream _codec( - ImmichRemoteThumbnailProvider key, - CacheManager cache, - ImageDecoderCallback decode, - ) async* { + Stream _codec(ImmichRemoteThumbnailProvider key, CacheManager cache, ImageDecoderCallback decode) async* { // Load a preview to the chunk events - final preview = getThumbnailUrlForRemoteId( - key.assetId, - type: api.AssetMediaSize.thumbnail, - ); + final preview = getThumbnailUrlForRemoteId(key.assetId, type: api.AssetMediaSize.thumbnail); - yield await ImageLoader.loadImageFromCache( - preview, - cache: cache, - decode: decode, - ); + yield await ImageLoader.loadImageFromCache(preview, cache: cache, decode: decode); } @override diff --git a/mobile/lib/providers/immich_logo_provider.g.dart b/mobile/lib/providers/immich_logo_provider.g.dart index 90b117d574..f1af433c1b 100644 --- a/mobile/lib/providers/immich_logo_provider.g.dart +++ b/mobile/lib/providers/immich_logo_provider.g.dart @@ -13,8 +13,9 @@ String _$immichLogoHash() => r'6de7fcca1ef9acef6ab7398eb0c664080747e0ea'; final immichLogoProvider = AutoDisposeFutureProvider.internal( immichLogo, name: r'immichLogoProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$immichLogoHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$immichLogoHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/mobile/lib/providers/infrastructure/action.provider.dart b/mobile/lib/providers/infrastructure/action.provider.dart index 26de1b4dba..9d05a6ecab 100644 --- a/mobile/lib/providers/infrastructure/action.provider.dart +++ b/mobile/lib/providers/infrastructure/action.provider.dart @@ -12,10 +12,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; final actionProvider = NotifierProvider( ActionNotifier.new, - dependencies: [ - multiSelectProvider, - timelineServiceProvider, - ], + dependencies: [multiSelectProvider, timelineServiceProvider], ); class ActionResult { @@ -74,37 +71,31 @@ class ActionNotifier extends Notifier { Iterable _getIdsForSource(ActionSource source) { final Set assets = _getAssets(source); return switch (T) { - const (RemoteAsset) => assets.whereType(), - const (LocalAsset) => assets.whereType(), - _ => const [], - } as Iterable; + const (RemoteAsset) => assets.whereType(), + const (LocalAsset) => assets.whereType(), + _ => const [], + } + as Iterable; } Set _getAssets(ActionSource source) { return switch (source) { ActionSource.timeline => ref.read(multiSelectProvider).selectedAssets, ActionSource.viewer => switch (ref.read(currentAssetNotifier)) { - BaseAsset asset => {asset}, - null => const {}, - }, + BaseAsset asset => {asset}, + null => const {}, + }, }; } - Future shareLink( - ActionSource source, - BuildContext context, - ) async { + Future shareLink(ActionSource source, BuildContext context) async { final ids = _getRemoteIdsForSource(source); try { await _service.shareLink(ids, context); return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to create shared link for assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -115,11 +106,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to favorite assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -130,11 +117,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to unfavorite assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -145,11 +128,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to archive assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -160,11 +139,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to unarchive assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -176,11 +151,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to move assets to lock folder', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -191,11 +162,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to remove assets from lock folder', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -207,11 +174,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to trash assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -222,11 +185,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to restore trash assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -238,11 +197,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to delete assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -254,11 +209,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to delete assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -269,18 +220,11 @@ class ActionNotifier extends Notifier { return ActionResult(count: deletedCount, success: true); } catch (error, stack) { _logger.severe('Failed to delete assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } - Future editLocation( - ActionSource source, - BuildContext context, - ) async { + Future editLocation(ActionSource source, BuildContext context) async { final ids = _getOwnedRemoteIdsForSource(source); try { final isEdited = await _service.editLocation(ids, context); @@ -291,29 +235,18 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to edit location for assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } - Future removeFromAlbum( - ActionSource source, - String albumId, - ) async { + Future removeFromAlbum(ActionSource source, String albumId) async { final ids = _getRemoteIdsForSource(source); try { final removedCount = await _service.removeFromAlbum(ids, albumId); return ActionResult(count: removedCount, success: true); } catch (error, stack) { _logger.severe('Failed to remove assets from album', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -324,11 +257,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to stack assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -339,10 +268,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: assets.length, success: true); } catch (error, stack) { _logger.severe('Failed to unstack assets', error, stack); - return ActionResult( - count: assets.length, - success: false, - ); + return ActionResult(count: assets.length, success: false); } } @@ -354,11 +280,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: count, success: true); } catch (error, stack) { _logger.severe('Failed to share assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -371,11 +293,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: enqueueCount, success: true); } catch (error, stack) { _logger.severe('Failed to download assets', error, stack); - return ActionResult( - count: assets.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: assets.length, success: false, error: error.toString()); } } @@ -386,11 +304,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: assets.length, success: true); } catch (error, stack) { _logger.severe('Failed manually upload assets', error, stack); - return ActionResult( - count: assets.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: assets.length, success: false, error: error.toString()); } } } diff --git a/mobile/lib/providers/infrastructure/album.provider.dart b/mobile/lib/providers/infrastructure/album.provider.dart index 4baead4b75..da0f9bc9ce 100644 --- a/mobile/lib/providers/infrastructure/album.provider.dart +++ b/mobile/lib/providers/infrastructure/album.provider.dart @@ -30,10 +30,7 @@ final remoteAlbumRepository = Provider( ); final remoteAlbumServiceProvider = Provider( - (ref) => RemoteAlbumService( - ref.watch(remoteAlbumRepository), - ref.watch(driftAlbumApiRepositoryProvider), - ), + (ref) => RemoteAlbumService(ref.watch(remoteAlbumRepository), ref.watch(driftAlbumApiRepositoryProvider)), dependencies: [remoteAlbumRepository], ); diff --git a/mobile/lib/providers/infrastructure/asset_viewer/current_asset.provider.dart b/mobile/lib/providers/infrastructure/asset_viewer/current_asset.provider.dart index 66de676c08..1956170c1e 100644 --- a/mobile/lib/providers/infrastructure/asset_viewer/current_asset.provider.dart +++ b/mobile/lib/providers/infrastructure/asset_viewer/current_asset.provider.dart @@ -4,9 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; -final currentAssetNotifier = AutoDisposeNotifierProvider( - CurrentAssetNotifier.new, -); +final currentAssetNotifier = AutoDisposeNotifierProvider(CurrentAssetNotifier.new); class CurrentAssetNotifier extends AutoDisposeNotifier { KeepAliveLink? _keepAliveLink; @@ -33,12 +31,10 @@ class CurrentAssetNotifier extends AutoDisposeNotifier { } } -final currentAssetExifProvider = FutureProvider.autoDispose( - (ref) { - final currentAsset = ref.watch(currentAssetNotifier); - if (currentAsset == null) { - return null; - } - return ref.watch(assetServiceProvider).getExif(currentAsset); - }, -); +final currentAssetExifProvider = FutureProvider.autoDispose((ref) { + final currentAsset = ref.watch(currentAssetNotifier); + if (currentAsset == null) { + return null; + } + return ref.watch(assetServiceProvider).getExif(currentAsset); +}); diff --git a/mobile/lib/providers/infrastructure/db.provider.g.dart b/mobile/lib/providers/infrastructure/db.provider.g.dart index 33b330192f..46abfb66a9 100644 --- a/mobile/lib/providers/infrastructure/db.provider.g.dart +++ b/mobile/lib/providers/infrastructure/db.provider.g.dart @@ -13,8 +13,9 @@ String _$isarHash() => r'69d3a06aa7e69a4381478e03f7956eb07d7f7feb'; final isarProvider = Provider.internal( isar, name: r'isarProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$isarHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$isarHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/mobile/lib/providers/infrastructure/person.provider.dart b/mobile/lib/providers/infrastructure/person.provider.dart index a733104b33..ac8a457e3a 100644 --- a/mobile/lib/providers/infrastructure/person.provider.dart +++ b/mobile/lib/providers/infrastructure/person.provider.dart @@ -2,6 +2,4 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/infrastructure/repositories/person.repository.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; -final driftPersonProvider = Provider( - (ref) => DriftPersonRepository(ref.watch(driftProvider)), -); +final driftPersonProvider = Provider((ref) => DriftPersonRepository(ref.watch(driftProvider))); diff --git a/mobile/lib/providers/infrastructure/remote_album.provider.dart b/mobile/lib/providers/infrastructure/remote_album.provider.dart index c6d9337b53..ca7735808c 100644 --- a/mobile/lib/providers/infrastructure/remote_album.provider.dart +++ b/mobile/lib/providers/infrastructure/remote_album.provider.dart @@ -15,19 +15,11 @@ class RemoteAlbumState { final List albums; final List filteredAlbums; - const RemoteAlbumState({ - required this.albums, - List? filteredAlbums, - }) : filteredAlbums = filteredAlbums ?? albums; + const RemoteAlbumState({required this.albums, List? filteredAlbums}) + : filteredAlbums = filteredAlbums ?? albums; - RemoteAlbumState copyWith({ - List? albums, - List? filteredAlbums, - }) { - return RemoteAlbumState( - albums: albums ?? this.albums, - filteredAlbums: filteredAlbums ?? this.filteredAlbums, - ); + RemoteAlbumState copyWith({List? albums, List? filteredAlbums}) { + return RemoteAlbumState(albums: albums ?? this.albums, filteredAlbums: filteredAlbums ?? this.filteredAlbums); } @override @@ -57,10 +49,7 @@ class RemoteAlbumNotifier extends Notifier { Future> _getAll() async { try { final albums = await _remoteAlbumService.getAll(); - state = state.copyWith( - albums: albums, - filteredAlbums: albums, - ); + state = state.copyWith(albums: albums, filteredAlbums: albums); return albums; } catch (error, stack) { _logger.severe('Failed to fetch albums', error, stack); @@ -72,33 +61,17 @@ class RemoteAlbumNotifier extends Notifier { await _getAll(); } - void searchAlbums( - String query, - String? userId, [ - QuickFilterMode filterMode = QuickFilterMode.all, - ]) { - final filtered = _remoteAlbumService.searchAlbums( - state.albums, - query, - userId, - filterMode, - ); + void searchAlbums(String query, String? userId, [QuickFilterMode filterMode = QuickFilterMode.all]) { + final filtered = _remoteAlbumService.searchAlbums(state.albums, query, userId, filterMode); - state = state.copyWith( - filteredAlbums: filtered, - ); + state = state.copyWith(filteredAlbums: filtered); } void clearSearch() { - state = state.copyWith( - filteredAlbums: state.albums, - ); + state = state.copyWith(filteredAlbums: state.albums); } - void sortFilteredAlbums( - RemoteAlbumSortMode sortMode, { - bool isReverse = false, - }) { + void sortFilteredAlbums(RemoteAlbumSortMode sortMode, {bool isReverse = false}) { final sortedAlbums = _remoteAlbumService.sortAlbums(state.filteredAlbums, sortMode, isReverse: isReverse); state = state.copyWith(filteredAlbums: sortedAlbums); } @@ -109,16 +82,9 @@ class RemoteAlbumNotifier extends Notifier { List assetIds = const [], }) async { try { - final album = await _remoteAlbumService.createAlbum( - title: title, - description: description, - assetIds: assetIds, - ); + final album = await _remoteAlbumService.createAlbum(title: title, description: description, assetIds: assetIds); - state = state.copyWith( - albums: [...state.albums, album], - filteredAlbums: [...state.filteredAlbums, album], - ); + state = state.copyWith(albums: [...state.albums, album], filteredAlbums: [...state.filteredAlbums, album]); return album; } catch (error, stack) { @@ -153,10 +119,7 @@ class RemoteAlbumNotifier extends Notifier { return album.id == albumId ? updatedAlbum : album; }).toList(); - state = state.copyWith( - albums: updatedAlbums, - filteredAlbums: updatedFilteredAlbums, - ); + state = state.copyWith(albums: updatedAlbums, filteredAlbums: updatedFilteredAlbums); return updatedAlbum; } catch (error, stack) { @@ -179,10 +142,7 @@ class RemoteAlbumNotifier extends Notifier { final updatedAlbums = state.albums.where((album) => album.id != albumId).toList(); final updatedFilteredAlbums = state.filteredAlbums.where((album) => album.id != albumId).toList(); - state = state.copyWith( - albums: updatedAlbums, - filteredAlbums: updatedFilteredAlbums, - ); + state = state.copyWith(albums: updatedAlbums, filteredAlbums: updatedFilteredAlbums); } Future> getAssets(String albumId) { @@ -190,32 +150,22 @@ class RemoteAlbumNotifier extends Notifier { } Future addAssets(String albumId, List assetIds) { - return _remoteAlbumService.addAssets( - albumId: albumId, - assetIds: assetIds, - ); + return _remoteAlbumService.addAssets(albumId: albumId, assetIds: assetIds); } Future addUsers(String albumId, List userIds) { - return _remoteAlbumService.addUsers( - albumId: albumId, - userIds: userIds, - ); + return _remoteAlbumService.addUsers(albumId: albumId, userIds: userIds); } } -final remoteAlbumDateRangeProvider = FutureProvider.family<(DateTime, DateTime), String>( - (ref, albumId) async { - final service = ref.watch(remoteAlbumServiceProvider); - return service.getDateRange(albumId); - }, -); +final remoteAlbumDateRangeProvider = FutureProvider.family<(DateTime, DateTime), String>((ref, albumId) async { + final service = ref.watch(remoteAlbumServiceProvider); + return service.getDateRange(albumId); +}); -final remoteAlbumSharedUsersProvider = FutureProvider.autoDispose.family, String>( - (ref, albumId) async { - final link = ref.keepAlive(); - ref.onDispose(() => link.close()); - final service = ref.watch(remoteAlbumServiceProvider); - return service.getSharedUsers(albumId); - }, -); +final remoteAlbumSharedUsersProvider = FutureProvider.autoDispose.family, String>((ref, albumId) async { + final link = ref.keepAlive(); + ref.onDispose(() => link.close()); + final service = ref.watch(remoteAlbumServiceProvider); + return service.getSharedUsers(albumId); +}); diff --git a/mobile/lib/providers/infrastructure/search.provider.dart b/mobile/lib/providers/infrastructure/search.provider.dart index cdcd3ee43b..7d992f9d5f 100644 --- a/mobile/lib/providers/infrastructure/search.provider.dart +++ b/mobile/lib/providers/infrastructure/search.provider.dart @@ -3,10 +3,6 @@ import 'package:immich_mobile/domain/services/search.service.dart'; import 'package:immich_mobile/infrastructure/repositories/search_api.repository.dart'; import 'package:immich_mobile/providers/api.provider.dart'; -final searchApiRepositoryProvider = Provider( - (ref) => SearchApiRepository(ref.watch(apiServiceProvider).searchApi), -); +final searchApiRepositoryProvider = Provider((ref) => SearchApiRepository(ref.watch(apiServiceProvider).searchApi)); -final searchServiceProvider = Provider( - (ref) => SearchService(ref.watch(searchApiRepositoryProvider)), -); +final searchServiceProvider = Provider((ref) => SearchService(ref.watch(searchApiRepositoryProvider))); diff --git a/mobile/lib/providers/infrastructure/stack.provider.dart b/mobile/lib/providers/infrastructure/stack.provider.dart index 71abd1e87a..0528fd0c91 100644 --- a/mobile/lib/providers/infrastructure/stack.provider.dart +++ b/mobile/lib/providers/infrastructure/stack.provider.dart @@ -2,6 +2,4 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/infrastructure/repositories/stack.repository.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; -final driftStackProvider = Provider( - (ref) => DriftStackRepository(ref.watch(driftProvider)), -); +final driftStackProvider = Provider((ref) => DriftStackRepository(ref.watch(driftProvider))); diff --git a/mobile/lib/providers/infrastructure/storage.provider.dart b/mobile/lib/providers/infrastructure/storage.provider.dart index 5bbbe51497..ccca964027 100644 --- a/mobile/lib/providers/infrastructure/storage.provider.dart +++ b/mobile/lib/providers/infrastructure/storage.provider.dart @@ -1,6 +1,4 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; -final storageRepositoryProvider = Provider( - (ref) => const StorageRepository(), -); +final storageRepositoryProvider = Provider((ref) => const StorageRepository()); diff --git a/mobile/lib/providers/infrastructure/store.provider.g.dart b/mobile/lib/providers/infrastructure/store.provider.g.dart index 22b783013a..98c978cb60 100644 --- a/mobile/lib/providers/infrastructure/store.provider.g.dart +++ b/mobile/lib/providers/infrastructure/store.provider.g.dart @@ -30,8 +30,9 @@ String _$storeServiceHash() => r'250e10497c42df360e9e1f9a618d0b19c1b5b0a0'; final storeServiceProvider = Provider.internal( storeService, name: r'storeServiceProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$storeServiceHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$storeServiceHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/mobile/lib/providers/infrastructure/sync.provider.dart b/mobile/lib/providers/infrastructure/sync.provider.dart index 2406c37fa8..ddc6eed441 100644 --- a/mobile/lib/providers/infrastructure/sync.provider.dart +++ b/mobile/lib/providers/infrastructure/sync.provider.dart @@ -20,13 +20,9 @@ final syncStreamServiceProvider = Provider( ), ); -final syncApiRepositoryProvider = Provider( - (ref) => SyncApiRepository(ref.watch(apiServiceProvider)), -); +final syncApiRepositoryProvider = Provider((ref) => SyncApiRepository(ref.watch(apiServiceProvider))); -final syncStreamRepositoryProvider = Provider( - (ref) => SyncStreamRepository(ref.watch(driftProvider)), -); +final syncStreamRepositoryProvider = Provider((ref) => SyncStreamRepository(ref.watch(driftProvider))); final localSyncServiceProvider = Provider( (ref) => LocalSyncService( diff --git a/mobile/lib/providers/infrastructure/timeline.provider.dart b/mobile/lib/providers/infrastructure/timeline.provider.dart index 1f8c344f31..06ec0242b2 100644 --- a/mobile/lib/providers/infrastructure/timeline.provider.dart +++ b/mobile/lib/providers/infrastructure/timeline.provider.dart @@ -33,13 +33,11 @@ final timelineFactoryProvider = Provider( ), ); -final timelineUsersProvider = StreamProvider>( - (ref) { - final currentUserId = ref.watch(currentUserProvider.select((u) => u?.id)); - if (currentUserId == null) { - return Stream.value([]); - } +final timelineUsersProvider = StreamProvider>((ref) { + final currentUserId = ref.watch(currentUserProvider.select((u) => u?.id)); + if (currentUserId == null) { + return Stream.value([]); + } - return ref.watch(timelineRepositoryProvider).watchTimelineUserIds(currentUserId); - }, -); + return ref.watch(timelineRepositoryProvider).watchTimelineUserIds(currentUserId); +}); diff --git a/mobile/lib/providers/infrastructure/user.provider.dart b/mobile/lib/providers/infrastructure/user.provider.dart index cd62be2bec..922b9866bb 100644 --- a/mobile/lib/providers/infrastructure/user.provider.dart +++ b/mobile/lib/providers/infrastructure/user.provider.dart @@ -22,10 +22,10 @@ UserApiRepository userApiRepository(Ref ref) => UserApiRepository(ref.watch(apiS @Riverpod(keepAlive: true) UserService userService(Ref ref) => UserService( - isarUserRepository: ref.watch(userRepositoryProvider), - userApiRepository: ref.watch(userApiRepositoryProvider), - storeService: ref.watch(storeServiceProvider), - ); + isarUserRepository: ref.watch(userRepositoryProvider), + userApiRepository: ref.watch(userApiRepositoryProvider), + storeService: ref.watch(storeServiceProvider), +); /// Drifts final driftPartnerRepositoryProvider = Provider( @@ -33,12 +33,7 @@ final driftPartnerRepositoryProvider = Provider( ); final driftPartnerServiceProvider = Provider( - (ref) => DriftPartnerService( - ref.watch(driftPartnerRepositoryProvider), - ref.watch(partnerApiRepositoryProvider), - ), + (ref) => DriftPartnerService(ref.watch(driftPartnerRepositoryProvider), ref.watch(partnerApiRepositoryProvider)), ); -final partnerUsersProvider = NotifierProvider>( - PartnerNotifier.new, -); +final partnerUsersProvider = NotifierProvider>(PartnerNotifier.new); diff --git a/mobile/lib/providers/infrastructure/user.provider.g.dart b/mobile/lib/providers/infrastructure/user.provider.g.dart index 7664b15fd5..f9148bf3a7 100644 --- a/mobile/lib/providers/infrastructure/user.provider.g.dart +++ b/mobile/lib/providers/infrastructure/user.provider.g.dart @@ -47,8 +47,9 @@ String _$userServiceHash() => r'181414dddc7891be6237e13d568c287a804228d1'; final userServiceProvider = Provider.internal( userService, name: r'userServiceProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$userServiceHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$userServiceHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/mobile/lib/providers/local_auth.provider.dart b/mobile/lib/providers/local_auth.provider.dart index 56a5b2191b..44fc5ad80c 100644 --- a/mobile/lib/providers/local_auth.provider.dart +++ b/mobile/lib/providers/local_auth.provider.dart @@ -10,10 +10,7 @@ import 'package:immich_mobile/services/secure_storage.service.dart'; import 'package:logging/logging.dart'; final localAuthProvider = StateNotifierProvider((ref) { - return LocalAuthNotifier( - ref.watch(localAuthServiceProvider), - ref.watch(secureStorageServiceProvider), - ); + return LocalAuthNotifier(ref.watch(localAuthServiceProvider), ref.watch(secureStorageServiceProvider)); }); class LocalAuthNotifier extends StateNotifier { @@ -23,17 +20,9 @@ class LocalAuthNotifier extends StateNotifier { final _log = Logger("LocalAuthNotifier"); LocalAuthNotifier(this._localAuthService, this._secureStorageService) - : super( - const BiometricStatus( - availableBiometrics: [], - canAuthenticate: false, - ), - ) { + : super(const BiometricStatus(availableBiometrics: [], canAuthenticate: false)) { _localAuthService.getStatus().then((value) { - state = state.copyWith( - canAuthenticate: value.canAuthenticate, - availableBiometrics: value.availableBiometrics, - ); + state = state.copyWith(canAuthenticate: value.canAuthenticate, availableBiometrics: value.availableBiometrics); }); } @@ -79,10 +68,7 @@ class LocalAuthNotifier extends StateNotifier { if (errorMessage.isNotEmpty) { context.showSnackBar( SnackBar( - content: Text( - errorMessage, - style: context.textTheme.labelLarge, - ), + content: Text(errorMessage, style: context.textTheme.labelLarge), duration: const Duration(seconds: 3), backgroundColor: context.colorScheme.errorContainer, ), diff --git a/mobile/lib/providers/map/map_marker.provider.g.dart b/mobile/lib/providers/map/map_marker.provider.g.dart index a4c1db7dc0..80a21a39b2 100644 --- a/mobile/lib/providers/map/map_marker.provider.g.dart +++ b/mobile/lib/providers/map/map_marker.provider.g.dart @@ -13,8 +13,9 @@ String _$mapMarkersHash() => r'a0c129fcddbf1b9bce4aafcd2e47a858ab6ef1c9'; final mapMarkersProvider = AutoDisposeFutureProvider>.internal( mapMarkers, name: r'mapMarkersProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$mapMarkersHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$mapMarkersHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/mobile/lib/providers/map/map_service.provider.g.dart b/mobile/lib/providers/map/map_service.provider.g.dart index 0bb5094c61..e8eb1cd1ee 100644 --- a/mobile/lib/providers/map/map_service.provider.g.dart +++ b/mobile/lib/providers/map/map_service.provider.g.dart @@ -13,8 +13,9 @@ String _$mapServiceHash() => r'ffc8f38b726083452b9df236ed58903879348987'; final mapServiceProvider = AutoDisposeProvider.internal( mapService, name: r'mapServiceProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$mapServiceHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$mapServiceHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/mobile/lib/providers/map/map_state.provider.dart b/mobile/lib/providers/map/map_state.provider.dart index 4337654be0..31f2849df6 100644 --- a/mobile/lib/providers/map/map_state.provider.dart +++ b/mobile/lib/providers/map/map_state.provider.dart @@ -28,22 +28,13 @@ class MapStateNotifier extends _$MapStateNotifier { } void switchTheme(ThemeMode mode) { - ref.read(appSettingsServiceProvider).setSetting( - AppSettingsEnum.mapThemeMode, - mode.index, - ); + ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapThemeMode, mode.index); state = state.copyWith(themeMode: mode); } void switchFavoriteOnly(bool isFavoriteOnly) { - ref.read(appSettingsServiceProvider).setSetting( - AppSettingsEnum.mapShowFavoriteOnly, - isFavoriteOnly, - ); - state = state.copyWith( - showFavoriteOnly: isFavoriteOnly, - shouldRefetchMarkers: true, - ); + ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapShowFavoriteOnly, isFavoriteOnly); + state = state.copyWith(showFavoriteOnly: isFavoriteOnly, shouldRefetchMarkers: true); } void setRefetchMarkers(bool shouldRefetch) { @@ -51,35 +42,17 @@ class MapStateNotifier extends _$MapStateNotifier { } void switchIncludeArchived(bool isIncludeArchived) { - ref.read(appSettingsServiceProvider).setSetting( - AppSettingsEnum.mapIncludeArchived, - isIncludeArchived, - ); - state = state.copyWith( - includeArchived: isIncludeArchived, - shouldRefetchMarkers: true, - ); + ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapIncludeArchived, isIncludeArchived); + state = state.copyWith(includeArchived: isIncludeArchived, shouldRefetchMarkers: true); } void switchWithPartners(bool isWithPartners) { - ref.read(appSettingsServiceProvider).setSetting( - AppSettingsEnum.mapwithPartners, - isWithPartners, - ); - state = state.copyWith( - withPartners: isWithPartners, - shouldRefetchMarkers: true, - ); + ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapwithPartners, isWithPartners); + state = state.copyWith(withPartners: isWithPartners, shouldRefetchMarkers: true); } void setRelativeTime(int relativeTime) { - ref.read(appSettingsServiceProvider).setSetting( - AppSettingsEnum.mapRelativeDate, - relativeTime, - ); - state = state.copyWith( - relativeTime: relativeTime, - shouldRefetchMarkers: true, - ); + ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapRelativeDate, relativeTime); + state = state.copyWith(relativeTime: relativeTime, shouldRefetchMarkers: true); } } diff --git a/mobile/lib/providers/map/map_state.provider.g.dart b/mobile/lib/providers/map/map_state.provider.g.dart index 85a237099c..94d0ff8698 100644 --- a/mobile/lib/providers/map/map_state.provider.g.dart +++ b/mobile/lib/providers/map/map_state.provider.g.dart @@ -12,14 +12,14 @@ String _$mapStateNotifierHash() => r'22e4e571bd0730dbc34b109255a62b920e9c7d66'; @ProviderFor(MapStateNotifier) final mapStateNotifierProvider = NotifierProvider.internal( - MapStateNotifier.new, - name: r'mapStateNotifierProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$mapStateNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); + MapStateNotifier.new, + name: r'mapStateNotifierProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$mapStateNotifierHash, + dependencies: null, + allTransitiveDependencies: null, + ); typedef _$MapStateNotifier = Notifier; // ignore_for_file: type=lint diff --git a/mobile/lib/providers/network.provider.dart b/mobile/lib/providers/network.provider.dart index 5cb2fae4b1..cd91ff6d56 100644 --- a/mobile/lib/providers/network.provider.dart +++ b/mobile/lib/providers/network.provider.dart @@ -2,9 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/services/network.service.dart'; final networkProvider = StateNotifierProvider((ref) { - return NetworkNotifier( - ref.watch(networkServiceProvider), - ); + return NetworkNotifier(ref.watch(networkServiceProvider)); }); class NetworkNotifier extends StateNotifier { diff --git a/mobile/lib/providers/notification_permission.provider.dart b/mobile/lib/providers/notification_permission.provider.dart index e293452390..da0badd4ec 100644 --- a/mobile/lib/providers/notification_permission.provider.dart +++ b/mobile/lib/providers/notification_permission.provider.dart @@ -5,9 +5,7 @@ import 'package:permission_handler/permission_handler.dart'; class NotificationPermissionNotifier extends StateNotifier { NotificationPermissionNotifier() - : super( - Platform.isAndroid ? PermissionStatus.granted : PermissionStatus.restricted, - ) { + : super(Platform.isAndroid ? PermissionStatus.granted : PermissionStatus.restricted) { // Sets the initial state getNotificationPermission().then((p) => state = p); } diff --git a/mobile/lib/providers/partner.provider.dart b/mobile/lib/providers/partner.provider.dart index 37e07958d3..5a85cea1d4 100644 --- a/mobile/lib/providers/partner.provider.dart +++ b/mobile/lib/providers/partner.provider.dart @@ -12,17 +12,20 @@ class PartnerSharedWithNotifier extends StateNotifier> { PartnerSharedWithNotifier(this._partnerService) : super([]) { Function eq = const ListEquality().equals; - _partnerService.getSharedWith().then((partners) { - if (!eq(state, partners)) { - state = partners; - } - }).then((_) { - streamSub = _partnerService.watchSharedWith().listen((partners) { - if (!eq(state, partners)) { - state = partners; - } - }); - }); + _partnerService + .getSharedWith() + .then((partners) { + if (!eq(state, partners)) { + state = partners; + } + }) + .then((_) { + streamSub = _partnerService.watchSharedWith().listen((partners) { + if (!eq(state, partners)) { + state = partners; + } + }); + }); } Future updatePartner(UserDto partner, {required bool inTimeline}) { @@ -39,9 +42,7 @@ class PartnerSharedWithNotifier extends StateNotifier> { } final partnerSharedWithProvider = StateNotifierProvider>((ref) { - return PartnerSharedWithNotifier( - ref.watch(partnerServiceProvider), - ); + return PartnerSharedWithNotifier(ref.watch(partnerServiceProvider)); }); class PartnerSharedByNotifier extends StateNotifier> { @@ -50,17 +51,20 @@ class PartnerSharedByNotifier extends StateNotifier> { PartnerSharedByNotifier(this._partnerService) : super([]) { Function eq = const ListEquality().equals; - _partnerService.getSharedBy().then((partners) { - if (!eq(state, partners)) { - state = partners; - } - }).then((_) { - streamSub = _partnerService.watchSharedBy().listen((partners) { - if (!eq(state, partners)) { - state = partners; - } - }); - }); + _partnerService + .getSharedBy() + .then((partners) { + if (!eq(state, partners)) { + state = partners; + } + }) + .then((_) { + streamSub = _partnerService.watchSharedBy().listen((partners) { + if (!eq(state, partners)) { + state = partners; + } + }); + }); } @override diff --git a/mobile/lib/providers/search/paginated_search.provider.dart b/mobile/lib/providers/search/paginated_search.provider.dart index dbeacb45c6..9a37d83320 100644 --- a/mobile/lib/providers/search/paginated_search.provider.dart +++ b/mobile/lib/providers/search/paginated_search.provider.dart @@ -28,10 +28,7 @@ class PaginatedSearchNotifier extends StateNotifier { return false; } - state = SearchResult( - assets: [...state.assets, ...result.assets], - nextPage: result.nextPage, - ); + state = SearchResult(assets: [...state.assets, ...result.assets], nextPage: result.nextPage); return true; } @@ -42,13 +39,8 @@ class PaginatedSearchNotifier extends StateNotifier { } @riverpod -Future paginatedSearchRenderList( - Ref ref, -) { +Future paginatedSearchRenderList(Ref ref) { final result = ref.watch(paginatedSearchProvider); final timelineService = ref.watch(timelineServiceProvider); - return timelineService.getTimelineFromAssets( - result.assets, - GroupAssetsBy.none, - ); + return timelineService.getTimelineFromAssets(result.assets, GroupAssetsBy.none); } diff --git a/mobile/lib/providers/search/paginated_search.provider.g.dart b/mobile/lib/providers/search/paginated_search.provider.g.dart index 650cf130fc..e984997967 100644 --- a/mobile/lib/providers/search/paginated_search.provider.g.dart +++ b/mobile/lib/providers/search/paginated_search.provider.g.dart @@ -13,14 +13,14 @@ String _$paginatedSearchRenderListHash() => @ProviderFor(paginatedSearchRenderList) final paginatedSearchRenderListProvider = AutoDisposeFutureProvider.internal( - paginatedSearchRenderList, - name: r'paginatedSearchRenderListProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$paginatedSearchRenderListHash, - dependencies: null, - allTransitiveDependencies: null, -); + paginatedSearchRenderList, + name: r'paginatedSearchRenderListProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$paginatedSearchRenderListHash, + dependencies: null, + allTransitiveDependencies: null, + ); @Deprecated('Will be removed in 3.0. Use Ref instead') // ignore: unused_element diff --git a/mobile/lib/providers/search/people.provider.dart b/mobile/lib/providers/search/people.provider.dart index f0faabd35a..3ff8d67983 100644 --- a/mobile/lib/providers/search/people.provider.dart +++ b/mobile/lib/providers/search/people.provider.dart @@ -9,9 +9,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'people.provider.g.dart'; @riverpod -Future> getAllPeople( - Ref ref, -) async { +Future> getAllPeople(Ref ref) async { final PersonService personService = ref.read(personServiceProvider); final people = await personService.getAllPeople(); @@ -30,11 +28,7 @@ Future personAssets(Ref ref, String personId) async { } @riverpod -Future updatePersonName( - Ref ref, - String personId, - String updatedName, -) async { +Future updatePersonName(Ref ref, String personId, String updatedName) async { final PersonService personService = ref.read(personServiceProvider); final person = await personService.updateName(personId, updatedName); diff --git a/mobile/lib/providers/search/people.provider.g.dart b/mobile/lib/providers/search/people.provider.g.dart index 4625891abb..9595c36eec 100644 --- a/mobile/lib/providers/search/people.provider.g.dart +++ b/mobile/lib/providers/search/people.provider.g.dart @@ -12,13 +12,14 @@ String _$getAllPeopleHash() => r'2c5e6a207683f15ab209650615fdf9cb7f76c736'; @ProviderFor(getAllPeople) final getAllPeopleProvider = AutoDisposeFutureProvider>.internal( - getAllPeople, - name: r'getAllPeopleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$getAllPeopleHash, - dependencies: null, - allTransitiveDependencies: null, -); + getAllPeople, + name: r'getAllPeopleProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$getAllPeopleHash, + dependencies: null, + allTransitiveDependencies: null, + ); @Deprecated('Will be removed in 3.0. Use Ref instead') // ignore: unused_element @@ -56,21 +57,15 @@ class PersonAssetsFamily extends Family> { const PersonAssetsFamily(); /// See also [personAssets]. - PersonAssetsProvider call( - String personId, - ) { - return PersonAssetsProvider( - personId, - ); + PersonAssetsProvider call(String personId) { + return PersonAssetsProvider(personId); } @override PersonAssetsProvider getProviderOverride( covariant PersonAssetsProvider provider, ) { - return call( - provider.personId, - ); + return call(provider.personId); } static const Iterable? _dependencies = null; @@ -91,24 +86,19 @@ class PersonAssetsFamily extends Family> { /// See also [personAssets]. class PersonAssetsProvider extends AutoDisposeFutureProvider { /// See also [personAssets]. - PersonAssetsProvider( - String personId, - ) : this._internal( - (ref) => personAssets( - ref as PersonAssetsRef, - personId, - ), - from: personAssetsProvider, - name: r'personAssetsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$personAssetsHash, - dependencies: PersonAssetsFamily._dependencies, - allTransitiveDependencies: - PersonAssetsFamily._allTransitiveDependencies, - personId: personId, - ); + PersonAssetsProvider(String personId) + : this._internal( + (ref) => personAssets(ref as PersonAssetsRef, personId), + from: personAssetsProvider, + name: r'personAssetsProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$personAssetsHash, + dependencies: PersonAssetsFamily._dependencies, + allTransitiveDependencies: + PersonAssetsFamily._allTransitiveDependencies, + personId: personId, + ); PersonAssetsProvider._internal( super._createNotifier, { @@ -167,7 +157,8 @@ mixin PersonAssetsRef on AutoDisposeFutureProviderRef { } class _PersonAssetsProviderElement - extends AutoDisposeFutureProviderElement with PersonAssetsRef { + extends AutoDisposeFutureProviderElement + with PersonAssetsRef { _PersonAssetsProviderElement(super.provider); @override @@ -186,24 +177,15 @@ class UpdatePersonNameFamily extends Family> { const UpdatePersonNameFamily(); /// See also [updatePersonName]. - UpdatePersonNameProvider call( - String personId, - String updatedName, - ) { - return UpdatePersonNameProvider( - personId, - updatedName, - ); + UpdatePersonNameProvider call(String personId, String updatedName) { + return UpdatePersonNameProvider(personId, updatedName); } @override UpdatePersonNameProvider getProviderOverride( covariant UpdatePersonNameProvider provider, ) { - return call( - provider.personId, - provider.updatedName, - ); + return call(provider.personId, provider.updatedName); } static const Iterable? _dependencies = null; @@ -224,27 +206,21 @@ class UpdatePersonNameFamily extends Family> { /// See also [updatePersonName]. class UpdatePersonNameProvider extends AutoDisposeFutureProvider { /// See also [updatePersonName]. - UpdatePersonNameProvider( - String personId, - String updatedName, - ) : this._internal( - (ref) => updatePersonName( - ref as UpdatePersonNameRef, - personId, - updatedName, - ), - from: updatePersonNameProvider, - name: r'updatePersonNameProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$updatePersonNameHash, - dependencies: UpdatePersonNameFamily._dependencies, - allTransitiveDependencies: - UpdatePersonNameFamily._allTransitiveDependencies, - personId: personId, - updatedName: updatedName, - ); + UpdatePersonNameProvider(String personId, String updatedName) + : this._internal( + (ref) => + updatePersonName(ref as UpdatePersonNameRef, personId, updatedName), + from: updatePersonNameProvider, + name: r'updatePersonNameProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$updatePersonNameHash, + dependencies: UpdatePersonNameFamily._dependencies, + allTransitiveDependencies: + UpdatePersonNameFamily._allTransitiveDependencies, + personId: personId, + updatedName: updatedName, + ); UpdatePersonNameProvider._internal( super._createNotifier, { @@ -312,7 +288,8 @@ mixin UpdatePersonNameRef on AutoDisposeFutureProviderRef { } class _UpdatePersonNameProviderElement - extends AutoDisposeFutureProviderElement with UpdatePersonNameRef { + extends AutoDisposeFutureProviderElement + with UpdatePersonNameRef { _UpdatePersonNameProviderElement(super.provider); @override @@ -320,5 +297,6 @@ class _UpdatePersonNameProviderElement @override String get updatedName => (origin as UpdatePersonNameProvider).updatedName; } + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/mobile/lib/providers/search/search_filter.provider.g.dart b/mobile/lib/providers/search/search_filter.provider.g.dart index 03f88b0332..5a322ca285 100644 --- a/mobile/lib/providers/search/search_filter.provider.g.dart +++ b/mobile/lib/providers/search/search_filter.provider.g.dart @@ -95,29 +95,28 @@ class GetSearchSuggestionsProvider String? make, String? model, }) : this._internal( - (ref) => getSearchSuggestions( - ref as GetSearchSuggestionsRef, - type, - locationCountry: locationCountry, - locationState: locationState, - make: make, - model: model, - ), - from: getSearchSuggestionsProvider, - name: r'getSearchSuggestionsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$getSearchSuggestionsHash, - dependencies: GetSearchSuggestionsFamily._dependencies, - allTransitiveDependencies: - GetSearchSuggestionsFamily._allTransitiveDependencies, - type: type, - locationCountry: locationCountry, - locationState: locationState, - make: make, - model: model, - ); + (ref) => getSearchSuggestions( + ref as GetSearchSuggestionsRef, + type, + locationCountry: locationCountry, + locationState: locationState, + make: make, + model: model, + ), + from: getSearchSuggestionsProvider, + name: r'getSearchSuggestionsProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$getSearchSuggestionsHash, + dependencies: GetSearchSuggestionsFamily._dependencies, + allTransitiveDependencies: + GetSearchSuggestionsFamily._allTransitiveDependencies, + type: type, + locationCountry: locationCountry, + locationState: locationState, + make: make, + model: model, + ); GetSearchSuggestionsProvider._internal( super._createNotifier, { @@ -227,5 +226,6 @@ class _GetSearchSuggestionsProviderElement @override String? get model => (origin as GetSearchSuggestionsProvider).model; } + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/mobile/lib/providers/search/search_page_state.provider.dart b/mobile/lib/providers/search/search_page_state.provider.dart index 4bbceca383..23d5606922 100644 --- a/mobile/lib/providers/search/search_page_state.provider.dart +++ b/mobile/lib/providers/search/search_page_state.provider.dart @@ -14,14 +14,7 @@ final getPreviewPlacesProvider = FutureProvider.autoDispose data.fieldName == "exifInfo.city").items; - final curatedContent = locations - .map( - (l) => SearchCuratedContent( - label: l.value, - id: l.data.id, - ), - ) - .toList(); + final curatedContent = locations.map((l) => SearchCuratedContent(label: l.value, id: l.data.id)).toList(); return curatedContent; }); @@ -36,12 +29,7 @@ final getAllPlacesProvider = FutureProvider.autoDispose SearchCuratedContent( - label: data.exifInfo!.city!, - id: data.id, - ), - ) + .map((data) => SearchCuratedContent(label: data.exifInfo!.city!, id: data.id)) .toList(); return curatedContent; diff --git a/mobile/lib/providers/server_info.provider.dart b/mobile/lib/providers/server_info.provider.dart index 4a5e65878b..25b1002b7a 100644 --- a/mobile/lib/providers/server_info.provider.dart +++ b/mobile/lib/providers/server_info.provider.dart @@ -11,42 +11,24 @@ import 'package:package_info_plus/package_info_plus.dart'; class ServerInfoNotifier extends StateNotifier { ServerInfoNotifier(this._serverInfoService) - : super( - const ServerInfo( - serverVersion: ServerVersion( - major: 0, - minor: 0, - patch: 0, - ), - latestVersion: ServerVersion( - major: 0, - minor: 0, - patch: 0, - ), - serverFeatures: ServerFeatures( - map: true, - trash: true, - oauthEnabled: false, - passwordLogin: true, - ), - serverConfig: ServerConfig( - trashDays: 30, - oauthButtonText: '', - externalDomain: '', - mapLightStyleUrl: 'https://tiles.immich.cloud/v1/style/light.json', - mapDarkStyleUrl: 'https://tiles.immich.cloud/v1/style/dark.json', - ), - serverDiskInfo: ServerDiskInfo( - diskAvailable: "0", - diskSize: "0", - diskUse: "0", - diskUsagePercentage: 0, - ), - isVersionMismatch: false, - isNewReleaseAvailable: false, - versionMismatchErrorMessage: "", + : super( + const ServerInfo( + serverVersion: ServerVersion(major: 0, minor: 0, patch: 0), + latestVersion: ServerVersion(major: 0, minor: 0, patch: 0), + serverFeatures: ServerFeatures(map: true, trash: true, oauthEnabled: false, passwordLogin: true), + serverConfig: ServerConfig( + trashDays: 30, + oauthButtonText: '', + externalDomain: '', + mapLightStyleUrl: 'https://tiles.immich.cloud/v1/style/light.json', + mapDarkStyleUrl: 'https://tiles.immich.cloud/v1/style/dark.json', ), - ); + serverDiskInfo: ServerDiskInfo(diskAvailable: "0", diskSize: "0", diskUse: "0", diskUsagePercentage: 0), + isVersionMismatch: false, + isNewReleaseAvailable: false, + versionMismatchErrorMessage: "", + ), + ); final ServerInfoService _serverInfoService; final _log = Logger("ServerInfoNotifier"); @@ -62,19 +44,14 @@ class ServerInfoNotifier extends StateNotifier { final serverVersion = await _serverInfoService.getServerVersion(); if (serverVersion == null) { - state = state.copyWith( - isVersionMismatch: true, - versionMismatchErrorMessage: "common_server_error".tr(), - ); + state = state.copyWith(isVersionMismatch: true, versionMismatchErrorMessage: "common_server_error".tr()); return; } await _checkServerVersionMismatch(serverVersion); } catch (e, stackTrace) { _log.severe("Failed to get server version", e, stackTrace); - state = state.copyWith( - isVersionMismatch: true, - ); + state = state.copyWith(isVersionMismatch: true); return; } } @@ -118,29 +95,21 @@ class ServerInfoNotifier extends StateNotifier { return; } - state = state.copyWith( - isVersionMismatch: false, - versionMismatchErrorMessage: "", - ); + state = state.copyWith(isVersionMismatch: false, versionMismatchErrorMessage: ""); } - handleNewRelease( - ServerVersion serverVersion, - ServerVersion latestVersion, - ) { + handleNewRelease(ServerVersion serverVersion, ServerVersion latestVersion) { // Update local server version _checkServerVersionMismatch(serverVersion); final majorEqual = latestVersion.major == serverVersion.major; final minorEqual = majorEqual && latestVersion.minor == serverVersion.minor; - final newVersionAvailable = latestVersion.major > serverVersion.major || + final newVersionAvailable = + latestVersion.major > serverVersion.major || (majorEqual && latestVersion.minor > serverVersion.minor) || (minorEqual && latestVersion.patch > serverVersion.patch); - state = state.copyWith( - latestVersion: latestVersion, - isNewReleaseAvailable: newVersionAvailable, - ); + state = state.copyWith(latestVersion: latestVersion, isNewReleaseAvailable: newVersionAvailable); } getServerFeatures() async { @@ -166,11 +135,7 @@ class ServerInfoNotifier extends StateNotifier { var minor = detail[1]; var patch = detail[2]; - return { - "major": int.parse(major), - "minor": int.parse(minor), - "patch": int.parse(patch.replaceAll("-DEBUG", "")), - }; + return {"major": int.parse(major), "minor": int.parse(minor), "patch": int.parse(patch.replaceAll("-DEBUG", ""))}; } } diff --git a/mobile/lib/providers/shared_link.provider.dart b/mobile/lib/providers/shared_link.provider.dart index 32dfed51f2..f574554bcb 100644 --- a/mobile/lib/providers/shared_link.provider.dart +++ b/mobile/lib/providers/shared_link.provider.dart @@ -21,7 +21,5 @@ class SharedLinksNotifier extends StateNotifier>> { } final sharedLinksStateProvider = StateNotifierProvider>>((ref) { - return SharedLinksNotifier( - ref.watch(sharedLinkServiceProvider), - ); + return SharedLinksNotifier(ref.watch(sharedLinkServiceProvider)); }); diff --git a/mobile/lib/providers/sync_status.provider.dart b/mobile/lib/providers/sync_status.provider.dart index bf535f525d..8e24bbf4d0 100644 --- a/mobile/lib/providers/sync_status.provider.dart +++ b/mobile/lib/providers/sync_status.provider.dart @@ -12,7 +12,7 @@ enum SyncStatus { SyncStatus.idle => "idle".tr(), SyncStatus.syncing => "running".tr(), SyncStatus.success => "success".tr(), - SyncStatus.error => "error".tr() + SyncStatus.error => "error".tr(), }; } } @@ -60,12 +60,7 @@ class SyncStatusState { } @override - int get hashCode => Object.hash( - remoteSyncStatus, - localSyncStatus, - hashJobStatus, - errorMessage, - ); + int get hashCode => Object.hash(remoteSyncStatus, localSyncStatus, hashJobStatus, errorMessage); } class SyncStatusNotifier extends Notifier { @@ -84,10 +79,7 @@ class SyncStatusNotifier extends Notifier { /// void setRemoteSyncStatus(SyncStatus status, [String? errorMessage]) { - state = state.copyWith( - remoteSyncStatus: status, - errorMessage: status == SyncStatus.error ? errorMessage : null, - ); + state = state.copyWith(remoteSyncStatus: status, errorMessage: status == SyncStatus.error ? errorMessage : null); } void startRemoteSync() => setRemoteSyncStatus(SyncStatus.syncing); @@ -99,10 +91,7 @@ class SyncStatusNotifier extends Notifier { /// void setLocalSyncStatus(SyncStatus status, [String? errorMessage]) { - state = state.copyWith( - localSyncStatus: status, - errorMessage: status == SyncStatus.error ? errorMessage : null, - ); + state = state.copyWith(localSyncStatus: status, errorMessage: status == SyncStatus.error ? errorMessage : null); } void startLocalSync() => setLocalSyncStatus(SyncStatus.syncing); @@ -114,10 +103,7 @@ class SyncStatusNotifier extends Notifier { /// void setHashJobStatus(SyncStatus status, [String? errorMessage]) { - state = state.copyWith( - hashJobStatus: status, - errorMessage: status == SyncStatus.error ? errorMessage : null, - ); + state = state.copyWith(hashJobStatus: status, errorMessage: status == SyncStatus.error ? errorMessage : null); } void startHashJob() => setHashJobStatus(SyncStatus.syncing); @@ -125,6 +111,4 @@ class SyncStatusNotifier extends Notifier { void errorHashJob(String error) => setHashJobStatus(SyncStatus.error, error); } -final syncStatusProvider = NotifierProvider( - SyncStatusNotifier.new, -); +final syncStatusProvider = NotifierProvider(SyncStatusNotifier.new); diff --git a/mobile/lib/providers/tab.provider.dart b/mobile/lib/providers/tab.provider.dart index a4875115ce..d523e72c38 100644 --- a/mobile/lib/providers/tab.provider.dart +++ b/mobile/lib/providers/tab.provider.dart @@ -3,6 +3,4 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; enum TabEnum { home, search, albums, library } /// Provides the currently active tab -final tabProvider = StateProvider( - (ref) => TabEnum.home, -); +final tabProvider = StateProvider((ref) => TabEnum.home); diff --git a/mobile/lib/providers/theme.provider.dart b/mobile/lib/providers/theme.provider.dart index bdf3735f8e..5f32e07578 100644 --- a/mobile/lib/providers/theme.provider.dart +++ b/mobile/lib/providers/theme.provider.dart @@ -31,13 +31,8 @@ final immichThemePresetProvider = StateProvider((ref) { try { return ImmichColorPreset.values.firstWhere((e) => e.name == primaryColorPreset); } catch (e) { - debugPrint( - "Theme preset $primaryColorPreset not found. Applying default preset.", - ); - appSettingsProvider.setSetting( - AppSettingsEnum.primaryColor, - defaultColorPresetName, - ); + debugPrint("Theme preset $primaryColorPreset not found. Applying default preset."); + appSettingsProvider.setSetting(AppSettingsEnum.primaryColor, defaultColorPresetName); return defaultColorPreset; } }); diff --git a/mobile/lib/providers/timeline.provider.dart b/mobile/lib/providers/timeline.provider.dart index 6faccff030..71ea308dbf 100644 --- a/mobile/lib/providers/timeline.provider.dart +++ b/mobile/lib/providers/timeline.provider.dart @@ -5,27 +5,21 @@ import 'package:immich_mobile/providers/locale_provider.dart'; import 'package:immich_mobile/services/timeline.service.dart'; import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; -final singleUserTimelineProvider = StreamProvider.family( - (ref, userId) { - if (userId == null) { - return const Stream.empty(); - } +final singleUserTimelineProvider = StreamProvider.family((ref, userId) { + if (userId == null) { + return const Stream.empty(); + } - ref.watch(localeProvider); - final timelineService = ref.watch(timelineServiceProvider); - return timelineService.watchHomeTimeline(userId); - }, - dependencies: [localeProvider], -); + ref.watch(localeProvider); + final timelineService = ref.watch(timelineServiceProvider); + return timelineService.watchHomeTimeline(userId); +}, dependencies: [localeProvider]); -final multiUsersTimelineProvider = StreamProvider.family>( - (ref, userIds) { - ref.watch(localeProvider); - final timelineService = ref.watch(timelineServiceProvider); - return timelineService.watchMultiUsersTimeline(userIds); - }, - dependencies: [localeProvider], -); +final multiUsersTimelineProvider = StreamProvider.family>((ref, userIds) { + ref.watch(localeProvider); + final timelineService = ref.watch(timelineServiceProvider); + return timelineService.watchMultiUsersTimeline(userIds); +}, dependencies: [localeProvider]); final albumTimelineProvider = StreamProvider.autoDispose.family((ref, id) { final album = ref.watch(albumWatcher(id)).value; @@ -65,10 +59,7 @@ final assetSelectionTimelineProvider = StreamProvider((ref) { final assetsTimelineProvider = FutureProvider.family>((ref, assets) { final timelineService = ref.watch(timelineServiceProvider); - return timelineService.getTimelineFromAssets( - assets, - null, - ); + return timelineService.getTimelineFromAssets(assets, null); }); final lockedTimelineProvider = StreamProvider((ref) { diff --git a/mobile/lib/providers/timeline/multiselect.provider.dart b/mobile/lib/providers/timeline/multiselect.provider.dart index d980ad22b5..742cbd7dea 100644 --- a/mobile/lib/providers/timeline/multiselect.provider.dart +++ b/mobile/lib/providers/timeline/multiselect.provider.dart @@ -15,26 +15,17 @@ class MultiSelectState { final Set lockedSelectionAssets; final bool forceEnable; - const MultiSelectState({ - required this.selectedAssets, - required this.lockedSelectionAssets, - this.forceEnable = false, - }); + const MultiSelectState({required this.selectedAssets, required this.lockedSelectionAssets, this.forceEnable = false}); bool get isEnabled => selectedAssets.isNotEmpty; /// Cloud only - bool get hasRemote => selectedAssets.any( - (asset) => asset.storage == AssetState.remote || asset.storage == AssetState.merged, - ); + bool get hasRemote => + selectedAssets.any((asset) => asset.storage == AssetState.remote || asset.storage == AssetState.merged); - bool get hasLocal => selectedAssets.any( - (asset) => asset.storage == AssetState.local, - ); + bool get hasLocal => selectedAssets.any((asset) => asset.storage == AssetState.local); - bool get hasMerged => selectedAssets.any( - (asset) => asset.storage == AssetState.merged, - ); + bool get hasMerged => selectedAssets.any((asset) => asset.storage == AssetState.merged); MultiSelectState copyWith({ Set? selectedAssets, @@ -74,12 +65,7 @@ class MultiSelectNotifier extends Notifier { @override MultiSelectState build() { - return _defaultState ?? - const MultiSelectState( - selectedAssets: {}, - lockedSelectionAssets: {}, - forceEnable: false, - ); + return _defaultState ?? const MultiSelectState(selectedAssets: {}, lockedSelectionAssets: {}, forceEnable: false); } void selectAsset(BaseAsset asset) { @@ -87,9 +73,7 @@ class MultiSelectNotifier extends Notifier { return; } - state = state.copyWith( - selectedAssets: {...state.selectedAssets, asset}, - ); + state = state.copyWith(selectedAssets: {...state.selectedAssets, asset}); } void deselectAsset(BaseAsset asset) { @@ -97,9 +81,7 @@ class MultiSelectNotifier extends Notifier { return; } - state = state.copyWith( - selectedAssets: state.selectedAssets.where((a) => a != asset).toSet(), - ); + state = state.copyWith(selectedAssets: state.selectedAssets.where((a) => a != asset).toSet()); } void toggleAssetSelection(BaseAsset asset) { @@ -111,11 +93,7 @@ class MultiSelectNotifier extends Notifier { } void reset() { - state = const MultiSelectState( - selectedAssets: {}, - lockedSelectionAssets: {}, - forceEnable: false, - ); + state = const MultiSelectState(selectedAssets: {}, lockedSelectionAssets: {}, forceEnable: false); } /// Bucket bulk operations @@ -125,9 +103,7 @@ class MultiSelectNotifier extends Notifier { selectedAssets.addAll(assets); - state = state.copyWith( - selectedAssets: selectedAssets, - ); + state = state.copyWith(selectedAssets: selectedAssets); } void deselectBucket(int offset, int bucketCount) async { @@ -164,20 +140,15 @@ class MultiSelectNotifier extends Notifier { } void setLockedSelectionAssets(Set assets) { - state = state.copyWith( - lockedSelectionAssets: assets, - ); + state = state.copyWith(lockedSelectionAssets: assets); } } -final bucketSelectionProvider = Provider.family>( - (ref, bucketAssets) { - final selectedAssets = ref.watch(multiSelectProvider.select((s) => s.selectedAssets)); +final bucketSelectionProvider = Provider.family>((ref, bucketAssets) { + final selectedAssets = ref.watch(multiSelectProvider.select((s) => s.selectedAssets)); - if (bucketAssets.isEmpty) return false; + if (bucketAssets.isEmpty) return false; - // Check if all assets in the bucket are selected - return bucketAssets.every((asset) => selectedAssets.contains(asset)); - }, - dependencies: [multiSelectProvider, timelineServiceProvider], -); + // Check if all assets in the bucket are selected + return bucketAssets.every((asset) => selectedAssets.contains(asset)); +}, dependencies: [multiSelectProvider, timelineServiceProvider]); diff --git a/mobile/lib/providers/trash.provider.dart b/mobile/lib/providers/trash.provider.dart index c78cccff8a..adf3b1027b 100644 --- a/mobile/lib/providers/trash.provider.dart +++ b/mobile/lib/providers/trash.provider.dart @@ -7,9 +7,7 @@ class TrashNotifier extends StateNotifier { final TrashService _trashService; final _log = Logger('TrashNotifier'); - TrashNotifier( - this._trashService, - ) : super(false); + TrashNotifier(this._trashService) : super(false); Future emptyTrash() async { try { @@ -43,7 +41,5 @@ class TrashNotifier extends StateNotifier { } final trashProvider = StateNotifierProvider((ref) { - return TrashNotifier( - ref.watch(trashServiceProvider), - ); + return TrashNotifier(ref.watch(trashServiceProvider)); }); diff --git a/mobile/lib/providers/upload_profile_image.provider.dart b/mobile/lib/providers/upload_profile_image.provider.dart index 0588b31b68..e9e467346b 100644 --- a/mobile/lib/providers/upload_profile_image.provider.dart +++ b/mobile/lib/providers/upload_profile_image.provider.dart @@ -6,26 +6,15 @@ import 'package:image_picker/image_picker.dart'; import 'package:immich_mobile/domain/services/user.service.dart'; import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; -enum UploadProfileStatus { - idle, - loading, - success, - failure, -} +enum UploadProfileStatus { idle, loading, success, failure } class UploadProfileImageState { // enum final UploadProfileStatus status; final String profileImagePath; - const UploadProfileImageState({ - required this.status, - required this.profileImagePath, - }); + const UploadProfileImageState({required this.status, required this.profileImagePath}); - UploadProfileImageState copyWith({ - UploadProfileStatus? status, - String? profileImagePath, - }) { + UploadProfileImageState copyWith({UploadProfileStatus? status, String? profileImagePath}) { return UploadProfileImageState( status: status ?? this.status, profileImagePath: profileImagePath ?? this.profileImagePath, @@ -68,29 +57,18 @@ class UploadProfileImageState { class UploadProfileImageNotifier extends StateNotifier { UploadProfileImageNotifier(this._userService) - : super( - const UploadProfileImageState( - profileImagePath: '', - status: UploadProfileStatus.idle, - ), - ); + : super(const UploadProfileImageState(profileImagePath: '', status: UploadProfileStatus.idle)); final UserService _userService; Future upload(XFile file) async { state = state.copyWith(status: UploadProfileStatus.loading); - var profileImagePath = await _userService.createProfileImage( - file.name, - await file.readAsBytes(), - ); + var profileImagePath = await _userService.createProfileImage(file.name, await file.readAsBytes()); if (profileImagePath != null) { debugPrint("Successfully upload profile image"); - state = state.copyWith( - status: UploadProfileStatus.success, - profileImagePath: profileImagePath, - ); + state = state.copyWith(status: UploadProfileStatus.success, profileImagePath: profileImagePath); return true; } diff --git a/mobile/lib/providers/websocket.provider.dart b/mobile/lib/providers/websocket.provider.dart index 6c24cc0568..fdc21592b5 100644 --- a/mobile/lib/providers/websocket.provider.dart +++ b/mobile/lib/providers/websocket.provider.dart @@ -22,23 +22,14 @@ import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; import 'package:socket_io_client/socket_io_client.dart'; -enum PendingAction { - assetDelete, - assetUploaded, - assetHidden, - assetTrash, -} +enum PendingAction { assetDelete, assetUploaded, assetHidden, assetTrash } class PendingChange { final String id; final PendingAction action; final dynamic value; - const PendingChange( - this.id, - this.action, - this.value, - ); + const PendingChange(this.id, this.action, this.value); @override String toString() => 'PendingChange(id: $id, action: $action, value: $value)'; @@ -59,17 +50,9 @@ class WebsocketState { final bool isConnected; final List pendingChanges; - const WebsocketState({ - this.socket, - required this.isConnected, - required this.pendingChanges, - }); + const WebsocketState({this.socket, required this.isConnected, required this.pendingChanges}); - WebsocketState copyWith({ - Socket? socket, - bool? isConnected, - List? pendingChanges, - }) { + WebsocketState copyWith({Socket? socket, bool? isConnected, List? pendingChanges}) { return WebsocketState( socket: socket ?? this.socket, isConnected: isConnected ?? this.isConnected, @@ -92,14 +75,7 @@ class WebsocketState { } class WebsocketNotifier extends StateNotifier { - WebsocketNotifier(this._ref) - : super( - const WebsocketState( - socket: null, - isConnected: false, - pendingChanges: [], - ), - ); + WebsocketNotifier(this._ref) : super(const WebsocketState(socket: null, isConnected: false, pendingChanges: [])); final _log = Logger('WebsocketNotifier'); final Ref _ref; @@ -147,29 +123,17 @@ class WebsocketNotifier extends StateNotifier { socket.onConnect((_) { debugPrint("Established Websocket Connection"); - state = WebsocketState( - isConnected: true, - socket: socket, - pendingChanges: state.pendingChanges, - ); + state = WebsocketState(isConnected: true, socket: socket, pendingChanges: state.pendingChanges); }); socket.onDisconnect((_) { debugPrint("Disconnect to Websocket Connection"); - state = WebsocketState( - isConnected: false, - socket: null, - pendingChanges: state.pendingChanges, - ); + state = WebsocketState(isConnected: false, socket: null, pendingChanges: state.pendingChanges); }); socket.on('error', (errorMessage) { _log.severe("Websocket Error - $errorMessage"); - state = WebsocketState( - isConnected: false, - socket: null, - pendingChanges: state.pendingChanges, - ); + state = WebsocketState(isConnected: false, socket: null, pendingChanges: state.pendingChanges); }); if (!Store.isBetaTimelineEnabled) { @@ -200,11 +164,7 @@ class WebsocketNotifier extends StateNotifier { var socket = state.socket?.disconnect(); if (socket?.disconnected == true) { - state = WebsocketState( - isConnected: false, - socket: null, - pendingChanges: state.pendingChanges, - ); + state = WebsocketState(isConnected: false, socket: null, pendingChanges: state.pendingChanges); } } @@ -248,10 +208,7 @@ class WebsocketNotifier extends StateNotifier { void addPendingChange(PendingAction action, dynamic value) { final now = DateTime.now(); state = state.copyWith( - pendingChanges: [ - ...state.pendingChanges, - PendingChange(now.millisecondsSinceEpoch.toString(), action, value), - ], + pendingChanges: [...state.pendingChanges, PendingChange(now.millisecondsSinceEpoch.toString(), action, value)], ); _debounce.run(handlePendingChanges); } @@ -264,9 +221,7 @@ class WebsocketNotifier extends StateNotifier { await _ref.read(syncServiceProvider).handleRemoteAssetRemoval(remoteIds); await _ref.read(assetProvider.notifier).getAllAsset(); - state = state.copyWith( - pendingChanges: state.pendingChanges.whereNot((c) => trashChanges.contains(c)).toList(), - ); + state = state.copyWith(pendingChanges: state.pendingChanges.whereNot((c) => trashChanges.contains(c)).toList()); } } @@ -275,9 +230,7 @@ class WebsocketNotifier extends StateNotifier { if (deleteChanges.isNotEmpty) { List remoteIds = deleteChanges.map((a) => a.value.toString()).toList(); await _ref.read(syncServiceProvider).handleRemoteAssetRemoval(remoteIds); - state = state.copyWith( - pendingChanges: state.pendingChanges.whereNot((c) => deleteChanges.contains(c)).toList(), - ); + state = state.copyWith(pendingChanges: state.pendingChanges.whereNot((c) => deleteChanges.contains(c)).toList()); } } @@ -304,9 +257,7 @@ class WebsocketNotifier extends StateNotifier { final db = _ref.watch(dbProvider); await db.writeTxn(() => db.assets.deleteAllByRemoteId(remoteIds)); - state = state.copyWith( - pendingChanges: state.pendingChanges.whereNot((c) => hiddenChanges.contains(c)).toList(), - ); + state = state.copyWith(pendingChanges: state.pendingChanges.whereNot((c) => hiddenChanges.contains(c)).toList()); } } @@ -372,9 +323,7 @@ class WebsocketNotifier extends StateNotifier { } try { - unawaited( - _ref.read(backgroundSyncProvider).syncWebsocketBatch(_batchedAssetUploadReady.toList()), - ); + unawaited(_ref.read(backgroundSyncProvider).syncWebsocketBatch(_batchedAssetUploadReady.toList())); } catch (error) { _log.severe("Error processing batched AssetUploadReadyV1 events: $error"); } diff --git a/mobile/lib/repositories/activity_api.repository.dart b/mobile/lib/repositories/activity_api.repository.dart index 36f380cba7..e8f9abc8c8 100644 --- a/mobile/lib/repositories/activity_api.repository.dart +++ b/mobile/lib/repositories/activity_api.repository.dart @@ -19,12 +19,7 @@ class ActivityApiRepository extends ApiRepository { return response.map(_toActivity).toList(); } - Future create( - String albumId, - ActivityType type, { - String? assetId, - String? comment, - }) async { + Future create(String albumId, ActivityType type, {String? assetId, String? comment}) async { final dto = ActivityCreateDto( albumId: albumId, type: type == ActivityType.comment ? ReactionType.comment : ReactionType.like, @@ -45,11 +40,11 @@ class ActivityApiRepository extends ApiRepository { } static Activity _toActivity(ActivityResponseDto dto) => Activity( - id: dto.id, - createdAt: dto.createdAt, - type: dto.type == ReactionType.comment ? ActivityType.comment : ActivityType.like, - user: UserConverter.fromSimpleUserDto(dto.user), - assetId: dto.assetId, - comment: dto.comment, - ); + id: dto.id, + createdAt: dto.createdAt, + type: dto.type == ReactionType.comment ? ActivityType.comment : ActivityType.like, + user: UserConverter.fromSimpleUserDto(dto.user), + assetId: dto.assetId, + comment: dto.comment, + ); } diff --git a/mobile/lib/repositories/album.repository.dart b/mobile/lib/repositories/album.repository.dart index c65dce325d..2d24004944 100644 --- a/mobile/lib/repositories/album.repository.dart +++ b/mobile/lib/repositories/album.repository.dart @@ -30,12 +30,7 @@ class AlbumRepository extends DatabaseRepository { Future create(Album album) => txn(() => db.albums.store(album)); - Future getByName( - String name, { - bool? shared, - bool? remote, - bool? owner, - }) { + Future getByName(String name, {bool? shared, bool? remote, bool? owner}) { var query = db.albums.filter().nameEqualTo(name); if (shared != null) { query = query.sharedEqualTo(shared); @@ -58,12 +53,7 @@ class AlbumRepository extends DatabaseRepository { Future delete(int albumId) => txn(() => db.albums.delete(albumId)); - Future> getAll({ - bool? shared, - bool? remote, - int? ownerId, - AlbumSort? sortBy, - }) { + Future> getAll({bool? shared, bool? remote, int? ownerId, AlbumSort? sortBy}) { final baseQuery = db.albums.where(); final QueryBuilder afterWhere; if (remote == null) { @@ -94,9 +84,8 @@ class AlbumRepository extends DatabaseRepository { return db.albums.filter().remoteIdEqualTo(remoteId).findFirst(); } - Future removeUsers(Album album, List users) => txn( - () => album.sharedUsers.update(unlink: users.map(entity.User.fromDto)), - ); + Future removeUsers(Album album, List users) => + txn(() => album.sharedUsers.update(unlink: users.map(entity.User.fromDto))); Future addAssets(Album album, List assets) => txn(() => album.assets.update(link: assets)); @@ -114,10 +103,7 @@ class AlbumRepository extends DatabaseRepository { Future deleteAllLocal() => txn(() => db.albums.where().localIdIsNotNull().deleteAll()); - Future> search( - String searchTerm, - QuickFilterMode filterMode, - ) async { + Future> search(String searchTerm, QuickFilterMode filterMode) async { var query = db.albums.filter().nameContains(searchTerm, caseSensitive: false).remoteIdIsNotNull(); final isarUserId = fastHash(Store.get(StoreKey.currentUser).id); diff --git a/mobile/lib/repositories/album_api.repository.dart b/mobile/lib/repositories/album_api.repository.dart index b5b7c72883..11fc1537c5 100644 --- a/mobile/lib/repositories/album_api.repository.dart +++ b/mobile/lib/repositories/album_api.repository.dart @@ -9,9 +9,7 @@ import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/repositories/api.repository.dart'; import 'package:openapi/api.dart'; -final albumApiRepositoryProvider = Provider( - (ref) => AlbumApiRepository(ref.watch(apiServiceProvider).albumsApi), -); +final albumApiRepositoryProvider = Provider((ref) => AlbumApiRepository(ref.watch(apiServiceProvider).albumsApi)); class AlbumApiRepository extends ApiRepository { final AlbumsApi _api; @@ -34,9 +32,7 @@ class AlbumApiRepository extends ApiRepository { Iterable sharedUserIds = const [], String? description, }) async { - final users = sharedUserIds.map( - (id) => AlbumUserCreateDto(userId: id, role: AlbumUserRole.editor), - ); + final users = sharedUserIds.map((id) => AlbumUserCreateDto(userId: id, role: AlbumUserRole.editor)); final responseDto = await checkNull( _api.createAlbum( CreateAlbumDto( @@ -51,19 +47,9 @@ class AlbumApiRepository extends ApiRepository { } // TODO: Change name after removing old method - Future createDriftAlbum( - String name, { - required Iterable assetIds, - String? description, - }) async { + Future createDriftAlbum(String name, {required Iterable assetIds, String? description}) async { final responseDto = await checkNull( - _api.createAlbum( - CreateAlbumDto( - albumName: name, - description: description, - assetIds: assetIds.toList(), - ), - ), + _api.createAlbum(CreateAlbumDto(albumName: name, description: description, assetIds: assetIds.toList())), ); return _toRemoteAlbum(responseDto); @@ -102,16 +88,8 @@ class AlbumApiRepository extends ApiRepository { return _api.deleteAlbum(albumId); } - Future<({List added, List duplicates})> addAssets( - String albumId, - Iterable assetIds, - ) async { - final response = await checkNull( - _api.addAssetsToAlbum( - albumId, - BulkIdsDto(ids: assetIds.toList()), - ), - ); + Future<({List added, List duplicates})> addAssets(String albumId, Iterable assetIds) async { + final response = await checkNull(_api.addAssetsToAlbum(albumId, BulkIdsDto(ids: assetIds.toList()))); final List added = []; final List duplicates = []; @@ -126,16 +104,8 @@ class AlbumApiRepository extends ApiRepository { return (added: added, duplicates: duplicates); } - Future<({List removed, List failed})> removeAssets( - String albumId, - Iterable assetIds, - ) async { - final response = await checkNull( - _api.removeAssetFromAlbum( - albumId, - BulkIdsDto(ids: assetIds.toList()), - ), - ); + Future<({List removed, List failed})> removeAssets(String albumId, Iterable assetIds) async { + final response = await checkNull(_api.removeAssetFromAlbum(albumId, BulkIdsDto(ids: assetIds.toList()))); final List removed = [], failed = []; for (final dto in response) { if (dto.success) { @@ -149,12 +119,7 @@ class AlbumApiRepository extends ApiRepository { Future addUsers(String albumId, Iterable userIds) async { final albumUsers = userIds.map((userId) => AlbumUserAddDto(userId: userId)).toList(); - final response = await checkNull( - _api.addUsersToAlbum( - albumId, - AddUsersDto(albumUsers: albumUsers), - ), - ); + final response = await checkNull(_api.addUsersToAlbum(albumId, AddUsersDto(albumUsers: albumUsers))); return _toAlbum(response); } diff --git a/mobile/lib/repositories/album_media.repository.dart b/mobile/lib/repositories/album_media.repository.dart index 6e9dda173c..89860f4e75 100644 --- a/mobile/lib/repositories/album_media.repository.dart +++ b/mobile/lib/repositories/album_media.repository.dart @@ -18,32 +18,30 @@ class AlbumMediaRepository { DateTimeCond? updateTimeCond, bool? containsPathModified, List? orderBy, - }) => - useCustomFilter - ? FilterOptionGroup( - imageOption: const FilterOption( - needTitle: true, - sizeConstraint: SizeConstraint(ignoreSize: true), - ), - videoOption: const FilterOption( - needTitle: true, - sizeConstraint: SizeConstraint(ignoreSize: true), - durationConstraint: DurationConstraint(allowNullable: true), - ), - containsPathModified: containsPathModified ?? false, - createTimeCond: DateTimeCond.def().copyWith(ignore: true), - updateTimeCond: updateTimeCond ?? DateTimeCond.def().copyWith(ignore: true), - orders: orderBy ?? [], - ) - : null; + }) => useCustomFilter + ? FilterOptionGroup( + imageOption: const FilterOption(needTitle: true, sizeConstraint: SizeConstraint(ignoreSize: true)), + videoOption: const FilterOption( + needTitle: true, + sizeConstraint: SizeConstraint(ignoreSize: true), + durationConstraint: DurationConstraint(allowNullable: true), + ), + containsPathModified: containsPathModified ?? false, + createTimeCond: DateTimeCond.def().copyWith(ignore: true), + updateTimeCond: updateTimeCond ?? DateTimeCond.def().copyWith(ignore: true), + orders: orderBy ?? [], + ) + : null; Future> getAll() async { final filter = useCustomFilter ? CustomFilter.sql(where: '${CustomColumns.base.width} > 0') : FilterOptionGroup(containsPathModified: true); - final List assetPathEntities = - await PhotoManager.getAssetPathList(hasAll: true, filterOption: filter); + final List assetPathEntities = await PhotoManager.getAssetPathList( + hasAll: true, + filterOption: filter, + ); return assetPathEntities.map(_toAlbum).toList(); } @@ -71,10 +69,7 @@ class AlbumMediaRepository { filterOption: _getAlbumFilter( updateTimeCond: modifiedFrom == null && modifiedUntil == null ? null - : DateTimeCond( - min: modifiedFrom ?? DateTime.utc(-271820), - max: modifiedUntil ?? DateTime.utc(275760), - ), + : DateTimeCond(min: modifiedFrom ?? DateTime.utc(-271820), max: modifiedUntil ?? DateTime.utc(275760)), orderBy: orderByModificationDate ? [const OrderOption(type: OrderOptionType.updateDate)] : [], ), ); @@ -84,10 +79,7 @@ class AlbumMediaRepository { } Future get(String id) async { - final assetPathEntity = await AssetPathEntity.fromId( - id, - filterOption: _getAlbumFilter(containsPathModified: true), - ); + final assetPathEntity = await AssetPathEntity.fromId(id, filterOption: _getAlbumFilter(containsPathModified: true)); return _toAlbum(assetPathEntity); } diff --git a/mobile/lib/repositories/asset.repository.dart b/mobile/lib/repositories/asset.repository.dart index 2b35364596..79af8b4921 100644 --- a/mobile/lib/repositories/asset.repository.dart +++ b/mobile/lib/repositories/asset.repository.dart @@ -52,16 +52,13 @@ class AssetRepository extends DatabaseRepository { } Future deleteByIds(List ids) => txn(() async { - await db.assets.deleteAll(ids); - await db.exifInfos.deleteAll(ids); - }); + await db.assets.deleteAll(ids); + await db.exifInfos.deleteAll(ids); + }); Future getByRemoteId(String id) => db.assets.getByRemoteId(id); - Future> getAllByRemoteId( - Iterable ids, { - AssetState? state, - }) async { + Future> getAllByRemoteId(Iterable ids, {AssetState? state}) async { if (ids.isEmpty) { return []; } @@ -69,10 +66,7 @@ class AssetRepository extends DatabaseRepository { return _getAllByRemoteIdImpl(ids, state).findAll(); } - QueryBuilder _getAllByRemoteIdImpl( - Iterable ids, - AssetState? state, - ) { + QueryBuilder _getAllByRemoteIdImpl(Iterable ids, AssetState? state) { final query = db.assets.remote(ids).filter(); return switch (state) { null => query.noOp(), @@ -82,12 +76,7 @@ class AssetRepository extends DatabaseRepository { }; } - Future> getAll({ - required String ownerId, - AssetState? state, - AssetSort? sortBy, - int? limit, - }) { + Future> getAll({required String ownerId, AssetState? state, AssetSort? sortBy, int? limit}) { final baseQuery = db.assets.where(); final isarUserIds = fastHash(ownerId); final QueryBuilder filteredQuery = switch (state) { @@ -133,19 +122,15 @@ class AssetRepository extends DatabaseRepository { return asset; } - Future upsertDuplicatedAssets(Iterable duplicatedAssets) => txn( - () => db.duplicatedAssets.putAll(duplicatedAssets.map(DuplicatedAsset.new).toList()), - ); + Future upsertDuplicatedAssets(Iterable duplicatedAssets) => + txn(() => db.duplicatedAssets.putAll(duplicatedAssets.map(DuplicatedAsset.new).toList())); Future> getAllDuplicatedAssetIds() => db.duplicatedAssets.where().idProperty().findAll(); Future getByOwnerIdChecksum(int ownerId, String checksum) => db.assets.getByOwnerIdChecksum(ownerId, checksum); - Future> getAllByOwnerIdChecksum( - List ownerIds, - List checksums, - ) => + Future> getAllByOwnerIdChecksum(List ownerIds, List checksums) => db.assets.getAllByOwnerIdChecksum(ownerIds, checksums); Future> getAllLocal() => db.assets.where().localIdIsNotNull().findAll(); @@ -211,26 +196,25 @@ Future> _getMatchesImpl( int ownerId, List assets, int limit, -) => - query - .ownerIdEqualTo(ownerId) - .anyOf( - assets, - (q, Asset a) => q - .fileNameEqualTo(a.fileName) - .and() - .durationInSecondsEqualTo(a.durationInSeconds) - .and() - .fileCreatedAtBetween( - a.fileCreatedAt.subtract(const Duration(hours: 12)), - a.fileCreatedAt.add(const Duration(hours: 12)), - ) - .and() - .not() - .checksumEqualTo(a.checksum), - ) - .sortByFileName() - .thenByFileCreatedAt() - .thenByFileModifiedAt() - .limit(limit) - .findAll(); +) => query + .ownerIdEqualTo(ownerId) + .anyOf( + assets, + (q, Asset a) => q + .fileNameEqualTo(a.fileName) + .and() + .durationInSecondsEqualTo(a.durationInSeconds) + .and() + .fileCreatedAtBetween( + a.fileCreatedAt.subtract(const Duration(hours: 12)), + a.fileCreatedAt.add(const Duration(hours: 12)), + ) + .and() + .not() + .checksumEqualTo(a.checksum), + ) + .sortByFileName() + .thenByFileCreatedAt() + .thenByFileModifiedAt() + .limit(limit) + .findAll(); diff --git a/mobile/lib/repositories/asset_api.repository.dart b/mobile/lib/repositories/asset_api.repository.dart index b85ebdea6b..26147292d7 100644 --- a/mobile/lib/repositories/asset_api.repository.dart +++ b/mobile/lib/repositories/asset_api.repository.dart @@ -23,17 +23,10 @@ class AssetApiRepository extends ApiRepository { final StacksApi _stacksApi; final TrashApi _trashApi; - AssetApiRepository( - this._api, - this._searchApi, - this._stacksApi, - this._trashApi, - ); + AssetApiRepository(this._api, this._searchApi, this._stacksApi, this._trashApi); Future update(String id, {String? description}) async { - final response = await checkNull( - _api.updateAsset(id, UpdateAssetDto(description: description)), - ); + final response = await checkNull(_api.updateAsset(id, UpdateAssetDto(description: description))); return Asset.remote(response); } @@ -44,13 +37,7 @@ class AssetApiRepository extends ApiRepository { int currentPage = 1; while (hasNext) { final response = await checkNull( - _searchApi.searchAssets( - MetadataSearchDto( - personIds: personIds, - page: currentPage, - size: 1000, - ), - ), + _searchApi.searchAssets(MetadataSearchDto(personIds: personIds, page: currentPage, size: 1000)), ); result.addAll(response.assets.items.map(Asset.remote)); hasNext = response.assets.nextPage != null; @@ -67,35 +54,16 @@ class AssetApiRepository extends ApiRepository { await _trashApi.restoreAssets(BulkIdsDto(ids: ids)); } - Future updateVisibility( - List ids, - AssetVisibilityEnum visibility, - ) async { - return _api.updateAssets( - AssetBulkUpdateDto(ids: ids, visibility: _mapVisibility(visibility)), - ); + Future updateVisibility(List ids, AssetVisibilityEnum visibility) async { + return _api.updateAssets(AssetBulkUpdateDto(ids: ids, visibility: _mapVisibility(visibility))); } - Future updateFavorite( - List ids, - bool isFavorite, - ) async { - return _api.updateAssets( - AssetBulkUpdateDto(ids: ids, isFavorite: isFavorite), - ); + Future updateFavorite(List ids, bool isFavorite) async { + return _api.updateAssets(AssetBulkUpdateDto(ids: ids, isFavorite: isFavorite)); } - Future updateLocation( - List ids, - LatLng location, - ) async { - return _api.updateAssets( - AssetBulkUpdateDto( - ids: ids, - latitude: location.latitude, - longitude: location.longitude, - ), - ); + Future updateLocation(List ids, LatLng location) async { + return _api.updateAssets(AssetBulkUpdateDto(ids: ids, latitude: location.latitude, longitude: location.longitude)); } Future stack(List ids) async { @@ -113,11 +81,11 @@ class AssetApiRepository extends ApiRepository { } _mapVisibility(AssetVisibilityEnum visibility) => switch (visibility) { - AssetVisibilityEnum.timeline => AssetVisibility.timeline, - AssetVisibilityEnum.hidden => AssetVisibility.hidden, - AssetVisibilityEnum.locked => AssetVisibility.locked, - AssetVisibilityEnum.archive => AssetVisibility.archive, - }; + AssetVisibilityEnum.timeline => AssetVisibility.timeline, + AssetVisibilityEnum.hidden => AssetVisibility.hidden, + AssetVisibilityEnum.locked => AssetVisibility.locked, + AssetVisibilityEnum.archive => AssetVisibility.archive, + }; Future getAssetMIMEType(String assetId) async { final response = await checkNull(_api.getAssetInfo(assetId)); @@ -129,10 +97,6 @@ class AssetApiRepository extends ApiRepository { extension on StackResponseDto { StackResponse toStack() { - return StackResponse( - id: id, - primaryAssetId: primaryAssetId, - assetIds: assets.map((asset) => asset.id).toList(), - ); + return StackResponse(id: id, primaryAssetId: primaryAssetId, assetIds: assets.map((asset) => asset.id).toList()); } } diff --git a/mobile/lib/repositories/asset_media.repository.dart b/mobile/lib/repositories/asset_media.repository.dart index 1355890766..a0af217f0c 100644 --- a/mobile/lib/repositories/asset_media.repository.dart +++ b/mobile/lib/repositories/asset_media.repository.dart @@ -14,9 +14,7 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/extensions/response_extensions.dart'; import 'package:share_plus/share_plus.dart'; -final assetMediaRepositoryProvider = Provider( - (ref) => AssetMediaRepository(ref.watch(assetApiRepositoryProvider)), -); +final assetMediaRepositoryProvider = Provider((ref) => AssetMediaRepository(ref.watch(assetApiRepositoryProvider))); class AssetMediaRepository { final AssetApiRepository _assetApiRepository; @@ -77,8 +75,8 @@ class AssetMediaRepository { final localId = (asset is LocalAsset) ? asset.id : asset is RemoteAsset - ? asset.localId - : null; + ? asset.localId + : null; if (localId != null) { File? f = await AssetEntity(id: localId, width: 1, height: 1, typeInt: 0).originFile; downloadedXFiles.add(XFile(f!.path)); diff --git a/mobile/lib/repositories/auth_api.repository.dart b/mobile/lib/repositories/auth_api.repository.dart index 992be918f9..e488f69578 100644 --- a/mobile/lib/repositories/auth_api.repository.dart +++ b/mobile/lib/repositories/auth_api.repository.dart @@ -13,21 +13,12 @@ class AuthApiRepository extends ApiRepository { AuthApiRepository(this._apiService); Future changePassword(String newPassword) async { - await _apiService.usersApi.updateMyUser( - UserUpdateMeDto( - password: newPassword, - ), - ); + await _apiService.usersApi.updateMyUser(UserUpdateMeDto(password: newPassword)); } Future login(String email, String password) async { final loginResponseDto = await checkNull( - _apiService.authenticationApi.login( - LoginCredentialDto( - email: email, - password: password, - ), - ), + _apiService.authenticationApi.login(LoginCredentialDto(email: email, password: password)), ); return _mapLoginReponse(loginResponseDto); diff --git a/mobile/lib/repositories/biometric.repository.dart b/mobile/lib/repositories/biometric.repository.dart index b185501d19..8bd5a85542 100644 --- a/mobile/lib/repositories/biometric.repository.dart +++ b/mobile/lib/repositories/biometric.repository.dart @@ -15,15 +15,10 @@ class BiometricRepository { final bool canAuthenticate = canAuthenticateWithBiometrics || await _localAuth.isDeviceSupported(); final availableBiometric = await _localAuth.getAvailableBiometrics(); - return BiometricStatus( - canAuthenticate: canAuthenticate, - availableBiometrics: availableBiometric, - ); + return BiometricStatus(canAuthenticate: canAuthenticate, availableBiometrics: availableBiometric); } Future authenticate(String? message) async { - return _localAuth.authenticate( - localizedReason: message ?? 'please_auth_to_access'.tr(), - ); + return _localAuth.authenticate(localizedReason: message ?? 'please_auth_to_access'.tr()); } } diff --git a/mobile/lib/repositories/download.repository.dart b/mobile/lib/repositories/download.repository.dart index 5908e31786..6f38a802e2 100644 --- a/mobile/lib/repositories/download.repository.dart +++ b/mobile/lib/repositories/download.repository.dart @@ -64,10 +64,7 @@ class DownloadRepository { } Future> getLiveVideoTasks() { - return _downloader.database.allRecordsWithStatus( - TaskStatus.complete, - group: kDownloadGroupLivePhoto, - ); + return _downloader.database.allRecordsWithStatus(TaskStatus.complete, group: kDownloadGroupLivePhoto); } Future deleteRecordsWithIds(List ids) { diff --git a/mobile/lib/repositories/drift_album_api_repository.dart b/mobile/lib/repositories/drift_album_api_repository.dart index f56cc9fbed..6de025fb47 100644 --- a/mobile/lib/repositories/drift_album_api_repository.dart +++ b/mobile/lib/repositories/drift_album_api_repository.dart @@ -14,34 +14,16 @@ class DriftAlbumApiRepository extends ApiRepository { DriftAlbumApiRepository(this._api); - Future createDriftAlbum( - String name, { - required Iterable assetIds, - String? description, - }) async { + Future createDriftAlbum(String name, {required Iterable assetIds, String? description}) async { final responseDto = await checkNull( - _api.createAlbum( - CreateAlbumDto( - albumName: name, - description: description, - assetIds: assetIds.toList(), - ), - ), + _api.createAlbum(CreateAlbumDto(albumName: name, description: description, assetIds: assetIds.toList())), ); return responseDto.toRemoteAlbum(); } - Future<({List removed, List failed})> removeAssets( - String albumId, - Iterable assetIds, - ) async { - final response = await checkNull( - _api.removeAssetFromAlbum( - albumId, - BulkIdsDto(ids: assetIds.toList()), - ), - ); + Future<({List removed, List failed})> removeAssets(String albumId, Iterable assetIds) async { + final response = await checkNull(_api.removeAssetFromAlbum(albumId, BulkIdsDto(ids: assetIds.toList()))); final List removed = [], failed = []; for (final dto in response) { if (dto.success) { @@ -53,16 +35,8 @@ class DriftAlbumApiRepository extends ApiRepository { return (removed: removed, failed: failed); } - Future<({List added, List failed})> addAssets( - String albumId, - Iterable assetIds, - ) async { - final response = await checkNull( - _api.addAssetsToAlbum( - albumId, - BulkIdsDto(ids: assetIds.toList()), - ), - ); + Future<({List added, List failed})> addAssets(String albumId, Iterable assetIds) async { + final response = await checkNull(_api.addAssetsToAlbum(albumId, BulkIdsDto(ids: assetIds.toList()))); final List added = [], failed = []; for (final dto in response) { if (dto.success) { @@ -108,17 +82,9 @@ class DriftAlbumApiRepository extends ApiRepository { return _api.deleteAlbum(albumId); } - Future addUsers( - String albumId, - Iterable userIds, - ) async { + Future addUsers(String albumId, Iterable userIds) async { final albumUsers = userIds.map((userId) => AlbumUserAddDto(userId: userId)).toList(); - final response = await checkNull( - _api.addUsersToAlbum( - albumId, - AddUsersDto(albumUsers: albumUsers), - ), - ); + final response = await checkNull(_api.addUsersToAlbum(albumId, AddUsersDto(albumUsers: albumUsers))); return response.toRemoteAlbum(); } } diff --git a/mobile/lib/repositories/file_media.repository.dart b/mobile/lib/repositories/file_media.repository.dart index 86f99e07dd..6d429e4777 100644 --- a/mobile/lib/repositories/file_media.repository.dart +++ b/mobile/lib/repositories/file_media.repository.dart @@ -10,56 +10,23 @@ final fileMediaRepositoryProvider = Provider((ref) => const FileMediaRepository( class FileMediaRepository { const FileMediaRepository(); - Future saveImage( - Uint8List data, { - required String title, - String? relativePath, - }) async { - final entity = await PhotoManager.editor.saveImage( - data, - filename: title, - title: title, - relativePath: relativePath, - ); + Future saveImage(Uint8List data, {required String title, String? relativePath}) async { + final entity = await PhotoManager.editor.saveImage(data, filename: title, title: title, relativePath: relativePath); return AssetMediaRepository.toAsset(entity); } - Future saveImageWithFile( - String filePath, { - String? title, - String? relativePath, - }) async { - final entity = await PhotoManager.editor.saveImageWithPath( - filePath, - title: title, - relativePath: relativePath, - ); + Future saveImageWithFile(String filePath, {String? title, String? relativePath}) async { + final entity = await PhotoManager.editor.saveImageWithPath(filePath, title: title, relativePath: relativePath); return AssetMediaRepository.toAsset(entity); } - Future saveLivePhoto({ - required File image, - required File video, - required String title, - }) async { - final entity = await PhotoManager.editor.darwin.saveLivePhoto( - imageFile: image, - videoFile: video, - title: title, - ); + Future saveLivePhoto({required File image, required File video, required String title}) async { + final entity = await PhotoManager.editor.darwin.saveLivePhoto(imageFile: image, videoFile: video, title: title); return AssetMediaRepository.toAsset(entity); } - Future saveVideo( - File file, { - required String title, - String? relativePath, - }) async { - final entity = await PhotoManager.editor.saveVideo( - file, - title: title, - relativePath: relativePath, - ); + Future saveVideo(File file, {required String title, String? relativePath}) async { + final entity = await PhotoManager.editor.saveVideo(file, title: title, relativePath: relativePath); return AssetMediaRepository.toAsset(entity); } diff --git a/mobile/lib/repositories/folder_api.repository.dart b/mobile/lib/repositories/folder_api.repository.dart index 9fcb57ae21..dfda19e45e 100644 --- a/mobile/lib/repositories/folder_api.repository.dart +++ b/mobile/lib/repositories/folder_api.repository.dart @@ -5,11 +5,7 @@ import 'package:immich_mobile/repositories/api.repository.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; -final folderApiRepositoryProvider = Provider( - (ref) => FolderApiRepository( - ref.watch(apiServiceProvider).viewApi, - ), -); +final folderApiRepositoryProvider = Provider((ref) => FolderApiRepository(ref.watch(apiServiceProvider).viewApi)); class FolderApiRepository extends ApiRepository { final ViewApi _api; diff --git a/mobile/lib/repositories/gcast.repository.dart b/mobile/lib/repositories/gcast.repository.dart index f896de2ab0..db3e0f45d0 100644 --- a/mobile/lib/repositories/gcast.repository.dart +++ b/mobile/lib/repositories/gcast.repository.dart @@ -33,19 +33,13 @@ class GCastRepository { }); // open the default receiver - sendMessage(CastSession.kNamespaceReceiver, { - 'type': 'LAUNCH', - 'appId': 'CC1AD845', - }); + sendMessage(CastSession.kNamespaceReceiver, {'type': 'LAUNCH', 'appId': 'CC1AD845'}); } Future disconnect() async { final sessionID = getSessionId(); - sendMessage(CastSession.kNamespaceReceiver, { - 'type': "STOP", - "sessionId": sessionID, - }); + sendMessage(CastSession.kNamespaceReceiver, {'type': "STOP", "sessionId": sessionID}); // wait 500ms to ensure the stop command is processed await Future.delayed(const Duration(milliseconds: 500)); diff --git a/mobile/lib/repositories/partner.repository.dart b/mobile/lib/repositories/partner.repository.dart index 23b6fadebb..7f5ce62e0c 100644 --- a/mobile/lib/repositories/partner.repository.dart +++ b/mobile/lib/repositories/partner.repository.dart @@ -5,9 +5,7 @@ import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/repositories/database.repository.dart'; import 'package:isar/isar.dart'; -final partnerRepositoryProvider = Provider( - (ref) => PartnerRepository(ref.watch(dbProvider)), -); +final partnerRepositoryProvider = Provider((ref) => PartnerRepository(ref.watch(dbProvider))); class PartnerRepository extends DatabaseRepository { const PartnerRepository(super.db); @@ -23,12 +21,14 @@ class PartnerRepository extends DatabaseRepository { } Stream> watchSharedBy() { - return (db.users.filter().isPartnerSharedByEqualTo(true).sortById().watch()) - .map((users) => users.map((u) => u.toDto()).toList()); + return (db.users.filter().isPartnerSharedByEqualTo(true).sortById().watch()).map( + (users) => users.map((u) => u.toDto()).toList(), + ); } Stream> watchSharedWith() { - return (db.users.filter().isPartnerSharedWithEqualTo(true).sortById().watch()) - .map((users) => users.map((u) => u.toDto()).toList()); + return (db.users.filter().isPartnerSharedWithEqualTo(true).sortById().watch()).map( + (users) => users.map((u) => u.toDto()).toList(), + ); } } diff --git a/mobile/lib/repositories/partner_api.repository.dart b/mobile/lib/repositories/partner_api.repository.dart index 00de1ea2f1..82554d62e8 100644 --- a/mobile/lib/repositories/partner_api.repository.dart +++ b/mobile/lib/repositories/partner_api.repository.dart @@ -5,16 +5,9 @@ import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/repositories/api.repository.dart'; import 'package:openapi/api.dart'; -enum Direction { - sharedWithMe, - sharedByMe, -} +enum Direction { sharedWithMe, sharedByMe } -final partnerApiRepositoryProvider = Provider( - (ref) => PartnerApiRepository( - ref.watch(apiServiceProvider).partnersApi, - ), -); +final partnerApiRepositoryProvider = Provider((ref) => PartnerApiRepository(ref.watch(apiServiceProvider).partnersApi)); class PartnerApiRepository extends ApiRepository { final PartnersApi _api; @@ -23,9 +16,7 @@ class PartnerApiRepository extends ApiRepository { Future> getAll(Direction direction) async { final response = await checkNull( - _api.getPartners( - direction == Direction.sharedByMe ? PartnerDirection.by : PartnerDirection.with_, - ), + _api.getPartners(direction == Direction.sharedByMe ? PartnerDirection.by : PartnerDirection.with_), ); return response.map(UserConverter.fromPartnerDto).toList(); } @@ -38,12 +29,7 @@ class PartnerApiRepository extends ApiRepository { Future delete(String id) => _api.removePartner(id); Future update(String id, {required bool inTimeline}) async { - final dto = await checkNull( - _api.updatePartner( - id, - UpdatePartnerDto(inTimeline: inTimeline), - ), - ); + final dto = await checkNull(_api.updatePartner(id, UpdatePartnerDto(inTimeline: inTimeline))); return UserConverter.fromPartnerDto(dto); } } diff --git a/mobile/lib/repositories/person_api.repository.dart b/mobile/lib/repositories/person_api.repository.dart index 26f11dd51d..545ad06741 100644 --- a/mobile/lib/repositories/person_api.repository.dart +++ b/mobile/lib/repositories/person_api.repository.dart @@ -4,9 +4,7 @@ import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/repositories/api.repository.dart'; import 'package:openapi/api.dart'; -final personApiRepositoryProvider = Provider( - (ref) => PersonApiRepository(ref.watch(apiServiceProvider).peopleApi), -); +final personApiRepositoryProvider = Provider((ref) => PersonApiRepository(ref.watch(apiServiceProvider).peopleApi)); class PersonApiRepository extends ApiRepository { final PeopleApi _api; @@ -19,17 +17,15 @@ class PersonApiRepository extends ApiRepository { } Future update(String id, {String? name}) async { - final dto = await checkNull( - _api.updatePerson(id, PersonUpdateDto(name: name)), - ); + final dto = await checkNull(_api.updatePerson(id, PersonUpdateDto(name: name))); return _toPerson(dto); } static PersonDto _toPerson(PersonResponseDto dto) => PersonDto( - birthDate: dto.birthDate, - id: dto.id, - isHidden: dto.isHidden, - name: dto.name, - thumbnailPath: dto.thumbnailPath, - ); + birthDate: dto.birthDate, + id: dto.id, + isHidden: dto.isHidden, + name: dto.name, + thumbnailPath: dto.thumbnailPath, + ); } diff --git a/mobile/lib/repositories/sessions_api.repository.dart b/mobile/lib/repositories/sessions_api.repository.dart index 8ef43d54f2..f25e724f19 100644 --- a/mobile/lib/repositories/sessions_api.repository.dart +++ b/mobile/lib/repositories/sessions_api.repository.dart @@ -5,9 +5,7 @@ import 'package:immich_mobile/repositories/api.repository.dart'; import 'package:openapi/api.dart'; final sessionsAPIRepositoryProvider = Provider( - (ref) => SessionsAPIRepository( - ref.watch(apiServiceProvider).sessionsApi, - ), + (ref) => SessionsAPIRepository(ref.watch(apiServiceProvider).sessionsApi), ); class SessionsAPIRepository extends ApiRepository { @@ -15,19 +13,9 @@ class SessionsAPIRepository extends ApiRepository { SessionsAPIRepository(this._api); - Future createSession( - String deviceType, - String deviceOS, { - int? duration, - }) async { + Future createSession(String deviceType, String deviceOS, {int? duration}) async { final dto = await checkNull( - _api.createSession( - SessionCreateDto( - deviceType: deviceType, - deviceOS: deviceOS, - duration: duration, - ), - ), + _api.createSession(SessionCreateDto(deviceType: deviceType, deviceOS: deviceOS, duration: duration)), ); return SessionCreateResponse( diff --git a/mobile/lib/repositories/share_handler.repository.dart b/mobile/lib/repositories/share_handler.repository.dart index e739ebe6ae..4650b04a79 100644 --- a/mobile/lib/repositories/share_handler.repository.dart +++ b/mobile/lib/repositories/share_handler.repository.dart @@ -4,9 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart'; import 'package:share_handler/share_handler.dart'; -final shareHandlerRepositoryProvider = Provider( - (ref) => ShareHandlerRepository(), -); +final shareHandlerRepositoryProvider = Provider((ref) => ShareHandlerRepository()); class ShareHandlerRepository { ShareHandlerRepository(); @@ -28,9 +26,7 @@ class ShareHandlerRepository { }); } - List _buildPayload( - List attachments, - ) { + List _buildPayload(List attachments) { final payload = []; for (final attachment in attachments) { diff --git a/mobile/lib/repositories/timeline.repository.dart b/mobile/lib/repositories/timeline.repository.dart index 1579300bc6..c8c173b6f6 100644 --- a/mobile/lib/repositories/timeline.repository.dart +++ b/mobile/lib/repositories/timeline.repository.dart @@ -48,10 +48,7 @@ class TimelineRepository extends DatabaseRepository { return _watchRenderList(query, GroupAssetsBy.none); } - Stream watchAlbumTimeline( - Album album, - GroupAssetsBy groupAssetByOption, - ) { + Stream watchAlbumTimeline(Album album, GroupAssetsBy groupAssetByOption) { final query = album.assets.filter().isTrashedEqualTo(false).not().visibilityEqualTo(AssetVisibilityEnum.locked); final withSortedOption = switch (album.sortOrder) { @@ -81,10 +78,7 @@ class TimelineRepository extends DatabaseRepository { return _watchRenderList(query, GroupAssetsBy.none); } - Stream watchHomeTimeline( - String userId, - GroupAssetsBy groupAssetByOption, - ) { + Stream watchHomeTimeline(String userId, GroupAssetsBy groupAssetByOption) { final query = db.assets .where() .ownerIdEqualToAnyChecksum(fastHash(userId)) @@ -97,10 +91,7 @@ class TimelineRepository extends DatabaseRepository { return _watchRenderList(query, groupAssetByOption); } - Stream watchMultiUsersTimeline( - List userIds, - GroupAssetsBy groupAssetByOption, - ) { + Stream watchMultiUsersTimeline(List userIds, GroupAssetsBy groupAssetByOption) { final isarUserIds = userIds.map(fastHash).toList(); final query = db.assets .where() @@ -113,10 +104,7 @@ class TimelineRepository extends DatabaseRepository { return _watchRenderList(query, groupAssetByOption); } - Future getTimelineFromAssets( - List assets, - GroupAssetsBy getGroupByOption, - ) { + Future getTimelineFromAssets(List assets, GroupAssetsBy getGroupByOption) { return RenderList.fromAssets(assets, getGroupByOption); } @@ -134,10 +122,7 @@ class TimelineRepository extends DatabaseRepository { return _watchRenderList(query, GroupAssetsBy.none); } - Stream watchLockedTimeline( - String userId, - GroupAssetsBy getGroupByOption, - ) { + Stream watchLockedTimeline(String userId, GroupAssetsBy getGroupByOption) { final query = db.assets .where() .ownerIdEqualToAnyChecksum(fastHash(userId)) diff --git a/mobile/lib/repositories/upload.repository.dart b/mobile/lib/repositories/upload.repository.dart index 1510eb208f..220dbf81c3 100644 --- a/mobile/lib/repositories/upload.repository.dart +++ b/mobile/lib/repositories/upload.repository.dart @@ -54,26 +54,11 @@ class UploadRepository { Future getUploadInfo() async { final [enqueuedTasks, runningTasks, canceledTasks, waitingTasks, pausedTasks] = await Future.wait([ - FileDownloader().database.allRecordsWithStatus( - TaskStatus.enqueued, - group: kBackupGroup, - ), - FileDownloader().database.allRecordsWithStatus( - TaskStatus.running, - group: kBackupGroup, - ), - FileDownloader().database.allRecordsWithStatus( - TaskStatus.canceled, - group: kBackupGroup, - ), - FileDownloader().database.allRecordsWithStatus( - TaskStatus.waitingToRetry, - group: kBackupGroup, - ), - FileDownloader().database.allRecordsWithStatus( - TaskStatus.paused, - group: kBackupGroup, - ), + FileDownloader().database.allRecordsWithStatus(TaskStatus.enqueued, group: kBackupGroup), + FileDownloader().database.allRecordsWithStatus(TaskStatus.running, group: kBackupGroup), + FileDownloader().database.allRecordsWithStatus(TaskStatus.canceled, group: kBackupGroup), + FileDownloader().database.allRecordsWithStatus(TaskStatus.waitingToRetry, group: kBackupGroup), + FileDownloader().database.allRecordsWithStatus(TaskStatus.paused, group: kBackupGroup), ]); debugPrint(""" diff --git a/mobile/lib/repositories/widget.repository.dart b/mobile/lib/repositories/widget.repository.dart index 09532f4b78..f21d31e1ec 100644 --- a/mobile/lib/repositories/widget.repository.dart +++ b/mobile/lib/repositories/widget.repository.dart @@ -11,10 +11,7 @@ class WidgetRepository { } Future refresh(String iosName, String androidName) async { - await HomeWidget.updateWidget( - iOSName: iosName, - qualifiedAndroidName: androidName, - ); + await HomeWidget.updateWidget(iOSName: iosName, qualifiedAndroidName: androidName); } Future setAppGroupId(String appGroupId) async { diff --git a/mobile/lib/routing/app_navigation_observer.dart b/mobile/lib/routing/app_navigation_observer.dart index 12766cef66..20973fabd1 100644 --- a/mobile/lib/routing/app_navigation_observer.dart +++ b/mobile/lib/routing/app_navigation_observer.dart @@ -8,18 +8,11 @@ class AppNavigationObserver extends AutoRouterObserver { /// Riverpod Instance final WidgetRef ref; - AppNavigationObserver({ - required this.ref, - }); + AppNavigationObserver({required this.ref}); @override - Future didChangeTabRoute( - TabPageRoute route, - TabPageRoute previousRoute, - ) async { - Future( - () => ref.read(inLockedViewProvider.notifier).state = false, - ); + Future didChangeTabRoute(TabPageRoute route, TabPageRoute previousRoute) async { + Future(() => ref.read(inLockedViewProvider.notifier).state = false); } @override @@ -41,13 +34,9 @@ class AppNavigationObserver extends AutoRouterObserver { route.settings.name == null && previousRoute?.settings.name == GalleryViewerRoute.name && isInLockedView; if (route.settings.name == LockedRoute.name || isFromLockedViewToDetailView || isFromDetailViewToInfoPanelView) { - Future( - () => ref.read(inLockedViewProvider.notifier).state = true, - ); + Future(() => ref.read(inLockedViewProvider.notifier).state = true); } else { - Future( - () => ref.read(inLockedViewProvider.notifier).state = false, - ); + Future(() => ref.read(inLockedViewProvider.notifier).state = false); } } @@ -62,13 +51,9 @@ class AppNavigationObserver extends AutoRouterObserver { if (route.settings.name == DriftLockedFolderRoute.name || isFromLockedViewToDetailView || isFromDetailViewToInfoPanelView) { - Future( - () => ref.read(inLockedViewProvider.notifier).state = true, - ); + Future(() => ref.read(inLockedViewProvider.notifier).state = true); } else { - Future( - () => ref.read(inLockedViewProvider.notifier).state = false, - ); + Future(() => ref.read(inLockedViewProvider.notifier).state = false); } } } diff --git a/mobile/lib/routing/duplicate_guard.dart b/mobile/lib/routing/duplicate_guard.dart index efc649bc41..6f83d5297e 100644 --- a/mobile/lib/routing/duplicate_guard.dart +++ b/mobile/lib/routing/duplicate_guard.dart @@ -8,9 +8,7 @@ class DuplicateGuard extends AutoRouteGuard { void onNavigation(NavigationResolver resolver, StackRouter router) async { // Duplicate navigation if (resolver.route.name == router.current.name) { - debugPrint( - 'DuplicateGuard: Preventing duplicate route navigation for ${resolver.route.name}', - ); + debugPrint('DuplicateGuard: Preventing duplicate route navigation for ${resolver.route.name}'); resolver.next(false); } else { resolver.next(true); diff --git a/mobile/lib/routing/locked_guard.dart b/mobile/lib/routing/locked_guard.dart index d731c7942c..851407ff16 100644 --- a/mobile/lib/routing/locked_guard.dart +++ b/mobile/lib/routing/locked_guard.dart @@ -17,11 +17,7 @@ class LockedGuard extends AutoRouteGuard { final LocalAuthService _localAuth; final _log = Logger("AuthGuard"); - LockedGuard( - this._apiService, - this._secureStorageService, - this._localAuth, - ); + LockedGuard(this._apiService, this._secureStorageService, this._localAuth); @override void onNavigation(NavigationResolver resolver, StackRouter router) async { @@ -58,9 +54,7 @@ class LockedGuard extends AutoRouteGuard { return; } - await _apiService.authenticationApi.unlockAuthSession( - SessionUnlockDto(pinCode: securePinCode), - ); + await _apiService.authenticationApi.unlockAuthSession(SessionUnlockDto(pinCode: securePinCode)); resolver.next(true); } on PlatformException catch (error) { diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index a72558508c..f547db65d6 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -150,38 +150,18 @@ class AppRouter extends RootStackRouter { @override late final List routes = [ AutoRoute(page: SplashScreenRoute.page, initial: true), - AutoRoute( - page: PermissionOnboardingRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: PermissionOnboardingRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: LoginRoute.page, guards: [_duplicateGuard]), AutoRoute(page: ChangePasswordRoute.page), - AutoRoute( - page: SearchRoute.page, - guards: [_authGuard, _duplicateGuard], - maintainState: false, - ), + AutoRoute(page: SearchRoute.page, guards: [_authGuard, _duplicateGuard], maintainState: false), CustomRoute( page: TabControllerRoute.page, guards: [_authGuard, _duplicateGuard], children: [ - AutoRoute( - page: PhotosRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: SearchRoute.page, - guards: [_authGuard, _duplicateGuard], - maintainState: false, - ), - AutoRoute( - page: LibraryRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: AlbumsRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: PhotosRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: SearchRoute.page, guards: [_authGuard, _duplicateGuard], maintainState: false), + AutoRoute(page: LibraryRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: AlbumsRoute.page, guards: [_authGuard, _duplicateGuard]), ], transitionsBuilder: TransitionsBuilders.fadeIn, ), @@ -189,23 +169,10 @@ class AppRouter extends RootStackRouter { page: TabShellRoute.page, guards: [_authGuard, _duplicateGuard], children: [ - AutoRoute( - page: MainTimelineRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftSearchRoute.page, - guards: [_authGuard, _duplicateGuard], - maintainState: false, - ), - AutoRoute( - page: DriftLibraryRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftAlbumsRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: MainTimelineRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftSearchRoute.page, guards: [_authGuard, _duplicateGuard], maintainState: false), + AutoRoute(page: DriftLibraryRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftAlbumsRoute.page, guards: [_authGuard, _duplicateGuard]), ], transitionsBuilder: TransitionsBuilders.fadeIn, ), @@ -214,18 +181,9 @@ class AppRouter extends RootStackRouter { guards: [_authGuard, _galleryGuard], transitionsBuilder: CustomTransitionsBuilders.zoomedPage, ), - AutoRoute( - page: BackupControllerRoute.page, - guards: [_authGuard, _duplicateGuard, _backupPermissionGuard], - ), - AutoRoute( - page: AllPlacesRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: CreateAlbumRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: BackupControllerRoute.page, guards: [_authGuard, _duplicateGuard, _backupPermissionGuard]), + AutoRoute(page: AllPlacesRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: CreateAlbumRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: EditImageRoute.page), AutoRoute(page: CropImageRoute.page), AutoRoute(page: FilterImageRoute.page), @@ -235,14 +193,8 @@ class AppRouter extends RootStackRouter { transitionsBuilder: TransitionsBuilders.slideLeft, ), AutoRoute(page: AllVideosRoute.page, guards: [_authGuard, _duplicateGuard]), - AutoRoute( - page: AllMotionPhotosRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: RecentlyTakenRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: AllMotionPhotosRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: RecentlyTakenRoute.page, guards: [_authGuard, _duplicateGuard]), CustomRoute( page: AlbumAssetSelectionRoute.page, guards: [_authGuard, _duplicateGuard], @@ -253,23 +205,14 @@ class AppRouter extends RootStackRouter { guards: [_authGuard, _duplicateGuard], transitionsBuilder: TransitionsBuilders.slideBottom, ), - AutoRoute( - page: AlbumViewerRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: AlbumViewerRoute.page, guards: [_authGuard, _duplicateGuard]), CustomRoute( page: AlbumAdditionalSharedUserSelectionRoute.page, guards: [_authGuard, _duplicateGuard], transitionsBuilder: TransitionsBuilders.slideBottom, ), - AutoRoute( - page: BackupAlbumSelectionRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: AlbumPreviewRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: BackupAlbumSelectionRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: AlbumPreviewRoute.page, guards: [_authGuard, _duplicateGuard]), CustomRoute( page: FailedBackupStatusRoute.page, guards: [_authGuard, _duplicateGuard], @@ -289,26 +232,13 @@ class AppRouter extends RootStackRouter { guards: [_authGuard, _duplicateGuard], transitionsBuilder: TransitionsBuilders.slideLeft, ), - CustomRoute( - page: FolderRoute.page, - guards: [_authGuard], - transitionsBuilder: TransitionsBuilders.fadeIn, - ), - AutoRoute( - page: PartnerDetailRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: PersonResultRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + CustomRoute(page: FolderRoute.page, guards: [_authGuard], transitionsBuilder: TransitionsBuilders.fadeIn), + AutoRoute(page: PartnerDetailRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: PersonResultRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: AllPeopleRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: MemoryRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: MapRoute.page, guards: [_authGuard, _duplicateGuard]), - AutoRoute( - page: AlbumOptionsRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: AlbumOptionsRoute.page, guards: [_authGuard, _duplicateGuard]), CustomRoute( page: TrashRoute.page, guards: [_authGuard, _duplicateGuard], @@ -319,28 +249,16 @@ class AppRouter extends RootStackRouter { guards: [_authGuard, _duplicateGuard], transitionsBuilder: TransitionsBuilders.slideLeft, ), - AutoRoute( - page: SharedLinkEditRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: SharedLinkEditRoute.page, guards: [_authGuard, _duplicateGuard]), CustomRoute( page: ActivitiesRoute.page, guards: [_authGuard, _duplicateGuard], transitionsBuilder: TransitionsBuilders.slideLeft, durationInMilliseconds: 200, ), - CustomRoute( - page: MapLocationPickerRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: BackupOptionsRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: HeaderSettingsRoute.page, - guards: [_duplicateGuard], - ), + CustomRoute(page: MapLocationPickerRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: BackupOptionsRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: HeaderSettingsRoute.page, guards: [_duplicateGuard]), CustomRoute( page: PeopleCollectionRoute.page, guards: [_authGuard, _duplicateGuard], @@ -361,54 +279,18 @@ class AppRouter extends RootStackRouter { guards: [_authGuard, _duplicateGuard], transitionsBuilder: TransitionsBuilders.slideLeft, ), - AutoRoute( - page: NativeVideoViewerRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: ShareIntentRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: LockedRoute.page, - guards: [_authGuard, _lockedGuard, _duplicateGuard], - ), - AutoRoute( - page: PinAuthRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: FeatInDevRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: LocalMediaSummaryRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: RemoteMediaSummaryRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftBackupRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftBackupAlbumSelectionRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: LocalTimelineRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: MainTimelineRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: RemoteAlbumRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: NativeVideoViewerRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: ShareIntentRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: LockedRoute.page, guards: [_authGuard, _lockedGuard, _duplicateGuard]), + AutoRoute(page: PinAuthRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: FeatInDevRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: LocalMediaSummaryRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: RemoteMediaSummaryRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftBackupRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftBackupAlbumSelectionRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: LocalTimelineRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: MainTimelineRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: RemoteAlbumRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute( page: AssetViewerRoute.page, guards: [_authGuard, _duplicateGuard], @@ -421,83 +303,26 @@ class AppRouter extends RootStackRouter { ), ), ), - AutoRoute( - page: DriftMemoryRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftFavoriteRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftTrashRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftArchiveRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftLockedFolderRoute.page, - guards: [_authGuard, _lockedGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftVideoRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftLibraryRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftAssetSelectionTimelineRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftPartnerDetailRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftRecentlyTakenRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftLocalAlbumsRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftCreateAlbumRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftPlaceRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftPlaceDetailRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftUserSelectionRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: ChangeExperienceRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: DriftMemoryRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftFavoriteRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftTrashRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftArchiveRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftLockedFolderRoute.page, guards: [_authGuard, _lockedGuard, _duplicateGuard]), + AutoRoute(page: DriftVideoRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftLibraryRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftAssetSelectionTimelineRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftPartnerDetailRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftRecentlyTakenRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftLocalAlbumsRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftCreateAlbumRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftPlaceRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftPlaceDetailRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftUserSelectionRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: ChangeExperienceRoute.page, guards: [_authGuard, _duplicateGuard]), - AutoRoute( - page: DriftPartnerRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftUploadDetailRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: BetaSyncSettingsRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: DriftPartnerRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftUploadDetailRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: BetaSyncSettingsRoute.page, guards: [_authGuard, _duplicateGuard]), // required to handle all deeplinks in deep_link.service.dart // auto_route_library#1722 RedirectRoute(path: '*', redirectTo: '/'), diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index 6b7e479cff..f67d1e2623 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -13,7 +13,8 @@ part of 'router.dart'; /// generated route for /// [ActivitiesPage] class ActivitiesRoute extends PageRouteInfo { - const ActivitiesRoute({List? children}) : super(ActivitiesRoute.name, initialChildren: children); + const ActivitiesRoute({List? children}) + : super(ActivitiesRoute.name, initialChildren: children); static const String name = 'ActivitiesRoute'; @@ -27,19 +28,20 @@ class ActivitiesRoute extends PageRouteInfo { /// generated route for /// [AlbumAdditionalSharedUserSelectionPage] -class AlbumAdditionalSharedUserSelectionRoute extends PageRouteInfo { +class AlbumAdditionalSharedUserSelectionRoute + extends PageRouteInfo { AlbumAdditionalSharedUserSelectionRoute({ Key? key, required Album album, List? children, }) : super( - AlbumAdditionalSharedUserSelectionRoute.name, - args: AlbumAdditionalSharedUserSelectionRouteArgs( - key: key, - album: album, - ), - initialChildren: children, - ); + AlbumAdditionalSharedUserSelectionRoute.name, + args: AlbumAdditionalSharedUserSelectionRouteArgs( + key: key, + album: album, + ), + initialChildren: children, + ); static const String name = 'AlbumAdditionalSharedUserSelectionRoute'; @@ -73,21 +75,22 @@ class AlbumAdditionalSharedUserSelectionRouteArgs { /// generated route for /// [AlbumAssetSelectionPage] -class AlbumAssetSelectionRoute extends PageRouteInfo { +class AlbumAssetSelectionRoute + extends PageRouteInfo { AlbumAssetSelectionRoute({ Key? key, required Set existingAssets, bool canDeselect = false, List? children, }) : super( - AlbumAssetSelectionRoute.name, - args: AlbumAssetSelectionRouteArgs( - key: key, - existingAssets: existingAssets, - canDeselect: canDeselect, - ), - initialChildren: children, - ); + AlbumAssetSelectionRoute.name, + args: AlbumAssetSelectionRouteArgs( + key: key, + existingAssets: existingAssets, + canDeselect: canDeselect, + ), + initialChildren: children, + ); static const String name = 'AlbumAssetSelectionRoute'; @@ -126,7 +129,8 @@ class AlbumAssetSelectionRouteArgs { /// generated route for /// [AlbumOptionsPage] class AlbumOptionsRoute extends PageRouteInfo { - const AlbumOptionsRoute({List? children}) : super(AlbumOptionsRoute.name, initialChildren: children); + const AlbumOptionsRoute({List? children}) + : super(AlbumOptionsRoute.name, initialChildren: children); static const String name = 'AlbumOptionsRoute'; @@ -146,10 +150,10 @@ class AlbumPreviewRoute extends PageRouteInfo { required Album album, List? children, }) : super( - AlbumPreviewRoute.name, - args: AlbumPreviewRouteArgs(key: key, album: album), - initialChildren: children, - ); + AlbumPreviewRoute.name, + args: AlbumPreviewRouteArgs(key: key, album: album), + initialChildren: children, + ); static const String name = 'AlbumPreviewRoute'; @@ -177,16 +181,17 @@ class AlbumPreviewRouteArgs { /// generated route for /// [AlbumSharedUserSelectionPage] -class AlbumSharedUserSelectionRoute extends PageRouteInfo { +class AlbumSharedUserSelectionRoute + extends PageRouteInfo { AlbumSharedUserSelectionRoute({ Key? key, required Set assets, List? children, }) : super( - AlbumSharedUserSelectionRoute.name, - args: AlbumSharedUserSelectionRouteArgs(key: key, assets: assets), - initialChildren: children, - ); + AlbumSharedUserSelectionRoute.name, + args: AlbumSharedUserSelectionRouteArgs(key: key, assets: assets), + initialChildren: children, + ); static const String name = 'AlbumSharedUserSelectionRoute'; @@ -220,10 +225,10 @@ class AlbumViewerRoute extends PageRouteInfo { required int albumId, List? children, }) : super( - AlbumViewerRoute.name, - args: AlbumViewerRouteArgs(key: key, albumId: albumId), - initialChildren: children, - ); + AlbumViewerRoute.name, + args: AlbumViewerRouteArgs(key: key, albumId: albumId), + initialChildren: children, + ); static const String name = 'AlbumViewerRoute'; @@ -252,7 +257,8 @@ class AlbumViewerRouteArgs { /// generated route for /// [AlbumsPage] class AlbumsRoute extends PageRouteInfo { - const AlbumsRoute({List? children}) : super(AlbumsRoute.name, initialChildren: children); + const AlbumsRoute({List? children}) + : super(AlbumsRoute.name, initialChildren: children); static const String name = 'AlbumsRoute'; @@ -268,7 +274,7 @@ class AlbumsRoute extends PageRouteInfo { /// [AllMotionPhotosPage] class AllMotionPhotosRoute extends PageRouteInfo { const AllMotionPhotosRoute({List? children}) - : super(AllMotionPhotosRoute.name, initialChildren: children); + : super(AllMotionPhotosRoute.name, initialChildren: children); static const String name = 'AllMotionPhotosRoute'; @@ -283,7 +289,8 @@ class AllMotionPhotosRoute extends PageRouteInfo { /// generated route for /// [AllPeoplePage] class AllPeopleRoute extends PageRouteInfo { - const AllPeopleRoute({List? children}) : super(AllPeopleRoute.name, initialChildren: children); + const AllPeopleRoute({List? children}) + : super(AllPeopleRoute.name, initialChildren: children); static const String name = 'AllPeopleRoute'; @@ -298,7 +305,8 @@ class AllPeopleRoute extends PageRouteInfo { /// generated route for /// [AllPlacesPage] class AllPlacesRoute extends PageRouteInfo { - const AllPlacesRoute({List? children}) : super(AllPlacesRoute.name, initialChildren: children); + const AllPlacesRoute({List? children}) + : super(AllPlacesRoute.name, initialChildren: children); static const String name = 'AllPlacesRoute'; @@ -313,7 +321,8 @@ class AllPlacesRoute extends PageRouteInfo { /// generated route for /// [AllVideosPage] class AllVideosRoute extends PageRouteInfo { - const AllVideosRoute({List? children}) : super(AllVideosRoute.name, initialChildren: children); + const AllVideosRoute({List? children}) + : super(AllVideosRoute.name, initialChildren: children); static const String name = 'AllVideosRoute'; @@ -333,10 +342,10 @@ class AppLogDetailRoute extends PageRouteInfo { required LogMessage logMessage, List? children, }) : super( - AppLogDetailRoute.name, - args: AppLogDetailRouteArgs(key: key, logMessage: logMessage), - initialChildren: children, - ); + AppLogDetailRoute.name, + args: AppLogDetailRouteArgs(key: key, logMessage: logMessage), + initialChildren: children, + ); static const String name = 'AppLogDetailRoute'; @@ -365,7 +374,8 @@ class AppLogDetailRouteArgs { /// generated route for /// [AppLogPage] class AppLogRoute extends PageRouteInfo { - const AppLogRoute({List? children}) : super(AppLogRoute.name, initialChildren: children); + const AppLogRoute({List? children}) + : super(AppLogRoute.name, initialChildren: children); static const String name = 'AppLogRoute'; @@ -380,7 +390,8 @@ class AppLogRoute extends PageRouteInfo { /// generated route for /// [ArchivePage] class ArchiveRoute extends PageRouteInfo { - const ArchiveRoute({List? children}) : super(ArchiveRoute.name, initialChildren: children); + const ArchiveRoute({List? children}) + : super(ArchiveRoute.name, initialChildren: children); static const String name = 'ArchiveRoute'; @@ -402,15 +413,15 @@ class AssetViewerRoute extends PageRouteInfo { int? heroOffset, List? children, }) : super( - AssetViewerRoute.name, - args: AssetViewerRouteArgs( - key: key, - initialIndex: initialIndex, - timelineService: timelineService, - heroOffset: heroOffset, - ), - initialChildren: children, - ); + AssetViewerRoute.name, + args: AssetViewerRouteArgs( + key: key, + initialIndex: initialIndex, + timelineService: timelineService, + heroOffset: heroOffset, + ), + initialChildren: children, + ); static const String name = 'AssetViewerRoute'; @@ -454,7 +465,7 @@ class AssetViewerRouteArgs { /// [BackupAlbumSelectionPage] class BackupAlbumSelectionRoute extends PageRouteInfo { const BackupAlbumSelectionRoute({List? children}) - : super(BackupAlbumSelectionRoute.name, initialChildren: children); + : super(BackupAlbumSelectionRoute.name, initialChildren: children); static const String name = 'BackupAlbumSelectionRoute'; @@ -470,7 +481,7 @@ class BackupAlbumSelectionRoute extends PageRouteInfo { /// [BackupControllerPage] class BackupControllerRoute extends PageRouteInfo { const BackupControllerRoute({List? children}) - : super(BackupControllerRoute.name, initialChildren: children); + : super(BackupControllerRoute.name, initialChildren: children); static const String name = 'BackupControllerRoute'; @@ -485,7 +496,8 @@ class BackupControllerRoute extends PageRouteInfo { /// generated route for /// [BackupOptionsPage] class BackupOptionsRoute extends PageRouteInfo { - const BackupOptionsRoute({List? children}) : super(BackupOptionsRoute.name, initialChildren: children); + const BackupOptionsRoute({List? children}) + : super(BackupOptionsRoute.name, initialChildren: children); static const String name = 'BackupOptionsRoute'; @@ -501,7 +513,7 @@ class BackupOptionsRoute extends PageRouteInfo { /// [BetaSyncSettingsPage] class BetaSyncSettingsRoute extends PageRouteInfo { const BetaSyncSettingsRoute({List? children}) - : super(BetaSyncSettingsRoute.name, initialChildren: children); + : super(BetaSyncSettingsRoute.name, initialChildren: children); static const String name = 'BetaSyncSettingsRoute'; @@ -521,13 +533,13 @@ class ChangeExperienceRoute extends PageRouteInfo { required bool switchingToBeta, List? children, }) : super( - ChangeExperienceRoute.name, - args: ChangeExperienceRouteArgs( - key: key, - switchingToBeta: switchingToBeta, - ), - initialChildren: children, - ); + ChangeExperienceRoute.name, + args: ChangeExperienceRouteArgs( + key: key, + switchingToBeta: switchingToBeta, + ), + initialChildren: children, + ); static const String name = 'ChangeExperienceRoute'; @@ -560,7 +572,7 @@ class ChangeExperienceRouteArgs { /// [ChangePasswordPage] class ChangePasswordRoute extends PageRouteInfo { const ChangePasswordRoute({List? children}) - : super(ChangePasswordRoute.name, initialChildren: children); + : super(ChangePasswordRoute.name, initialChildren: children); static const String name = 'ChangePasswordRoute'; @@ -580,10 +592,10 @@ class CreateAlbumRoute extends PageRouteInfo { List? assets, List? children, }) : super( - CreateAlbumRoute.name, - args: CreateAlbumRouteArgs(key: key, assets: assets), - initialChildren: children, - ); + CreateAlbumRoute.name, + args: CreateAlbumRouteArgs(key: key, assets: assets), + initialChildren: children, + ); static const String name = 'CreateAlbumRoute'; @@ -620,10 +632,10 @@ class CropImageRoute extends PageRouteInfo { required Asset asset, List? children, }) : super( - CropImageRoute.name, - args: CropImageRouteArgs(key: key, image: image, asset: asset), - initialChildren: children, - ); + CropImageRoute.name, + args: CropImageRouteArgs(key: key, image: image, asset: asset), + initialChildren: children, + ); static const String name = 'CropImageRoute'; @@ -658,7 +670,8 @@ class CropImageRouteArgs { /// generated route for /// [DriftAlbumsPage] class DriftAlbumsRoute extends PageRouteInfo { - const DriftAlbumsRoute({List? children}) : super(DriftAlbumsRoute.name, initialChildren: children); + const DriftAlbumsRoute({List? children}) + : super(DriftAlbumsRoute.name, initialChildren: children); static const String name = 'DriftAlbumsRoute'; @@ -673,7 +686,8 @@ class DriftAlbumsRoute extends PageRouteInfo { /// generated route for /// [DriftArchivePage] class DriftArchiveRoute extends PageRouteInfo { - const DriftArchiveRoute({List? children}) : super(DriftArchiveRoute.name, initialChildren: children); + const DriftArchiveRoute({List? children}) + : super(DriftArchiveRoute.name, initialChildren: children); static const String name = 'DriftArchiveRoute'; @@ -687,19 +701,20 @@ class DriftArchiveRoute extends PageRouteInfo { /// generated route for /// [DriftAssetSelectionTimelinePage] -class DriftAssetSelectionTimelineRoute extends PageRouteInfo { +class DriftAssetSelectionTimelineRoute + extends PageRouteInfo { DriftAssetSelectionTimelineRoute({ Key? key, Set lockedSelectionAssets = const {}, List? children, }) : super( - DriftAssetSelectionTimelineRoute.name, - args: DriftAssetSelectionTimelineRouteArgs( - key: key, - lockedSelectionAssets: lockedSelectionAssets, - ), - initialChildren: children, - ); + DriftAssetSelectionTimelineRoute.name, + args: DriftAssetSelectionTimelineRouteArgs( + key: key, + lockedSelectionAssets: lockedSelectionAssets, + ), + initialChildren: children, + ); static const String name = 'DriftAssetSelectionTimelineRoute'; @@ -737,7 +752,7 @@ class DriftAssetSelectionTimelineRouteArgs { /// [DriftBackupAlbumSelectionPage] class DriftBackupAlbumSelectionRoute extends PageRouteInfo { const DriftBackupAlbumSelectionRoute({List? children}) - : super(DriftBackupAlbumSelectionRoute.name, initialChildren: children); + : super(DriftBackupAlbumSelectionRoute.name, initialChildren: children); static const String name = 'DriftBackupAlbumSelectionRoute'; @@ -752,7 +767,8 @@ class DriftBackupAlbumSelectionRoute extends PageRouteInfo { /// generated route for /// [DriftBackupPage] class DriftBackupRoute extends PageRouteInfo { - const DriftBackupRoute({List? children}) : super(DriftBackupRoute.name, initialChildren: children); + const DriftBackupRoute({List? children}) + : super(DriftBackupRoute.name, initialChildren: children); static const String name = 'DriftBackupRoute'; @@ -768,7 +784,7 @@ class DriftBackupRoute extends PageRouteInfo { /// [DriftCreateAlbumPage] class DriftCreateAlbumRoute extends PageRouteInfo { const DriftCreateAlbumRoute({List? children}) - : super(DriftCreateAlbumRoute.name, initialChildren: children); + : super(DriftCreateAlbumRoute.name, initialChildren: children); static const String name = 'DriftCreateAlbumRoute'; @@ -783,7 +799,8 @@ class DriftCreateAlbumRoute extends PageRouteInfo { /// generated route for /// [DriftFavoritePage] class DriftFavoriteRoute extends PageRouteInfo { - const DriftFavoriteRoute({List? children}) : super(DriftFavoriteRoute.name, initialChildren: children); + const DriftFavoriteRoute({List? children}) + : super(DriftFavoriteRoute.name, initialChildren: children); static const String name = 'DriftFavoriteRoute'; @@ -798,7 +815,8 @@ class DriftFavoriteRoute extends PageRouteInfo { /// generated route for /// [DriftLibraryPage] class DriftLibraryRoute extends PageRouteInfo { - const DriftLibraryRoute({List? children}) : super(DriftLibraryRoute.name, initialChildren: children); + const DriftLibraryRoute({List? children}) + : super(DriftLibraryRoute.name, initialChildren: children); static const String name = 'DriftLibraryRoute'; @@ -814,7 +832,7 @@ class DriftLibraryRoute extends PageRouteInfo { /// [DriftLocalAlbumsPage] class DriftLocalAlbumsRoute extends PageRouteInfo { const DriftLocalAlbumsRoute({List? children}) - : super(DriftLocalAlbumsRoute.name, initialChildren: children); + : super(DriftLocalAlbumsRoute.name, initialChildren: children); static const String name = 'DriftLocalAlbumsRoute'; @@ -830,7 +848,7 @@ class DriftLocalAlbumsRoute extends PageRouteInfo { /// [DriftLockedFolderPage] class DriftLockedFolderRoute extends PageRouteInfo { const DriftLockedFolderRoute({List? children}) - : super(DriftLockedFolderRoute.name, initialChildren: children); + : super(DriftLockedFolderRoute.name, initialChildren: children); static const String name = 'DriftLockedFolderRoute'; @@ -851,14 +869,14 @@ class DriftMemoryRoute extends PageRouteInfo { Key? key, List? children, }) : super( - DriftMemoryRoute.name, - args: DriftMemoryRouteArgs( - memories: memories, - memoryIndex: memoryIndex, - key: key, - ), - initialChildren: children, - ); + DriftMemoryRoute.name, + args: DriftMemoryRouteArgs( + memories: memories, + memoryIndex: memoryIndex, + key: key, + ), + initialChildren: children, + ); static const String name = 'DriftMemoryRoute'; @@ -896,16 +914,17 @@ class DriftMemoryRouteArgs { /// generated route for /// [DriftPartnerDetailPage] -class DriftPartnerDetailRoute extends PageRouteInfo { +class DriftPartnerDetailRoute + extends PageRouteInfo { DriftPartnerDetailRoute({ Key? key, required PartnerUserDto partner, List? children, }) : super( - DriftPartnerDetailRoute.name, - args: DriftPartnerDetailRouteArgs(key: key, partner: partner), - initialChildren: children, - ); + DriftPartnerDetailRoute.name, + args: DriftPartnerDetailRouteArgs(key: key, partner: partner), + initialChildren: children, + ); static const String name = 'DriftPartnerDetailRoute'; @@ -934,7 +953,8 @@ class DriftPartnerDetailRouteArgs { /// generated route for /// [DriftPartnerPage] class DriftPartnerRoute extends PageRouteInfo { - const DriftPartnerRoute({List? children}) : super(DriftPartnerRoute.name, initialChildren: children); + const DriftPartnerRoute({List? children}) + : super(DriftPartnerRoute.name, initialChildren: children); static const String name = 'DriftPartnerRoute'; @@ -954,10 +974,10 @@ class DriftPlaceDetailRoute extends PageRouteInfo { required String place, List? children, }) : super( - DriftPlaceDetailRoute.name, - args: DriftPlaceDetailRouteArgs(key: key, place: place), - initialChildren: children, - ); + DriftPlaceDetailRoute.name, + args: DriftPlaceDetailRouteArgs(key: key, place: place), + initialChildren: children, + ); static const String name = 'DriftPlaceDetailRoute'; @@ -991,10 +1011,10 @@ class DriftPlaceRoute extends PageRouteInfo { LatLng? currentLocation, List? children, }) : super( - DriftPlaceRoute.name, - args: DriftPlaceRouteArgs(key: key, currentLocation: currentLocation), - initialChildren: children, - ); + DriftPlaceRoute.name, + args: DriftPlaceRouteArgs(key: key, currentLocation: currentLocation), + initialChildren: children, + ); static const String name = 'DriftPlaceRoute'; @@ -1029,7 +1049,7 @@ class DriftPlaceRouteArgs { /// [DriftRecentlyTakenPage] class DriftRecentlyTakenRoute extends PageRouteInfo { const DriftRecentlyTakenRoute({List? children}) - : super(DriftRecentlyTakenRoute.name, initialChildren: children); + : super(DriftRecentlyTakenRoute.name, initialChildren: children); static const String name = 'DriftRecentlyTakenRoute'; @@ -1049,10 +1069,10 @@ class DriftSearchRoute extends PageRouteInfo { SearchFilter? preFilter, List? children, }) : super( - DriftSearchRoute.name, - args: DriftSearchRouteArgs(key: key, preFilter: preFilter), - initialChildren: children, - ); + DriftSearchRoute.name, + args: DriftSearchRouteArgs(key: key, preFilter: preFilter), + initialChildren: children, + ); static const String name = 'DriftSearchRoute'; @@ -1083,7 +1103,8 @@ class DriftSearchRouteArgs { /// generated route for /// [DriftTrashPage] class DriftTrashRoute extends PageRouteInfo { - const DriftTrashRoute({List? children}) : super(DriftTrashRoute.name, initialChildren: children); + const DriftTrashRoute({List? children}) + : super(DriftTrashRoute.name, initialChildren: children); static const String name = 'DriftTrashRoute'; @@ -1099,7 +1120,7 @@ class DriftTrashRoute extends PageRouteInfo { /// [DriftUploadDetailPage] class DriftUploadDetailRoute extends PageRouteInfo { const DriftUploadDetailRoute({List? children}) - : super(DriftUploadDetailRoute.name, initialChildren: children); + : super(DriftUploadDetailRoute.name, initialChildren: children); static const String name = 'DriftUploadDetailRoute'; @@ -1113,16 +1134,17 @@ class DriftUploadDetailRoute extends PageRouteInfo { /// generated route for /// [DriftUserSelectionPage] -class DriftUserSelectionRoute extends PageRouteInfo { +class DriftUserSelectionRoute + extends PageRouteInfo { DriftUserSelectionRoute({ Key? key, required RemoteAlbum album, List? children, }) : super( - DriftUserSelectionRoute.name, - args: DriftUserSelectionRouteArgs(key: key, album: album), - initialChildren: children, - ); + DriftUserSelectionRoute.name, + args: DriftUserSelectionRouteArgs(key: key, album: album), + initialChildren: children, + ); static const String name = 'DriftUserSelectionRoute'; @@ -1151,7 +1173,8 @@ class DriftUserSelectionRouteArgs { /// generated route for /// [DriftVideoPage] class DriftVideoRoute extends PageRouteInfo { - const DriftVideoRoute({List? children}) : super(DriftVideoRoute.name, initialChildren: children); + const DriftVideoRoute({List? children}) + : super(DriftVideoRoute.name, initialChildren: children); static const String name = 'DriftVideoRoute'; @@ -1173,15 +1196,15 @@ class EditImageRoute extends PageRouteInfo { required bool isEdited, List? children, }) : super( - EditImageRoute.name, - args: EditImageRouteArgs( - key: key, - asset: asset, - image: image, - isEdited: isEdited, - ), - initialChildren: children, - ); + EditImageRoute.name, + args: EditImageRouteArgs( + key: key, + asset: asset, + image: image, + isEdited: isEdited, + ), + initialChildren: children, + ); static const String name = 'EditImageRoute'; @@ -1225,7 +1248,7 @@ class EditImageRouteArgs { /// [FailedBackupStatusPage] class FailedBackupStatusRoute extends PageRouteInfo { const FailedBackupStatusRoute({List? children}) - : super(FailedBackupStatusRoute.name, initialChildren: children); + : super(FailedBackupStatusRoute.name, initialChildren: children); static const String name = 'FailedBackupStatusRoute'; @@ -1240,7 +1263,8 @@ class FailedBackupStatusRoute extends PageRouteInfo { /// generated route for /// [FavoritesPage] class FavoritesRoute extends PageRouteInfo { - const FavoritesRoute({List? children}) : super(FavoritesRoute.name, initialChildren: children); + const FavoritesRoute({List? children}) + : super(FavoritesRoute.name, initialChildren: children); static const String name = 'FavoritesRoute'; @@ -1255,7 +1279,8 @@ class FavoritesRoute extends PageRouteInfo { /// generated route for /// [FeatInDevPage] class FeatInDevRoute extends PageRouteInfo { - const FeatInDevRoute({List? children}) : super(FeatInDevRoute.name, initialChildren: children); + const FeatInDevRoute({List? children}) + : super(FeatInDevRoute.name, initialChildren: children); static const String name = 'FeatInDevRoute'; @@ -1276,10 +1301,10 @@ class FilterImageRoute extends PageRouteInfo { required Asset asset, List? children, }) : super( - FilterImageRoute.name, - args: FilterImageRouteArgs(key: key, image: image, asset: asset), - initialChildren: children, - ); + FilterImageRoute.name, + args: FilterImageRouteArgs(key: key, image: image, asset: asset), + initialChildren: children, + ); static const String name = 'FilterImageRoute'; @@ -1323,10 +1348,10 @@ class FolderRoute extends PageRouteInfo { RecursiveFolder? folder, List? children, }) : super( - FolderRoute.name, - args: FolderRouteArgs(key: key, folder: folder), - initialChildren: children, - ); + FolderRoute.name, + args: FolderRouteArgs(key: key, folder: folder), + initialChildren: children, + ); static const String name = 'FolderRoute'; @@ -1365,16 +1390,16 @@ class GalleryViewerRoute extends PageRouteInfo { bool showStack = false, List? children, }) : super( - GalleryViewerRoute.name, - args: GalleryViewerRouteArgs( - key: key, - renderList: renderList, - initialIndex: initialIndex, - heroOffset: heroOffset, - showStack: showStack, - ), - initialChildren: children, - ); + GalleryViewerRoute.name, + args: GalleryViewerRouteArgs( + key: key, + renderList: renderList, + initialIndex: initialIndex, + heroOffset: heroOffset, + showStack: showStack, + ), + initialChildren: children, + ); static const String name = 'GalleryViewerRoute'; @@ -1422,7 +1447,7 @@ class GalleryViewerRouteArgs { /// [HeaderSettingsPage] class HeaderSettingsRoute extends PageRouteInfo { const HeaderSettingsRoute({List? children}) - : super(HeaderSettingsRoute.name, initialChildren: children); + : super(HeaderSettingsRoute.name, initialChildren: children); static const String name = 'HeaderSettingsRoute'; @@ -1437,7 +1462,8 @@ class HeaderSettingsRoute extends PageRouteInfo { /// generated route for /// [LibraryPage] class LibraryRoute extends PageRouteInfo { - const LibraryRoute({List? children}) : super(LibraryRoute.name, initialChildren: children); + const LibraryRoute({List? children}) + : super(LibraryRoute.name, initialChildren: children); static const String name = 'LibraryRoute'; @@ -1452,7 +1478,8 @@ class LibraryRoute extends PageRouteInfo { /// generated route for /// [LocalAlbumsPage] class LocalAlbumsRoute extends PageRouteInfo { - const LocalAlbumsRoute({List? children}) : super(LocalAlbumsRoute.name, initialChildren: children); + const LocalAlbumsRoute({List? children}) + : super(LocalAlbumsRoute.name, initialChildren: children); static const String name = 'LocalAlbumsRoute'; @@ -1468,7 +1495,7 @@ class LocalAlbumsRoute extends PageRouteInfo { /// [LocalMediaSummaryPage] class LocalMediaSummaryRoute extends PageRouteInfo { const LocalMediaSummaryRoute({List? children}) - : super(LocalMediaSummaryRoute.name, initialChildren: children); + : super(LocalMediaSummaryRoute.name, initialChildren: children); static const String name = 'LocalMediaSummaryRoute'; @@ -1488,10 +1515,10 @@ class LocalTimelineRoute extends PageRouteInfo { required LocalAlbum album, List? children, }) : super( - LocalTimelineRoute.name, - args: LocalTimelineRouteArgs(key: key, album: album), - initialChildren: children, - ); + LocalTimelineRoute.name, + args: LocalTimelineRouteArgs(key: key, album: album), + initialChildren: children, + ); static const String name = 'LocalTimelineRoute'; @@ -1520,7 +1547,8 @@ class LocalTimelineRouteArgs { /// generated route for /// [LockedPage] class LockedRoute extends PageRouteInfo { - const LockedRoute({List? children}) : super(LockedRoute.name, initialChildren: children); + const LockedRoute({List? children}) + : super(LockedRoute.name, initialChildren: children); static const String name = 'LockedRoute'; @@ -1535,7 +1563,8 @@ class LockedRoute extends PageRouteInfo { /// generated route for /// [LoginPage] class LoginRoute extends PageRouteInfo { - const LoginRoute({List? children}) : super(LoginRoute.name, initialChildren: children); + const LoginRoute({List? children}) + : super(LoginRoute.name, initialChildren: children); static const String name = 'LoginRoute'; @@ -1550,7 +1579,8 @@ class LoginRoute extends PageRouteInfo { /// generated route for /// [MainTimelinePage] class MainTimelineRoute extends PageRouteInfo { - const MainTimelineRoute({List? children}) : super(MainTimelineRoute.name, initialChildren: children); + const MainTimelineRoute({List? children}) + : super(MainTimelineRoute.name, initialChildren: children); static const String name = 'MainTimelineRoute'; @@ -1570,13 +1600,13 @@ class MapLocationPickerRoute extends PageRouteInfo { LatLng initialLatLng = const LatLng(0, 0), List? children, }) : super( - MapLocationPickerRoute.name, - args: MapLocationPickerRouteArgs( - key: key, - initialLatLng: initialLatLng, - ), - initialChildren: children, - ); + MapLocationPickerRoute.name, + args: MapLocationPickerRouteArgs( + key: key, + initialLatLng: initialLatLng, + ), + initialChildren: children, + ); static const String name = 'MapLocationPickerRoute'; @@ -1614,11 +1644,11 @@ class MapLocationPickerRouteArgs { /// [MapPage] class MapRoute extends PageRouteInfo { MapRoute({Key? key, LatLng? initialLocation, List? children}) - : super( - MapRoute.name, - args: MapRouteArgs(key: key, initialLocation: initialLocation), - initialChildren: children, - ); + : super( + MapRoute.name, + args: MapRouteArgs(key: key, initialLocation: initialLocation), + initialChildren: children, + ); static const String name = 'MapRoute'; @@ -1655,14 +1685,14 @@ class MemoryRoute extends PageRouteInfo { Key? key, List? children, }) : super( - MemoryRoute.name, - args: MemoryRouteArgs( - memories: memories, - memoryIndex: memoryIndex, - key: key, - ), - initialChildren: children, - ); + MemoryRoute.name, + args: MemoryRouteArgs( + memories: memories, + memoryIndex: memoryIndex, + key: key, + ), + initialChildren: children, + ); static const String name = 'MemoryRoute'; @@ -1709,16 +1739,16 @@ class NativeVideoViewerRoute extends PageRouteInfo { int playbackDelayFactor = 1, List? children, }) : super( - NativeVideoViewerRoute.name, - args: NativeVideoViewerRouteArgs( - key: key, - asset: asset, - image: image, - showControls: showControls, - playbackDelayFactor: playbackDelayFactor, - ), - initialChildren: children, - ); + NativeVideoViewerRoute.name, + args: NativeVideoViewerRouteArgs( + key: key, + asset: asset, + image: image, + showControls: showControls, + playbackDelayFactor: playbackDelayFactor, + ), + initialChildren: children, + ); static const String name = 'NativeVideoViewerRoute'; @@ -1770,10 +1800,10 @@ class PartnerDetailRoute extends PageRouteInfo { required UserDto partner, List? children, }) : super( - PartnerDetailRoute.name, - args: PartnerDetailRouteArgs(key: key, partner: partner), - initialChildren: children, - ); + PartnerDetailRoute.name, + args: PartnerDetailRouteArgs(key: key, partner: partner), + initialChildren: children, + ); static const String name = 'PartnerDetailRoute'; @@ -1802,7 +1832,8 @@ class PartnerDetailRouteArgs { /// generated route for /// [PartnerPage] class PartnerRoute extends PageRouteInfo { - const PartnerRoute({List? children}) : super(PartnerRoute.name, initialChildren: children); + const PartnerRoute({List? children}) + : super(PartnerRoute.name, initialChildren: children); static const String name = 'PartnerRoute'; @@ -1818,7 +1849,7 @@ class PartnerRoute extends PageRouteInfo { /// [PeopleCollectionPage] class PeopleCollectionRoute extends PageRouteInfo { const PeopleCollectionRoute({List? children}) - : super(PeopleCollectionRoute.name, initialChildren: children); + : super(PeopleCollectionRoute.name, initialChildren: children); static const String name = 'PeopleCollectionRoute'; @@ -1834,7 +1865,7 @@ class PeopleCollectionRoute extends PageRouteInfo { /// [PermissionOnboardingPage] class PermissionOnboardingRoute extends PageRouteInfo { const PermissionOnboardingRoute({List? children}) - : super(PermissionOnboardingRoute.name, initialChildren: children); + : super(PermissionOnboardingRoute.name, initialChildren: children); static const String name = 'PermissionOnboardingRoute'; @@ -1855,14 +1886,14 @@ class PersonResultRoute extends PageRouteInfo { required String personName, List? children, }) : super( - PersonResultRoute.name, - args: PersonResultRouteArgs( - key: key, - personId: personId, - personName: personName, - ), - initialChildren: children, - ); + PersonResultRoute.name, + args: PersonResultRouteArgs( + key: key, + personId: personId, + personName: personName, + ), + initialChildren: children, + ); static const String name = 'PersonResultRoute'; @@ -1901,7 +1932,8 @@ class PersonResultRouteArgs { /// generated route for /// [PhotosPage] class PhotosRoute extends PageRouteInfo { - const PhotosRoute({List? children}) : super(PhotosRoute.name, initialChildren: children); + const PhotosRoute({List? children}) + : super(PhotosRoute.name, initialChildren: children); static const String name = 'PhotosRoute'; @@ -1921,10 +1953,10 @@ class PinAuthRoute extends PageRouteInfo { bool createPinCode = false, List? children, }) : super( - PinAuthRoute.name, - args: PinAuthRouteArgs(key: key, createPinCode: createPinCode), - initialChildren: children, - ); + PinAuthRoute.name, + args: PinAuthRouteArgs(key: key, createPinCode: createPinCode), + initialChildren: children, + ); static const String name = 'PinAuthRoute'; @@ -1960,13 +1992,13 @@ class PlacesCollectionRoute extends PageRouteInfo { LatLng? currentLocation, List? children, }) : super( - PlacesCollectionRoute.name, - args: PlacesCollectionRouteArgs( - key: key, - currentLocation: currentLocation, - ), - initialChildren: children, - ); + PlacesCollectionRoute.name, + args: PlacesCollectionRouteArgs( + key: key, + currentLocation: currentLocation, + ), + initialChildren: children, + ); static const String name = 'PlacesCollectionRoute'; @@ -2000,7 +2032,8 @@ class PlacesCollectionRouteArgs { /// generated route for /// [RecentlyTakenPage] class RecentlyTakenRoute extends PageRouteInfo { - const RecentlyTakenRoute({List? children}) : super(RecentlyTakenRoute.name, initialChildren: children); + const RecentlyTakenRoute({List? children}) + : super(RecentlyTakenRoute.name, initialChildren: children); static const String name = 'RecentlyTakenRoute'; @@ -2020,10 +2053,10 @@ class RemoteAlbumRoute extends PageRouteInfo { required RemoteAlbum album, List? children, }) : super( - RemoteAlbumRoute.name, - args: RemoteAlbumRouteArgs(key: key, album: album), - initialChildren: children, - ); + RemoteAlbumRoute.name, + args: RemoteAlbumRouteArgs(key: key, album: album), + initialChildren: children, + ); static const String name = 'RemoteAlbumRoute'; @@ -2053,7 +2086,7 @@ class RemoteAlbumRouteArgs { /// [RemoteMediaSummaryPage] class RemoteMediaSummaryRoute extends PageRouteInfo { const RemoteMediaSummaryRoute({List? children}) - : super(RemoteMediaSummaryRoute.name, initialChildren: children); + : super(RemoteMediaSummaryRoute.name, initialChildren: children); static const String name = 'RemoteMediaSummaryRoute'; @@ -2073,10 +2106,10 @@ class SearchRoute extends PageRouteInfo { SearchFilter? prefilter, List? children, }) : super( - SearchRoute.name, - args: SearchRouteArgs(key: key, prefilter: prefilter), - initialChildren: children, - ); + SearchRoute.name, + args: SearchRouteArgs(key: key, prefilter: prefilter), + initialChildren: children, + ); static const String name = 'SearchRoute'; @@ -2107,7 +2140,8 @@ class SearchRouteArgs { /// generated route for /// [SettingsPage] class SettingsRoute extends PageRouteInfo { - const SettingsRoute({List? children}) : super(SettingsRoute.name, initialChildren: children); + const SettingsRoute({List? children}) + : super(SettingsRoute.name, initialChildren: children); static const String name = 'SettingsRoute'; @@ -2127,10 +2161,10 @@ class SettingsSubRoute extends PageRouteInfo { Key? key, List? children, }) : super( - SettingsSubRoute.name, - args: SettingsSubRouteArgs(section: section, key: key), - initialChildren: children, - ); + SettingsSubRoute.name, + args: SettingsSubRouteArgs(section: section, key: key), + initialChildren: children, + ); static const String name = 'SettingsSubRoute'; @@ -2164,10 +2198,10 @@ class ShareIntentRoute extends PageRouteInfo { required List attachments, List? children, }) : super( - ShareIntentRoute.name, - args: ShareIntentRouteArgs(key: key, attachments: attachments), - initialChildren: children, - ); + ShareIntentRoute.name, + args: ShareIntentRouteArgs(key: key, attachments: attachments), + initialChildren: children, + ); static const String name = 'ShareIntentRoute'; @@ -2203,15 +2237,15 @@ class SharedLinkEditRoute extends PageRouteInfo { String? albumId, List? children, }) : super( - SharedLinkEditRoute.name, - args: SharedLinkEditRouteArgs( - key: key, - existingLink: existingLink, - assetsList: assetsList, - albumId: albumId, - ), - initialChildren: children, - ); + SharedLinkEditRoute.name, + args: SharedLinkEditRouteArgs( + key: key, + existingLink: existingLink, + assetsList: assetsList, + albumId: albumId, + ), + initialChildren: children, + ); static const String name = 'SharedLinkEditRoute'; @@ -2256,7 +2290,8 @@ class SharedLinkEditRouteArgs { /// generated route for /// [SharedLinkPage] class SharedLinkRoute extends PageRouteInfo { - const SharedLinkRoute({List? children}) : super(SharedLinkRoute.name, initialChildren: children); + const SharedLinkRoute({List? children}) + : super(SharedLinkRoute.name, initialChildren: children); static const String name = 'SharedLinkRoute'; @@ -2271,7 +2306,8 @@ class SharedLinkRoute extends PageRouteInfo { /// generated route for /// [SplashScreenPage] class SplashScreenRoute extends PageRouteInfo { - const SplashScreenRoute({List? children}) : super(SplashScreenRoute.name, initialChildren: children); + const SplashScreenRoute({List? children}) + : super(SplashScreenRoute.name, initialChildren: children); static const String name = 'SplashScreenRoute'; @@ -2286,7 +2322,8 @@ class SplashScreenRoute extends PageRouteInfo { /// generated route for /// [TabControllerPage] class TabControllerRoute extends PageRouteInfo { - const TabControllerRoute({List? children}) : super(TabControllerRoute.name, initialChildren: children); + const TabControllerRoute({List? children}) + : super(TabControllerRoute.name, initialChildren: children); static const String name = 'TabControllerRoute'; @@ -2301,7 +2338,8 @@ class TabControllerRoute extends PageRouteInfo { /// generated route for /// [TabShellPage] class TabShellRoute extends PageRouteInfo { - const TabShellRoute({List? children}) : super(TabShellRoute.name, initialChildren: children); + const TabShellRoute({List? children}) + : super(TabShellRoute.name, initialChildren: children); static const String name = 'TabShellRoute'; @@ -2316,7 +2354,8 @@ class TabShellRoute extends PageRouteInfo { /// generated route for /// [TrashPage] class TrashRoute extends PageRouteInfo { - const TrashRoute({List? children}) : super(TrashRoute.name, initialChildren: children); + const TrashRoute({List? children}) + : super(TrashRoute.name, initialChildren: children); static const String name = 'TrashRoute'; diff --git a/mobile/lib/services/action.service.dart b/mobile/lib/services/action.service.dart index 4ed80d9f90..5a23f16534 100644 --- a/mobile/lib/services/action.service.dart +++ b/mobile/lib/services/action.service.dart @@ -49,11 +49,7 @@ class ActionService { ); Future shareLink(List remoteIds, BuildContext context) async { - context.pushRoute( - SharedLinkEditRoute( - assetsList: remoteIds, - ), - ); + context.pushRoute(SharedLinkEditRoute(assetsList: remoteIds)); } Future favorite(List remoteIds) async { @@ -67,39 +63,18 @@ class ActionService { } Future archive(List remoteIds) async { - await _assetApiRepository.updateVisibility( - remoteIds, - AssetVisibilityEnum.archive, - ); - await _remoteAssetRepository.updateVisibility( - remoteIds, - AssetVisibility.archive, - ); + await _assetApiRepository.updateVisibility(remoteIds, AssetVisibilityEnum.archive); + await _remoteAssetRepository.updateVisibility(remoteIds, AssetVisibility.archive); } Future unArchive(List remoteIds) async { - await _assetApiRepository.updateVisibility( - remoteIds, - AssetVisibilityEnum.timeline, - ); - await _remoteAssetRepository.updateVisibility( - remoteIds, - AssetVisibility.timeline, - ); + await _assetApiRepository.updateVisibility(remoteIds, AssetVisibilityEnum.timeline); + await _remoteAssetRepository.updateVisibility(remoteIds, AssetVisibility.timeline); } - Future moveToLockFolder( - List remoteIds, - List localIds, - ) async { - await _assetApiRepository.updateVisibility( - remoteIds, - AssetVisibilityEnum.locked, - ); - await _remoteAssetRepository.updateVisibility( - remoteIds, - AssetVisibility.locked, - ); + Future moveToLockFolder(List remoteIds, List localIds) async { + await _assetApiRepository.updateVisibility(remoteIds, AssetVisibilityEnum.locked); + await _remoteAssetRepository.updateVisibility(remoteIds, AssetVisibility.locked); // Ask user if they want to delete local copies if (localIds.isNotEmpty) { @@ -112,14 +87,8 @@ class ActionService { } Future removeFromLockFolder(List remoteIds) async { - await _assetApiRepository.updateVisibility( - remoteIds, - AssetVisibilityEnum.timeline, - ); - await _remoteAssetRepository.updateVisibility( - remoteIds, - AssetVisibility.timeline, - ); + await _assetApiRepository.updateVisibility(remoteIds, AssetVisibilityEnum.timeline); + await _remoteAssetRepository.updateVisibility(remoteIds, AssetVisibility.timeline); } Future trash(List remoteIds) async { @@ -145,10 +114,7 @@ class ActionService { } } - Future deleteRemoteAndLocal( - List remoteIds, - List localIds, - ) async { + Future deleteRemoteAndLocal(List remoteIds, List localIds) async { await _assetApiRepository.delete(remoteIds, true); await _remoteAssetRepository.delete(remoteIds); @@ -171,10 +137,7 @@ class ActionService { return 0; } - Future editLocation( - List remoteIds, - BuildContext context, - ) async { + Future editLocation(List remoteIds, BuildContext context) async { maplibre.LatLng? initialLatLng; if (remoteIds.length == 1) { final exif = await _remoteAssetRepository.getExif(remoteIds[0]); @@ -184,23 +147,14 @@ class ActionService { } } - final location = await showLocationPicker( - context: context, - initialLatLng: initialLatLng, - ); + final location = await showLocationPicker(context: context, initialLatLng: initialLatLng); if (location == null) { return false; } - await _assetApiRepository.updateLocation( - remoteIds, - location, - ); - await _remoteAssetRepository.updateLocation( - remoteIds, - location, - ); + await _assetApiRepository.updateLocation(remoteIds, location); + await _remoteAssetRepository.updateLocation(remoteIds, location); return true; } diff --git a/mobile/lib/services/activity.service.dart b/mobile/lib/services/activity.service.dart index 611d985afe..7a0a092e7d 100644 --- a/mobile/lib/services/activity.service.dart +++ b/mobile/lib/services/activity.service.dart @@ -11,10 +11,7 @@ class ActivityService with ErrorLoggerMixin { ActivityService(this._activityApiRepository); - Future> getAllActivities( - String albumId, { - String? assetId, - }) async { + Future> getAllActivities(String albumId, {String? assetId}) async { return logError( () => _activityApiRepository.getAll(albumId, assetId: assetId), defaultValue: [], @@ -41,19 +38,9 @@ class ActivityService with ErrorLoggerMixin { ); } - AsyncFuture addActivity( - String albumId, - ActivityType type, { - String? assetId, - String? comment, - }) async { + AsyncFuture addActivity(String albumId, ActivityType type, {String? assetId, String? comment}) async { return guardError( - () => _activityApiRepository.create( - albumId, - type, - assetId: assetId, - comment: comment, - ), + () => _activityApiRepository.create(albumId, type, assetId: assetId, comment: comment), errorMessage: "Failed to create $type for album $albumId", ); } diff --git a/mobile/lib/services/album.service.dart b/mobile/lib/services/album.service.dart index 308b9acc44..454f652035 100644 --- a/mobile/lib/services/album.service.dart +++ b/mobile/lib/services/album.service.dart @@ -77,7 +77,7 @@ class AlbumService { final (selectedIds, excludedIds, onDevice) = await ( _backupAlbumRepository.getIdsBySelection(BackupSelection.select).then((value) => value.toSet()), _backupAlbumRepository.getIdsBySelection(BackupSelection.exclude).then((value) => value.toSet()), - _albumMediaRepository.getAll() + _albumMediaRepository.getAll(), ).wait; _log.info("Found ${onDevice.length} device albums"); if (selectedIds.isEmpty) { @@ -102,9 +102,7 @@ class AlbumService { } // remove all excluded albums onDevice.removeWhere((e) => excludedIds.contains(e.localId)); - _log.info( - "Ignoring ${excludedIds.length} excluded albums resulting in ${onDevice.length} device albums", - ); + _log.info("Ignoring ${excludedIds.length} excluded albums resulting in ${onDevice.length} device albums"); } final allAlbum = onDevice.firstWhereOrNull((album) => album.isAll); @@ -130,16 +128,11 @@ class AlbumService { return changes; } - Future> _loadExcludedAssetIds( - List albums, - Set excludedAlbumIds, - ) async { + Future> _loadExcludedAssetIds(List albums, Set excludedAlbumIds) async { final Set result = HashSet(); for (final batchAlbums in albums.where((album) => excludedAlbumIds.contains(album.localId)).slices(5)) { await batchAlbums - .map( - (album) => _albumMediaRepository.getAssetIds(album.localId!).then((assetIds) => result.addAll(assetIds)), - ) + .map((album) => _albumMediaRepository.getAssetIds(album.localId!).then((assetIds) => result.addAll(assetIds))) .wait; } return result; @@ -167,13 +160,10 @@ class AlbumService { _albumApiRepository.getAll(shared: true), // Passing null (or nothing) for `shared` returns only albums that // explicitly belong to us - _albumApiRepository.getAll(shared: null) + _albumApiRepository.getAll(shared: null), ).wait; - final albums = HashSet( - equals: (a, b) => a.remoteId == b.remoteId, - hashCode: (a) => a.remoteId.hashCode, - ); + final albums = HashSet(equals: (a, b) => a.remoteId == b.remoteId, hashCode: (a) => a.remoteId.hashCode); albums.addAll(sharedAlbum); albums.addAll(ownedAlbum); @@ -205,7 +195,7 @@ class AlbumService { */ Future _getNextAlbumName() async { const baseName = "Untitled"; - for (int round = 0;; round++) { + for (int round = 0; ; round++) { final proposedName = "$baseName${round == 0 ? "" : " ($round)"}"; if (null == await _albumRepository.getByName(proposedName, owner: true)) { @@ -214,46 +204,28 @@ class AlbumService { } } - Future createAlbumWithGeneratedName( - Iterable assets, - ) async { - return createAlbum( - await _getNextAlbumName(), - assets, - [], - ); + Future createAlbumWithGeneratedName(Iterable assets) async { + return createAlbum(await _getNextAlbumName(), assets, []); } - Future addAssets( - Album album, - Iterable assets, - ) async { + Future addAssets(Album album, Iterable assets) async { try { - final result = await _albumApiRepository.addAssets( - album.remoteId!, - assets.map((asset) => asset.remoteId!), - ); + final result = await _albumApiRepository.addAssets(album.remoteId!, assets.map((asset) => asset.remoteId!)); - final List addedAssets = - result.added.map((id) => assets.firstWhere((asset) => asset.remoteId == id)).toList(); + final List addedAssets = result.added + .map((id) => assets.firstWhere((asset) => asset.remoteId == id)) + .toList(); await _updateAssets(album.id, add: addedAssets); - return AlbumAddAssetsResponse( - alreadyInAlbum: result.duplicates, - successfullyAdded: addedAssets.length, - ); + return AlbumAddAssetsResponse(alreadyInAlbum: result.duplicates, successfullyAdded: addedAssets.length); } catch (e) { debugPrint("Error addAssets ${e.toString()}"); } return null; } - Future _updateAssets( - int albumId, { - List add = const [], - List remove = const [], - }) => + Future _updateAssets(int albumId, {List add = const [], List remove = const []}) => _albumRepository.transaction(() async { final album = await _albumRepository.get(albumId); if (album == null) return; @@ -265,10 +237,7 @@ class AlbumService { Future setActivityStatus(Album album, bool enabled) async { try { - final updatedAlbum = await _albumApiRepository.update( - album.remoteId!, - activityEnabled: enabled, - ); + final updatedAlbum = await _albumApiRepository.update(album.remoteId!, activityEnabled: enabled); album.activityEnabled = updatedAlbum.activityEnabled; await _albumRepository.update(album); return true; @@ -291,9 +260,7 @@ class AlbumService { final List albums = await _albumRepository.getAll(shared: true); final List existing = []; for (Album album in albums) { - existing.addAll( - await _assetRepository.getByAlbum(album, notOwnedBy: [userId]), - ); + existing.addAll(await _assetRepository.getByAlbum(album, notOwnedBy: [userId])); } final List idsToRemove = _syncService.sharedAssetsToRemove(foreignAssets, existing); if (idsToRemove.isNotEmpty) { @@ -319,15 +286,9 @@ class AlbumService { } } - Future removeAsset( - Album album, - Iterable assets, - ) async { + Future removeAsset(Album album, Iterable assets) async { try { - final result = await _albumApiRepository.removeAssets( - album.remoteId!, - assets.map((asset) => asset.remoteId!), - ); + final result = await _albumApiRepository.removeAssets(album.remoteId!, assets.map((asset) => asset.remoteId!)); final toRemove = result.removed.map((id) => assets.firstWhere((asset) => asset.remoteId == id)); await _updateAssets(album.id, remove: toRemove.toList()); return true; @@ -337,15 +298,9 @@ class AlbumService { return false; } - Future removeUser( - Album album, - UserDto user, - ) async { + Future removeUser(Album album, UserDto user) async { try { - await _albumApiRepository.removeUser( - album.remoteId!, - userId: user.id, - ); + await _albumApiRepository.removeUser(album.remoteId!, userId: user.id); album.sharedUsers.remove(entity.User.fromDto(user)); await _albumRepository.removeUsers(album, [user]); @@ -360,20 +315,14 @@ class AlbumService { } } - Future addUsers( - Album album, - List userIds, - ) async { + Future addUsers(Album album, List userIds) async { try { final updatedAlbum = await _albumApiRepository.addUsers(album.remoteId!, userIds); album.sharedUsers.addAll(updatedAlbum.remoteUsers); album.shared = true; - await _albumRepository.addUsers( - album, - album.sharedUsers.map((u) => u.toDto()).toList(), - ); + await _albumRepository.addUsers(album, album.sharedUsers.map((u) => u.toDto()).toList()); await _albumRepository.update(album); return true; @@ -383,15 +332,9 @@ class AlbumService { return false; } - Future changeTitleAlbum( - Album album, - String newAlbumTitle, - ) async { + Future changeTitleAlbum(Album album, String newAlbumTitle) async { try { - final updatedAlbum = await _albumApiRepository.update( - album.remoteId!, - name: newAlbumTitle, - ); + final updatedAlbum = await _albumApiRepository.update(album.remoteId!, name: newAlbumTitle); album.name = updatedAlbum.name; await _albumRepository.update(album); @@ -402,15 +345,9 @@ class AlbumService { } } - Future changeDescriptionAlbum( - Album album, - String newAlbumDescription, - ) async { + Future changeDescriptionAlbum(Album album, String newAlbumDescription) async { try { - final updatedAlbum = await _albumApiRepository.update( - album.remoteId!, - description: newAlbumDescription, - ); + final updatedAlbum = await _albumApiRepository.update(album.remoteId!, description: newAlbumDescription); album.description = updatedAlbum.description; await _albumRepository.update(album); @@ -421,26 +358,13 @@ class AlbumService { } } - Future getAlbumByName( - String name, { - bool? remote, - bool? shared, - bool? owner, - }) => - _albumRepository.getByName( - name, - remote: remote, - shared: shared, - owner: owner, - ); + Future getAlbumByName(String name, {bool? remote, bool? shared, bool? owner}) => + _albumRepository.getByName(name, remote: remote, shared: shared, owner: owner); /// /// Add the uploaded asset to the selected albums /// - Future syncUploadAlbums( - List albumNames, - List assetIds, - ) async { + Future syncUploadAlbums(List albumNames, List assetIds) async { for (final albumName in albumNames) { Album? album = await getAlbumByName(albumName, remote: true, owner: true); album ??= await createAlbum(albumName, []); @@ -479,10 +403,7 @@ class AlbumService { return _albumRepository.watchAlbum(id); } - Future> search( - String searchTerm, - QuickFilterMode filterMode, - ) async { + Future> search(String searchTerm, QuickFilterMode filterMode) async { return _albumRepository.search(searchTerm, filterMode); } diff --git a/mobile/lib/services/api.service.dart b/mobile/lib/services/api.service.dart index b2e73c1b28..fca9080c86 100644 --- a/mobile/lib/services/api.service.dart +++ b/mobile/lib/services/api.service.dart @@ -127,11 +127,7 @@ class ApiService implements Authentication { } on SocketException catch (_) { return false; } catch (error, stackTrace) { - _log.severe( - "Error while checking server availability", - error, - stackTrace, - ); + _log.severe("Error while checking server availability", error, stackTrace); return false; } return true; @@ -145,10 +141,7 @@ class ApiService implements Authentication { headers.addAll(getRequestHeaders()); final res = await client - .get( - Uri.parse("$baseUrl/.well-known/immich"), - headers: headers, - ) + .get(Uri.parse("$baseUrl/.well-known/immich"), headers: headers) .timeout(const Duration(seconds: 5)); if (res.statusCode == 200) { @@ -211,10 +204,7 @@ class ApiService implements Authentication { } @override - Future applyToParams( - List queryParams, - Map headerParams, - ) { + Future applyToParams(List queryParams, Map headerParams) { return Future(() { var headers = ApiService.getRequestHeaders(); headerParams.addAll(headers); diff --git a/mobile/lib/services/app_settings.service.dart b/mobile/lib/services/app_settings.service.dart index cefc52385a..e705912aa5 100644 --- a/mobile/lib/services/app_settings.service.dart +++ b/mobile/lib/services/app_settings.service.dart @@ -5,26 +5,10 @@ import 'package:immich_mobile/entities/store.entity.dart'; enum AppSettingsEnum { loadPreview(StoreKey.loadPreview, "loadPreview", true), loadOriginal(StoreKey.loadOriginal, "loadOriginal", false), - themeMode( - StoreKey.themeMode, - "themeMode", - "system", - ), // "light","dark","system" - primaryColor( - StoreKey.primaryColor, - "primaryColor", - defaultColorPresetName, - ), - dynamicTheme( - StoreKey.dynamicTheme, - "dynamicTheme", - false, - ), - colorfulInterface( - StoreKey.colorfulInterface, - "colorfulInterface", - true, - ), + themeMode(StoreKey.themeMode, "themeMode", "system"), // "light","dark","system" + primaryColor(StoreKey.primaryColor, "primaryColor", defaultColorPresetName), + dynamicTheme(StoreKey.dynamicTheme, "dynamicTheme", false), + colorfulInterface(StoreKey.colorfulInterface, "colorfulInterface", true), tilesPerRow(StoreKey.tilesPerRow, "tilesPerRow", 4), dynamicLayout(StoreKey.dynamicLayout, "dynamicLayout", false), groupAssetsBy(StoreKey.groupAssetsBy, "groupBy", 0), @@ -33,43 +17,23 @@ enum AppSettingsEnum { "uploadErrorNotificationGracePeriod", 2, ), - backgroundBackupTotalProgress( - StoreKey.backgroundBackupTotalProgress, - "backgroundBackupTotalProgress", - true, - ), + backgroundBackupTotalProgress(StoreKey.backgroundBackupTotalProgress, "backgroundBackupTotalProgress", true), backgroundBackupSingleProgress( StoreKey.backgroundBackupSingleProgress, "backgroundBackupSingleProgress", false, ), storageIndicator(StoreKey.storageIndicator, "storageIndicator", true), - thumbnailCacheSize( - StoreKey.thumbnailCacheSize, - "thumbnailCacheSize", - 10000, - ), + thumbnailCacheSize(StoreKey.thumbnailCacheSize, "thumbnailCacheSize", 10000), imageCacheSize(StoreKey.imageCacheSize, "imageCacheSize", 350), - albumThumbnailCacheSize( - StoreKey.albumThumbnailCacheSize, - "albumThumbnailCacheSize", - 200, - ), - selectedAlbumSortOrder( - StoreKey.selectedAlbumSortOrder, - "selectedAlbumSortOrder", - 0, - ), + albumThumbnailCacheSize(StoreKey.albumThumbnailCacheSize, "albumThumbnailCacheSize", 200), + selectedAlbumSortOrder(StoreKey.selectedAlbumSortOrder, "selectedAlbumSortOrder", 0), advancedTroubleshooting(StoreKey.advancedTroubleshooting, null, false), manageLocalMediaAndroid(StoreKey.manageLocalMediaAndroid, null, false), logLevel(StoreKey.logLevel, null, 5), // Level.INFO = 5 preferRemoteImage(StoreKey.preferRemoteImage, null, false), loopVideo(StoreKey.loopVideo, "loopVideo", true), - loadOriginalVideo( - StoreKey.loadOriginalVideo, - "loadOriginalVideo", - false, - ), + loadOriginalVideo(StoreKey.loadOriginalVideo, "loadOriginalVideo", false), mapThemeMode(StoreKey.mapThemeMode, null, 0), mapShowFavoriteOnly(StoreKey.mapShowFavoriteOnly, null, false), mapIncludeArchived(StoreKey.mapIncludeArchived, null, false), @@ -77,22 +41,13 @@ enum AppSettingsEnum { mapRelativeDate(StoreKey.mapRelativeDate, null, 0), allowSelfSignedSSLCert(StoreKey.selfSignedCert, null, false), ignoreIcloudAssets(StoreKey.ignoreIcloudAssets, null, false), - selectedAlbumSortReverse( - StoreKey.selectedAlbumSortReverse, - null, - false, - ), + selectedAlbumSortReverse(StoreKey.selectedAlbumSortReverse, null, false), enableHapticFeedback(StoreKey.enableHapticFeedback, null, true), syncAlbums(StoreKey.syncAlbums, null, false), autoEndpointSwitching(StoreKey.autoEndpointSwitching, null, false), - photoManagerCustomFilter( - StoreKey.photoManagerCustomFilter, - null, - true, - ), + photoManagerCustomFilter(StoreKey.photoManagerCustomFilter, null, true), betaTimeline(StoreKey.betaTimeline, null, false), - enableBackup(StoreKey.enableBackup, null, false), - ; + enableBackup(StoreKey.enableBackup, null, false); const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue); diff --git a/mobile/lib/services/asset.service.dart b/mobile/lib/services/asset.service.dart index e9318dd0bf..ee61929c81 100644 --- a/mobile/lib/services/asset.service.dart +++ b/mobile/lib/services/asset.service.dart @@ -78,8 +78,9 @@ class AssetService { /// required. Returns `true` if there were any changes. Future refreshRemoteAssets() async { final syncedUserIds = await _etagRepository.getAllIds(); - final List syncedUsers = - syncedUserIds.isEmpty ? [] : (await _isarUserRepository.getByUserIds(syncedUserIds)).nonNulls.toList(); + final List syncedUsers = syncedUserIds.isEmpty + ? [] + : (await _isarUserRepository.getByUserIds(syncedUserIds)).nonNulls.toList(); final Stopwatch sw = Stopwatch()..start(); final bool changes = await _syncService.syncRemoteAssetsToDb( users: syncedUsers, @@ -95,10 +96,7 @@ class AssetService { List users, DateTime since, ) async { - final dto = AssetDeltaSyncDto( - updatedAfter: since, - userIds: users.map((e) => e.id).toList(), - ); + final dto = AssetDeltaSyncDto(updatedAfter: since, userIds: users.map((e) => e.id).toList()); final changes = await _apiService.syncApi.getDeltaSync(dto); return changes == null || changes.needsFullSync ? (null, null) @@ -107,19 +105,13 @@ class AssetService { /// Returns the list of people of the given asset id. // If the server is not reachable `null` is returned. - Future?> getRemotePeopleOfAsset( - String remoteId, - ) async { + Future?> getRemotePeopleOfAsset(String remoteId) async { try { final AssetResponseDto? dto = await _apiService.assetsApi.getAssetInfo(remoteId); return dto?.people; } catch (error, stack) { - log.severe( - 'Error while getting remote asset info: ${error.toString()}', - error, - stack, - ); + log.severe('Error while getting remote asset info: ${error.toString()}', error, stack); return null; } @@ -133,18 +125,11 @@ class AssetService { String? lastId; // will break on error or once all assets are loaded while (true) { - final dto = AssetFullSyncDto( - limit: chunkSize, - updatedUntil: until, - lastId: lastId, - userId: user.id, - ); + final dto = AssetFullSyncDto(limit: chunkSize, updatedUntil: until, lastId: lastId, userId: user.id); log.fine("Requesting $chunkSize assets from $lastId"); final List? assets = await _apiService.syncApi.getFullSyncForUser(dto); if (assets == null) return null; - log.fine( - "Received ${assets.length} assets from ${assets.firstOrNull?.id} to ${assets.lastOrNull?.id}", - ); + log.fine("Received ${assets.length} assets from ${assets.firstOrNull?.id} to ${assets.lastOrNull?.id}"); allAssets.addAll(assets.map(Asset.remote)); if (assets.length != chunkSize) break; lastId = assets.last.id; @@ -182,10 +167,7 @@ class AssetService { return a; } - Future updateAssets( - List assets, - UpdateAssetDto updateAssetDto, - ) async { + Future updateAssets(List assets, UpdateAssetDto updateAssetDto) async { return await _apiService.assetsApi.updateAssets( AssetBulkUpdateDto( ids: assets.map((e) => e.remoteId!).toList(), @@ -198,10 +180,7 @@ class AssetService { ); } - Future> changeFavoriteStatus( - List assets, - bool isFavorite, - ) async { + Future> changeFavoriteStatus(List assets, bool isFavorite) async { try { await updateAssets(assets, UpdateAssetDto(isFavorite: isFavorite)); @@ -218,16 +197,11 @@ class AssetService { } } - Future> changeArchiveStatus( - List assets, - bool isArchived, - ) async { + Future> changeArchiveStatus(List assets, bool isArchived) async { try { await updateAssets( assets, - UpdateAssetDto( - visibility: isArchived ? AssetVisibility.archive : AssetVisibility.timeline, - ), + UpdateAssetDto(visibility: isArchived ? AssetVisibility.archive : AssetVisibility.timeline), ); for (var element in assets) { @@ -244,15 +218,9 @@ class AssetService { } } - Future?> changeDateTime( - List assets, - String updatedDt, - ) async { + Future?> changeDateTime(List assets, String updatedDt) async { try { - await updateAssets( - assets, - UpdateAssetDto(dateTimeOriginal: updatedDt), - ); + await updateAssets(assets, UpdateAssetDto(dateTimeOriginal: updatedDt)); for (var element in assets) { element.fileCreatedAt = DateTime.parse(updatedDt); @@ -268,24 +236,12 @@ class AssetService { } } - Future?> changeLocation( - List assets, - LatLng location, - ) async { + Future?> changeLocation(List assets, LatLng location) async { try { - await updateAssets( - assets, - UpdateAssetDto( - latitude: location.latitude, - longitude: location.longitude, - ), - ); + await updateAssets(assets, UpdateAssetDto(latitude: location.latitude, longitude: location.longitude)); for (var element in assets) { - element.exifInfo = element.exifInfo?.copyWith( - latitude: location.latitude, - longitude: location.longitude, - ); + element.exifInfo = element.exifInfo?.copyWith(latitude: location.latitude, longitude: location.longitude); } await _syncService.upsertAssetsWithExif(assets); @@ -310,18 +266,13 @@ class AssetService { await refreshRemoteAssets(); final owner = _userService.getMyUser(); - final remoteAssets = await _assetRepository.getAll( - ownerId: owner.id, - state: AssetState.merged, - ); + final remoteAssets = await _assetRepository.getAll(ownerId: owner.id, state: AssetState.merged); /// Map Map> assetToAlbums = {}; for (BackupCandidate candidate in candidates) { - final asset = remoteAssets.firstWhereOrNull( - (a) => a.localId == candidate.asset.localId, - ); + final asset = remoteAssets.firstWhereOrNull((a) => a.localId == candidate.asset.localId); if (asset != null) { for (final albumName in candidate.albumNames) { @@ -342,10 +293,7 @@ class AssetService { } } - Future setDescription( - Asset asset, - String newDescription, - ) async { + Future setDescription(Asset asset, String newDescription) async { final remoteAssetId = asset.remoteId; final localExifId = asset.exifInfo?.assetId; @@ -354,10 +302,7 @@ class AssetService { return; } - final result = await _assetApiRepository.update( - remoteAssetId, - description: newDescription, - ); + final result = await _assetApiRepository.update(remoteAssetId, description: newDescription); final description = result.exifInfo?.description; @@ -437,10 +382,7 @@ class AssetService { } /// Delete assets from the server and unreference from the database - Future deleteRemoteAssets( - Iterable assets, { - bool shouldDeletePermanently = false, - }) async { + Future deleteRemoteAssets(Iterable assets, {bool shouldDeletePermanently = false}) async { final candidates = assets.where((a) => a.isRemote); if (candidates.isEmpty) { @@ -448,10 +390,7 @@ class AssetService { } await _apiService.assetsApi.deleteAssets( - AssetBulkDeleteDto( - ids: candidates.map((a) => a.remoteId!).toList(), - force: shouldDeletePermanently, - ), + AssetBulkDeleteDto(ids: candidates.map((a) => a.remoteId!).toList(), force: shouldDeletePermanently), ); /// Update asset info bassed on the deletion type. @@ -470,8 +409,10 @@ class AssetService { await _assetRepository.updateAll(payload.toList()); if (shouldDeletePermanently) { - final remoteAssetIds = - assets.where((asset) => asset.storage == AssetState.remote).map((asset) => asset.id).toList(); + final remoteAssetIds = assets + .where((asset) => asset.storage == AssetState.remote) + .map((asset) => asset.id) + .toList(); await _assetRepository.deleteByIds(remoteAssetIds); } }); @@ -479,10 +420,7 @@ class AssetService { /// Delete assets on both local file system and the server. /// Unreference from the database. - Future deleteAssets( - Iterable assets, { - bool shouldDeletePermanently = false, - }) async { + Future deleteAssets(Iterable assets, {bool shouldDeletePermanently = false}) async { final hasLocal = assets.any((asset) => asset.isLocal); final hasRemote = assets.any((asset) => asset.isRemote); @@ -491,10 +429,7 @@ class AssetService { } if (hasRemote) { - await deleteRemoteAssets( - assets, - shouldDeletePermanently: shouldDeletePermanently, - ); + await deleteRemoteAssets(assets, shouldDeletePermanently: shouldDeletePermanently); } } @@ -512,14 +447,8 @@ class AssetService { return _assetRepository.getMotionAssets(me.id); } - Future setVisibility( - List assets, - AssetVisibilityEnum visibility, - ) async { - await _assetApiRepository.updateVisibility( - assets.map((asset) => asset.remoteId!).toList(), - visibility, - ); + Future setVisibility(List assets, AssetVisibilityEnum visibility) async { + await _assetApiRepository.updateVisibility(assets.map((asset) => asset.remoteId!).toList(), visibility); final updatedAssets = assets.map((asset) { asset.visibility = visibility; diff --git a/mobile/lib/services/auth.service.dart b/mobile/lib/services/auth.service.dart index e8c4c5e97e..91c23cac1c 100644 --- a/mobile/lib/services/auth.service.dart +++ b/mobile/lib/services/auth.service.dart @@ -111,10 +111,7 @@ class AuthService { _log.severe("Error clearing local data", error, stackTrace); }); - await _appSettingsService.setSetting( - AppSettingsEnum.enableBackup, - false, - ); + await _appSettingsService.setSetting(AppSettingsEnum.enableBackup, false); } } diff --git a/mobile/lib/services/background.service.dart b/mobile/lib/services/background.service.dart index 5302df49ce..dea20b6d98 100644 --- a/mobile/lib/services/background.service.dart +++ b/mobile/lib/services/background.service.dart @@ -55,8 +55,10 @@ class BackgroundService { String _lastPrintedDetailContent = ""; String? _lastPrintedDetailTitle; late final ThrottleProgressUpdate _throttledNotifiy = ThrottleProgressUpdate(_updateProgress, notifyInterval); - late final ThrottleProgressUpdate _throttledDetailNotify = - ThrottleProgressUpdate(_updateDetailProgress, notifyInterval); + late final ThrottleProgressUpdate _throttledDetailNotify = ThrottleProgressUpdate( + _updateDetailProgress, + notifyInterval, + ); bool get isBackgroundInitialized { return _isBackgroundInitialized; @@ -87,15 +89,12 @@ class BackgroundService { int triggerMaxDelay = 50000, }) async { try { - final bool ok = await _foregroundChannel.invokeMethod( - 'configure', - [ - requireUnmetered, - requireCharging, - triggerUpdateDelay, - triggerMaxDelay, - ], - ); + final bool ok = await _foregroundChannel.invokeMethod('configure', [ + requireUnmetered, + requireCharging, + triggerUpdateDelay, + triggerMaxDelay, + ]); return ok; } catch (error) { return false; @@ -140,10 +139,7 @@ class BackgroundService { } Future?> digestFiles(List paths) { - return _foregroundChannel.invokeListMethod( - "digestFiles", - paths, - ); + return _foregroundChannel.invokeListMethod("digestFiles", paths); } /// Updates the notification shown by the background service @@ -158,10 +154,15 @@ class BackgroundService { }) async { try { if (_isBackgroundInitialized) { - return _backgroundChannel.invokeMethod( - 'updateNotification', - [title, content, progress, max, indeterminate, isDetail, onlyIfFG], - ); + return _backgroundChannel.invokeMethod('updateNotification', [ + title, + content, + progress, + max, + indeterminate, + isDetail, + onlyIfFG, + ]); } } catch (error) { debugPrint("[_updateNotification] failed to communicate with plugin"); @@ -170,11 +171,7 @@ class BackgroundService { } /// Shows a new priority notification - Future _showErrorNotification({ - required String title, - String? content, - String? individualTag, - }) async { + Future _showErrorNotification({required String title, String? content, String? individualTag}) async { try { if (_isBackgroundInitialized && _errorGracePeriodExceeded) { return await _backgroundChannel.invokeMethod('showError', [title, content, individualTag]); @@ -191,9 +188,7 @@ class BackgroundService { return await _backgroundChannel.invokeMethod('clearErrorNotifications'); } } catch (error) { - debugPrint( - "[_clearErrorNotifications] failed to communicate with plugin", - ); + debugPrint("[_clearErrorNotifications] failed to communicate with plugin"); } return false; } @@ -302,10 +297,7 @@ class BackgroundService { // indefinitely and can run later // Android is fine to wait here until the lock releases final waitForLock = Platform.isIOS - ? acquireLock().timeout( - const Duration(seconds: 5), - onTimeout: () => false, - ) + ? acquireLock().timeout(const Duration(seconds: 5), onTimeout: () => false) : acquireLock(); final bool hasAccess = await waitForLock; @@ -341,20 +333,13 @@ class BackgroundService { final db = await Bootstrap.initIsar(); await Bootstrap.initDomain(db); - final ref = ProviderContainer( - overrides: [ - dbProvider.overrideWithValue(db), - isarProvider.overrideWithValue(db), - ], - ); + final ref = ProviderContainer(overrides: [dbProvider.overrideWithValue(db), isarProvider.overrideWithValue(db)]); HttpSSLOptions.apply(); ref.read(apiServiceProvider).setAccessToken(Store.get(StoreKey.accessToken)); await ref.read(authServiceProvider).setOpenApiServiceEndpoint(); if (kDebugMode) { - debugPrint( - "[BG UPLOAD] Using endpoint: ${ref.read(apiServiceProvider).apiClient.basePath}", - ); + debugPrint("[BG UPLOAD] Using endpoint: ${ref.read(apiServiceProvider).apiClient.basePath}"); } final selectedAlbums = await ref.read(backupAlbumRepositoryProvider).getAllBySelection(BackupSelection.select); @@ -418,10 +403,7 @@ class BackgroundService { return false; } - Set toUpload = await backupService.buildUploadCandidates( - selectedAlbums, - excludedAlbums, - ); + Set toUpload = await backupService.buildUploadCandidates(selectedAlbums, excludedAlbums); try { toUpload = await backupService.removeAlreadyUploadedAssets(toUpload); @@ -444,12 +426,7 @@ class BackgroundService { _uploadedAssetsCount = 0; _updateNotification( title: "backup_background_service_in_progress_notification".tr(), - content: notifyTotalProgress - ? formatAssetBackupProgress( - _uploadedAssetsCount, - _assetsToUploadCount, - ) - : null, + content: notifyTotalProgress ? formatAssetBackupProgress(_uploadedAssetsCount, _assetsToUploadCount) : null, progress: 0, max: notifyTotalProgress ? _assetsToUploadCount : 0, indeterminate: !notifyTotalProgress, @@ -463,9 +440,7 @@ class BackgroundService { toUpload, _cancellationToken!, pmProgressHandler: pmProgressHandler, - onSuccess: (result) => _onAssetUploaded( - shouldNotify: notifyTotalProgress, - ), + onSuccess: (result) => _onAssetUploaded(shouldNotify: notifyTotalProgress), onProgress: (bytes, totalBytes) => _onProgress(bytes, totalBytes, shouldNotify: notifySingleProgress), onCurrentAsset: (asset) => _onSetCurrentBackupAsset(asset, shouldNotify: notifySingleProgress), onError: _onBackupError, @@ -482,9 +457,7 @@ class BackgroundService { return ok; } - void _onAssetUploaded({ - bool shouldNotify = false, - }) async { + void _onAssetUploaded({bool shouldNotify = false}) async { if (!shouldNotify) { return; } @@ -522,31 +495,27 @@ class BackgroundService { progress: _uploadedAssetsCount, max: _assetsToUploadCount, title: title, - content: formatAssetBackupProgress( - _uploadedAssetsCount, - _assetsToUploadCount, - ), + content: formatAssetBackupProgress(_uploadedAssetsCount, _assetsToUploadCount), ); } void _onBackupError(ErrorUploadAsset errorAssetInfo) { _showErrorNotification( - title: - "backup_background_service_upload_failure_notification".tr(namedArgs: {'filename': errorAssetInfo.fileName}), + title: "backup_background_service_upload_failure_notification".tr( + namedArgs: {'filename': errorAssetInfo.fileName}, + ), individualTag: errorAssetInfo.id, ); } - void _onSetCurrentBackupAsset( - CurrentUploadAsset currentUploadAsset, { - bool shouldNotify = false, - }) { + void _onSetCurrentBackupAsset(CurrentUploadAsset currentUploadAsset, {bool shouldNotify = false}) { if (!shouldNotify) { return; } - _throttledDetailNotify.title = "backup_background_service_current_upload_notification" - .tr(namedArgs: {'filename': currentUploadAsset.fileName}); + _throttledDetailNotify.title = "backup_background_service_current_upload_notification".tr( + namedArgs: {'filename': currentUploadAsset.fileName}, + ); _throttledDetailNotify.progress = 0; _throttledDetailNotify.total = 0; } diff --git a/mobile/lib/services/backup.service.dart b/mobile/lib/services/backup.service.dart index ea332dd1f9..3e29222b4c 100644 --- a/mobile/lib/services/backup.service.dart +++ b/mobile/lib/services/backup.service.dart @@ -74,9 +74,8 @@ class BackupService { } } - Future _saveDuplicatedAssetIds(List deviceAssetIds) => _assetRepository.transaction( - () => _assetRepository.upsertDuplicatedAssets(deviceAssetIds), - ); + Future _saveDuplicatedAssetIds(List deviceAssetIds) => + _assetRepository.transaction(() => _assetRepository.upsertDuplicatedAssets(deviceAssetIds)); /// Get duplicated asset id from database Future> getDuplicatedAssetIds() async { @@ -135,8 +134,8 @@ class BackupService { backupAlbum.id, modifiedFrom: useTimeFilter ? - // subtract 2 seconds to prevent missing assets due to rounding issues - backupAlbum.lastBackup.subtract(const Duration(seconds: 2)) + // subtract 2 seconds to prevent missing assets due to rounding issues + backupAlbum.lastBackup.subtract(const Duration(seconds: 2)) : null, modifiedUntil: useTimeFilter ? now : null, ); @@ -149,9 +148,7 @@ class BackupService { for (final asset in assets) { List albumNames = [localAlbum.name]; - final existingAsset = candidates.firstWhereOrNull( - (candidate) => candidate.asset.localId == asset.localId, - ); + final existingAsset = candidates.firstWhereOrNull((candidate) => candidate.asset.localId == asset.localId); if (existingAsset != null) { albumNames.addAll(existingAsset.albumNames); @@ -168,17 +165,13 @@ class BackupService { } /// Returns a new list of assets not yet uploaded - Future> removeAlreadyUploadedAssets( - Set candidates, - ) async { + Future> removeAlreadyUploadedAssets(Set candidates) async { if (candidates.isEmpty) { return candidates; } final Set duplicatedAssetIds = await getDuplicatedAssetIds(); - candidates.removeWhere( - (candidate) => duplicatedAssetIds.contains(candidate.asset.localId), - ); + candidates.removeWhere((candidate) => duplicatedAssetIds.contains(candidate.asset.localId)); if (candidates.isEmpty) { return candidates; @@ -188,10 +181,7 @@ class BackupService { try { final String deviceId = Store.get(StoreKey.deviceId); final CheckExistingAssetsResponseDto? duplicates = await _apiService.assetsApi.checkExistingAssets( - CheckExistingAssetsDto( - deviceAssetIds: candidates.map((c) => c.asset.localId!).toList(), - deviceId: deviceId, - ), + CheckExistingAssetsDto(deviceAssetIds: candidates.map((c) => c.asset.localId!).toList(), deviceId: deviceId), ); if (duplicates != null) { existing.addAll(duplicates.existingIds); @@ -215,8 +205,10 @@ class BackupService { if (Platform.isAndroid && !(await pm.Permission.accessMediaLocation.status).isGranted) { // double check that permission is granted here, to guard against // uploading corrupt assets without EXIF information - _log.warning("Media location permission is not granted. " - "Cannot access original assets for backup."); + _log.warning( + "Media location permission is not granted. " + "Cannot access original assets for backup.", + ); return false; } @@ -232,13 +224,11 @@ class BackupService { /// Upload images before video assets for background tasks /// these are further sorted by using their creation date List _sortPhotosFirst(List candidates) { - return candidates.sorted( - (a, b) { - final cmp = a.asset.type.index - b.asset.type.index; - if (cmp != 0) return cmp; - return a.asset.fileCreatedAt.compareTo(b.asset.fileCreatedAt); - }, - ); + return candidates.sorted((a, b) { + final cmp = a.asset.type.index - b.asset.type.index; + if (cmp != 0) return cmp; + return a.asset.fileCreatedAt.compareTo(b.asset.fileCreatedAt); + }); } Future backupAsset( @@ -295,10 +285,7 @@ class BackupService { file = await asset.local!.loadFile(progressHandler: pmProgressHandler); if (asset.local!.isLivePhoto) { - livePhotoFile = await asset.local!.loadFile( - withSubtype: true, - progressHandler: pmProgressHandler, - ); + livePhotoFile = await asset.local!.loadFile(withSubtype: true, progressHandler: pmProgressHandler); } } else { file = await asset.local!.originFile.timeout(const Duration(seconds: 5)); @@ -314,9 +301,7 @@ class BackupService { if (asset.local!.isLivePhoto) { if (livePhotoFile == null) { - _log.warning( - "Failed to obtain motion part of the livePhoto - $originalFileName", - ); + _log.warning("Failed to obtain motion part of the livePhoto - $originalFileName"); } } @@ -356,22 +341,14 @@ class BackupService { String? livePhotoVideoId; if (asset.local!.isLivePhoto && livePhotoFile != null) { - livePhotoVideoId = await uploadLivePhotoVideo( - originalFileName, - livePhotoFile, - baseRequest, - cancelToken, - ); + livePhotoVideoId = await uploadLivePhotoVideo(originalFileName, livePhotoFile, baseRequest, cancelToken); } if (livePhotoVideoId != null) { baseRequest.fields['livePhotoVideoId'] = livePhotoVideoId; } - final response = await httpClient.send( - baseRequest, - cancellationToken: cancelToken, - ); + final response = await httpClient.send(baseRequest, cancellationToken: cancelToken); final responseBody = jsonDecode(await response.stream.bytesToString()); @@ -417,10 +394,7 @@ class BackupService { ); if (shouldSyncAlbums) { - await _albumService.syncUploadAlbums( - candidate.albumNames, - [responseBody['id'] as String], - ); + await _albumService.syncUploadAlbums(candidate.albumNames, [responseBody['id'] as String]); } } } on http.CancelledException { @@ -459,10 +433,7 @@ class BackupService { if (livePhotoVideoFile == null) { return null; } - final livePhotoTitle = p.setExtension( - originalFileName, - p.extension(livePhotoVideoFile.path), - ); + final livePhotoTitle = p.setExtension(originalFileName, p.extension(livePhotoVideoFile.path)); final fileStream = livePhotoVideoFile.openRead(); final livePhotoRawUploadData = http.MultipartFile( "assetData", @@ -470,49 +441,36 @@ class BackupService { livePhotoVideoFile.lengthSync(), filename: livePhotoTitle, ); - final livePhotoReq = MultipartRequest( - baseRequest.method, - baseRequest.url, - onProgress: baseRequest.onProgress, - ) + final livePhotoReq = MultipartRequest(baseRequest.method, baseRequest.url, onProgress: baseRequest.onProgress) ..headers.addAll(baseRequest.headers) ..fields.addAll(baseRequest.fields); livePhotoReq.files.add(livePhotoRawUploadData); - var response = await httpClient.send( - livePhotoReq, - cancellationToken: cancelToken, - ); + var response = await httpClient.send(livePhotoReq, cancellationToken: cancelToken); var responseBody = jsonDecode(await response.stream.bytesToString()); if (![200, 201].contains(response.statusCode)) { var error = responseBody; - debugPrint( - "Error(${error['statusCode']}) uploading livePhoto for assetId | $livePhotoTitle | ${error['error']}", - ); + debugPrint("Error(${error['statusCode']}) uploading livePhoto for assetId | $livePhotoTitle | ${error['error']}"); } return responseBody.containsKey('id') ? responseBody['id'] : null; } String _getAssetType(AssetType assetType) => switch (assetType) { - AssetType.audio => "AUDIO", - AssetType.image => "IMAGE", - AssetType.video => "VIDEO", - AssetType.other => "OTHER", - }; + AssetType.audio => "AUDIO", + AssetType.image => "IMAGE", + AssetType.video => "VIDEO", + AssetType.other => "OTHER", + }; } class MultipartRequest extends http.MultipartRequest { /// Creates a new [MultipartRequest]. - MultipartRequest( - super.method, - super.url, { - required this.onProgress, - }); + MultipartRequest(super.method, super.url, {required this.onProgress}); final void Function(int bytes, int totalBytes) onProgress; diff --git a/mobile/lib/services/backup_verification.service.dart b/mobile/lib/services/backup_verification.service.dart index 2f61a125ea..c0f3c0205e 100644 --- a/mobile/lib/services/backup_verification.service.dart +++ b/mobile/lib/services/backup_verification.service.dart @@ -37,11 +37,7 @@ class BackupVerificationService { /// Returns at most [limit] assets that were backed up without exif Future> findWronglyBackedUpAssets({int limit = 100}) async { final owner = _userService.getMyUser().id; - final List onlyLocal = await _assetRepository.getAll( - ownerId: owner, - state: AssetState.local, - limit: limit, - ); + final List onlyLocal = await _assetRepository.getAll(ownerId: owner, state: AssetState.local, limit: limit); final List remoteMatches = await _assetRepository.getMatches( assets: onlyLocal, ownerId: owner, @@ -75,41 +71,32 @@ class BackupVerificationService { if (deleteCandidates.length > 10) { // performs 2 checks in parallel for a nice speedup final half = deleteCandidates.length ~/ 2; - final lower = compute( - _computeSaveToDelete, - ( - deleteCandidates: deleteCandidates.slice(0, half), - originals: originals.slice(0, half), - auth: Store.get(StoreKey.accessToken), - endpoint: Store.get(StoreKey.serverEndpoint), - rootIsolateToken: isolateToken, - fileMediaRepository: _fileMediaRepository, - ), - ); - final upper = compute( - _computeSaveToDelete, - ( - deleteCandidates: deleteCandidates.slice(half), - originals: originals.slice(half), - auth: Store.get(StoreKey.accessToken), - endpoint: Store.get(StoreKey.serverEndpoint), - rootIsolateToken: isolateToken, - fileMediaRepository: _fileMediaRepository, - ), - ); + final lower = compute(_computeSaveToDelete, ( + deleteCandidates: deleteCandidates.slice(0, half), + originals: originals.slice(0, half), + auth: Store.get(StoreKey.accessToken), + endpoint: Store.get(StoreKey.serverEndpoint), + rootIsolateToken: isolateToken, + fileMediaRepository: _fileMediaRepository, + )); + final upper = compute(_computeSaveToDelete, ( + deleteCandidates: deleteCandidates.slice(half), + originals: originals.slice(half), + auth: Store.get(StoreKey.accessToken), + endpoint: Store.get(StoreKey.serverEndpoint), + rootIsolateToken: isolateToken, + fileMediaRepository: _fileMediaRepository, + )); toDelete = await lower + await upper; } else { - toDelete = await compute( - _computeSaveToDelete, - ( - deleteCandidates: deleteCandidates, - originals: originals, - auth: Store.get(StoreKey.accessToken), - endpoint: Store.get(StoreKey.serverEndpoint), - rootIsolateToken: isolateToken, - fileMediaRepository: _fileMediaRepository, - ), - ); + toDelete = await compute(_computeSaveToDelete, ( + deleteCandidates: deleteCandidates, + originals: originals, + auth: Store.get(StoreKey.accessToken), + endpoint: Store.get(StoreKey.serverEndpoint), + rootIsolateToken: isolateToken, + fileMediaRepository: _fileMediaRepository, + )); } return toDelete; } @@ -122,7 +109,8 @@ class BackupVerificationService { String endpoint, RootIsolateToken rootIsolateToken, FileMediaRepository fileMediaRepository, - }) tuple, + }) + tuple, ) async { assert(tuple.deleteCandidates.length == tuple.originals.length); final List result = []; @@ -134,22 +122,14 @@ class BackupVerificationService { apiService.setEndpoint(tuple.endpoint); apiService.setAccessToken(tuple.auth); for (int i = 0; i < tuple.deleteCandidates.length; i++) { - if (await _compareAssets( - tuple.deleteCandidates[i], - tuple.originals[i], - apiService, - )) { + if (await _compareAssets(tuple.deleteCandidates[i], tuple.originals[i], apiService)) { result.add(tuple.deleteCandidates[i]); } } return result; } - static Future _compareAssets( - Asset remote, - Asset local, - ApiService apiService, - ) async { + static Future _compareAssets(Asset remote, Asset local, ApiService apiService) async { if (remote.checksum == local.checksum) return false; ExifInfo? exif = remote.exifInfo; if (exif != null && exif.latitude != null) return false; @@ -169,10 +149,7 @@ class BackupVerificationService { latLng.latitude != null && (remote.fileCreatedAt.isAtSameMomentAs(local.fileCreatedAt) || remote.fileModifiedAt.isAtSameMomentAs(local.fileModifiedAt) || - _sameExceptTimeZone( - remote.fileCreatedAt, - local.fileCreatedAt, - ))) { + _sameExceptTimeZone(remote.fileCreatedAt, local.fileCreatedAt))) { if (remote.type == AssetType.video) { // it's very unlikely that a video of same length, filesize, name // and date is wrong match. Cannot easily compare videos anyway diff --git a/mobile/lib/services/deep_link.service.dart b/mobile/lib/services/deep_link.service.dart index c08eacd0e3..1b717a6eeb 100644 --- a/mobile/lib/services/deep_link.service.dart +++ b/mobile/lib/services/deep_link.service.dart @@ -66,7 +66,6 @@ class DeepLinkService { return DeepLink([ // we need something to segue back to if the app was cold started // TODO: use MainTimelineRoute this when beta is default - if (isColdStart) (Store.isBetaTimelineEnabled) ? const MainTimelineRoute() : const PhotosRoute(), route, ]); @@ -96,10 +95,7 @@ class DeepLinkService { return _handleColdStart(deepLinkRoute, isColdStart); } - Future handleMyImmichApp( - PlatformDeepLink link, - bool isColdStart, - ) async { + Future handleMyImmichApp(PlatformDeepLink link, bool isColdStart) async { final path = link.uri.path; const uuidRegex = r'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}'; @@ -152,10 +148,7 @@ class DeepLinkService { return null; } - return AssetViewerRoute( - initialIndex: 0, - timelineService: _betaTimelineFactory.fromAssets([asset]), - ); + return AssetViewerRoute(initialIndex: 0, timelineService: _betaTimelineFactory.fromAssets([asset])); } else { // TODO: Remove this when beta is default final asset = await _assetService.getAssetByRemoteId(assetId); @@ -166,12 +159,7 @@ class DeepLinkService { _currentAsset.set(asset); final renderList = await RenderList.fromAssets([asset], GroupAssetsBy.auto); - return GalleryViewerRoute( - renderList: renderList, - initialIndex: 0, - heroOffset: 0, - showStack: true, - ); + return GalleryViewerRoute(renderList: renderList, initialIndex: 0, heroOffset: 0, showStack: true); } } diff --git a/mobile/lib/services/download.service.dart b/mobile/lib/services/download.service.dart index a540ca103f..7d2cf01b7c 100644 --- a/mobile/lib/services/download.service.dart +++ b/mobile/lib/services/download.service.dart @@ -14,10 +14,7 @@ import 'package:immich_mobile/services/api.service.dart'; import 'package:logging/logging.dart'; final downloadServiceProvider = Provider( - (ref) => DownloadService( - ref.watch(fileMediaRepositoryProvider), - ref.watch(downloadRepositoryProvider), - ), + (ref) => DownloadService(ref.watch(fileMediaRepositoryProvider), ref.watch(downloadRepositoryProvider)), ); class DownloadService { @@ -29,10 +26,7 @@ class DownloadService { void Function(TaskStatusUpdate)? onLivePhotoDownloadStatus; void Function(TaskProgressUpdate)? onTaskProgress; - DownloadService( - this._fileMediaRepository, - this._downloadRepository, - ) { + DownloadService(this._fileMediaRepository, this._downloadRepository) { _downloadRepository.onImageDownloadStatus = _onImageDownloadCallback; _downloadRepository.onVideoDownloadStatus = _onVideoDownloadCallback; _downloadRepository.onLivePhotoDownloadStatus = _onLivePhotoDownloadCallback; @@ -82,11 +76,7 @@ class DownloadService { final relativePath = Platform.isAndroid ? 'DCIM/Immich' : null; final file = File(filePath); try { - final Asset? resultAsset = await _fileMediaRepository.saveVideo( - file, - title: title, - relativePath: relativePath, - ); + final Asset? resultAsset = await _fileMediaRepository.saveVideo(file, title: title, relativePath: relativePath); return resultAsset != null; } catch (error, stack) { _log.severe("Error saving video", error, stack); @@ -98,10 +88,7 @@ class DownloadService { } } - Future saveLivePhotos( - Task task, - String livePhotosId, - ) async { + Future saveLivePhotos(Task task, String livePhotosId) async { final records = await _downloadRepository.getLiveVideoTasks(); if (records.length < 2) { return false; @@ -142,10 +129,7 @@ class DownloadService { await videoFile.delete(); } - await _downloadRepository.deleteRecordsWithIds([ - imageRecord.task.taskId, - videoRecord.task.taskId, - ]); + await _downloadRepository.deleteRecordsWithIds([imageRecord.task.taskId, videoRecord.task.taskId]); } } @@ -169,19 +153,13 @@ class DownloadService { asset.remoteId!, asset.fileName, group: kDownloadGroupLivePhoto, - metadata: LivePhotosMetadata( - part: LivePhotosPart.image, - id: asset.remoteId!, - ).toJson(), + metadata: LivePhotosMetadata(part: LivePhotosPart.image, id: asset.remoteId!).toJson(), ), _buildDownloadTask( asset.livePhotoVideoId!, asset.fileName.toUpperCase().replaceAll(RegExp(r"\.(JPG|HEIC)$"), '.MOV'), group: kDownloadGroupLivePhoto, - metadata: LivePhotosMetadata( - part: LivePhotosPart.video, - id: asset.remoteId!, - ).toJson(), + metadata: LivePhotosMetadata(part: LivePhotosPart.video, id: asset.remoteId!).toJson(), ), ]; } @@ -199,12 +177,7 @@ class DownloadService { ]; } - DownloadTask _buildDownloadTask( - String id, - String filename, { - String? group, - String? metadata, - }) { + DownloadTask _buildDownloadTask(String id, String filename, {String? group, String? metadata}) { final path = r'/assets/{id}/original'.replaceAll('{id}', id); final serverEndpoint = Store.get(StoreKey.serverEndpoint); final headers = ApiService.getRequestHeaders(); @@ -221,11 +194,7 @@ class DownloadService { } } -TaskRecord _findTaskRecord( - List records, - String livePhotosId, - LivePhotosPart part, -) { +TaskRecord _findTaskRecord(List records, String livePhotosId, LivePhotosPart part) { return records.firstWhere((record) { final metadata = LivePhotosMetadata.fromJson(record.task.metaData); return metadata.id == livePhotosId && metadata.part == part; diff --git a/mobile/lib/services/entity.service.dart b/mobile/lib/services/entity.service.dart index 468cc8f684..fe7358fce6 100644 --- a/mobile/lib/services/entity.service.dart +++ b/mobile/lib/services/entity.service.dart @@ -8,10 +8,7 @@ import 'package:immich_mobile/repositories/asset.repository.dart'; class EntityService { final AssetRepository _assetRepository; final IsarUserRepository _isarUserRepository; - const EntityService( - this._assetRepository, - this._isarUserRepository, - ); + const EntityService(this._assetRepository, this._isarUserRepository); Future fillAlbumWithDatabaseEntities(Album album) async { final ownerId = album.ownerId; @@ -43,8 +40,5 @@ class EntityService { } final entityServiceProvider = Provider( - (ref) => EntityService( - ref.watch(assetRepositoryProvider), - ref.watch(userRepositoryProvider), - ), + (ref) => EntityService(ref.watch(assetRepositoryProvider), ref.watch(userRepositoryProvider)), ); diff --git a/mobile/lib/services/folder.service.dart b/mobile/lib/services/folder.service.dart index 3f4936bb61..91fb455110 100644 --- a/mobile/lib/services/folder.service.dart +++ b/mobile/lib/services/folder.service.dart @@ -6,9 +6,7 @@ import 'package:immich_mobile/models/folder/root_folder.model.dart'; import 'package:immich_mobile/repositories/folder_api.repository.dart'; import 'package:logging/logging.dart'; -final folderServiceProvider = Provider( - (ref) => FolderService(ref.watch(folderApiRepositoryProvider)), -); +final folderServiceProvider = Provider((ref) => FolderService(ref.watch(folderApiRepositoryProvider))); class FolderService { final FolderApiRepository _folderApiRepository; @@ -44,11 +42,7 @@ class FolderService { if (!folderMap[parentPath]!.any((f) => f.name == segments[i])) { folderMap[parentPath]!.add( - RecursiveFolder( - path: parentPath == '_root_' ? '' : parentPath, - name: segments[i], - subfolders: [], - ), + RecursiveFolder(path: parentPath == '_root_' ? '' : parentPath, name: segments[i], subfolders: []), ); // Sort folders based on order parameter folderMap[parentPath]!.sort( @@ -64,9 +58,7 @@ class FolderService { if (folderMap.containsKey(fullPath)) { folder.subfolders.addAll(folderMap[fullPath]!); // Sort subfolders based on order parameter - folder.subfolders.sort( - (a, b) => order == SortOrder.desc ? b.name.compareTo(a.name) : a.name.compareTo(b.name), - ); + folder.subfolders.sort((a, b) => order == SortOrder.desc ? b.name.compareTo(a.name) : a.name.compareTo(b.name)); for (var subfolder in folder.subfolders) { attachSubfolders(subfolder); } @@ -75,24 +67,16 @@ class FolderService { List rootSubfolders = folderMap['_root_'] ?? []; // Sort root subfolders based on order parameter - rootSubfolders.sort( - (a, b) => order == SortOrder.desc ? b.name.compareTo(a.name) : a.name.compareTo(b.name), - ); + rootSubfolders.sort((a, b) => order == SortOrder.desc ? b.name.compareTo(a.name) : a.name.compareTo(b.name)); for (var folder in rootSubfolders) { attachSubfolders(folder); } - return RootFolder( - subfolders: rootSubfolders, - path: '/', - ); + return RootFolder(subfolders: rootSubfolders, path: '/'); } - Future> getFolderAssets( - RootFolder folder, - SortOrder order, - ) async { + Future> getFolderAssets(RootFolder folder, SortOrder order) async { try { if (folder is RecursiveFolder) { String fullPath = folder.path.isEmpty ? folder.name : '${folder.path}/${folder.name}'; @@ -110,11 +94,7 @@ class FolderService { final result = await _folderApiRepository.getAssetsForPath('/'); return result; } catch (e, stack) { - _log.severe( - "Failed to fetch assets for folder ${folder is RecursiveFolder ? folder.name : "root"}", - e, - stack, - ); + _log.severe("Failed to fetch assets for folder ${folder is RecursiveFolder ? folder.name : "root"}", e, stack); return []; } } diff --git a/mobile/lib/services/gcast.service.dart b/mobile/lib/services/gcast.service.dart index de9b8bbcb2..dcf7685237 100644 --- a/mobile/lib/services/gcast.service.dart +++ b/mobile/lib/services/gcast.service.dart @@ -41,11 +41,7 @@ class GCastService { void Function(CastState)? onCastState; - GCastService( - this._gCastRepository, - this._sessionsApiService, - this._assetApiRepository, - ) { + GCastService(this._gCastRepository, this._sessionsApiService, this._assetApiRepository) { _gCastRepository.onCastStatus = _onCastStatusCallback; _gCastRepository.onCastMessage = _onCastMessageCallback; } @@ -100,9 +96,7 @@ class GCastService { } if (status["media"] != null && status["media"]["duration"] != null) { - final duration = Duration( - milliseconds: (status["media"]["duration"] * 1000 ?? 0).toInt(), - ); + final duration = Duration(milliseconds: (status["media"]["duration"] * 1000 ?? 0).toInt()); onDuration?.call(duration); } @@ -170,13 +164,8 @@ class GCastService { } final unauthenticatedUrl = asset.isVideo - ? getPlaybackUrlForRemoteId( - asset.id, - ) - : getThumbnailUrlForRemoteId( - asset.id, - type: AssetMediaSize.fullsize, - ); + ? getPlaybackUrlForRemoteId(asset.id) + : getThumbnailUrlForRemoteId(asset.id, type: AssetMediaSize.fullsize); final authenticatedURL = "$unauthenticatedUrl&sessionKey=${sessionKey?.token}"; @@ -220,17 +209,11 @@ class GCastService { } void play() { - _gCastRepository.sendMessage(CastSession.kNamespaceMedia, { - "type": "PLAY", - "mediaSessionId": _sessionId, - }); + _gCastRepository.sendMessage(CastSession.kNamespaceMedia, {"type": "PLAY", "mediaSessionId": _sessionId}); } void pause() { - _gCastRepository.sendMessage(CastSession.kNamespaceMedia, { - "type": "PAUSE", - "mediaSessionId": _sessionId, - }); + _gCastRepository.sendMessage(CastSession.kNamespaceMedia, {"type": "PAUSE", "mediaSessionId": _sessionId}); } void seekTo(Duration position) { @@ -242,10 +225,7 @@ class GCastService { } void stop() { - _gCastRepository.sendMessage(CastSession.kNamespaceMedia, { - "type": "STOP", - "mediaSessionId": _sessionId, - }); + _gCastRepository.sendMessage(CastSession.kNamespaceMedia, {"type": "STOP", "mediaSessionId": _sessionId}); _mediaStatusPollingTimer?.cancel(); currentAssetId = null; @@ -258,14 +238,13 @@ class GCastService { final dests = await _gCastRepository.listDestinations(); return dests - .map( - (device) => (device.extras["fn"] ?? "Google Cast", CastDestinationType.googleCast, device), - ) + .map((device) => (device.extras["fn"] ?? "Google Cast", CastDestinationType.googleCast, device)) .where((device) { - final caString = device.$3.extras["ca"]; - final caNumber = int.tryParse(caString ?? "0") ?? 0; + final caString = device.$3.extras["ca"]; + final caNumber = int.tryParse(caString ?? "0") ?? 0; - return isDisplay(caNumber); - }).toList(growable: false); + return isDisplay(caNumber); + }) + .toList(growable: false); } } diff --git a/mobile/lib/services/hash.service.dart b/mobile/lib/services/hash.service.dart index f0554bf00b..48302be79c 100644 --- a/mobile/lib/services/hash.service.dart +++ b/mobile/lib/services/hash.service.dart @@ -17,8 +17,8 @@ class HashService { required BackgroundService backgroundService, this.batchSizeLimit = kBatchHashSizeLimit, this.batchFileLimit = kBatchHashFileLimit, - }) : _deviceAssetRepository = deviceAssetRepository, - _backgroundService = backgroundService; + }) : _deviceAssetRepository = deviceAssetRepository, + _backgroundService = backgroundService; final IsarDeviceAssetRepository _deviceAssetRepository; final BackgroundService _backgroundService; @@ -33,9 +33,7 @@ class HashService { assets.sort(Asset.compareByLocalId); // Get and sort DB entries - guaranteed to be a subset of assets - final hashesInDB = await _deviceAssetRepository.getByIds( - assets.map((a) => a.localId!).toList(), - ); + final hashesInDB = await _deviceAssetRepository.getByIds(assets.map((a) => a.localId!).toList()); hashesInDB.sort((a, b) => a.assetId.compareTo(b.assetId)); int dbIndex = 0; @@ -60,9 +58,7 @@ class HashService { matchingDbEntry.hash.isNotEmpty && matchingDbEntry.modifiedTime.isAtSameMomentAs(asset.fileModifiedAt)) { // Reuse the existing hash - hashedAssets.add( - asset.copyWith(checksum: base64.encode(matchingDbEntry.hash)), - ); + hashedAssets.add(asset.copyWith(checksum: base64.encode(matchingDbEntry.hash))); continue; } @@ -125,10 +121,7 @@ class HashService { /// Processes a batch of files and returns a list of successfully hashed assets after saving /// them in [DeviceAssetToHash] for future retrieval - Future> _processBatch( - List<_AssetPath> toBeHashed, - List toBeDeleted, - ) async { + Future> _processBatch(List<_AssetPath> toBeHashed, List toBeDeleted) async { _log.info("Hashing ${toBeHashed.length} files"); final hashes = await _hashFiles(toBeHashed.map((e) => e.path).toList()); assert( @@ -143,13 +136,7 @@ class HashService { final asset = toBeHashed.elementAtOrNull(index)?.asset; if (asset != null && hash?.length == 20) { hashedAssets.add(asset.copyWith(checksum: base64.encode(hash!))); - toBeAdded.add( - DeviceAsset( - assetId: asset.localId!, - hash: hash, - modifiedTime: asset.fileModifiedAt, - ), - ); + toBeAdded.add(DeviceAsset(assetId: asset.localId!, hash: hash, modifiedTime: asset.fileModifiedAt)); } else { _log.warning("Failed to hash file ${asset?.localId ?? ''}"); if (asset != null) { diff --git a/mobile/lib/services/local_auth.service.dart b/mobile/lib/services/local_auth.service.dart index 12da5f256b..4721911e8d 100644 --- a/mobile/lib/services/local_auth.service.dart +++ b/mobile/lib/services/local_auth.service.dart @@ -2,11 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/models/auth/biometric_status.model.dart'; import 'package:immich_mobile/repositories/biometric.repository.dart'; -final localAuthServiceProvider = Provider( - (ref) => LocalAuthService( - ref.watch(biometricRepositoryProvider), - ), -); +final localAuthServiceProvider = Provider((ref) => LocalAuthService(ref.watch(biometricRepositoryProvider))); class LocalAuthService { final BiometricRepository _biometricRepository; diff --git a/mobile/lib/services/local_files_manager.service.dart b/mobile/lib/services/local_files_manager.service.dart index ae935a131c..7cb3067342 100644 --- a/mobile/lib/services/local_files_manager.service.dart +++ b/mobile/lib/services/local_files_manager.service.dart @@ -2,9 +2,7 @@ import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:logging/logging.dart'; -final localFileManagerServiceProvider = Provider( - (ref) => const LocalFilesManagerService(), -); +final localFileManagerServiceProvider = Provider((ref) => const LocalFilesManagerService()); class LocalFilesManagerService { const LocalFilesManagerService(); @@ -22,10 +20,7 @@ class LocalFilesManagerService { Future restoreFromTrash(String fileName, int type) async { try { - return await _channel.invokeMethod( - 'restoreFromTrash', - {'fileName': fileName, 'type': type}, - ); + return await _channel.invokeMethod('restoreFromTrash', {'fileName': fileName, 'type': type}); } catch (e, s) { _logger.warning('Error restore file from trash', e, s); return false; diff --git a/mobile/lib/services/local_notification.service.dart b/mobile/lib/services/local_notification.service.dart index d67cc23616..e7fc3292e2 100644 --- a/mobile/lib/services/local_notification.service.dart +++ b/mobile/lib/services/local_notification.service.dart @@ -6,10 +6,7 @@ import 'package:immich_mobile/providers/notification_permission.provider.dart'; import 'package:permission_handler/permission_handler.dart'; final localNotificationService = Provider( - (ref) => LocalNotificationService( - ref.watch(notificationPermissionProvider), - ref, - ), + (ref) => LocalNotificationService(ref.watch(notificationPermissionProvider), ref), ); class LocalNotificationService { @@ -46,10 +43,7 @@ class LocalNotificationService { AndroidNotificationDetails androidNotificationDetails, DarwinNotificationDetails iosNotificationDetails, ) async { - final notificationDetails = NotificationDetails( - android: androidNotificationDetails, - iOS: iosNotificationDetails, - ); + final notificationDetails = NotificationDetails(android: androidNotificationDetails, iOS: iosNotificationDetails); if (_permissionStatus == PermissionStatus.granted) { await _localNotificationPlugin.show(id, title, body, notificationDetails); @@ -95,20 +89,12 @@ class LocalNotificationService { ongoing: true, actions: (showActions ?? false) ? [ - const AndroidNotificationAction( - cancelUploadActionID, - 'Cancel', - showsUserInterface: true, - ), + const AndroidNotificationAction(cancelUploadActionID, 'Cancel', showsUserInterface: true), ] : null, ) // Non-progress notification - : AndroidNotificationDetails( - androidChannelID, - androidChannelName, - playSound: false, - ); + : AndroidNotificationDetails(androidChannelID, androidChannelName, playSound: false); final iosNotificationDetails = DarwinNotificationDetails( presentBadge: true, @@ -116,18 +102,10 @@ class LocalNotificationService { presentBanner: presentBanner, ); - return _showOrUpdateNotification( - notificationlId, - title, - body, - androidNotificationDetails, - iosNotificationDetails, - ); + return _showOrUpdateNotification(notificationlId, title, body, androidNotificationDetails, iosNotificationDetails); } - void _onDidReceiveForegroundNotificationResponse( - NotificationResponse notificationResponse, - ) { + void _onDidReceiveForegroundNotificationResponse(NotificationResponse notificationResponse) { // Handle notification actions switch (notificationResponse.actionId) { case cancelUploadActionID: diff --git a/mobile/lib/services/memory.service.dart b/mobile/lib/services/memory.service.dart index ca877e7c78..e485bb0957 100644 --- a/mobile/lib/services/memory.service.dart +++ b/mobile/lib/services/memory.service.dart @@ -7,10 +7,7 @@ import 'package:immich_mobile/services/api.service.dart'; import 'package:logging/logging.dart'; final memoryServiceProvider = StateProvider((ref) { - return MemoryService( - ref.watch(apiServiceProvider), - ref.watch(assetRepositoryProvider), - ); + return MemoryService(ref.watch(apiServiceProvider), ref.watch(assetRepositoryProvider)); }); class MemoryService { @@ -38,17 +35,8 @@ class MemoryService { final dbAssets = await _assetRepository.getAllByRemoteId(memory.assets.map((e) => e.id)); final yearsAgo = now.year - memory.data.year; if (dbAssets.isNotEmpty) { - final String title = 'years_ago'.t( - args: { - 'years': yearsAgo.toString(), - }, - ); - memories.add( - Memory( - title: title, - assets: dbAssets, - ), - ); + final String title = 'years_ago'.t(args: {'years': yearsAgo.toString()}); + memories.add(Memory(title: title, assets: dbAssets)); } } @@ -72,16 +60,9 @@ class MemoryService { return null; } final yearsAgo = DateTime.now().year - memoryResponse.data.year; - final String title = 'years_ago'.t( - args: { - 'years': yearsAgo.toString(), - }, - ); + final String title = 'years_ago'.t(args: {'years': yearsAgo.toString()}); - return Memory( - title: title, - assets: dbAssets, - ); + return Memory(title: title, assets: dbAssets); } catch (error, stack) { log.severe("Cannot get memory with ID: $id", error, stack); return null; diff --git a/mobile/lib/services/network.service.dart b/mobile/lib/services/network.service.dart index de55da8d7c..8622400e7a 100644 --- a/mobile/lib/services/network.service.dart +++ b/mobile/lib/services/network.service.dart @@ -3,10 +3,7 @@ import 'package:immich_mobile/repositories/network.repository.dart'; import 'package:immich_mobile/repositories/permission.repository.dart'; final networkServiceProvider = Provider((ref) { - return NetworkService( - ref.watch(networkRepositoryProvider), - ref.watch(permissionRepositoryProvider), - ); + return NetworkService(ref.watch(networkRepositoryProvider), ref.watch(permissionRepositoryProvider)); }); class NetworkService { diff --git a/mobile/lib/services/oauth.service.dart b/mobile/lib/services/oauth.service.dart index 9a54a8d7c9..99ceca3229 100644 --- a/mobile/lib/services/oauth.service.dart +++ b/mobile/lib/services/oauth.service.dart @@ -11,24 +11,14 @@ class OAuthService { final log = Logger('OAuthService'); OAuthService(this._apiService); - Future getOAuthServerUrl( - String serverUrl, - String state, - String codeChallenge, - ) async { + Future getOAuthServerUrl(String serverUrl, String state, String codeChallenge) async { // Resolve API server endpoint from user provided serverUrl await _apiService.resolveAndSetEndpoint(serverUrl); final redirectUri = '$callbackUrlScheme:///oauth-callback'; - log.info( - "Starting OAuth flow with redirect URI: $redirectUri", - ); + log.info("Starting OAuth flow with redirect URI: $redirectUri"); final dto = await _apiService.oAuthApi.startOAuth( - OAuthConfigDto( - redirectUri: redirectUri, - state: state, - codeChallenge: codeChallenge, - ), + OAuthConfigDto(redirectUri: redirectUri, state: state, codeChallenge: codeChallenge), ); final authUrl = dto?.url; @@ -37,31 +27,17 @@ class OAuthService { return authUrl; } - Future oAuthLogin( - String oauthUrl, - String state, - String codeVerifier, - ) async { - String result = await FlutterWebAuth2.authenticate( - url: oauthUrl, - callbackUrlScheme: callbackUrlScheme, - ); + Future oAuthLogin(String oauthUrl, String state, String codeVerifier) async { + String result = await FlutterWebAuth2.authenticate(url: oauthUrl, callbackUrlScheme: callbackUrlScheme); log.info('Received OAuth callback: $result'); if (result.startsWith('app.immich:/oauth-callback')) { - result = result.replaceAll( - 'app.immich:/oauth-callback', - 'app.immich:///oauth-callback', - ); + result = result.replaceAll('app.immich:/oauth-callback', 'app.immich:///oauth-callback'); } return await _apiService.oAuthApi.finishOAuth( - OAuthCallbackDto( - url: result, - state: state, - codeVerifier: codeVerifier, - ), + OAuthCallbackDto(url: result, state: state, codeVerifier: codeVerifier), ); } } diff --git a/mobile/lib/services/partner.service.dart b/mobile/lib/services/partner.service.dart index 7b4f8a09b0..b8e5ae9a4d 100644 --- a/mobile/lib/services/partner.service.dart +++ b/mobile/lib/services/partner.service.dart @@ -20,11 +20,7 @@ class PartnerService { final IsarUserRepository _isarUserRepository; final Logger _log = Logger("PartnerService"); - PartnerService( - this._partnerApiRepository, - this._isarUserRepository, - this._partnerRepository, - ); + PartnerService(this._partnerApiRepository, this._isarUserRepository, this._partnerRepository); Future> getSharedWith() async { return _partnerRepository.getSharedWith(); @@ -64,15 +60,9 @@ class PartnerService { return false; } - Future updatePartner( - UserDto partner, { - required bool inTimeline, - }) async { + Future updatePartner(UserDto partner, {required bool inTimeline}) async { try { - final dto = await _partnerApiRepository.update( - partner.id, - inTimeline: inTimeline, - ); + final dto = await _partnerApiRepository.update(partner.id, inTimeline: inTimeline); await _isarUserRepository.update(partner.copyWith(inTimeline: dto.inTimeline)); return true; } catch (e) { diff --git a/mobile/lib/services/person.service.dart b/mobile/lib/services/person.service.dart index a2f7203d37..37b16a8d29 100644 --- a/mobile/lib/services/person.service.dart +++ b/mobile/lib/services/person.service.dart @@ -11,10 +11,10 @@ part 'person.service.g.dart'; @riverpod PersonService personService(Ref ref) => PersonService( - ref.watch(personApiRepositoryProvider), - ref.watch(assetApiRepositoryProvider), - ref.read(assetRepositoryProvider), - ); + ref.watch(personApiRepositoryProvider), + ref.watch(assetApiRepositoryProvider), + ref.read(assetRepositoryProvider), +); class PersonService { final Logger _log = Logger("PersonService"); @@ -22,11 +22,7 @@ class PersonService { final AssetApiRepository _assetApiRepository; final AssetRepository _assetRepository; - PersonService( - this._personApiRepository, - this._assetApiRepository, - this._assetRepository, - ); + PersonService(this._personApiRepository, this._assetApiRepository, this._assetRepository); Future> getAllPeople() async { try { diff --git a/mobile/lib/services/search.service.dart b/mobile/lib/services/search.service.dart index 2aa39fcf80..250fb67d82 100644 --- a/mobile/lib/services/search.service.dart +++ b/mobile/lib/services/search.service.dart @@ -25,11 +25,7 @@ class SearchService { final SearchApiRepository _searchApiRepository; final _log = Logger("SearchService"); - SearchService( - this._apiService, - this._assetRepository, - this._searchApiRepository, - ); + SearchService(this._apiService, this._assetRepository, this._searchApiRepository); Future?> getSearchSuggestions( SearchSuggestionType type, { diff --git a/mobile/lib/services/secure_storage.service.dart b/mobile/lib/services/secure_storage.service.dart index 38e6deb0d4..95d2e7a2c8 100644 --- a/mobile/lib/services/secure_storage.service.dart +++ b/mobile/lib/services/secure_storage.service.dart @@ -2,9 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/repositories/secure_storage.repository.dart'; final secureStorageServiceProvider = Provider( - (ref) => SecureStorageService( - ref.watch(secureStorageRepositoryProvider), - ), + (ref) => SecureStorageService(ref.watch(secureStorageRepositoryProvider)), ); class SecureStorageService { diff --git a/mobile/lib/services/server_info.service.dart b/mobile/lib/services/server_info.service.dart index 75ce68a73c..4319d9dbae 100644 --- a/mobile/lib/services/server_info.service.dart +++ b/mobile/lib/services/server_info.service.dart @@ -7,11 +7,7 @@ import 'package:immich_mobile/models/server_info/server_version.model.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/services/api.service.dart'; -final serverInfoServiceProvider = Provider( - (ref) => ServerInfoService( - ref.watch(apiServiceProvider), - ), -); +final serverInfoServiceProvider = Provider((ref) => ServerInfoService(ref.watch(apiServiceProvider))); class ServerInfoService { final ApiService _apiService; diff --git a/mobile/lib/services/share.service.dart b/mobile/lib/services/share.service.dart index fec8b76af0..7ba385d71c 100644 --- a/mobile/lib/services/share.service.dart +++ b/mobile/lib/services/share.service.dart @@ -39,10 +39,7 @@ class ShareService { final res = await _apiService.assetsApi.downloadAssetWithHttpInfo(asset.remoteId!); if (res.statusCode != 200) { - _log.severe( - "Asset download for ${asset.fileName} failed", - res.toLoggerString(), - ); + _log.severe("Asset download for ${asset.fileName} failed", res.toLoggerString()); continue; } @@ -57,18 +54,13 @@ class ShareService { } if (downloadedXFiles.length != assets.length) { - _log.warning( - "Partial share - Requested: ${assets.length}, Sharing: ${downloadedXFiles.length}", - ); + _log.warning("Partial share - Requested: ${assets.length}, Sharing: ${downloadedXFiles.length}"); } final size = MediaQuery.of(context).size; Share.shareXFiles( downloadedXFiles, - sharePositionOrigin: Rect.fromPoints( - Offset.zero, - Offset(size.width / 3, size.height), - ), + sharePositionOrigin: Rect.fromPoints(Offset.zero, Offset(size.width / 3, size.height)), ); return true; } catch (error) { diff --git a/mobile/lib/services/share_intent_service.dart b/mobile/lib/services/share_intent_service.dart index e514e5bbdc..fca5c4a188 100644 --- a/mobile/lib/services/share_intent_service.dart +++ b/mobile/lib/services/share_intent_service.dart @@ -2,19 +2,13 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart'; import 'package:immich_mobile/repositories/share_handler.repository.dart'; -final shareIntentServiceProvider = Provider( - (ref) => ShareIntentService( - ref.watch(shareHandlerRepositoryProvider), - ), -); +final shareIntentServiceProvider = Provider((ref) => ShareIntentService(ref.watch(shareHandlerRepositoryProvider))); class ShareIntentService { final ShareHandlerRepository shareHandlerRepository; void Function(List attachments)? onSharedMedia; - ShareIntentService( - this.shareHandlerRepository, - ); + ShareIntentService(this.shareHandlerRepository); void init() { shareHandlerRepository.onSharedMedia = onSharedMedia; diff --git a/mobile/lib/services/shared_link.service.dart b/mobile/lib/services/shared_link.service.dart index 44923b39d7..25151c234f 100644 --- a/mobile/lib/services/shared_link.service.dart +++ b/mobile/lib/services/shared_link.service.dart @@ -5,9 +5,7 @@ import 'package:immich_mobile/services/api.service.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; -final sharedLinkServiceProvider = Provider( - (ref) => SharedLinkService(ref.watch(apiServiceProvider)), -); +final sharedLinkServiceProvider = Provider((ref) => SharedLinkService(ref.watch(apiServiceProvider))); class SharedLinkService { final ApiService _apiService; diff --git a/mobile/lib/services/stack.service.dart b/mobile/lib/services/stack.service.dart index d46c0c0b99..c24b9fb7f8 100644 --- a/mobile/lib/services/stack.service.dart +++ b/mobile/lib/services/stack.service.dart @@ -23,24 +23,16 @@ class StackService { Future createStack(List assetIds) async { try { - return _api.stacksApi.createStack( - StackCreateDto(assetIds: assetIds), - ); + return _api.stacksApi.createStack(StackCreateDto(assetIds: assetIds)); } catch (error) { debugPrint("Error while creating stack: $error"); } return null; } - Future updateStack( - String stackId, - String primaryAssetId, - ) async { + Future updateStack(String stackId, String primaryAssetId) async { try { - return await _api.stacksApi.updateStack( - stackId, - StackUpdateDto(primaryAssetId: primaryAssetId), - ); + return await _api.stacksApi.updateStack(stackId, StackUpdateDto(primaryAssetId: primaryAssetId)); } catch (error) { debugPrint("Error while updating stack children: $error"); } @@ -68,8 +60,5 @@ class StackService { } final stackServiceProvider = Provider( - (ref) => StackService( - ref.watch(apiServiceProvider), - ref.watch(assetRepositoryProvider), - ), + (ref) => StackService(ref.watch(apiServiceProvider), ref.watch(assetRepositoryProvider)), ); diff --git a/mobile/lib/services/sync.service.dart b/mobile/lib/services/sync.service.dart index 723dc9c34b..7b420413cf 100644 --- a/mobile/lib/services/sync.service.dart +++ b/mobile/lib/services/sync.service.dart @@ -100,38 +100,26 @@ class SyncService { /// Returns `true` if there were any changes Future syncRemoteAssetsToDb({ required List users, - required Future<(List? toUpsert, List? toDelete)> Function( - List users, - DateTime since, - ) getChangedAssets, + required Future<(List? toUpsert, List? toDelete)> Function(List users, DateTime since) + getChangedAssets, required FutureOr?> Function(UserDto user, DateTime until) loadAssets, - }) => - _lock.run( - () async => - await _syncRemoteAssetChanges(users, getChangedAssets) ?? - await _syncRemoteAssetsFull(getUsersFromServer, loadAssets), - ); + }) => _lock.run( + () async => + await _syncRemoteAssetChanges(users, getChangedAssets) ?? + await _syncRemoteAssetsFull(getUsersFromServer, loadAssets), + ); /// Syncs remote albums to the database /// returns `true` if there were any changes - Future syncRemoteAlbumsToDb( - List remote, - ) => - _lock.run(() => _syncRemoteAlbumsToDb(remote)); + Future syncRemoteAlbumsToDb(List remote) => _lock.run(() => _syncRemoteAlbumsToDb(remote)); /// Syncs all device albums and their assets to the database /// Returns `true` if there were any changes - Future syncLocalAlbumAssetsToDb( - List onDevice, [ - Set? excludedAssets, - ]) => + Future syncLocalAlbumAssetsToDb(List onDevice, [Set? excludedAssets]) => _lock.run(() => _syncLocalAlbumAssetsToDb(onDevice, excludedAssets)); /// returns all Asset IDs that are not contained in the existing list - List sharedAssetsToRemove( - List deleteCandidates, - List existing, - ) { + List sharedAssetsToRemove(List deleteCandidates, List existing) { if (deleteCandidates.isEmpty) { return []; } @@ -200,10 +188,8 @@ class SyncService { /// Efficiently syncs assets via changes. Returns `null` when a full sync is required. Future _syncRemoteAssetChanges( List users, - Future<(List? toUpsert, List? toDelete)> Function( - List users, - DateTime since, - ) getChangedAssets, + Future<(List? toUpsert, List? toDelete)> Function(List users, DateTime since) + getChangedAssets, ) async { final currentUser = _userService.getMyUser(); final DateTime? since = (await _eTagRepository.get(currentUser.id))?.time?.toUtc(); @@ -237,9 +223,7 @@ class SyncService { final List localAssets = await _assetRepository.getAllLocal(); final List matchedAssets = localAssets.where((asset) => idsToDelete.contains(asset.remoteId)).toList(); - final mediaUrls = await Future.wait( - matchedAssets.map((asset) => asset.local?.getMediaUrl() ?? Future.value(null)), - ); + final mediaUrls = await Future.wait(matchedAssets.map((asset) => asset.local?.getMediaUrl() ?? Future.value(null))); await _localFilesManager.moveToTrash(mediaUrls.nonNulls.toList()); } @@ -247,18 +231,9 @@ class SyncService { /// Deletes remote-only assets, updates merged assets to be local-only Future handleRemoteAssetRemoval(List idsToDelete) async { return _assetRepository.transaction(() async { - await _assetRepository.deleteAllByRemoteId( - idsToDelete, - state: AssetState.remote, - ); - final merged = await _assetRepository.getAllByRemoteId( - idsToDelete, - state: AssetState.merged, - ); - if (Platform.isAndroid && - _appSettingsService.getSetting( - AppSettingsEnum.manageLocalMediaAndroid, - )) { + await _assetRepository.deleteAllByRemoteId(idsToDelete, state: AssetState.remote); + final merged = await _assetRepository.getAllByRemoteId(idsToDelete, state: AssetState.merged); + if (Platform.isAndroid && _appSettingsService.getSetting(AppSettingsEnum.manageLocalMediaAndroid)) { await _moveToTrashMatchedAssets(idsToDelete); } if (merged.isEmpty) return; @@ -304,10 +279,7 @@ class SyncService { if (remote == null) { return false; } - final List inDb = await _assetRepository.getAll( - ownerId: user.id, - sortBy: AssetSort.checksum, - ); + final List inDb = await _assetRepository.getAll(ownerId: user.id, sortBy: AssetSort.checksum); assert(inDb.isSorted(Asset.compareByChecksum), "inDb not sorted!"); remote.sort(Asset.compareByChecksum); @@ -343,15 +315,10 @@ class SyncService { /// Syncs remote albums to the database /// returns `true` if there were any changes - Future _syncRemoteAlbumsToDb( - List remoteAlbums, - ) async { + Future _syncRemoteAlbumsToDb(List remoteAlbums) async { remoteAlbums.sortBy((e) => e.remoteId!); - final List dbAlbums = await _albumRepository.getAll( - remote: true, - sortBy: AlbumSort.remoteId, - ); + final List dbAlbums = await _albumRepository.getAll(remote: true, sortBy: AlbumSort.remoteId); final List toDelete = []; final List existing = []; @@ -379,12 +346,7 @@ class SyncService { /// syncs albums from the server to the local database (does not support /// syncing changes from local back to server) /// accumulates - Future _syncRemoteAlbum( - Album dto, - Album album, - List deleteCandidates, - List existing, - ) async { + Future _syncRemoteAlbum(Album dto, Album album, List deleteCandidates, List existing) async { if (!_hasRemoteAlbumChanged(dto, album)) { return false; } @@ -393,18 +355,11 @@ class SyncService { final originalDto = dto; dto = await _albumApiRepository.get(dto.remoteId!); - final assetsInDb = await _assetRepository.getByAlbum( - album, - sortBy: AssetSort.ownerIdChecksum, - ); + final assetsInDb = await _assetRepository.getByAlbum(album, sortBy: AssetSort.ownerIdChecksum); assert(assetsInDb.isSorted(Asset.compareByOwnerChecksum), "inDb unsorted!"); final List assetsOnRemote = dto.remoteAssets.toList(); assetsOnRemote.sort(Asset.compareByOwnerChecksum); - final (toAdd, toUpdate, toUnlink) = _diffAssets( - assetsOnRemote, - assetsInDb, - compare: Asset.compareByOwnerChecksum, - ); + final (toAdd, toUpdate, toUnlink) = _diffAssets(assetsOnRemote, assetsInDb, compare: Asset.compareByOwnerChecksum); // update shared users final List sharedUsers = album.sharedUsers.map((u) => u.toDto()).toList(growable: false); @@ -476,10 +431,7 @@ class SyncService { /// Adds a remote album to the database while making sure to add any foreign /// (shared) assets to the database beforehand /// accumulates assets already existing in the database - Future _addAlbumFromServer( - Album album, - List existing, - ) async { + Future _addAlbumFromServer(Album album, List existing) async { if (album.remoteAssetCount != album.remoteAssets.length) { album = await _albumApiRepository.get(album.remoteId!); } @@ -493,23 +445,20 @@ class SyncService { await _entityService.fillAlbumWithDatabaseEntities(album); await _albumRepository.create(album); } else { - _log.warning("Failed to add album from server: assetCount ${album.remoteAssetCount} != " - "asset array length ${album.remoteAssets.length} for album ${album.name}"); + _log.warning( + "Failed to add album from server: assetCount ${album.remoteAssetCount} != " + "asset array length ${album.remoteAssets.length} for album ${album.name}", + ); } } /// Accumulates all suitable album assets to the `deleteCandidates` and /// removes the album from the database. - Future _removeAlbumFromDb( - Album album, - List deleteCandidates, - ) async { + Future _removeAlbumFromDb(Album album, List deleteCandidates) async { if (album.isLocal) { _log.info("Removing local album $album from DB"); // delete assets in DB unless they are remote or part of some other album - deleteCandidates.addAll( - await _assetRepository.getByAlbum(album, state: AssetState.local), - ); + deleteCandidates.addAll(await _assetRepository.getByAlbum(album, state: AssetState.local)); } else if (album.shared) { // delete assets in DB unless they belong to this user or are part of some other shared album or belong to a partner final userIds = (await _getAllAccessibleUsers()).map((user) => user.id); @@ -526,10 +475,7 @@ class SyncService { /// Syncs all device albums and their assets to the database /// Returns `true` if there were any changes - Future _syncLocalAlbumAssetsToDb( - List onDevice, [ - Set? excludedAssets, - ]) async { + Future _syncLocalAlbumAssetsToDb(List onDevice, [Set? excludedAssets]) async { onDevice.sort((a, b) => a.localId!.compareTo(b.localId!)); final inDb = await _albumRepository.getAll(remote: false, sortBy: AlbumSort.localId); final List deleteCandidates = []; @@ -538,31 +484,19 @@ class SyncService { onDevice, inDb, compare: (Album a, Album b) => a.localId!.compareTo(b.localId!), - both: (Album a, Album b) => _syncAlbumInDbAndOnDevice( - a, - b, - deleteCandidates, - existing, - excludedAssets, - ), + both: (Album a, Album b) => _syncAlbumInDbAndOnDevice(a, b, deleteCandidates, existing, excludedAssets), onlyFirst: (Album a) => _addAlbumFromDevice(a, existing, excludedAssets), onlySecond: (Album a) => _removeAlbumFromDb(a, deleteCandidates), ); - _log.fine( - "Syncing all local albums almost done. Collected ${deleteCandidates.length} asset candidates to delete", - ); + _log.fine("Syncing all local albums almost done. Collected ${deleteCandidates.length} asset candidates to delete"); final (toDelete, toUpdate) = _handleAssetRemoval(deleteCandidates, existing, remote: false); - _log.fine( - "${toDelete.length} assets to delete, ${toUpdate.length} to update", - ); + _log.fine("${toDelete.length} assets to delete, ${toUpdate.length} to update"); if (toDelete.isNotEmpty || toUpdate.isNotEmpty) { await _assetRepository.transaction(() async { await _assetRepository.deleteByIds(toDelete); await _assetRepository.updateAll(toUpdate); }); - _log.info( - "Removed ${toDelete.length} and updated ${toUpdate.length} local assets from DB", - ); + _log.info("Removed ${toDelete.length} and updated ${toUpdate.length} local assets from DB"); } return anyChanges; } @@ -580,9 +514,7 @@ class SyncService { ]) async { _log.info("Syncing a local album to DB: ${deviceAlbum.name}"); if (!forceRefresh && !await _hasAlbumChangeOnDevice(deviceAlbum, dbAlbum)) { - _log.info( - "Local album ${deviceAlbum.name} has not changed. Skipping sync.", - ); + _log.info("Local album ${deviceAlbum.name} has not changed. Skipping sync."); return false; } _log.info("Local album ${deviceAlbum.name} has changed. Syncing..."); @@ -599,10 +531,7 @@ class SyncService { assert(inDb.isSorted(Asset.compareByChecksum), "inDb not sorted!"); final int assetCountOnDevice = await _albumMediaRepository.getAssetCount(deviceAlbum.localId!); - final List onDevice = await _getHashedAssets( - deviceAlbum, - excludedAssets: excludedAssets, - ); + final List onDevice = await _getHashedAssets(deviceAlbum, excludedAssets: excludedAssets); _removeDuplicates(onDevice); // _removeDuplicates sorts `onDevice` by checksum final (toAdd, toUpdate, toDelete) = _diffAssets(onDevice, inDb); @@ -613,16 +542,9 @@ class SyncService { dbAlbum.description == deviceAlbum.description && dbAlbum.modifiedAt.isAtSameMomentAs(deviceAlbum.modifiedAt)) { // changes only affeted excluded albums - _log.info( - "Only excluded assets in local album ${deviceAlbum.name} changed. Stopping sync.", - ); + _log.info("Only excluded assets in local album ${deviceAlbum.name} changed. Stopping sync."); if (assetCountOnDevice != (await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount))?.assetCount) { - await _eTagRepository.upsertAll([ - ETag( - id: deviceAlbum.eTagKeyAssetCount, - assetCount: assetCountOnDevice, - ), - ]); + await _eTagRepository.upsertAll([ETag(id: deviceAlbum.eTagKeyAssetCount, assetCount: assetCountOnDevice)]); } return false; } @@ -648,12 +570,7 @@ class SyncService { await _albumRepository.removeAssets(dbAlbum, toDelete); await _albumRepository.recalculateMetadata(dbAlbum); await _albumRepository.update(dbAlbum); - await _eTagRepository.upsertAll([ - ETag( - id: deviceAlbum.eTagKeyAssetCount, - assetCount: assetCountOnDevice, - ), - ]); + await _eTagRepository.upsertAll([ETag(id: deviceAlbum.eTagKeyAssetCount, assetCount: assetCountOnDevice)]); }); _log.info("Synced changes of local album ${deviceAlbum.name} to DB"); } catch (e) { @@ -667,17 +584,13 @@ class SyncService { /// returns `true` if successful, else `false` Future _syncDeviceAlbumFast(Album deviceAlbum, Album dbAlbum) async { if (!deviceAlbum.modifiedAt.isAfter(dbAlbum.modifiedAt)) { - _log.info( - "Local album ${deviceAlbum.name} has not changed. Skipping sync.", - ); + _log.info("Local album ${deviceAlbum.name} has not changed. Skipping sync."); return false; } final int totalOnDevice = await _albumMediaRepository.getAssetCount(deviceAlbum.localId!); final int lastKnownTotal = (await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount))?.assetCount ?? 0; if (totalOnDevice <= lastKnownTotal) { - _log.info( - "Local album ${deviceAlbum.name} totalOnDevice is less than lastKnownTotal. Skipping sync.", - ); + _log.info("Local album ${deviceAlbum.name} totalOnDevice is less than lastKnownTotal. Skipping sync."); return false; } final List newAssets = await _getHashedAssets( @@ -701,16 +614,11 @@ class SyncService { await _albumRepository.addAssets(dbAlbum, existingInDb + updated); await _albumRepository.recalculateMetadata(dbAlbum); await _albumRepository.update(dbAlbum); - await _eTagRepository.upsertAll( - [ETag(id: deviceAlbum.eTagKeyAssetCount, assetCount: totalOnDevice)], - ); + await _eTagRepository.upsertAll([ETag(id: deviceAlbum.eTagKeyAssetCount, assetCount: totalOnDevice)]); }); _log.info("Fast synced local album ${deviceAlbum.name} to DB"); } catch (e) { - _log.severe( - "Failed to fast sync local album ${deviceAlbum.name} to DB", - e, - ); + _log.severe("Failed to fast sync local album ${deviceAlbum.name} to DB", e); return false; } @@ -719,21 +627,12 @@ class SyncService { /// Adds a new album from the device to the database and Accumulates all /// assets already existing in the database to the list of `existing` assets - Future _addAlbumFromDevice( - Album album, - List existing, [ - Set? excludedAssets, - ]) async { + Future _addAlbumFromDevice(Album album, List existing, [Set? excludedAssets]) async { _log.info("Adding a new local album to DB: ${album.name}"); - final assets = await _getHashedAssets( - album, - excludedAssets: excludedAssets, - ); + final assets = await _getHashedAssets(album, excludedAssets: excludedAssets); _removeDuplicates(assets); final (existingInDb, updated) = await _linkWithExistingFromDb(assets); - _log.info( - "${existingInDb.length} assets already existed in DB, to upsert ${updated.length}", - ); + _log.info("${existingInDb.length} assets already existed in DB, to upsert ${updated.length}"); await upsertAssetsWithExif(updated); existing.addAll(existingInDb); album.assets.addAll(existingInDb); @@ -743,9 +642,7 @@ class SyncService { try { await _albumRepository.create(album); final int assetCount = await _albumMediaRepository.getAssetCount(album.localId!); - await _eTagRepository.upsertAll([ - ETag(id: album.eTagKeyAssetCount, assetCount: assetCount), - ]); + await _eTagRepository.upsertAll([ETag(id: album.eTagKeyAssetCount, assetCount: assetCount)]); _log.info("Added a new local album to DB: ${album.name}"); } catch (e) { _log.severe("Failed to add new local album ${album.name} to DB", e); @@ -753,9 +650,7 @@ class SyncService { } /// Returns a tuple (existing, updated) - Future<(List existing, List updated)> _linkWithExistingFromDb( - List assets, - ) async { + Future<(List existing, List updated)> _linkWithExistingFromDb(List assets) async { if (assets.isEmpty) return ([].cast(), [].cast()); final List inDb = await _assetRepository.getAllByOwnerIdChecksum( @@ -789,17 +684,12 @@ class SyncService { if (asset.isTrashed) { final mediaUrl = await asset.local?.getMediaUrl(); if (mediaUrl == null) { - _log.warning( - "Failed to get media URL for asset ${asset.name} while moving to trash", - ); + _log.warning("Failed to get media URL for asset ${asset.name} while moving to trash"); continue; } trashMediaUrls.add(mediaUrl); } else { - await _localFilesManager.restoreFromTrash( - asset.fileName, - asset.type.index, - ); + await _localFilesManager.restoreFromTrash(asset.fileName, asset.type.index); } } @@ -812,10 +702,7 @@ class SyncService { Future upsertAssetsWithExif(List assets) async { if (assets.isEmpty) return; - if (Platform.isAndroid && - _appSettingsService.getSetting( - AppSettingsEnum.manageLocalMediaAndroid, - )) { + if (Platform.isAndroid && _appSettingsService.getSetting(AppSettingsEnum.manageLocalMediaAndroid)) { _toggleTrashStatusForAssets(assets); } @@ -842,21 +729,15 @@ class SyncService { final Asset? b = inDb[i]; if (b == null) { if (!a.isInDb) { - _log.warning( - "Trying to update an asset that does not exist in DB:\n$a", - ); + _log.warning("Trying to update an asset that does not exist in DB:\n$a"); } } else if (a.id != b.id) { - _log.warning( - "Trying to insert another asset with the same checksum+owner. In DB:\n$b\nTo insert:\n$a", - ); + _log.warning("Trying to insert another asset with the same checksum+owner. In DB:\n$b\nTo insert:\n$a"); } } for (int i = 1; i < assets.length; i++) { if (Asset.compareByOwnerChecksum(assets[i - 1], assets[i]) == 0) { - _log.warning( - "Trying to insert duplicate assets:\n${assets[i - 1]}\n${assets[i]}", - ); + _log.warning("Trying to insert duplicate assets:\n${assets[i - 1]}\n${assets[i]}"); } } } @@ -878,18 +759,16 @@ class SyncService { modifiedFrom: modifiedFrom, modifiedUntil: modifiedUntil, ); - final filtered = - excludedAssets == null ? entities : entities.where((e) => !excludedAssets.contains(e.localId!)).toList(); + final filtered = excludedAssets == null + ? entities + : entities.where((e) => !excludedAssets.contains(e.localId!)).toList(); return _hashService.hashAssets(filtered); } List _removeDuplicates(List assets) { final int before = assets.length; assets.sort(Asset.compareByOwnerChecksumCreatedModified); - assets.uniqueConsecutive( - compare: Asset.compareByOwnerChecksum, - onDuplicate: (a, b) => {}, - ); + assets.uniqueConsecutive(compare: Asset.compareByOwnerChecksum, onDuplicate: (a, b) => {}); final int duplicates = before - assets.length; if (duplicates > 0) { _log.warning("Ignored $duplicates duplicate assets on device"); @@ -898,10 +777,7 @@ class SyncService { } /// returns `true` if the albums differ on the surface - Future _hasAlbumChangeOnDevice( - Album deviceAlbum, - Album dbAlbum, - ) async { + Future _hasAlbumChangeOnDevice(Album deviceAlbum, Album dbAlbum) async { return deviceAlbum.name != dbAlbum.name || deviceAlbum.description != dbAlbum.description || !deviceAlbum.modifiedAt.isAtSameMomentAs(dbAlbum.modifiedAt) || @@ -966,9 +842,7 @@ class SyncService { sharedWith, compare: (UserDto a, UserDto b) => a.id.compareTo(b.id), both: (UserDto a, UserDto b) { - updatedSharedWith.add( - a.copyWith(inTimeline: b.inTimeline, isPartnerSharedWith: true), - ); + updatedSharedWith.add(a.copyWith(inTimeline: b.inTimeline, isPartnerSharedWith: true)); return true; }, onlyFirst: (UserDto a) => updatedSharedWith.add(a), @@ -1065,8 +939,5 @@ bool _hasRemoteAlbumChanged(Album remoteAlbum, Album dbAlbum) { !remoteAlbum.modifiedAt.isAtSameMomentAs(dbAlbum.modifiedAt) || !isAtSameMomentAs(remoteAlbum.startDate, dbAlbum.startDate) || !isAtSameMomentAs(remoteAlbum.endDate, dbAlbum.endDate) || - !isAtSameMomentAs( - remoteAlbum.lastModifiedAssetTimestamp, - dbAlbum.lastModifiedAssetTimestamp, - ); + !isAtSameMomentAs(remoteAlbum.lastModifiedAssetTimestamp, dbAlbum.lastModifiedAssetTimestamp); } diff --git a/mobile/lib/services/timeline.service.dart b/mobile/lib/services/timeline.service.dart index 1a4f9e2685..eaff1027d8 100644 --- a/mobile/lib/services/timeline.service.dart +++ b/mobile/lib/services/timeline.service.dart @@ -21,11 +21,7 @@ class TimelineService { final AppSettingsService _appSettingsService; final UserService _userService; - const TimelineService( - this._timelineRepository, - this._appSettingsService, - this._userService, - ); + const TimelineService(this._timelineRepository, this._appSettingsService, this._userService); Future> getTimelineUserIds() async { final me = _userService.getMyUser(); @@ -42,10 +38,7 @@ class TimelineService { } Stream watchMultiUsersTimeline(List userIds) { - return _timelineRepository.watchMultiUsersTimeline( - userIds, - _getGroupByOption(), - ); + return _timelineRepository.watchMultiUsersTimeline(userIds, _getGroupByOption()); } Stream watchArchiveTimeline() async* { @@ -61,10 +54,7 @@ class TimelineService { } Stream watchAlbumTimeline(Album album) async* { - yield* _timelineRepository.watchAlbumTimeline( - album, - _getGroupByOption(), - ); + yield* _timelineRepository.watchAlbumTimeline(album, _getGroupByOption()); } Stream watchTrashTimeline() async* { @@ -79,10 +69,7 @@ class TimelineService { return _timelineRepository.watchAllVideosTimeline(user.id); } - Future getTimelineFromAssets( - List assets, - GroupAssetsBy? groupBy, - ) { + Future getTimelineFromAssets(List assets, GroupAssetsBy? groupBy) { GroupAssetsBy groupOption = GroupAssetsBy.none; if (groupBy == null) { groupOption = _getGroupByOption(); @@ -90,10 +77,7 @@ class TimelineService { groupOption = groupBy; } - return _timelineRepository.getTimelineFromAssets( - assets, - groupOption, - ); + return _timelineRepository.getTimelineFromAssets(assets, groupOption); } Stream watchAssetSelectionTimeline() async* { @@ -109,9 +93,6 @@ class TimelineService { Stream watchLockedTimelineProvider() async* { final user = _userService.getMyUser(); - yield* _timelineRepository.watchLockedTimeline( - user.id, - _getGroupByOption(), - ); + yield* _timelineRepository.watchLockedTimeline(user.id, _getGroupByOption()); } } diff --git a/mobile/lib/services/trash.service.dart b/mobile/lib/services/trash.service.dart index 6cd7dfc641..2c51a68c59 100644 --- a/mobile/lib/services/trash.service.dart +++ b/mobile/lib/services/trash.service.dart @@ -20,17 +20,11 @@ class TrashService { final AssetRepository _assetRepository; final UserService _userService; - const TrashService( - this._apiService, - this._assetRepository, - this._userService, - ); + const TrashService(this._apiService, this._assetRepository, this._userService); Future restoreAssets(Iterable assetList) async { final remoteAssets = assetList.where((a) => a.isRemote); - await _apiService.trashApi.restoreAssets( - BulkIdsDto(ids: remoteAssets.map((e) => e.remoteId!).toList()), - ); + await _apiService.trashApi.restoreAssets(BulkIdsDto(ids: remoteAssets.map((e) => e.remoteId!).toList())); final updatedAssets = remoteAssets.map((asset) { asset.isTrashed = false; @@ -49,15 +43,9 @@ class TrashService { final ids = trashedAssets.map((e) => e.remoteId!).toList(); await _assetRepository.transaction(() async { - await _assetRepository.deleteAllByRemoteId( - ids, - state: AssetState.remote, - ); + await _assetRepository.deleteAllByRemoteId(ids, state: AssetState.remote); - final merged = await _assetRepository.getAllByRemoteId( - ids, - state: AssetState.merged, - ); + final merged = await _assetRepository.getAllByRemoteId(ids, state: AssetState.merged); if (merged.isEmpty) { return; } diff --git a/mobile/lib/services/upload.service.dart b/mobile/lib/services/upload.service.dart index e8acf791a2..dca5c02feb 100644 --- a/mobile/lib/services/upload.service.dart +++ b/mobile/lib/services/upload.service.dart @@ -32,12 +32,7 @@ final uploadServiceProvider = Provider((ref) { }); class UploadService { - UploadService( - this._uploadRepository, - this._backupRepository, - this._storageRepository, - this._localAssetRepository, - ) { + UploadService(this._uploadRepository, this._backupRepository, this._storageRepository, this._localAssetRepository) { _uploadRepository.onUploadStatus = _onUploadCallback; _uploadRepository.onTaskProgress = _onTaskProgressCallback; } @@ -114,10 +109,7 @@ class UploadService { /// Find backup candidates /// Build the upload tasks /// Enqueue the tasks - Future startBackup( - String userId, - void Function(EnqueueStatus status) onEnqueueTasks, - ) async { + Future startBackup(String userId, void Function(EnqueueStatus status) onEnqueueTasks) async { shouldAbortQueuingTasks = false; final candidates = await _backupRepository.getCandidates(userId); @@ -146,12 +138,7 @@ class UploadService { count += tasks.length; enqueueTasks(tasks); - onEnqueueTasks( - EnqueueStatus( - enqueueCount: count, - totalCount: candidates.length, - ), - ); + onEnqueueTasks(EnqueueStatus(enqueueCount: count, totalCount: candidates.length)); } } } @@ -205,10 +192,7 @@ class UploadService { return; } - final uploadTask = await _getLivePhotoUploadTask( - localAsset, - response['id'] as String, - ); + final uploadTask = await _getLivePhotoUploadTask(localAsset, response['id'] as String); if (uploadTask == null) { return; @@ -220,11 +204,7 @@ class UploadService { } } - Future _getUploadTask( - LocalAsset asset, { - String group = kBackupGroup, - int? priority, - }) async { + Future _getUploadTask(LocalAsset asset, {String group = kBackupGroup, int? priority}) async { final entity = await _storageRepository.getAssetEntityForAsset(asset); if (entity == null) { return null; @@ -252,12 +232,7 @@ class UploadService { return null; } - final originalFileName = entity.isLivePhoto - ? p.setExtension( - asset.name, - p.extension(file.path), - ) - : asset.name; + final originalFileName = entity.isLivePhoto ? p.setExtension(asset.name, p.extension(file.path)) : asset.name; String metadata = UploadTaskMetadata( localAssetId: asset.id, @@ -275,10 +250,7 @@ class UploadService { ); } - Future _getLivePhotoUploadTask( - LocalAsset asset, - String livePhotoVideoId, - ) async { + Future _getLivePhotoUploadTask(LocalAsset asset, String livePhotoVideoId) async { final entity = await _storageRepository.getAssetEntityForAsset(asset); if (entity == null) { return null; @@ -289,9 +261,7 @@ class UploadService { return null; } - final fields = { - 'livePhotoVideoId': livePhotoVideoId, - }; + final fields = {'livePhotoVideoId': livePhotoVideoId}; return buildUploadTask( file, @@ -357,17 +327,9 @@ class UploadTaskMetadata { final bool isLivePhotos; final String livePhotoVideoId; - const UploadTaskMetadata({ - required this.localAssetId, - required this.isLivePhotos, - required this.livePhotoVideoId, - }); + const UploadTaskMetadata({required this.localAssetId, required this.isLivePhotos, required this.livePhotoVideoId}); - UploadTaskMetadata copyWith({ - String? localAssetId, - bool? isLivePhotos, - String? livePhotoVideoId, - }) { + UploadTaskMetadata copyWith({String? localAssetId, bool? isLivePhotos, String? livePhotoVideoId}) { return UploadTaskMetadata( localAssetId: localAssetId ?? this.localAssetId, isLivePhotos: isLivePhotos ?? this.isLivePhotos, diff --git a/mobile/lib/services/widget.service.dart b/mobile/lib/services/widget.service.dart index fb2022784f..8c5d21b389 100644 --- a/mobile/lib/services/widget.service.dart +++ b/mobile/lib/services/widget.service.dart @@ -3,9 +3,7 @@ import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/repositories/widget.repository.dart'; final widgetServiceProvider = Provider((ref) { - return WidgetService( - ref.watch(widgetRepositoryProvider), - ); + return WidgetService(ref.watch(widgetRepositoryProvider)); }); class WidgetService { diff --git a/mobile/lib/theme/color_scheme.dart b/mobile/lib/theme/color_scheme.dart index c01b7cfa5a..f32b358ad9 100644 --- a/mobile/lib/theme/color_scheme.dart +++ b/mobile/lib/theme/color_scheme.dart @@ -6,10 +6,7 @@ final Map _themePresets = { ImmichColorPreset.indigo: ImmichTheme( light: ColorScheme.fromSeed( seedColor: immichBrandColorLight, - ).copyWith( - primary: immichBrandColorLight, - onSurface: const Color.fromARGB(255, 34, 31, 32), - ), + ).copyWith(primary: immichBrandColorLight, onSurface: const Color.fromARGB(255, 34, 31, 32)), dark: ColorScheme.fromSeed( seedColor: immichBrandColorDark, brightness: Brightness.dark, @@ -17,24 +14,15 @@ final Map _themePresets = { ), ImmichColorPreset.deepPurple: ImmichTheme( light: ColorScheme.fromSeed(seedColor: const Color(0xFF6F43C0)), - dark: ColorScheme.fromSeed( - seedColor: const Color(0xFFD3BBFF), - brightness: Brightness.dark, - ), + dark: ColorScheme.fromSeed(seedColor: const Color(0xFFD3BBFF), brightness: Brightness.dark), ), ImmichColorPreset.pink: ImmichTheme( light: ColorScheme.fromSeed(seedColor: const Color(0xFFED79B5)), - dark: ColorScheme.fromSeed( - seedColor: const Color(0xFFED79B5), - brightness: Brightness.dark, - ), + dark: ColorScheme.fromSeed(seedColor: const Color(0xFFED79B5), brightness: Brightness.dark), ), ImmichColorPreset.red: ImmichTheme( light: ColorScheme.fromSeed(seedColor: const Color(0xFFC51C16)), - dark: ColorScheme.fromSeed( - seedColor: const Color(0xFFD3302F), - brightness: Brightness.dark, - ), + dark: ColorScheme.fromSeed(seedColor: const Color(0xFFD3302F), brightness: Brightness.dark), ), ImmichColorPreset.orange: ImmichTheme( light: ColorScheme.fromSeed( @@ -49,37 +37,22 @@ final Map _themePresets = { ), ImmichColorPreset.yellow: ImmichTheme( light: ColorScheme.fromSeed(seedColor: const Color(0xFFFFB400)), - dark: ColorScheme.fromSeed( - seedColor: const Color(0xFFFFB400), - brightness: Brightness.dark, - ), + dark: ColorScheme.fromSeed(seedColor: const Color(0xFFFFB400), brightness: Brightness.dark), ), ImmichColorPreset.lime: ImmichTheme( light: ColorScheme.fromSeed(seedColor: const Color(0xFFCDDC39)), - dark: ColorScheme.fromSeed( - seedColor: const Color(0xFFCDDC39), - brightness: Brightness.dark, - ), + dark: ColorScheme.fromSeed(seedColor: const Color(0xFFCDDC39), brightness: Brightness.dark), ), ImmichColorPreset.green: ImmichTheme( light: ColorScheme.fromSeed(seedColor: const Color(0xFF18C249)), - dark: ColorScheme.fromSeed( - seedColor: const Color(0xFF18C249), - brightness: Brightness.dark, - ), + dark: ColorScheme.fromSeed(seedColor: const Color(0xFF18C249), brightness: Brightness.dark), ), ImmichColorPreset.cyan: ImmichTheme( light: ColorScheme.fromSeed(seedColor: const Color(0xFF00BCD4)), - dark: ColorScheme.fromSeed( - seedColor: const Color(0xFF00BCD4), - brightness: Brightness.dark, - ), + dark: ColorScheme.fromSeed(seedColor: const Color(0xFF00BCD4), brightness: Brightness.dark), ), ImmichColorPreset.slateGray: ImmichTheme( - light: ColorScheme.fromSeed( - seedColor: const Color(0xFF696969), - dynamicSchemeVariant: DynamicSchemeVariant.neutral, - ), + light: ColorScheme.fromSeed(seedColor: const Color(0xFF696969), dynamicSchemeVariant: DynamicSchemeVariant.neutral), dark: ColorScheme.fromSeed( seedColor: const Color(0xff696969), brightness: Brightness.dark, diff --git a/mobile/lib/theme/dynamic_theme.dart b/mobile/lib/theme/dynamic_theme.dart index 8ebf783469..99b949c9ac 100644 --- a/mobile/lib/theme/dynamic_theme.dart +++ b/mobile/lib/theme/dynamic_theme.dart @@ -18,14 +18,8 @@ abstract final class DynamicTheme { // Some palettes do not generate surface container colors accurately, // so we regenerate all colors using the primary color _theme = ImmichTheme( - light: ColorScheme.fromSeed( - seedColor: primaryColor, - brightness: Brightness.light, - ), - dark: ColorScheme.fromSeed( - seedColor: primaryColor, - brightness: Brightness.dark, - ), + light: ColorScheme.fromSeed(seedColor: primaryColor, brightness: Brightness.light), + dark: ColorScheme.fromSeed(seedColor: primaryColor, brightness: Brightness.dark), ); } } catch (error) { diff --git a/mobile/lib/theme/theme_data.dart b/mobile/lib/theme/theme_data.dart index 32695ef26e..8e3773839c 100644 --- a/mobile/lib/theme/theme_data.dart +++ b/mobile/lib/theme/theme_data.dart @@ -10,10 +10,7 @@ class ImmichTheme { const ImmichTheme({required this.light, required this.dark}); } -ThemeData getThemeData({ - required ColorScheme colorScheme, - required Locale locale, -}) { +ThemeData getThemeData({required ColorScheme colorScheme, required Locale locale}) { final isDark = colorScheme.brightness == Brightness.dark; return ThemeData( @@ -26,9 +23,7 @@ ThemeData getThemeData({ scaffoldBackgroundColor: colorScheme.surface, splashColor: colorScheme.primary.withValues(alpha: 0.1), highlightColor: colorScheme.primary.withValues(alpha: 0.1), - bottomSheetTheme: BottomSheetThemeData( - backgroundColor: colorScheme.surfaceContainer, - ), + bottomSheetTheme: BottomSheetThemeData(backgroundColor: colorScheme.surfaceContainer), fontFamily: _getFontFamilyFromLocale(locale), snackBarTheme: SnackBarThemeData( contentTextStyle: TextStyle( @@ -52,30 +47,12 @@ ThemeData getThemeData({ centerTitle: true, ), textTheme: const TextTheme( - displayLarge: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w600, - ), - displayMedium: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w600, - ), - displaySmall: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - ), - titleSmall: TextStyle( - fontSize: 16.0, - fontWeight: FontWeight.w600, - ), - titleMedium: TextStyle( - fontSize: 18.0, - fontWeight: FontWeight.w600, - ), - titleLarge: TextStyle( - fontSize: 26.0, - fontWeight: FontWeight.w600, - ), + displayLarge: TextStyle(fontSize: 18, fontWeight: FontWeight.w600), + displayMedium: TextStyle(fontSize: 14, fontWeight: FontWeight.w600), + displaySmall: TextStyle(fontSize: 12, fontWeight: FontWeight.w600), + titleSmall: TextStyle(fontSize: 16.0, fontWeight: FontWeight.w600), + titleMedium: TextStyle(fontSize: 18.0, fontWeight: FontWeight.w600), + titleLarge: TextStyle(fontSize: 26.0, fontWeight: FontWeight.w600), ), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( @@ -83,81 +60,43 @@ ThemeData getThemeData({ foregroundColor: isDark ? Colors.black87 : Colors.white, ), ), - chipTheme: const ChipThemeData( - side: BorderSide.none, - ), - sliderTheme: const SliderThemeData( - thumbShape: RoundSliderThumbShape(enabledThumbRadius: 7), - trackHeight: 2.0, - ), - bottomNavigationBarTheme: const BottomNavigationBarThemeData( - type: BottomNavigationBarType.fixed, - ), + chipTheme: const ChipThemeData(side: BorderSide.none), + sliderTheme: const SliderThemeData(thumbShape: RoundSliderThumbShape(enabledThumbRadius: 7), trackHeight: 2.0), + bottomNavigationBarTheme: const BottomNavigationBarThemeData(type: BottomNavigationBarType.fixed), popupMenuTheme: const PopupMenuThemeData( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10)), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), ), navigationBarTheme: NavigationBarThemeData( backgroundColor: isDark ? colorScheme.surfaceContainer : colorScheme.surface, - labelTextStyle: const WidgetStatePropertyAll( - TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - ), - ), + labelTextStyle: const WidgetStatePropertyAll(TextStyle(fontSize: 14, fontWeight: FontWeight.w500)), ), inputDecorationTheme: InputDecorationTheme( focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: colorScheme.primary, - ), + borderSide: BorderSide(color: colorScheme.primary), borderRadius: const BorderRadius.all(Radius.circular(15)), ), enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: colorScheme.outlineVariant, - ), + borderSide: BorderSide(color: colorScheme.outlineVariant), borderRadius: const BorderRadius.all(Radius.circular(15)), ), - labelStyle: TextStyle( - color: colorScheme.primary, - ), - hintStyle: const TextStyle( - fontSize: 14.0, - fontWeight: FontWeight.normal, - ), - ), - textSelectionTheme: TextSelectionThemeData( - cursorColor: colorScheme.primary, + labelStyle: TextStyle(color: colorScheme.primary), + hintStyle: const TextStyle(fontSize: 14.0, fontWeight: FontWeight.normal), ), + textSelectionTheme: TextSelectionThemeData(cursorColor: colorScheme.primary), dropdownMenuTheme: DropdownMenuThemeData( menuStyle: const MenuStyle( shape: WidgetStatePropertyAll( - RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(15)), - ), + RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15))), ), ), inputDecorationTheme: InputDecorationTheme( - focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: colorScheme.primary, - ), - ), + focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: colorScheme.primary)), enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: colorScheme.outlineVariant, - ), + borderSide: BorderSide(color: colorScheme.outlineVariant), borderRadius: const BorderRadius.all(Radius.circular(15)), ), - labelStyle: TextStyle( - color: colorScheme.primary, - ), - hintStyle: const TextStyle( - fontSize: 14.0, - fontWeight: FontWeight.normal, - ), + labelStyle: TextStyle(color: colorScheme.primary), + hintStyle: const TextStyle(fontSize: 14.0, fontWeight: FontWeight.normal), ), ), dialogTheme: DialogThemeData(backgroundColor: colorScheme.surfaceContainer), @@ -174,9 +113,7 @@ ThemeData getThemeData({ // This method replaces all surface shades in ImmichTheme to a static ones // as we are creating the colorscheme through seedColor the default surfaces are // tinted with primary color -ImmichTheme decolorizeSurfaces({ - required ImmichTheme theme, -}) { +ImmichTheme decolorizeSurfaces({required ImmichTheme theme}) { return ImmichTheme( light: theme.light.copyWith( surface: const Color(0xFFf9f9f9), diff --git a/mobile/lib/utils/backup_progress.dart b/mobile/lib/utils/backup_progress.dart index 3d2af2877e..36050f5e20 100644 --- a/mobile/lib/utils/backup_progress.dart +++ b/mobile/lib/utils/backup_progress.dart @@ -60,11 +60,7 @@ class ThrottleProgressUpdate { int progress = 0; int total = 0; - void call({ - final String? title, - final int progress = 0, - final int total = 0, - }) { + void call({final String? title, final int progress = 0, final int total = 0}) { final time = Timeline.now; this.title = title ?? this.title; this.progress = progress; diff --git a/mobile/lib/utils/bootstrap.dart b/mobile/lib/utils/bootstrap.dart index 26f3b49242..8c4ca077c4 100644 --- a/mobile/lib/utils/bootstrap.dart +++ b/mobile/lib/utils/bootstrap.dart @@ -48,10 +48,7 @@ abstract final class Bootstrap { ); } - static Future initDomain( - Isar db, { - bool shouldBufferLogs = true, - }) async { + static Future initDomain(Isar db, {bool shouldBufferLogs = true}) async { await StoreService.init(storeRepository: IsarStoreRepository(db)); await LogService.init( logRepository: IsarLogRepository(db), diff --git a/mobile/lib/utils/cache/custom_image_cache.dart b/mobile/lib/utils/cache/custom_image_cache.dart index 6465a68222..194c55f9df 100644 --- a/mobile/lib/utils/cache/custom_image_cache.dart +++ b/mobile/lib/utils/cache/custom_image_cache.dart @@ -39,7 +39,8 @@ final class CustomImageCache implements ImageCache { /// Gets the cache for the given key /// [_large] is used for [ImmichLocalImageProvider] and [ImmichRemoteImageProvider] /// [_small] is used for [ImmichLocalThumbnailProvider] and [ImmichRemoteThumbnailProvider] - ImageCache _cacheForKey(Object key) => (key is ImmichLocalImageProvider || + ImageCache _cacheForKey(Object key) => + (key is ImmichLocalImageProvider || key is ImmichRemoteImageProvider || key is LocalFullImageProvider || key is RemoteFullImageProvider) @@ -73,8 +74,7 @@ final class CustomImageCache implements ImageCache { Object key, ImageStreamCompleter Function() loader, { ImageErrorListener? onError, - }) => - _cacheForKey(key).putIfAbsent(key, loader, onError: onError); + }) => _cacheForKey(key).putIfAbsent(key, loader, onError: onError); @override ImageCacheStatus statusForKey(Object key) => _cacheForKey(key).statusForKey(key); diff --git a/mobile/lib/utils/color_filter_generator.dart b/mobile/lib/utils/color_filter_generator.dart index d4217a9319..92aed4b1a0 100644 --- a/mobile/lib/utils/color_filter_generator.dart +++ b/mobile/lib/utils/color_filter_generator.dart @@ -27,9 +27,7 @@ class BrightnessFilter extends StatelessWidget { @override Widget build(BuildContext context) { return ColorFiltered( - colorFilter: ColorFilter.matrix( - _ColorFilterGenerator.brightnessAdjustMatrix(brightness), - ), + colorFilter: ColorFilter.matrix(_ColorFilterGenerator.brightnessAdjustMatrix(brightness)), child: child, ); } @@ -44,9 +42,7 @@ class SaturationFilter extends StatelessWidget { @override Widget build(BuildContext context) { return ColorFiltered( - colorFilter: ColorFilter.matrix( - _ColorFilterGenerator.saturationAdjustMatrix(saturation), - ), + colorFilter: ColorFilter.matrix(_ColorFilterGenerator.saturationAdjustMatrix(saturation)), child: child, ); } diff --git a/mobile/lib/utils/debounce.dart b/mobile/lib/utils/debounce.dart index e8d3e6fb24..5451b569ca 100644 --- a/mobile/lib/utils/debounce.dart +++ b/mobile/lib/utils/debounce.dart @@ -68,21 +68,10 @@ Debouncer useDebouncer({ Duration interval = const Duration(milliseconds: 300), Duration? maxWaitTime, List? keys, -}) => - use( - _DebouncerHook( - interval: interval, - maxWaitTime: maxWaitTime, - keys: keys, - ), - ); +}) => use(_DebouncerHook(interval: interval, maxWaitTime: maxWaitTime, keys: keys)); class _DebouncerHook extends Hook { - const _DebouncerHook({ - required this.interval, - this.maxWaitTime, - super.keys, - }); + const _DebouncerHook({required this.interval, this.maxWaitTime, super.keys}); final Duration interval; final Duration? maxWaitTime; @@ -92,10 +81,7 @@ class _DebouncerHook extends Hook { } class _DebouncerHookState extends HookState { - late final debouncer = Debouncer( - interval: hook.interval, - maxWaitTime: hook.maxWaitTime, - ); + late final debouncer = Debouncer(interval: hook.interval, maxWaitTime: hook.maxWaitTime); @override Debouncer build(_) => debouncer; diff --git a/mobile/lib/utils/draggable_scroll_controller.dart b/mobile/lib/utils/draggable_scroll_controller.dart index cd7ae5b0e1..bab5214446 100644 --- a/mobile/lib/utils/draggable_scroll_controller.dart +++ b/mobile/lib/utils/draggable_scroll_controller.dart @@ -5,20 +5,12 @@ import 'package:flutter_hooks/flutter_hooks.dart'; /// /// See also: /// - [DraggableScrollableController] -DraggableScrollableController useDraggableScrollController({ - List? keys, -}) { - return use( - _DraggableScrollControllerHook( - keys: keys, - ), - ); +DraggableScrollableController useDraggableScrollController({List? keys}) { + return use(_DraggableScrollControllerHook(keys: keys)); } class _DraggableScrollControllerHook extends Hook { - const _DraggableScrollControllerHook({ - super.keys, - }); + const _DraggableScrollControllerHook({super.keys}); @override HookState> createState() => diff --git a/mobile/lib/utils/hooks/app_settings_update_hook.dart b/mobile/lib/utils/hooks/app_settings_update_hook.dart index a4968feeae..954e44229a 100644 --- a/mobile/lib/utils/hooks/app_settings_update_hook.dart +++ b/mobile/lib/utils/hooks/app_settings_update_hook.dart @@ -3,16 +3,11 @@ import 'package:flutter_hooks/flutter_hooks.dart' hide Store; import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -ValueNotifier useAppSettingsState( - AppSettingsEnum key, -) { +ValueNotifier useAppSettingsState(AppSettingsEnum key) { final notifier = useState(Store.get(key.storeKey, key.defaultValue)); // Listen to changes to the notifier and update app settings - useValueChanged( - notifier.value, - (_, __) => Store.put(key.storeKey, notifier.value), - ); + useValueChanged(notifier.value, (_, __) => Store.put(key.storeKey, notifier.value)); return notifier; } diff --git a/mobile/lib/utils/hooks/blurhash_hook.dart b/mobile/lib/utils/hooks/blurhash_hook.dart index 62208c4cf5..ac5fd31724 100644 --- a/mobile/lib/utils/hooks/blurhash_hook.dart +++ b/mobile/lib/utils/hooks/blurhash_hook.dart @@ -10,9 +10,7 @@ ObjectRef useBlurHashRef(Asset? asset) { return useRef(null); } - final rbga = thumbhash.thumbHashToRGBA( - base64Decode(asset!.thumbhash!), - ); + final rbga = thumbhash.thumbHashToRGBA(base64Decode(asset!.thumbhash!)); return useRef(thumbhash.rgbaToBmp(rbga)); } @@ -22,9 +20,7 @@ ObjectRef useDriftBlurHashRef(RemoteAsset? asset) { return useRef(null); } - final rbga = thumbhash.thumbHashToRGBA( - base64Decode(asset!.thumbHash!), - ); + final rbga = thumbhash.thumbHashToRGBA(base64Decode(asset!.thumbHash!)); return useRef(thumbhash.rgbaToBmp(rbga)); } diff --git a/mobile/lib/utils/hooks/crop_controller_hook.dart b/mobile/lib/utils/hooks/crop_controller_hook.dart index 04bc978754..663bca3dbf 100644 --- a/mobile/lib/utils/hooks/crop_controller_hook.dart +++ b/mobile/lib/utils/hooks/crop_controller_hook.dart @@ -4,9 +4,5 @@ import 'dart:ui'; // Import the dart:ui library for Rect /// A hook that provides a [CropController] instance. CropController useCropController() { - return useMemoized( - () => CropController( - defaultCrop: const Rect.fromLTRB(0, 0, 1, 1), - ), - ); + return useMemoized(() => CropController(defaultCrop: const Rect.fromLTRB(0, 0, 1, 1))); } diff --git a/mobile/lib/utils/hooks/interval_hook.dart b/mobile/lib/utils/hooks/interval_hook.dart index 0c346065f7..907fbad102 100644 --- a/mobile/lib/utils/hooks/interval_hook.dart +++ b/mobile/lib/utils/hooks/interval_hook.dart @@ -8,11 +8,8 @@ void useInterval(Duration delay, VoidCallback callback) { final savedCallback = useRef(callback); savedCallback.value = callback; - useEffect( - () { - final timer = Timer.periodic(delay, (_) => savedCallback.value()); - return timer.cancel; - }, - [delay], - ); + useEffect(() { + final timer = Timer.periodic(delay, (_) => savedCallback.value()); + return timer.cancel; + }, [delay]); } diff --git a/mobile/lib/utils/hooks/timer_hook.dart b/mobile/lib/utils/hooks/timer_hook.dart index 577e46f5d4..36b78d8631 100644 --- a/mobile/lib/utils/hooks/timer_hook.dart +++ b/mobile/lib/utils/hooks/timer_hook.dart @@ -2,26 +2,15 @@ import 'package:async/async.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; -RestartableTimer useTimer( - Duration duration, - void Function() callback, -) { - return use( - _TimerHook( - duration: duration, - callback: callback, - ), - ); +RestartableTimer useTimer(Duration duration, void Function() callback) { + return use(_TimerHook(duration: duration, callback: callback)); } class _TimerHook extends Hook { final Duration duration; final void Function() callback; - const _TimerHook({ - required this.duration, - required this.callback, - }); + const _TimerHook({required this.duration, required this.callback}); @override HookState> createState() => _TimerHookState(); } diff --git a/mobile/lib/utils/http_ssl_cert_override.dart b/mobile/lib/utils/http_ssl_cert_override.dart index f64757cf9d..a4c97a532f 100644 --- a/mobile/lib/utils/http_ssl_cert_override.dart +++ b/mobile/lib/utils/http_ssl_cert_override.dart @@ -10,11 +10,7 @@ class HttpSSLCertOverride extends HttpOverrides { final SSLClientCertStoreVal? _clientCert; late final SecurityContext? _ctxWithCert; - HttpSSLCertOverride( - this._allowSelfSignedSSLCert, - this._serverHost, - this._clientCert, - ) { + HttpSSLCertOverride(this._allowSelfSignedSSLCert, this._serverHost, this._clientCert) { if (_clientCert != null) { _ctxWithCert = SecurityContext(withTrustedRoots: true); if (_ctxWithCert != null) { diff --git a/mobile/lib/utils/http_ssl_options.dart b/mobile/lib/utils/http_ssl_options.dart index eaf6e77e4a..c4e2ad69f7 100644 --- a/mobile/lib/utils/http_ssl_options.dart +++ b/mobile/lib/utils/http_ssl_options.dart @@ -31,15 +31,12 @@ class HttpSSLOptions { HttpOverrides.global = HttpSSLCertOverride(allowSelfSignedSSLCert, serverHost, clientCert); if (applyNative && Platform.isAndroid) { - _channel.invokeMethod("apply", [ - allowSelfSignedSSLCert, - serverHost, - clientCert?.data, - clientCert?.password, - ]).onError((e, _) { - final log = Logger("HttpSSLOptions"); - log.severe('Failed to set SSL options', e.message); - }); + _channel + .invokeMethod("apply", [allowSelfSignedSSLCert, serverHost, clientCert?.data, clientCert?.password]) + .onError((e, _) { + final log = Logger("HttpSSLOptions"); + log.severe('Failed to set SSL options', e.message); + }); } } } diff --git a/mobile/lib/utils/image_url_builder.dart b/mobile/lib/utils/image_url_builder.dart index bde50f3a90..21722cb901 100644 --- a/mobile/lib/utils/image_url_builder.dart +++ b/mobile/lib/utils/image_url_builder.dart @@ -5,24 +5,15 @@ import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:openapi/api.dart'; -String getThumbnailUrl( - final Asset asset, { - AssetMediaSize type = AssetMediaSize.thumbnail, -}) { +String getThumbnailUrl(final Asset asset, {AssetMediaSize type = AssetMediaSize.thumbnail}) { return getThumbnailUrlForRemoteId(asset.remoteId!, type: type); } -String getThumbnailCacheKey( - final Asset asset, { - AssetMediaSize type = AssetMediaSize.thumbnail, -}) { +String getThumbnailCacheKey(final Asset asset, {AssetMediaSize type = AssetMediaSize.thumbnail}) { return getThumbnailCacheKeyForRemoteId(asset.remoteId!, type: type); } -String getThumbnailCacheKeyForRemoteId( - final String id, { - AssetMediaSize type = AssetMediaSize.thumbnail, -}) { +String getThumbnailCacheKeyForRemoteId(final String id, {AssetMediaSize type = AssetMediaSize.thumbnail}) { if (type == AssetMediaSize.thumbnail) { return 'thumbnail-image-$id'; } else { @@ -30,30 +21,18 @@ String getThumbnailCacheKeyForRemoteId( } } -String getAlbumThumbnailUrl( - final Album album, { - AssetMediaSize type = AssetMediaSize.thumbnail, -}) { +String getAlbumThumbnailUrl(final Album album, {AssetMediaSize type = AssetMediaSize.thumbnail}) { if (album.thumbnail.value?.remoteId == null) { return ''; } - return getThumbnailUrlForRemoteId( - album.thumbnail.value!.remoteId!, - type: type, - ); + return getThumbnailUrlForRemoteId(album.thumbnail.value!.remoteId!, type: type); } -String getAlbumThumbNailCacheKey( - final Album album, { - AssetMediaSize type = AssetMediaSize.thumbnail, -}) { +String getAlbumThumbNailCacheKey(final Album album, {AssetMediaSize type = AssetMediaSize.thumbnail}) { if (album.thumbnail.value?.remoteId == null) { return ''; } - return getThumbnailCacheKeyForRemoteId( - album.thumbnail.value!.remoteId!, - type: type, - ); + return getThumbnailCacheKeyForRemoteId(album.thumbnail.value!.remoteId!, type: type); } String getOriginalUrlForRemoteId(final String id) { @@ -66,10 +45,7 @@ String getImageCacheKey(final Asset asset) { return '${isFromDto ? asset.remoteId : asset.id}_fullStage'; } -String getThumbnailUrlForRemoteId( - final String id, { - AssetMediaSize type = AssetMediaSize.thumbnail, -}) { +String getThumbnailUrlForRemoteId(final String id, {AssetMediaSize type = AssetMediaSize.thumbnail}) { return '${Store.get(StoreKey.serverEndpoint)}/assets/$id/thumbnail?size=${type.value}'; } diff --git a/mobile/lib/utils/immich_loading_overlay.dart b/mobile/lib/utils/immich_loading_overlay.dart index b44f78e0bd..be49c3bae9 100644 --- a/mobile/lib/utils/immich_loading_overlay.dart +++ b/mobile/lib/utils/immich_loading_overlay.dart @@ -9,10 +9,7 @@ final _loadingEntry = OverlayEntry( child: DecoratedBox( decoration: BoxDecoration(color: context.colorScheme.surface.withAlpha(200)), child: const Center( - child: DelayedLoadingIndicator( - delay: Duration(seconds: 1), - fadeInDuration: Duration(milliseconds: 400), - ), + child: DelayedLoadingIndicator(delay: Duration(seconds: 1), fadeInDuration: Duration(milliseconds: 400)), ), ), ), diff --git a/mobile/lib/utils/isolate.dart b/mobile/lib/utils/isolate.dart index 7f8e8510d3..a57c3ebbd5 100644 --- a/mobile/lib/utils/isolate.dart +++ b/mobile/lib/utils/isolate.dart @@ -51,15 +51,9 @@ Cancelable runInIsolateGentle({ HttpSSLOptions.apply(applyNative: false); return await computation(ref); } on CanceledError { - log.warning( - "Computation cancelled ${debugLabel == null ? '' : ' for $debugLabel'}", - ); + log.warning("Computation cancelled ${debugLabel == null ? '' : ' for $debugLabel'}"); } catch (error, stack) { - log.severe( - "Error in runInIsolateGentle ${debugLabel == null ? '' : ' for $debugLabel'}", - error, - stack, - ); + log.severe("Error in runInIsolateGentle ${debugLabel == null ? '' : ' for $debugLabel'}", error, stack); } finally { try { await LogService.I.flushBuffer(); diff --git a/mobile/lib/utils/map_utils.dart b/mobile/lib/utils/map_utils.dart index 3dd849a044..80e20b7c6c 100644 --- a/mobile/lib/utils/map_utils.dart +++ b/mobile/lib/utils/map_utils.dart @@ -48,21 +48,18 @@ class MapUtils { ); static Map _addFeature(MapMarker marker) => { - 'type': 'Feature', - 'id': marker.assetRemoteId, - 'geometry': { - 'type': 'Point', - 'coordinates': [marker.latLng.longitude, marker.latLng.latitude], - }, - }; + 'type': 'Feature', + 'id': marker.assetRemoteId, + 'geometry': { + 'type': 'Point', + 'coordinates': [marker.latLng.longitude, marker.latLng.latitude], + }, + }; - static Map generateGeoJsonForMarkers( - List markers, - ) => - { - 'type': 'FeatureCollection', - 'features': markers.map(_addFeature).toList(), - }; + static Map generateGeoJsonForMarkers(List markers) => { + 'type': 'FeatureCollection', + 'features': markers.map(_addFeature).toList(), + }; static Future<(Position?, LocationPermission?)> checkPermAndGetLocation({ required BuildContext context, @@ -71,10 +68,7 @@ class MapUtils { try { bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); if (!serviceEnabled && !silent) { - showDialog( - context: context, - builder: (context) => _LocationServiceDisabledDialog(), - ); + showDialog(context: context, builder: (context) => _LocationServiceDisabledDialog()); return (null, LocationPermission.deniedForever); } @@ -116,24 +110,24 @@ class MapUtils { class _LocationServiceDisabledDialog extends ConfirmDialog { _LocationServiceDisabledDialog() - : super( - title: 'map_location_service_disabled_title'.tr(), - content: 'map_location_service_disabled_content'.tr(), - cancel: 'cancel'.tr(), - ok: 'yes'.tr(), - onOk: () async { - await Geolocator.openLocationSettings(); - }, - ); + : super( + title: 'map_location_service_disabled_title'.tr(), + content: 'map_location_service_disabled_content'.tr(), + cancel: 'cancel'.tr(), + ok: 'yes'.tr(), + onOk: () async { + await Geolocator.openLocationSettings(); + }, + ); } class _LocationPermissionDisabledDialog extends ConfirmDialog { _LocationPermissionDisabledDialog() - : super( - title: 'map_no_location_permission_title'.tr(), - content: 'map_no_location_permission_content'.tr(), - cancel: 'cancel'.tr(), - ok: 'yes'.tr(), - onOk: () {}, - ); + : super( + title: 'map_no_location_permission_title'.tr(), + content: 'map_no_location_permission_content'.tr(), + cancel: 'cancel'.tr(), + ok: 'yes'.tr(), + onOk: () {}, + ); } diff --git a/mobile/lib/utils/migration.dart b/mobile/lib/utils/migration.dart index 7b61e5521d..609b3cfca8 100644 --- a/mobile/lib/utils/migration.dart +++ b/mobile/lib/utils/migration.dart @@ -85,16 +85,14 @@ Future _migrateTo(Isar db, int version) async { Future _migrateDeviceAsset(Isar db) async { final ids = Platform.isAndroid ? (await db.androidDeviceAssets.where().findAll()) - .map((a) => _DeviceAsset(assetId: a.id.toString(), hash: a.hash)) - .toList() + .map((a) => _DeviceAsset(assetId: a.id.toString(), hash: a.hash)) + .toList() : (await db.iOSDeviceAssets.where().findAll()).map((i) => _DeviceAsset(assetId: i.id, hash: i.hash)).toList(); final PermissionState ps = await PhotoManager.requestPermissionExtend(); if (!ps.hasAccess) { if (kDebugMode) { - debugPrint( - "[MIGRATION] Photo library permission not granted. Skipping device asset migration.", - ); + debugPrint("[MIGRATION] Photo library permission not granted. Skipping device asset migration."); } return; @@ -105,9 +103,7 @@ Future _migrateDeviceAsset(Isar db) async { if (paths.isEmpty) { localAssets = (await db.assets.where().anyOf(ids, (query, id) => query.localIdEqualTo(id.assetId)).findAll()) - .map( - (a) => _DeviceAsset(assetId: a.localId!, dateTime: a.fileModifiedAt), - ) + .map((a) => _DeviceAsset(assetId: a.localId!, dateTime: a.fileModifiedAt)) .toList(); } else { final AssetPathEntity albumWithAll = paths.first; @@ -129,34 +125,24 @@ Future _migrateDeviceAsset(Isar db) async { compare: (a, b) => a.assetId.compareTo(b.assetId), both: (deviceAsset, asset) { toAdd.add( - DeviceAssetEntity( - assetId: deviceAsset.assetId, - hash: deviceAsset.hash!, - modifiedTime: asset.dateTime!, - ), + DeviceAssetEntity(assetId: deviceAsset.assetId, hash: deviceAsset.hash!, modifiedTime: asset.dateTime!), ); return false; }, onlyFirst: (deviceAsset) { if (kDebugMode) { - debugPrint( - '[MIGRATION] Local asset not found in DeviceAsset: ${deviceAsset.assetId}', - ); + debugPrint('[MIGRATION] Local asset not found in DeviceAsset: ${deviceAsset.assetId}'); } }, onlySecond: (asset) { if (kDebugMode) { - debugPrint( - '[MIGRATION] Local asset not found in DeviceAsset: ${asset.assetId}', - ); + debugPrint('[MIGRATION] Local asset not found in DeviceAsset: ${asset.assetId}'); } }, ); if (kDebugMode) { - debugPrint( - "[MIGRATION] Total number of device assets migrated - ${toAdd.length}", - ); + debugPrint("[MIGRATION] Total number of device assets migrated - ${toAdd.length}"); } await db.writeTxn(() async { @@ -171,24 +157,17 @@ Future migrateDeviceAssetToSqlite(Isar db, Drift drift) async { for (final deviceAsset in isarDeviceAssets) { batch.update( drift.localAssetEntity, - LocalAssetEntityCompanion( - checksum: Value(base64.encode(deviceAsset.hash)), - ), + LocalAssetEntityCompanion(checksum: Value(base64.encode(deviceAsset.hash))), where: (t) => t.id.equals(deviceAsset.assetId), ); } }); } catch (error) { - debugPrint( - "[MIGRATION] Error while migrating device assets to SQLite: $error", - ); + debugPrint("[MIGRATION] Error while migrating device assets to SQLite: $error"); } } -Future migrateBackupAlbumsToSqlite( - Isar db, - Drift drift, -) async { +Future migrateBackupAlbumsToSqlite(Isar db, Drift drift) async { try { final isarBackupAlbums = await db.backupAlbums.where().findAll(); // Recents is a virtual album on Android, and we don't have it with the new sync @@ -197,23 +176,17 @@ Future migrateBackupAlbumsToSqlite( final recentAlbum = isarBackupAlbums.firstWhereOrNull((album) => album.id == 'isAll'); if (recentAlbum != null) { await drift.localAlbumEntity.update().write( - const LocalAlbumEntityCompanion( - backupSelection: Value(BackupSelection.selected), - ), - ); + const LocalAlbumEntityCompanion(backupSelection: Value(BackupSelection.selected)), + ); final excluded = isarBackupAlbums - .where( - (album) => album.selection == isar_backup_album.BackupSelection.exclude, - ) + .where((album) => album.selection == isar_backup_album.BackupSelection.exclude) .map((album) => album.id) .toList(); await drift.batch((batch) async { for (final id in excluded) { batch.update( drift.localAlbumEntity, - const LocalAlbumEntityCompanion( - backupSelection: Value(BackupSelection.excluded), - ), + const LocalAlbumEntityCompanion(backupSelection: Value(BackupSelection.excluded)), where: (t) => t.id.equals(id), ); } @@ -227,22 +200,18 @@ Future migrateBackupAlbumsToSqlite( batch.update( drift.localAlbumEntity, LocalAlbumEntityCompanion( - backupSelection: Value( - switch (album.selection) { - isar_backup_album.BackupSelection.none => BackupSelection.none, - isar_backup_album.BackupSelection.select => BackupSelection.selected, - isar_backup_album.BackupSelection.exclude => BackupSelection.excluded, - }, - ), + backupSelection: Value(switch (album.selection) { + isar_backup_album.BackupSelection.none => BackupSelection.none, + isar_backup_album.BackupSelection.select => BackupSelection.selected, + isar_backup_album.BackupSelection.exclude => BackupSelection.excluded, + }), ), where: (t) => t.id.equals(album.id), ); } }); } catch (error) { - debugPrint( - "[MIGRATION] Error while migrating backup albums to SQLite: $error", - ); + debugPrint("[MIGRATION] Error while migrating backup albums to SQLite: $error"); } } @@ -259,12 +228,10 @@ Future runNewSync(WidgetRef ref, {bool full = false}) async { final backgroundManager = ref.read(backgroundSyncProvider); Future.wait([ - backgroundManager.syncLocal(full: full).then( - (_) { - Logger("runNewSync").fine("Hashing assets after syncLocal"); - backgroundManager.hashAssets(); - }, - ), + backgroundManager.syncLocal(full: full).then((_) { + Logger("runNewSync").fine("Hashing assets after syncLocal"); + backgroundManager.hashAssets(); + }), backgroundManager.syncRemote(), ]); } diff --git a/mobile/lib/utils/openapi_patching.dart b/mobile/lib/utils/openapi_patching.dart index 8e14d232f8..efbcf1c139 100644 --- a/mobile/lib/utils/openapi_patching.dart +++ b/mobile/lib/utils/openapi_patching.dart @@ -17,16 +17,8 @@ dynamic upgradeDto(dynamic value, String targetType) { break; case 'ServerConfigDto': if (value is Map) { - addDefault( - value, - 'mapLightStyleUrl', - 'https://tiles.immich.cloud/v1/style/light.json', - ); - addDefault( - value, - 'mapDarkStyleUrl', - 'https://tiles.immich.cloud/v1/style/dark.json', - ); + addDefault(value, 'mapLightStyleUrl', 'https://tiles.immich.cloud/v1/style/light.json'); + addDefault(value, 'mapDarkStyleUrl', 'https://tiles.immich.cloud/v1/style/dark.json'); } case 'UserResponseDto': if (value is Map) { diff --git a/mobile/lib/utils/remote_album.utils.dart b/mobile/lib/utils/remote_album.utils.dart index af853e08d2..b63df899ce 100644 --- a/mobile/lib/utils/remote_album.utils.dart +++ b/mobile/lib/utils/remote_album.utils.dart @@ -1,55 +1,37 @@ import 'package:collection/collection.dart'; import 'package:immich_mobile/domain/models/album/album.model.dart'; -typedef AlbumSortFn = List Function( - List albums, - bool isReverse, -); +typedef AlbumSortFn = List Function(List albums, bool isReverse); class _RemoteAlbumSortHandlers { const _RemoteAlbumSortHandlers._(); static const AlbumSortFn created = _sortByCreated; - static List _sortByCreated( - List albums, - bool isReverse, - ) { + static List _sortByCreated(List albums, bool isReverse) { final sorted = albums.sortedBy((album) => album.createdAt); return (isReverse ? sorted.reversed : sorted).toList(); } static const AlbumSortFn title = _sortByTitle; - static List _sortByTitle( - List albums, - bool isReverse, - ) { + static List _sortByTitle(List albums, bool isReverse) { final sorted = albums.sortedBy((album) => album.name); return (isReverse ? sorted.reversed : sorted).toList(); } static const AlbumSortFn lastModified = _sortByLastModified; - static List _sortByLastModified( - List albums, - bool isReverse, - ) { + static List _sortByLastModified(List albums, bool isReverse) { final sorted = albums.sortedBy((album) => album.updatedAt); return (isReverse ? sorted.reversed : sorted).toList(); } static const AlbumSortFn assetCount = _sortByAssetCount; - static List _sortByAssetCount( - List albums, - bool isReverse, - ) { + static List _sortByAssetCount(List albums, bool isReverse) { final sorted = albums.sorted((a, b) => a.assetCount.compareTo(b.assetCount)); return (isReverse ? sorted.reversed : sorted).toList(); } static const AlbumSortFn mostRecent = _sortByMostRecent; - static List _sortByMostRecent( - List albums, - bool isReverse, - ) { + static List _sortByMostRecent(List albums, bool isReverse) { final sorted = albums.sorted((a, b) { // For most recent, we sort by updatedAt in descending order return b.updatedAt.compareTo(a.updatedAt); @@ -58,10 +40,7 @@ class _RemoteAlbumSortHandlers { } static const AlbumSortFn mostOldest = _sortByMostOldest; - static List _sortByMostOldest( - List albums, - bool isReverse, - ) { + static List _sortByMostOldest(List albums, bool isReverse) { final sorted = albums.sorted((a, b) { // For oldest, we sort by createdAt in ascending order return a.createdAt.compareTo(b.createdAt); @@ -72,14 +51,8 @@ class _RemoteAlbumSortHandlers { enum RemoteAlbumSortMode { title("library_page_sort_title", _RemoteAlbumSortHandlers.title), - assetCount( - "library_page_sort_asset_count", - _RemoteAlbumSortHandlers.assetCount, - ), - lastModified( - "library_page_sort_last_modified", - _RemoteAlbumSortHandlers.lastModified, - ), + assetCount("library_page_sort_asset_count", _RemoteAlbumSortHandlers.assetCount), + lastModified("library_page_sort_last_modified", _RemoteAlbumSortHandlers.lastModified), created("library_page_sort_created", _RemoteAlbumSortHandlers.created), mostRecent("sort_recent", _RemoteAlbumSortHandlers.mostRecent), mostOldest("sort_oldest", _RemoteAlbumSortHandlers.mostOldest); diff --git a/mobile/lib/utils/selection_handlers.dart b/mobile/lib/utils/selection_handlers.dart index 633ce2463a..d128ef8fac 100644 --- a/mobile/lib/utils/selection_handlers.dart +++ b/mobile/lib/utils/selection_handlers.dart @@ -16,27 +16,21 @@ import 'package:immich_mobile/widgets/common/location_picker.dart'; import 'package:immich_mobile/widgets/common/share_dialog.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; -void handleShareAssets( - WidgetRef ref, - BuildContext context, - Iterable selection, -) { +void handleShareAssets(WidgetRef ref, BuildContext context, Iterable selection) { showDialog( context: context, builder: (BuildContext buildContext) { - ref.watch(shareServiceProvider).shareAssets(selection.toList(), context).then( - (bool status) { - if (!status) { - ImmichToast.show( - context: context, - msg: 'image_viewer_page_state_provider_share_error'.tr(), - toastType: ToastType.error, - gravity: ToastGravity.BOTTOM, - ); - } - buildContext.pop(); - }, - ); + ref.watch(shareServiceProvider).shareAssets(selection.toList(), context).then((bool status) { + if (!status) { + ImmichToast.show( + context: context, + msg: 'image_viewer_page_state_provider_share_error'.tr(), + toastType: ToastType.error, + gravity: ToastGravity.BOTTOM, + ); + } + buildContext.pop(); + }); return const ShareDialog(); }, barrierDismissible: false, @@ -58,11 +52,7 @@ Future handleArchiveAssets( ? 'moved_to_archive'.t(context: context, args: {'count': selection.length}) : 'moved_to_library'.t(context: context, args: {'count': selection.length}); if (context.mounted) { - ImmichToast.show( - context: context, - msg: message, - gravity: toastGravity, - ); + ImmichToast.show(context: context, msg: message, gravity: toastGravity); } } } @@ -83,20 +73,12 @@ Future handleFavoriteAssets( ? 'Added ${selection.length} $assetOrAssets to favorites' : 'Removed ${selection.length} $assetOrAssets from favorites'; if (context.mounted) { - ImmichToast.show( - context: context, - msg: toastMessage, - gravity: toastGravity, - ); + ImmichToast.show(context: context, msg: toastMessage, gravity: toastGravity); } } } -Future handleEditDateTime( - WidgetRef ref, - BuildContext context, - List selection, -) async { +Future handleEditDateTime(WidgetRef ref, BuildContext context, List selection) async { DateTime? initialDate; String? timeZone; Duration? offset; @@ -122,27 +104,17 @@ Future handleEditDateTime( ref.read(assetServiceProvider).changeDateTime(selection.toList(), dateTime); } -Future handleEditLocation( - WidgetRef ref, - BuildContext context, - List selection, -) async { +Future handleEditLocation(WidgetRef ref, BuildContext context, List selection) async { LatLng? initialLatLng; if (selection.length == 1) { final asset = selection.first; final assetWithExif = await ref.watch(assetServiceProvider).loadExif(asset); if (assetWithExif.exifInfo?.latitude != null && assetWithExif.exifInfo?.longitude != null) { - initialLatLng = LatLng( - assetWithExif.exifInfo!.latitude!, - assetWithExif.exifInfo!.longitude!, - ); + initialLatLng = LatLng(assetWithExif.exifInfo!.latitude!, assetWithExif.exifInfo!.longitude!); } } - final location = await showLocationPicker( - context: context, - initialLatLng: initialLatLng, - ); + final location = await showLocationPicker(context: context, initialLatLng: initialLatLng); if (location == null) { return; @@ -165,11 +137,7 @@ Future handleSetAssetsVisibility( ? 'Added ${selection.length} $assetOrAssets to locked folder' : 'Removed ${selection.length} $assetOrAssets from locked folder'; if (context.mounted) { - ImmichToast.show( - context: context, - msg: toastMessage, - gravity: ToastGravity.BOTTOM, - ); + ImmichToast.show(context: context, msg: toastMessage, gravity: ToastGravity.BOTTOM); } } } diff --git a/mobile/lib/utils/throttle.dart b/mobile/lib/utils/throttle.dart index c4427472d3..8b41d92318 100644 --- a/mobile/lib/utils/throttle.dart +++ b/mobile/lib/utils/throttle.dart @@ -25,17 +25,11 @@ class Throttler { /// Creates a [Throttler] that will be disposed automatically. If no [interval] is provided, a /// default interval of 300ms is used to throttle the function calls -Throttler useThrottler({ - Duration interval = const Duration(milliseconds: 300), - List? keys, -}) => +Throttler useThrottler({Duration interval = const Duration(milliseconds: 300), List? keys}) => use(_ThrottleHook(interval: interval, keys: keys)); class _ThrottleHook extends Hook { - const _ThrottleHook({ - required this.interval, - super.keys, - }); + const _ThrottleHook({required this.interval, super.keys}); final Duration interval; diff --git a/mobile/lib/utils/thumbnail_utils.dart b/mobile/lib/utils/thumbnail_utils.dart index 758305c8bc..685dc2b1c2 100644 --- a/mobile/lib/utils/thumbnail_utils.dart +++ b/mobile/lib/utils/thumbnail_utils.dart @@ -3,12 +3,7 @@ import 'package:immich_mobile/domain/models/exif.model.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; -String getAltText( - ExifInfo? exifInfo, - DateTime fileCreatedAt, - AssetType type, - List peopleNames, -) { +String getAltText(ExifInfo? exifInfo, DateTime fileCreatedAt, AssetType type, List peopleNames) { if (exifInfo?.description != null && exifInfo!.description!.isNotEmpty) { return exifInfo.description!; } @@ -41,14 +36,14 @@ String getAltText( 1 => "image_alt_text_date_place_1_person", 2 => "image_alt_text_date_place_2_people", 3 => "image_alt_text_date_place_3_people", - _ => "image_alt_text_date_place_4_or_more_people" + _ => "image_alt_text_date_place_4_or_more_people", }) : (switch (peopleNames.length) { 0 => "image_alt_text_date", 1 => "image_alt_text_date_1_person", 2 => "image_alt_text_date_2_people", 3 => "image_alt_text_date_3_people", - _ => "image_alt_text_date_4_or_more_people" + _ => "image_alt_text_date_4_or_more_people", }); return (template, args); } diff --git a/mobile/lib/utils/url_helper.dart b/mobile/lib/utils/url_helper.dart index ec65b4f7ee..e3d5b8ed57 100644 --- a/mobile/lib/utils/url_helper.dart +++ b/mobile/lib/utils/url_helper.dart @@ -44,13 +44,14 @@ String punycodeEncodeUrl(String serverUrl) { final serverUri = Uri.tryParse(serverUrl); if (serverUri == null || serverUri.host.isEmpty) return ''; - final encodedHost = Uri.decodeComponent(serverUri.host).split('.').map( - (segment) { - // If segment is already ASCII, then return as it is. - if (segment.runes.every((c) => c < 0x80)) return segment; - return 'xn--${punycodeEncode(segment)}'; - }, - ).join('.'); + final encodedHost = Uri.decodeComponent(serverUri.host) + .split('.') + .map((segment) { + // If segment is already ASCII, then return as it is. + if (segment.runes.every((c) => c < 0x80)) return segment; + return 'xn--${punycodeEncode(segment)}'; + }) + .join('.'); return serverUri.replace(host: encodedHost).toString(); } @@ -76,15 +77,16 @@ String? punycodeDecodeUrl(String? serverUrl) { final serverUri = serverUrl != null ? Uri.tryParse(serverUrl) : null; if (serverUri == null || serverUri.host.isEmpty) return null; - final decodedHost = serverUri.host.split('.').map( - (segment) { - if (segment.toLowerCase().startsWith('xn--')) { - return punycodeDecode(segment.substring(4)); - } - // If segment is not punycode encoded, then return as it is. - return segment; - }, - ).join('.'); + final decodedHost = serverUri.host + .split('.') + .map((segment) { + if (segment.toLowerCase().startsWith('xn--')) { + return punycodeDecode(segment.substring(4)); + } + // If segment is not punycode encoded, then return as it is. + return segment; + }) + .join('.'); return Uri.decodeFull(serverUri.replace(host: decodedHost).toString()); } diff --git a/mobile/lib/utils/version_compatibility.dart b/mobile/lib/utils/version_compatibility.dart index 19d9aa38d4..fa8dfb0b9e 100644 --- a/mobile/lib/utils/version_compatibility.dart +++ b/mobile/lib/utils/version_compatibility.dart @@ -1,9 +1,4 @@ -String? getVersionCompatibilityMessage( - int appMajor, - int appMinor, - int serverMajor, - int serverMinor, -) { +String? getVersionCompatibilityMessage(int appMajor, int appMinor, int serverMajor, int serverMinor) { if (serverMajor != appMajor) { return 'Your app major version is not compatible with the server!'; } diff --git a/mobile/lib/widgets/activities/activity_text_field.dart b/mobile/lib/widgets/activities/activity_text_field.dart index f111de5e53..e3958b6287 100644 --- a/mobile/lib/widgets/activities/activity_text_field.dart +++ b/mobile/lib/widgets/activities/activity_text_field.dart @@ -13,12 +13,7 @@ class ActivityTextField extends HookConsumerWidget { final String? likeId; final Function(String) onSubmit; - const ActivityTextField({ - required this.onSubmit, - this.isEnabled = true, - this.likeId, - super.key, - }); + const ActivityTextField({required this.onSubmit, this.isEnabled = true, this.likeId, super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -31,13 +26,10 @@ class ActivityTextField extends HookConsumerWidget { final liked = likeId != null; // Show keyboard immediately on activities open - useEffect( - () { - inputFocusNode.requestFocus(); - return null; - }, - [], - ); + useEffect(() { + inputFocusNode.requestFocus(); + return null; + }, []); // Pass text to callback and reset controller void onEditingComplete() { @@ -70,29 +62,19 @@ class ActivityTextField extends HookConsumerWidget { prefixIcon: user != null ? Padding( padding: const EdgeInsets.symmetric(horizontal: 15), - child: UserCircleAvatar( - user: user, - size: 30, - radius: 15, - ), + child: UserCircleAvatar(user: user, size: 30, radius: 15), ) : null, suffixIcon: Padding( padding: const EdgeInsets.only(right: 10), child: IconButton( - icon: Icon( - liked ? Icons.favorite_rounded : Icons.favorite_border_rounded, - ), + icon: Icon(liked ? Icons.favorite_rounded : Icons.favorite_border_rounded), onPressed: liked ? removeLike : addLike, ), ), suffixIconColor: liked ? Colors.red[700] : null, hintText: !isEnabled ? 'shared_album_activities_input_disable'.tr() : 'say_something'.tr(), - hintStyle: TextStyle( - fontWeight: FontWeight.normal, - fontSize: 14, - color: Colors.grey[600], - ), + hintStyle: TextStyle(fontWeight: FontWeight.normal, fontSize: 14, color: Colors.grey[600]), ), onEditingComplete: onEditingComplete, onTapOutside: (_) => inputFocusNode.unfocus(), diff --git a/mobile/lib/widgets/activities/activity_tile.dart b/mobile/lib/widgets/activities/activity_tile.dart index a2bc5135c6..4b66bd5eaf 100644 --- a/mobile/lib/widgets/activities/activity_tile.dart +++ b/mobile/lib/widgets/activities/activity_tile.dart @@ -26,10 +26,7 @@ class ActivityTile extends HookConsumerWidget { ? Container( width: 44, alignment: Alignment.center, - child: Icon( - Icons.favorite_rounded, - color: Colors.red[700], - ), + child: Icon(Icons.favorite_rounded, color: Colors.red[700]), ) : UserCircleAvatar(user: activity.user), title: _ActivityTitle( @@ -50,11 +47,7 @@ class _ActivityTitle extends StatelessWidget { final String createdAt; final bool leftAlign; - const _ActivityTitle({ - required this.userName, - required this.createdAt, - required this.leftAlign, - }); + const _ActivityTitle({required this.userName, required this.createdAt, required this.leftAlign}); @override Widget build(BuildContext context) { @@ -65,16 +58,8 @@ class _ActivityTitle extends StatelessWidget { mainAxisAlignment: leftAlign ? MainAxisAlignment.start : MainAxisAlignment.spaceBetween, mainAxisSize: leftAlign ? MainAxisSize.min : MainAxisSize.max, children: [ - Text( - userName, - style: textStyle, - overflow: TextOverflow.ellipsis, - ), - if (leftAlign) - Text( - " • ", - style: textStyle, - ), + Text(userName, style: textStyle, overflow: TextOverflow.ellipsis), + if (leftAlign) Text(" • ", style: textStyle), Expanded( child: Text( createdAt, @@ -101,9 +86,7 @@ class _ActivityAssetThumbnail extends StatelessWidget { decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(4)), image: DecorationImage( - image: ImmichRemoteThumbnailProvider( - assetId: assetId, - ), + image: ImmichRemoteThumbnailProvider(assetId: assetId), fit: BoxFit.cover, ), ), diff --git a/mobile/lib/widgets/activities/dismissible_activity.dart b/mobile/lib/widgets/activities/dismissible_activity.dart index b6c083f616..2f017d51ed 100644 --- a/mobile/lib/widgets/activities/dismissible_activity.dart +++ b/mobile/lib/widgets/activities/dismissible_activity.dart @@ -8,20 +8,13 @@ class DismissibleActivity extends StatelessWidget { final ActivityTile body; final Function(String)? onDismiss; - const DismissibleActivity( - this.activityId, - this.body, { - this.onDismiss, - super.key, - }); + const DismissibleActivity(this.activityId, this.body, {this.onDismiss, super.key}); @override Widget build(BuildContext context) { return Dismissible( key: Key(activityId), - dismissThresholds: const { - DismissDirection.horizontal: 0.7, - }, + dismissThresholds: const {DismissDirection.horizontal: 0.7}, direction: DismissDirection.horizontal, confirmDismiss: (direction) => onDismiss != null ? showDialog( @@ -51,10 +44,7 @@ class _DismissBackground extends StatelessWidget { final AlignmentDirectional alignment; final bool withDeleteIcon; - const _DismissBackground({ - required this.withDeleteIcon, - this.alignment = AlignmentDirectional.centerStart, - }); + const _DismissBackground({required this.withDeleteIcon, this.alignment = AlignmentDirectional.centerStart}); @override Widget build(BuildContext context) { @@ -64,10 +54,7 @@ class _DismissBackground extends StatelessWidget { child: withDeleteIcon ? const Padding( padding: EdgeInsets.all(15), - child: Icon( - Icons.delete_sweep_rounded, - color: Colors.black, - ), + child: Icon(Icons.delete_sweep_rounded, color: Colors.black), ) : null, ); diff --git a/mobile/lib/widgets/album/add_to_album_bottom_sheet.dart b/mobile/lib/widgets/album/add_to_album_bottom_sheet.dart index c256c558d6..d8f6a8885a 100644 --- a/mobile/lib/widgets/album/add_to_album_bottom_sheet.dart +++ b/mobile/lib/widgets/album/add_to_album_bottom_sheet.dart @@ -17,46 +17,33 @@ class AddToAlbumBottomSheet extends HookConsumerWidget { /// The asset to add to an album final List assets; - const AddToAlbumBottomSheet({ - super.key, - required this.assets, - }); + const AddToAlbumBottomSheet({super.key, required this.assets}); @override Widget build(BuildContext context, WidgetRef ref) { final albums = ref.watch(albumProvider).where((a) => a.isRemote).toList(); final albumService = ref.watch(albumServiceProvider); - useEffect( - () { - // Fetch album updates, e.g., cover image - ref.read(albumProvider.notifier).refreshRemoteAlbums(); + useEffect(() { + // Fetch album updates, e.g., cover image + ref.read(albumProvider.notifier).refreshRemoteAlbums(); - return null; - }, - [], - ); + return null; + }, []); void addToAlbum(Album album) async { - final result = await albumService.addAssets( - album, - assets, - ); + final result = await albumService.addAssets(album, assets); if (result != null) { if (result.alreadyInAlbum.isNotEmpty) { ImmichToast.show( context: context, - msg: 'add_to_album_bottom_sheet_already_exists'.tr( - namedArgs: {"album": album.name}, - ), + msg: 'add_to_album_bottom_sheet_already_exists'.tr(namedArgs: {"album": album.name}), ); } else { ImmichToast.show( context: context, - msg: 'add_to_album_bottom_sheet_added'.tr( - namedArgs: {"album": album.name}, - ), + msg: 'add_to_album_bottom_sheet_added'.tr(namedArgs: {"album": album.name}), ); } } @@ -66,10 +53,7 @@ class AddToAlbumBottomSheet extends HookConsumerWidget { return Card( elevation: 0, shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(15), - topRight: Radius.circular(15), - ), + borderRadius: BorderRadius.only(topLeft: Radius.circular(15), topRight: Radius.circular(15)), ), child: CustomScrollView( slivers: [ @@ -80,33 +64,17 @@ class AddToAlbumBottomSheet extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 12), - const Align( - alignment: Alignment.center, - child: CustomDraggingHandle(), - ), + const Align(alignment: Alignment.center, child: CustomDraggingHandle()), const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - 'add_to_album'.tr(), - style: context.textTheme.displayMedium, - ), + Text('add_to_album'.tr(), style: context.textTheme.displayMedium), TextButton.icon( - icon: Icon( - Icons.add, - color: context.primaryColor, - ), - label: Text( - 'common_create_new_album'.tr(), - style: TextStyle(color: context.primaryColor), - ), + icon: Icon(Icons.add, color: context.primaryColor), + label: Text('common_create_new_album'.tr(), style: TextStyle(color: context.primaryColor)), onPressed: () { - context.pushRoute( - CreateAlbumRoute( - assets: assets, - ), - ); + context.pushRoute(CreateAlbumRoute(assets: assets)); }, ), ], diff --git a/mobile/lib/widgets/album/add_to_album_sliverlist.dart b/mobile/lib/widgets/album/add_to_album_sliverlist.dart index b0f2a0a49a..defbd90388 100644 --- a/mobile/lib/widgets/album/add_to_album_sliverlist.dart +++ b/mobile/lib/widgets/album/add_to_album_sliverlist.dart @@ -28,8 +28,10 @@ class AddToAlbumSliverList extends HookConsumerWidget { final sortedSharedAlbums = albumSortMode.sortFn(sharedAlbums, albumSortIsReverse); return SliverList( - delegate: - SliverChildBuilderDelegate(childCount: albums.length + (sharedAlbums.isEmpty ? 0 : 1), (context, index) { + delegate: SliverChildBuilderDelegate(childCount: albums.length + (sharedAlbums.isEmpty ? 0 : 1), ( + context, + index, + ) { // Build shared expander if (index == 0 && sortedSharedAlbums.isNotEmpty) { return Padding( @@ -56,10 +58,7 @@ class AddToAlbumSliverList extends HookConsumerWidget { // Build albums list final offset = index - (sharedAlbums.isNotEmpty ? 1 : 0); final album = sortedAlbums[offset]; - return AlbumThumbnailListTile( - album: album, - onTap: enabled ? () => onAddToAlbum(album) : () {}, - ); + return AlbumThumbnailListTile(album: album, onTap: enabled ? () => onAddToAlbum(album) : () {}); }), ); } diff --git a/mobile/lib/widgets/album/album_action_filled_button.dart b/mobile/lib/widgets/album/album_action_filled_button.dart index 48a8a27f59..04447ffab6 100644 --- a/mobile/lib/widgets/album/album_action_filled_button.dart +++ b/mobile/lib/widgets/album/album_action_filled_button.dart @@ -6,12 +6,7 @@ class AlbumActionFilledButton extends StatelessWidget { final String labelText; final IconData iconData; - const AlbumActionFilledButton({ - super.key, - this.onPressed, - required this.labelText, - required this.iconData, - }); + const AlbumActionFilledButton({super.key, this.onPressed, required this.labelText, required this.iconData}); @override Widget build(BuildContext context) { @@ -20,24 +15,12 @@ class AlbumActionFilledButton extends StatelessWidget { child: OutlinedButton.icon( style: OutlinedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 16), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(20)), - ), - side: BorderSide( - color: context.colorScheme.surfaceContainerHighest, - width: 1, - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20))), + side: BorderSide(color: context.colorScheme.surfaceContainerHighest, width: 1), backgroundColor: context.colorScheme.surfaceContainerHigh, ), - icon: Icon( - iconData, - size: 18, - color: context.primaryColor, - ), - label: Text( - labelText, - style: context.textTheme.labelLarge?.copyWith(), - ), + icon: Icon(iconData, size: 18, color: context.primaryColor), + label: Text(labelText, style: context.textTheme.labelLarge?.copyWith()), onPressed: onPressed, ), ); diff --git a/mobile/lib/widgets/album/album_thumbnail_card.dart b/mobile/lib/widgets/album/album_thumbnail_card.dart index e8d0425d4e..6c56f5d843 100644 --- a/mobile/lib/widgets/album/album_thumbnail_card.dart +++ b/mobile/lib/widgets/album/album_thumbnail_card.dart @@ -16,13 +16,7 @@ class AlbumThumbnailCard extends ConsumerWidget { final bool showOwner; final bool showTitle; - const AlbumThumbnailCard({ - super.key, - required this.album, - this.onTap, - this.showOwner = false, - this.showTitle = true, - }); + const AlbumThumbnailCard({super.key, required this.album, this.onTap, this.showOwner = false, this.showTitle = true}); final Album album; @@ -36,24 +30,14 @@ class AlbumThumbnailCard extends ConsumerWidget { return Container( height: cardSize, width: cardSize, - decoration: BoxDecoration( - color: context.colorScheme.surfaceContainerHigh, - ), + decoration: BoxDecoration(color: context.colorScheme.surfaceContainerHigh), child: Center( - child: Icon( - Icons.no_photography, - size: cardSize * .15, - color: context.colorScheme.primary, - ), + child: Icon(Icons.no_photography, size: cardSize * .15, color: context.colorScheme.primary), ), ); } - buildAlbumThumbnail() => ImmichThumbnail( - asset: album.thumbnail.value, - width: cardSize, - height: cardSize, - ); + buildAlbumThumbnail() => ImmichThumbnail(asset: album.thumbnail.value, width: cardSize, height: cardSize); buildAlbumTextRow() { // Add the owner name to the subtitle @@ -62,12 +46,7 @@ class AlbumThumbnailCard extends ConsumerWidget { if (album.ownerId == ref.read(currentUserProvider)?.id) { owner = 'owned'.tr(); } else if (album.ownerName != null) { - owner = 'shared_by_user'.t( - context: context, - args: { - 'user': album.ownerName!, - }, - ); + owner = 'shared_by_user'.t(context: context, args: {'user': album.ownerName!}); } } @@ -75,19 +54,12 @@ class AlbumThumbnailCard extends ConsumerWidget { TextSpan( children: [ TextSpan( - text: 'items_count'.t( - context: context, - args: { - 'count': album.assetCount, - }, - ), + text: 'items_count'.t(context: context, args: {'count': album.assetCount}), ), if (owner != null) const TextSpan(text: ' • '), if (owner != null) TextSpan(text: owner), ], - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), overflow: TextOverflow.fade, ); @@ -106,9 +78,7 @@ class AlbumThumbnailCard extends ConsumerWidget { width: cardSize, height: cardSize, child: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), child: album.thumbnail.value == null ? buildEmptyThumbnail() : buildAlbumThumbnail(), ), ), diff --git a/mobile/lib/widgets/album/album_thumbnail_listtile.dart b/mobile/lib/widgets/album/album_thumbnail_listtile.dart index 8332cde889..423410eedf 100644 --- a/mobile/lib/widgets/album/album_thumbnail_listtile.dart +++ b/mobile/lib/widgets/album/album_thumbnail_listtile.dart @@ -11,11 +11,7 @@ import 'package:immich_mobile/utils/image_url_builder.dart'; import 'package:openapi/api.dart'; class AlbumThumbnailListTile extends StatelessWidget { - const AlbumThumbnailListTile({ - super.key, - required this.album, - this.onTap, - }); + const AlbumThumbnailListTile({super.key, required this.album, this.onTap}); final Album album; final void Function()? onTap; @@ -26,15 +22,11 @@ class AlbumThumbnailListTile extends StatelessWidget { buildEmptyThumbnail() { return Container( - decoration: BoxDecoration( - color: context.isDarkTheme ? Colors.grey[800] : Colors.grey[200], - ), + decoration: BoxDecoration(color: context.isDarkTheme ? Colors.grey[800] : Colors.grey[200]), child: SizedBox( height: cardSize, width: cardSize, - child: const Center( - child: Icon(Icons.no_photography), - ), + child: const Center(child: Icon(Icons.no_photography)), ), ); } @@ -45,10 +37,7 @@ class AlbumThumbnailListTile extends StatelessWidget { height: cardSize, fit: BoxFit.cover, fadeInDuration: const Duration(milliseconds: 200), - imageUrl: getAlbumThumbnailUrl( - album, - type: AssetMediaSize.thumbnail, - ), + imageUrl: getAlbumThumbnailUrl(album, type: AssetMediaSize.thumbnail), httpHeaders: ApiService.getRequestHeaders(), cacheKey: getAlbumThumbNailCacheKey(album, type: AssetMediaSize.thumbnail), errorWidget: (context, url, error) => const Icon(Icons.image_not_supported_outlined), @@ -57,7 +46,8 @@ class AlbumThumbnailListTile extends StatelessWidget { return GestureDetector( behavior: HitTestBehavior.opaque, - onTap: onTap ?? + onTap: + onTap ?? () { context.pushRoute(AlbumViewerRoute(albumId: album.id)); }, @@ -79,37 +69,18 @@ class AlbumThumbnailListTile extends StatelessWidget { Text( album.name, overflow: TextOverflow.ellipsis, - style: const TextStyle( - fontWeight: FontWeight.bold, - ), + style: const TextStyle(fontWeight: FontWeight.bold), ), Row( mainAxisSize: MainAxisSize.min, children: [ Text( - 'items_count'.t( - context: context, - args: { - 'count': album.assetCount, - }, - ), - style: const TextStyle( - fontSize: 12, - ), + 'items_count'.t(context: context, args: {'count': album.assetCount}), + style: const TextStyle(fontSize: 12), ), if (album.shared) ...[ - const Text( - ' • ', - style: TextStyle( - fontSize: 12, - ), - ), - Text( - 'shared'.tr(), - style: const TextStyle( - fontSize: 12, - ), - ), + const Text(' • ', style: TextStyle(fontSize: 12)), + Text('shared'.tr(), style: const TextStyle(fontSize: 12)), ], ], ), diff --git a/mobile/lib/widgets/album/album_title_text_field.dart b/mobile/lib/widgets/album/album_title_text_field.dart index 7807a6e6ae..0a7438b7ae 100644 --- a/mobile/lib/widgets/album/album_title_text_field.dart +++ b/mobile/lib/widgets/album/album_title_text_field.dart @@ -31,11 +31,7 @@ class AlbumTitleTextField extends ConsumerWidget { ref.watch(albumTitleProvider.notifier).setAlbumTitle(v); }, focusNode: albumTitleTextFieldFocusNode, - style: TextStyle( - fontSize: 28, - color: context.colorScheme.onSurface, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 28, color: context.colorScheme.onSurface, fontWeight: FontWeight.bold), controller: albumTitleController, onTap: () { isAlbumTitleTextFieldFocus.value = true; @@ -52,24 +48,17 @@ class AlbumTitleTextField extends ConsumerWidget { albumTitleController.clear(); isAlbumTitleEmpty.value = true; }, - icon: Icon( - Icons.cancel_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.cancel_rounded, color: context.primaryColor), splashRadius: 10, ) : null, enabledBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.transparent), - borderRadius: BorderRadius.all( - Radius.circular(10), - ), + borderRadius: BorderRadius.all(Radius.circular(10)), ), focusedBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.transparent), - borderRadius: BorderRadius.all( - Radius.circular(10), - ), + borderRadius: BorderRadius.all(Radius.circular(10)), ), hintText: 'add_a_title'.tr(), hintStyle: context.themeData.inputDecorationTheme.hintStyle?.copyWith( diff --git a/mobile/lib/widgets/album/album_viewer_appbar.dart b/mobile/lib/widgets/album/album_viewer_appbar.dart index f13f1c3b21..420218d7e5 100644 --- a/mobile/lib/widgets/album/album_viewer_appbar.dart +++ b/mobile/lib/widgets/album/album_viewer_appbar.dart @@ -82,10 +82,7 @@ class AlbumViewerAppbar extends HookConsumerWidget implements PreferredSizeWidge onPressed: () => context.pop('Cancel'), child: Text( 'cancel', - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold), ).tr(), ), TextButton( @@ -95,10 +92,7 @@ class AlbumViewerAppbar extends HookConsumerWidget implements PreferredSizeWidge }, child: Text( 'confirm', - style: TextStyle( - fontWeight: FontWeight.bold, - color: context.colorScheme.error, - ), + style: TextStyle(fontWeight: FontWeight.bold, color: context.colorScheme.error), ).tr(), ), ], @@ -128,10 +122,7 @@ class AlbumViewerAppbar extends HookConsumerWidget implements PreferredSizeWidge album.ownerId == userId ? ListTile( leading: const Icon(Icons.delete_forever_rounded), - title: const Text( - 'delete_album', - style: TextStyle(fontWeight: FontWeight.w500), - ).tr(), + title: const Text('delete_album', style: TextStyle(fontWeight: FontWeight.w500)).tr(), onTap: onDeleteAlbumPressed, ) : ListTile( @@ -172,18 +163,12 @@ class AlbumViewerAppbar extends HookConsumerWidget implements PreferredSizeWidge onAddUsers(); } }, - title: const Text( - "album_viewer_page_share_add_users", - style: TextStyle(fontWeight: FontWeight.w500), - ).tr(), + title: const Text("album_viewer_page_share_add_users", style: TextStyle(fontWeight: FontWeight.w500)).tr(), ), ListTile( leading: const Icon(Icons.swap_vert_rounded), onTap: onSortOrderToggled, - title: const Text( - "change_display_order", - style: TextStyle(fontWeight: FontWeight.w500), - ).tr(), + title: const Text("change_display_order", style: TextStyle(fontWeight: FontWeight.w500)).tr(), ), ListTile( leading: const Icon(Icons.link_rounded), @@ -191,18 +176,12 @@ class AlbumViewerAppbar extends HookConsumerWidget implements PreferredSizeWidge context.pushRoute(SharedLinkEditRoute(albumId: album.remoteId)); context.pop(); }, - title: const Text( - "control_bottom_app_bar_share_link", - style: TextStyle(fontWeight: FontWeight.w500), - ).tr(), + title: const Text("control_bottom_app_bar_share_link", style: TextStyle(fontWeight: FontWeight.w500)).tr(), ), ListTile( leading: const Icon(Icons.settings_rounded), onTap: () => context.navigateTo(const AlbumOptionsRoute()), - title: const Text( - "options", - style: TextStyle(fontWeight: FontWeight.w500), - ).tr(), + title: const Text("options", style: TextStyle(fontWeight: FontWeight.w500)).tr(), ), ]; @@ -216,10 +195,7 @@ class AlbumViewerAppbar extends HookConsumerWidget implements PreferredSizeWidge onAddPhotos(); } }, - title: const Text( - "add_photos", - style: TextStyle(fontWeight: FontWeight.w500), - ).tr(), + title: const Text("add_photos", style: TextStyle(fontWeight: FontWeight.w500)).tr(), ), ]; showModalBottomSheet( @@ -250,18 +226,13 @@ class AlbumViewerAppbar extends HookConsumerWidget implements PreferredSizeWidge icon: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - const Icon( - Icons.mode_comment_outlined, - ), + const Icon(Icons.mode_comment_outlined), if (comments != 0) Padding( padding: const EdgeInsets.only(left: 5), child: Text( comments.toString(), - style: TextStyle( - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), + style: TextStyle(fontWeight: FontWeight.bold, color: context.primaryColor), ), ), ], @@ -285,8 +256,9 @@ class AlbumViewerAppbar extends HookConsumerWidget implements PreferredSizeWidge } titleFocusNode.unfocus(); } else if (newAlbumDescription.isNotEmpty) { - bool isSuccessDescription = - await ref.watch(albumViewerProvider.notifier).changeAlbumDescription(album, newAlbumDescription); + bool isSuccessDescription = await ref + .watch(albumViewerProvider.notifier) + .changeAlbumDescription(album, newAlbumDescription); if (!isSuccessDescription) { ImmichToast.show( context: context, @@ -322,11 +294,7 @@ class AlbumViewerAppbar extends HookConsumerWidget implements PreferredSizeWidge actions: [ if (album.shared && (album.activityEnabled || comments != 0)) buildActivitiesButton(), if (album.isRemote) ...[ - IconButton( - splashRadius: 25, - onPressed: buildBottomSheet, - icon: const Icon(Icons.more_horiz_rounded), - ), + IconButton(splashRadius: 25, onPressed: buildBottomSheet, icon: const Icon(Icons.more_horiz_rounded)), ], ], ); diff --git a/mobile/lib/widgets/album/album_viewer_editable_description.dart b/mobile/lib/widgets/album/album_viewer_editable_description.dart index 94f41dc7fd..decd268ff3 100644 --- a/mobile/lib/widgets/album/album_viewer_editable_description.dart +++ b/mobile/lib/widgets/album/album_viewer_editable_description.dart @@ -8,11 +8,7 @@ import 'package:immich_mobile/providers/album/album_viewer.provider.dart'; class AlbumViewerEditableDescription extends HookConsumerWidget { final String albumDescription; final FocusNode descriptionFocusNode; - const AlbumViewerEditableDescription({ - super.key, - required this.albumDescription, - required this.descriptionFocusNode, - }); + const AlbumViewerEditableDescription({super.key, required this.albumDescription, required this.descriptionFocusNode}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -31,15 +27,12 @@ class AlbumViewerEditableDescription extends HookConsumerWidget { } } - useEffect( - () { - descriptionFocusNode.addListener(onFocusModeChange); - return () { - descriptionFocusNode.removeListener(onFocusModeChange); - }; - }, - [], - ); + useEffect(() { + descriptionFocusNode.addListener(onFocusModeChange); + return () { + descriptionFocusNode.removeListener(onFocusModeChange); + }; + }, []); return Material( color: Colors.transparent, @@ -72,19 +65,12 @@ class AlbumViewerEditableDescription extends HookConsumerWidget { onPressed: () { descriptionTextEditController.clear(); }, - icon: Icon( - Icons.cancel_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.cancel_rounded, color: context.primaryColor), splashRadius: 10, ) : null, - enabledBorder: const OutlineInputBorder( - borderSide: BorderSide(color: Colors.transparent), - ), - focusedBorder: const OutlineInputBorder( - borderSide: BorderSide(color: Colors.transparent), - ), + enabledBorder: const OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)), + focusedBorder: const OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)), focusColor: Colors.grey[300], fillColor: context.scaffoldBackgroundColor, filled: descriptionFocusNode.hasFocus, diff --git a/mobile/lib/widgets/album/album_viewer_editable_title.dart b/mobile/lib/widgets/album/album_viewer_editable_title.dart index b64be09ff9..c84e613017 100644 --- a/mobile/lib/widgets/album/album_viewer_editable_title.dart +++ b/mobile/lib/widgets/album/album_viewer_editable_title.dart @@ -8,11 +8,7 @@ import 'package:immich_mobile/providers/album/album_viewer.provider.dart'; class AlbumViewerEditableTitle extends HookConsumerWidget { final String albumName; final FocusNode titleFocusNode; - const AlbumViewerEditableTitle({ - super.key, - required this.albumName, - required this.titleFocusNode, - }); + const AlbumViewerEditableTitle({super.key, required this.albumName, required this.titleFocusNode}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -31,15 +27,12 @@ class AlbumViewerEditableTitle extends HookConsumerWidget { } } - useEffect( - () { - titleFocusNode.addListener(onFocusModeChange); - return () { - titleFocusNode.removeListener(onFocusModeChange); - }; - }, - [], - ); + useEffect(() { + titleFocusNode.addListener(onFocusModeChange); + return () { + titleFocusNode.removeListener(onFocusModeChange); + }; + }, []); return Material( color: Colors.transparent, @@ -51,9 +44,7 @@ class AlbumViewerEditableTitle extends HookConsumerWidget { } }, focusNode: titleFocusNode, - style: context.textTheme.headlineLarge?.copyWith( - fontWeight: FontWeight.w700, - ), + style: context.textTheme.headlineLarge?.copyWith(fontWeight: FontWeight.w700), controller: titleTextEditController, onTap: () { context.focusScope.requestFocus(titleFocusNode); @@ -66,35 +57,23 @@ class AlbumViewerEditableTitle extends HookConsumerWidget { } }, decoration: InputDecoration( - contentPadding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 0, - ), + contentPadding: const EdgeInsets.symmetric(horizontal: 8, vertical: 0), suffixIcon: titleFocusNode.hasFocus ? IconButton( onPressed: () { titleTextEditController.clear(); }, - icon: Icon( - Icons.cancel_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.cancel_rounded, color: context.primaryColor), splashRadius: 10, ) : null, - enabledBorder: const OutlineInputBorder( - borderSide: BorderSide(color: Colors.transparent), - ), - focusedBorder: const OutlineInputBorder( - borderSide: BorderSide(color: Colors.transparent), - ), + enabledBorder: const OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)), + focusedBorder: const OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)), focusColor: Colors.grey[300], fillColor: context.scaffoldBackgroundColor, filled: titleFocusNode.hasFocus, hintText: 'add_a_title'.tr(), - hintStyle: context.themeData.inputDecorationTheme.hintStyle?.copyWith( - fontSize: 28, - ), + hintStyle: context.themeData.inputDecorationTheme.hintStyle?.copyWith(fontSize: 28), ), ), ); diff --git a/mobile/lib/widgets/album/remote_album_shared_user_icons.dart b/mobile/lib/widgets/album/remote_album_shared_user_icons.dart index f7f3f62b32..7be5db1798 100644 --- a/mobile/lib/widgets/album/remote_album_shared_user_icons.dart +++ b/mobile/lib/widgets/album/remote_album_shared_user_icons.dart @@ -5,9 +5,7 @@ import 'package:immich_mobile/providers/infrastructure/remote_album.provider.dar import 'package:immich_mobile/widgets/common/user_circle_avatar.dart'; class RemoteAlbumSharedUserIcons extends ConsumerWidget { - const RemoteAlbumSharedUserIcons({ - super.key, - }); + const RemoteAlbumSharedUserIcons({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -31,12 +29,7 @@ class RemoteAlbumSharedUserIcons extends ConsumerWidget { itemBuilder: ((context, index) { return Padding( padding: const EdgeInsets.only(right: 4.0), - child: UserCircleAvatar( - user: sharedUsers[index], - radius: 18, - size: 36, - hasBorder: true, - ), + child: UserCircleAvatar(user: sharedUsers[index], radius: 18, size: 36, hasBorder: true), ); }), itemCount: sharedUsers.length, diff --git a/mobile/lib/widgets/album/shared_album_thumbnail_image.dart b/mobile/lib/widgets/album/shared_album_thumbnail_image.dart index e485763114..b21e86d145 100644 --- a/mobile/lib/widgets/album/shared_album_thumbnail_image.dart +++ b/mobile/lib/widgets/album/shared_album_thumbnail_image.dart @@ -14,15 +14,7 @@ class SharedAlbumThumbnailImage extends HookConsumerWidget { onTap: () { // debugPrint("View ${asset.id}"); }, - child: Stack( - children: [ - ImmichThumbnail( - asset: asset, - width: 500, - height: 500, - ), - ], - ), + child: Stack(children: [ImmichThumbnail(asset: asset, width: 500, height: 500)]), ); } } diff --git a/mobile/lib/widgets/asset_grid/asset_drag_region.dart b/mobile/lib/widgets/asset_grid/asset_drag_region.dart index f27fae64e8..71e55acbd6 100644 --- a/mobile/lib/widgets/asset_grid/asset_drag_region.dart +++ b/mobile/lib/widgets/asset_grid/asset_drag_region.dart @@ -163,12 +163,7 @@ class AssetIndexWrapper extends SingleChildRenderObjectWidget { final int rowIndex; final int sectionIndex; - const AssetIndexWrapper({ - required Widget super.child, - required this.rowIndex, - required this.sectionIndex, - super.key, - }); + const AssetIndexWrapper({required Widget super.child, required this.rowIndex, required this.sectionIndex, super.key}); @override // ignore: library_private_types_in_public_api @@ -191,19 +186,14 @@ class AssetIndexWrapper extends SingleChildRenderObjectWidget { class _AssetIndexProxy extends RenderProxyBox { AssetIndex index; - _AssetIndexProxy({ - required this.index, - }); + _AssetIndexProxy({required this.index}); } class AssetIndex { final int rowIndex; final int sectionIndex; - const AssetIndex({ - required this.rowIndex, - required this.sectionIndex, - }); + const AssetIndex({required this.rowIndex, required this.sectionIndex}); @override bool operator ==(covariant AssetIndex other) { diff --git a/mobile/lib/widgets/asset_grid/asset_grid_data_structure.dart b/mobile/lib/widgets/asset_grid/asset_grid_data_structure.dart index 88b993a026..d95d6efe2e 100644 --- a/mobile/lib/widgets/asset_grid/asset_grid_data_structure.dart +++ b/mobile/lib/widgets/asset_grid/asset_grid_data_structure.dart @@ -8,12 +8,7 @@ import 'package:logging/logging.dart'; final log = Logger('AssetGridDataStructure'); -enum RenderAssetGridElementType { - assets, - assetRow, - groupDividerTitle, - monthTitle; -} +enum RenderAssetGridElementType { assets, assetRow, groupDividerTitle, monthTitle } class RenderAssetGridElement { final RenderAssetGridElementType type; @@ -33,13 +28,7 @@ class RenderAssetGridElement { }); } -enum GroupAssetsBy { - day, - month, - auto, - none, - ; -} +enum GroupAssetsBy { day, month, auto, none } class RenderList { final List elements; @@ -87,10 +76,7 @@ class RenderList { // when scrolling backward, end shortly after the requested offset... // ... to guard against the user scrolling in the other direction // a tiny bit resulting in a another required load from the DB - final start = max( - 0, - forward ? offset - oppositeSize : (len > batchSize ? offset : offset + count - len), - ); + final start = max(0, forward ? offset - oppositeSize : (len > batchSize ? offset : offset + count - len)); // load the calculated batch (start:start+len) from the DB and put it into the buffer _buf = query!.offset(start).limit(len).findAllSync(); _bufOffset = start; @@ -117,19 +103,14 @@ class RenderList { // request the asset from the database (not changing the buffer!) final asset = query!.offset(index).findFirstSync(); if (asset == null) { - throw Exception( - "Asset at index $index does no longer exist in database", - ); + throw Exception("Asset at index $index does no longer exist in database"); } return asset; } throw Exception("RenderList has neither assets nor query"); } - static Future fromQuery( - QueryBuilder query, - GroupAssetsBy groupBy, - ) => + static Future fromQuery(QueryBuilder query, GroupAssetsBy groupBy) => _buildRenderList(null, query, groupBy); static Future _buildRenderList( @@ -145,12 +126,7 @@ class RenderList { if (groupBy == GroupAssetsBy.none) { final int total = assets?.length ?? query!.countSync(); - final dateLoader = query != null - ? DateBatchLoader( - query: query, - batchSize: 1000 * sectionSize, - ) - : null; + final dateLoader = query != null ? DateBatchLoader(query: query, batchSize: 1000 * sectionSize) : null; for (int i = 0; i < total; i += sectionSize) { final date = assets != null ? assets[i].fileCreatedAt : await dateLoader?.getDate(i); @@ -224,11 +200,11 @@ class RenderList { for (int j = 0; j < count; j += sectionSize) { final type = j == 0 ? (groupBy != GroupAssetsBy.month && newMonth - ? RenderAssetGridElementType.monthTitle - : RenderAssetGridElementType.groupDividerTitle) + ? RenderAssetGridElementType.monthTitle + : RenderAssetGridElementType.groupDividerTitle) : (groupBy == GroupAssetsBy.auto - ? RenderAssetGridElementType.groupDividerTitle - : RenderAssetGridElementType.assets); + ? RenderAssetGridElementType.groupDividerTitle + : RenderAssetGridElementType.assets); final sectionCount = j + sectionSize > count ? count - j : sectionSize; assert(sectionCount > 0 && sectionCount <= sectionSize); elements.add( @@ -257,11 +233,7 @@ class RenderList { : await query!.offset(offset).limit(pageSize).fileCreatedAtProperty().findAll(); int i = 0; for (final date in dates) { - final d = DateTime( - date.year, - date.month, - groupBy == GroupAssetsBy.month ? 1 : date.day, - ); + final d = DateTime(date.year, date.month, groupBy == GroupAssetsBy.month ? 1 : date.day); current ??= d; if (current != d) { addElems(current, prevDate); @@ -288,10 +260,7 @@ class RenderList { static RenderList empty() => RenderList([], null, []); - static Future fromAssets( - List assets, - GroupAssetsBy groupBy, - ) => + static Future fromAssets(List assets, GroupAssetsBy groupBy) => _buildRenderList(assets, null, groupBy); /// Deletes an asset from the render list and clears the buffer @@ -310,10 +279,7 @@ class DateBatchLoader { List _buffer = []; int _bufferStart = 0; - DateBatchLoader({ - required this.query, - required this.batchSize, - }); + DateBatchLoader({required this.query, required this.batchSize}); Future getDate(int index) async { if (!_isIndexInBuffer(index)) { diff --git a/mobile/lib/widgets/asset_grid/control_bottom_app_bar.dart b/mobile/lib/widgets/asset_grid/control_bottom_app_bar.dart index e265162850..b1e54af62a 100644 --- a/mobile/lib/widgets/asset_grid/control_bottom_app_bar.dart +++ b/mobile/lib/widgets/asset_grid/control_bottom_app_bar.dart @@ -81,43 +81,26 @@ class ControlBottomAppBar extends HookConsumerWidget { final isInLockedView = ref.watch(inLockedViewProvider); void minimize() { - scrollController.animateTo( - bottomPadding, - duration: const Duration(milliseconds: 300), - curve: Curves.easeOut, - ); + scrollController.animateTo(bottomPadding, duration: const Duration(milliseconds: 300), curve: Curves.easeOut); } - useEffect( - () { - controlBottomAppBarNotifier.addListener(minimize); - return () { - controlBottomAppBarNotifier.removeListener(minimize); - }; - }, - [], - ); + useEffect(() { + controlBottomAppBarNotifier.addListener(minimize); + return () { + controlBottomAppBarNotifier.removeListener(minimize); + }; + }, []); - void showForceDeleteDialog( - Function(bool) deleteCb, { - String? alertMsg, - }) { + void showForceDeleteDialog(Function(bool) deleteCb, {String? alertMsg}) { showDialog( context: context, builder: (BuildContext context) { - return DeleteDialog( - alert: alertMsg, - onDelete: () => deleteCb(true), - ); + return DeleteDialog(alert: alertMsg, onDelete: () => deleteCb(true)); }, ); } - void handleRemoteDelete( - bool force, - Function(bool) deleteCb, { - String? alertMsg, - }) { + void handleRemoteDelete(bool force, Function(bool) deleteCb, {String? alertMsg}) { if (!force) { deleteCb(force); return; @@ -153,11 +136,7 @@ class ControlBottomAppBar extends HookConsumerWidget { if (hasRemote && onDownload != null) ConstrainedBox( constraints: const BoxConstraints(maxWidth: 90), - child: ControlBoxButton( - iconData: Icons.download, - label: "download".tr(), - onPressed: onDownload, - ), + child: ControlBoxButton(iconData: Icons.download, label: "download".tr(), onPressed: onDownload), ), if (hasLocal && hasRemote && onDelete != null && !isInLockedView) ConstrainedBox( @@ -178,17 +157,10 @@ class ControlBottomAppBar extends HookConsumerWidget { ? "control_bottom_app_bar_trash_from_immich".tr() : "control_bottom_app_bar_delete_from_immich".tr(), onPressed: enabled - ? () => handleRemoteDelete( - !trashEnabled, - onDeleteServer!, - alertMsg: "delete_dialog_alert_remote", - ) + ? () => handleRemoteDelete(!trashEnabled, onDeleteServer!, alertMsg: "delete_dialog_alert_remote") : null, onLongPressed: enabled - ? () => showForceDeleteDialog( - onDeleteServer!, - alertMsg: "delete_dialog_alert_remote", - ) + ? () => showForceDeleteDialog(onDeleteServer!, alertMsg: "delete_dialog_alert_remote") : null, ), ), @@ -199,10 +171,7 @@ class ControlBottomAppBar extends HookConsumerWidget { iconData: Icons.delete_forever, label: "delete_dialog_title".tr(), onPressed: enabled - ? () => showForceDeleteDialog( - onDeleteServer!, - alertMsg: "delete_dialog_alert_remote", - ) + ? () => showForceDeleteDialog(onDeleteServer!, alertMsg: "delete_dialog_alert_remote") : null, ), ), @@ -221,9 +190,7 @@ class ControlBottomAppBar extends HookConsumerWidget { showDialog( context: context, builder: (BuildContext context) { - return DeleteLocalOnlyDialog( - onDeleteLocal: onDeleteLocal!, - ); + return DeleteLocalOnlyDialog(onDeleteLocal: onDeleteLocal!); }, ); } @@ -281,13 +248,11 @@ class ControlBottomAppBar extends HookConsumerWidget { label: "upload".tr(), onPressed: enabled ? () => showDialog( - context: context, - builder: (BuildContext context) { - return UploadDialog( - onUpload: onUpload, - ); - }, - ) + context: context, + builder: (BuildContext context) { + return UploadDialog(onUpload: onUpload); + }, + ) : null, ), ]; @@ -325,10 +290,7 @@ class ControlBottomAppBar extends HookConsumerWidget { surfaceTintColor: context.colorScheme.surfaceContainerHigh, elevation: 6.0, shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(12), - topRight: Radius.circular(12), - ), + borderRadius: BorderRadius.only(topLeft: Radius.circular(12), topRight: Radius.circular(12)), ), margin: const EdgeInsets.all(0), child: CustomScrollView( @@ -349,14 +311,8 @@ class ControlBottomAppBar extends HookConsumerWidget { ), ), if (hasRemote && !isInLockedView) ...[ - const Divider( - indent: 16, - endIndent: 16, - thickness: 1, - ), - _AddToAlbumTitleRow( - onCreateNewAlbum: enabled ? onCreateNewAlbum : null, - ), + const Divider(indent: 16, endIndent: 16, thickness: 1), + _AddToAlbumTitleRow(onCreateNewAlbum: enabled ? onCreateNewAlbum : null), ], ], ), @@ -380,9 +336,7 @@ class ControlBottomAppBar extends HookConsumerWidget { } class _AddToAlbumTitleRow extends StatelessWidget { - const _AddToAlbumTitleRow({ - required this.onCreateNewAlbum, - }); + const _AddToAlbumTitleRow({required this.onCreateNewAlbum}); final VoidCallback? onCreateNewAlbum; @@ -393,23 +347,13 @@ class _AddToAlbumTitleRow extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - "add_to_album", - style: context.textTheme.titleSmall, - ).tr(), + Text("add_to_album", style: context.textTheme.titleSmall).tr(), TextButton.icon( onPressed: onCreateNewAlbum, - icon: Icon( - Icons.add, - color: context.primaryColor, - ), + icon: Icon(Icons.add, color: context.primaryColor), label: Text( "common_create_new_album", - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.bold, - fontSize: 14, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold, fontSize: 14), ).tr(), ), ], diff --git a/mobile/lib/widgets/asset_grid/delete_dialog.dart b/mobile/lib/widgets/asset_grid/delete_dialog.dart index ecfb4130dc..e7c7775e54 100644 --- a/mobile/lib/widgets/asset_grid/delete_dialog.dart +++ b/mobile/lib/widgets/asset_grid/delete_dialog.dart @@ -5,22 +5,19 @@ import 'package:immich_mobile/widgets/common/confirm_dialog.dart'; class DeleteDialog extends ConfirmDialog { const DeleteDialog({super.key, String? alert, required Function onDelete}) - : super( - title: "delete_dialog_title", - content: alert ?? "delete_dialog_alert", - cancel: "cancel", - ok: "delete", - onOk: onDelete, - ); + : super( + title: "delete_dialog_title", + content: alert ?? "delete_dialog_alert", + cancel: "cancel", + ok: "delete", + onOk: onDelete, + ); } class DeleteLocalOnlyDialog extends StatelessWidget { final void Function(bool onlyMerged) onDeleteLocal; - const DeleteLocalOnlyDialog({ - super.key, - required this.onDeleteLocal, - }); + const DeleteLocalOnlyDialog({super.key, required this.onDeleteLocal}); @override Widget build(BuildContext context) { @@ -35,9 +32,7 @@ class DeleteLocalOnlyDialog extends StatelessWidget { } return AlertDialog( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), title: const Text("delete_dialog_title").tr(), content: const Text("delete_dialog_alert_local_non_backed_up").tr(), actions: [ @@ -45,30 +40,21 @@ class DeleteLocalOnlyDialog extends StatelessWidget { onPressed: () => context.pop(), child: Text( "cancel", - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold), ).tr(), ), TextButton( onPressed: onDeleteBackedUpOnly, child: Text( "delete_local_dialog_ok_backed_up_only", - style: TextStyle( - color: context.colorScheme.tertiary, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.colorScheme.tertiary, fontWeight: FontWeight.bold), ).tr(), ), TextButton( onPressed: onForceDelete, child: Text( "delete_local_dialog_ok_force", - style: TextStyle( - color: Colors.red[400], - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: Colors.red[400], fontWeight: FontWeight.bold), ).tr(), ), ], diff --git a/mobile/lib/widgets/asset_grid/disable_multi_select_button.dart b/mobile/lib/widgets/asset_grid/disable_multi_select_button.dart index 50b38c2a4a..93a1d53f4e 100644 --- a/mobile/lib/widgets/asset_grid/disable_multi_select_button.dart +++ b/mobile/lib/widgets/asset_grid/disable_multi_select_button.dart @@ -3,11 +3,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; class DisableMultiSelectButton extends ConsumerWidget { - const DisableMultiSelectButton({ - super.key, - required this.onPressed, - required this.selectedItemCount, - }); + const DisableMultiSelectButton({super.key, required this.onPressed, required this.selectedItemCount}); final Function onPressed; final int selectedItemCount; @@ -22,16 +18,10 @@ class DisableMultiSelectButton extends ConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 4.0), child: ElevatedButton.icon( onPressed: () => onPressed(), - icon: Icon( - Icons.close_rounded, - color: context.colorScheme.onPrimary, - ), + icon: Icon(Icons.close_rounded, color: context.colorScheme.onPrimary), label: Text( '$selectedItemCount', - style: context.textTheme.titleMedium?.copyWith( - height: 2.5, - color: context.colorScheme.onPrimary, - ), + style: context.textTheme.titleMedium?.copyWith(height: 2.5, color: context.colorScheme.onPrimary), ), ), ), diff --git a/mobile/lib/widgets/asset_grid/draggable_scrollbar.dart b/mobile/lib/widgets/asset_grid/draggable_scrollbar.dart index ffe8c54320..3de52c2816 100644 --- a/mobile/lib/widgets/asset_grid/draggable_scrollbar.dart +++ b/mobile/lib/widgets/asset_grid/draggable_scrollbar.dart @@ -3,14 +3,15 @@ import 'dart:async'; import 'package:flutter/material.dart'; /// Build the Scroll Thumb and label using the current configuration -typedef ScrollThumbBuilder = Widget Function( - Color backgroundColor, - Animation thumbAnimation, - Animation labelAnimation, - double height, { - Text? labelText, - BoxConstraints? labelConstraints, -}); +typedef ScrollThumbBuilder = + Widget Function( + Color backgroundColor, + Animation thumbAnimation, + Animation labelAnimation, + double height, { + Text? labelText, + BoxConstraints? labelConstraints, + }); /// Build a Text widget using the current scroll offset typedef LabelTextBuilder = Text Function(double offsetY); @@ -79,8 +80,8 @@ class DraggableScrollbar extends StatefulWidget { this.scrollbarTimeToFade = const Duration(milliseconds: 600), this.labelTextBuilder, this.labelConstraints, - }) : assert(child.scrollDirection == Axis.vertical), - scrollThumbBuilder = _thumbRRectBuilder(alwaysVisibleScrollThumb); + }) : assert(child.scrollDirection == Axis.vertical), + scrollThumbBuilder = _thumbRRectBuilder(alwaysVisibleScrollThumb); DraggableScrollbar.arrows({ super.key, @@ -95,8 +96,8 @@ class DraggableScrollbar extends StatefulWidget { this.scrollbarTimeToFade = const Duration(milliseconds: 600), this.labelTextBuilder, this.labelConstraints, - }) : assert(child.scrollDirection == Axis.vertical), - scrollThumbBuilder = _thumbArrowBuilder(alwaysVisibleScrollThumb); + }) : assert(child.scrollDirection == Axis.vertical), + scrollThumbBuilder = _thumbArrowBuilder(alwaysVisibleScrollThumb); DraggableScrollbar.semicircle({ super.key, @@ -111,12 +112,8 @@ class DraggableScrollbar extends StatefulWidget { this.scrollbarTimeToFade = const Duration(milliseconds: 600), this.labelTextBuilder, this.labelConstraints, - }) : assert(child.scrollDirection == Axis.vertical), - scrollThumbBuilder = _thumbSemicircleBuilder( - heightScrollThumb * 0.6, - scrollThumbKey, - alwaysVisibleScrollThumb, - ); + }) : assert(child.scrollDirection == Axis.vertical), + scrollThumbBuilder = _thumbSemicircleBuilder(heightScrollThumb * 0.6, scrollThumbKey, alwaysVisibleScrollThumb); @override DraggableScrollbarState createState() => DraggableScrollbarState(); @@ -149,17 +146,10 @@ class DraggableScrollbar extends StatefulWidget { if (alwaysVisibleScrollThumb) { return scrollThumbAndLabel; } - return SlideFadeTransition( - animation: thumbAnimation!, - child: scrollThumbAndLabel, - ); + return SlideFadeTransition(animation: thumbAnimation!, child: scrollThumbAndLabel); } - static ScrollThumbBuilder _thumbSemicircleBuilder( - double width, - Key? scrollThumbKey, - bool alwaysVisibleScrollThumb, - ) { + static ScrollThumbBuilder _thumbSemicircleBuilder(double width, Key? scrollThumbKey, bool alwaysVisibleScrollThumb) { return ( Color backgroundColor, Animation thumbAnimation, @@ -180,9 +170,7 @@ class DraggableScrollbar extends StatefulWidget { topRight: const Radius.circular(4.0), bottomRight: const Radius.circular(4.0), ), - child: Container( - constraints: BoxConstraints.tight(Size(width, height)), - ), + child: Container(constraints: BoxConstraints.tight(Size(width, height))), ), ); @@ -198,9 +186,7 @@ class DraggableScrollbar extends StatefulWidget { }; } - static ScrollThumbBuilder _thumbArrowBuilder( - bool alwaysVisibleScrollThumb, - ) { + static ScrollThumbBuilder _thumbArrowBuilder(bool alwaysVisibleScrollThumb) { return ( Color backgroundColor, Animation thumbAnimation, @@ -216,9 +202,7 @@ class DraggableScrollbar extends StatefulWidget { width: 20.0, decoration: BoxDecoration( color: backgroundColor, - borderRadius: const BorderRadius.all( - Radius.circular(12.0), - ), + borderRadius: const BorderRadius.all(Radius.circular(12.0)), ), ), ); @@ -235,9 +219,7 @@ class DraggableScrollbar extends StatefulWidget { }; } - static ScrollThumbBuilder _thumbRRectBuilder( - bool alwaysVisibleScrollThumb, - ) { + static ScrollThumbBuilder _thumbRRectBuilder(bool alwaysVisibleScrollThumb) { return ( Color backgroundColor, Animation thumbAnimation, @@ -250,11 +232,7 @@ class DraggableScrollbar extends StatefulWidget { elevation: 4.0, color: backgroundColor, borderRadius: const BorderRadius.all(Radius.circular(7.0)), - child: Container( - constraints: BoxConstraints.tight( - Size(16.0, height), - ), - ), + child: Container(constraints: BoxConstraints.tight(Size(16.0, height))), ); return buildScrollThumbAndLabel( @@ -296,11 +274,7 @@ class ScrollLabel extends StatelessWidget { elevation: 4.0, color: backgroundColor, borderRadius: const BorderRadius.all(Radius.circular(16.0)), - child: Container( - constraints: constraints ?? _defaultConstraints, - alignment: Alignment.center, - child: child, - ), + child: Container(constraints: constraints ?? _defaultConstraints, alignment: Alignment.center, child: child), ), ), ); @@ -325,25 +299,13 @@ class DraggableScrollbarState extends State with TickerProvi _viewOffset = 0.0; _isDragInProcess = false; - _thumbAnimationController = AnimationController( - vsync: this, - duration: widget.scrollbarAnimationDuration, - ); + _thumbAnimationController = AnimationController(vsync: this, duration: widget.scrollbarAnimationDuration); - _thumbAnimation = CurvedAnimation( - parent: _thumbAnimationController, - curve: Curves.fastOutSlowIn, - ); + _thumbAnimation = CurvedAnimation(parent: _thumbAnimationController, curve: Curves.fastOutSlowIn); - _labelAnimationController = AnimationController( - vsync: this, - duration: widget.scrollbarAnimationDuration, - ); + _labelAnimationController = AnimationController(vsync: this, duration: widget.scrollbarAnimationDuration); - _labelAnimation = CurvedAnimation( - parent: _labelAnimationController, - curve: Curves.fastOutSlowIn, - ); + _labelAnimation = CurvedAnimation(parent: _labelAnimationController, curve: Curves.fastOutSlowIn); } @override @@ -366,9 +328,7 @@ class DraggableScrollbarState extends State with TickerProvi Widget build(BuildContext context) { Text? labelText; if (widget.labelTextBuilder != null && _isDragInProcess) { - labelText = widget.labelTextBuilder!( - _viewOffset + _barOffset + widget.heightScrollThumb / 2, - ); + labelText = widget.labelTextBuilder!(_viewOffset + _barOffset + widget.heightScrollThumb / 2); } return LayoutBuilder( @@ -382,9 +342,7 @@ class DraggableScrollbarState extends State with TickerProvi }, child: Stack( children: [ - RepaintBoundary( - child: widget.child, - ), + RepaintBoundary(child: widget.child), RepaintBoundary( child: GestureDetector( onVerticalDragStart: _onVerticalDragStart, @@ -422,11 +380,7 @@ class DraggableScrollbarState extends State with TickerProvi setState(() { if (notification is ScrollUpdateNotification) { - _barOffset += getBarDelta( - notification.scrollDelta!, - barMaxScrollExtent, - viewMaxScrollExtent, - ); + _barOffset += getBarDelta(notification.scrollDelta!, barMaxScrollExtent, viewMaxScrollExtent); if (_barOffset < barMinScrollExtent) { _barOffset = barMinScrollExtent; @@ -459,19 +413,11 @@ class DraggableScrollbarState extends State with TickerProvi }); } - double getBarDelta( - double scrollViewDelta, - double barMaxScrollExtent, - double viewMaxScrollExtent, - ) { + double getBarDelta(double scrollViewDelta, double barMaxScrollExtent, double viewMaxScrollExtent) { return scrollViewDelta * barMaxScrollExtent / viewMaxScrollExtent; } - double getScrollViewDelta( - double barDelta, - double barMaxScrollExtent, - double viewMaxScrollExtent, - ) { + double getScrollViewDelta(double barDelta, double barMaxScrollExtent, double viewMaxScrollExtent) { return barDelta * viewMaxScrollExtent / barMaxScrollExtent; } @@ -498,11 +444,7 @@ class DraggableScrollbarState extends State with TickerProvi _barOffset = barMaxScrollExtent; } - double viewDelta = getScrollViewDelta( - details.delta.dy, - barMaxScrollExtent, - viewMaxScrollExtent, - ); + double viewDelta = getScrollViewDelta(details.delta.dy, barMaxScrollExtent, viewMaxScrollExtent); _viewOffset = widget.controller.position.pixels + viewDelta; if (_viewOffset < widget.controller.position.minScrollExtent) { @@ -545,14 +487,8 @@ class ArrowCustomPainter extends CustomPainter { final baseX = size.width / 2; final baseY = size.height / 2; - canvas.drawPath( - _trianglePath(Offset(baseX, baseY - 2.0), width, height, true), - paint, - ); - canvas.drawPath( - _trianglePath(Offset(baseX, baseY + 2.0), width, height, false), - paint, - ); + canvas.drawPath(_trianglePath(Offset(baseX, baseY - 2.0), width, height, true), paint); + canvas.drawPath(_trianglePath(Offset(baseX, baseY + 2.0), width, height, false), paint); } static Path _trianglePath(Offset o, double width, double height, bool isUp) { @@ -583,10 +519,7 @@ class ArrowClipper extends CustomClipper { path.lineTo(startPointX + arrowWidth / 2, startPointY - arrowWidth / 2); path.lineTo(startPointX + arrowWidth, startPointY); path.lineTo(startPointX + arrowWidth, startPointY + 1.0); - path.lineTo( - startPointX + arrowWidth / 2, - startPointY - arrowWidth / 2 + 1.0, - ); + path.lineTo(startPointX + arrowWidth / 2, startPointY - arrowWidth / 2 + 1.0); path.lineTo(startPointX, startPointY + 1.0); path.close(); @@ -595,10 +528,7 @@ class ArrowClipper extends CustomClipper { path.lineTo(startPointX + arrowWidth / 2, startPointY + arrowWidth / 2); path.lineTo(startPointX, startPointY); path.lineTo(startPointX, startPointY - 1.0); - path.lineTo( - startPointX + arrowWidth / 2, - startPointY + arrowWidth / 2 - 1.0, - ); + path.lineTo(startPointX + arrowWidth / 2, startPointY + arrowWidth / 2 - 1.0); path.lineTo(startPointX + arrowWidth, startPointY - 1.0); path.close(); @@ -613,11 +543,7 @@ class SlideFadeTransition extends StatelessWidget { final Animation animation; final Widget child; - const SlideFadeTransition({ - super.key, - required this.animation, - required this.child, - }); + const SlideFadeTransition({super.key, required this.animation, required this.child}); @override Widget build(BuildContext context) { @@ -625,14 +551,8 @@ class SlideFadeTransition extends StatelessWidget { animation: animation, builder: (context, child) => animation.value == 0.0 ? const SizedBox() : child!, child: SlideTransition( - position: Tween( - begin: const Offset(0.3, 0.0), - end: const Offset(0.0, 0.0), - ).animate(animation), - child: FadeTransition( - opacity: animation, - child: child, - ), + position: Tween(begin: const Offset(0.3, 0.0), end: const Offset(0.0, 0.0)).animate(animation), + child: FadeTransition(opacity: animation, child: child), ), ); } diff --git a/mobile/lib/widgets/asset_grid/draggable_scrollbar_custom.dart b/mobile/lib/widgets/asset_grid/draggable_scrollbar_custom.dart index 63fa3be763..17f35311f0 100644 --- a/mobile/lib/widgets/asset_grid/draggable_scrollbar_custom.dart +++ b/mobile/lib/widgets/asset_grid/draggable_scrollbar_custom.dart @@ -4,14 +4,15 @@ import 'package:flutter/material.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; /// Build the Scroll Thumb and label using the current configuration -typedef ScrollThumbBuilder = Widget Function( - Color backgroundColor, - Animation thumbAnimation, - Animation labelAnimation, - double height, { - Text? labelText, - BoxConstraints? labelConstraints, -}); +typedef ScrollThumbBuilder = + Widget Function( + Color backgroundColor, + Animation thumbAnimation, + Animation labelAnimation, + double height, { + Text? labelText, + BoxConstraints? labelConstraints, + }); /// Build a Text widget using the current scroll offset typedef LabelTextBuilder = Text Function(int item); @@ -75,12 +76,8 @@ class DraggableScrollbar extends StatefulWidget { this.scrollbarTimeToFade = const Duration(milliseconds: 600), this.labelTextBuilder, this.labelConstraints, - }) : assert(child.scrollDirection == Axis.vertical), - scrollThumbBuilder = _thumbSemicircleBuilder( - heightScrollThumb * 0.6, - scrollThumbKey, - alwaysVisibleScrollThumb, - ); + }) : assert(child.scrollDirection == Axis.vertical), + scrollThumbBuilder = _thumbSemicircleBuilder(heightScrollThumb * 0.6, scrollThumbKey, alwaysVisibleScrollThumb); @override DraggableScrollbarState createState() => DraggableScrollbarState(); @@ -113,17 +110,10 @@ class DraggableScrollbar extends StatefulWidget { if (alwaysVisibleScrollThumb) { return scrollThumbAndLabel; } - return SlideFadeTransition( - animation: thumbAnimation!, - child: scrollThumbAndLabel, - ); + return SlideFadeTransition(animation: thumbAnimation!, child: scrollThumbAndLabel); } - static ScrollThumbBuilder _thumbSemicircleBuilder( - double width, - Key? scrollThumbKey, - bool alwaysVisibleScrollThumb, - ) { + static ScrollThumbBuilder _thumbSemicircleBuilder(double width, Key? scrollThumbKey, bool alwaysVisibleScrollThumb) { return ( Color backgroundColor, Animation thumbAnimation, @@ -144,9 +134,7 @@ class DraggableScrollbar extends StatefulWidget { topRight: const Radius.circular(4.0), bottomRight: const Radius.circular(4.0), ), - child: Container( - constraints: BoxConstraints.tight(Size(width, height)), - ), + child: Container(constraints: BoxConstraints.tight(Size(width, height))), ), ); @@ -219,25 +207,13 @@ class DraggableScrollbarState extends State with TickerProvi _isDragInProcess = false; _currentItem = 0; - _thumbAnimationController = AnimationController( - vsync: this, - duration: widget.scrollbarAnimationDuration, - ); + _thumbAnimationController = AnimationController(vsync: this, duration: widget.scrollbarAnimationDuration); - _thumbAnimation = CurvedAnimation( - parent: _thumbAnimationController, - curve: Curves.fastOutSlowIn, - ); + _thumbAnimation = CurvedAnimation(parent: _thumbAnimationController, curve: Curves.fastOutSlowIn); - _labelAnimationController = AnimationController( - vsync: this, - duration: widget.scrollbarAnimationDuration, - ); + _labelAnimationController = AnimationController(vsync: this, duration: widget.scrollbarAnimationDuration); - _labelAnimation = CurvedAnimation( - parent: _labelAnimationController, - curve: Curves.fastOutSlowIn, - ); + _labelAnimation = CurvedAnimation(parent: _labelAnimationController, curve: Curves.fastOutSlowIn); } @override @@ -272,9 +248,7 @@ class DraggableScrollbarState extends State with TickerProvi }, child: Stack( children: [ - RepaintBoundary( - child: widget.child, - ), + RepaintBoundary(child: widget.child), RepaintBoundary( child: GestureDetector( onVerticalDragStart: _onVerticalDragStart, @@ -370,16 +344,12 @@ class DraggableScrollbarState extends State with TickerProvi /// If the bar is at the bottom but the item position is still smaller than the max item count (due to rounding error) /// jump to the end of the list if (barMaxScrollExtent - _barOffset < 10 && itemPosition < maxItemCount) { - widget.controller.jumpTo( - index: maxItemCount, - ); + widget.controller.jumpTo(index: maxItemCount); return; } - widget.controller.jumpTo( - index: itemPosition, - ); + widget.controller.jumpTo(index: itemPosition); } Timer? dragHaltTimer; @@ -405,12 +375,9 @@ class DraggableScrollbarState extends State with TickerProvi dragHaltTimer?.cancel(); widget.scrollStateListener(true); - dragHaltTimer = Timer( - const Duration(milliseconds: 500), - () { - widget.scrollStateListener(false); - }, - ); + dragHaltTimer = Timer(const Duration(milliseconds: 500), () { + widget.scrollStateListener(false); + }); } _jumpToBarPosition(); @@ -451,14 +418,8 @@ class ArrowCustomPainter extends CustomPainter { final baseX = size.width / 2; final baseY = size.height / 2; - canvas.drawPath( - _trianglePath(Offset(baseX, baseY - 2.0), width, height, true), - paint, - ); - canvas.drawPath( - _trianglePath(Offset(baseX, baseY + 2.0), width, height, false), - paint, - ); + canvas.drawPath(_trianglePath(Offset(baseX, baseY - 2.0), width, height, true), paint); + canvas.drawPath(_trianglePath(Offset(baseX, baseY + 2.0), width, height, false), paint); } static Path _trianglePath(Offset o, double width, double height, bool isUp) { @@ -489,10 +450,7 @@ class ArrowClipper extends CustomClipper { path.lineTo(startPointX + arrowWidth / 2, startPointY - arrowWidth / 2); path.lineTo(startPointX + arrowWidth, startPointY); path.lineTo(startPointX + arrowWidth, startPointY + 1.0); - path.lineTo( - startPointX + arrowWidth / 2, - startPointY - arrowWidth / 2 + 1.0, - ); + path.lineTo(startPointX + arrowWidth / 2, startPointY - arrowWidth / 2 + 1.0); path.lineTo(startPointX, startPointY + 1.0); path.close(); @@ -501,10 +459,7 @@ class ArrowClipper extends CustomClipper { path.lineTo(startPointX + arrowWidth / 2, startPointY + arrowWidth / 2); path.lineTo(startPointX, startPointY); path.lineTo(startPointX, startPointY - 1.0); - path.lineTo( - startPointX + arrowWidth / 2, - startPointY + arrowWidth / 2 - 1.0, - ); + path.lineTo(startPointX + arrowWidth / 2, startPointY + arrowWidth / 2 - 1.0); path.lineTo(startPointX + arrowWidth, startPointY - 1.0); path.close(); @@ -519,11 +474,7 @@ class SlideFadeTransition extends StatelessWidget { final Animation animation; final Widget child; - const SlideFadeTransition({ - super.key, - required this.animation, - required this.child, - }); + const SlideFadeTransition({super.key, required this.animation, required this.child}); @override Widget build(BuildContext context) { @@ -531,14 +482,8 @@ class SlideFadeTransition extends StatelessWidget { animation: animation, builder: (context, child) => animation.value == 0.0 ? const SizedBox() : child!, child: SlideTransition( - position: Tween( - begin: const Offset(0.3, 0.0), - end: const Offset(0.0, 0.0), - ).animate(animation), - child: FadeTransition( - opacity: animation, - child: child, - ), + position: Tween(begin: const Offset(0.3, 0.0), end: const Offset(0.0, 0.0)).animate(animation), + child: FadeTransition(opacity: animation, child: child), ), ); } diff --git a/mobile/lib/widgets/asset_grid/group_divider_title.dart b/mobile/lib/widgets/asset_grid/group_divider_title.dart index 81f9392d37..1464c941f0 100644 --- a/mobile/lib/widgets/asset_grid/group_divider_title.dart +++ b/mobile/lib/widgets/asset_grid/group_divider_title.dart @@ -30,13 +30,10 @@ class GroupDividerTitle extends HookConsumerWidget { final appSettingService = ref.watch(appSettingsServiceProvider); final groupBy = useState(GroupAssetsBy.day); - useEffect( - () { - groupBy.value = GroupAssetsBy.values[appSettingService.getSetting(AppSettingsEnum.groupAssetsBy)]; - return null; - }, - [], - ); + useEffect(() { + groupBy.value = GroupAssetsBy.values[appSettingService.getSetting(AppSettingsEnum.groupAssetsBy)]; + return null; + }, []); void handleTitleIconClick() { ref.read(hapticFeedbackProvider.notifier).heavyImpact(); @@ -59,9 +56,7 @@ class GroupDividerTitle extends HookConsumerWidget { Text( text, style: groupBy.value == GroupAssetsBy.month - ? context.textTheme.bodyLarge?.copyWith( - fontSize: 24.0, - ) + ? context.textTheme.bodyLarge?.copyWith(fontSize: 24.0) : context.textTheme.labelLarge?.copyWith( color: context.textTheme.labelLarge?.color?.withAlpha(250), fontWeight: FontWeight.w500, diff --git a/mobile/lib/widgets/asset_grid/immich_asset_grid.dart b/mobile/lib/widgets/asset_grid/immich_asset_grid.dart index 112d8074ad..ab6b350a7b 100644 --- a/mobile/lib/widgets/asset_grid/immich_asset_grid.dart +++ b/mobile/lib/widgets/asset_grid/immich_asset_grid.dart @@ -60,9 +60,7 @@ class ImmichAssetGrid extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { var settings = ref.watch(appSettingsServiceProvider); - final perRow = useState( - assetsPerRow ?? settings.getSetting(AppSettingsEnum.tilesPerRow)!, - ); + final perRow = useState(assetsPerRow ?? settings.getSetting(AppSettingsEnum.tilesPerRow)!); final scaleFactor = useState(7.0 - perRow.value); final baseScaleFactor = useState(7.0 - perRow.value); @@ -82,22 +80,21 @@ class ImmichAssetGrid extends HookConsumerWidget { return RawGestureDetector( gestures: { CustomScaleGestureRecognizer: GestureRecognizerFactoryWithHandlers( - () => CustomScaleGestureRecognizer(), (CustomScaleGestureRecognizer scale) { - scale.onStart = (details) { - baseScaleFactor.value = scaleFactor.value; - }; + () => CustomScaleGestureRecognizer(), + (CustomScaleGestureRecognizer scale) { + scale.onStart = (details) { + baseScaleFactor.value = scaleFactor.value; + }; - scale.onUpdate = (details) { - scaleFactor.value = max( - min(5.0, baseScaleFactor.value * details.scale), - 1.0, - ); - if (7 - scaleFactor.value.toInt() != perRow.value) { - perRow.value = 7 - scaleFactor.value.toInt(); - settings.setSetting(AppSettingsEnum.tilesPerRow, perRow.value); - } - }; - }), + scale.onUpdate = (details) { + scaleFactor.value = max(min(5.0, baseScaleFactor.value * details.scale), 1.0); + if (7 - scaleFactor.value.toInt() != perRow.value) { + perRow.value = 7 - scaleFactor.value.toInt(); + settings.setSetting(AppSettingsEnum.tilesPerRow, perRow.value); + } + }; + }, + ), }, child: ImmichAssetGridView( onRefresh: onRefresh, @@ -125,9 +122,7 @@ class ImmichAssetGrid extends HookConsumerWidget { if (renderList != null) return buildAssetGridView(renderList!); final renderListFuture = ref.watch(assetsTimelineProvider(assets!)); - return renderListFuture.widgetWhen( - onData: (renderList) => buildAssetGridView(renderList), - ); + return renderListFuture.widgetWhen(onData: (renderList) => buildAssetGridView(renderList)); } } diff --git a/mobile/lib/widgets/asset_grid/immich_asset_grid_view.dart b/mobile/lib/widgets/asset_grid/immich_asset_grid_view.dart index ccea8307ae..7db03a33aa 100644 --- a/mobile/lib/widgets/asset_grid/immich_asset_grid_view.dart +++ b/mobile/lib/widgets/asset_grid/immich_asset_grid_view.dart @@ -34,10 +34,7 @@ import 'disable_multi_select_button.dart'; import 'draggable_scrollbar_custom.dart'; import 'group_divider_title.dart'; -typedef ImmichAssetGridSelectionListener = void Function( - bool, - Set, -); +typedef ImmichAssetGridSelectionListener = void Function(bool, Set); class ImmichAssetGridView extends ConsumerStatefulWidget { final RenderList renderList; @@ -161,16 +158,10 @@ class ImmichAssetGridViewState extends ConsumerState { // the scroll_position widget crashes. This is a workaround to prevent this. // If the index is within the last 10 elements, we jump instead of scrolling. if (widget.renderList.elements.length <= index + 10) { - _itemScrollController.jumpTo( - index: index, - ); + _itemScrollController.jumpTo(index: index); return; } - await _itemScrollController.scrollTo( - index: index, - alignment: 0, - duration: const Duration(milliseconds: 500), - ); + await _itemScrollController.scrollTo(index: index, alignment: 0, duration: const Duration(milliseconds: 500)); } Widget _itemBuilder(BuildContext c, int position) { @@ -219,18 +210,12 @@ class ImmichAssetGridViewState extends ConsumerState { return Text( DateFormat.yMMMM().format(date), - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold), ); } Widget _buildMultiSelectIndicator() { - return DisableMultiSelectButton( - onPressed: () => _deselectAll(), - selectedItemCount: _selectedAssets.length, - ); + return DisableMultiSelectButton(onPressed: () => _deselectAll(), selectedItemCount: _selectedAssets.length); } Widget _buildAssetGrid() { @@ -250,10 +235,7 @@ class ImmichAssetGridViewState extends ConsumerState { } final listWidget = ScrollablePositionedList.builder( - padding: EdgeInsets.only( - top: appBarOffset() ? 60 : 0, - bottom: 220, - ), + padding: EdgeInsets.only(top: appBarOffset() ? 60 : 0, bottom: 220), itemBuilder: _itemBuilder, itemPositionsListener: _itemPositionsListener, physics: _scrollPhysics, @@ -269,8 +251,9 @@ class ImmichAssetGridViewState extends ConsumerState { scrollStateListener: dragScrolling, itemPositionsListener: _itemPositionsListener, controller: _itemScrollController, - backgroundColor: - context.isDarkTheme ? context.colorScheme.primary.darken(amount: .5) : context.colorScheme.primary, + backgroundColor: context.isDarkTheme + ? context.colorScheme.primary.darken(amount: .5) + : context.colorScheme.primary, labelTextBuilder: widget.showLabel ? _labelBuilder : null, padding: appBarOffset() ? const EdgeInsets.only(top: 60) : const EdgeInsets.only(), heightOffset: appBarOffset() ? 60 : 0, @@ -284,12 +267,8 @@ class ImmichAssetGridViewState extends ConsumerState { return widget.onRefresh == null ? child : appBarOffset() - ? RefreshIndicator( - onRefresh: widget.onRefresh!, - edgeOffset: 30, - child: child, - ) - : RefreshIndicator(onRefresh: widget.onRefresh!, child: child); + ? RefreshIndicator(onRefresh: widget.onRefresh!, edgeOffset: 30, child: child) + : RefreshIndicator(onRefresh: widget.onRefresh!, child: child); } void _scrollToDate() { @@ -312,9 +291,7 @@ class ImmichAssetGridViewState extends ConsumerState { // If the exact date is not found, the timeline is grouped by month, // thus we search for the month if (index == -1) { - index = widget.renderList.elements.indexWhere( - (e) => e.date.year == date.year && e.date.month == date.month, - ); + index = widget.renderList.elements.indexWhere((e) => e.date.year == date.year && e.date.month == date.month); } if (index < widget.renderList.elements.length) { @@ -411,13 +388,8 @@ class ImmichAssetGridViewState extends ConsumerState { void _scrollToTop() { // for some reason, this is necessary as well in order // to correctly reposition the drag thumb scroll bar - _itemScrollController.jumpTo( - index: 0, - ); - _itemScrollController.scrollTo( - index: 0, - duration: const Duration(milliseconds: 200), - ); + _itemScrollController.jumpTo(index: 0); + _itemScrollController.scrollTo(index: 0, duration: const Duration(milliseconds: 200)); } void _setDragStartIndex(AssetIndex index) { @@ -495,9 +467,7 @@ class ImmichAssetGridViewState extends ConsumerState { final sectionAssets = widget.renderList.loadAssets(section.offset, section.count); if (currentSectionIndex == startSectionIndex) { - selectedAssets.addAll( - sectionAssets.slice(startSectionAssetIndex, sectionAssets.length), - ); + selectedAssets.addAll(sectionAssets.slice(startSectionAssetIndex, sectionAssets.length)); } else { selectedAssets.addAll(sectionAssets); } @@ -509,13 +479,9 @@ class ImmichAssetGridViewState extends ConsumerState { if (section != null) { final sectionAssets = widget.renderList.loadAssets(section.offset, section.count); if (startSectionIndex == endSectionIndex) { - selectedAssets.addAll( - sectionAssets.slice(startSectionAssetIndex, endSectionAssetIndex + 1), - ); + selectedAssets.addAll(sectionAssets.slice(startSectionAssetIndex, endSectionAssetIndex + 1)); } else { - selectedAssets.addAll( - sectionAssets.slice(0, endSectionAssetIndex + 1), - ); + selectedAssets.addAll(sectionAssets.slice(0, endSectionAssetIndex + 1)); } } @@ -554,9 +520,8 @@ class ImmichAssetGridViewState extends ConsumerState { onAssetEnter: _handleDragAssetEnter, onEnd: _stopDrag, onScroll: _dragDragScroll, - onScrollStart: () => WidgetsBinding.instance.addPostFrameCallback( - (_) => controlBottomAppBarNotifier.minimize(), - ), + onScrollStart: () => + WidgetsBinding.instance.addPostFrameCallback((_) => controlBottomAppBarNotifier.minimize()), child: _buildAssetGrid(), ), if (widget.showMultiSelectIndicator && widget.selectionActive) _buildMultiSelectIndicator(), @@ -590,10 +555,7 @@ class _PlaceholderRow extends StatelessWidget { key: ValueKey(i), width: width, height: height, - margin: EdgeInsets.only( - bottom: margin, - right: i + 1 == number ? 0.0 : margin, - ), + margin: EdgeInsets.only(bottom: margin, right: i + 1 == number ? 0.0 : margin), ), ], ); @@ -639,9 +601,7 @@ class _Section extends StatelessWidget { }); @override - Widget build( - BuildContext context, - ) { + Widget build(BuildContext context) { return LayoutBuilder( builder: (context, constraints) { final width = constraints.maxWidth / assetsPerRow - margin * (assetsPerRow - 1) / assetsPerRow; @@ -675,10 +635,7 @@ class _Section extends StatelessWidget { key: ValueKey(i), rowStartIndex: i * assetsPerRow, sectionIndex: sectionIndex, - assets: assetsToRender.nestedSlice( - i * assetsPerRow, - min((i + 1) * assetsPerRow, section.count), - ), + assets: assetsToRender.nestedSlice(i * assetsPerRow, min((i + 1) * assetsPerRow, section.count)), absoluteOffset: section.offset + i * assetsPerRow, width: width, assetsPerRow: assetsPerRow, @@ -706,9 +663,7 @@ class _Section extends StatelessWidget { class _MonthTitle extends StatelessWidget { final DateTime date; - const _MonthTitle({ - required this.date, - }); + const _MonthTitle({required this.date}); @override Widget build(BuildContext context) { @@ -719,10 +674,7 @@ class _MonthTitle extends StatelessWidget { padding: const EdgeInsets.only(left: 12.0, top: 24.0), child: Text( toBeginningOfSentenceCase(title, context.locale.languageCode), - style: const TextStyle( - fontSize: 26, - fontWeight: FontWeight.w500, - ), + style: const TextStyle(fontSize: 26, fontWeight: FontWeight.w500), ), ); } @@ -821,11 +773,7 @@ class _AssetRow extends StatelessWidget { // Normalize: final sum = arConfiguration.sum; - widthDistribution.setRange( - 0, - widthDistribution.length, - arConfiguration.map((e) => (e * assets.length) / sum), - ); + widthDistribution.setRange(0, widthDistribution.length, arConfiguration.map((e) => (e * assets.length) / sum)); } return Row( key: key, @@ -835,10 +783,7 @@ class _AssetRow extends StatelessWidget { return Container( width: width * widthDistribution[index], height: width, - margin: EdgeInsets.only( - bottom: margin, - right: last ? 0.0 : margin, - ), + margin: EdgeInsets.only(bottom: margin, right: last ? 0.0 : margin), child: GestureDetector( onTap: () { if (selectionActive) { diff --git a/mobile/lib/widgets/asset_grid/multiselect_grid.dart b/mobile/lib/widgets/asset_grid/multiselect_grid.dart index a7c1290b30..c28f407e1e 100644 --- a/mobile/lib/widgets/asset_grid/multiselect_grid.dart +++ b/mobile/lib/widgets/asset_grid/multiselect_grid.dart @@ -79,53 +79,38 @@ class MultiselectGrid extends HookConsumerWidget { final currentUser = ref.watch(currentUserProvider); final processing = useProcessingOverlay(); - useEffect( - () { - selectionEnabledHook.addListener(() { - multiselectEnabled.state = selectionEnabledHook.value; - }); + useEffect(() { + selectionEnabledHook.addListener(() { + multiselectEnabled.state = selectionEnabledHook.value; + }); - return () { - // This does not work in tests - if (kReleaseMode) { - selectionEnabledHook.dispose(); - } - }; - }, - [], - ); + return () { + // This does not work in tests + if (kReleaseMode) { + selectionEnabledHook.dispose(); + } + }; + }, []); - void selectionListener( - bool multiselect, - Set selectedAssets, - ) { + void selectionListener(bool multiselect, Set selectedAssets) { selectionEnabledHook.value = multiselect; selection.value = selectedAssets; selectionAssetState.value = AssetSelectionState.fromSelection(selectedAssets); } errorBuilder(String? msg) => msg != null && msg.isNotEmpty - ? () => ImmichToast.show( - context: context, - msg: msg, - gravity: ToastGravity.BOTTOM, - ) + ? () => ImmichToast.show(context: context, msg: msg, gravity: ToastGravity.BOTTOM) : null; - Iterable ownedRemoteSelection({ - String? localErrorMessage, - String? ownerErrorMessage, - }) { + Iterable ownedRemoteSelection({String? localErrorMessage, String? ownerErrorMessage}) { final assets = selection.value; - return assets.remoteOnly(errorCallback: errorBuilder(localErrorMessage)).ownedOnly( - currentUser, - errorCallback: errorBuilder(ownerErrorMessage), - ); + return assets + .remoteOnly(errorCallback: errorBuilder(localErrorMessage)) + .ownedOnly(currentUser, errorCallback: errorBuilder(ownerErrorMessage)); } - Iterable remoteSelection({String? errorMessage}) => selection.value.remoteOnly( - errorCallback: errorBuilder(errorMessage), - ); + Iterable remoteSelection({String? errorMessage}) => + selection.value.remoteOnly(errorCallback: errorBuilder(errorMessage)); void onShareAssets(bool shareLocal) { processing.value = true; @@ -174,10 +159,7 @@ class MultiselectGrid extends HookConsumerWidget { processing.value = true; try { final toDelete = selection.value - .ownedOnly( - currentUser, - errorCallback: errorBuilder('home_page_delete_err_partner'.tr()), - ) + .ownedOnly(currentUser, errorCallback: errorBuilder('home_page_delete_err_partner'.tr())) .toList(); final isDeleted = await ref.read(assetProvider.notifier).deleteAssets(toDelete, force: force); @@ -237,25 +219,10 @@ class MultiselectGrid extends HookConsumerWidget { final failedCount = totalCount - successCount; final msg = failedCount > 0 - ? 'assets_downloaded_failed'.t( - context: context, - args: { - 'count': successCount, - 'error': failedCount, - }, - ) - : 'assets_downloaded_successfully'.t( - context: context, - args: { - 'count': successCount, - }, - ); + ? 'assets_downloaded_failed'.t(context: context, args: {'count': successCount, 'error': failedCount}) + : 'assets_downloaded_successfully'.t(context: context, args: {'count': successCount}); - ImmichToast.show( - context: context, - msg: msg, - gravity: ToastGravity.BOTTOM, - ); + ImmichToast.show(context: context, msg: msg, gravity: ToastGravity.BOTTOM); } finally { processing.value = false; selectionEnabledHook.value = false; @@ -270,10 +237,9 @@ class MultiselectGrid extends HookConsumerWidget { ownerErrorMessage: 'home_page_delete_err_partner'.tr(), ).toList(); - final isDeleted = await ref.read(assetProvider.notifier).deleteRemoteAssets( - toDelete, - shouldDeletePermanently: shouldDeletePermanently, - ); + final isDeleted = await ref + .read(assetProvider.notifier) + .deleteRemoteAssets(toDelete, shouldDeletePermanently: shouldDeletePermanently); if (isDeleted) { ImmichToast.show( context: context, @@ -293,10 +259,9 @@ class MultiselectGrid extends HookConsumerWidget { processing.value = true; selectionEnabledHook.value = false; try { - ref.read(manualUploadProvider.notifier).uploadAssets( - context, - selection.value.where((a) => a.storage == AssetState.local), - ); + ref + .read(manualUploadProvider.notifier) + .uploadAssets(context, selection.value.where((a) => a.storage == AssetState.local)); } finally { processing.value = false; } @@ -305,16 +270,11 @@ class MultiselectGrid extends HookConsumerWidget { void onAddToAlbum(Album album) async { processing.value = true; try { - final Iterable assets = remoteSelection( - errorMessage: "home_page_add_to_album_err_local".tr(), - ); + final Iterable assets = remoteSelection(errorMessage: "home_page_add_to_album_err_local".tr()); if (assets.isEmpty) { return; } - final result = await ref.read(albumServiceProvider).addAssets( - album, - assets, - ); + final result = await ref.read(albumServiceProvider).addAssets(album, assets); if (result != null) { if (result.alreadyInAlbum.isNotEmpty) { @@ -332,10 +292,7 @@ class MultiselectGrid extends HookConsumerWidget { ImmichToast.show( context: context, msg: "home_page_add_to_album_success".tr( - namedArgs: { - "album": album.name, - "added": result.successfullyAdded.toString(), - }, + namedArgs: {"album": album.name, "added": result.successfullyAdded.toString()}, ), toastType: ToastType.success, ); @@ -350,9 +307,7 @@ class MultiselectGrid extends HookConsumerWidget { void onCreateNewAlbum() async { processing.value = true; try { - final Iterable assets = remoteSelection( - errorMessage: "home_page_add_to_album_err_local".tr(), - ); + final Iterable assets = remoteSelection(errorMessage: "home_page_add_to_album_err_local".tr()); if (assets.isEmpty) { return; } @@ -376,9 +331,7 @@ class MultiselectGrid extends HookConsumerWidget { return; } - await ref.read(stackServiceProvider).createStack( - selection.value.map((e) => e.remoteId!).toList(), - ); + await ref.read(stackServiceProvider).createStack(selection.value.map((e) => e.remoteId!).toList()); } finally { processing.value = false; selectionEnabledHook.value = false; @@ -426,12 +379,7 @@ class MultiselectGrid extends HookConsumerWidget { final isInLockedView = ref.read(inLockedViewProvider); final visibility = isInLockedView ? AssetVisibilityEnum.timeline : AssetVisibilityEnum.locked; - await handleSetAssetsVisibility( - ref, - context, - visibility, - remoteAssets.toList(), - ); + await handleSetAssetsVisibility(ref, context, visibility, remoteAssets.toList()); } } finally { processing.value = false; @@ -439,41 +387,34 @@ class MultiselectGrid extends HookConsumerWidget { } } - Future Function() wrapLongRunningFun( - Future Function() fun, { - bool showOverlay = true, - }) => - () async { - if (showOverlay) processing.value = true; - try { - final result = await fun(); - if (result.runtimeType != bool || result == true) { - selectionEnabledHook.value = false; - } - return result; - } finally { - if (showOverlay) processing.value = false; - } - }; + Future Function() wrapLongRunningFun(Future Function() fun, {bool showOverlay = true}) => () async { + if (showOverlay) processing.value = true; + try { + final result = await fun(); + if (result.runtimeType != bool || result == true) { + selectionEnabledHook.value = false; + } + return result; + } finally { + if (showOverlay) processing.value = false; + } + }; return SafeArea( top: true, bottom: false, child: Stack( children: [ - ref.watch(renderListProvider).when( + ref + .watch(renderListProvider) + .when( data: (data) => data.isEmpty && (buildLoadingIndicator != null || topWidget == null) ? (buildLoadingIndicator ?? buildEmptyIndicator)() : ImmichAssetGrid( renderList: data, listener: selectionListener, selectionActive: selectionEnabledHook.value, - onRefresh: onRefresh == null - ? null - : wrapLongRunningFun( - onRefresh!, - showOverlay: false, - ), + onRefresh: onRefresh == null ? null : wrapLongRunningFun(onRefresh!, showOverlay: false), topWidget: topWidget, showStack: stackEnabled, showDragScrollLabel: dragScrollLabelEnabled, @@ -506,9 +447,7 @@ class MultiselectGrid extends HookConsumerWidget { unarchive: unarchive, onToggleLocked: onToggleLockedVisibility, onRemoveFromAlbum: onRemoveFromAlbum != null - ? wrapLongRunningFun( - () => onRemoveFromAlbum!(selection.value), - ) + ? wrapLongRunningFun(() => onRemoveFromAlbum!(selection.value)) : null, ), ], diff --git a/mobile/lib/widgets/asset_grid/multiselect_grid_status_indicator.dart b/mobile/lib/widgets/asset_grid/multiselect_grid_status_indicator.dart index 10a541cec3..3a1fa82a28 100644 --- a/mobile/lib/widgets/asset_grid/multiselect_grid_status_indicator.dart +++ b/mobile/lib/widgets/asset_grid/multiselect_grid_status_indicator.dart @@ -5,11 +5,7 @@ import 'package:immich_mobile/providers/asset_viewer/render_list_status_provider import 'package:immich_mobile/widgets/common/delayed_loading_indicator.dart'; class MultiselectGridStatusIndicator extends HookConsumerWidget { - const MultiselectGridStatusIndicator({ - super.key, - this.buildLoadingIndicator, - this.emptyIndicator, - }); + const MultiselectGridStatusIndicator({super.key, this.buildLoadingIndicator, this.emptyIndicator}); final Widget Function()? buildLoadingIndicator; final Widget? emptyIndicator; @@ -18,16 +14,13 @@ class MultiselectGridStatusIndicator extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final renderListStatus = ref.watch(renderListStatusProvider); return switch (renderListStatus) { - RenderListStatusEnum.loading => buildLoadingIndicator == null - ? const Center( - child: DelayedLoadingIndicator( - delay: Duration(milliseconds: 500), - ), - ) - : buildLoadingIndicator!(), + RenderListStatusEnum.loading => + buildLoadingIndicator == null + ? const Center(child: DelayedLoadingIndicator(delay: Duration(milliseconds: 500))) + : buildLoadingIndicator!(), RenderListStatusEnum.empty => emptyIndicator ?? Center(child: const Text("no_assets_to_show").tr()), RenderListStatusEnum.error => Center(child: const Text("error_loading_assets").tr()), - RenderListStatusEnum.complete => const SizedBox() + RenderListStatusEnum.complete => const SizedBox(), }; } } diff --git a/mobile/lib/widgets/asset_grid/thumbnail_image.dart b/mobile/lib/widgets/asset_grid/thumbnail_image.dart index 10815d00bb..93385b88b3 100644 --- a/mobile/lib/widgets/asset_grid/thumbnail_image.dart +++ b/mobile/lib/widgets/asset_grid/thumbnail_image.dart @@ -41,8 +41,9 @@ class ThumbnailImage extends StatelessWidget { @override Widget build(BuildContext context) { - final assetContainerColor = - context.isDarkTheme ? context.primaryColor.darken(amount: 0.6) : context.primaryColor.lighten(amount: 0.8); + final assetContainerColor = context.isDarkTheme + ? context.primaryColor.darken(amount: 0.6) + : context.primaryColor.lighten(amount: 0.8); return Stack( children: [ @@ -52,16 +53,13 @@ class ThumbnailImage extends StatelessWidget { decoration: BoxDecoration( border: multiselectEnabled && isSelected ? canDeselect - ? Border.all( - color: assetContainerColor, - width: 8, - ) - : const Border( - top: BorderSide(color: Colors.grey, width: 8), - right: BorderSide(color: Colors.grey, width: 8), - bottom: BorderSide(color: Colors.grey, width: 8), - left: BorderSide(color: Colors.grey, width: 8), - ) + ? Border.all(color: assetContainerColor, width: 8) + : const Border( + top: BorderSide(color: Colors.grey, width: 8), + right: BorderSide(color: Colors.grey, width: 8), + bottom: BorderSide(color: Colors.grey, width: 8), + left: BorderSide(color: Colors.grey, width: 8), + ) : const Border(), ), child: Stack( @@ -76,21 +74,9 @@ class ThumbnailImage extends StatelessWidget { ), if (showStorageIndicator) _StorageIcon(storage: asset.storage), if (asset.isFavorite) - const Positioned( - left: 8, - bottom: 5, - child: Icon( - Icons.favorite, - color: Colors.white, - size: 16, - ), - ), + const Positioned(left: 8, bottom: 5, child: Icon(Icons.favorite, color: Colors.white, size: 16)), if (asset.isVideo) _VideoIcon(duration: asset.duration), - if (asset.stackCount > 0) - _StackIcon( - isVideo: asset.isVideo, - stackCount: asset.stackCount, - ), + if (asset.stackCount > 0) _StackIcon(isVideo: asset.isVideo, stackCount: asset.stackCount), ], ), ), @@ -98,15 +84,9 @@ class ThumbnailImage extends StatelessWidget { isSelected ? const Padding( padding: EdgeInsets.all(3.0), - child: Align( - alignment: Alignment.topLeft, - child: _SelectedIcon(), - ), + child: Align(alignment: Alignment.topLeft, child: _SelectedIcon()), ) - : const Icon( - Icons.circle_outlined, - color: Colors.white, - ), + : const Icon(Icons.circle_outlined, color: Colors.white), ], ); } @@ -117,18 +97,13 @@ class _SelectedIcon extends StatelessWidget { @override Widget build(BuildContext context) { - final assetContainerColor = - context.isDarkTheme ? context.primaryColor.darken(amount: 0.6) : context.primaryColor.lighten(amount: 0.8); + final assetContainerColor = context.isDarkTheme + ? context.primaryColor.darken(amount: 0.6) + : context.primaryColor.lighten(amount: 0.8); return DecoratedBox( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: assetContainerColor, - ), - child: Icon( - Icons.check_circle_rounded, - color: context.primaryColor, - ), + decoration: BoxDecoration(shape: BoxShape.circle, color: assetContainerColor), + child: Icon(Icons.check_circle_rounded, color: context.primaryColor), ); } } @@ -147,18 +122,10 @@ class _VideoIcon extends StatelessWidget { children: [ Text( duration.format(), - style: const TextStyle( - color: Colors.white, - fontSize: 10, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold), ), const SizedBox(width: 3), - const Icon( - Icons.play_circle_fill_rounded, - color: Colors.white, - size: 18, - ), + const Icon(Icons.play_circle_fill_rounded, color: Colors.white, size: 18), ], ), ); @@ -181,21 +148,10 @@ class _StackIcon extends StatelessWidget { if (stackCount > 1) Text( "$stackCount", - style: const TextStyle( - color: Colors.white, - fontSize: 10, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold), ), - if (stackCount > 1) - const SizedBox( - width: 3, - ), - const Icon( - Icons.burst_mode_rounded, - color: Colors.white, - size: 18, - ), + if (stackCount > 1) const SizedBox(width: 3), + const Icon(Icons.burst_mode_rounded, color: Colors.white, size: 18), ], ), ); @@ -211,53 +167,35 @@ class _StorageIcon extends StatelessWidget { Widget build(BuildContext context) { return switch (storage) { AssetState.local => const Positioned( - right: 8, - bottom: 5, - child: Icon( - Icons.cloud_off_outlined, - color: Color.fromRGBO(255, 255, 255, 0.8), - size: 16, - shadows: [ - Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - offset: Offset(0.0, 0.0), - ), - ], - ), + right: 8, + bottom: 5, + child: Icon( + Icons.cloud_off_outlined, + color: Color.fromRGBO(255, 255, 255, 0.8), + size: 16, + shadows: [Shadow(blurRadius: 5.0, color: Color.fromRGBO(0, 0, 0, 0.6), offset: Offset(0.0, 0.0))], ), + ), AssetState.remote => const Positioned( - right: 8, - bottom: 5, - child: Icon( - Icons.cloud_outlined, - color: Color.fromRGBO(255, 255, 255, 0.8), - size: 16, - shadows: [ - Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - offset: Offset(0.0, 0.0), - ), - ], - ), + right: 8, + bottom: 5, + child: Icon( + Icons.cloud_outlined, + color: Color.fromRGBO(255, 255, 255, 0.8), + size: 16, + shadows: [Shadow(blurRadius: 5.0, color: Color.fromRGBO(0, 0, 0, 0.6), offset: Offset(0.0, 0.0))], ), + ), AssetState.merged => const Positioned( - right: 8, - bottom: 5, - child: Icon( - Icons.cloud_done_outlined, - color: Color.fromRGBO(255, 255, 255, 0.8), - size: 16, - shadows: [ - Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - offset: Offset(0.0, 0.0), - ), - ], - ), + right: 8, + bottom: 5, + child: Icon( + Icons.cloud_done_outlined, + color: Color.fromRGBO(255, 255, 255, 0.8), + size: 16, + shadows: [Shadow(blurRadius: 5.0, color: Color.fromRGBO(0, 0, 0, 0.6), offset: Offset(0.0, 0.0))], ), + ), }; } } @@ -288,13 +226,7 @@ class _ImageIcon extends StatelessWidget { tag: isDto ? '${asset.remoteId}-$heroOffset' : asset.id + heroOffset, child: Stack( children: [ - SizedBox.expand( - child: ImmichThumbnail( - asset: asset, - height: 250, - width: 250, - ), - ), + SizedBox.expand(child: ImmichThumbnail(asset: asset, height: 250, width: 250)), const DecoratedBox( decoration: BoxDecoration( gradient: LinearGradient( @@ -321,10 +253,7 @@ class _ImageIcon extends StatelessWidget { return DecoratedBox( decoration: canDeselect ? BoxDecoration(color: assetContainerColor) : const BoxDecoration(color: Colors.grey), - child: ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(15.0)), - child: image, - ), + child: ClipRRect(borderRadius: const BorderRadius.all(Radius.circular(15.0)), child: image), ); } } diff --git a/mobile/lib/widgets/asset_grid/thumbnail_placeholder.dart b/mobile/lib/widgets/asset_grid/thumbnail_placeholder.dart index 5b12426a50..a84dfbae37 100644 --- a/mobile/lib/widgets/asset_grid/thumbnail_placeholder.dart +++ b/mobile/lib/widgets/asset_grid/thumbnail_placeholder.dart @@ -7,12 +7,7 @@ class ThumbnailPlaceholder extends StatelessWidget { final double width; final double height; - const ThumbnailPlaceholder({ - super.key, - this.margin = EdgeInsets.zero, - this.width = 250, - this.height = 250, - }); + const ThumbnailPlaceholder({super.key, this.margin = EdgeInsets.zero, this.width = 250, this.height = 250}); @override Widget build(BuildContext context) { @@ -26,11 +21,7 @@ class ThumbnailPlaceholder extends StatelessWidget { height: height, margin: margin, decoration: BoxDecoration( - gradient: LinearGradient( - colors: gradientColors, - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - ), + gradient: LinearGradient(colors: gradientColors, begin: Alignment.topCenter, end: Alignment.bottomCenter), ), ); } diff --git a/mobile/lib/widgets/asset_grid/upload_dialog.dart b/mobile/lib/widgets/asset_grid/upload_dialog.dart index c2a38fab8c..86e2759566 100644 --- a/mobile/lib/widgets/asset_grid/upload_dialog.dart +++ b/mobile/lib/widgets/asset_grid/upload_dialog.dart @@ -4,11 +4,11 @@ class UploadDialog extends ConfirmDialog { final Function onUpload; const UploadDialog({super.key, required this.onUpload}) - : super( - title: 'upload_dialog_title', - content: 'upload_dialog_info', - cancel: 'cancel', - ok: 'upload', - onOk: onUpload, - ); + : super( + title: 'upload_dialog_title', + content: 'upload_dialog_info', + cancel: 'cancel', + ok: 'upload', + onOk: onUpload, + ); } diff --git a/mobile/lib/widgets/asset_viewer/advanced_bottom_sheet.dart b/mobile/lib/widgets/asset_viewer/advanced_bottom_sheet.dart index 6fda361632..faa058ced4 100644 --- a/mobile/lib/widgets/asset_viewer/advanced_bottom_sheet.dart +++ b/mobile/lib/widgets/asset_viewer/advanced_bottom_sheet.dart @@ -8,11 +8,7 @@ class AdvancedBottomSheet extends HookConsumerWidget { final Asset assetDetail; final ScrollController? scrollController; - const AdvancedBottomSheet({ - super.key, - required this.assetDetail, - this.scrollController, - }); + const AdvancedBottomSheet({super.key, required this.assetDetail, this.scrollController}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -26,27 +22,15 @@ class AdvancedBottomSheet extends HookConsumerWidget { return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - const Align( - child: Text( - "ADVANCED INFO", - style: TextStyle(fontSize: 12.0), - ), - ), + const Align(child: Text("ADVANCED INFO", style: TextStyle(fontSize: 12.0))), const SizedBox(height: 32.0), Container( decoration: BoxDecoration( color: context.isDarkTheme ? Colors.grey[900] : Colors.grey[200], - borderRadius: const BorderRadius.all( - Radius.circular(15.0), - ), + borderRadius: const BorderRadius.all(Radius.circular(15.0)), ), child: Padding( - padding: const EdgeInsets.only( - right: 16.0, - left: 16, - top: 8, - bottom: 16, - ), + padding: const EdgeInsets.only(right: 16.0, left: 16, top: 8, bottom: 16), child: ListView( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), @@ -55,28 +39,18 @@ class AdvancedBottomSheet extends HookConsumerWidget { alignment: Alignment.centerRight, child: IconButton( onPressed: () { - Clipboard.setData( - ClipboardData( - text: assetDetail.toString(), - ), - ).then((_) { + Clipboard.setData(ClipboardData(text: assetDetail.toString())).then((_) { context.scaffoldMessenger.showSnackBar( SnackBar( content: Text( "Copied to clipboard", - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ), ), ); }); }, - icon: Icon( - Icons.copy, - size: 16.0, - color: context.primaryColor, - ), + icon: Icon(Icons.copy, size: 16.0, color: context.primaryColor), ), ), SelectableText( diff --git a/mobile/lib/widgets/asset_viewer/animated_play_pause.dart b/mobile/lib/widgets/asset_viewer/animated_play_pause.dart index dd0d95b93b..e7ceac6105 100644 --- a/mobile/lib/widgets/asset_viewer/animated_play_pause.dart +++ b/mobile/lib/widgets/asset_viewer/animated_play_pause.dart @@ -2,12 +2,7 @@ import 'package:flutter/material.dart'; /// A widget that animates implicitly between a play and a pause icon. class AnimatedPlayPause extends StatefulWidget { - const AnimatedPlayPause({ - super.key, - required this.playing, - this.size, - this.color, - }); + const AnimatedPlayPause({super.key, required this.playing, this.size, this.color}); final double? size; final bool playing; diff --git a/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart b/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart index 66392b4bb4..c7125e2200 100644 --- a/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart +++ b/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart @@ -72,10 +72,7 @@ class BottomGalleryBar extends ConsumerWidget { void handleDelete() async { Future onDelete(bool force) async { - final isDeleted = await ref.read(assetProvider.notifier).deleteAssets( - {asset}, - force: force, - ); + final isDeleted = await ref.read(assetProvider.notifier).deleteAssets({asset}, force: force); if (isDeleted && isStackPrimaryAsset) { // Workaround for asset remaining in the gallery renderList.deleteAsset(asset); @@ -101,12 +98,7 @@ class BottomGalleryBar extends ConsumerWidget { if (isDeleted) { // Can only trash assets stored in server. Local assets are always permanently removed for now if (context.mounted && asset.isRemote && isStackPrimaryAsset) { - ImmichToast.show( - durationInSecond: 1, - context: context, - msg: 'Asset trashed', - gravity: ToastGravity.BOTTOM, - ); + ImmichToast.show(durationInSecond: 1, context: context, msg: 'Asset trashed', gravity: ToastGravity.BOTTOM); } removeAssetFromStack(); } @@ -149,19 +141,13 @@ class BottomGalleryBar extends ConsumerWidget { mainAxisSize: MainAxisSize.min, children: [ ListTile( - leading: const Icon( - Icons.filter_none_outlined, - size: 18, - ), + leading: const Icon(Icons.filter_none_outlined, size: 18), onTap: () async { await unStack(); ctx.pop(); context.maybePop(); }, - title: const Text( - "viewer_unstack", - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(), + title: const Text("viewer_unstack", style: TextStyle(fontWeight: FontWeight.bold)).tr(), ), ], ), @@ -189,11 +175,7 @@ class BottomGalleryBar extends ConsumerWidget { context.navigator.push( MaterialPageRoute( - builder: (context) => EditImagePage( - asset: asset, - image: image, - isEdited: false, - ), + builder: (context) => EditImagePage(asset: asset, image: image, isEdited: false), ), ); } @@ -221,9 +203,7 @@ class BottomGalleryBar extends ConsumerWidget { return; } - ref.read(downloadStateProvider.notifier).downloadAsset( - asset, - ); + ref.read(downloadStateProvider.notifier).downloadAsset(asset); } handleRemoveFromAlbum() async { @@ -258,12 +238,11 @@ class BottomGalleryBar extends ConsumerWidget { final List> albumActions = [ { BottomNavigationBarItem( - icon: Icon( - Platform.isAndroid ? Icons.share_rounded : Icons.ios_share_rounded, - ), + icon: Icon(Platform.isAndroid ? Icons.share_rounded : Icons.ios_share_rounded), label: 'share'.tr(), tooltip: 'share'.tr(), - ): (_) => shareAsset(), + ): (_) => + shareAsset(), }, if (asset.isImage && !isInLockedView) { @@ -271,7 +250,8 @@ class BottomGalleryBar extends ConsumerWidget { icon: const Icon(Icons.tune_outlined), label: 'edit'.tr(), tooltip: 'edit'.tr(), - ): (_) => handleEdit(), + ): (_) => + handleEdit(), }, if (isOwner && !isInLockedView) { @@ -285,7 +265,8 @@ class BottomGalleryBar extends ConsumerWidget { icon: const Icon(Icons.archive_outlined), label: 'archive'.tr(), tooltip: 'archive'.tr(), - ): (_) => handleArchive(), + ): (_) => + handleArchive(), }, if (isOwner && asset.stackCount > 0 && !isInLockedView) { @@ -293,7 +274,8 @@ class BottomGalleryBar extends ConsumerWidget { icon: const Icon(Icons.burst_mode_outlined), label: 'stack'.tr(), tooltip: 'stack'.tr(), - ): (_) => showStackActionItems(), + ): (_) => + showStackActionItems(), }, if (isOwner && !isInAlbum) { @@ -301,7 +283,8 @@ class BottomGalleryBar extends ConsumerWidget { icon: const Icon(Icons.delete_outline), label: 'delete'.tr(), tooltip: 'delete'.tr(), - ): (_) => handleDelete(), + ): (_) => + handleDelete(), }, if (!isOwner) { @@ -309,7 +292,8 @@ class BottomGalleryBar extends ConsumerWidget { icon: const Icon(Icons.download_outlined), label: 'download'.tr(), tooltip: 'download'.tr(), - ): (_) => handleDownload(), + ): (_) => + handleDownload(), }, if (isInAlbum) { @@ -317,7 +301,8 @@ class BottomGalleryBar extends ConsumerWidget { icon: const Icon(Icons.remove_circle_outline), label: 'remove_from_album'.tr(), tooltip: 'remove_from_album'.tr(), - ): (_) => handleRemoveFromAlbum(), + ): (_) => + handleRemoveFromAlbum(), }, ]; return IgnorePointer( @@ -344,16 +329,8 @@ class BottomGalleryBar extends ConsumerWidget { backgroundColor: Colors.transparent, unselectedIconTheme: const IconThemeData(color: Colors.white), selectedIconTheme: const IconThemeData(color: Colors.white), - unselectedLabelStyle: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.w500, - height: 2.3, - ), - selectedLabelStyle: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.w500, - height: 2.3, - ), + unselectedLabelStyle: const TextStyle(color: Colors.white, fontWeight: FontWeight.w500, height: 2.3), + selectedLabelStyle: const TextStyle(color: Colors.white, fontWeight: FontWeight.w500, height: 2.3), unselectedFontSize: 14, selectedFontSize: 14, selectedItemColor: Colors.white, diff --git a/mobile/lib/widgets/asset_viewer/cast_dialog.dart b/mobile/lib/widgets/asset_viewer/cast_dialog.dart index a0373bcb6c..4db1d9bb69 100644 --- a/mobile/lib/widgets/asset_viewer/cast_dialog.dart +++ b/mobile/lib/widgets/asset_viewer/cast_dialog.dart @@ -21,10 +21,7 @@ class CastDialog extends ConsumerWidget { } return AlertDialog( - title: const Text( - "cast", - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(), + title: const Text("cast", style: TextStyle(fontWeight: FontWeight.bold)).tr(), content: SizedBox( width: 250, height: 250, @@ -32,20 +29,13 @@ class CastDialog extends ConsumerWidget { future: ref.read(castProvider.notifier).getDevices(), builder: (context, snapshot) { if (snapshot.hasError) { - return Text( - 'Error: ${snapshot.error.toString()}', - ); + return Text('Error: ${snapshot.error.toString()}'); } else if (!snapshot.hasData) { - return const SizedBox( - height: 48, - child: Center(child: CircularProgressIndicator()), - ); + return const SizedBox(height: 48, child: Center(child: CircularProgressIndicator())); } if (snapshot.data!.isEmpty) { - return const Text( - 'no_cast_devices_found', - ).tr(); + return const Text('no_cast_devices_found').tr(); } final devices = snapshot.data!; @@ -74,13 +64,7 @@ class CastDialog extends ConsumerWidget { // It's a section header return Padding( padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Text( - item, - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ).tr(), + child: Text(item, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16)).tr(), ); } else { final (deviceName, type, deviceObj) = item as (String, CastDestinationType, dynamic); @@ -88,9 +72,7 @@ class CastDialog extends ConsumerWidget { return ListTile( title: Text( deviceName, - style: TextStyle( - color: isCurrentDevice(deviceName) ? context.colorScheme.primary : null, - ), + style: TextStyle(color: isCurrentDevice(deviceName) ? context.colorScheme.primary : null), ), leading: Icon( type == CastDestinationType.googleCast ? Icons.cast : Icons.cast_connected, @@ -99,8 +81,8 @@ class CastDialog extends ConsumerWidget { trailing: isCurrentDevice(deviceName) ? Icon(Icons.check, color: context.colorScheme.primary) : isDeviceConnecting(deviceName) - ? const CircularProgressIndicator() - : null, + ? const CircularProgressIndicator() + : null, onTap: () async { if (isDeviceConnecting(deviceName)) { return; @@ -127,20 +109,14 @@ class CastDialog extends ConsumerWidget { onPressed: () => ref.read(castProvider.notifier).disconnect(), child: Text( "stop_casting", - style: TextStyle( - color: context.colorScheme.secondary, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.colorScheme.secondary, fontWeight: FontWeight.bold), ).tr(), ), TextButton( onPressed: () => context.pop(), child: Text( "close", - style: TextStyle( - color: context.colorScheme.primary, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.colorScheme.primary, fontWeight: FontWeight.bold), ).tr(), ), ], diff --git a/mobile/lib/widgets/asset_viewer/center_play_button.dart b/mobile/lib/widgets/asset_viewer/center_play_button.dart index 6d7aead9d1..26d0a41129 100644 --- a/mobile/lib/widgets/asset_viewer/center_play_button.dart +++ b/mobile/lib/widgets/asset_viewer/center_play_button.dart @@ -29,19 +29,13 @@ class CenterPlayButton extends StatelessWidget { opacity: show ? 1.0 : 0.0, duration: const Duration(milliseconds: 100), child: DecoratedBox( - decoration: BoxDecoration( - color: backgroundColor, - shape: BoxShape.circle, - ), + decoration: BoxDecoration(color: backgroundColor, shape: BoxShape.circle), child: IconButton( iconSize: 32, padding: const EdgeInsets.all(12.0), icon: isFinished ? Icon(Icons.replay, color: iconColor) - : AnimatedPlayPause( - color: iconColor, - playing: isPlaying, - ), + : AnimatedPlayPause(color: iconColor, playing: isPlaying), onPressed: onPressed, ), ), diff --git a/mobile/lib/widgets/asset_viewer/custom_video_player_controls.dart b/mobile/lib/widgets/asset_viewer/custom_video_player_controls.dart index d70761f37d..0e766c77b9 100644 --- a/mobile/lib/widgets/asset_viewer/custom_video_player_controls.dart +++ b/mobile/lib/widgets/asset_viewer/custom_video_player_controls.dart @@ -13,36 +13,28 @@ import 'package:immich_mobile/widgets/common/delayed_loading_indicator.dart'; class CustomVideoPlayerControls extends HookConsumerWidget { final Duration hideTimerDuration; - const CustomVideoPlayerControls({ - super.key, - this.hideTimerDuration = const Duration(seconds: 5), - }); + const CustomVideoPlayerControls({super.key, this.hideTimerDuration = const Duration(seconds: 5)}); @override Widget build(BuildContext context, WidgetRef ref) { - final assetIsVideo = ref.watch( - currentAssetProvider.select((asset) => asset != null && asset.isVideo), - ); + final assetIsVideo = ref.watch(currentAssetProvider.select((asset) => asset != null && asset.isVideo)); final showControls = ref.watch(showControlsProvider); final VideoPlaybackState state = ref.watch(videoPlaybackValueProvider.select((value) => value.state)); final cast = ref.watch(castProvider); // A timer to hide the controls - final hideTimer = useTimer( - hideTimerDuration, - () { - if (!context.mounted) { - return; - } - final state = ref.read(videoPlaybackValueProvider).state; + final hideTimer = useTimer(hideTimerDuration, () { + if (!context.mounted) { + return; + } + final state = ref.read(videoPlaybackValueProvider).state; - // Do not hide on paused - if (state != VideoPlaybackState.paused && state != VideoPlaybackState.completed && assetIsVideo) { - ref.read(showControlsProvider.notifier).show = false; - } - }, - ); + // Do not hide on paused + if (state != VideoPlaybackState.paused && state != VideoPlaybackState.completed && assetIsVideo) { + ref.read(showControlsProvider.notifier).show = false; + } + }); final showBuffering = state == VideoPlaybackState.buffering && !cast.isCasting; /// Shows the controls and starts the timer to hide them @@ -93,11 +85,7 @@ class CustomVideoPlayerControls extends HookConsumerWidget { child: Stack( children: [ if (showBuffering) - const Center( - child: DelayedLoadingIndicator( - fadeInDuration: Duration(milliseconds: 400), - ), - ) + const Center(child: DelayedLoadingIndicator(fadeInDuration: Duration(milliseconds: 400))) else GestureDetector( onTap: () => ref.read(showControlsProvider.notifier).show = false, diff --git a/mobile/lib/widgets/asset_viewer/description_input.dart b/mobile/lib/widgets/asset_viewer/description_input.dart index 3ac60fd613..b0cefd63fa 100644 --- a/mobile/lib/widgets/asset_viewer/description_input.dart +++ b/mobile/lib/widgets/asset_viewer/description_input.dart @@ -14,11 +14,7 @@ import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:logging/logging.dart'; class DescriptionInput extends HookConsumerWidget { - DescriptionInput({ - super.key, - required this.asset, - this.exifInfo, - }); + DescriptionInput({super.key, required this.asset, this.exifInfo}); final Asset asset; final ExifInfo? exifInfo; @@ -37,16 +33,13 @@ class DescriptionInput extends HookConsumerWidget { final hasDescription = useState(false); final isOwner = fastHash(owner?.id ?? '') == asset.ownerId; - useEffect( - () { - assetService.getDescription(asset).then((value) { - controller.text = value; - hasDescription.value = value.isNotEmpty; - }); - return null; - }, - [assetWithExif.value], - ); + useEffect(() { + assetService.getDescription(asset).then((value) { + controller.text = value; + hasDescription.value = value.isNotEmpty; + }); + return null; + }, [assetWithExif.value]); if (!isOwner && !hasDescription.value) { return const SizedBox.shrink(); @@ -55,19 +48,12 @@ class DescriptionInput extends HookConsumerWidget { submitDescription(String description) async { hasError.value = false; try { - await assetService.setDescription( - asset, - description, - ); + await assetService.setDescription(asset, description); controller.text = description; } catch (error, stack) { hasError.value = true; _log.severe("Error updating description", error, stack); - ImmichToast.show( - context: context, - msg: "description_input_submit_error".tr(), - toastType: ToastType.error, - ); + ImmichToast.show(context: context, msg: "description_input_submit_error".tr(), toastType: ToastType.error); } } @@ -80,10 +66,7 @@ class DescriptionInput extends HookConsumerWidget { controller.clear(); isTextEmpty.value = true; }, - icon: Icon( - Icons.cancel_rounded, - color: context.colorScheme.onSurfaceSecondary, - ), + icon: Icon(Icons.cancel_rounded, color: context.colorScheme.onSurfaceSecondary), splashRadius: 10, ); } diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/asset_date_time.dart b/mobile/lib/widgets/asset_viewer/detail_panel/asset_date_time.dart index e29da52280..df8f6593df 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/asset_date_time.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/asset_date_time.dart @@ -36,18 +36,8 @@ class AssetDateTime extends ConsumerWidget { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - formattedDateTime, - style: context.textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w600, - ), - ), - if (asset.isRemote) - IconButton( - onPressed: editDateTime, - icon: const Icon(Icons.edit_outlined), - iconSize: 20, - ), + Text(formattedDateTime, style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600)), + if (asset.isRemote) IconButton(onPressed: editDateTime, icon: const Icon(Icons.edit_outlined), iconSize: 20), ], ); } diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/asset_details.dart b/mobile/lib/widgets/asset_viewer/detail_panel/asset_details.dart index 59b52344e7..f0f9a2efcb 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/asset_details.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/asset_details.dart @@ -12,11 +12,7 @@ class AssetDetails extends ConsumerWidget { final Asset asset; final ExifInfo? exifInfo; - const AssetDetails({ - super.key, - required this.asset, - this.exifInfo, - }); + const AssetDetails({super.key, required this.asset, this.exifInfo}); @override Widget build(BuildContext context, WidgetRef ref) { diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/asset_location.dart b/mobile/lib/widgets/asset_viewer/detail_panel/asset_location.dart index 2a9e6f4a24..7ad290c152 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/asset_location.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/asset_location.dart @@ -11,10 +11,7 @@ import 'package:immich_mobile/widgets/asset_viewer/detail_panel/exif_map.dart'; class AssetLocation extends HookConsumerWidget { final Asset asset; - const AssetLocation({ - super.key, - required this.asset, - }); + const AssetLocation({super.key, required this.asset}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -35,10 +32,7 @@ class AssetLocation extends HookConsumerWidget { leading: const Icon(Icons.location_on), title: Text( "add_a_location", - style: context.textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w600, - color: context.primaryColor, - ), + style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor), ).tr(), onTap: editLocation, ) @@ -56,10 +50,7 @@ class AssetLocation extends HookConsumerWidget { bool hasLocationName = (cityName != null && stateName != null); return hasLocationName - ? Text( - "$cityName, $stateName", - style: context.textTheme.labelLarge, - ) + ? Text("$cityName, $stateName", style: context.textTheme.labelLarge) : const SizedBox.shrink(); } @@ -79,25 +70,16 @@ class AssetLocation extends HookConsumerWidget { ), ).tr(), if (asset.isRemote) - IconButton( - onPressed: editLocation, - icon: const Icon(Icons.edit_outlined), - iconSize: 20, - ), + IconButton(onPressed: editLocation, icon: const Icon(Icons.edit_outlined), iconSize: 20), ], ), asset.isRemote ? const SizedBox.shrink() : const SizedBox(height: 16), - ExifMap( - exifInfo: exifInfo!, - markerId: asset.remoteId, - ), + ExifMap(exifInfo: exifInfo!, markerId: asset.remoteId), const SizedBox(height: 16), getLocationName(), Text( "${exifInfo.latitude!.toStringAsFixed(4)}, ${exifInfo.longitude!.toStringAsFixed(4)}", - style: context.textTheme.labelMedium?.copyWith( - color: context.textTheme.labelMedium?.color?.withAlpha(150), - ), + style: context.textTheme.labelMedium?.copyWith(color: context.textTheme.labelMedium?.color?.withAlpha(150)), ), ], ), diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/camera_info.dart b/mobile/lib/widgets/asset_viewer/detail_panel/camera_info.dart index bd859b8ced..5ae29d32c7 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/camera_info.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/camera_info.dart @@ -5,10 +5,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; class CameraInfo extends StatelessWidget { final ExifInfo exifInfo; - const CameraInfo({ - super.key, - required this.exifInfo, - }); + const CameraInfo({super.key, required this.exifInfo}); @override Widget build(BuildContext context) { @@ -16,14 +13,8 @@ class CameraInfo extends StatelessWidget { return ListTile( contentPadding: const EdgeInsets.all(0), dense: true, - leading: Icon( - Icons.camera, - color: textColor.withAlpha(200), - ), - title: Text( - "${exifInfo.make} ${exifInfo.model}", - style: context.textTheme.labelLarge, - ), + leading: Icon(Icons.camera, color: textColor.withAlpha(200)), + title: Text("${exifInfo.make} ${exifInfo.model}", style: context.textTheme.labelLarge), subtitle: exifInfo.f != null || exifInfo.exposureSeconds != null || exifInfo.mm != null || exifInfo.iso != null ? Text( "ƒ/${exifInfo.fNumber} ${exifInfo.exposureTime} ${exifInfo.focalLength} mm ISO ${exifInfo.iso ?? ''} ", diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/exif_map.dart b/mobile/lib/widgets/asset_viewer/detail_panel/exif_map.dart index 7b6325cf2c..04d01194e9 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/exif_map.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/exif_map.dart @@ -11,12 +11,7 @@ class ExifMap extends StatelessWidget { final String? markerId; final MapCreatedCallback? onMapCreated; - const ExifMap({ - super.key, - required this.exifInfo, - this.markerId = 'marker', - this.onMapCreated, - }); + const ExifMap({super.key, required this.exifInfo, this.markerId = 'marker', this.onMapCreated}); @override Widget build(BuildContext context) { @@ -35,20 +30,13 @@ class ExifMap extends StatelessWidget { Uri uri = Uri( scheme: 'geo', host: '$latitude,$longitude', - queryParameters: { - 'z': '$zoomLevel', - 'q': '$latitude,$longitude', - }, + queryParameters: {'z': '$zoomLevel', 'q': '$latitude,$longitude'}, ); if (await canLaunchUrl(uri)) { return uri; } } else if (Platform.isIOS) { - var params = { - 'll': '$latitude,$longitude', - 'q': '$latitude,$longitude', - 'z': '$zoomLevel', - }; + var params = {'ll': '$latitude,$longitude', 'q': '$latitude,$longitude', 'z': '$zoomLevel'}; Uri uri = Uri.https('maps.apple.com', '/', params); if (await canLaunchUrl(uri)) { return uri; @@ -66,10 +54,7 @@ class ExifMap extends StatelessWidget { return LayoutBuilder( builder: (context, constraints) { return MapThumbnail( - centre: LatLng( - exifInfo.latitude ?? 0, - exifInfo.longitude ?? 0, - ), + centre: LatLng(exifInfo.latitude ?? 0, exifInfo.longitude ?? 0), height: 150, width: constraints.maxWidth, zoom: 12.0, diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/file_info.dart b/mobile/lib/widgets/asset_viewer/detail_panel/file_info.dart index 486918c436..78d9ac1776 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/file_info.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/file_info.dart @@ -6,10 +6,7 @@ import 'package:immich_mobile/utils/bytes_units.dart'; class FileInfo extends StatelessWidget { final Asset asset; - const FileInfo({ - super.key, - required this.asset, - }); + const FileInfo({super.key, required this.asset}); @override Widget build(BuildContext context) { @@ -41,15 +38,9 @@ class FileInfo extends StatelessWidget { return ListTile( contentPadding: const EdgeInsets.all(0), dense: true, - leading: Icon( - Icons.image, - color: textColor.withAlpha(200), - ), + leading: Icon(Icons.image, color: textColor.withAlpha(200)), titleAlignment: ListTileTitleAlignment.center, - title: Text( - title, - style: context.textTheme.labelLarge, - ), + title: Text(title, style: context.textTheme.labelLarge), subtitle: subtitle == null ? null : Text(subtitle), ); } diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/people_info.dart b/mobile/lib/widgets/asset_viewer/detail_panel/people_info.dart index a97a04a453..b2aa50493c 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/people_info.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/people_info.dart @@ -21,10 +21,7 @@ class PeopleInfo extends ConsumerWidget { final peopleProvider = ref.watch(assetPeopleNotifierProvider(asset).notifier); final people = ref.watch(assetPeopleNotifierProvider(asset)).value?.where((p) => !p.isHidden); - showPersonNameEditModel( - String personId, - String personName, - ) { + showPersonNameEditModel(String personId, String personName) { return showDialog( context: context, useRootNavigator: false, @@ -37,7 +34,8 @@ class PeopleInfo extends ConsumerWidget { }); } - final curatedPeople = people + final curatedPeople = + people ?.map( (p) => SearchCuratedContent( id: p.id, @@ -78,17 +76,10 @@ class PeopleInfo extends ConsumerWidget { content: curatedPeople, onTap: (content, index) { context - .pushRoute( - PersonResultRoute( - personId: content.id, - personName: content.label, - ), - ) + .pushRoute(PersonResultRoute(personId: content.id, personName: content.label)) .then((_) => peopleProvider.refresh()); }, - onNameTap: (person, index) => { - showPersonNameEditModel(person.id, person.label), - }, + onNameTap: (person, index) => {showPersonNameEditModel(person.id, person.label)}, ), ), ], diff --git a/mobile/lib/widgets/asset_viewer/formatted_duration.dart b/mobile/lib/widgets/asset_viewer/formatted_duration.dart index d18dc92575..fbcc8e6482 100644 --- a/mobile/lib/widgets/asset_viewer/formatted_duration.dart +++ b/mobile/lib/widgets/asset_viewer/formatted_duration.dart @@ -11,11 +11,7 @@ class FormattedDuration extends StatelessWidget { width: data.inHours > 0 ? 70 : 60, // use a fixed width to prevent jitter child: Text( data.format(), - style: const TextStyle( - fontSize: 14.0, - color: Colors.white, - fontWeight: FontWeight.w500, - ), + style: const TextStyle(fontSize: 14.0, color: Colors.white, fontWeight: FontWeight.w500), textAlign: TextAlign.center, ), ); diff --git a/mobile/lib/widgets/asset_viewer/gallery_app_bar.dart b/mobile/lib/widgets/asset_viewer/gallery_app_bar.dart index 1c24412259..dcb0334801 100644 --- a/mobile/lib/widgets/asset_viewer/gallery_app_bar.dart +++ b/mobile/lib/widgets/asset_viewer/gallery_app_bar.dart @@ -51,11 +51,7 @@ class GalleryAppBar extends ConsumerWidget { final result = await ref.read(trashProvider.notifier).restoreAssets([asset]); if (result && context.mounted) { - ImmichToast.show( - context: context, - msg: 'asset_restored_successfully'.tr(), - gravity: ToastGravity.BOTTOM, - ); + ImmichToast.show(context: context, msg: 'asset_restored_successfully'.tr(), gravity: ToastGravity.BOTTOM); } } @@ -75,16 +71,10 @@ class GalleryAppBar extends ConsumerWidget { addToAlbum(Asset addToAlbumAsset) { showModalBottomSheet( elevation: 0, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(15.0), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15.0))), context: context, builder: (BuildContext _) { - return AddToAlbumBottomSheet( - assets: [addToAlbumAsset], - ); + return AddToAlbumBottomSheet(assets: [addToAlbumAsset]); }, ); } diff --git a/mobile/lib/widgets/asset_viewer/top_control_app_bar.dart b/mobile/lib/widgets/asset_viewer/top_control_app_bar.dart index 1d04115b7c..35f3840797 100644 --- a/mobile/lib/widgets/asset_viewer/top_control_app_bar.dart +++ b/mobile/lib/widgets/asset_viewer/top_control_app_bar.dart @@ -58,10 +58,7 @@ class TopControlAppBar extends HookConsumerWidget { Widget buildFavoriteButton(a) { return IconButton( onPressed: () => onFavorite(a), - icon: Icon( - a.isFavorite ? Icons.favorite : Icons.favorite_border, - color: Colors.grey[200], - ), + icon: Icon(a.isFavorite ? Icons.favorite : Icons.favorite_border, color: Colors.grey[200]), ); } @@ -70,10 +67,7 @@ class TopControlAppBar extends HookConsumerWidget { onPressed: () { onLocatePressed(); }, - icon: Icon( - Icons.image_search, - color: Colors.grey[200], - ), + icon: Icon(Icons.image_search, color: Colors.grey[200]), ); } @@ -82,20 +76,14 @@ class TopControlAppBar extends HookConsumerWidget { onPressed: () { onMoreInfoPressed(); }, - icon: Icon( - Icons.info_outline_rounded, - color: Colors.grey[200], - ), + icon: Icon(Icons.info_outline_rounded, color: Colors.grey[200]), ); } Widget buildDownloadButton() { return IconButton( onPressed: onDownloadPressed, - icon: Icon( - Icons.cloud_download_outlined, - color: Colors.grey[200], - ), + icon: Icon(Icons.cloud_download_outlined, color: Colors.grey[200]), ); } @@ -104,10 +92,7 @@ class TopControlAppBar extends HookConsumerWidget { onPressed: () { onAddToAlbumPressed(); }, - icon: Icon( - Icons.add, - color: Colors.grey[200], - ), + icon: Icon(Icons.add, color: Colors.grey[200]), ); } @@ -116,10 +101,7 @@ class TopControlAppBar extends HookConsumerWidget { onPressed: () { onRestorePressed(); }, - icon: Icon( - Icons.history_rounded, - color: Colors.grey[200], - ), + icon: Icon(Icons.history_rounded, color: Colors.grey[200]), ); } @@ -131,19 +113,13 @@ class TopControlAppBar extends HookConsumerWidget { icon: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Icon( - Icons.mode_comment_outlined, - color: Colors.grey[200], - ), + Icon(Icons.mode_comment_outlined, color: Colors.grey[200]), if (comments != 0) Padding( padding: const EdgeInsets.only(left: 5), child: Text( comments.toString(), - style: TextStyle( - fontWeight: FontWeight.bold, - color: Colors.grey[200], - ), + style: TextStyle(fontWeight: FontWeight.bold, color: Colors.grey[200]), ), ), ], @@ -154,10 +130,7 @@ class TopControlAppBar extends HookConsumerWidget { Widget buildUploadButton() { return IconButton( onPressed: onUploadPressed, - icon: Icon( - Icons.backup_outlined, - color: Colors.grey[200], - ), + icon: Icon(Icons.backup_outlined, color: Colors.grey[200]), ); } @@ -166,21 +139,14 @@ class TopControlAppBar extends HookConsumerWidget { onPressed: () { context.maybePop(); }, - icon: Icon( - Icons.arrow_back_ios_new_rounded, - size: 20.0, - color: Colors.grey[200], - ), + icon: Icon(Icons.arrow_back_ios_new_rounded, size: 20.0, color: Colors.grey[200]), ); } Widget buildCastButton() { return IconButton( onPressed: () { - showDialog( - context: context, - builder: (context) => const CastDialog(), - ); + showDialog(context: context, builder: (context) => const CastDialog()); }, icon: Icon( isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded, diff --git a/mobile/lib/widgets/asset_viewer/video_controls.dart b/mobile/lib/widgets/asset_viewer/video_controls.dart index 22aa2b17d1..42f6078478 100644 --- a/mobile/lib/widgets/asset_viewer/video_controls.dart +++ b/mobile/lib/widgets/asset_viewer/video_controls.dart @@ -12,9 +12,6 @@ class VideoControls extends ConsumerWidget { final isPortrait = context.orientation == Orientation.portrait; return isPortrait ? const VideoPosition() - : const Padding( - padding: EdgeInsets.symmetric(horizontal: 60.0), - child: VideoPosition(), - ); + : const Padding(padding: EdgeInsets.symmetric(horizontal: 60.0), child: VideoPosition()); } } diff --git a/mobile/lib/widgets/asset_viewer/video_position.dart b/mobile/lib/widgets/asset_viewer/video_position.dart index 2bd2eb80bf..c12bb5e682 100644 --- a/mobile/lib/widgets/asset_viewer/video_position.dart +++ b/mobile/lib/widgets/asset_viewer/video_position.dart @@ -17,12 +17,8 @@ class VideoPosition extends HookConsumerWidget { final isCasting = ref.watch(castProvider).isCasting; final (position, duration) = isCasting - ? ref.watch( - castProvider.select((c) => (c.currentTime, c.duration)), - ) - : ref.watch( - videoPlaybackValueProvider.select((v) => (v.position, v.duration)), - ); + ? ref.watch(castProvider.select((c) => (c.currentTime, c.duration))) + : ref.watch(videoPlaybackValueProvider.select((v) => (v.position, v.duration))); final wasPlaying = useRef(true); return duration == Duration.zero @@ -34,20 +30,14 @@ class VideoPosition extends HookConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 12.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - FormattedDuration(position), - FormattedDuration(duration), - ], + children: [FormattedDuration(position), FormattedDuration(duration)], ), ), Row( children: [ Expanded( child: Slider( - value: min( - position.inMicroseconds / duration.inMicroseconds * 100, - 100, - ), + value: min(position.inMicroseconds / duration.inMicroseconds * 100, 100), min: 0, max: 100, thumbColor: Colors.white, @@ -98,10 +88,7 @@ class _VideoPositionPlaceholder extends StatelessWidget { padding: EdgeInsets.symmetric(horizontal: 12.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - FormattedDuration(Duration.zero), - FormattedDuration(Duration.zero), - ], + children: [FormattedDuration(Duration.zero), FormattedDuration(Duration.zero)], ), ), Row( diff --git a/mobile/lib/widgets/backup/album_info_card.dart b/mobile/lib/widgets/backup/album_info_card.dart index 696168a384..d635e136bc 100644 --- a/mobile/lib/widgets/backup/album_info_card.dart +++ b/mobile/lib/widgets/backup/album_info_card.dart @@ -16,10 +16,7 @@ import 'package:immich_mobile/widgets/common/immich_toast.dart'; class AlbumInfoCard extends HookConsumerWidget { final AvailableAlbum album; - const AlbumInfoCard({ - super.key, - required this.album, - }); + const AlbumInfoCard({super.key, required this.album}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -29,10 +26,7 @@ class AlbumInfoCard extends HookConsumerWidget { final isDarkTheme = context.isDarkTheme; - ColorFilter selectedFilter = ColorFilter.mode( - context.primaryColor.withAlpha(100), - BlendMode.darken, - ); + ColorFilter selectedFilter = ColorFilter.mode(context.primaryColor.withAlpha(100), BlendMode.darken); ColorFilter excludedFilter = ColorFilter.mode(Colors.red.withAlpha(75), BlendMode.darken); ColorFilter unselectedFilter = const ColorFilter.mode(Colors.black, BlendMode.color); @@ -40,9 +34,7 @@ class AlbumInfoCard extends HookConsumerWidget { if (isSelected) { return Chip( visualDensity: VisualDensity.compact, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(5)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), label: Text( "album_info_card_backup_album_included", style: TextStyle( @@ -56,9 +48,7 @@ class AlbumInfoCard extends HookConsumerWidget { } else if (isExcluded) { return Chip( visualDensity: VisualDensity.compact, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(5)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), label: Text( "album_info_card_backup_album_excluded", style: TextStyle( @@ -145,24 +135,16 @@ class AlbumInfoCard extends HookConsumerWidget { child: const Image( width: double.infinity, height: double.infinity, - image: AssetImage( - 'assets/immich-logo.png', - ), + image: AssetImage('assets/immich-logo.png'), fit: BoxFit.cover, ), ), - Positioned( - bottom: 10, - right: 25, - child: buildSelectedTextBox(), - ), + Positioned(bottom: 10, right: 25, child: buildSelectedTextBox()), ], ), ), Padding( - padding: const EdgeInsets.only( - left: 25, - ), + padding: const EdgeInsets.only(left: 25), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -173,20 +155,13 @@ class AlbumInfoCard extends HookConsumerWidget { children: [ Text( album.name, - style: TextStyle( - fontSize: 14, - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 14, color: context.primaryColor, fontWeight: FontWeight.bold), ), Padding( padding: const EdgeInsets.only(top: 2.0), child: Text( album.assetCount.toString() + (album.isAll ? " (${'all'.tr()})" : ""), - style: TextStyle( - fontSize: 12, - color: Colors.grey[600], - ), + style: TextStyle(fontSize: 12, color: Colors.grey[600]), ), ), ], @@ -194,15 +169,9 @@ class AlbumInfoCard extends HookConsumerWidget { ), IconButton( onPressed: () { - context.pushRoute( - AlbumPreviewRoute(album: album.album), - ); + context.pushRoute(AlbumPreviewRoute(album: album.album)); }, - icon: Icon( - Icons.image_outlined, - color: context.primaryColor, - size: 24, - ), + icon: Icon(Icons.image_outlined, color: context.primaryColor, size: 24), splashRadius: 25, ), ], diff --git a/mobile/lib/widgets/backup/album_info_list_tile.dart b/mobile/lib/widgets/backup/album_info_list_tile.dart index 7558b909bb..9796f45e8b 100644 --- a/mobile/lib/widgets/backup/album_info_list_tile.dart +++ b/mobile/lib/widgets/backup/album_info_list_tile.dart @@ -35,23 +35,14 @@ class AlbumInfoListTile extends HookConsumerWidget { buildIcon() { if (isSelected) { - return Icon( - Icons.check_circle_rounded, - color: context.colorScheme.primary, - ); + return Icon(Icons.check_circle_rounded, color: context.colorScheme.primary); } if (isExcluded) { - return Icon( - Icons.remove_circle_rounded, - color: context.colorScheme.error, - ); + return Icon(Icons.remove_circle_rounded, color: context.colorScheme.error); } - return Icon( - Icons.circle, - color: context.colorScheme.surfaceContainerHighest, - ); + return Icon(Icons.circle, color: context.colorScheme.surfaceContainerHighest); } return GestureDetector( @@ -92,25 +83,13 @@ class AlbumInfoListTile extends HookConsumerWidget { } }, leading: buildIcon(), - title: Text( - album.name, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ), + title: Text(album.name, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), subtitle: Text(album.assetCount.toString()), trailing: IconButton( onPressed: () { - context.pushRoute( - AlbumPreviewRoute(album: album.album), - ); + context.pushRoute(AlbumPreviewRoute(album: album.album)); }, - icon: Icon( - Icons.image_outlined, - color: context.primaryColor, - size: 24, - ), + icon: Icon(Icons.image_outlined, color: context.primaryColor, size: 24), splashRadius: 25, ), ), diff --git a/mobile/lib/widgets/backup/asset_info_table.dart b/mobile/lib/widgets/backup/asset_info_table.dart index a87832d3f1..2cccded2bb 100644 --- a/mobile/lib/widgets/backup/asset_info_table.dart +++ b/mobile/lib/widgets/backup/asset_info_table.dart @@ -14,9 +14,7 @@ class BackupAssetInfoTable extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final isManualUpload = ref.watch( - backupProvider.select( - (value) => value.backupProgress == BackUpProgressEnum.manualInProgress, - ), + backupProvider.select((value) => value.backupProgress == BackUpProgressEnum.manualInProgress), ); final isUploadInProgress = ref.watch( @@ -29,18 +27,13 @@ class BackupAssetInfoTable extends ConsumerWidget { ); final asset = isManualUpload - ? ref.watch( - manualUploadProvider.select((value) => value.currentUploadAsset), - ) + ? ref.watch(manualUploadProvider.select((value) => value.currentUploadAsset)) : ref.watch(backupProvider.select((value) => value.currentUploadAsset)); return Padding( padding: const EdgeInsets.only(top: 8.0), child: Table( - border: TableBorder.all( - color: context.colorScheme.outlineVariant, - width: 1, - ), + border: TableBorder.all(color: context.colorScheme.outlineVariant, width: 1), children: [ TableRow( children: [ @@ -48,21 +41,19 @@ class BackupAssetInfoTable extends ConsumerWidget { verticalAlignment: TableCellVerticalAlignment.middle, child: Padding( padding: const EdgeInsets.all(6.0), - child: Text( - 'backup_controller_page_filename', - style: TextStyle( - color: context.colorScheme.onSurfaceSecondary, - fontWeight: FontWeight.bold, - fontSize: 10.0, - ), - ).tr( - namedArgs: isUploadInProgress - ? { - 'filename': asset.fileName, - 'size': asset.fileType.toLowerCase(), - } - : {'filename': "-", 'size': "-"}, - ), + child: + Text( + 'backup_controller_page_filename', + style: TextStyle( + color: context.colorScheme.onSurfaceSecondary, + fontWeight: FontWeight.bold, + fontSize: 10.0, + ), + ).tr( + namedArgs: isUploadInProgress + ? {'filename': asset.fileName, 'size': asset.fileType.toLowerCase()} + : {'filename': "-", 'size': "-"}, + ), ), ), ], @@ -80,11 +71,7 @@ class BackupAssetInfoTable extends ConsumerWidget { fontWeight: FontWeight.bold, fontSize: 10.0, ), - ).tr( - namedArgs: { - 'date': isUploadInProgress ? _getAssetCreationDate(asset) : "-", - }, - ), + ).tr(namedArgs: {'date': isUploadInProgress ? _getAssetCreationDate(asset) : "-"}), ), ), ], @@ -101,9 +88,7 @@ class BackupAssetInfoTable extends ConsumerWidget { fontWeight: FontWeight.bold, fontSize: 10.0, ), - ).tr( - namedArgs: {'id': isUploadInProgress ? asset.id : "-"}, - ), + ).tr(namedArgs: {'id': isUploadInProgress ? asset.id : "-"}), ), ), ], diff --git a/mobile/lib/widgets/backup/backup_info_card.dart b/mobile/lib/widgets/backup/backup_info_card.dart index 54551da35a..767d94bbf9 100644 --- a/mobile/lib/widgets/backup/backup_info_card.dart +++ b/mobile/lib/widgets/backup/backup_info_card.dart @@ -7,12 +7,7 @@ class BackupInfoCard extends StatelessWidget { final String title; final String subtitle; final String info; - const BackupInfoCard({ - super.key, - required this.title, - required this.subtitle, - required this.info, - }); + const BackupInfoCard({super.key, required this.title, required this.subtitle, required this.info}); @override Widget build(BuildContext context) { @@ -21,40 +16,26 @@ class BackupInfoCard extends StatelessWidget { borderRadius: const BorderRadius.all( Radius.circular(20), // if you need this ), - side: BorderSide( - color: context.colorScheme.outlineVariant, - width: 1, - ), + side: BorderSide(color: context.colorScheme.outlineVariant, width: 1), ), elevation: 0, borderOnForeground: false, child: ListTile( minVerticalPadding: 18, isThreeLine: true, - title: Text( - title, - style: context.textTheme.titleMedium, - ), + title: Text(title, style: context.textTheme.titleMedium), subtitle: Padding( padding: const EdgeInsets.only(top: 4.0, right: 18.0), child: Text( subtitle, - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), ), trailing: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text( - info, - style: context.textTheme.titleLarge, - ), - Text( - "backup_info_card_assets", - style: context.textTheme.labelLarge, - ).tr(), + Text(info, style: context.textTheme.titleLarge), + Text("backup_info_card_assets", style: context.textTheme.labelLarge).tr(), ], ), ), diff --git a/mobile/lib/widgets/backup/current_backup_asset_info_box.dart b/mobile/lib/widgets/backup/current_backup_asset_info_box.dart index b6d0edb200..c2f94e706a 100644 --- a/mobile/lib/widgets/backup/current_backup_asset_info_box.dart +++ b/mobile/lib/widgets/backup/current_backup_asset_info_box.dart @@ -16,18 +16,11 @@ class CurrentUploadingAssetInfoBox extends StatelessWidget { Widget build(BuildContext context) { return ListTile( isThreeLine: true, - leading: Icon( - Icons.image_outlined, - color: context.primaryColor, - size: 30, - ), + leading: Icon(Icons.image_outlined, color: context.primaryColor, size: 30), title: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - "backup_controller_page_uploading_file_info", - style: context.textTheme.titleSmall, - ).tr(), + Text("backup_controller_page_uploading_file_info", style: context.textTheme.titleSmall).tr(), const BackupErrorChip(), ], ), diff --git a/mobile/lib/widgets/backup/drift_album_info_list_tile.dart b/mobile/lib/widgets/backup/drift_album_info_list_tile.dart index 25d5bfef28..cc3485e6f9 100644 --- a/mobile/lib/widgets/backup/drift_album_info_list_tile.dart +++ b/mobile/lib/widgets/backup/drift_album_info_list_tile.dart @@ -36,23 +36,14 @@ class DriftAlbumInfoListTile extends HookConsumerWidget { buildIcon() { if (isSelected) { - return Icon( - Icons.check_circle_rounded, - color: context.colorScheme.primary, - ); + return Icon(Icons.check_circle_rounded, color: context.colorScheme.primary); } if (isExcluded) { - return Icon( - Icons.remove_circle_rounded, - color: context.colorScheme.error, - ); + return Icon(Icons.remove_circle_rounded, color: context.colorScheme.error); } - return Icon( - Icons.circle, - color: context.colorScheme.surfaceContainerHighest, - ); + return Icon(Icons.circle, color: context.colorScheme.surfaceContainerHighest); } return GestureDetector( @@ -90,23 +81,13 @@ class DriftAlbumInfoListTile extends HookConsumerWidget { } }, leading: buildIcon(), - title: Text( - album.name, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ), + title: Text(album.name, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), subtitle: Text(album.assetCount.toString()), trailing: IconButton( onPressed: () { context.pushRoute(LocalTimelineRoute(album: album)); }, - icon: Icon( - Icons.image_outlined, - color: context.primaryColor, - size: 24, - ), + icon: Icon(Icons.image_outlined, color: context.primaryColor, size: 24), splashRadius: 25, ), ), diff --git a/mobile/lib/widgets/backup/error_chip.dart b/mobile/lib/widgets/backup/error_chip.dart index 3c743a0903..191049cd75 100644 --- a/mobile/lib/widgets/backup/error_chip.dart +++ b/mobile/lib/widgets/backup/error_chip.dart @@ -17,10 +17,7 @@ class BackupErrorChip extends ConsumerWidget { } return ActionChip( - avatar: const Icon( - Icons.info, - color: red400, - ), + avatar: const Icon(Icons.info, color: red400), elevation: 1, visualDensity: VisualDensity.compact, label: const BackupErrorChipText(), diff --git a/mobile/lib/widgets/backup/error_chip_text.dart b/mobile/lib/widgets/backup/error_chip_text.dart index 38c527ccfa..c987dfd331 100644 --- a/mobile/lib/widgets/backup/error_chip_text.dart +++ b/mobile/lib/widgets/backup/error_chip_text.dart @@ -16,11 +16,7 @@ class BackupErrorChipText extends ConsumerWidget { return const Text( "backup_controller_page_failed", - style: TextStyle( - color: red400, - fontWeight: FontWeight.bold, - fontSize: 11, - ), + style: TextStyle(color: red400, fontWeight: FontWeight.bold, fontSize: 11), ).tr(namedArgs: {'count': count.toString()}); } } diff --git a/mobile/lib/widgets/backup/icloud_download_progress_bar.dart b/mobile/lib/widgets/backup/icloud_download_progress_bar.dart index 7d292112ea..9f0f7ec3eb 100644 --- a/mobile/lib/widgets/backup/icloud_download_progress_bar.dart +++ b/mobile/lib/widgets/backup/icloud_download_progress_bar.dart @@ -10,18 +10,12 @@ class IcloudDownloadProgressBar extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final isManualUpload = ref.watch( - backupProvider.select( - (value) => value.backupProgress == BackUpProgressEnum.manualInProgress, - ), + backupProvider.select((value) => value.backupProgress == BackUpProgressEnum.manualInProgress), ); final isIcloudAsset = isManualUpload - ? ref.watch( - manualUploadProvider.select((value) => value.currentUploadAsset.isIcloudAsset), - ) - : ref.watch( - backupProvider.select((value) => value.currentUploadAsset.isIcloudAsset), - ); + ? ref.watch(manualUploadProvider.select((value) => value.currentUploadAsset.isIcloudAsset)) + : ref.watch(backupProvider.select((value) => value.currentUploadAsset.isIcloudAsset)); if (!isIcloudAsset) { return const SizedBox(); @@ -33,13 +27,7 @@ class IcloudDownloadProgressBar extends ConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Row( children: [ - SizedBox( - width: 110, - child: Text( - "iCloud Download", - style: context.textTheme.labelSmall, - ), - ), + SizedBox(width: 110, child: Text("iCloud Download", style: context.textTheme.labelSmall)), Expanded( child: LinearProgressIndicator( minHeight: 10.0, @@ -47,10 +35,7 @@ class IcloudDownloadProgressBar extends ConsumerWidget { borderRadius: const BorderRadius.all(Radius.circular(10.0)), ), ), - Text( - " ${iCloudDownloadProgress ~/ 1}%", - style: const TextStyle(fontSize: 12), - ), + Text(" ${iCloudDownloadProgress ~/ 1}%", style: const TextStyle(fontSize: 12)), ], ), ); diff --git a/mobile/lib/widgets/backup/ios_debug_info_tile.dart b/mobile/lib/widgets/backup/ios_debug_info_tile.dart index 0f96e4cef6..be333c6460 100644 --- a/mobile/lib/widgets/backup/ios_debug_info_tile.dart +++ b/mobile/lib/widgets/backup/ios_debug_info_tile.dart @@ -9,10 +9,7 @@ import 'package:immich_mobile/providers/backup/ios_background_settings.provider. /// more confident about background sync class IosDebugInfoTile extends HookConsumerWidget { final IOSBackgroundSettings settings; - const IosDebugInfoTile({ - super.key, - required this.settings, - }); + const IosDebugInfoTile({super.key, required this.settings}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -37,31 +34,16 @@ class IosDebugInfoTile extends HookConsumerWidget { subtitle = 'ios_debug_info_processing_ran_at'.t(context: context, args: {'dateTime': df.format(processing)}); } else { final fetchOrProcessing = fetch!.isAfter(processing!) ? fetch : processing; - subtitle = 'ios_debug_info_last_sync_at'.t( - context: context, - args: {'dateTime': df.format(fetchOrProcessing)}, - ); + subtitle = 'ios_debug_info_last_sync_at'.t(context: context, args: {'dateTime': df.format(fetchOrProcessing)}); } return ListTile( title: Text( title, - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 14, - color: context.primaryColor, - ), - ), - subtitle: Text( - subtitle, - style: const TextStyle( - fontSize: 14, - ), - ), - leading: Icon( - Icons.bug_report, - color: context.primaryColor, + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14, color: context.primaryColor), ), + subtitle: Text(subtitle, style: const TextStyle(fontSize: 14)), + leading: Icon(Icons.bug_report, color: context.primaryColor), ); } } diff --git a/mobile/lib/widgets/backup/upload_progress_bar.dart b/mobile/lib/widgets/backup/upload_progress_bar.dart index b11a8562b9..65ff6c758a 100644 --- a/mobile/lib/widgets/backup/upload_progress_bar.dart +++ b/mobile/lib/widgets/backup/upload_progress_bar.dart @@ -11,39 +11,22 @@ class BackupUploadProgressBar extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final isManualUpload = ref.watch( - backupProvider.select( - (value) => value.backupProgress == BackUpProgressEnum.manualInProgress, - ), + backupProvider.select((value) => value.backupProgress == BackUpProgressEnum.manualInProgress), ); final isIcloudAsset = isManualUpload - ? ref.watch( - manualUploadProvider.select((value) => value.currentUploadAsset.isIcloudAsset), - ) - : ref.watch( - backupProvider.select((value) => value.currentUploadAsset.isIcloudAsset), - ); + ? ref.watch(manualUploadProvider.select((value) => value.currentUploadAsset.isIcloudAsset)) + : ref.watch(backupProvider.select((value) => value.currentUploadAsset.isIcloudAsset)); final uploadProgress = isManualUpload - ? ref.watch( - manualUploadProvider.select((value) => value.progressInPercentage), - ) - : ref.watch( - backupProvider.select((value) => value.progressInPercentage), - ); + ? ref.watch(manualUploadProvider.select((value) => value.progressInPercentage)) + : ref.watch(backupProvider.select((value) => value.progressInPercentage)); return Padding( padding: const EdgeInsets.only(top: 8.0), child: Row( children: [ - if (isIcloudAsset) - SizedBox( - width: 110, - child: Text( - "Immich Upload", - style: context.textTheme.labelSmall, - ), - ), + if (isIcloudAsset) SizedBox(width: 110, child: Text("Immich Upload", style: context.textTheme.labelSmall)), Expanded( child: LinearProgressIndicator( minHeight: 10.0, diff --git a/mobile/lib/widgets/backup/upload_stats.dart b/mobile/lib/widgets/backup/upload_stats.dart index 965202ce33..c9b626c51c 100644 --- a/mobile/lib/widgets/backup/upload_stats.dart +++ b/mobile/lib/widgets/backup/upload_stats.dart @@ -10,34 +10,23 @@ class BackupUploadStats extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final isManualUpload = ref.watch( - backupProvider.select( - (value) => value.backupProgress == BackUpProgressEnum.manualInProgress, - ), + backupProvider.select((value) => value.backupProgress == BackUpProgressEnum.manualInProgress), ); final uploadFileProgress = isManualUpload - ? ref.watch( - manualUploadProvider.select((value) => value.progressInFileSize), - ) + ? ref.watch(manualUploadProvider.select((value) => value.progressInFileSize)) : ref.watch(backupProvider.select((value) => value.progressInFileSize)); final uploadFileSpeed = isManualUpload - ? ref.watch( - manualUploadProvider.select((value) => value.progressInFileSpeed), - ) - : ref.watch( - backupProvider.select((value) => value.progressInFileSpeed), - ); + ? ref.watch(manualUploadProvider.select((value) => value.progressInFileSpeed)) + : ref.watch(backupProvider.select((value) => value.progressInFileSpeed)); return Padding( padding: const EdgeInsets.only(top: 2.0, bottom: 2.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - uploadFileProgress, - style: const TextStyle(fontSize: 10, fontFamily: "OverpassMono"), - ), + Text(uploadFileProgress, style: const TextStyle(fontSize: 10, fontFamily: "OverpassMono")), Text( _formatUploadFileSpeed(uploadFileSpeed), style: const TextStyle(fontSize: 10, fontFamily: "OverpassMono"), diff --git a/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart b/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart index e0fa03ba01..ccfc374fef 100644 --- a/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart +++ b/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart @@ -34,27 +34,18 @@ class ImmichAppBarDialog extends HookConsumerWidget { final user = ref.watch(currentUserProvider); final isLoggingOut = useState(false); - useEffect( - () { - ref.read(backupProvider.notifier).updateDiskInfo(); - ref.read(currentUserProvider.notifier).refresh(); - return null; - }, - [], - ); + useEffect(() { + ref.read(backupProvider.notifier).updateDiskInfo(); + ref.read(currentUserProvider.notifier).refresh(); + return null; + }, []); buildTopRow() { return Stack( children: [ Align( alignment: Alignment.topLeft, - child: InkWell( - onTap: () => context.pop(), - child: const Icon( - Icons.close, - size: 20, - ), - ), + child: InkWell(onTap: () => context.pop(), child: const Icon(Icons.close, size: 20)), ), Center( child: Image.asset( @@ -66,29 +57,16 @@ class ImmichAppBarDialog extends HookConsumerWidget { ); } - buildActionButton( - IconData icon, - String text, - Function() onTap, { - Widget? trailing, - }) { + buildActionButton(IconData icon, String text, Function() onTap, {Widget? trailing}) { return ListTile( dense: true, visualDensity: VisualDensity.standard, contentPadding: const EdgeInsets.only(left: 30, right: 30), minLeadingWidth: 40, - leading: SizedBox( - child: Icon( - icon, - color: theme.textTheme.labelLarge?.color?.withAlpha(250), - size: 20, - ), - ), + leading: SizedBox(child: Icon(icon, color: theme.textTheme.labelLarge?.color?.withAlpha(250), size: 20)), title: Text( text, - style: theme.textTheme.labelLarge?.copyWith( - color: theme.textTheme.labelLarge?.color?.withAlpha(250), - ), + style: theme.textTheme.labelLarge?.copyWith(color: theme.textTheme.labelLarge?.color?.withAlpha(250)), ).tr(), onTap: onTap, trailing: trailing, @@ -96,11 +74,7 @@ class ImmichAppBarDialog extends HookConsumerWidget { } buildSettingButton() { - return buildActionButton( - Icons.settings_outlined, - "settings", - () => context.pushRoute(const SettingsRoute()), - ); + return buildActionButton(Icons.settings_outlined, "settings", () => context.pushRoute(const SettingsRoute())); } buildAppLogButton() { @@ -142,10 +116,7 @@ class ImmichAppBarDialog extends HookConsumerWidget { ); }, trailing: isLoggingOut.value - ? const SizedBox.square( - dimension: 20, - child: CircularProgressIndicator(strokeWidth: 2), - ) + ? const SizedBox.square(dimension: 20, child: CircularProgressIndicator(strokeWidth: 2)) : null, ); } @@ -165,20 +136,13 @@ class ImmichAppBarDialog extends HookConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 3), child: Container( padding: const EdgeInsets.symmetric(vertical: 4), - decoration: BoxDecoration( - color: context.colorScheme.surface, - ), + decoration: BoxDecoration(color: context.colorScheme.surface), child: ListTile( minLeadingWidth: 50, - leading: Icon( - Icons.storage_rounded, - color: theme.primaryColor, - ), + leading: Icon(Icons.storage_rounded, color: theme.primaryColor), title: Text( "backup_controller_page_server_storage", - style: context.textTheme.labelLarge?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.w500), ).tr(), isThreeLine: true, subtitle: Padding( @@ -196,12 +160,9 @@ class ImmichAppBarDialog extends HookConsumerWidget { ), Padding( padding: const EdgeInsets.only(top: 12.0), - child: const Text('backup_controller_page_storage_format').tr( - namedArgs: { - 'used': usedDiskSpace, - 'total': totalDiskSpace, - }, - ), + child: const Text( + 'backup_controller_page_storage_format', + ).tr(namedArgs: {'used': usedDiskSpace, 'total': totalDiskSpace}), ), ], ), @@ -220,43 +181,19 @@ class ImmichAppBarDialog extends HookConsumerWidget { InkWell( onTap: () { context.pop(); - launchUrl( - Uri.parse('https://immich.app'), - mode: LaunchMode.externalApplication, - ); + launchUrl(Uri.parse('https://immich.app'), mode: LaunchMode.externalApplication); }, - child: Text( - "documentation", - style: context.textTheme.bodySmall, - ).tr(), - ), - const SizedBox( - width: 20, - child: Text( - "•", - textAlign: TextAlign.center, - ), + child: Text("documentation", style: context.textTheme.bodySmall).tr(), ), + const SizedBox(width: 20, child: Text("•", textAlign: TextAlign.center)), InkWell( onTap: () { context.pop(); - launchUrl( - Uri.parse('https://github.com/immich-app/immich'), - mode: LaunchMode.externalApplication, - ); + launchUrl(Uri.parse('https://github.com/immich-app/immich'), mode: LaunchMode.externalApplication); }, - child: Text( - "profile_drawer_github", - style: context.textTheme.bodySmall, - ).tr(), - ), - const SizedBox( - width: 20, - child: Text( - "•", - textAlign: TextAlign.center, - ), + child: Text("profile_drawer_github", style: context.textTheme.bodySmall).tr(), ), + const SizedBox(width: 20, child: Text("•", textAlign: TextAlign.center)), InkWell( onTap: () async { context.pop(); @@ -291,20 +228,13 @@ class ImmichAppBarDialog extends HookConsumerWidget { right: horizontalPadding, bottom: isHorizontal ? 20 : 100, ), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(20), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20))), child: SizedBox( child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ - Container( - padding: const EdgeInsets.all(20), - child: buildTopRow(), - ), + Container(padding: const EdgeInsets.all(20), child: buildTopRow()), const AppBarProfileInfoBox(), buildStorageInformation(), const AppBarServerInfo(), diff --git a/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart b/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart index 080c17e951..d12ee7b0d1 100644 --- a/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart +++ b/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart @@ -10,9 +10,7 @@ import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart'; import 'package:immich_mobile/widgets/common/user_circle_avatar.dart'; class AppBarProfileInfoBox extends HookConsumerWidget { - const AppBarProfileInfoBox({ - super.key, - }); + const AppBarProfileInfoBox({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -29,38 +27,24 @@ class AppBarProfileInfoBox extends HookConsumerWidget { ); } - final userImage = UserCircleAvatar( - radius: 22, - size: 44, - user: user, - ); + final userImage = UserCircleAvatar(radius: 22, size: 44, user: user); if (uploadProfileImageStatus == UploadProfileStatus.loading) { - return const SizedBox( - height: 40, - width: 40, - child: ImmichLoadingIndicator(borderRadius: 20), - ); + return const SizedBox(height: 40, width: 40, child: ImmichLoadingIndicator(borderRadius: 20)); } return userImage; } pickUserProfileImage() async { - final XFile? image = await ImagePicker().pickImage( - source: ImageSource.gallery, - maxHeight: 1024, - maxWidth: 1024, - ); + final XFile? image = await ImagePicker().pickImage(source: ImageSource.gallery, maxHeight: 1024, maxWidth: 1024); if (image != null) { var success = await ref.watch(uploadProfileImageProvider.notifier).upload(image); if (success) { final profileImagePath = ref.read(uploadProfileImageProvider).profileImagePath; - ref.watch(authProvider.notifier).updateUserProfileImagePath( - profileImagePath, - ); + ref.watch(authProvider.notifier).updateUserProfileImagePath(profileImagePath); if (user != null) { ref.read(currentUserProvider.notifier).refresh(); } @@ -74,10 +58,7 @@ class AppBarProfileInfoBox extends HookConsumerWidget { width: double.infinity, decoration: BoxDecoration( color: context.colorScheme.surface, - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(10), - topRight: Radius.circular(10), - ), + borderRadius: const BorderRadius.only(topLeft: Radius.circular(10), topRight: Radius.circular(10)), ), child: ListTile( minLeadingWidth: 50, @@ -93,16 +74,10 @@ class AppBarProfileInfoBox extends HookConsumerWidget { child: Material( color: context.colorScheme.surfaceContainerHighest, elevation: 3, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(50.0)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(50.0))), child: Padding( padding: const EdgeInsets.all(5.0), - child: Icon( - Icons.camera_alt_outlined, - color: context.primaryColor, - size: 14, - ), + child: Icon(Icons.camera_alt_outlined, color: context.primaryColor, size: 14), ), ), ), @@ -111,16 +86,11 @@ class AppBarProfileInfoBox extends HookConsumerWidget { ), title: Text( authState.name, - style: context.textTheme.titleMedium?.copyWith( - color: context.primaryColor, - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleMedium?.copyWith(color: context.primaryColor, fontWeight: FontWeight.w500), ), subtitle: Text( authState.userEmail, - style: context.textTheme.bodySmall?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodySmall?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), ), ), diff --git a/mobile/lib/widgets/common/app_bar_dialog/app_bar_server_info.dart b/mobile/lib/widgets/common/app_bar_dialog/app_bar_server_info.dart index 6b990c1c08..4aacfb3322 100644 --- a/mobile/lib/widgets/common/app_bar_dialog/app_bar_server_info.dart +++ b/mobile/lib/widgets/common/app_bar_dialog/app_bar_server_info.dart @@ -11,9 +11,7 @@ import 'package:immich_mobile/utils/url_helper.dart'; import 'package:package_info_plus/package_info_plus.dart'; class AppBarServerInfo extends HookConsumerWidget { - const AppBarServerInfo({ - super.key, - }); + const AppBarServerInfo({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -27,29 +25,20 @@ class AppBarServerInfo extends HookConsumerWidget { getPackageInfo() async { PackageInfo packageInfo = await PackageInfo.fromPlatform(); - appInfo.value = { - "version": packageInfo.version, - "buildNumber": packageInfo.buildNumber, - }; + appInfo.value = {"version": packageInfo.version, "buildNumber": packageInfo.buildNumber}; } - useEffect( - () { - getPackageInfo(); - return null; - }, - [], - ); + useEffect(() { + getPackageInfo(); + return null; + }, []); return Padding( padding: const EdgeInsets.only(left: 10.0, right: 10.0, bottom: 10.0), child: Container( decoration: BoxDecoration( color: context.colorScheme.surface, - borderRadius: const BorderRadius.only( - bottomLeft: Radius.circular(10), - bottomRight: Radius.circular(10), - ), + borderRadius: const BorderRadius.only(bottomLeft: Radius.circular(10), bottomRight: Radius.circular(10)), ), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8), @@ -63,17 +52,10 @@ class AppBarServerInfo extends HookConsumerWidget { ? serverInfoState.versionMismatchErrorMessage : "profile_drawer_client_server_up_to_date".tr(), textAlign: TextAlign.center, - style: TextStyle( - fontSize: 11, - color: context.primaryColor, - fontWeight: FontWeight.w500, - ), + style: TextStyle(fontSize: 11, color: context.primaryColor, fontWeight: FontWeight.w500), ), ), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 10), - child: Divider(thickness: 1), - ), + const Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: Divider(thickness: 1)), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -106,10 +88,7 @@ class AppBarServerInfo extends HookConsumerWidget { ), ], ), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 10), - child: Divider(thickness: 1), - ), + const Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: Divider(thickness: 1)), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -144,10 +123,7 @@ class AppBarServerInfo extends HookConsumerWidget { ), ], ), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 10), - child: Divider(thickness: 1), - ), + const Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: Divider(thickness: 1)), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -197,10 +173,7 @@ class AppBarServerInfo extends HookConsumerWidget { ), ], ), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 10), - child: Divider(thickness: 1), - ), + const Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: Divider(thickness: 1)), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -212,11 +185,7 @@ class AppBarServerInfo extends HookConsumerWidget { if (serverInfoState.isNewReleaseAvailable) const Padding( padding: EdgeInsets.only(right: 5.0), - child: Icon( - Icons.info, - color: Color.fromARGB(255, 243, 188, 106), - size: 12, - ), + child: Icon(Icons.info, color: Color.fromARGB(255, 243, 188, 106), size: 12), ), Text( "latest_version".tr(), diff --git a/mobile/lib/widgets/common/confirm_dialog.dart b/mobile/lib/widgets/common/confirm_dialog.dart index 411770e78f..153c124595 100644 --- a/mobile/lib/widgets/common/confirm_dialog.dart +++ b/mobile/lib/widgets/common/confirm_dialog.dart @@ -26,9 +26,7 @@ class ConfirmDialog extends StatelessWidget { } return AlertDialog( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), title: Text(title).tr(), content: Text(content).tr(), actions: [ @@ -36,20 +34,14 @@ class ConfirmDialog extends StatelessWidget { onPressed: () => context.pop(false), child: Text( cancel, - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold), ).tr(), ), TextButton( onPressed: onOkPressed, child: Text( ok, - style: TextStyle( - color: context.colorScheme.error, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.colorScheme.error, fontWeight: FontWeight.bold), ).tr(), ), ], diff --git a/mobile/lib/widgets/common/date_time_picker.dart b/mobile/lib/widgets/common/date_time_picker.dart index 92d0b48684..113462c6c8 100644 --- a/mobile/lib/widgets/common/date_time_picker.dart +++ b/mobile/lib/widgets/common/date_time_picker.dart @@ -16,11 +16,8 @@ Future showDateTimePicker({ }) { return showDialog( context: context, - builder: (context) => _DateTimePicker( - initialDateTime: initialDateTime, - initialTZ: initialTZ, - initialTZOffset: initialTZOffset, - ), + builder: (context) => + _DateTimePicker(initialDateTime: initialDateTime, initialTZ: initialTZ, initialTZOffset: initialTZOffset), ); } @@ -33,18 +30,12 @@ class _DateTimePicker extends HookWidget { final String? initialTZ; final Duration? initialTZOffset; - const _DateTimePicker({ - this.initialDateTime, - this.initialTZ, - this.initialTZOffset, - }); + const _DateTimePicker({this.initialDateTime, this.initialTZ, this.initialTZOffset}); _TimeZoneOffset _getInitiationLocation() { if (initialTZ != null) { try { - return _TimeZoneOffset.fromLocation( - tz.timeZoneDatabase.get(initialTZ!), - ); + return _TimeZoneOffset.fromLocation(tz.timeZoneDatabase.get(initialTZ!)); } on LocationNotFoundException { // no-op } @@ -59,10 +50,8 @@ class _DateTimePicker extends HookWidget { (location) => location.currentTimeZone.offset == offsetInMilli, ); // Prefer locations with abbreviation first - final location = locations.firstWhereOrNull( - (e) => !e.currentTimeZone.abbreviation.contains("0"), - ) ?? - locations.firstOrNull; + final location = + locations.firstWhereOrNull((e) => !e.currentTimeZone.abbreviation.contains("0")) ?? locations.firstOrNull; if (location != null) { return _TimeZoneOffset.fromLocation(location); } @@ -86,11 +75,7 @@ class _DateTimePicker extends HookWidget { (timezone) => DropdownMenuEntry<_TimeZoneOffset>( value: timezone, label: timezone.display, - style: ButtonStyle( - textStyle: WidgetStatePropertyAll( - context.textTheme.bodyMedium, - ), - ), + style: ButtonStyle(textStyle: WidgetStatePropertyAll(context.textTheme.bodyMedium)), ), ) .toList(); @@ -109,10 +94,7 @@ class _DateTimePicker extends HookWidget { return; } - final newTime = await showTimePicker( - context: context, - initialTime: TimeOfDay.fromDateTime(date.value), - ); + final newTime = await showTimePicker(context: context, initialTime: TimeOfDay.fromDateTime(date.value)); if (newTime == null) { return; @@ -145,10 +127,7 @@ class _DateTimePicker extends HookWidget { onPressed: popWithDateTime, child: Text( "action_common_update", - style: context.textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w600, - color: context.primaryColor, - ), + style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor), ).tr(), ), ], @@ -156,46 +135,22 @@ class _DateTimePicker extends HookWidget { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( - "date_and_time", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - ), - ).tr(), + const Text("date_and_time", style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600)).tr(), const SizedBox(height: 32), ListTile( tileColor: context.colorScheme.surfaceContainerHighest, shape: ShapeBorder.lerp( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(10), - ), - ), - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(10), - ), - ), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), 1, ), - trailing: Icon( - Icons.edit_outlined, - size: 18, - color: context.primaryColor, - ), - title: Text( - DateFormat("dd-MM-yyyy hh:mm a").format(date.value), - style: context.textTheme.bodyMedium, - ).tr(), + trailing: Icon(Icons.edit_outlined, size: 18, color: context.primaryColor), + title: Text(DateFormat("dd-MM-yyyy hh:mm a").format(date.value), style: context.textTheme.bodyMedium).tr(), onTap: pickDate, ), const SizedBox(height: 24), DropdownSearchMenu( - trailingIcon: Icon( - Icons.arrow_drop_down, - color: context.primaryColor, - ), + trailingIcon: Icon(Icons.arrow_drop_down, color: context.primaryColor), hintText: "timezone".tr(), label: const Text('timezone').tr(), textStyle: context.textTheme.bodyMedium, @@ -213,26 +168,17 @@ class _TimeZoneOffset implements Comparable<_TimeZoneOffset> { final String display; final Location location; - const _TimeZoneOffset({ - required this.display, - required this.location, - }); + const _TimeZoneOffset({required this.display, required this.location}); - _TimeZoneOffset copyWith({ - String? display, - Location? location, - }) { - return _TimeZoneOffset( - display: display ?? this.display, - location: location ?? this.location, - ); + _TimeZoneOffset copyWith({String? display, Location? location}) { + return _TimeZoneOffset(display: display ?? this.display, location: location ?? this.location); } int get offsetInMilliseconds => location.currentTimeZone.offset; _TimeZoneOffset.fromLocation(tz.Location l) - : display = _getFormattedOffset(l.currentTimeZone.offset, l), - location = l; + : display = _getFormattedOffset(l.currentTimeZone.offset, l), + location = l; @override int compareTo(_TimeZoneOffset other) { diff --git a/mobile/lib/widgets/common/delayed_loading_indicator.dart b/mobile/lib/widgets/common/delayed_loading_indicator.dart index e54762bb9f..5fad10530e 100644 --- a/mobile/lib/widgets/common/delayed_loading_indicator.dart +++ b/mobile/lib/widgets/common/delayed_loading_indicator.dart @@ -11,12 +11,7 @@ class DelayedLoadingIndicator extends StatelessWidget { /// An optional fade in duration to animate the loading final Duration? fadeInDuration; - const DelayedLoadingIndicator({ - super.key, - this.delay = const Duration(seconds: 3), - this.child, - this.fadeInDuration, - }); + const DelayedLoadingIndicator({super.key, this.delay = const Duration(seconds: 3), this.child, this.fadeInDuration}); @override Widget build(BuildContext context) { @@ -25,18 +20,12 @@ class DelayedLoadingIndicator extends StatelessWidget { builder: (context, snapshot) { late Widget c; if (snapshot.connectionState == ConnectionState.done) { - c = child ?? - const ImmichLoadingIndicator( - key: ValueKey('loading'), - ); + c = child ?? const ImmichLoadingIndicator(key: ValueKey('loading')); } else { c = Container(key: const ValueKey('hiding')); } - return AnimatedSwitcher( - duration: fadeInDuration ?? Duration.zero, - child: c, - ); + return AnimatedSwitcher(duration: fadeInDuration ?? Duration.zero, child: c); }, ); } diff --git a/mobile/lib/widgets/common/drag_sheet.dart b/mobile/lib/widgets/common/drag_sheet.dart index 0e4651d819..5d1fda1beb 100644 --- a/mobile/lib/widgets/common/drag_sheet.dart +++ b/mobile/lib/widgets/common/drag_sheet.dart @@ -18,13 +18,7 @@ class CustomDraggingHandle extends StatelessWidget { } class ControlBoxButton extends StatelessWidget { - const ControlBoxButton({ - super.key, - required this.label, - required this.iconData, - this.onPressed, - this.onLongPressed, - }); + const ControlBoxButton({super.key, required this.label, required this.iconData, this.onPressed, this.onLongPressed}); final String label; final IconData iconData; @@ -37,9 +31,7 @@ class ControlBoxButton extends StatelessWidget { return MaterialButton( padding: const EdgeInsets.all(10), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(20)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20))), onPressed: onPressed, onLongPress: onLongPressed, minWidth: minWidth, diff --git a/mobile/lib/widgets/common/dropdown_search_menu.dart b/mobile/lib/widgets/common/dropdown_search_menu.dart index cde60b2afb..bf0c75c8aa 100644 --- a/mobile/lib/widgets/common/dropdown_search_menu.dart +++ b/mobile/lib/widgets/common/dropdown_search_menu.dart @@ -34,13 +34,8 @@ class DropdownSearchMenu extends HookWidget { ); final showTimeZoneDropdown = useState(false); - final effectiveConstraints = menuConstraints ?? - const BoxConstraints( - minWidth: 280, - maxWidth: 280, - minHeight: 0, - maxHeight: 280, - ); + final effectiveConstraints = + menuConstraints ?? const BoxConstraints(minWidth: 280, maxWidth: 280, minHeight: 0, maxHeight: 280); final inputDecoration = InputDecoration( contentPadding: const EdgeInsets.fromLTRB(12, 4, 12, 4), @@ -58,12 +53,7 @@ class DropdownSearchMenu extends HookWidget { child: InputDecorator( decoration: inputDecoration, child: selectedItem.value != null - ? Text( - selectedItem.value!.label, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: textStyle, - ) + ? Text(selectedItem.value!.label, maxLines: 1, overflow: TextOverflow.ellipsis, style: textStyle) : null, ), ), @@ -89,9 +79,7 @@ class DropdownSearchMenu extends HookWidget { autofocus: true, focusNode: focusNode, controller: textEditingController, - decoration: inputDecoration.copyWith( - hintText: "search_timezone".tr(), - ), + decoration: inputDecoration.copyWith(hintText: "search_timezone".tr()), maxLines: 1, style: context.textTheme.bodyMedium, expands: false, @@ -125,23 +113,14 @@ class DropdownSearchMenu extends HookWidget { builder: (BuildContext context) { final bool highlight = AutocompleteHighlightedOption.of(context) == index; if (highlight) { - SchedulerBinding.instance.addPostFrameCallback( - (Duration timeStamp) { - Scrollable.ensureVisible( - context, - alignment: 0.5, - ); - }, - debugLabel: 'AutocompleteOptions.ensureVisible', - ); + SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) { + Scrollable.ensureVisible(context, alignment: 0.5); + }, debugLabel: 'AutocompleteOptions.ensureVisible'); } return Container( color: highlight ? Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.12) : null, padding: const EdgeInsets.all(16.0), - child: Text( - option.label, - style: textStyle, - ), + child: Text(option.label, style: textStyle), ); }, ), diff --git a/mobile/lib/widgets/common/fade_in_placeholder_image.dart b/mobile/lib/widgets/common/fade_in_placeholder_image.dart index 2be32fa8ba..2461dbe6bf 100644 --- a/mobile/lib/widgets/common/fade_in_placeholder_image.dart +++ b/mobile/lib/widgets/common/fade_in_placeholder_image.dart @@ -22,12 +22,7 @@ class FadeInPlaceholderImage extends StatelessWidget { fit: StackFit.expand, children: [ placeholder, - FadeInImage( - fadeInDuration: duration, - image: image, - fit: fit, - placeholder: MemoryImage(kTransparentImage), - ), + FadeInImage(fadeInDuration: duration, image: image, fit: fit, placeholder: MemoryImage(kTransparentImage)), ], ), ); diff --git a/mobile/lib/widgets/common/immich_app_bar.dart b/mobile/lib/widgets/common/immich_app_bar.dart index 0d77e02aa5..7eaedd27b5 100644 --- a/mobile/lib/widgets/common/immich_app_bar.dart +++ b/mobile/lib/widgets/common/immich_app_bar.dart @@ -36,23 +36,13 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { buildProfileIndicator() { return InkWell( - onTap: () => showDialog( - context: context, - useRootNavigator: false, - builder: (ctx) => const ImmichAppBarDialog(), - ), + onTap: () => + showDialog(context: context, useRootNavigator: false, builder: (ctx) => const ImmichAppBarDialog()), borderRadius: const BorderRadius.all(Radius.circular(12)), child: Badge( label: Container( - decoration: BoxDecoration( - color: Colors.black, - borderRadius: BorderRadius.circular(widgetSize / 2), - ), - child: const Icon( - Icons.info, - color: Color.fromARGB(255, 243, 188, 106), - size: widgetSize / 2, - ), + decoration: BoxDecoration(color: Colors.black, borderRadius: BorderRadius.circular(widgetSize / 2)), + child: const Icon(Icons.info, color: Color.fromARGB(255, 243, 188, 106), size: widgetSize / 2), ), backgroundColor: Colors.transparent, alignment: Alignment.bottomRight, @@ -60,17 +50,10 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { serverInfoState.isVersionMismatch || ((user?.isAdmin ?? false) && serverInfoState.isNewReleaseAvailable), offset: const Offset(-2, -12), child: user == null - ? const Icon( - Icons.face_outlined, - size: widgetSize, - ) + ? const Icon(Icons.face_outlined, size: widgetSize) : Semantics( label: "logged_in_as".tr(namedArgs: {"user": user.name}), - child: UserCircleAvatar( - radius: 17, - size: 31, - user: user, - ), + child: UserCircleAvatar(radius: 17, size: 31, user: user), ), ), ); @@ -124,9 +107,7 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { height: widgetSize / 2, decoration: BoxDecoration( color: badgeBackground, - border: Border.all( - color: context.colorScheme.outline.withValues(alpha: .3), - ), + border: Border.all(color: context.colorScheme.outline.withValues(alpha: .3)), borderRadius: BorderRadius.circular(widgetSize / 2), ), child: indicatorIcon, @@ -135,22 +116,14 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { alignment: Alignment.bottomRight, isLabelVisible: indicatorIcon != null, offset: const Offset(-2, -12), - child: Icon( - Icons.backup_rounded, - size: widgetSize, - color: context.primaryColor, - ), + child: Icon(Icons.backup_rounded, size: widgetSize, color: context.primaryColor), ), ); } return AppBar( backgroundColor: context.themeData.appBarTheme.backgroundColor, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(5), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), automaticallyImplyLeading: false, centerTitle: false, title: Builder( @@ -176,12 +149,7 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { ), actions: [ if (actions != null) - ...actions!.map( - (action) => Padding( - padding: const EdgeInsets.only(right: 16), - child: action, - ), - ), + ...actions!.map((action) => Padding(padding: const EdgeInsets.only(right: 16), child: action)), if (kDebugMode || kProfileMode) IconButton( icon: const Icon(Icons.science_rounded), @@ -192,25 +160,13 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { padding: const EdgeInsets.only(right: 12), child: IconButton( onPressed: () { - showDialog( - context: context, - builder: (context) => const CastDialog(), - ); + showDialog(context: context, builder: (context) => const CastDialog()); }, - icon: Icon( - isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded, - ), + icon: Icon(isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded), ), ), - if (showUploadButton) - Padding( - padding: const EdgeInsets.only(right: 20), - child: buildBackupIndicator(), - ), - Padding( - padding: const EdgeInsets.only(right: 20), - child: buildProfileIndicator(), - ), + if (showUploadButton) Padding(padding: const EdgeInsets.only(right: 20), child: buildBackupIndicator()), + Padding(padding: const EdgeInsets.only(right: 20), child: buildProfileIndicator()), ], ); } diff --git a/mobile/lib/widgets/common/immich_image.dart b/mobile/lib/widgets/common/immich_image.dart index f51748edd6..c8bc9c1f6a 100644 --- a/mobile/lib/widgets/common/immich_image.dart +++ b/mobile/lib/widgets/common/immich_image.dart @@ -28,32 +28,19 @@ class ImmichImage extends StatelessWidget { // either by using the asset ID or the asset itself /// [asset] is the Asset to request, or else use [assetId] to get a remote /// image provider - static ImageProvider imageProvider({ - Asset? asset, - String? assetId, - double width = 1080, - double height = 1920, - }) { + static ImageProvider imageProvider({Asset? asset, String? assetId, double width = 1080, double height = 1920}) { if (asset == null && assetId == null) { throw Exception('Must supply either asset or assetId'); } if (asset == null) { - return ImmichRemoteImageProvider( - assetId: assetId!, - ); + return ImmichRemoteImageProvider(assetId: assetId!); } if (useLocal(asset)) { - return ImmichLocalImageProvider( - asset: asset, - width: width, - height: height, - ); + return ImmichLocalImageProvider(asset: asset, width: width, height: height); } else { - return ImmichRemoteImageProvider( - assetId: asset.remoteId!, - ); + return ImmichRemoteImageProvider(assetId: asset.remoteId!); } } @@ -68,17 +55,11 @@ class ImmichImage extends StatelessWidget { color: Colors.grey, width: width, height: height, - child: const Center( - child: Icon(Icons.no_photography), - ), + child: const Center(child: Icon(Icons.no_photography)), ); } - final imageProviderInstance = ImmichImage.imageProvider( - asset: asset, - width: context.width, - height: context.height, - ); + final imageProviderInstance = ImmichImage.imageProvider(asset: asset, width: context.width, height: context.height); return OctoImage( fadeInDuration: const Duration(milliseconds: 0), @@ -96,11 +77,7 @@ class ImmichImage extends StatelessWidget { errorBuilder: (context, error, stackTrace) { imageProviderInstance.evict(); - return Icon( - Icons.image_not_supported_outlined, - size: 32, - color: Colors.red[200], - ); + return Icon(Icons.image_not_supported_outlined, size: 32, color: Colors.red[200]); }, ); } diff --git a/mobile/lib/widgets/common/immich_loading_indicator.dart b/mobile/lib/widgets/common/immich_loading_indicator.dart index 8f9eaeaa99..52f957f7e7 100644 --- a/mobile/lib/widgets/common/immich_loading_indicator.dart +++ b/mobile/lib/widgets/common/immich_loading_indicator.dart @@ -5,22 +5,15 @@ import 'package:immich_mobile/widgets/common/immich_logo.dart'; class ImmichLoadingIndicator extends HookWidget { final double? borderRadius; - const ImmichLoadingIndicator({ - super.key, - this.borderRadius, - }); + const ImmichLoadingIndicator({super.key, this.borderRadius}); @override Widget build(BuildContext context) { - final logoAnimationController = useAnimationController( - duration: const Duration(seconds: 6), - ) + final logoAnimationController = useAnimationController(duration: const Duration(seconds: 6)) ..reverse() ..repeat(); - final borderAnimationController = useAnimationController( - duration: const Duration(seconds: 6), - )..repeat(); + final borderAnimationController = useAnimationController(duration: const Duration(seconds: 6))..repeat(); return Container( height: 80, @@ -34,10 +27,7 @@ class ImmichLoadingIndicator extends HookWidget { animation: borderAnimationController, builder: (context, child) { return CustomPaint( - painter: GradientBorderPainter( - animation: borderAnimationController.value, - strokeWidth: 3, - ), + painter: GradientBorderPainter(animation: borderAnimationController.value, strokeWidth: 3), child: child, ); }, @@ -45,9 +35,7 @@ class ImmichLoadingIndicator extends HookWidget { padding: const EdgeInsets.all(15), child: RotationTransition( turns: logoAnimationController, - child: const ImmichLogo( - heroTag: 'logo', - ), + child: const ImmichLogo(heroTag: 'logo'), ), ), ), @@ -67,10 +55,7 @@ class GradientBorderPainter extends CustomPainter { const Color(0xFF18C249), ]; - GradientBorderPainter({ - required this.animation, - required this.strokeWidth, - }); + GradientBorderPainter({required this.animation, required this.strokeWidth}); @override void paint(Canvas canvas, Size size) { @@ -96,10 +81,7 @@ class GradientBorderPainter extends CustomPainter { colors.first.withValues(alpha: opacity), ], // Add evenly distributed stops - stops: List.generate( - colors.length + 1, - (index) => index / colors.length, - ), + stops: List.generate(colors.length + 1, (index) => index / colors.length), tileMode: TileMode.clamp, // Use transformations to rotate the gradient transform: GradientRotation(-animation * 2 * 3.14159), diff --git a/mobile/lib/widgets/common/immich_logo.dart b/mobile/lib/widgets/common/immich_logo.dart index 43987878cb..a369275282 100644 --- a/mobile/lib/widgets/common/immich_logo.dart +++ b/mobile/lib/widgets/common/immich_logo.dart @@ -4,11 +4,7 @@ class ImmichLogo extends StatelessWidget { final double size; final dynamic heroTag; - const ImmichLogo({ - super.key, - this.size = 100, - this.heroTag, - }); + const ImmichLogo({super.key, this.size = 100, this.heroTag}); @override Widget build(BuildContext context) { diff --git a/mobile/lib/widgets/common/immich_sliver_app_bar.dart b/mobile/lib/widgets/common/immich_sliver_app_bar.dart index 18f29b9f9d..06a97d1ce5 100644 --- a/mobile/lib/widgets/common/immich_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/immich_sliver_app_bar.dart @@ -52,11 +52,7 @@ class ImmichSliverAppBar extends ConsumerWidget { pinned: pinned, snap: snap, expandedHeight: expandedHeight, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(5), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), automaticallyImplyLeading: false, centerTitle: false, title: title ?? const _ImmichLogoWithText(), @@ -66,38 +62,21 @@ class ImmichSliverAppBar extends ConsumerWidget { padding: const EdgeInsets.only(right: 12), child: IconButton( onPressed: () { - showDialog( - context: context, - builder: (context) => const CastDialog(), - ); + showDialog(context: context, builder: (context) => const CastDialog()); }, - icon: Icon( - isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded, - ), + icon: Icon(isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded), ), ), const _SyncStatusIndicator(), if (actions != null) - ...actions!.map( - (action) => Padding( - padding: const EdgeInsets.only(right: 16), - child: action, - ), - ), + ...actions!.map((action) => Padding(padding: const EdgeInsets.only(right: 16), child: action)), if (kDebugMode || kProfileMode) IconButton( icon: const Icon(Icons.science_rounded), onPressed: () => context.pushRoute(const FeatInDevRoute()), ), - if (showUploadButton) - const Padding( - padding: EdgeInsets.only(right: 20), - child: _BackupIndicator(), - ), - const Padding( - padding: EdgeInsets.only(right: 20), - child: _ProfileIndicator(), - ), + if (showUploadButton) const Padding(padding: EdgeInsets.only(right: 20), child: _BackupIndicator()), + const Padding(padding: EdgeInsets.only(right: 20), child: _ProfileIndicator()), ], ), ); @@ -159,23 +138,12 @@ class _ProfileIndicator extends ConsumerWidget { const widgetSize = 30.0; return InkWell( - onTap: () => showDialog( - context: context, - useRootNavigator: false, - builder: (ctx) => const ImmichAppBarDialog(), - ), + onTap: () => showDialog(context: context, useRootNavigator: false, builder: (ctx) => const ImmichAppBarDialog()), borderRadius: const BorderRadius.all(Radius.circular(12)), child: Badge( label: Container( - decoration: BoxDecoration( - color: Colors.black, - borderRadius: BorderRadius.circular(widgetSize / 2), - ), - child: const Icon( - Icons.info, - color: Color.fromARGB(255, 243, 188, 106), - size: widgetSize / 2, - ), + decoration: BoxDecoration(color: Colors.black, borderRadius: BorderRadius.circular(widgetSize / 2)), + child: const Icon(Icons.info, color: Color.fromARGB(255, 243, 188, 106), size: widgetSize / 2), ), backgroundColor: Colors.transparent, alignment: Alignment.bottomRight, @@ -183,17 +151,10 @@ class _ProfileIndicator extends ConsumerWidget { serverInfoState.isVersionMismatch || ((user?.isAdmin ?? false) && serverInfoState.isNewReleaseAvailable), offset: const Offset(-2, -12), child: user == null - ? const Icon( - Icons.face_outlined, - size: widgetSize, - ) + ? const Icon(Icons.face_outlined, size: widgetSize) : Semantics( label: "logged_in_as".tr(namedArgs: {"user": user.name}), - child: UserCircleAvatar( - radius: 17, - size: 31, - user: user, - ), + child: UserCircleAvatar(radius: 17, size: 31, user: user), ), ), ); @@ -218,9 +179,7 @@ class _BackupIndicator extends ConsumerWidget { height: widgetSize / 2, decoration: BoxDecoration( color: badgeBackground, - border: Border.all( - color: context.colorScheme.outline.withValues(alpha: .3), - ), + border: Border.all(color: context.colorScheme.outline.withValues(alpha: .3)), borderRadius: BorderRadius.circular(widgetSize / 2), ), child: indicatorIcon, @@ -229,11 +188,7 @@ class _BackupIndicator extends ConsumerWidget { alignment: Alignment.bottomRight, isLabelVisible: indicatorIcon != null, offset: const Offset(-2, -12), - child: Icon( - Icons.backup_rounded, - size: widgetSize, - color: context.primaryColor, - ), + child: Icon(Icons.backup_rounded, size: widgetSize, color: context.primaryColor), ), ); } @@ -263,8 +218,9 @@ class _BackupIndicator extends ConsumerWidget { return Container( padding: const EdgeInsets.all(3.5), child: Theme( - data: context.themeData - .copyWith(progressIndicatorTheme: context.themeData.progressIndicatorTheme.copyWith(year2023: true)), + data: context.themeData.copyWith( + progressIndicatorTheme: context.themeData.progressIndicatorTheme.copyWith(year2023: true), + ), child: CircularProgressIndicator( strokeWidth: 2, strokeCap: StrokeCap.round, @@ -302,27 +258,13 @@ class _SyncStatusIndicatorState extends ConsumerState<_SyncStatusIndicator> with @override void initState() { super.initState(); - _rotationController = AnimationController( - duration: const Duration(seconds: 2), - vsync: this, - ); - _dismissalController = AnimationController( - duration: const Duration(milliseconds: 300), - vsync: this, - ); - _rotationAnimation = Tween( - begin: 0.0, - end: 1.0, - ).animate(_rotationController); + _rotationController = AnimationController(duration: const Duration(seconds: 2), vsync: this); + _dismissalController = AnimationController(duration: const Duration(milliseconds: 300), vsync: this); + _rotationAnimation = Tween(begin: 0.0, end: 1.0).animate(_rotationController); _dismissalAnimation = Tween( begin: 1.0, end: 0.0, - ).animate( - CurvedAnimation( - parent: _dismissalController, - curve: Curves.easeOutQuart, - ), - ); + ).animate(CurvedAnimation(parent: _dismissalController, curve: Curves.easeOutQuart)); } @override @@ -366,11 +308,7 @@ class _SyncStatusIndicatorState extends ConsumerState<_SyncStatusIndicator> with opacity: isSyncing ? 1.0 : _dismissalAnimation.value, child: Transform.rotate( angle: _rotationAnimation.value * 2 * 3.14159 * -1, // Rotate counter-clockwise - child: Icon( - Icons.sync, - size: 24, - color: context.primaryColor, - ), + child: Icon(Icons.sync, size: 24, color: context.primaryColor), ), ), ), diff --git a/mobile/lib/widgets/common/immich_thumbnail.dart b/mobile/lib/widgets/common/immich_thumbnail.dart index 5e3bb610d8..612a6a4bd0 100644 --- a/mobile/lib/widgets/common/immich_thumbnail.dart +++ b/mobile/lib/widgets/common/immich_thumbnail.dart @@ -13,13 +13,7 @@ import 'package:octo_image/octo_image.dart'; import 'package:immich_mobile/providers/user.provider.dart'; class ImmichThumbnail extends HookConsumerWidget { - const ImmichThumbnail({ - this.asset, - this.width = 250, - this.height = 250, - this.fit = BoxFit.cover, - super.key, - }); + const ImmichThumbnail({this.asset, this.width = 250, this.height = 250, this.fit = BoxFit.cover, super.key}); final Asset? asset; final double width; @@ -30,35 +24,19 @@ class ImmichThumbnail extends HookConsumerWidget { /// either by using the asset ID or the asset itself /// [asset] is the Asset to request, or else use [assetId] to get a remote /// image provider - static ImageProvider imageProvider({ - Asset? asset, - String? assetId, - String? userId, - int thumbnailSize = 256, - }) { + static ImageProvider imageProvider({Asset? asset, String? assetId, String? userId, int thumbnailSize = 256}) { if (asset == null && assetId == null) { throw Exception('Must supply either asset or assetId'); } if (asset == null) { - return ImmichRemoteThumbnailProvider( - assetId: assetId!, - ); + return ImmichRemoteThumbnailProvider(assetId: assetId!); } if (ImmichImage.useLocal(asset)) { - return ImmichLocalThumbnailProvider( - asset: asset, - height: thumbnailSize, - width: thumbnailSize, - userId: userId, - ); + return ImmichLocalThumbnailProvider(asset: asset, height: thumbnailSize, width: thumbnailSize, userId: userId); } else { - return ImmichRemoteThumbnailProvider( - assetId: asset.remoteId!, - height: thumbnailSize, - width: thumbnailSize, - ); + return ImmichRemoteThumbnailProvider(assetId: asset.remoteId!, height: thumbnailSize, width: thumbnailSize); } } @@ -72,23 +50,13 @@ class ImmichThumbnail extends HookConsumerWidget { color: Colors.grey, width: width, height: height, - child: const Center( - child: Icon(Icons.no_photography), - ), + child: const Center(child: Icon(Icons.no_photography)), ); } - final assetAltText = getAltText( - asset!.exifInfo, - asset!.fileCreatedAt, - asset!.type, - [], - ); + final assetAltText = getAltText(asset!.exifInfo, asset!.fileCreatedAt, asset!.type, []); - final thumbnailProviderInstance = ImmichThumbnail.imageProvider( - asset: asset, - userId: userId, - ); + final thumbnailProviderInstance = ImmichThumbnail.imageProvider(asset: asset, userId: userId); customErrorBuilder(BuildContext ctx, Object error, StackTrace? stackTrace) { thumbnailProviderInstance.evict(); diff --git a/mobile/lib/widgets/common/immich_title_text.dart b/mobile/lib/widgets/common/immich_title_text.dart index 456ecdc9cf..3a848a1db6 100644 --- a/mobile/lib/widgets/common/immich_title_text.dart +++ b/mobile/lib/widgets/common/immich_title_text.dart @@ -5,18 +5,12 @@ class ImmichTitleText extends StatelessWidget { final double fontSize; final Color? color; - const ImmichTitleText({ - super.key, - this.fontSize = 48, - this.color, - }); + const ImmichTitleText({super.key, this.fontSize = 48, this.color}); @override Widget build(BuildContext context) { return Image( - image: AssetImage( - context.isDarkTheme ? 'assets/immich-text-dark.png' : 'assets/immich-text-light.png', - ), + image: AssetImage(context.isDarkTheme ? 'assets/immich-text-dark.png' : 'assets/immich-text-light.png'), width: fontSize * 4, filterQuality: FilterQuality.high, color: context.primaryColor, diff --git a/mobile/lib/widgets/common/immich_toast.dart b/mobile/lib/widgets/common/immich_toast.dart index 945568a74c..dad8b33283 100644 --- a/mobile/lib/widgets/common/immich_toast.dart +++ b/mobile/lib/widgets/common/immich_toast.dart @@ -16,25 +16,16 @@ class ImmichToast { fToast.init(context); Color getColor(ToastType type, BuildContext context) => switch (type) { - ToastType.info => context.primaryColor, - ToastType.success => const Color.fromARGB(255, 78, 140, 124), - ToastType.error => const Color.fromARGB(255, 220, 48, 85), - }; + ToastType.info => context.primaryColor, + ToastType.success => const Color.fromARGB(255, 78, 140, 124), + ToastType.error => const Color.fromARGB(255, 220, 48, 85), + }; Icon getIcon(ToastType type) => switch (type) { - ToastType.info => Icon( - Icons.info_outline_rounded, - color: context.primaryColor, - ), - ToastType.success => const Icon( - Icons.check_circle_rounded, - color: Color.fromARGB(255, 78, 140, 124), - ), - ToastType.error => const Icon( - Icons.error_outline_rounded, - color: Color.fromARGB(255, 240, 162, 156), - ), - }; + ToastType.info => Icon(Icons.info_outline_rounded, color: context.primaryColor), + ToastType.success => const Icon(Icons.check_circle_rounded, color: Color.fromARGB(255, 78, 140, 124)), + ToastType.error => const Icon(Icons.error_outline_rounded, color: Color.fromARGB(255, 240, 162, 156)), + }; fToast.showToast( child: Container( @@ -42,26 +33,17 @@ class ImmichToast { decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(16.0)), color: context.colorScheme.surfaceContainer, - border: Border.all( - color: context.colorScheme.outline.withValues(alpha: .5), - width: 1, - ), + border: Border.all(color: context.colorScheme.outline.withValues(alpha: .5), width: 1), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ getIcon(toastType), - const SizedBox( - width: 12.0, - ), + const SizedBox(width: 12.0), Flexible( child: Text( msg, - style: TextStyle( - color: getColor(toastType, context), - fontWeight: FontWeight.w600, - fontSize: 14, - ), + style: TextStyle(color: getColor(toastType, context), fontWeight: FontWeight.w600, fontSize: 14), ), ), ], diff --git a/mobile/lib/widgets/common/local_album_sliver_app_bar.dart b/mobile/lib/widgets/common/local_album_sliver_app_bar.dart index 4880865e66..85d8f4bb01 100644 --- a/mobile/lib/widgets/common/local_album_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/local_album_sliver_app_bar.dart @@ -12,14 +12,10 @@ class LocalAlbumsSliverAppBar extends StatelessWidget { pinned: true, snap: false, backgroundColor: context.colorScheme.surfaceContainer, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(5)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), automaticallyImplyLeading: true, centerTitle: true, - title: Text( - "on_this_device".t(context: context), - ), + title: Text("on_this_device".t(context: context)), ); } } diff --git a/mobile/lib/widgets/common/location_picker.dart b/mobile/lib/widgets/common/location_picker.dart index 81f8440836..1f63299dd7 100644 --- a/mobile/lib/widgets/common/location_picker.dart +++ b/mobile/lib/widgets/common/location_picker.dart @@ -9,16 +9,11 @@ import 'package:immich_mobile/widgets/map/map_thumbnail.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; -Future showLocationPicker({ - required BuildContext context, - LatLng? initialLatLng, -}) { +Future showLocationPicker({required BuildContext context, LatLng? initialLatLng}) { return showDialog( context: context, useRootNavigator: false, - builder: (ctx) => _LocationPicker( - initialLatLng: initialLatLng, - ), + builder: (ctx) => _LocationPicker(initialLatLng: initialLatLng), ); } @@ -27,9 +22,7 @@ enum _LocationPickerMode { map, manual } class _LocationPicker extends HookWidget { final LatLng? initialLatLng; - const _LocationPicker({ - this.initialLatLng, - }); + const _LocationPicker({this.initialLatLng}); @override Widget build(BuildContext context) { @@ -39,9 +32,7 @@ class _LocationPicker extends HookWidget { final pickerMode = useState(_LocationPickerMode.map); Future onMapTap() async { - final newLatLng = await context.pushRoute( - MapLocationPickerRoute(initialLatLng: latlng), - ); + final newLatLng = await context.pushRoute(MapLocationPickerRoute(initialLatLng: latlng)); if (newLatLng != null) { latitude.value = newLatLng.latitude; longitude.value = newLatLng.longitude; @@ -81,10 +72,7 @@ class _LocationPicker extends HookWidget { onPressed: () => context.maybePop(latlng), child: Text( "action_common_update", - style: context.textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w600, - color: context.primaryColor, - ), + style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor), ).tr(), ), ], @@ -129,10 +117,7 @@ class _ManualPickerInput extends HookWidget { autofocus: false, decoration: InputDecoration( labelText: decorationText.tr(), - labelStyle: TextStyle( - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), + labelStyle: TextStyle(fontWeight: FontWeight.bold, color: context.primaryColor), floatingLabelBehavior: FloatingLabelBehavior.auto, border: const OutlineInputBorder(), hintText: hintText.tr(), @@ -188,10 +173,7 @@ class _ManualPicker extends HookWidget { return Column( mainAxisSize: MainAxisSize.min, children: [ - const Text( - "edit_location_dialog_title", - textAlign: TextAlign.center, - ).tr(), + const Text("edit_location_dialog_title", textAlign: TextAlign.center).tr(), const SizedBox(height: 12), TextButton.icon( icon: const Text("location_picker_choose_on_map").tr(), @@ -228,27 +210,17 @@ class _MapPicker extends StatelessWidget { final Function() onModeSwitch; final Function() onMapTap; - const _MapPicker({ - required this.latlng, - required this.onModeSwitch, - required this.onMapTap, - super.key, - }); + const _MapPicker({required this.latlng, required this.onModeSwitch, required this.onMapTap, super.key}); @override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, children: [ - const Text( - "edit_location_dialog_title", - textAlign: TextAlign.center, - ).tr(), + const Text("edit_location_dialog_title", textAlign: TextAlign.center).tr(), const SizedBox(height: 12), TextButton.icon( - icon: Text( - "${latlng.latitude.toStringAsFixed(4)}, ${latlng.longitude.toStringAsFixed(4)}", - ), + icon: Text("${latlng.latitude.toStringAsFixed(4)}, ${latlng.longitude.toStringAsFixed(4)}"), label: const Icon(Icons.edit_outlined, size: 16), onPressed: onModeSwitch, ), diff --git a/mobile/lib/widgets/common/mesmerizing_sliver_app_bar.dart b/mobile/lib/widgets/common/mesmerizing_sliver_app_bar.dart index 2130a07866..359b400456 100644 --- a/mobile/lib/widgets/common/mesmerizing_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/mesmerizing_sliver_app_bar.dart @@ -14,11 +14,7 @@ import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; class MesmerizingSliverAppBar extends ConsumerStatefulWidget { - const MesmerizingSliverAppBar({ - super.key, - required this.title, - this.icon = Icons.camera, - }); + const MesmerizingSliverAppBar({super.key, required this.title, this.icon = Icons.camera}); final String title; final IconData icon; @@ -62,23 +58,11 @@ class _MesmerizingSliverAppBarState extends ConsumerState 0.95 ? Text( widget.title, - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.w600, - fontSize: 18, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.w600, fontSize: 18), ) : null, ), @@ -131,11 +111,7 @@ class _ExpandedBackground extends ConsumerStatefulWidget { final String title; final IconData icon; - const _ExpandedBackground({ - required this.scrollProgress, - required this.title, - required this.icon, - }); + const _ExpandedBackground({required this.scrollProgress, required this.title, required this.icon}); @override ConsumerState<_ExpandedBackground> createState() => _ExpandedBackgroundState(); @@ -149,20 +125,12 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S void initState() { super.initState(); - _slideController = AnimationController( - duration: const Duration(milliseconds: 800), - vsync: this, - ); + _slideController = AnimationController(duration: const Duration(milliseconds: 800), vsync: this); _slideAnimation = Tween( begin: const Offset(0, 1.5), end: Offset.zero, - ).animate( - CurvedAnimation( - parent: _slideController, - curve: Curves.easeOutCubic, - ), - ); + ).animate(CurvedAnimation(parent: _slideController, curve: Curves.easeOutCubic)); Future.delayed(const Duration(milliseconds: 100), () { if (mounted) { @@ -188,10 +156,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S offset: Offset(0, widget.scrollProgress * 50), child: Transform.scale( scale: 1.4 - (widget.scrollProgress * 0.2), - child: _RandomAssetBackground( - timelineService: timelineService, - icon: widget.icon, - ), + child: _RandomAssetBackground(timelineService: timelineService, icon: widget.icon), ), ), Container( @@ -202,9 +167,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S colors: [ Colors.transparent, Colors.transparent, - Colors.black.withValues( - alpha: 0.6 + (widget.scrollProgress * 0.2), - ), + Colors.black.withValues(alpha: 0.6 + (widget.scrollProgress * 0.2)), ], stops: const [0.0, 0.65, 1.0], ), @@ -232,21 +195,12 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S fontSize: 36, fontWeight: FontWeight.bold, letterSpacing: 0.5, - shadows: [ - Shadow( - offset: Offset(0, 2), - blurRadius: 12, - color: Colors.black45, - ), - ], + shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black45)], ), ), ), ), - AnimatedContainer( - duration: const Duration(milliseconds: 300), - child: const _ItemCountText(), - ), + AnimatedContainer(duration: const Duration(milliseconds: 300), child: const _ItemCountText()), ], ), ), @@ -280,26 +234,15 @@ class _ItemCountTextState extends ConsumerState<_ItemCountText> { @override Widget build(BuildContext context) { - final assetCount = ref.watch( - timelineServiceProvider.select((s) => s.totalAssets), - ); + final assetCount = ref.watch(timelineServiceProvider.select((s) => s.totalAssets)); return Text( - 'items_count'.t( - context: context, - args: {"count": assetCount}, - ), + 'items_count'.t(context: context, args: {"count": assetCount}), style: context.textTheme.labelLarge?.copyWith( // letterSpacing: 0.2, fontWeight: FontWeight.bold, color: Colors.white, - shadows: [ - const Shadow( - offset: Offset(0, 1), - blurRadius: 6, - color: Colors.black45, - ), - ], + shadows: [const Shadow(offset: Offset(0, 1), blurRadius: 6, color: Colors.black45)], ), ); } @@ -309,10 +252,7 @@ class _RandomAssetBackground extends StatefulWidget { final TimelineService timelineService; final IconData icon; - const _RandomAssetBackground({ - required this.timelineService, - required this.icon, - }); + const _RandomAssetBackground({required this.timelineService, required this.icon}); @override State<_RandomAssetBackground> createState() => _RandomAssetBackgroundState(); @@ -332,50 +272,26 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic void initState() { super.initState(); - _zoomController = AnimationController( - duration: const Duration(seconds: 12), - vsync: this, - ); + _zoomController = AnimationController(duration: const Duration(seconds: 12), vsync: this); - _crossFadeController = AnimationController( - duration: const Duration(milliseconds: 1200), - vsync: this, - ); + _crossFadeController = AnimationController(duration: const Duration(milliseconds: 1200), vsync: this); _zoomAnimation = Tween( begin: 1.0, end: 1.2, - ).animate( - CurvedAnimation( - parent: _zoomController, - curve: Curves.easeInOut, - ), - ); + ).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut)); _panAnimation = Tween( begin: Offset.zero, end: const Offset(0.5, -0.5), - ).animate( - CurvedAnimation( - parent: _zoomController, - curve: Curves.easeInOut, - ), - ); + ).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut)); _crossFadeAnimation = Tween( begin: 0.0, end: 1.0, - ).animate( - CurvedAnimation( - parent: _crossFadeController, - curve: Curves.easeInOutCubic, - ), - ); + ).animate(CurvedAnimation(parent: _crossFadeController, curve: Curves.easeInOutCubic)); - Future.delayed( - Durations.medium1, - () => _loadFirstAsset(), - ); + Future.delayed(Durations.medium1, () => _loadFirstAsset()); } @override @@ -465,9 +381,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic } return AnimatedBuilder( - animation: Listenable.merge( - [_zoomAnimation, _panAnimation, _crossFadeAnimation], - ), + animation: Listenable.merge([_zoomAnimation, _panAnimation, _crossFadeAnimation]), builder: (context, child) { return Transform.scale( scale: _zoomAnimation.value, @@ -499,11 +413,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic return SizedBox( width: double.infinity, height: double.infinity, - child: Icon( - Icons.error_outline_rounded, - size: 24, - color: Colors.red[300], - ), + child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]), ); }, ), @@ -530,11 +440,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic return SizedBox( width: double.infinity, height: double.infinity, - child: Icon( - Icons.error_outline_rounded, - size: 24, - color: Colors.red[300], - ), + child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]), ); }, ), diff --git a/mobile/lib/widgets/common/remote_album_sliver_app_bar.dart b/mobile/lib/widgets/common/remote_album_sliver_app_bar.dart index 6f26c87da7..2efe8a3ce1 100644 --- a/mobile/lib/widgets/common/remote_album_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/remote_album_sliver_app_bar.dart @@ -63,25 +63,13 @@ class _MesmerizingSliverAppBarState extends ConsumerState actionIconShadows = [ if (_scrollProgress < 0.95) - Shadow( - offset: const Offset(0, 2), - blurRadius: 5, - color: Colors.black.withValues(alpha: 0.5), - ) + Shadow(offset: const Offset(0, 2), blurRadius: 5, color: Colors.black.withValues(alpha: 0.5)) else - const Shadow( - offset: Offset(0, 2), - blurRadius: 0, - color: Colors.transparent, - ), + const Shadow(offset: Offset(0, 2), blurRadius: 0, color: Colors.transparent), ]; return isMultiSelectEnabled @@ -111,20 +99,12 @@ class _MesmerizingSliverAppBarState extends ConsumerState 0.95 ? Text( currentAlbum.name, - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.w600, - fontSize: 18, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.w600, fontSize: 18), ) : null, ), @@ -174,11 +150,7 @@ class _ExpandedBackground extends ConsumerStatefulWidget { final IconData icon; final void Function()? onEditTitle; - const _ExpandedBackground({ - required this.scrollProgress, - required this.icon, - this.onEditTitle, - }); + const _ExpandedBackground({required this.scrollProgress, required this.icon, this.onEditTitle}); @override ConsumerState<_ExpandedBackground> createState() => _ExpandedBackgroundState(); @@ -192,20 +164,12 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S void initState() { super.initState(); - _slideController = AnimationController( - duration: const Duration(milliseconds: 800), - vsync: this, - ); + _slideController = AnimationController(duration: const Duration(milliseconds: 800), vsync: this); _slideAnimation = Tween( begin: const Offset(0, 1.5), end: Offset.zero, - ).animate( - CurvedAnimation( - parent: _slideController, - curve: Curves.easeOutCubic, - ), - ); + ).animate(CurvedAnimation(parent: _slideController, curve: Curves.easeOutCubic)); Future.delayed(const Duration(milliseconds: 100), () { if (mounted) { @@ -229,9 +193,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S return const SizedBox.shrink(); } - final dateRange = ref.watch( - remoteAlbumDateRangeProvider(currentAlbum.id), - ); + final dateRange = ref.watch(remoteAlbumDateRangeProvider(currentAlbum.id)); return Stack( fit: StackFit.expand, children: [ @@ -239,18 +201,12 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S offset: Offset(0, widget.scrollProgress * 50), child: Transform.scale( scale: 1.4 - (widget.scrollProgress * 0.2), - child: _RandomAssetBackground( - timelineService: timelineService, - icon: widget.icon, - ), + child: _RandomAssetBackground(timelineService: timelineService, icon: widget.icon), ), ), ClipRect( child: BackdropFilter( - filter: ImageFilter.blur( - sigmaX: widget.scrollProgress * 2.0, - sigmaY: widget.scrollProgress * 2.0, - ), + filter: ImageFilter.blur(sigmaX: widget.scrollProgress * 2.0, sigmaY: widget.scrollProgress * 2.0), child: Container( decoration: BoxDecoration( gradient: LinearGradient( @@ -260,9 +216,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S Colors.black.withValues(alpha: 0.05), Colors.transparent, Colors.black.withValues(alpha: 0.3), - Colors.black.withValues( - alpha: 0.6 + (widget.scrollProgress * 0.25), - ), + Colors.black.withValues(alpha: 0.6 + (widget.scrollProgress * 0.25)), ], stops: const [0.0, 0.15, 0.55, 1.0], ), @@ -291,32 +245,17 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S ), style: const TextStyle( color: Colors.white, - shadows: [ - Shadow( - offset: Offset(0, 2), - blurRadius: 12, - color: Colors.black87, - ), - ], + shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black87)], ), ), const Text( " • ", style: TextStyle( color: Colors.white, - shadows: [ - Shadow( - offset: Offset(0, 2), - blurRadius: 12, - color: Colors.black87, - ), - ], + shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black87)], ), ), - AnimatedContainer( - duration: const Duration(milliseconds: 300), - child: const _ItemCountText(), - ), + AnimatedContainer(duration: const Duration(milliseconds: 300), child: const _ItemCountText()), ], ), GestureDetector( @@ -333,13 +272,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S fontSize: 36, fontWeight: FontWeight.bold, letterSpacing: 0.5, - shadows: [ - Shadow( - offset: Offset(0, 2), - blurRadius: 12, - color: Colors.black54, - ), - ], + shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black54)], ), ), ), @@ -349,31 +282,20 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S GestureDetector( onTap: widget.onEditTitle, child: ConstrainedBox( - constraints: const BoxConstraints( - maxHeight: 80, - ), + constraints: const BoxConstraints(maxHeight: 80), child: SingleChildScrollView( child: Text( currentAlbum.description, style: const TextStyle( color: Colors.white, fontSize: 14, - shadows: [ - Shadow( - offset: Offset(0, 2), - blurRadius: 8, - color: Colors.black54, - ), - ], + shadows: [Shadow(offset: Offset(0, 2), blurRadius: 8, color: Colors.black54)], ), ), ), ), ), - const Padding( - padding: EdgeInsets.only(top: 8.0), - child: RemoteAlbumSharedUserIcons(), - ), + const Padding(padding: EdgeInsets.only(top: 8.0), child: RemoteAlbumSharedUserIcons()), ], ), ), @@ -407,24 +329,13 @@ class _ItemCountTextState extends ConsumerState<_ItemCountText> { @override Widget build(BuildContext context) { - final assetCount = ref.watch( - timelineServiceProvider.select((s) => s.totalAssets), - ); + final assetCount = ref.watch(timelineServiceProvider.select((s) => s.totalAssets)); return Text( - 'items_count'.t( - context: context, - args: {"count": assetCount}, - ), + 'items_count'.t(context: context, args: {"count": assetCount}), style: context.textTheme.labelLarge?.copyWith( color: Colors.white, - shadows: [ - const Shadow( - offset: Offset(0, 2), - blurRadius: 12, - color: Colors.black87, - ), - ], + shadows: [const Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black87)], ), ); } @@ -434,10 +345,7 @@ class _RandomAssetBackground extends StatefulWidget { final TimelineService timelineService; final IconData icon; - const _RandomAssetBackground({ - required this.timelineService, - required this.icon, - }); + const _RandomAssetBackground({required this.timelineService, required this.icon}); @override State<_RandomAssetBackground> createState() => _RandomAssetBackgroundState(); @@ -457,50 +365,26 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic void initState() { super.initState(); - _zoomController = AnimationController( - duration: const Duration(seconds: 12), - vsync: this, - ); + _zoomController = AnimationController(duration: const Duration(seconds: 12), vsync: this); - _crossFadeController = AnimationController( - duration: const Duration(milliseconds: 1200), - vsync: this, - ); + _crossFadeController = AnimationController(duration: const Duration(milliseconds: 1200), vsync: this); _zoomAnimation = Tween( begin: 1.0, end: 1.2, - ).animate( - CurvedAnimation( - parent: _zoomController, - curve: Curves.easeInOut, - ), - ); + ).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut)); _panAnimation = Tween( begin: Offset.zero, end: const Offset(0.5, -0.5), - ).animate( - CurvedAnimation( - parent: _zoomController, - curve: Curves.easeInOut, - ), - ); + ).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut)); _crossFadeAnimation = Tween( begin: 0.0, end: 1.0, - ).animate( - CurvedAnimation( - parent: _crossFadeController, - curve: Curves.easeInOutCubic, - ), - ); + ).animate(CurvedAnimation(parent: _crossFadeController, curve: Curves.easeInOutCubic)); - Future.delayed( - Durations.medium1, - () => _loadFirstAsset(), - ); + Future.delayed(Durations.medium1, () => _loadFirstAsset()); } @override @@ -590,9 +474,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic } return AnimatedBuilder( - animation: Listenable.merge( - [_zoomAnimation, _panAnimation, _crossFadeAnimation], - ), + animation: Listenable.merge([_zoomAnimation, _panAnimation, _crossFadeAnimation]), builder: (context, child) { return Transform.scale( scale: _zoomAnimation.value, @@ -624,11 +506,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic return SizedBox( width: double.infinity, height: double.infinity, - child: Icon( - Icons.error_outline_rounded, - size: 24, - color: Colors.red[300], - ), + child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]), ); }, ), @@ -655,11 +533,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic return SizedBox( width: double.infinity, height: double.infinity, - child: Icon( - Icons.error_outline_rounded, - size: 24, - color: Colors.red[300], - ), + child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]), ); }, ), diff --git a/mobile/lib/widgets/common/scaffold_error_body.dart b/mobile/lib/widgets/common/scaffold_error_body.dart index f1d7685f73..2e2d8fb506 100644 --- a/mobile/lib/widgets/common/scaffold_error_body.dart +++ b/mobile/lib/widgets/common/scaffold_error_body.dart @@ -15,11 +15,7 @@ class ScaffoldErrorBody extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - Text( - "scaffold_body_error_occurred", - style: context.textTheme.displayMedium, - textAlign: TextAlign.center, - ).tr(), + Text("scaffold_body_error_occurred", style: context.textTheme.displayMedium, textAlign: TextAlign.center).tr(), if (withIcon) Center( child: Padding( @@ -34,11 +30,7 @@ class ScaffoldErrorBody extends StatelessWidget { if (withIcon && errorMsg != null) Padding( padding: const EdgeInsets.all(20), - child: Text( - errorMsg!, - style: context.textTheme.displaySmall, - textAlign: TextAlign.center, - ), + child: Text(errorMsg!, style: context.textTheme.displaySmall, textAlign: TextAlign.center), ), ], ); diff --git a/mobile/lib/widgets/common/search_field.dart b/mobile/lib/widgets/common/search_field.dart index 97ac75a63b..84af2d050c 100644 --- a/mobile/lib/widgets/common/search_field.dart +++ b/mobile/lib/widgets/common/search_field.dart @@ -43,40 +43,22 @@ class SearchField extends StatelessWidget { contentPadding: contentPadding, filled: filled, fillColor: context.primaryColor.withValues(alpha: 0.1), - hintStyle: context.textTheme.bodyLarge?.copyWith( - color: context.themeData.colorScheme.onSurfaceSecondary, - ), + hintStyle: context.textTheme.bodyLarge?.copyWith(color: context.themeData.colorScheme.onSurfaceSecondary), border: OutlineInputBorder( - borderRadius: const BorderRadius.all( - Radius.circular(25), - ), - borderSide: BorderSide( - color: context.colorScheme.surfaceDim, - ), + borderRadius: const BorderRadius.all(Radius.circular(25)), + borderSide: BorderSide(color: context.colorScheme.surfaceDim), ), enabledBorder: OutlineInputBorder( - borderRadius: const BorderRadius.all( - Radius.circular(25), - ), - borderSide: BorderSide( - color: context.colorScheme.surfaceContainer, - ), + borderRadius: const BorderRadius.all(Radius.circular(25)), + borderSide: BorderSide(color: context.colorScheme.surfaceContainer), ), disabledBorder: OutlineInputBorder( - borderRadius: const BorderRadius.all( - Radius.circular(25), - ), - borderSide: BorderSide( - color: context.colorScheme.surfaceDim, - ), + borderRadius: const BorderRadius.all(Radius.circular(25)), + borderSide: BorderSide(color: context.colorScheme.surfaceDim), ), focusedBorder: OutlineInputBorder( - borderRadius: const BorderRadius.all( - Radius.circular(25), - ), - borderSide: BorderSide( - color: context.colorScheme.primary.withAlpha(100), - ), + borderRadius: const BorderRadius.all(Radius.circular(25)), + borderSide: BorderSide(color: context.colorScheme.primary.withAlpha(100)), ), prefixIcon: prefixIcon, suffixIcon: suffixIcon, diff --git a/mobile/lib/widgets/common/selection_sliver_app_bar.dart b/mobile/lib/widgets/common/selection_sliver_app_bar.dart index 4a6dbbf385..780062e50e 100644 --- a/mobile/lib/widgets/common/selection_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/selection_sliver_app_bar.dart @@ -7,9 +7,7 @@ import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; class SelectionSliverAppBar extends ConsumerStatefulWidget { - const SelectionSliverAppBar({ - super.key, - }); + const SelectionSliverAppBar({super.key}); @override ConsumerState createState() => _SelectionSliverAppBarState(); @@ -18,13 +16,9 @@ class SelectionSliverAppBar extends ConsumerStatefulWidget { class _SelectionSliverAppBarState extends ConsumerState { @override Widget build(BuildContext context) { - final selection = ref.watch( - multiSelectProvider.select((s) => s.selectedAssets), - ); + final selection = ref.watch(multiSelectProvider.select((s) => s.selectedAssets)); - final toExclude = ref.watch( - multiSelectProvider.select((s) => s.lockedSelectionAssets), - ); + final toExclude = ref.watch(multiSelectProvider.select((s) => s.lockedSelectionAssets)); final filteredAssets = selection.where((asset) { return !toExclude.contains(asset); @@ -40,9 +34,7 @@ class _SelectionSliverAppBarState extends ConsumerState { pinned: true, snap: false, backgroundColor: context.colorScheme.surfaceContainer, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(5)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), automaticallyImplyLeading: false, leading: IconButton( icon: const Icon(Icons.close_rounded), @@ -52,22 +44,13 @@ class _SelectionSliverAppBarState extends ConsumerState { }, ), centerTitle: true, - title: Text( - "Select {count}".t( - context: context, - args: { - 'count': filteredAssets.length.toString(), - }, - ), - ), + title: Text("Select {count}".t(context: context, args: {'count': filteredAssets.length.toString()})), actions: [ TextButton( onPressed: () => onDone(filteredAssets), child: Text( 'done'.t(context: context), - style: context.textTheme.titleSmall?.copyWith( - color: context.colorScheme.primary, - ), + style: context.textTheme.titleSmall?.copyWith(color: context.colorScheme.primary), ), ), ], diff --git a/mobile/lib/widgets/common/share_dialog.dart b/mobile/lib/widgets/common/share_dialog.dart index 1c7eb3580a..625390c4b7 100644 --- a/mobile/lib/widgets/common/share_dialog.dart +++ b/mobile/lib/widgets/common/share_dialog.dart @@ -11,10 +11,7 @@ class ShareDialog extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ const CircularProgressIndicator(), - Container( - margin: const EdgeInsets.only(top: 12), - child: const Text('share_dialog_preparing').tr(), - ), + Container(margin: const EdgeInsets.only(top: 12), child: const Text('share_dialog_preparing').tr()), ], ), ); diff --git a/mobile/lib/widgets/common/thumbhash_placeholder.dart b/mobile/lib/widgets/common/thumbhash_placeholder.dart index f73aa869f7..0cb1222989 100644 --- a/mobile/lib/widgets/common/thumbhash_placeholder.dart +++ b/mobile/lib/widgets/common/thumbhash_placeholder.dart @@ -6,21 +6,14 @@ import 'package:octo_image/octo_image.dart'; /// Simple set to show [OctoPlaceholder.circularProgressIndicator] as /// placeholder and [OctoError.icon] as error. -OctoSet blurHashOrPlaceholder( - Uint8List? blurhash, { - BoxFit? fit, - Text? errorMessage, -}) { +OctoSet blurHashOrPlaceholder(Uint8List? blurhash, {BoxFit? fit, Text? errorMessage}) { return OctoSet( placeholderBuilder: blurHashPlaceholderBuilder(blurhash, fit: fit), errorBuilder: blurHashErrorBuilder(blurhash, fit: fit, message: errorMessage), ); } -OctoPlaceholderBuilder blurHashPlaceholderBuilder( - Uint8List? blurhash, { - BoxFit? fit, -}) { +OctoPlaceholderBuilder blurHashPlaceholderBuilder(Uint8List? blurhash, {BoxFit? fit}) { return (context) => blurhash == null ? const ThumbnailPlaceholder() : FadeInPlaceholderImage( diff --git a/mobile/lib/widgets/common/user_circle_avatar.dart b/mobile/lib/widgets/common/user_circle_avatar.dart index c1d34c4baa..8be71e9b2e 100644 --- a/mobile/lib/widgets/common/user_circle_avatar.dart +++ b/mobile/lib/widgets/common/user_circle_avatar.dart @@ -16,13 +16,7 @@ class UserCircleAvatar extends ConsumerWidget { double size; bool hasBorder; - UserCircleAvatar({ - super.key, - this.radius = 22, - this.size = 44, - this.hasBorder = false, - required this.user, - }); + UserCircleAvatar({super.key, this.radius = 22, this.size = 44, this.hasBorder = false, required this.user}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -43,12 +37,7 @@ class UserCircleAvatar extends ConsumerWidget { child: Container( decoration: BoxDecoration( shape: BoxShape.circle, - border: hasBorder - ? Border.all( - color: Colors.grey[500]!, - width: 1, - ) - : null, + border: hasBorder ? Border.all(color: Colors.grey[500]!, width: 1) : null, ), child: CircleAvatar( backgroundColor: userAvatarColor, diff --git a/mobile/lib/widgets/forms/change_password_form.dart b/mobile/lib/widgets/forms/change_password_form.dart index d5fdf570dd..179b05a712 100644 --- a/mobile/lib/widgets/forms/change_password_form.dart +++ b/mobile/lib/widgets/forms/change_password_form.dart @@ -33,25 +33,13 @@ class ChangePasswordForm extends HookConsumerWidget { children: [ Text( 'change_password'.tr(), - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), + style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: context.primaryColor), ), Padding( padding: const EdgeInsets.symmetric(vertical: 24.0), child: Text( - 'change_password_form_description'.tr( - namedArgs: { - 'name': authState.name, - }, - ), - style: TextStyle( - fontSize: 14, - color: context.colorScheme.onSurface, - fontWeight: FontWeight.w600, - ), + 'change_password_form_description'.tr(namedArgs: {'name': authState.name}), + style: TextStyle(fontSize: 14, color: context.colorScheme.onSurface, fontWeight: FontWeight.w600), ), ), Form( @@ -70,8 +58,9 @@ class ChangePasswordForm extends HookConsumerWidget { passwordController: passwordController, onPressed: () async { if (formKey.currentState!.validate()) { - var isSuccess = - await ref.read(authProvider.notifier).changePassword(passwordController.value.text); + var isSuccess = await ref + .read(authProvider.notifier) + .changePassword(passwordController.value.text); if (isSuccess) { await ref.read(authProvider.notifier).logout(); @@ -139,11 +128,7 @@ class ConfirmPasswordInput extends StatelessWidget { final TextEditingController originalController; final TextEditingController confirmController; - const ConfirmPasswordInput({ - super.key, - required this.originalController, - required this.confirmController, - }); + const ConfirmPasswordInput({super.key, required this.originalController, required this.confirmController}); String? _validateInput(String? email) { if (confirmController.value != originalController.value) { @@ -171,11 +156,7 @@ class ConfirmPasswordInput extends StatelessWidget { class ChangePasswordButton extends ConsumerWidget { final TextEditingController passwordController; final VoidCallback onPressed; - const ChangePasswordButton({ - super.key, - required this.passwordController, - required this.onPressed, - }); + const ChangePasswordButton({super.key, required this.passwordController, required this.onPressed}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -185,10 +166,7 @@ class ChangePasswordButton extends ConsumerWidget { padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 25), ), onPressed: onPressed, - child: Text( - 'change_password'.tr(), - style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ), + child: Text('change_password'.tr(), style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), ); } } diff --git a/mobile/lib/widgets/forms/login/email_input.dart b/mobile/lib/widgets/forms/login/email_input.dart index 52f2a598f9..4d90d918ac 100644 --- a/mobile/lib/widgets/forms/login/email_input.dart +++ b/mobile/lib/widgets/forms/login/email_input.dart @@ -6,12 +6,7 @@ class EmailInput extends StatelessWidget { final FocusNode? focusNode; final Function()? onSubmit; - const EmailInput({ - super.key, - required this.controller, - this.focusNode, - this.onSubmit, - }); + const EmailInput({super.key, required this.controller, this.focusNode, this.onSubmit}); String? _validateInput(String? email) { if (email == null || email == '') return null; @@ -32,10 +27,7 @@ class EmailInput extends StatelessWidget { labelText: 'email'.tr(), border: const OutlineInputBorder(), hintText: 'login_form_email_hint'.tr(), - hintStyle: const TextStyle( - fontWeight: FontWeight.normal, - fontSize: 14, - ), + hintStyle: const TextStyle(fontWeight: FontWeight.normal, fontSize: 14), ), validator: _validateInput, autovalidateMode: AutovalidateMode.always, diff --git a/mobile/lib/widgets/forms/login/loading_icon.dart b/mobile/lib/widgets/forms/login/loading_icon.dart index 9d3f5eab64..052ce43ac7 100644 --- a/mobile/lib/widgets/forms/login/loading_icon.dart +++ b/mobile/lib/widgets/forms/login/loading_icon.dart @@ -7,15 +7,7 @@ class LoadingIcon extends StatelessWidget { Widget build(BuildContext context) { return const Padding( padding: EdgeInsets.only(top: 18.0), - child: SizedBox( - width: 24, - height: 24, - child: FittedBox( - child: CircularProgressIndicator( - strokeWidth: 2, - ), - ), - ), + child: SizedBox(width: 24, height: 24, child: FittedBox(child: CircularProgressIndicator(strokeWidth: 2))), ); } } diff --git a/mobile/lib/widgets/forms/login/login_button.dart b/mobile/lib/widgets/forms/login/login_button.dart index 479c53a9b7..0f9fb21d8f 100644 --- a/mobile/lib/widgets/forms/login/login_button.dart +++ b/mobile/lib/widgets/forms/login/login_button.dart @@ -5,23 +5,15 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; class LoginButton extends ConsumerWidget { final Function() onPressed; - const LoginButton({ - super.key, - required this.onPressed, - }); + const LoginButton({super.key, required this.onPressed}); @override Widget build(BuildContext context, WidgetRef ref) { return ElevatedButton.icon( - style: ElevatedButton.styleFrom( - padding: const EdgeInsets.symmetric(vertical: 12), - ), + style: ElevatedButton.styleFrom(padding: const EdgeInsets.symmetric(vertical: 12)), onPressed: onPressed, icon: const Icon(Icons.login_rounded), - label: const Text( - "login", - style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ).tr(), + label: const Text("login", style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)).tr(), ); } } diff --git a/mobile/lib/widgets/forms/login/login_form.dart b/mobile/lib/widgets/forms/login/login_form.dart index 7ac070b912..b4944ee1e9 100644 --- a/mobile/lib/widgets/forms/login/login_form.dart +++ b/mobile/lib/widgets/forms/login/login_form.dart @@ -54,9 +54,7 @@ class LoginForm extends HookConsumerWidget { final isOauthEnable = useState(false); final isPasswordLoginEnable = useState(false); final oAuthButtonLabel = useState('OAuth'); - final logoAnimationController = useAnimationController( - duration: const Duration(seconds: 60), - )..repeat(); + final logoAnimationController = useAnimationController(duration: const Duration(seconds: 60))..repeat(); final serverInfo = ref.watch(serverInfoProvider); final warningMessage = useState(null); final loginFormKey = GlobalKey(); @@ -90,11 +88,7 @@ class LoginForm extends HookConsumerWidget { // Guard empty URL if (serverUrl.isEmpty) { - ImmichToast.show( - context: context, - msg: "login_form_server_empty".tr(), - toastType: ToastType.error, - ); + ImmichToast.show(context: context, msg: "login_form_server_empty".tr(), toastType: ToastType.error); } try { @@ -148,16 +142,13 @@ class LoginForm extends HookConsumerWidget { isLoadingServer.value = false; } - useEffect( - () { - final serverUrl = getServerUrl(); - if (serverUrl != null) { - serverEndpointController.text = serverUrl; - } - return null; - }, - [], - ); + useEffect(() { + final serverUrl = getServerUrl(); + if (serverUrl != null) { + serverEndpointController.text = serverUrl; + } + return null; + }, []); populateTestLoginInfo() { emailController.text = 'demo@immich.app'; @@ -180,10 +171,7 @@ class LoginForm extends HookConsumerWidget { invalidateAllApiRepositoryProviders(ref); try { - final result = await ref.read(authProvider.notifier).login( - emailController.text, - passwordController.text, - ); + final result = await ref.read(authProvider.notifier).login(emailController.text, passwordController.text); if (result.shouldChangePassword && !result.isAdmin) { context.pushRoute(const ChangePasswordRoute()); @@ -212,12 +200,7 @@ class LoginForm extends HookConsumerWidget { String generateRandomString(int length) { const chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890'; final random = Random.secure(); - return String.fromCharCodes( - Iterable.generate( - length, - (_) => chars.codeUnitAt(random.nextInt(chars.length)), - ), - ); + return String.fromCharCodes(Iterable.generate(length, (_) => chars.codeUnitAt(random.nextInt(chars.length)))); } List randomBytes(int length) { @@ -273,23 +256,17 @@ class LoginForm extends HookConsumerWidget { if (oAuthServerUrl != null) { try { - final loginResponseDto = await oAuthService.oAuthLogin( - oAuthServerUrl, - state, - codeVerifier, - ); + final loginResponseDto = await oAuthService.oAuthLogin(oAuthServerUrl, state, codeVerifier); if (loginResponseDto == null) { return; } - log.info( - "Finished OAuth login with response: ${loginResponseDto.userEmail}", - ); + log.info("Finished OAuth login with response: ${loginResponseDto.userEmail}"); - final isSuccess = await ref.watch(authProvider.notifier).saveAuthInfo( - accessToken: loginResponseDto.accessToken, - ); + final isSuccess = await ref + .watch(authProvider.notifier) + .saveAuthInfo(accessToken: loginResponseDto.accessToken); if (isSuccess) { isLoading.value = false; @@ -374,10 +351,7 @@ class LoginForm extends HookConsumerWidget { ), onPressed: isLoadingServer.value ? null : getServerAuthSettings, icon: const Icon(Icons.arrow_forward_rounded), - label: const Text( - 'next', - style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ).tr(), + label: const Text('next', style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)).tr(), ), ), ], @@ -401,17 +375,10 @@ class LoginForm extends HookConsumerWidget { padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: context.isDarkTheme ? Colors.red.shade700 : Colors.red.shade100, - borderRadius: const BorderRadius.all( - Radius.circular(8), - ), - border: Border.all( - color: context.isDarkTheme ? Colors.red.shade900 : Colors.red[200]!, - ), - ), - child: Text( - warningMessage.value!, - textAlign: TextAlign.center, + borderRadius: const BorderRadius.all(Radius.circular(8)), + border: Border.all(color: context.isDarkTheme ? Colors.red.shade900 : Colors.red[200]!), ), + child: Text(warningMessage.value!, textAlign: TextAlign.center), ), ); } @@ -435,11 +402,7 @@ class LoginForm extends HookConsumerWidget { onSubmit: passwordFocusNode.requestFocus, ), const SizedBox(height: 8), - PasswordInput( - controller: passwordController, - focusNode: passwordFocusNode, - onSubmit: login, - ), + PasswordInput(controller: passwordController, focusNode: passwordFocusNode, onSubmit: login), ], // Note: This used to have an AnimatedSwitcher, but was removed @@ -455,12 +418,8 @@ class LoginForm extends HookConsumerWidget { if (isOauthEnable.value) ...[ if (isPasswordLoginEnable.value) Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), - child: Divider( - color: context.isDarkTheme ? Colors.white : Colors.black, - ), + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Divider(color: context.isDarkTheme ? Colors.white : Colors.black), ), OAuthLoginButton( serverEndpointController: serverEndpointController, @@ -471,10 +430,7 @@ class LoginForm extends HookConsumerWidget { ], ], ), - if (!isOauthEnable.value && !isPasswordLoginEnable.value) - Center( - child: const Text('login_disabled').tr(), - ), + if (!isOauthEnable.value && !isPasswordLoginEnable.value) Center(child: const Text('login_disabled').tr()), const SizedBox(height: 12), TextButton.icon( icon: const Icon(Icons.arrow_back), @@ -498,9 +454,7 @@ class LoginForm extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisAlignment: MainAxisAlignment.center, children: [ - SizedBox( - height: constraints.maxHeight / 5, - ), + SizedBox(height: constraints.maxHeight / 5), Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.end, @@ -510,24 +464,16 @@ class LoginForm extends HookConsumerWidget { onLongPress: () => populateTestLoginInfo1(), child: RotationTransition( turns: logoAnimationController, - child: const ImmichLogo( - heroTag: 'logo', - ), + child: const ImmichLogo(heroTag: 'logo'), ), ), - const Padding( - padding: EdgeInsets.only(top: 8.0, bottom: 16), - child: ImmichTitleText(), - ), + const Padding(padding: EdgeInsets.only(top: 8.0, bottom: 16), child: ImmichTitleText()), ], ), // Note: This used to have an AnimatedSwitcher, but was removed // because of https://github.com/flutter/flutter/issues/120874 - Form( - key: loginFormKey, - child: serverSelectionOrLogin, - ), + Form(key: loginFormKey, child: serverSelectionOrLogin), ], ), ), diff --git a/mobile/lib/widgets/forms/login/o_auth_login_button.dart b/mobile/lib/widgets/forms/login/o_auth_login_button.dart index 465d88a4d2..2d9b603b3c 100644 --- a/mobile/lib/widgets/forms/login/o_auth_login_button.dart +++ b/mobile/lib/widgets/forms/login/o_auth_login_button.dart @@ -25,10 +25,7 @@ class OAuthLoginButton extends ConsumerWidget { ), onPressed: onPressed, icon: const Icon(Icons.pin_rounded), - label: Text( - buttonLabel, - style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ), + label: Text(buttonLabel, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), ); } } diff --git a/mobile/lib/widgets/forms/login/password_input.dart b/mobile/lib/widgets/forms/login/password_input.dart index 074899bd57..5cdfcc9567 100644 --- a/mobile/lib/widgets/forms/login/password_input.dart +++ b/mobile/lib/widgets/forms/login/password_input.dart @@ -8,12 +8,7 @@ class PasswordInput extends HookConsumerWidget { final FocusNode? focusNode; final Function()? onSubmit; - const PasswordInput({ - super.key, - required this.controller, - this.focusNode, - this.onSubmit, - }); + const PasswordInput({super.key, required this.controller, this.focusNode, this.onSubmit}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -26,15 +21,10 @@ class PasswordInput extends HookConsumerWidget { labelText: 'password'.tr(), border: const OutlineInputBorder(), hintText: 'login_form_password_hint'.tr(), - hintStyle: const TextStyle( - fontWeight: FontWeight.normal, - fontSize: 14, - ), + hintStyle: const TextStyle(fontWeight: FontWeight.normal, fontSize: 14), suffixIcon: IconButton( onPressed: () => isPasswordVisible.value = !isPasswordVisible.value, - icon: Icon( - isPasswordVisible.value ? Icons.visibility_off_sharp : Icons.visibility_sharp, - ), + icon: Icon(isPasswordVisible.value ? Icons.visibility_off_sharp : Icons.visibility_sharp), ), ), autofillHints: const [AutofillHints.password], diff --git a/mobile/lib/widgets/forms/login/server_endpoint_input.dart b/mobile/lib/widgets/forms/login/server_endpoint_input.dart index cddf9e9985..f9bc1690af 100644 --- a/mobile/lib/widgets/forms/login/server_endpoint_input.dart +++ b/mobile/lib/widgets/forms/login/server_endpoint_input.dart @@ -7,12 +7,7 @@ class ServerEndpointInput extends StatelessWidget { final FocusNode focusNode; final Function()? onSubmit; - const ServerEndpointInput({ - super.key, - required this.controller, - required this.focusNode, - this.onSubmit, - }); + const ServerEndpointInput({super.key, required this.controller, required this.focusNode, this.onSubmit}); String? _validateInput(String? url) { if (url == null || url.isEmpty) return null; diff --git a/mobile/lib/widgets/forms/pin_input.dart b/mobile/lib/widgets/forms/pin_input.dart index 6602946d7d..88e27f005e 100644 --- a/mobile/lib/widgets/forms/pin_input.dart +++ b/mobile/lib/widgets/forms/pin_input.dart @@ -43,11 +43,7 @@ class PinInput extends StatelessWidget { final defaultPinTheme = PinTheme( width: getPinSize().width, height: getPinSize().height, - textStyle: TextStyle( - fontSize: 24, - color: context.colorScheme.onSurface, - fontFamily: 'Overpass Mono', - ), + textStyle: TextStyle(fontSize: 24, color: context.colorScheme.onSurface, fontFamily: 'Overpass Mono'), decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(19)), border: Border.all(color: context.colorScheme.surfaceBright), @@ -70,34 +66,19 @@ class PinInput extends StatelessWidget { forceErrorState: hasError ?? false, autofocus: autoFocus ?? false, obscureText: obscureText ?? false, - obscuringWidget: Icon( - Icons.vpn_key_rounded, - color: context.primaryColor, - size: 20, - ), - separatorBuilder: (index) => const SizedBox( - height: 64, - width: 3, - ), + obscuringWidget: Icon(Icons.vpn_key_rounded, color: context.primaryColor, size: 20), + separatorBuilder: (index) => const SizedBox(height: 64, width: 3), cursor: Column( mainAxisAlignment: MainAxisAlignment.end, children: [ - Container( - margin: const EdgeInsets.only(bottom: 9), - width: 18, - height: 2, - color: context.primaryColor, - ), + Container(margin: const EdgeInsets.only(bottom: 9), width: 18, height: 2, color: context.primaryColor), ], ), defaultPinTheme: defaultPinTheme, focusedPinTheme: defaultPinTheme.copyWith( decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(19)), - border: Border.all( - color: context.primaryColor.withValues(alpha: 0.5), - width: 2, - ), + border: Border.all(color: context.primaryColor.withValues(alpha: 0.5), width: 2), color: context.colorScheme.surfaceContainerHigh, ), ), @@ -105,10 +86,7 @@ class PinInput extends StatelessWidget { decoration: BoxDecoration( color: context.colorScheme.error.withAlpha(15), borderRadius: const BorderRadius.all(Radius.circular(19)), - border: Border.all( - color: context.colorScheme.error.withAlpha(100), - width: 2, - ), + border: Border.all(color: context.colorScheme.error.withAlpha(100), width: 2), ), ), pinputAutovalidateMode: PinputAutovalidateMode.onSubmit, diff --git a/mobile/lib/widgets/forms/pin_registration_form.dart b/mobile/lib/widgets/forms/pin_registration_form.dart index c3cfd3a864..d126169aad 100644 --- a/mobile/lib/widgets/forms/pin_registration_form.dart +++ b/mobile/lib/widgets/forms/pin_registration_form.dart @@ -9,10 +9,7 @@ import 'package:immich_mobile/widgets/forms/pin_input.dart'; class PinRegistrationForm extends HookConsumerWidget { final Function() onDone; - const PinRegistrationForm({ - super.key, - required this.onDone, - }); + const PinRegistrationForm({super.key, required this.onDone}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -40,35 +37,25 @@ class PinRegistrationForm extends HookConsumerWidget { } try { - await ref.read(authProvider.notifier).setupPinCode( - newPinCodeController.text, - ); + await ref.read(authProvider.notifier).setupPinCode(newPinCodeController.text); onDone(); } catch (error) { hasError.value = true; - context.showSnackBar( - SnackBar(content: Text(error.toString())), - ); + context.showSnackBar(SnackBar(content: Text(error.toString()))); } } return Form( child: Column( children: [ - Icon( - Icons.pin_outlined, - size: 64, - color: context.primaryColor, - ), + Icon(Icons.pin_outlined, size: 64, color: context.primaryColor), const SizedBox(height: 32), SizedBox( width: context.width * 0.7, child: Text( 'setup_pin_code'.tr(), - style: context.textTheme.labelLarge!.copyWith( - fontSize: 24, - ), + style: context.textTheme.labelLarge!.copyWith(fontSize: 24), textAlign: TextAlign.center, ), ), @@ -76,9 +63,7 @@ class PinRegistrationForm extends HookConsumerWidget { width: context.width * 0.8, child: Text( 'new_pin_code_subtitle'.tr(), - style: context.textTheme.bodyLarge!.copyWith( - fontSize: 16, - ), + style: context.textTheme.bodyLarge!.copyWith(fontSize: 16), textAlign: TextAlign.center, ), ), @@ -113,10 +98,7 @@ class PinRegistrationForm extends HookConsumerWidget { child: Row( children: [ Expanded( - child: ElevatedButton( - onPressed: createNewPinCode, - child: Text('create'.tr()), - ), + child: ElevatedButton(onPressed: createNewPinCode, child: Text('create'.tr())), ), ], ), diff --git a/mobile/lib/widgets/forms/pin_verification_form.dart b/mobile/lib/widgets/forms/pin_verification_form.dart index 8a3f0b55df..2b7e3e8251 100644 --- a/mobile/lib/widgets/forms/pin_verification_form.dart +++ b/mobile/lib/widgets/forms/pin_verification_form.dart @@ -49,11 +49,7 @@ class PinVerificationForm extends HookConsumerWidget { AnimatedSwitcher( duration: const Duration(milliseconds: 200), child: isVerified.value - ? Icon( - successIcon ?? Icons.lock_open_rounded, - size: 64, - color: Colors.green[300], - ) + ? Icon(successIcon ?? Icons.lock_open_rounded, size: 64, color: Colors.green[300]) : Icon( icon ?? Icons.lock_outline_rounded, size: 64, @@ -65,9 +61,7 @@ class PinVerificationForm extends HookConsumerWidget { width: context.width * 0.7, child: Text( description ?? 'enter_your_pin_code_subtitle'.tr(), - style: context.textTheme.labelLarge!.copyWith( - fontSize: 18, - ), + style: context.textTheme.labelLarge!.copyWith(fontSize: 18), textAlign: TextAlign.center, ), ), diff --git a/mobile/lib/widgets/map/map_app_bar.dart b/mobile/lib/widgets/map/map_app_bar.dart index 2715386737..73706c7661 100644 --- a/mobile/lib/widgets/map/map_app_bar.dart +++ b/mobile/lib/widgets/map/map_app_bar.dart @@ -52,16 +52,12 @@ class _NonSelectionRow extends StatelessWidget { children: [ ElevatedButton( onPressed: () => context.maybePop(), - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.arrow_back_ios_new_rounded), ), ElevatedButton( onPressed: onSettingsPressed, - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.more_vert_rounded), ), ], @@ -78,10 +74,7 @@ class _SelectionRow extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final isProcessing = useProcessingOverlay(); - Future handleProcessing( - FutureOr Function() action, [ - bool reloadMarkers = false, - ]) async { + Future handleProcessing(FutureOr Function() action, [bool reloadMarkers = false]) async { isProcessing.value = true; await action(); // Reset state @@ -101,9 +94,7 @@ class _SelectionRow extends HookConsumerWidget { icon: const Icon(Icons.close_rounded), label: Text( '${selectedAssets.value.length}', - style: context.textTheme.titleMedium?.copyWith( - color: context.colorScheme.onPrimary, - ), + style: context.textTheme.titleMedium?.copyWith(color: context.colorScheme.onPrimary), ), ), ), @@ -112,43 +103,20 @@ class _SelectionRow extends HookConsumerWidget { mainAxisAlignment: MainAxisAlignment.end, children: [ ElevatedButton( - onPressed: () => handleProcessing( - () => handleShareAssets( - ref, - context, - selectedAssets.value.toList(), - ), - ), - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + onPressed: () => handleProcessing(() => handleShareAssets(ref, context, selectedAssets.value.toList())), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.ios_share_rounded), ), ElevatedButton( - onPressed: () => handleProcessing( - () => handleFavoriteAssets( - ref, - context, - selectedAssets.value.toList(), - ), - ), - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + onPressed: () => + handleProcessing(() => handleFavoriteAssets(ref, context, selectedAssets.value.toList())), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.favorite), ), ElevatedButton( - onPressed: () => handleProcessing( - () => handleArchiveAssets( - ref, - context, - selectedAssets.value.toList(), - ), - true, - ), - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + onPressed: () => + handleProcessing(() => handleArchiveAssets(ref, context, selectedAssets.value.toList()), true), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.archive), ), ], diff --git a/mobile/lib/widgets/map/map_asset_grid.dart b/mobile/lib/widgets/map/map_asset_grid.dart index ce2a486fc5..893c36d43f 100644 --- a/mobile/lib/widgets/map/map_asset_grid.dart +++ b/mobile/lib/widgets/map/map_asset_grid.dart @@ -119,7 +119,8 @@ class MapAssetGrid extends HookConsumerWidget { final rowOffset = renderElement.offset; // Column offset = (total trailingEdge - trailingEdge crossed) / offset for each asset final totalOffset = item.itemTrailingEdge - item.itemLeadingEdge; - final edgeOffset = (totalOffset - partialOffset) / + final edgeOffset = + (totalOffset - partialOffset) / // Round the total count to the next multiple of [assetsPerRow] ((renderElement.totalCount / assetsPerRow) * assetsPerRow).floor(); @@ -146,34 +147,32 @@ class MapAssetGrid extends HookConsumerWidget { // Place it just below the drag handle heightFactor: 0.87, child: assetsInBounds.value.isNotEmpty - ? ref.watch(assetsTimelineProvider(assetsInBounds.value)).when( - data: (renderList) { - // Cache render list here to use it back during visibleItemsListener - cachedRenderList.value = renderList; - return ValueListenableBuilder( - valueListenable: selectedAssets, - builder: (_, value, __) => ImmichAssetGrid( - shrinkWrap: true, - renderList: renderList, - showDragScroll: false, - assetsPerRow: assetsPerRow, - showMultiSelectIndicator: false, - selectionActive: value.isNotEmpty, - listener: onAssetsSelected, - visibleItemsListener: (pos) => gridScrollThrottler.run(() => handleVisibleItems(pos)), - ), - ); - }, - error: (error, stackTrace) { - log.warning( - "Cannot get assets in the current map bounds", - error, - stackTrace, - ); - return const SizedBox.shrink(); - }, - loading: () => const SizedBox.shrink(), - ) + ? ref + .watch(assetsTimelineProvider(assetsInBounds.value)) + .when( + data: (renderList) { + // Cache render list here to use it back during visibleItemsListener + cachedRenderList.value = renderList; + return ValueListenableBuilder( + valueListenable: selectedAssets, + builder: (_, value, __) => ImmichAssetGrid( + shrinkWrap: true, + renderList: renderList, + showDragScroll: false, + assetsPerRow: assetsPerRow, + showMultiSelectIndicator: false, + selectionActive: value.isNotEmpty, + listener: onAssetsSelected, + visibleItemsListener: (pos) => gridScrollThrottler.run(() => handleVisibleItems(pos)), + ), + ); + }, + error: (error, stackTrace) { + log.warning("Cannot get assets in the current map bounds", error, stackTrace); + return const SizedBox.shrink(); + }, + loading: () => const SizedBox.shrink(), + ) : const _MapNoAssetsInSheet(), ), ), @@ -194,11 +193,7 @@ class _MapNoAssetsInSheet extends StatelessWidget { @override Widget build(BuildContext context) { - const image = Image( - height: 150, - width: 150, - image: AssetImage('assets/lighthouse.png'), - ); + const image = Image(height: 150, width: 150, image: AssetImage('assets/lighthouse.png')); return Center( child: ListView( @@ -206,21 +201,12 @@ class _MapNoAssetsInSheet extends StatelessWidget { children: [ context.isDarkTheme ? const InvertionFilter( - child: SaturationFilter( - saturation: -1, - child: BrightnessFilter( - brightness: -5, - child: image, - ), - ), + child: SaturationFilter(saturation: -1, child: BrightnessFilter(brightness: -5, child: image)), ) : image, const SizedBox(height: 20), Center( - child: Text( - "map_zoom_to_see_photos".tr(), - style: context.textTheme.displayLarge?.copyWith(fontSize: 18), - ), + child: Text("map_zoom_to_see_photos".tr(), style: context.textTheme.displayLarge?.copyWith(fontSize: 18)), ), ], ), @@ -254,10 +240,7 @@ class _MapSheetDragRegion extends StatelessWidget { margin: EdgeInsets.zero, shape: context.isMobile ? const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topRight: Radius.circular(20), - topLeft: Radius.circular(20), - ), + borderRadius: BorderRadius.only(topRight: Radius.circular(20), topLeft: Radius.circular(20)), ) : const BeveledRectangleBorder(), elevation: 0.0, @@ -291,10 +274,7 @@ class _MapSheetDragRegion extends StatelessWidget { right: 18, top: 24, child: IconButton( - icon: Icon( - Icons.map_outlined, - color: context.textTheme.displayLarge?.color, - ), + icon: Icon(Icons.map_outlined, color: context.textTheme.displayLarge?.color), iconSize: 24, tooltip: 'Zoom to bounds', onPressed: () => onZoomToAsset?.call(value!), diff --git a/mobile/lib/widgets/map/map_bottom_sheet.dart b/mobile/lib/widgets/map/map_bottom_sheet.dart index d8c1cc638e..baf85e8075 100644 --- a/mobile/lib/widgets/map/map_bottom_sheet.dart +++ b/mobile/lib/widgets/map/map_bottom_sheet.dart @@ -34,11 +34,7 @@ class MapBottomSheet extends HookConsumerWidget { void handleMapEvents(MapEvent event) async { if (event is MapCloseBottomSheet) { - sheetController.animateTo( - 0.1, - duration: const Duration(milliseconds: 200), - curve: Curves.linearToEaseOut, - ); + sheetController.animateTo(0.1, duration: const Duration(milliseconds: 200), curve: Curves.linearToEaseOut); } } @@ -85,9 +81,7 @@ class MapBottomSheet extends HookConsumerWidget { duration: const Duration(milliseconds: 150), child: ElevatedButton( onPressed: onZoomToLocation, - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.my_location), ), ), diff --git a/mobile/lib/widgets/map/map_settings/map_settings_list_tile.dart b/mobile/lib/widgets/map/map_settings/map_settings_list_tile.dart index 5c755d80be..51567eff15 100644 --- a/mobile/lib/widgets/map/map_settings/map_settings_list_tile.dart +++ b/mobile/lib/widgets/map/map_settings/map_settings_list_tile.dart @@ -8,21 +8,13 @@ class MapSettingsListTile extends StatelessWidget { final bool selected; final Function(bool) onChanged; - const MapSettingsListTile({ - super.key, - required this.title, - required this.selected, - required this.onChanged, - }); + const MapSettingsListTile({super.key, required this.title, required this.selected, required this.onChanged}); @override Widget build(BuildContext context) { return SwitchListTile.adaptive( activeColor: context.primaryColor, - title: Text( - title, - style: context.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold), - ).tr(), + title: Text(title, style: context.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold)).tr(), value: selected, onChanged: onChanged, ); diff --git a/mobile/lib/widgets/map/map_settings/map_settings_time_dropdown.dart b/mobile/lib/widgets/map/map_settings/map_settings_time_dropdown.dart index a627ff8f29..b601887e1e 100644 --- a/mobile/lib/widgets/map/map_settings/map_settings_time_dropdown.dart +++ b/mobile/lib/widgets/map/map_settings/map_settings_time_dropdown.dart @@ -5,11 +5,7 @@ class MapTimeDropDown extends StatelessWidget { final int relativeTime; final Function(int) onTimeChange; - const MapTimeDropDown({ - super.key, - required this.relativeTime, - required this.onTimeChange, - }); + const MapTimeDropDown({super.key, required this.relativeTime, required this.onTimeChange}); @override Widget build(BuildContext context) { @@ -20,10 +16,7 @@ class MapTimeDropDown extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.only(bottom: 20), - child: Text( - "date_range".tr(), - style: const TextStyle(fontWeight: FontWeight.bold), - ), + child: Text("date_range".tr(), style: const TextStyle(fontWeight: FontWeight.bold)), ), LayoutBuilder( builder: (_, constraints) => DropdownMenu( @@ -33,53 +26,19 @@ class MapTimeDropDown extends StatelessWidget { initialSelection: relativeTime, onSelected: (value) => onTimeChange(value!), dropdownMenuEntries: [ - DropdownMenuEntry( - value: 0, - label: "all".tr(), - ), - DropdownMenuEntry( - value: 1, - label: "map_settings_date_range_option_day".tr(), - ), - DropdownMenuEntry( - value: 7, - label: "map_settings_date_range_option_days".tr( - namedArgs: {'days': "7"}, - ), - ), - DropdownMenuEntry( - value: 30, - label: "map_settings_date_range_option_days".tr( - namedArgs: {'days': "30"}, - ), - ), + DropdownMenuEntry(value: 0, label: "all".tr()), + DropdownMenuEntry(value: 1, label: "map_settings_date_range_option_day".tr()), + DropdownMenuEntry(value: 7, label: "map_settings_date_range_option_days".tr(namedArgs: {'days': "7"})), + DropdownMenuEntry(value: 30, label: "map_settings_date_range_option_days".tr(namedArgs: {'days': "30"})), DropdownMenuEntry( value: now - .difference( - DateTime( - now.year - 1, - now.month, - now.day, - now.hour, - now.minute, - now.second, - ), - ) + .difference(DateTime(now.year - 1, now.month, now.day, now.hour, now.minute, now.second)) .inDays, label: "map_settings_date_range_option_year".tr(), ), DropdownMenuEntry( value: now - .difference( - DateTime( - now.year - 3, - now.month, - now.day, - now.hour, - now.minute, - now.second, - ), - ) + .difference(DateTime(now.year - 3, now.month, now.day, now.hour, now.minute, now.second)) .inDays, label: "map_settings_date_range_option_years".tr(namedArgs: {'years': "3"}), ), diff --git a/mobile/lib/widgets/map/map_settings/map_theme_picker.dart b/mobile/lib/widgets/map/map_settings/map_theme_picker.dart index 747ae06a54..63f35ebe4c 100644 --- a/mobile/lib/widgets/map/map_settings/map_theme_picker.dart +++ b/mobile/lib/widgets/map/map_settings/map_theme_picker.dart @@ -8,11 +8,7 @@ class MapThemePicker extends StatelessWidget { final ThemeMode themeMode; final Function(ThemeMode) onThemeChange; - const MapThemePicker({ - super.key, - required this.themeMode, - required this.onThemeChange, - }); + const MapThemePicker({super.key, required this.themeMode, required this.onThemeChange}); @override Widget build(BuildContext context) { @@ -76,10 +72,7 @@ class _BorderedMapThumbnail extends StatelessWidget { Container( decoration: BoxDecoration( border: Border.fromBorderSide( - BorderSide( - width: 4, - color: shouldHighlight ? context.colorScheme.onSurface : Colors.transparent, - ), + BorderSide(width: 4, color: shouldHighlight ? context.colorScheme.onSurface : Colors.transparent), ), borderRadius: const BorderRadius.all(Radius.circular(20)), ), @@ -95,9 +88,7 @@ class _BorderedMapThumbnail extends StatelessWidget { padding: const EdgeInsets.only(top: 10), child: Text( name, - style: context.textTheme.bodyMedium?.copyWith( - fontWeight: shouldHighlight ? FontWeight.bold : null, - ), + style: context.textTheme.bodyMedium?.copyWith(fontWeight: shouldHighlight ? FontWeight.bold : null), ), ), ], diff --git a/mobile/lib/widgets/map/map_theme_override.dart b/mobile/lib/widgets/map/map_theme_override.dart index 3f9ae0f43f..57f970b0d1 100644 --- a/mobile/lib/widgets/map/map_theme_override.dart +++ b/mobile/lib/widgets/map/map_theme_override.dart @@ -85,11 +85,7 @@ class _MapThemeOverrideState extends ConsumerState with Widget ? getThemeData(colorScheme: appTheme.dark, locale: locale) : getThemeData(colorScheme: appTheme.light, locale: locale), child: widget.mapBuilder.call( - ref.watch( - mapStateNotifierProvider.select( - (v) => _isDarkTheme ? v.darkStyleFetched : v.lightStyleFetched, - ), - ), + ref.watch(mapStateNotifierProvider.select((v) => _isDarkTheme ? v.darkStyleFetched : v.lightStyleFetched)), ), ); } diff --git a/mobile/lib/widgets/map/map_thumbnail.dart b/mobile/lib/widgets/map/map_thumbnail.dart index 0dc1ad3a4f..55f5ff77c6 100644 --- a/mobile/lib/widgets/map/map_thumbnail.dart +++ b/mobile/lib/widgets/map/map_thumbnail.dart @@ -110,11 +110,7 @@ class MapThumbnail extends HookConsumerWidget { ValueListenableBuilder( valueListenable: position, builder: (_, value, __) => value != null && assetMarkerRemoteId != null - ? PositionedAssetMarkerIcon( - size: height / 2, - point: value, - assetRemoteId: assetMarkerRemoteId!, - ) + ? PositionedAssetMarkerIcon(size: height / 2, point: value, assetRemoteId: assetMarkerRemoteId!) : const SizedBox.shrink(), ), ], diff --git a/mobile/lib/widgets/map/positioned_asset_marker_icon.dart b/mobile/lib/widgets/map/positioned_asset_marker_icon.dart index 6207a6ab56..0944f7ce3e 100644 --- a/mobile/lib/widgets/map/positioned_asset_marker_icon.dart +++ b/mobile/lib/widgets/map/positioned_asset_marker_icon.dart @@ -35,10 +35,7 @@ class PositionedAssetMarkerIcon extends StatelessWidget { onTap: () => onTap?.call(), child: SizedBox.square( dimension: size, - child: _AssetMarkerIcon( - id: assetRemoteId, - key: Key(assetRemoteId), - ), + child: _AssetMarkerIcon(id: assetRemoteId, key: Key(assetRemoteId)), ), ), ); @@ -46,10 +43,7 @@ class PositionedAssetMarkerIcon extends StatelessWidget { } class _AssetMarkerIcon extends StatelessWidget { - const _AssetMarkerIcon({ - required this.id, - super.key, - }); + const _AssetMarkerIcon({required this.id, super.key}); final String id; @@ -71,10 +65,7 @@ class _AssetMarkerIcon extends StatelessWidget { primaryRadius: constraints.maxHeight * 0.06, secondaryRadius: constraints.maxHeight * 0.038, ), - child: SizedBox( - height: constraints.maxHeight * 0.14, - width: constraints.maxWidth * 0.14, - ), + child: SizedBox(height: constraints.maxHeight * 0.14, width: constraints.maxWidth * 0.14), ), ), Positioned( @@ -129,26 +120,11 @@ class _PinPainter extends CustomPainter { ..style = PaintingStyle.stroke ..strokeWidth = 2; - canvas.drawCircle( - Offset(size.width / 2, size.height), - primaryRadius, - primaryBrush, - ); - canvas.drawCircle( - Offset(size.width / 2, size.height), - secondaryRadius, - secondaryBrush, - ); + canvas.drawCircle(Offset(size.width / 2, size.height), primaryRadius, primaryBrush); + canvas.drawCircle(Offset(size.width / 2, size.height), secondaryRadius, secondaryBrush); canvas.drawPath(getTrianglePath(size.width, size.height), primaryBrush); // The line is to make the above triangluar path more prominent since it has a slight curve - canvas.drawLine( - Offset(size.width / 2, 0), - Offset( - size.width / 2, - size.height, - ), - lineBrush, - ); + canvas.drawLine(Offset(size.width / 2, 0), Offset(size.width / 2, size.height), lineBrush); } Path getTrianglePath(double x, double y) { @@ -157,18 +133,8 @@ class _PinPainter extends CustomPainter { final secondEndPoint = Offset(x, 0); return Path() - ..quadraticBezierTo( - controlPoint.dx, - controlPoint.dy, - firstEndPoint.dx, - firstEndPoint.dy, - ) - ..quadraticBezierTo( - controlPoint.dx, - controlPoint.dy, - secondEndPoint.dx, - secondEndPoint.dy, - ) + ..quadraticBezierTo(controlPoint.dx, controlPoint.dy, firstEndPoint.dx, firstEndPoint.dy) + ..quadraticBezierTo(controlPoint.dx, controlPoint.dy, secondEndPoint.dx, secondEndPoint.dy) ..lineTo(0, 0); } diff --git a/mobile/lib/widgets/memories/memory_bottom_info.dart b/mobile/lib/widgets/memories/memory_bottom_info.dart index 1797b5c1c3..4b43821782 100644 --- a/mobile/lib/widgets/memories/memory_bottom_info.dart +++ b/mobile/lib/widgets/memories/memory_bottom_info.dart @@ -16,45 +16,35 @@ class MemoryBottomInfo extends StatelessWidget { final df = DateFormat.yMMMMd(); return Padding( padding: const EdgeInsets.all(16.0), - child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - memory.title, - style: TextStyle( - color: Colors.grey[400], - fontSize: 13.0, - fontWeight: FontWeight.w500, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + memory.title, + style: TextStyle(color: Colors.grey[400], fontSize: 13.0, fontWeight: FontWeight.w500), ), - ), - Text( - df.format( - memory.assets[0].fileCreatedAt, + Text( + df.format(memory.assets[0].fileCreatedAt), + style: const TextStyle(color: Colors.white, fontSize: 15.0, fontWeight: FontWeight.w500), ), - style: const TextStyle( - color: Colors.white, - fontSize: 15.0, - fontWeight: FontWeight.w500, - ), - ), - ], - ), - MaterialButton( - minWidth: 0, - onPressed: () { - context.maybePop(); - scrollToDateNotifierProvider.scrollToDate(memory.assets[0].fileCreatedAt); - }, - shape: const CircleBorder(), - color: Colors.white.withValues(alpha: 0.2), - elevation: 0, - child: const Icon( - Icons.open_in_new, - color: Colors.white, + ], ), - ), - ]), + MaterialButton( + minWidth: 0, + onPressed: () { + context.maybePop(); + scrollToDateNotifierProvider.scrollToDate(memory.assets[0].fileCreatedAt); + }, + shape: const CircleBorder(), + color: Colors.white.withValues(alpha: 0.2), + elevation: 0, + child: const Icon(Icons.open_in_new, color: Colors.white), + ), + ], + ), ); } } diff --git a/mobile/lib/widgets/memories/memory_card.dart b/mobile/lib/widgets/memories/memory_card.dart index 1faa114936..189cc67428 100644 --- a/mobile/lib/widgets/memories/memory_card.dart +++ b/mobile/lib/widgets/memories/memory_card.dart @@ -14,13 +14,7 @@ class MemoryCard extends StatelessWidget { final bool showTitle; final Function()? onVideoEnded; - const MemoryCard({ - required this.asset, - required this.title, - required this.showTitle, - this.onVideoEnded, - super.key, - }); + const MemoryCard({required this.asset, required this.title, required this.showTitle, this.onVideoEnded, super.key}); @override Widget build(BuildContext context) { @@ -28,17 +22,12 @@ class MemoryCard extends StatelessWidget { color: Colors.black, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(25.0)), - side: BorderSide( - color: Colors.black, - width: 1.0, - ), + side: BorderSide(color: Colors.black, width: 1.0), ), clipBehavior: Clip.hardEdge, child: Stack( children: [ - SizedBox.expand( - child: _BlurredBackdrop(asset: asset), - ), + SizedBox.expand(child: _BlurredBackdrop(asset: asset)), LayoutBuilder( builder: (context, constraints) { // Determine the fit using the aspect ratio @@ -56,12 +45,7 @@ class MemoryCard extends StatelessWidget { if (asset.isImage) { return Hero( tag: 'memory-${asset.id}', - child: ImmichImage( - asset, - fit: fit, - height: double.infinity, - width: double.infinity, - ), + child: ImmichImage(asset, fit: fit, height: double.infinity, width: double.infinity), ); } else { return Hero( @@ -74,12 +58,7 @@ class MemoryCard extends StatelessWidget { asset: asset, showControls: false, playbackDelayFactor: 2, - image: ImmichImage( - asset, - width: context.width, - height: context.height, - fit: BoxFit.contain, - ), + image: ImmichImage(asset, width: context.width, height: context.height, fit: BoxFit.contain), ), ), ); @@ -92,10 +71,7 @@ class MemoryCard extends StatelessWidget { bottom: 18.0, child: Text( title, - style: context.textTheme.headlineMedium?.copyWith( - color: Colors.white, - fontWeight: FontWeight.w500, - ), + style: context.textTheme.headlineMedium?.copyWith(color: Colors.white, fontWeight: FontWeight.w500), ), ), ], @@ -116,16 +92,9 @@ class _BlurredBackdrop extends HookWidget { // Use a nice cheap blur hash image decoration return Container( decoration: BoxDecoration( - image: DecorationImage( - image: MemoryImage( - blurhash, - ), - fit: BoxFit.cover, - ), - ), - child: Container( - color: Colors.black.withValues(alpha: 0.2), + image: DecorationImage(image: MemoryImage(blurhash), fit: BoxFit.cover), ), + child: Container(color: Colors.black.withValues(alpha: 0.2)), ); } else { // Fall back to using a more expensive image filtered @@ -136,17 +105,11 @@ class _BlurredBackdrop extends HookWidget { child: Container( decoration: BoxDecoration( image: DecorationImage( - image: ImmichImage.imageProvider( - asset: asset, - height: context.height, - width: context.width, - ), + image: ImmichImage.imageProvider(asset: asset, height: context.height, width: context.width), fit: BoxFit.cover, ), ), - child: Container( - color: Colors.black.withValues(alpha: 0.2), - ), + child: Container(color: Colors.black.withValues(alpha: 0.2)), ), ); } diff --git a/mobile/lib/widgets/memories/memory_epilogue.dart b/mobile/lib/widgets/memories/memory_epilogue.dart index 10349aa431..b866ed049a 100644 --- a/mobile/lib/widgets/memories/memory_epilogue.dart +++ b/mobile/lib/widgets/memories/memory_epilogue.dart @@ -12,24 +12,15 @@ class MemoryEpilogue extends StatefulWidget { } class _MemoryEpilogueState extends State with TickerProviderStateMixin { - late final _animationController = AnimationController( - vsync: this, - duration: const Duration( - seconds: 2, - ), - )..repeat( - reverse: true, - ); + late final _animationController = AnimationController(vsync: this, duration: const Duration(seconds: 2)) + ..repeat(reverse: true); late final Animation _animation; @override void initState() { super.initState(); - _animation = CurvedAnimation( - parent: _animationController, - curve: Curves.easeIn, - ); + _animation = CurvedAnimation(parent: _animationController, curve: Curves.easeIn); } @override @@ -55,16 +46,12 @@ class _MemoryEpilogueState extends State with TickerProviderStat const SizedBox(height: 16.0), Text( "memories_all_caught_up", - style: context.textTheme.headlineMedium?.copyWith( - color: Colors.white, - ), + style: context.textTheme.headlineMedium?.copyWith(color: Colors.white), ).tr(), const SizedBox(height: 16.0), Text( "memories_check_back_tomorrow", - style: context.textTheme.bodyMedium?.copyWith( - color: Colors.white, - ), + style: context.textTheme.bodyMedium?.copyWith(color: Colors.white), ).tr(), const SizedBox(height: 16.0), TextButton( @@ -92,23 +79,14 @@ class _MemoryEpilogueState extends State with TickerProviderStat child: AnimatedBuilder( animation: _animation, builder: (context, child) { - return Transform.translate( - offset: Offset(0, 8 * _animationController.value), - child: child, - ); + return Transform.translate(offset: Offset(0, 8 * _animationController.value), child: child); }, - child: const Icon( - size: 32, - Icons.expand_less_sharp, - color: Colors.white, - ), + child: const Icon(size: 32, Icons.expand_less_sharp, color: Colors.white), ), ), Text( "memories_swipe_to_close", - style: context.textTheme.bodyMedium?.copyWith( - color: Colors.white, - ), + style: context.textTheme.bodyMedium?.copyWith(color: Colors.white), ).tr(), ], ), diff --git a/mobile/lib/widgets/memories/memory_lane.dart b/mobile/lib/widgets/memories/memory_lane.dart index 3f97bd1ea4..727950fd86 100644 --- a/mobile/lib/widgets/memories/memory_lane.dart +++ b/mobile/lib/widgets/memories/memory_lane.dart @@ -22,17 +22,13 @@ class MemoryLane extends HookConsumerWidget { .whenData( (memories) => memories != null ? ConstrainedBox( - constraints: const BoxConstraints( - maxHeight: 200, - ), + constraints: const BoxConstraints(maxHeight: 200), child: CarouselView( itemExtent: 145.0, shrinkExtent: 1.0, elevation: 2, backgroundColor: Colors.black, - overlayColor: WidgetStateProperty.all( - Colors.white.withValues(alpha: 0.1), - ), + overlayColor: WidgetStateProperty.all(Colors.white.withValues(alpha: 0.1)), onTap: (memoryIndex) { ref.read(hapticFeedbackProvider.notifier).heavyImpact(); if (memories[memoryIndex].assets.isNotEmpty) { @@ -42,20 +38,10 @@ class MemoryLane extends HookConsumerWidget { ref.read(videoPlaybackValueProvider.notifier).reset(); } } - context.pushRoute( - MemoryRoute( - memories: memories, - memoryIndex: memoryIndex, - ), - ); + context.pushRoute(MemoryRoute(memories: memories, memoryIndex: memoryIndex)); }, children: memories - .mapIndexed( - (index, memory) => MemoryCard( - index: index, - memory: memory, - ), - ) + .mapIndexed((index, memory) => MemoryCard(index: index, memory: memory)) .toList(), ), ) @@ -68,11 +54,7 @@ class MemoryLane extends HookConsumerWidget { } class MemoryCard extends ConsumerWidget { - const MemoryCard({ - super.key, - required this.index, - required this.memory, - }); + const MemoryCard({super.key, required this.index, required this.memory}); final int index; final Memory memory; @@ -83,10 +65,7 @@ class MemoryCard extends ConsumerWidget { child: Stack( children: [ ColorFiltered( - colorFilter: ColorFilter.mode( - Colors.black.withValues(alpha: 0.2), - BlendMode.darken, - ), + colorFilter: ColorFilter.mode(Colors.black.withValues(alpha: 0.2), BlendMode.darken), child: Hero( tag: 'memory-${memory.assets[0].id}', child: ImmichImage( @@ -94,10 +73,7 @@ class MemoryCard extends ConsumerWidget { fit: BoxFit.cover, width: 205, height: 200, - placeholder: const ThumbnailPlaceholder( - width: 105, - height: 200, - ), + placeholder: const ThumbnailPlaceholder(width: 105, height: 200), ), ), ), @@ -105,16 +81,10 @@ class MemoryCard extends ConsumerWidget { bottom: 16, left: 16, child: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 114, - ), + constraints: const BoxConstraints(maxWidth: 114), child: Text( memory.title, - style: const TextStyle( - fontWeight: FontWeight.w600, - color: Colors.white, - fontSize: 15, - ), + style: const TextStyle(fontWeight: FontWeight.w600, color: Colors.white, fontSize: 15), ), ), ), diff --git a/mobile/lib/widgets/memories/memory_progress_indicator.dart b/mobile/lib/widgets/memories/memory_progress_indicator.dart index 646846cd11..aab1dc1a97 100644 --- a/mobile/lib/widgets/memories/memory_progress_indicator.dart +++ b/mobile/lib/widgets/memories/memory_progress_indicator.dart @@ -8,11 +8,7 @@ class MemoryProgressIndicator extends StatelessWidget { /// The current value of the indicator final double value; - const MemoryProgressIndicator({ - super.key, - required this.ticks, - required this.value, - }); + const MemoryProgressIndicator({super.key, required this.ticks, required this.value}); @override Widget build(BuildContext context) { @@ -37,14 +33,7 @@ class MemoryProgressIndicator extends StatelessWidget { width: tickWidth, height: 4, decoration: BoxDecoration( - border: i == 0 - ? null - : const Border( - left: BorderSide( - color: Colors.black, - width: 1, - ), - ), + border: i == 0 ? null : const Border(left: BorderSide(color: Colors.black, width: 1)), ), ), ), diff --git a/mobile/lib/widgets/photo_view/photo_view.dart b/mobile/lib/widgets/photo_view/photo_view.dart index 0b769d559b..69be96ed53 100644 --- a/mobile/lib/widgets/photo_view/photo_view.dart +++ b/mobile/lib/widgets/photo_view/photo_view.dart @@ -15,9 +15,7 @@ export 'src/photo_view_scale_state.dart'; export 'src/utils/photo_view_hero_attributes.dart'; typedef PhotoViewControllerCallback = PhotoViewControllerBase Function(); -typedef PhotoViewControllerCallbackBuilder = void Function( - PhotoViewControllerCallback photoViewMethod, -); +typedef PhotoViewControllerCallbackBuilder = void Function(PhotoViewControllerCallback photoViewMethod); /// A [StatefulWidget] that contains all the photo view rendering elements. /// @@ -269,8 +267,8 @@ class PhotoView extends StatefulWidget { this.disableScaleGestures, this.errorBuilder, this.enablePanAlways, - }) : child = null, - childSize = null; + }) : child = null, + childSize = null; /// Creates a widget that displays a zoomable child. /// @@ -310,12 +308,12 @@ class PhotoView extends StatefulWidget { this.disableScaleGestures, this.disableGestures, this.enablePanAlways, - }) : semanticLabel = null, - errorBuilder = null, - imageProvider = null, - gaplessPlayback = false, - loadingBuilder = null, - index = 0; + }) : semanticLabel = null, + errorBuilder = null, + imageProvider = null, + gaplessPlayback = false, + loadingBuilder = null, + index = 0; /// Given a [imageProvider] it resolves into an zoomable image widget using. It /// is required @@ -543,10 +541,7 @@ class _PhotoViewState extends State with AutomaticKeepAliveClientMixi Widget build(BuildContext context) { super.build(context); return LayoutBuilder( - builder: ( - BuildContext context, - BoxConstraints constraints, - ) { + builder: (BuildContext context, BoxConstraints constraints) { final computedOuterSize = widget.customSize ?? constraints.biggest; final backgroundDecoration = widget.backgroundDecoration ?? const BoxDecoration(color: Colors.black); @@ -623,72 +618,49 @@ class _PhotoViewState extends State with AutomaticKeepAliveClientMixi /// The default [ScaleStateCycle] PhotoViewScaleState defaultScaleStateCycle(PhotoViewScaleState actual) => switch (actual) { - PhotoViewScaleState.initial => PhotoViewScaleState.covering, - PhotoViewScaleState.covering => PhotoViewScaleState.originalSize, - PhotoViewScaleState.originalSize => PhotoViewScaleState.initial, - PhotoViewScaleState.zoomedIn || PhotoViewScaleState.zoomedOut => PhotoViewScaleState.initial, - }; + PhotoViewScaleState.initial => PhotoViewScaleState.covering, + PhotoViewScaleState.covering => PhotoViewScaleState.originalSize, + PhotoViewScaleState.originalSize => PhotoViewScaleState.initial, + PhotoViewScaleState.zoomedIn || PhotoViewScaleState.zoomedOut => PhotoViewScaleState.initial, +}; /// A type definition for a [Function] that receives the actual [PhotoViewScaleState] and returns the next one /// It is used internally to walk in the "doubletap gesture cycle". /// It is passed to [PhotoView.scaleStateCycle] -typedef ScaleStateCycle = PhotoViewScaleState Function( - PhotoViewScaleState actual, -); +typedef ScaleStateCycle = PhotoViewScaleState Function(PhotoViewScaleState actual); /// A type definition for a callback when the user taps up the photoview region -typedef PhotoViewImageTapUpCallback = Function( - BuildContext context, - TapUpDetails details, - PhotoViewControllerValue controllerValue, -); +typedef PhotoViewImageTapUpCallback = + Function(BuildContext context, TapUpDetails details, PhotoViewControllerValue controllerValue); /// A type definition for a callback when the user taps down the photoview region -typedef PhotoViewImageTapDownCallback = Function( - BuildContext context, - TapDownDetails details, - PhotoViewControllerValue controllerValue, -); +typedef PhotoViewImageTapDownCallback = + Function(BuildContext context, TapDownDetails details, PhotoViewControllerValue controllerValue); /// A type definition for a callback when the user drags up -typedef PhotoViewImageDragStartCallback = Function( - BuildContext context, - DragStartDetails details, - PhotoViewControllerBase controllerValue, - PhotoViewScaleStateController scaleStateController, -); +typedef PhotoViewImageDragStartCallback = + Function( + BuildContext context, + DragStartDetails details, + PhotoViewControllerBase controllerValue, + PhotoViewScaleStateController scaleStateController, + ); /// A type definition for a callback when the user drags -typedef PhotoViewImageDragUpdateCallback = Function( - BuildContext context, - DragUpdateDetails details, - PhotoViewControllerValue controllerValue, -); +typedef PhotoViewImageDragUpdateCallback = + Function(BuildContext context, DragUpdateDetails details, PhotoViewControllerValue controllerValue); /// A type definition for a callback when the user taps down the photoview region -typedef PhotoViewImageDragEndCallback = Function( - BuildContext context, - DragEndDetails details, - PhotoViewControllerValue controllerValue, -); +typedef PhotoViewImageDragEndCallback = + Function(BuildContext context, DragEndDetails details, PhotoViewControllerValue controllerValue); /// A type definition for a callback when a user finished scale -typedef PhotoViewImageScaleEndCallback = Function( - BuildContext context, - ScaleEndDetails details, - PhotoViewControllerValue controllerValue, -); +typedef PhotoViewImageScaleEndCallback = + Function(BuildContext context, ScaleEndDetails details, PhotoViewControllerValue controllerValue); /// A type definition for a callback when the user long press start -typedef PhotoViewImageLongPressStartCallback = Function( - BuildContext context, - LongPressStartDetails details, - PhotoViewControllerValue controllerValue, -); +typedef PhotoViewImageLongPressStartCallback = + Function(BuildContext context, LongPressStartDetails details, PhotoViewControllerValue controllerValue); /// A type definition for a callback to show a widget while the image is loading, a [ImageChunkEvent] is passed to inform progress -typedef LoadingBuilder = Widget Function( - BuildContext context, - ImageChunkEvent? event, - int index, -); +typedef LoadingBuilder = Widget Function(BuildContext context, ImageChunkEvent? event, int index); diff --git a/mobile/lib/widgets/photo_view/photo_view_gallery.dart b/mobile/lib/widgets/photo_view/photo_view_gallery.dart index 3f8188f7e2..3d56f3f657 100644 --- a/mobile/lib/widgets/photo_view/photo_view_gallery.dart +++ b/mobile/lib/widgets/photo_view/photo_view_gallery.dart @@ -20,16 +20,10 @@ import 'package:immich_mobile/widgets/photo_view/src/photo_view_scale_state.dart import 'package:immich_mobile/widgets/photo_view/src/utils/photo_view_hero_attributes.dart'; /// A type definition for a [Function] that receives a index after a page change in [PhotoViewGallery] -typedef PhotoViewGalleryPageChangedCallback = void Function( - int index, - PhotoViewControllerBase? controller, -); +typedef PhotoViewGalleryPageChangedCallback = void Function(int index, PhotoViewControllerBase? controller); /// A type definition for a [Function] that defines a page in [PhotoViewGallery.build] -typedef PhotoViewGalleryBuilder = PhotoViewGalleryPageOptions Function( - BuildContext context, - int index, -); +typedef PhotoViewGalleryBuilder = PhotoViewGalleryPageOptions Function(BuildContext context, int index); /// A [StatefulWidget] that shows multiple [PhotoView] widgets in a [PageView] /// @@ -126,8 +120,8 @@ class PhotoViewGallery extends StatefulWidget { this.customSize, this.allowImplicitScrolling = false, this.enablePanAlways = false, - }) : itemCount = null, - builder = null; + }) : itemCount = null, + builder = null; /// Construct a gallery with dynamic items. /// @@ -151,9 +145,9 @@ class PhotoViewGallery extends StatefulWidget { this.customSize, this.allowImplicitScrolling = false, this.enablePanAlways = false, - }) : pageOptions = null, - assert(itemCount != null), - assert(builder != null); + }) : pageOptions = null, + assert(itemCount != null), + assert(builder != null); /// A list of options to describe the items in the gallery final List? pageOptions; @@ -340,15 +334,10 @@ class _PhotoViewGalleryState extends State { heroAttributes: pageOption.heroAttributes, ); - return ClipRect( - child: photoView, - ); + return ClipRect(child: photoView); } - PhotoViewGalleryPageOptions _buildPageOption( - BuildContext context, - int index, - ) { + PhotoViewGalleryPageOptions _buildPageOption(BuildContext context, int index) { if (widget._isBuilder) { return widget.builder!(context, index); } @@ -386,9 +375,9 @@ class PhotoViewGalleryPageOptions { this.disableScaleGestures, this.disableGestures, this.errorBuilder, - }) : child = null, - childSize = null, - assert(imageProvider != null); + }) : child = null, + childSize = null, + assert(imageProvider != null); const PhotoViewGalleryPageOptions.customChild({ this.key, @@ -415,8 +404,8 @@ class PhotoViewGalleryPageOptions { this.filterQuality, this.disableScaleGestures, this.disableGestures, - }) : errorBuilder = null, - imageProvider = null; + }) : errorBuilder = null, + imageProvider = null; final Key? key; diff --git a/mobile/lib/widgets/photo_view/src/controller/photo_view_controller.dart b/mobile/lib/widgets/photo_view/src/controller/photo_view_controller.dart index 6a860695b2..2c8b406385 100644 --- a/mobile/lib/widgets/photo_view/src/controller/photo_view_controller.dart +++ b/mobile/lib/widgets/photo_view/src/controller/photo_view_controller.dart @@ -72,12 +72,7 @@ abstract class PhotoViewControllerBase { Offset? rotationFocusPoint; /// Update multiple fields of the state with only one update streamed. - void updateMultiple({ - Offset? position, - double? scale, - double? rotation, - Offset? rotationFocusPoint, - }); + void updateMultiple({Offset? position, double? scale, double? rotation, Offset? rotationFocusPoint}); } /// The state value stored and streamed by [PhotoViewController]. @@ -122,19 +117,16 @@ class PhotoViewControllerValue { /// For details of fields and methods, check [PhotoViewControllerBase]. /// class PhotoViewController implements PhotoViewControllerBase { - PhotoViewController({ - Offset initialPosition = Offset.zero, - double initialRotation = 0.0, - double? initialScale, - }) : _valueNotifier = IgnorableValueNotifier( - PhotoViewControllerValue( - position: initialPosition, - rotation: initialRotation, - scale: initialScale, - rotationFocusPoint: null, - ), + PhotoViewController({Offset initialPosition = Offset.zero, double initialRotation = 0.0, double? initialScale}) + : _valueNotifier = IgnorableValueNotifier( + PhotoViewControllerValue( + position: initialPosition, + rotation: initialRotation, + scale: initialScale, + rotationFocusPoint: null, ), - super() { + ), + super() { initial = value; prevValue = initial; @@ -299,12 +291,7 @@ class PhotoViewController implements PhotoViewControllerBase value.rotationFocusPoint; @override - void updateMultiple({ - Offset? position, - double? scale, - double? rotation, - Offset? rotationFocusPoint, - }) { + void updateMultiple({Offset? position, double? scale, double? rotation, Offset? rotationFocusPoint}) { prevValue = value; value = PhotoViewControllerValue( position: position ?? value.position, diff --git a/mobile/lib/widgets/photo_view/src/controller/photo_view_controller_delegate.dart b/mobile/lib/widgets/photo_view/src/controller/photo_view_controller_delegate.dart index d28577ea49..6825bf8ee1 100644 --- a/mobile/lib/widgets/photo_view/src/controller/photo_view_controller_delegate.dart +++ b/mobile/lib/widgets/photo_view/src/controller/photo_view_controller_delegate.dart @@ -35,23 +35,15 @@ mixin PhotoViewControllerDelegate on State { controller.setScaleInvisibly(scale); return; } - final double prevScale = controller.scale ?? - getScaleForScaleState( - scaleStateController.prevScaleState, - scaleBoundaries, - ); + final double prevScale = + controller.scale ?? getScaleForScaleState(scaleStateController.prevScaleState, scaleBoundaries); - final double nextScale = getScaleForScaleState( - scaleStateController.scaleState, - scaleBoundaries, - ); + final double nextScale = getScaleForScaleState(scaleStateController.scaleState, scaleBoundaries); _animateScale!(prevScale, nextScale); } - void addAnimateOnScaleStateUpdate( - void Function(double prevScale, double nextScale) animateScale, - ) { + void addAnimateOnScaleStateUpdate(void Function(double prevScale, double nextScale) animateScale) { _animateScale = animateScale; } @@ -62,8 +54,9 @@ mixin PhotoViewControllerDelegate on State { if (controller.scale == controller.prevValue.scale) { return; } - final PhotoViewScaleState newScaleState = - (scale > scaleBoundaries.initialScale) ? PhotoViewScaleState.zoomedIn : PhotoViewScaleState.zoomedOut; + final PhotoViewScaleState newScaleState = (scale > scaleBoundaries.initialScale) + ? PhotoViewScaleState.zoomedIn + : PhotoViewScaleState.zoomedOut; scaleStateController.setInvisibly(newScaleState); } @@ -76,10 +69,7 @@ mixin PhotoViewControllerDelegate on State { final scaleExistsOnController = controller.scale != null; if (needsRecalc || !scaleExistsOnController) { - final newScale = getScaleForScaleState( - scaleStateController.scaleState, - scaleBoundaries, - ); + final newScale = getScaleForScaleState(scaleStateController.scaleState, scaleBoundaries); markNeedsScaleRecalc = false; scale = newScale; return newScale; @@ -89,12 +79,7 @@ mixin PhotoViewControllerDelegate on State { set scale(double scale) => controller.setScaleInvisibly(scale); - void updateMultiple({ - Offset? position, - double? scale, - double? rotation, - Offset? rotationFocusPoint, - }) { + void updateMultiple({Offset? position, double? scale, double? rotation, Offset? rotationFocusPoint}) { controller.updateMultiple( position: position, scale: scale, @@ -106,8 +91,9 @@ mixin PhotoViewControllerDelegate on State { PhotoViewScaleState getScaleStateFromNewScale(double newScale) { PhotoViewScaleState newScaleState = PhotoViewScaleState.initial; if (scale != scaleBoundaries.initialScale) { - newScaleState = - (newScale > scaleBoundaries.initialScale) ? PhotoViewScaleState.zoomedIn : PhotoViewScaleState.zoomedOut; + newScaleState = (newScale > scaleBoundaries.initialScale) + ? PhotoViewScaleState.zoomedIn + : PhotoViewScaleState.zoomedOut; } return newScaleState; } @@ -115,8 +101,9 @@ mixin PhotoViewControllerDelegate on State { void updateScaleStateFromNewScale(double newScale) { PhotoViewScaleState newScaleState = PhotoViewScaleState.initial; if (scale != scaleBoundaries.initialScale) { - newScaleState = - (newScale > scaleBoundaries.initialScale) ? PhotoViewScaleState.zoomedIn : PhotoViewScaleState.zoomedOut; + newScaleState = (newScale > scaleBoundaries.initialScale) + ? PhotoViewScaleState.zoomedIn + : PhotoViewScaleState.zoomedOut; } scaleStateController.setInvisibly(newScaleState); } @@ -127,10 +114,7 @@ mixin PhotoViewControllerDelegate on State { scaleStateController.scaleState = scaleStateCycle(scaleState); return; } - final double originalScale = getScaleForScaleState( - scaleState, - scaleBoundaries, - ); + final double originalScale = getScaleForScaleState(scaleState, scaleBoundaries); double prevScale = originalScale; PhotoViewScaleState prevScaleState = scaleState; diff --git a/mobile/lib/widgets/photo_view/src/controller/photo_view_scalestate_controller.dart b/mobile/lib/widgets/photo_view/src/controller/photo_view_scalestate_controller.dart index e96aff7780..8d078db2c8 100644 --- a/mobile/lib/widgets/photo_view/src/controller/photo_view_scalestate_controller.dart +++ b/mobile/lib/widgets/photo_view/src/controller/photo_view_scalestate_controller.dart @@ -19,8 +19,9 @@ typedef ScaleStateListener = void Function(double prevScale, double nextScale); /// The updates should be done via [scaleState] setter and the updated listened via [outputScaleStateStream] /// class PhotoViewScaleStateController { - late final IgnorableValueNotifier _scaleStateNotifier = - IgnorableValueNotifier(PhotoViewScaleState.initial)..addListener(_scaleStateChangeListener); + late final IgnorableValueNotifier _scaleStateNotifier = IgnorableValueNotifier( + PhotoViewScaleState.initial, + )..addListener(_scaleStateChangeListener); final StreamController _outputScaleStateCtrl = StreamController.broadcast() ..sink.add(PhotoViewScaleState.initial); diff --git a/mobile/lib/widgets/photo_view/src/core/photo_view_core.dart b/mobile/lib/widgets/photo_view/src/core/photo_view_core.dart index e1ec36862a..944e5ba7e6 100644 --- a/mobile/lib/widgets/photo_view/src/core/photo_view_core.dart +++ b/mobile/lib/widgets/photo_view/src/core/photo_view_core.dart @@ -18,9 +18,7 @@ import 'package:immich_mobile/widgets/photo_view/src/core/photo_view_gesture_det import 'package:immich_mobile/widgets/photo_view/src/core/photo_view_hit_corners.dart'; import 'package:immich_mobile/widgets/photo_view/src/utils/photo_view_utils.dart'; -const _defaultDecoration = BoxDecoration( - color: Color.fromRGBO(0, 0, 0, 1.0), -); +const _defaultDecoration = BoxDecoration(color: Color.fromRGBO(0, 0, 0, 1.0)); /// Internal widget in which controls all animations lifecycle, core responses /// to user gestures, updates to the controller state and mounts the entire PhotoView Layout @@ -77,9 +75,9 @@ class PhotoViewCore extends StatefulWidget { required this.disableGestures, required this.disableScaleGestures, required this.enablePanAlways, - }) : semanticLabel = null, - imageProvider = null, - gaplessPlayback = false; + }) : semanticLabel = null, + imageProvider = null, + gaplessPlayback = false; final Decoration? backgroundDecoration; final ImageProvider? imageProvider; @@ -163,9 +161,9 @@ class PhotoViewCoreState extends State } bool _shouldAllowPanRotate() => switch (scaleStateController.scaleState) { - PhotoViewScaleState.zoomedIn => scaleStateController.hasZoomedOutManually, - _ => true, - }; + PhotoViewScaleState.zoomedIn => scaleStateController.hasZoomedOutManually, + _ => true, + }; void onScaleUpdate(ScaleUpdateDetails details) { final double newScale = _scaleBefore! * details.scale; @@ -206,10 +204,7 @@ class PhotoViewCoreState extends State if (s > maxScale) { final double scaleComebackRatio = maxScale / s; animateScale(s, maxScale); - final Offset clampedPosition = clampPosition( - position: p * scaleComebackRatio, - scale: maxScale, - ); + final Offset clampedPosition = clampPosition(position: p * scaleComebackRatio, scale: maxScale); animatePosition(p, clampedPosition); return; } @@ -218,13 +213,7 @@ class PhotoViewCoreState extends State if (s < minScale) { final double scaleComebackRatio = minScale / s; animateScale(s, minScale); - animatePosition( - p, - clampPosition( - position: p * scaleComebackRatio, - scale: minScale, - ), - ); + animatePosition(p, clampPosition(position: p * scaleComebackRatio, scale: minScale)); return; } // get magnitude from gesture velocity @@ -233,10 +222,7 @@ class PhotoViewCoreState extends State // animate velocity only if there is no scale change and a significant magnitude if (_scaleBefore! / s == 1.0 && magnitude >= 400.0) { final Offset direction = details.velocity.pixelsPerSecond / magnitude; - animatePosition( - p, - clampPosition(position: p + direction * 100.0), - ); + animatePosition(p, clampPosition(position: p + direction * 100.0)); } } @@ -248,10 +234,7 @@ class PhotoViewCoreState extends State if (!mounted) { return; } - _scaleAnimation = Tween( - begin: from, - end: to, - ).animate(_scaleAnimationController); + _scaleAnimation = Tween(begin: from, end: to).animate(_scaleAnimationController); _scaleAnimationController ..value = 0.0 ..fling(velocity: 0.4); @@ -355,10 +338,7 @@ class PhotoViewCoreState extends State return StreamBuilder( stream: controller.outputStateStream, initialData: controller.prevValue, - builder: ( - BuildContext context, - AsyncSnapshot snapshot, - ) { + builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData) { final PhotoViewControllerValue value = snapshot.data!; final useImageScale = widget.filterQuality != FilterQuality.none; @@ -371,11 +351,7 @@ class PhotoViewCoreState extends State ..rotateZ(value.rotation); final Widget customChildLayout = CustomSingleChildLayout( - delegate: _CenterWithOriginalSizeDelegate( - scaleBoundaries.childSize, - basePosition, - useImageScale, - ), + delegate: _CenterWithOriginalSizeDelegate(scaleBoundaries.childSize, basePosition, useImageScale), child: _buildHero(_buildChild()), ); @@ -383,11 +359,7 @@ class PhotoViewCoreState extends State constraints: widget.tightMode ? BoxConstraints.tight(scaleBoundaries.childSize * scale) : null, decoration: widget.backgroundDecoration ?? _defaultDecoration, child: Center( - child: Transform( - transform: matrix, - alignment: basePosition, - child: customChildLayout, - ), + child: Transform(transform: matrix, alignment: basePosition, child: customChildLayout), ), ); @@ -402,28 +374,20 @@ class PhotoViewCoreState extends State onScaleUpdate: widget.disableScaleGestures ? null : onScaleUpdate, onScaleEnd: widget.disableScaleGestures ? null : onScaleEnd, onDragStart: widget.onDragStart != null - ? (details) => widget.onDragStart!( - context, - details, - widget.controller, - widget.scaleStateController, - ) + ? (details) => widget.onDragStart!(context, details, widget.controller, widget.scaleStateController) : null, onDragEnd: widget.onDragEnd != null ? (details) => widget.onDragEnd!(context, details, widget.controller.value) : null, onDragUpdate: widget.onDragUpdate != null - ? (details) => widget.onDragUpdate!( - context, - details, - widget.controller.value, - ) + ? (details) => widget.onDragUpdate!(context, details, widget.controller.value) : null, hitDetector: this, onTapUp: widget.onTapUp != null ? (details) => widget.onTapUp!(context, details, value) : null, onTapDown: widget.onTapDown != null ? (details) => widget.onTapDown!(context, details, value) : null, - onLongPressStart: - widget.onLongPressStart != null ? (details) => widget.onLongPressStart!(context, details, value) : null, + onLongPressStart: widget.onLongPressStart != null + ? (details) => widget.onLongPressStart!(context, details, value) + : null, child: child, ); } else { @@ -462,11 +426,7 @@ class PhotoViewCoreState extends State } class _CenterWithOriginalSizeDelegate extends SingleChildLayoutDelegate { - const _CenterWithOriginalSizeDelegate( - this.subjectSize, - this.basePosition, - this.useImageScale, - ); + const _CenterWithOriginalSizeDelegate(this.subjectSize, this.basePosition, this.useImageScale); final Size subjectSize; final Alignment basePosition; diff --git a/mobile/lib/widgets/photo_view/src/core/photo_view_gesture_detector.dart b/mobile/lib/widgets/photo_view/src/core/photo_view_gesture_detector.dart index 6f456713a9..7a5406c675 100644 --- a/mobile/lib/widgets/photo_view/src/core/photo_view_gesture_detector.dart +++ b/mobile/lib/widgets/photo_view/src/core/photo_view_gesture_detector.dart @@ -103,15 +103,13 @@ class PhotoViewGestureDetector extends StatelessWidget { ); gestures[LongPressGestureRecognizer] = GestureRecognizerFactoryWithHandlers( - () => LongPressGestureRecognizer(debugOwner: this), (LongPressGestureRecognizer instance) { - instance.onLongPressStart = onLongPressStart; - }); - - return RawGestureDetector( - behavior: behavior, - gestures: gestures, - child: child, + () => LongPressGestureRecognizer(debugOwner: this), + (LongPressGestureRecognizer instance) { + instance.onLongPressStart = onLongPressStart; + }, ); + + return RawGestureDetector(behavior: behavior, gestures: gestures, child: child); } } @@ -241,16 +239,11 @@ class PhotoViewGestureRecognizer extends ScaleGestureRecognizer { /// ); /// ``` class PhotoViewGestureDetectorScope extends InheritedWidget { - const PhotoViewGestureDetectorScope({ - super.key, - this.axis, - this.touchSlopFactor = .2, - required super.child, - }); + const PhotoViewGestureDetectorScope({super.key, this.axis, this.touchSlopFactor = .2, required super.child}); static PhotoViewGestureDetectorScope? of(BuildContext context) { - final PhotoViewGestureDetectorScope? scope = - context.dependOnInheritedWidgetOfExactType(); + final PhotoViewGestureDetectorScope? scope = context + .dependOnInheritedWidgetOfExactType(); return scope; } @@ -273,10 +266,7 @@ class PhotoViewGestureDetectorScope extends InheritedWidget { // we cannot change that, but we can prevent the scrollable from panning until this threshold is reached // and let other recognizers accept the gesture instead class PhotoViewPageViewScrollPhysics extends ScrollPhysics { - const PhotoViewPageViewScrollPhysics({ - this.touchSlopFactor = 0.1, - super.parent, - }); + const PhotoViewPageViewScrollPhysics({this.touchSlopFactor = 0.1, super.parent}); // in [0, 1] // 0: most reactive but will not let PhotoView recognizers accept gestures @@ -285,10 +275,7 @@ class PhotoViewPageViewScrollPhysics extends ScrollPhysics { @override PhotoViewPageViewScrollPhysics applyTo(ScrollPhysics? ancestor) { - return PhotoViewPageViewScrollPhysics( - touchSlopFactor: touchSlopFactor, - parent: buildParent(ancestor), - ); + return PhotoViewPageViewScrollPhysics(touchSlopFactor: touchSlopFactor, parent: buildParent(ancestor)); } @override diff --git a/mobile/lib/widgets/photo_view/src/core/photo_view_hit_corners.dart b/mobile/lib/widgets/photo_view/src/core/photo_view_hit_corners.dart index b02b7feb68..eac0cb50ce 100644 --- a/mobile/lib/widgets/photo_view/src/core/photo_view_hit_corners.dart +++ b/mobile/lib/widgets/photo_view/src/core/photo_view_hit_corners.dart @@ -25,10 +25,7 @@ mixin HitCornersDetector on PhotoViewControllerDelegate { return HitCorners(y <= cornersY.min, y >= cornersY.max); } - bool _shouldMoveAxis( - HitCorners hitCorners, - double mainAxisMove, - ) { + bool _shouldMoveAxis(HitCorners hitCorners, double mainAxisMove) { if (mainAxisMove == 0) { return false; } diff --git a/mobile/lib/widgets/photo_view/src/photo_view_default_widgets.dart b/mobile/lib/widgets/photo_view/src/photo_view_default_widgets.dart index 912fb5e839..fac0550c3b 100644 --- a/mobile/lib/widgets/photo_view/src/photo_view_default_widgets.dart +++ b/mobile/lib/widgets/photo_view/src/photo_view_default_widgets.dart @@ -9,13 +9,7 @@ class PhotoViewDefaultError extends StatelessWidget { Widget build(BuildContext context) { return DecoratedBox( decoration: decoration, - child: Center( - child: Icon( - Icons.broken_image, - color: Colors.grey[400], - size: 40.0, - ), - ), + child: Center(child: Icon(Icons.broken_image, color: Colors.grey[400], size: 40.0)), ); } } @@ -32,11 +26,7 @@ class PhotoViewDefaultLoading extends StatelessWidget { final value = loadedBytes != null && expectedBytes != null ? loadedBytes / expectedBytes : null; return Center( - child: SizedBox( - width: 20.0, - height: 20.0, - child: CircularProgressIndicator(value: value), - ), + child: SizedBox(width: 20.0, height: 20.0, child: CircularProgressIndicator(value: value)), ); } } diff --git a/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart b/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart index d4afe85d2b..4f283e55fd 100644 --- a/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart +++ b/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart @@ -109,9 +109,7 @@ class _ImageWrapperState extends State { // retrieve image from the provider void _resolveImage() { - final ImageStream newStream = widget.imageProvider.resolve( - const ImageConfiguration(), - ); + final ImageStream newStream = widget.imageProvider.resolve(const ImageConfiguration()); _updateSourceStream(newStream); } @@ -125,10 +123,7 @@ class _ImageWrapperState extends State { void handleImageFrame(ImageInfo info, bool synchronousCall) { setupCB() { - _imageSize = Size( - info.image.width.toDouble(), - info.image.height.toDouble(), - ); + _imageSize = Size(info.image.width.toDouble(), info.image.height.toDouble()); _loading = false; _imageInfo = _imageInfo; @@ -154,11 +149,7 @@ class _ImageWrapperState extends State { }()); } - _imageStreamListener = ImageStreamListener( - handleImageFrame, - onChunk: handleImageChunk, - onError: handleError, - ); + _imageStreamListener = ImageStreamListener(handleImageFrame, onChunk: handleImageChunk, onError: handleError); return _imageStreamListener!; } @@ -227,20 +218,14 @@ class _ImageWrapperState extends State { return widget.loadingBuilder!(context, _loadingProgress, widget.index); } - return PhotoViewDefaultLoading( - event: _loadingProgress, - ); + return PhotoViewDefaultLoading(event: _loadingProgress); } - Widget _buildError( - BuildContext context, - ) { + Widget _buildError(BuildContext context) { if (widget.errorBuilder != null) { return widget.errorBuilder!(context, _lastException!, _lastStack); } - return PhotoViewDefaultError( - decoration: widget.backgroundDecoration, - ); + return PhotoViewDefaultError(decoration: widget.backgroundDecoration); } } diff --git a/mobile/lib/widgets/photo_view/src/utils/ignorable_change_notifier.dart b/mobile/lib/widgets/photo_view/src/utils/ignorable_change_notifier.dart index da213903f6..3ca31cb8f9 100644 --- a/mobile/lib/widgets/photo_view/src/utils/ignorable_change_notifier.dart +++ b/mobile/lib/widgets/photo_view/src/utils/ignorable_change_notifier.dart @@ -58,11 +58,7 @@ class IgnorableChangeNotifier extends ChangeNotifier { } } catch (exception, stack) { FlutterError.reportError( - FlutterErrorDetails( - exception: exception, - stack: stack, - library: 'Photoview library', - ), + FlutterErrorDetails(exception: exception, stack: stack, library: 'Photoview library'), ); } } diff --git a/mobile/lib/widgets/photo_view/src/utils/photo_view_utils.dart b/mobile/lib/widgets/photo_view/src/utils/photo_view_utils.dart index 1efdc50161..d120955250 100644 --- a/mobile/lib/widgets/photo_view/src/utils/photo_view_utils.dart +++ b/mobile/lib/widgets/photo_view/src/utils/photo_view_utils.dart @@ -5,22 +5,15 @@ import "package:immich_mobile/widgets/photo_view/src/photo_view_computed_scale.d import 'package:immich_mobile/widgets/photo_view/src/photo_view_scale_state.dart'; /// Given a [PhotoViewScaleState], returns a scale value considering [scaleBoundaries]. -double getScaleForScaleState( - PhotoViewScaleState scaleState, - ScaleBoundaries scaleBoundaries, -) { +double getScaleForScaleState(PhotoViewScaleState scaleState, ScaleBoundaries scaleBoundaries) { return switch (scaleState) { PhotoViewScaleState.initial || PhotoViewScaleState.zoomedIn || - PhotoViewScaleState.zoomedOut => - _clampSize(scaleBoundaries.initialScale, scaleBoundaries), + PhotoViewScaleState.zoomedOut => _clampSize(scaleBoundaries.initialScale, scaleBoundaries), PhotoViewScaleState.covering => _clampSize( - _scaleForCovering( - scaleBoundaries.outerSize, - scaleBoundaries.childSize, - ), - scaleBoundaries, - ), + _scaleForCovering(scaleBoundaries.outerSize, scaleBoundaries.childSize), + scaleBoundaries, + ), PhotoViewScaleState.originalSize => _clampSize(1.0, scaleBoundaries), }; } @@ -28,13 +21,7 @@ double getScaleForScaleState( /// Internal class to wraps custom scale boundaries (min, max and initial) /// Also, stores values regarding the two sizes: the container and the child. class ScaleBoundaries { - const ScaleBoundaries( - this._minScale, - this._maxScale, - this._initialScale, - this.outerSize, - this.childSize, - ); + const ScaleBoundaries(this._minScale, this._maxScale, this._initialScale, this.outerSize, this.childSize); final dynamic _minScale; final dynamic _maxScale; diff --git a/mobile/lib/widgets/search/curated_people_row.dart b/mobile/lib/widgets/search/curated_people_row.dart index 10c19c7e60..74fc3e1c34 100644 --- a/mobile/lib/widgets/search/curated_people_row.dart +++ b/mobile/lib/widgets/search/curated_people_row.dart @@ -15,13 +15,7 @@ class CuratedPeopleRow extends StatelessWidget { final Function(SearchCuratedContent, int)? onTap; final Function(SearchCuratedContent, int)? onNameTap; - const CuratedPeopleRow({ - super.key, - required this.content, - this.onTap, - this.padding, - required this.onNameTap, - }); + const CuratedPeopleRow({super.key, required this.content, this.onTap, this.padding, required this.onNameTap}); @override Widget build(BuildContext context) { @@ -50,19 +44,13 @@ class CuratedPeopleRow extends StatelessWidget { elevation: 3, child: CircleAvatar( maxRadius: imageSize / 2, - backgroundImage: NetworkImage( - getFaceThumbnailUrl(person.id), - headers: headers, - ), + backgroundImage: NetworkImage(getFaceThumbnailUrl(person.id), headers: headers), ), ), ), ), const SizedBox(height: 8), - SizedBox( - width: imageSize, - child: _buildPersonLabel(context, person, index), - ), + SizedBox(width: imageSize, child: _buildPersonLabel(context, person, index)), ], ), ); @@ -72,19 +60,13 @@ class CuratedPeopleRow extends StatelessWidget { ); } - Widget _buildPersonLabel( - BuildContext context, - SearchCuratedContent person, - int index, - ) { + Widget _buildPersonLabel(BuildContext context, SearchCuratedContent person, int index) { if (person.label.isEmpty) { return GestureDetector( onTap: () => onNameTap?.call(person, index), child: Text( "exif_bottom_sheet_person_add_person", - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), maxLines: 2, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, @@ -101,11 +83,7 @@ class CuratedPeopleRow extends StatelessWidget { style: context.textTheme.labelLarge, maxLines: 2, ), - if (person.subtitle != null) - Text( - person.subtitle!, - textAlign: TextAlign.center, - ), + if (person.subtitle != null) Text(person.subtitle!, textAlign: TextAlign.center), ], ); } diff --git a/mobile/lib/widgets/search/curated_places_row.dart b/mobile/lib/widgets/search/curated_places_row.dart index 38092071d0..9d21292bde 100644 --- a/mobile/lib/widgets/search/curated_places_row.dart +++ b/mobile/lib/widgets/search/curated_places_row.dart @@ -31,9 +31,7 @@ class CuratedPlacesRow extends StatelessWidget { height: imageSize, child: ListView.separated( scrollDirection: Axis.horizontal, - padding: const EdgeInsets.symmetric( - horizontal: 16, - ), + padding: const EdgeInsets.symmetric(horizontal: 16), separatorBuilder: (context, index) => const SizedBox(width: 10), itemBuilder: (context, index) { // Injecting Map thumbnail as the first element diff --git a/mobile/lib/widgets/search/explore_grid.dart b/mobile/lib/widgets/search/explore_grid.dart index 1841f7f051..a6e1cf5aac 100644 --- a/mobile/lib/widgets/search/explore_grid.dart +++ b/mobile/lib/widgets/search/explore_grid.dart @@ -13,11 +13,7 @@ class ExploreGrid extends StatelessWidget { final List curatedContent; final bool isPeople; - const ExploreGrid({ - super.key, - required this.curatedContent, - this.isPeople = false, - }); + const ExploreGrid({super.key, required this.curatedContent, this.isPeople = false}); @override Widget build(BuildContext context) { @@ -27,10 +23,7 @@ class ExploreGrid extends StatelessWidget { child: SizedBox( height: 100, width: 100, - child: ThumbnailWithInfo( - textInfo: '', - onTap: () {}, - ), + child: ThumbnailWithInfo(textInfo: '', onTap: () {}), ), ); } @@ -53,26 +46,15 @@ class ExploreGrid extends StatelessWidget { borderRadius: 0, onTap: () { isPeople - ? context.pushRoute( - PersonResultRoute( - personId: content.id, - personName: content.label, - ), - ) + ? context.pushRoute(PersonResultRoute(personId: content.id, personName: content.label)) : context.pushRoute( SearchRoute( prefilter: SearchFilter( people: {}, - location: SearchLocationFilter( - city: content.label, - ), + location: SearchLocationFilter(city: content.label), camera: SearchCameraFilter(), date: SearchDateFilter(), - display: SearchDisplayFilters( - isNotInAlbum: false, - isArchive: false, - isFavorite: false, - ), + display: SearchDisplayFilters(isNotInAlbum: false, isArchive: false, isFavorite: false), mediaType: AssetType.other, ), ), diff --git a/mobile/lib/widgets/search/person_name_edit_form.dart b/mobile/lib/widgets/search/person_name_edit_form.dart index 886f17b2cc..d95d7c7483 100644 --- a/mobile/lib/widgets/search/person_name_edit_form.dart +++ b/mobile/lib/widgets/search/person_name_edit_form.dart @@ -16,11 +16,7 @@ class PersonNameEditForm extends HookConsumerWidget { final String personId; final String personName; - const PersonNameEditForm({ - super.key, - required this.personId, - required this.personName, - }); + const PersonNameEditForm({super.key, required this.personId, required this.personName}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -28,10 +24,7 @@ class PersonNameEditForm extends HookConsumerWidget { final isError = useState(false); return AlertDialog( - title: const Text( - "add_a_name", - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(), + title: const Text("add_a_name", style: TextStyle(fontWeight: FontWeight.bold)).tr(), content: SingleChildScrollView( child: TextFormField( controller: controller, @@ -46,23 +39,16 @@ class PersonNameEditForm extends HookConsumerWidget { ), actions: [ TextButton( - onPressed: () => context.pop( - const PersonNameEditFormResult(false, ''), - ), + onPressed: () => context.pop(const PersonNameEditFormResult(false, '')), child: Text( "cancel", - style: TextStyle( - color: Colors.red[300], - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: Colors.red[300], fontWeight: FontWeight.bold), ).tr(), ), TextButton( onPressed: () async { isError.value = false; - final result = await ref.read( - updatePersonNameProvider(personId, controller.text).future, - ); + final result = await ref.read(updatePersonNameProvider(personId, controller.text).future); isError.value = !result; if (result) { context.pop(PersonNameEditFormResult(true, controller.text)); @@ -70,10 +56,7 @@ class PersonNameEditForm extends HookConsumerWidget { }, child: Text( "save", - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold), ).tr(), ), ], diff --git a/mobile/lib/widgets/search/search_filter/camera_picker.dart b/mobile/lib/widgets/search/search_filter/camera_picker.dart index a7c0bb89af..a5204c2fbc 100644 --- a/mobile/lib/widgets/search/search_filter/camera_picker.dart +++ b/mobile/lib/widgets/search/search_filter/camera_picker.dart @@ -21,30 +21,14 @@ class CameraPicker extends HookConsumerWidget { final selectedMake = useState(filter?.make); final selectedModel = useState(filter?.model); - final make = ref.watch( - getSearchSuggestionsProvider( - SearchSuggestionType.cameraMake, - ), - ); + final make = ref.watch(getSearchSuggestionsProvider(SearchSuggestionType.cameraMake)); - final models = ref.watch( - getSearchSuggestionsProvider( - SearchSuggestionType.cameraModel, - make: selectedMake.value, - ), - ); + final models = ref.watch(getSearchSuggestionsProvider(SearchSuggestionType.cameraModel, make: selectedMake.value)); final makeWidget = SearchDropdown( dropdownMenuEntries: switch (make) { AsyncError() => [], - AsyncData(:final value) => value - .map( - (e) => DropdownMenuEntry( - value: e, - label: e, - ), - ) - .toList(), + AsyncData(:final value) => value.map((e) => DropdownMenuEntry(value: e, label: e)).toList(), _ => [], }, label: const Text('make').tr(), @@ -56,24 +40,14 @@ class CameraPicker extends HookConsumerWidget { } selectedMake.value = value.toString(); modelTextController.value = TextEditingValue.empty; - onSelect({ - 'make': selectedMake.value, - 'model': null, - }); + onSelect({'make': selectedMake.value, 'model': null}); }, ); final modelWidget = SearchDropdown( dropdownMenuEntries: switch (models) { AsyncError() => [], - AsyncData(:final value) => value - .map( - (e) => DropdownMenuEntry( - value: e, - label: e, - ), - ) - .toList(), + AsyncData(:final value) => value.map((e) => DropdownMenuEntry(value: e, label: e)).toList(), _ => [], }, label: const Text('model').tr(), @@ -81,21 +55,12 @@ class CameraPicker extends HookConsumerWidget { leadingIcon: const Icon(Icons.camera), onSelected: (value) { selectedModel.value = value.toString(); - onSelect({ - 'make': selectedMake.value, - 'model': selectedModel.value, - }); + onSelect({'make': selectedMake.value, 'model': selectedModel.value}); }, ); if (context.isMobile) { - return Column( - children: [ - makeWidget, - const SizedBox(height: 8), - modelWidget, - ], - ); + return Column(children: [makeWidget, const SizedBox(height: 8), modelWidget]); } return Row( diff --git a/mobile/lib/widgets/search/search_filter/common/dropdown.dart b/mobile/lib/widgets/search/search_filter/common/dropdown.dart index dd0ec44e45..70cbfd2c15 100644 --- a/mobile/lib/widgets/search/search_filter/common/dropdown.dart +++ b/mobile/lib/widgets/search/search_filter/common/dropdown.dart @@ -20,9 +20,7 @@ class SearchDropdown extends StatelessWidget { Widget build(BuildContext context) { final menuStyle = const MenuStyle( shape: WidgetStatePropertyAll( - RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(15)), - ), + RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15))), ), ); diff --git a/mobile/lib/widgets/search/search_filter/display_option_picker.dart b/mobile/lib/widgets/search/search_filter/display_option_picker.dart index 5deed5fe1b..a64eab7b71 100644 --- a/mobile/lib/widgets/search/search_filter/display_option_picker.dart +++ b/mobile/lib/widgets/search/search_filter/display_option_picker.dart @@ -3,18 +3,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:immich_mobile/models/search/search_filter.model.dart'; -enum DisplayOption { - notInAlbum, - favorite, - archive, -} +enum DisplayOption { notInAlbum, favorite, archive } class DisplayOptionPicker extends HookWidget { - const DisplayOptionPicker({ - super.key, - required this.onSelect, - this.filter, - }); + const DisplayOptionPicker({super.key, required this.onSelect, this.filter}); final Function(Map) onSelect; final SearchDisplayFilters? filter; @@ -34,10 +26,7 @@ class DisplayOptionPicker extends HookWidget { title: const Text('search_filter_display_option_not_in_album').tr(), value: options.value[DisplayOption.notInAlbum], onChanged: (bool? value) { - options.value = { - ...options.value, - DisplayOption.notInAlbum: value!, - }; + options.value = {...options.value, DisplayOption.notInAlbum: value!}; onSelect(options.value); }, ), @@ -45,10 +34,7 @@ class DisplayOptionPicker extends HookWidget { title: const Text('favorite').tr(), value: options.value[DisplayOption.favorite], onChanged: (value) { - options.value = { - ...options.value, - DisplayOption.favorite: value!, - }; + options.value = {...options.value, DisplayOption.favorite: value!}; onSelect(options.value); }, ), @@ -56,10 +42,7 @@ class DisplayOptionPicker extends HookWidget { title: const Text('archive').tr(), value: options.value[DisplayOption.archive], onChanged: (value) { - options.value = { - ...options.value, - DisplayOption.archive: value!, - }; + options.value = {...options.value, DisplayOption.archive: value!}; onSelect(options.value); }, ), diff --git a/mobile/lib/widgets/search/search_filter/filter_bottom_sheet_scaffold.dart b/mobile/lib/widgets/search/search_filter/filter_bottom_sheet_scaffold.dart index f534b94256..e8226b5b3a 100644 --- a/mobile/lib/widgets/search/search_filter/filter_bottom_sheet_scaffold.dart +++ b/mobile/lib/widgets/search/search_filter/filter_bottom_sheet_scaffold.dart @@ -33,10 +33,7 @@ class FilterBottomSheetScaffold extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.all(16.0), - child: Text( - title, - style: context.textTheme.headlineSmall, - ), + child: Text(title, style: context.textTheme.headlineSmall), ), buildChildWidget(), Padding( diff --git a/mobile/lib/widgets/search/search_filter/location_picker.dart b/mobile/lib/widgets/search/search_filter/location_picker.dart index eea4b52256..608183a2f6 100644 --- a/mobile/lib/widgets/search/search_filter/location_picker.dart +++ b/mobile/lib/widgets/search/search_filter/location_picker.dart @@ -52,14 +52,7 @@ class LocationPicker extends HookConsumerWidget { SearchDropdown( dropdownMenuEntries: switch (countries) { AsyncError() => [], - AsyncData(:final value) => value - .map( - (e) => DropdownMenuEntry( - value: e, - label: e, - ), - ) - .toList(), + AsyncData(:final value) => value.map((e) => DropdownMenuEntry(value: e, label: e)).toList(), _ => [], }, label: const Text('country').tr(), @@ -71,27 +64,14 @@ class LocationPicker extends HookConsumerWidget { selectedCountry.value = value.toString(); stateTextController.value = TextEditingValue.empty; cityTextController.value = TextEditingValue.empty; - onSelected({ - 'country': selectedCountry.value, - 'state': null, - 'city': null, - }); + onSelected({'country': selectedCountry.value, 'state': null, 'city': null}); }, ), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), SearchDropdown( dropdownMenuEntries: switch (states) { AsyncError() => [], - AsyncData(:final value) => value - .map( - (e) => DropdownMenuEntry( - value: e, - label: e, - ), - ) - .toList(), + AsyncData(:final value) => value.map((e) => DropdownMenuEntry(value: e, label: e)).toList(), _ => [], }, label: const Text('state').tr(), @@ -102,38 +82,21 @@ class LocationPicker extends HookConsumerWidget { } selectedState.value = value.toString(); cityTextController.value = TextEditingValue.empty; - onSelected({ - 'country': selectedCountry.value, - 'state': selectedState.value, - 'city': null, - }); + onSelected({'country': selectedCountry.value, 'state': selectedState.value, 'city': null}); }, ), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), SearchDropdown( dropdownMenuEntries: switch (cities) { AsyncError() => [], - AsyncData(:final value) => value - .map( - (e) => DropdownMenuEntry( - value: e, - label: e, - ), - ) - .toList(), + AsyncData(:final value) => value.map((e) => DropdownMenuEntry(value: e, label: e)).toList(), _ => [], }, label: const Text('city').tr(), controller: cityTextController, onSelected: (value) { selectedCity.value = value.toString(); - onSelected({ - 'country': selectedCountry.value, - 'state': selectedState.value, - 'city': selectedCity.value, - }); + onSelected({'country': selectedCountry.value, 'state': selectedState.value, 'city': selectedCity.value}); }, ), ], diff --git a/mobile/lib/widgets/search/search_filter/people_picker.dart b/mobile/lib/widgets/search/search_filter/people_picker.dart index 991664dd93..b2a7a18c7c 100644 --- a/mobile/lib/widgets/search/search_filter/people_picker.dart +++ b/mobile/lib/widgets/search/search_filter/people_picker.dart @@ -40,10 +40,7 @@ class PeoplePicker extends HookConsumerWidget { ), Padding( padding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 0), - child: Divider( - color: context.colorScheme.surfaceContainerHighest, - thickness: 1, - ), + child: Divider(color: context.colorScheme.surfaceContainerHighest, thickness: 1), ), Expanded( child: people.widgetWhen( @@ -51,16 +48,12 @@ class PeoplePicker extends HookConsumerWidget { return ListView.builder( shrinkWrap: true, itemCount: people - .where( - (person) => person.name.toLowerCase().contains(searchQuery.value.toLowerCase()), - ) + .where((person) => person.name.toLowerCase().contains(searchQuery.value.toLowerCase())) .length, padding: const EdgeInsets.all(8), itemBuilder: (context, index) { final person = people - .where( - (person) => person.name.toLowerCase().contains(searchQuery.value.toLowerCase()), - ) + .where((person) => person.name.toLowerCase().contains(searchQuery.value.toLowerCase())) .toList()[index]; final isSelected = selectedPeople.value.contains(person); @@ -82,10 +75,7 @@ class PeoplePicker extends HookConsumerWidget { elevation: 3, child: CircleAvatar( maxRadius: imageSize / 2, - backgroundImage: NetworkImage( - getFaceThumbnailUrl(person.id), - headers: headers, - ), + backgroundImage: NetworkImage(getFaceThumbnailUrl(person.id), headers: headers), ), ), ), diff --git a/mobile/lib/widgets/search/search_filter/search_filter_chip.dart b/mobile/lib/widgets/search/search_filter/search_filter_chip.dart index 60d1366fbd..a72b4668dd 100644 --- a/mobile/lib/widgets/search/search_filter/search_filter_chip.dart +++ b/mobile/lib/widgets/search/search_filter/search_filter_chip.dart @@ -7,13 +7,7 @@ class SearchFilterChip extends StatelessWidget { final Widget? currentFilter; final IconData icon; - const SearchFilterChip({ - super.key, - required this.label, - required this.onTap, - required this.icon, - this.currentFilter, - }); + const SearchFilterChip({super.key, required this.label, required this.onTap, required this.icon, this.currentFilter}); @override Widget build(BuildContext context) { @@ -23,21 +17,10 @@ class SearchFilterChip extends StatelessWidget { child: Card( elevation: 0, color: context.primaryColor.withValues(alpha: .5), - shape: StadiumBorder( - side: BorderSide(color: context.colorScheme.secondaryContainer), - ), + shape: StadiumBorder(side: BorderSide(color: context.colorScheme.secondaryContainer)), child: Padding( padding: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 14.0), - child: Row( - children: [ - Icon( - icon, - size: 18, - ), - const SizedBox(width: 4.0), - currentFilter!, - ], - ), + child: Row(children: [Icon(icon, size: 18), const SizedBox(width: 4.0), currentFilter!]), ), ), ); @@ -46,21 +29,10 @@ class SearchFilterChip extends StatelessWidget { onTap: onTap, child: Card( elevation: 0, - shape: StadiumBorder( - side: BorderSide(color: context.colorScheme.outline.withAlpha(15)), - ), + shape: StadiumBorder(side: BorderSide(color: context.colorScheme.outline.withAlpha(15))), child: Padding( padding: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 14.0), - child: Row( - children: [ - Icon( - icon, - size: 18, - ), - const SizedBox(width: 4.0), - Text(label), - ], - ), + child: Row(children: [Icon(icon, size: 18), const SizedBox(width: 4.0), Text(label)]), ), ), ); diff --git a/mobile/lib/widgets/search/search_map_thumbnail.dart b/mobile/lib/widgets/search/search_map_thumbnail.dart index 78af8f936b..7533e46f1a 100644 --- a/mobile/lib/widgets/search/search_map_thumbnail.dart +++ b/mobile/lib/widgets/search/search_map_thumbnail.dart @@ -7,10 +7,7 @@ import 'package:immich_mobile/widgets/search/thumbnail_with_info_container.dart' import 'package:maplibre_gl/maplibre_gl.dart'; class SearchMapThumbnail extends StatelessWidget { - const SearchMapThumbnail({ - super.key, - this.size = 60.0, - }); + const SearchMapThumbnail({super.key, this.size = 60.0}); final double size; final bool showTitle = true; @@ -23,16 +20,7 @@ class SearchMapThumbnail extends StatelessWidget { context.pushRoute(MapRoute()); }, child: IgnorePointer( - child: MapThumbnail( - zoom: 2, - centre: const LatLng( - 47, - 5, - ), - height: size, - width: size, - showAttribution: false, - ), + child: MapThumbnail(zoom: 2, centre: const LatLng(47, 5), height: size, width: size, showAttribution: false), ), ); } diff --git a/mobile/lib/widgets/search/search_row_section.dart b/mobile/lib/widgets/search/search_row_section.dart index 352c7f6a40..b8584fefef 100644 --- a/mobile/lib/widgets/search/search_row_section.dart +++ b/mobile/lib/widgets/search/search_row_section.dart @@ -25,10 +25,7 @@ class SearchRowSection extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 16), - child: SearchRowTitle( - onViewAllPressed: onViewAllPressed, - title: title, - ), + child: SearchRowTitle(onViewAllPressed: onViewAllPressed, title: title), ), child, ], diff --git a/mobile/lib/widgets/search/search_row_title.dart b/mobile/lib/widgets/search/search_row_title.dart index 4fa0d1f854..dc0a4ba6cb 100644 --- a/mobile/lib/widgets/search/search_row_title.dart +++ b/mobile/lib/widgets/search/search_row_title.dart @@ -3,11 +3,7 @@ import 'package:flutter/material.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; class SearchRowTitle extends StatelessWidget { - const SearchRowTitle({ - super.key, - required this.onViewAllPressed, - required this.title, - }); + const SearchRowTitle({super.key, required this.onViewAllPressed, required this.title}); final Function() onViewAllPressed; final String title; @@ -17,19 +13,12 @@ class SearchRowTitle extends StatelessWidget { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - title, - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + Text(title, style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500)), TextButton( onPressed: onViewAllPressed, child: Text( 'search_page_view_all_button', - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), ).tr(), ), ], diff --git a/mobile/lib/widgets/search/thumbnail_with_info.dart b/mobile/lib/widgets/search/thumbnail_with_info.dart index 23bdbc915b..af9460f929 100644 --- a/mobile/lib/widgets/search/thumbnail_with_info.dart +++ b/mobile/lib/widgets/search/thumbnail_with_info.dart @@ -39,12 +39,7 @@ class ThumbnailWithInfo extends StatelessWidget { errorWidget: (context, url, error) => const Icon(Icons.image_not_supported_outlined), ), ) - : Center( - child: Icon( - noImageIcon ?? Icons.not_listed_location, - color: textAndIconColor, - ), - ), + : Center(child: Icon(noImageIcon ?? Icons.not_listed_location, color: textAndIconColor)), ); } } diff --git a/mobile/lib/widgets/search/thumbnail_with_info_container.dart b/mobile/lib/widgets/search/thumbnail_with_info_container.dart index e29d9e780c..e4b8f69f83 100644 --- a/mobile/lib/widgets/search/thumbnail_with_info_container.dart +++ b/mobile/lib/widgets/search/thumbnail_with_info_container.dart @@ -27,10 +27,7 @@ class ThumbnailWithInfoContainer extends StatelessWidget { decoration: BoxDecoration( borderRadius: BorderRadius.circular(borderRadius), gradient: LinearGradient( - colors: [ - context.colorScheme.surfaceContainer, - context.colorScheme.surfaceContainer.darken(amount: .1), - ], + colors: [context.colorScheme.surfaceContainer, context.colorScheme.surfaceContainer.darken(amount: .1)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), @@ -54,11 +51,7 @@ class ThumbnailWithInfoContainer extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 8) + const EdgeInsets.only(bottom: 8), child: Text( label, - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 14, - ), + style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 14), maxLines: 2, softWrap: false, overflow: TextOverflow.ellipsis, diff --git a/mobile/lib/widgets/settings/advanced_settings.dart b/mobile/lib/widgets/settings/advanced_settings.dart index 3f569863de..3f196b840b 100644 --- a/mobile/lib/widgets/settings/advanced_settings.dart +++ b/mobile/lib/widgets/settings/advanced_settings.dart @@ -34,10 +34,7 @@ class AdvancedSettings extends HookConsumerWidget { final logLevel = Level.LEVELS[levelId.value].name; - useValueChanged( - levelId.value, - (_, __) => LogService.I.setLogLevel(Level.LEVELS[levelId.value].toLogLevel()), - ); + useValueChanged(levelId.value, (_, __) => LogService.I.setLogLevel(Level.LEVELS[levelId.value].toLogLevel())); Future checkAndroidVersion() async { if (Platform.isAndroid) { diff --git a/mobile/lib/widgets/settings/asset_list_settings/asset_list_group_settings.dart b/mobile/lib/widgets/settings/asset_list_settings/asset_list_group_settings.dart index df974fff30..04786bf916 100644 --- a/mobile/lib/widgets/settings/asset_list_settings/asset_list_group_settings.dart +++ b/mobile/lib/widgets/settings/asset_list_settings/asset_list_group_settings.dart @@ -11,9 +11,7 @@ import 'package:immich_mobile/widgets/settings/settings_radio_list_tile.dart'; import 'package:immich_mobile/widgets/settings/settings_sub_title.dart'; class GroupSettings extends HookConsumerWidget { - const GroupSettings({ - super.key, - }); + const GroupSettings({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -21,10 +19,7 @@ class GroupSettings extends HookConsumerWidget { final groupBy = GroupAssetsBy.values[groupByIndex.value]; Future updateAppSettings(GroupAssetsBy groupBy) async { - await ref.watch(appSettingsServiceProvider).setSetting( - AppSettingsEnum.groupAssetsBy, - groupBy.index, - ); + await ref.watch(appSettingsServiceProvider).setSetting(AppSettingsEnum.groupAssetsBy, groupBy.index); ref.invalidate(appSettingsServiceProvider); } @@ -41,18 +36,9 @@ class GroupSettings extends HookConsumerWidget { SettingsSubTitle(title: "asset_list_group_by_sub_title".tr()), SettingsRadioListTile( groups: [ - SettingsRadioGroup( - title: 'asset_list_layout_settings_group_by_month_day'.tr(), - value: GroupAssetsBy.day, - ), - SettingsRadioGroup( - title: 'month'.tr(), - value: GroupAssetsBy.month, - ), - SettingsRadioGroup( - title: 'asset_list_layout_settings_group_automatically'.tr(), - value: GroupAssetsBy.auto, - ), + SettingsRadioGroup(title: 'asset_list_layout_settings_group_by_month_day'.tr(), value: GroupAssetsBy.day), + SettingsRadioGroup(title: 'month'.tr(), value: GroupAssetsBy.month), + SettingsRadioGroup(title: 'asset_list_layout_settings_group_automatically'.tr(), value: GroupAssetsBy.auto), ], groupBy: groupBy, onRadioChanged: changeGroupValue, diff --git a/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart b/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart index de8ae5c2b2..bcb4a5ec9c 100644 --- a/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart +++ b/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart @@ -9,9 +9,7 @@ import 'package:immich_mobile/widgets/settings/settings_sub_title.dart'; import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; class LayoutSettings extends HookConsumerWidget { - const LayoutSettings({ - super.key, - }); + const LayoutSettings({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { diff --git a/mobile/lib/widgets/settings/asset_list_settings/asset_list_settings.dart b/mobile/lib/widgets/settings/asset_list_settings/asset_list_settings.dart index 550e3b5165..9f0ed0aa87 100644 --- a/mobile/lib/widgets/settings/asset_list_settings/asset_list_settings.dart +++ b/mobile/lib/widgets/settings/asset_list_settings/asset_list_settings.dart @@ -10,9 +10,7 @@ import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; import 'asset_list_layout_settings.dart'; class AssetListSettings extends HookConsumerWidget { - const AssetListSettings({ - super.key, - }); + const AssetListSettings({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -28,9 +26,6 @@ class AssetListSettings extends HookConsumerWidget { const GroupSettings(), ]; - return SettingsSubPageScaffold( - settings: assetListSetting, - showDivider: true, - ); + return SettingsSubPageScaffold(settings: assetListSetting, showDivider: true); } } diff --git a/mobile/lib/widgets/settings/asset_viewer_settings/asset_viewer_settings.dart b/mobile/lib/widgets/settings/asset_viewer_settings/asset_viewer_settings.dart index 23dca85b6f..5dea38d85e 100644 --- a/mobile/lib/widgets/settings/asset_viewer_settings/asset_viewer_settings.dart +++ b/mobile/lib/widgets/settings/asset_viewer_settings/asset_viewer_settings.dart @@ -4,20 +4,12 @@ import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart'; import 'video_viewer_settings.dart'; class AssetViewerSettings extends StatelessWidget { - const AssetViewerSettings({ - super.key, - }); + const AssetViewerSettings({super.key}); @override Widget build(BuildContext context) { - final assetViewerSetting = [ - const ImageViewerQualitySetting(), - const VideoViewerSettings(), - ]; + final assetViewerSetting = [const ImageViewerQualitySetting(), const VideoViewerSettings()]; - return SettingsSubPageScaffold( - settings: assetViewerSetting, - showDivider: true, - ); + return SettingsSubPageScaffold(settings: assetViewerSetting, showDivider: true); } } diff --git a/mobile/lib/widgets/settings/asset_viewer_settings/image_viewer_quality_setting.dart b/mobile/lib/widgets/settings/asset_viewer_settings/image_viewer_quality_setting.dart index 444977d9a2..aed88b90b0 100644 --- a/mobile/lib/widgets/settings/asset_viewer_settings/image_viewer_quality_setting.dart +++ b/mobile/lib/widgets/settings/asset_viewer_settings/image_viewer_quality_setting.dart @@ -9,9 +9,7 @@ import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; class ImageViewerQualitySetting extends HookConsumerWidget { - const ImageViewerQualitySetting({ - super.key, - }); + const ImageViewerQualitySetting({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -24,10 +22,7 @@ class ImageViewerQualitySetting extends HookConsumerWidget { SettingsSubTitle(title: "setting_image_viewer_title".tr()), ListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 20), - title: Text( - 'setting_image_viewer_help', - style: context.textTheme.bodyMedium, - ).tr(), + title: Text('setting_image_viewer_help', style: context.textTheme.bodyMedium).tr(), ), SettingsSwitchListTile( valueNotifier: isPreview, diff --git a/mobile/lib/widgets/settings/asset_viewer_settings/video_viewer_settings.dart b/mobile/lib/widgets/settings/asset_viewer_settings/video_viewer_settings.dart index 66f7e96943..1d8d9812be 100644 --- a/mobile/lib/widgets/settings/asset_viewer_settings/video_viewer_settings.dart +++ b/mobile/lib/widgets/settings/asset_viewer_settings/video_viewer_settings.dart @@ -8,9 +8,7 @@ import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; class VideoViewerSettings extends HookConsumerWidget { - const VideoViewerSettings({ - super.key, - }); + const VideoViewerSettings({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { diff --git a/mobile/lib/widgets/settings/backup_settings/background_settings.dart b/mobile/lib/widgets/settings/backup_settings/background_settings.dart index 5207969045..038a567dc2 100644 --- a/mobile/lib/widgets/settings/backup_settings/background_settings.dart +++ b/mobile/lib/widgets/settings/backup_settings/background_settings.dart @@ -24,12 +24,7 @@ class BackgroundBackupSettings extends ConsumerWidget { void showErrorToUser(String msg) { final snackBar = SnackBar( - content: Text( - msg.tr(), - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), - ), + content: Text(msg.tr(), style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor)), backgroundColor: Colors.red, ); context.scaffoldMessenger.showSnackBar(snackBar); @@ -41,20 +36,14 @@ class BackgroundBackupSettings extends ConsumerWidget { barrierDismissible: false, builder: (BuildContext ctx) { return AlertDialog( - title: const Text( - 'backup_controller_page_background_battery_info_title', - ).tr(), + title: const Text('backup_controller_page_background_battery_info_title').tr(), content: SingleChildScrollView( - child: const Text( - 'backup_controller_page_background_battery_info_message', - ).tr(), + child: const Text('backup_controller_page_background_battery_info_message').tr(), ), actions: [ ElevatedButton( - onPressed: () => launchUrl( - Uri.parse('https://dontkillmyapp.com'), - mode: LaunchMode.externalApplication, - ), + onPressed: () => + launchUrl(Uri.parse('https://dontkillmyapp.com'), mode: LaunchMode.externalApplication), child: const Text( "backup_controller_page_background_battery_info_link", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12), @@ -79,7 +68,9 @@ class BackgroundBackupSettings extends ConsumerWidget { title: 'backup_controller_page_background_is_off'.tr(), subtileText: 'backup_controller_page_background_description'.tr(), buttonText: 'backup_controller_page_background_turn_on'.tr(), - onButtonTap: () => ref.read(backupProvider.notifier).configureBackgroundBackup( + onButtonTap: () => ref + .read(backupProvider.notifier) + .configureBackgroundBackup( enabled: true, onError: showErrorToUser, onBatteryInfo: showBatteryOptimizationInfoToUser, @@ -90,10 +81,7 @@ class BackgroundBackupSettings extends ConsumerWidget { return Column( children: [ if (!Platform.isIOS || iosSettings?.appRefreshEnabled == true) - _BackgroundSettingsEnabled( - onError: showErrorToUser, - onBatteryInfo: showBatteryOptimizationInfoToUser, - ), + _BackgroundSettingsEnabled(onError: showErrorToUser, onBatteryInfo: showBatteryOptimizationInfoToUser), if (Platform.isIOS && iosSettings?.appRefreshEnabled != true) const _IOSBackgroundRefreshDisabled(), if (Platform.isIOS && iosSettings != null) IosDebugInfoTile(settings: iosSettings), ], @@ -120,10 +108,7 @@ class _BackgroundSettingsEnabled extends HookConsumerWidget { final void Function(String msg) onError; final void Function() onBatteryInfo; - const _BackgroundSettingsEnabled({ - required this.onError, - required this.onBatteryInfo, - }); + const _BackgroundSettingsEnabled({required this.onError, required this.onBatteryInfo}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -131,41 +116,45 @@ class _BackgroundSettingsEnabled extends HookConsumerWidget { final isWifiRequiredNotifier = useValueNotifier(isWifiRequired); useValueChanged( isWifiRequired, - (_, __) => WidgetsBinding.instance.addPostFrameCallback( - (_) => isWifiRequiredNotifier.value = isWifiRequired, - ), + (_, __) => WidgetsBinding.instance.addPostFrameCallback((_) => isWifiRequiredNotifier.value = isWifiRequired), ); final isChargingRequired = ref.watch(backupProvider.select((s) => s.backupRequireCharging)); final isChargingRequiredNotifier = useValueNotifier(isChargingRequired); useValueChanged( isChargingRequired, - (_, __) => WidgetsBinding.instance.addPostFrameCallback( - (_) => isChargingRequiredNotifier.value = isChargingRequired, - ), + (_, __) => + WidgetsBinding.instance.addPostFrameCallback((_) => isChargingRequiredNotifier.value = isChargingRequired), ); int backupDelayToSliderValue(int ms) => switch (ms) { - 5000 => 0, - 30000 => 1, - 120000 => 2, - _ => 3, - }; + 5000 => 0, + 30000 => 1, + 120000 => 2, + _ => 3, + }; - int backupDelayToMilliseconds(int v) => switch (v) { 0 => 5000, 1 => 30000, 2 => 120000, _ => 600000 }; + int backupDelayToMilliseconds(int v) => switch (v) { + 0 => 5000, + 1 => 30000, + 2 => 120000, + _ => 600000, + }; String formatBackupDelaySliderValue(int v) => switch (v) { - 0 => 'setting_notifications_notify_seconds'.tr(namedArgs: {'count': '5'}), - 1 => 'setting_notifications_notify_seconds'.tr(namedArgs: {'count': '30'}), - 2 => 'setting_notifications_notify_minutes'.tr(namedArgs: {'count': '2'}), - _ => 'setting_notifications_notify_minutes'.tr(namedArgs: {'count': '10'}), - }; + 0 => 'setting_notifications_notify_seconds'.tr(namedArgs: {'count': '5'}), + 1 => 'setting_notifications_notify_seconds'.tr(namedArgs: {'count': '30'}), + 2 => 'setting_notifications_notify_minutes'.tr(namedArgs: {'count': '2'}), + _ => 'setting_notifications_notify_minutes'.tr(namedArgs: {'count': '10'}), + }; final backupTriggerDelay = ref.watch(backupProvider.select((s) => s.backupTriggerDelay)); final triggerDelay = useState(backupDelayToSliderValue(backupTriggerDelay)); useValueChanged( triggerDelay.value, - (_, __) => ref.read(backupProvider.notifier).configureBackgroundBackup( + (_, __) => ref + .read(backupProvider.notifier) + .configureBackgroundBackup( triggerDelay: backupDelayToMilliseconds(triggerDelay.value), onError: onError, onBatteryInfo: onBatteryInfo, @@ -177,40 +166,32 @@ class _BackgroundSettingsEnabled extends HookConsumerWidget { iconColor: context.primaryColor, title: 'backup_controller_page_background_is_on'.tr(), buttonText: 'backup_controller_page_background_turn_off'.tr(), - onButtonTap: () => ref.read(backupProvider.notifier).configureBackgroundBackup( - enabled: false, - onError: onError, - onBatteryInfo: onBatteryInfo, - ), + onButtonTap: () => ref + .read(backupProvider.notifier) + .configureBackgroundBackup(enabled: false, onError: onError, onBatteryInfo: onBatteryInfo), subtitle: Column( children: [ SettingsSwitchListTile( valueNotifier: isWifiRequiredNotifier, title: 'backup_controller_page_background_wifi'.tr(), icon: Icons.wifi, - onChanged: (enabled) => ref.read(backupProvider.notifier).configureBackgroundBackup( - requireWifi: enabled, - onError: onError, - onBatteryInfo: onBatteryInfo, - ), + onChanged: (enabled) => ref + .read(backupProvider.notifier) + .configureBackgroundBackup(requireWifi: enabled, onError: onError, onBatteryInfo: onBatteryInfo), ), SettingsSwitchListTile( valueNotifier: isChargingRequiredNotifier, title: 'backup_controller_page_background_charging'.tr(), icon: Icons.charging_station, - onChanged: (enabled) => ref.read(backupProvider.notifier).configureBackgroundBackup( - requireCharging: enabled, - onError: onError, - onBatteryInfo: onBatteryInfo, - ), + onChanged: (enabled) => ref + .read(backupProvider.notifier) + .configureBackgroundBackup(requireCharging: enabled, onError: onError, onBatteryInfo: onBatteryInfo), ), if (Platform.isAndroid) SettingsSliderListTile( valueNotifier: triggerDelay, text: 'backup_controller_page_background_delay'.tr( - namedArgs: { - 'duration': formatBackupDelaySliderValue(triggerDelay.value), - }, + namedArgs: {'duration': formatBackupDelaySliderValue(triggerDelay.value)}, ), maxValue: 3.0, noDivisons: 3, diff --git a/mobile/lib/widgets/settings/backup_settings/backup_settings.dart b/mobile/lib/widgets/settings/backup_settings/backup_settings.dart index 59ea6f2105..50aa57da9f 100644 --- a/mobile/lib/widgets/settings/backup_settings/backup_settings.dart +++ b/mobile/lib/widgets/settings/backup_settings/backup_settings.dart @@ -15,9 +15,7 @@ import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; class BackupSettings extends HookConsumerWidget { - const BackupSettings({ - super.key, - }); + const BackupSettings({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -71,22 +69,14 @@ class BackupSettings extends HookConsumerWidget { SettingsButtonListTile( icon: Icons.photo_album_outlined, title: 'sync_albums'.tr(), - subtitle: Text( - "sync_albums_manual_subtitle".tr(), - ), + subtitle: Text("sync_albums_manual_subtitle".tr()), buttonText: 'sync_albums'.tr(), child: isAlbumSyncInProgress.value ? const CircularProgressIndicator() - : ElevatedButton( - onPressed: syncAlbums, - child: Text('sync'.tr()), - ), + : ElevatedButton(onPressed: syncAlbums, child: Text('sync'.tr())), ), ]; - return SettingsSubPageScaffold( - settings: backupSettings, - showDivider: true, - ); + return SettingsSubPageScaffold(settings: backupSettings, showDivider: true); } } diff --git a/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart b/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart index 85da49357b..12a3c51e8e 100644 --- a/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart +++ b/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart @@ -17,9 +17,7 @@ import 'package:path_provider/path_provider.dart'; import 'package:share_plus/share_plus.dart'; class BetaSyncSettings extends HookConsumerWidget { - const BetaSyncSettings({ - super.key, - }); + const BetaSyncSettings({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -35,17 +33,11 @@ class BetaSyncSettings extends HookConsumerWidget { final memoryCount = memoryService.getCount(); final getLocalHashedCount = assetService.getLocalHashedCount(); - return await Future.wait([ - assetCounts, - localAlbumCounts, - remoteAlbumCounts, - memoryCount, - getLocalHashedCount, - ]); + return await Future.wait([assetCounts, localAlbumCounts, remoteAlbumCounts, memoryCount, getLocalHashedCount]); } Future resetDatabase() async { -// https://github.com/simolus3/drift/commit/bd80a46264b6dd833ef4fd87fffc03f5a832ab41#diff-3f879e03b4a35779344ef16170b9353608dd9c42385f5402ec6035aac4dd8a04R76-R94 + // https://github.com/simolus3/drift/commit/bd80a46264b6dd833ef4fd87fffc03f5a832ab41#diff-3f879e03b4a35779344ef16170b9353608dd9c42385f5402ec6035aac4dd8a04R76-R94 final drift = ref.read(driftProvider); final database = drift.attachedDatabase; await database.exclusively(() async { @@ -62,14 +54,10 @@ class BetaSyncSettings extends HookConsumerWidget { database.resolvedEngine.executor, drift_db.OpeningDetails(null, database.schemaVersion), ); - await database.customStatement( - 'PRAGMA user_version = ${database.schemaVersion}', - ); + await database.customStatement('PRAGMA user_version = ${database.schemaVersion}'); // Refresh all stream queries - database.notifyUpdates({ - for (final table in database.allTables) drift_db.TableUpdate.onTable(table), - }); + database.notifyUpdates({for (final table in database.allTables) drift_db.TableUpdate.onTable(table)}); }); } @@ -83,28 +71,18 @@ class BetaSyncSettings extends HookConsumerWidget { if (!await dbFile.exists()) { if (context.mounted) { context.scaffoldMessenger.showSnackBar( - SnackBar( - content: Text("Database file not found".t(context: context)), - ), + SnackBar(content: Text("Database file not found".t(context: context))), ); } return; } final timestamp = DateTime.now().millisecondsSinceEpoch; - final exportFile = File( - path.join( - documentsDir.path, - 'immich_export_$timestamp.sqlite', - ), - ); + final exportFile = File(path.join(documentsDir.path, 'immich_export_$timestamp.sqlite')); await dbFile.copy(exportFile.path); - await Share.shareXFiles( - [XFile(exportFile.path)], - text: 'Immich Database Export', - ); + await Share.shareXFiles([XFile(exportFile.path)], text: 'Immich Database Export'); Future.delayed(const Duration(seconds: 30), () async { if (await exportFile.exists()) { @@ -114,17 +92,13 @@ class BetaSyncSettings extends HookConsumerWidget { if (context.mounted) { context.scaffoldMessenger.showSnackBar( - SnackBar( - content: Text("Database exported successfully".t(context: context)), - ), + SnackBar(content: Text("Database exported successfully".t(context: context))), ); } } catch (e) { if (context.mounted) { context.scaffoldMessenger.showSnackBar( - SnackBar( - content: Text("Failed to export database: $e".t(context: context)), - ), + SnackBar(content: Text("Failed to export database: $e".t(context: context))), ); } } @@ -225,27 +199,17 @@ class BetaSyncSettings extends HookConsumerWidget { ], ), ), - const Divider( - height: 1, - indent: 16, - endIndent: 16, - ), + const Divider(height: 1, indent: 16, endIndent: 16), const SizedBox(height: 24), _SectionHeaderText(text: "jobs".t(context: context)), ListTile( title: Text( "sync_local".t(context: context), - style: const TextStyle( - fontWeight: FontWeight.w500, - ), - ), - subtitle: Text( - "tap_to_run_job".t(context: context), + style: const TextStyle(fontWeight: FontWeight.w500), ), + subtitle: Text("tap_to_run_job".t(context: context)), leading: const Icon(Icons.sync), - trailing: _SyncStatusIcon( - status: ref.watch(syncStatusProvider).localSyncStatus, - ), + trailing: _SyncStatusIcon(status: ref.watch(syncStatusProvider).localSyncStatus), onTap: () { ref.read(backgroundSyncProvider).syncLocal(full: true); }, @@ -253,17 +217,11 @@ class BetaSyncSettings extends HookConsumerWidget { ListTile( title: Text( "sync_remote".t(context: context), - style: const TextStyle( - fontWeight: FontWeight.w500, - ), - ), - subtitle: Text( - "tap_to_run_job".t(context: context), + style: const TextStyle(fontWeight: FontWeight.w500), ), + subtitle: Text("tap_to_run_job".t(context: context)), leading: const Icon(Icons.cloud_sync), - trailing: _SyncStatusIcon( - status: ref.watch(syncStatusProvider).remoteSyncStatus, - ), + trailing: _SyncStatusIcon(status: ref.watch(syncStatusProvider).remoteSyncStatus), onTap: () { ref.read(backgroundSyncProvider).syncRemote(); }, @@ -271,64 +229,40 @@ class BetaSyncSettings extends HookConsumerWidget { ListTile( title: Text( "hash_asset".t(context: context), - style: const TextStyle( - fontWeight: FontWeight.w500, - ), + style: const TextStyle(fontWeight: FontWeight.w500), ), leading: const Icon(Icons.tag), - subtitle: Text( - "tap_to_run_job".t(context: context), - ), - trailing: _SyncStatusIcon( - status: ref.watch(syncStatusProvider).hashJobStatus, - ), + subtitle: Text("tap_to_run_job".t(context: context)), + trailing: _SyncStatusIcon(status: ref.watch(syncStatusProvider).hashJobStatus), onTap: () { ref.read(backgroundSyncProvider).hashAssets(); }, ), - const Divider( - height: 1, - indent: 16, - endIndent: 16, - ), + const Divider(height: 1, indent: 16, endIndent: 16), const SizedBox(height: 24), _SectionHeaderText(text: "actions".t(context: context)), ListTile( title: Text( "export_database".t(context: context), - style: const TextStyle( - fontWeight: FontWeight.w500, - ), - ), - subtitle: Text( - "export_database_description".t(context: context), + style: const TextStyle(fontWeight: FontWeight.w500), ), + subtitle: Text("export_database_description".t(context: context)), leading: const Icon(Icons.download), onTap: exportDatabase, ), ListTile( title: Text( "reset_sqlite".t(context: context), - style: TextStyle( - color: context.colorScheme.error, - fontWeight: FontWeight.w500, - ), - ), - leading: Icon( - Icons.settings_backup_restore_rounded, - color: context.colorScheme.error, + style: TextStyle(color: context.colorScheme.error, fontWeight: FontWeight.w500), ), + leading: Icon(Icons.settings_backup_restore_rounded, color: context.colorScheme.error), onTap: () async { showDialog( context: context, builder: (context) { return AlertDialog( - title: Text( - "reset_sqlite".t(context: context), - ), - content: Text( - "reset_sqlite_confirmation".t(context: context), - ), + title: Text("reset_sqlite".t(context: context)), + content: Text("reset_sqlite_confirmation".t(context: context)), actions: [ TextButton( onPressed: () => context.pop(), @@ -339,18 +273,12 @@ class BetaSyncSettings extends HookConsumerWidget { await resetDatabase(); context.pop(); context.scaffoldMessenger.showSnackBar( - SnackBar( - content: Text( - "reset_sqlite_success".t(context: context), - ), - ), + SnackBar(content: Text("reset_sqlite_success".t(context: context))), ); }, child: Text( "confirm".t(context: context), - style: TextStyle( - color: context.colorScheme.error, - ), + style: TextStyle(color: context.colorScheme.error), ), ), ], @@ -370,31 +298,15 @@ class BetaSyncSettings extends HookConsumerWidget { class _SyncStatusIcon extends StatelessWidget { final SyncStatus status; - const _SyncStatusIcon({ - required this.status, - }); + const _SyncStatusIcon({required this.status}); @override Widget build(BuildContext context) { return switch (status) { - SyncStatus.idle => const Icon( - Icons.pause_circle_outline_rounded, - ), - SyncStatus.syncing => const SizedBox( - height: 24, - width: 24, - child: CircularProgressIndicator( - strokeWidth: 2, - ), - ), - SyncStatus.success => const Icon( - Icons.check_circle_outline, - color: Colors.green, - ), - SyncStatus.error => Icon( - Icons.error_outline, - color: context.colorScheme.error, - ), + SyncStatus.idle => const Icon(Icons.pause_circle_outline_rounded), + SyncStatus.syncing => const SizedBox(height: 24, width: 24, child: CircularProgressIndicator(strokeWidth: 2)), + SyncStatus.success => const Icon(Icons.check_circle_outline, color: Colors.green), + SyncStatus.error => Icon(Icons.error_outline, color: context.colorScheme.error), }; } } @@ -402,9 +314,7 @@ class _SyncStatusIcon extends StatelessWidget { class _SectionHeaderText extends StatelessWidget { final String text; - const _SectionHeaderText({ - required this.text, - }); + const _SectionHeaderText({required this.text}); @override Widget build(BuildContext context) { diff --git a/mobile/lib/widgets/settings/beta_sync_settings/entity_count_tile.dart b/mobile/lib/widgets/settings/beta_sync_settings/entity_count_tile.dart index 2441e7d1ca..ac357c2dee 100644 --- a/mobile/lib/widgets/settings/beta_sync_settings/entity_count_tile.dart +++ b/mobile/lib/widgets/settings/beta_sync_settings/entity_count_tile.dart @@ -7,12 +7,7 @@ class EntitiyCountTile extends StatelessWidget { final String label; final IconData icon; - const EntitiyCountTile({ - super.key, - required this.count, - required this.label, - required this.icon, - }); + const EntitiyCountTile({super.key, required this.count, required this.label, required this.icon}); String zeroPadding(int number, int targetWidth) { final numStr = number.toString(); @@ -31,10 +26,7 @@ class EntitiyCountTile extends StatelessWidget { decoration: BoxDecoration( color: context.colorScheme.surfaceContainerLow, borderRadius: const BorderRadius.all(Radius.circular(16)), - border: Border.all( - width: 0.5, - color: context.colorScheme.outline.withAlpha(25), - ), + border: Border.all(width: 0.5, color: context.colorScheme.outline.withAlpha(25)), ), child: Column( mainAxisSize: MainAxisSize.min, @@ -44,18 +36,11 @@ class EntitiyCountTile extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.start, children: [ - Icon( - icon, - color: context.primaryColor, - ), + Icon(icon, color: context.primaryColor), const SizedBox(width: 8), Text( label, - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.bold, - fontSize: 16, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold, fontSize: 16), ), ], ), @@ -66,23 +51,15 @@ class EntitiyCountTile extends StatelessWidget { final maxDigits = calculateMaxDigits(constraints.maxWidth); return RichText( text: TextSpan( - style: const TextStyle( - fontSize: 18, - fontFamily: 'OverpassMono', - fontWeight: FontWeight.w600, - ), + style: const TextStyle(fontSize: 18, fontFamily: 'OverpassMono', fontWeight: FontWeight.w600), children: [ TextSpan( text: zeroPadding(count, maxDigits), - style: TextStyle( - color: context.colorScheme.onSurfaceSecondary.withAlpha(75), - ), + style: TextStyle(color: context.colorScheme.onSurfaceSecondary.withAlpha(75)), ), TextSpan( text: count.toString(), - style: TextStyle( - color: context.primaryColor, - ), + style: TextStyle(color: context.primaryColor), ), ], ), diff --git a/mobile/lib/widgets/settings/beta_timeline_list_tile.dart b/mobile/lib/widgets/settings/beta_timeline_list_tile.dart index f5f6d66898..3d41094b76 100644 --- a/mobile/lib/widgets/settings/beta_timeline_list_tile.dart +++ b/mobile/lib/widgets/settings/beta_timeline_list_tile.dart @@ -13,9 +13,7 @@ import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; class BetaTimelineListTile extends ConsumerStatefulWidget { - const BetaTimelineListTile({ - super.key, - }); + const BetaTimelineListTile({super.key}); @override ConsumerState createState() => _BetaTimelineListTileState(); @@ -30,31 +28,22 @@ class _BetaTimelineListTileState extends ConsumerState wit @override void initState() { super.initState(); - _animationController = AnimationController( - duration: const Duration(seconds: 3), - vsync: this, - ); + _animationController = AnimationController(duration: const Duration(seconds: 3), vsync: this); - _rotationAnimation = Tween(begin: 0, end: 2 * math.pi).animate( - CurvedAnimation( - parent: _animationController, - curve: Curves.linear, - ), - ); + _rotationAnimation = Tween( + begin: 0, + end: 2 * math.pi, + ).animate(CurvedAnimation(parent: _animationController, curve: Curves.linear)); - _pulseAnimation = Tween(begin: 1, end: 1.1).animate( - CurvedAnimation( - parent: _animationController, - curve: Curves.easeInOut, - ), - ); + _pulseAnimation = Tween( + begin: 1, + end: 1.1, + ).animate(CurvedAnimation(parent: _animationController, curve: Curves.easeInOut)); - _gradientAnimation = Tween(begin: 0, end: 1).animate( - CurvedAnimation( - parent: _animationController, - curve: Curves.easeInOut, - ), - ); + _gradientAnimation = Tween( + begin: 0, + end: 1, + ).animate(CurvedAnimation(parent: _animationController, curve: Curves.easeInOut)); _animationController.repeat(reverse: true); } @@ -85,12 +74,8 @@ class _BetaTimelineListTileState extends ConsumerState wit return AlertDialog( title: value ? const Text("Enable Beta Timeline") : const Text("Disable Beta Timeline"), content: value - ? const Text( - "Are you sure you want to enable the beta timeline?", - ) - : const Text( - "Are you sure you want to disable the beta timeline?", - ), + ? const Text("Are you sure you want to enable the beta timeline?") + : const Text("Are you sure you want to disable the beta timeline?"), actions: [ TextButton( onPressed: () { @@ -98,27 +83,16 @@ class _BetaTimelineListTileState extends ConsumerState wit }, child: Text( "cancel".t(context: context), - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: context.colorScheme.outline, - ), + style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: context.colorScheme.outline), ), ), ElevatedButton( onPressed: () async { Navigator.of(context).pop(); - await ref.read(appSettingsServiceProvider).setSetting( - AppSettingsEnum.betaTimeline, - value, - ); - context.router.replaceAll( - [ChangeExperienceRoute(switchingToBeta: value)], - ); + await ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.betaTimeline, value); + context.router.replaceAll([ChangeExperienceRoute(switchingToBeta: value)]); }, - child: Text( - "ok".t(context: context), - ), + child: Text("ok".t(context: context)), ), ], ); @@ -156,11 +130,7 @@ class _BetaTimelineListTileState extends ConsumerState wit transform: GradientRotation(_rotationAnimation.value * 0.5), ), boxShadow: [ - BoxShadow( - color: context.primaryColor.withValues(alpha: 0.1), - blurRadius: 8, - offset: const Offset(0, 2), - ), + BoxShadow(color: context.primaryColor.withValues(alpha: 0.1), blurRadius: 8, offset: const Offset(0, 2)), ], ), child: Container( @@ -193,11 +163,7 @@ class _BetaTimelineListTileState extends ConsumerState wit ], ), ), - child: Icon( - Icons.auto_awesome, - color: context.primaryColor, - size: 20, - ), + child: Icon(Icons.auto_awesome, color: context.primaryColor, size: 20), ), ), ), @@ -211,20 +177,13 @@ class _BetaTimelineListTileState extends ConsumerState wit children: [ Text( "advanced_settings_beta_timeline_title".t(context: context), - style: context.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600), ), const SizedBox(width: 8), Container( - padding: const EdgeInsets.symmetric( - horizontal: 6, - vertical: 2, - ), + padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(8), - ), + borderRadius: const BorderRadius.all(Radius.circular(8)), gradient: LinearGradient( colors: [ context.primaryColor.withValues(alpha: 0.8), diff --git a/mobile/lib/widgets/settings/custom_proxy_headers_settings/custome_proxy_headers_settings.dart b/mobile/lib/widgets/settings/custom_proxy_headers_settings/custome_proxy_headers_settings.dart index 2e1f165602..f0e248b39d 100644 --- a/mobile/lib/widgets/settings/custom_proxy_headers_settings/custome_proxy_headers_settings.dart +++ b/mobile/lib/widgets/settings/custom_proxy_headers_settings/custome_proxy_headers_settings.dart @@ -15,15 +15,11 @@ class CustomeProxyHeaderSettings extends StatelessWidget { dense: true, title: Text( "headers_settings_tile_title".tr(), - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500), ), subtitle: Text( "headers_settings_tile_subtitle".tr(), - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), onTap: () => context.pushRoute(const HeaderSettingsRoute()), ); diff --git a/mobile/lib/widgets/settings/language_settings.dart b/mobile/lib/widgets/settings/language_settings.dart index 012c966b60..d0b3ac0021 100644 --- a/mobile/lib/widgets/settings/language_settings.dart +++ b/mobile/lib/widgets/settings/language_settings.dart @@ -48,9 +48,7 @@ class LanguageSettings extends HookConsumerWidget { filteredLocaleEntries.value = localeEntries; } else { filteredLocaleEntries.value = localeEntries - .where( - (entry) => entry.key.toLowerCase().contains(searchTerm.toLowerCase()), - ) + .where((entry) => entry.key.toLowerCase().contains(searchTerm.toLowerCase())) .toList(); } }); @@ -61,17 +59,14 @@ class LanguageSettings extends HookConsumerWidget { onSearch(''); } - useEffect( - () { - void searchListener() => onSearch(searchController.text); - searchController.addListener(searchListener); - return () { - searchController.removeListener(searchListener); - debounceTimer.value?.cancel(); - }; - }, - [searchController], - ); + useEffect(() { + void searchListener() => onSearch(searchController.text); + searchController.addListener(searchListener); + return () { + searchController.removeListener(searchListener); + debounceTimer.value?.cancel(); + }; + }, [searchController]); return SafeArea( child: Column( @@ -111,11 +106,7 @@ class LanguageSettings extends HookConsumerWidget { _LanguageApplyButton( isDisabled: isButtonDisabled, isLoading: isLoading.value, - onPressed: () => _applyLanguageChange( - context, - selectedLocale, - isLoading, - ), + onPressed: () => _applyLanguageChange(context, selectedLocale, isLoading), ), ], ), @@ -140,9 +131,7 @@ class _LanguageSearchBar extends StatelessWidget { Widget build(BuildContext context) { return Container( padding: const EdgeInsets.only(top: 16, bottom: 8, left: 50, right: 50), - decoration: BoxDecoration( - color: context.colorScheme.surface, - ), + decoration: BoxDecoration(color: context.colorScheme.surface), child: DecoratedBox( decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(24)), @@ -162,10 +151,7 @@ class _LanguageSearchBar extends StatelessWidget { hintText: 'language_search_hint'.t(context: context), prefixIcon: const Icon(Icons.search_rounded), suffixIcon: controller.text.isNotEmpty - ? IconButton( - icon: const Icon(Icons.clear_rounded), - onPressed: onClear, - ) + ? IconButton(icon: const Icon(Icons.clear_rounded), onPressed: onClear) : null, controller: controller, onChanged: onChanged, @@ -186,24 +172,16 @@ class _LanguageNotFound extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon( - Icons.search_off_rounded, - size: 64, - color: context.colorScheme.onSurface.withValues(alpha: 0.4), - ), + Icon(Icons.search_off_rounded, size: 64, color: context.colorScheme.onSurface.withValues(alpha: 0.4)), const SizedBox(height: 8), Text( 'language_no_results_title'.t(context: context), - style: context.textTheme.titleMedium?.copyWith( - color: context.colorScheme.onSurface, - ), + style: context.textTheme.titleMedium?.copyWith(color: context.colorScheme.onSurface), ), const SizedBox(height: 4), Text( 'language_no_results_subtitle'.t(context: context), - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurface.withValues(alpha: 0.8), - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurface.withValues(alpha: 0.8)), ), ], ), @@ -212,11 +190,7 @@ class _LanguageNotFound extends StatelessWidget { } class _LanguageApplyButton extends StatelessWidget { - const _LanguageApplyButton({ - required this.isDisabled, - required this.isLoading, - required this.onPressed, - }); + const _LanguageApplyButton({required this.isDisabled, required this.isLoading, required this.onPressed}); final bool isDisabled; final bool isLoading; @@ -225,9 +199,7 @@ class _LanguageApplyButton extends StatelessWidget { @override Widget build(BuildContext context) { return DecoratedBox( - decoration: BoxDecoration( - color: context.colorScheme.surface, - ), + decoration: BoxDecoration(color: context.colorScheme.surface), child: Padding( padding: const EdgeInsets.all(16.0), child: SizedBox( @@ -236,18 +208,10 @@ class _LanguageApplyButton extends StatelessWidget { child: ElevatedButton( onPressed: isDisabled ? null : onPressed, child: isLoading - ? const SizedBox.square( - dimension: 24, - child: CircularProgressIndicator( - strokeWidth: 2, - ), - ) + ? const SizedBox.square(dimension: 24, child: CircularProgressIndicator(strokeWidth: 2)) : Text( 'setting_languages_apply'.t(context: context), - style: const TextStyle( - fontWeight: FontWeight.w600, - fontSize: 16.0, - ), + style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 16.0), ), ), ), @@ -273,20 +237,12 @@ class _LanguageItem extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( - padding: const EdgeInsets.symmetric( - vertical: 4.0, - horizontal: 8.0, - ), + padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 8.0), child: DecoratedBox( decoration: BoxDecoration( color: context.colorScheme.surfaceContainerLowest.withValues(alpha: .6), - borderRadius: const BorderRadius.all( - Radius.circular(16.0), - ), - border: Border.all( - color: context.colorScheme.outlineVariant.withValues(alpha: .4), - width: 1.0, - ), + borderRadius: const BorderRadius.all(Radius.circular(16.0)), + border: Border.all(color: context.colorScheme.outlineVariant.withValues(alpha: .4), width: 1.0), ), child: ListTile( title: Text( @@ -296,22 +252,12 @@ class _LanguageItem extends StatelessWidget { color: isSelected ? context.colorScheme.primary : context.colorScheme.onSurfaceVariant, ), ), - trailing: isSelected - ? Icon( - Icons.check, - color: context.colorScheme.primary, - size: 20, - ) - : null, + trailing: isSelected ? Icon(Icons.check, color: context.colorScheme.primary, size: 20) : null, onTap: onTap, selected: isSelected, selectedTileColor: context.colorScheme.primary.withValues(alpha: .15), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(16.0)), - ), - contentPadding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16.0))), + contentPadding: const EdgeInsets.symmetric(horizontal: 16.0), ), ), ); diff --git a/mobile/lib/widgets/settings/local_storage_settings.dart b/mobile/lib/widgets/settings/local_storage_settings.dart index 06db78cb97..af9e4079bb 100644 --- a/mobile/lib/widgets/settings/local_storage_settings.dart +++ b/mobile/lib/widgets/settings/local_storage_settings.dart @@ -14,13 +14,10 @@ class LocalStorageSettings extends HookConsumerWidget { final isarDb = ref.watch(dbProvider); final cacheItemCount = useState(0); - useEffect( - () { - cacheItemCount.value = isarDb.duplicatedAssets.countSync(); - return null; - }, - [], - ); + useEffect(() { + cacheItemCount.value = isarDb.duplicatedAssets.countSync(); + return null; + }, []); void clearCache() async { await isarDb.writeTxn(() => isarDb.duplicatedAssets.clear()); @@ -32,15 +29,11 @@ class LocalStorageSettings extends HookConsumerWidget { dense: true, title: Text( "cache_settings_duplicated_assets_title", - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500), ).tr(namedArgs: {'count': "${cacheItemCount.value}"}), subtitle: Text( "cache_settings_duplicated_assets_subtitle", - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ).tr(), trailing: TextButton( onPressed: cacheItemCount.value > 0 ? clearCache : null, diff --git a/mobile/lib/widgets/settings/networking_settings/endpoint_input.dart b/mobile/lib/widgets/settings/networking_settings/endpoint_input.dart index ec0069f2d1..a712ce416c 100644 --- a/mobile/lib/widgets/settings/networking_settings/endpoint_input.dart +++ b/mobile/lib/widgets/settings/networking_settings/endpoint_input.dart @@ -97,10 +97,7 @@ class EndpointInputState extends ConsumerState { color: Colors.red, alignment: Alignment.centerRight, padding: const EdgeInsets.only(right: 16), - child: const Icon( - Icons.delete, - color: Colors.white, - ), + child: const Icon(Icons.delete, color: Colors.white), ), child: ListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 24), @@ -120,27 +117,19 @@ class EndpointInputState extends ConsumerState { autovalidateMode: AutovalidateMode.onUserInteraction, validator: validateUrl, keyboardType: TextInputType.url, - style: const TextStyle( - fontFamily: 'Inconsolata', - fontWeight: FontWeight.w600, - fontSize: 14, - ), + style: const TextStyle(fontFamily: 'Inconsolata', fontWeight: FontWeight.w600, fontSize: 14), decoration: InputDecoration( hintText: 'http(s)://immich.domain.com', contentPadding: const EdgeInsets.all(16), filled: true, fillColor: context.colorScheme.surfaceContainer, - border: const OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(16)), - ), + border: const OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(16))), errorBorder: OutlineInputBorder( borderSide: BorderSide(color: Colors.red[300]!), borderRadius: const BorderRadius.all(Radius.circular(16)), ), disabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: context.isDarkTheme ? Colors.grey[900]! : Colors.grey[300]!, - ), + borderSide: BorderSide(color: context.isDarkTheme ? Colors.grey[900]! : Colors.grey[300]!), borderRadius: const BorderRadius.all(Radius.circular(16)), ), ), diff --git a/mobile/lib/widgets/settings/networking_settings/external_network_preference.dart b/mobile/lib/widgets/settings/networking_settings/external_network_preference.dart index d0c212adf5..8cc6079961 100644 --- a/mobile/lib/widgets/settings/networking_settings/external_network_preference.dart +++ b/mobile/lib/widgets/settings/networking_settings/external_network_preference.dart @@ -17,9 +17,7 @@ class ExternalNetworkPreference extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final entries = useState( - [const AuxilaryEndpoint(url: '', status: AuxCheckStatus.unknown)], - ); + final entries = useState([const AuxilaryEndpoint(url: '', status: AuxCheckStatus.unknown)]); final canSave = useState(false); saveEndpointList() { @@ -29,10 +27,7 @@ class ExternalNetworkPreference extends HookConsumerWidget { final jsonString = jsonEncode(endpointList); - Store.put( - StoreKey.externalEndpointList, - jsonString, - ); + Store.put(StoreKey.externalEndpointList, jsonString); } updateValidationStatus(String url, int index, AuxCheckStatus status) { @@ -59,11 +54,7 @@ class ExternalNetworkPreference extends HookConsumerWidget { saveEndpointList(); } - Widget proxyDecorator( - Widget child, - int index, - Animation animation, - ) { + Widget proxyDecorator(Widget child, int index, Animation animation) { return AnimatedBuilder( animation: animation, builder: (BuildContext context, Widget? child) { @@ -77,20 +68,17 @@ class ExternalNetworkPreference extends HookConsumerWidget { ); } - useEffect( - () { - final jsonString = Store.tryGet(StoreKey.externalEndpointList); + useEffect(() { + final jsonString = Store.tryGet(StoreKey.externalEndpointList); - if (jsonString == null) { - return null; - } - - final List jsonList = jsonDecode(jsonString); - entries.value = jsonList.map((e) => AuxilaryEndpoint.fromJson(e)).toList(); + if (jsonString == null) { return null; - }, - const [], - ); + } + + final List jsonList = jsonDecode(jsonString); + entries.value = jsonList.map((e) => AuxilaryEndpoint.fromJson(e)).toList(); + return null; + }, const []); return Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), @@ -99,21 +87,14 @@ class ExternalNetworkPreference extends HookConsumerWidget { decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(16)), color: context.colorScheme.surfaceContainerLow, - border: Border.all( - color: context.colorScheme.surfaceContainerHighest, - width: 1, - ), + border: Border.all(color: context.colorScheme.surfaceContainerHighest, width: 1), ), child: Stack( children: [ Positioned( bottom: -36, right: -36, - child: Icon( - Icons.dns_rounded, - size: 120, - color: context.primaryColor.withValues(alpha: 0.05), - ), + child: Icon(Icons.dns_rounded, size: 120, color: context.primaryColor.withValues(alpha: 0.05)), ), ListView( padding: const EdgeInsets.symmetric(vertical: 16.0), @@ -121,14 +102,8 @@ class ExternalNetworkPreference extends HookConsumerWidget { shrinkWrap: true, children: [ Padding( - padding: const EdgeInsets.symmetric( - vertical: 4.0, - horizontal: 24, - ), - child: Text( - "external_network_sheet_info".tr(), - style: context.textTheme.bodyMedium, - ), + padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 24), + child: Text("external_network_sheet_info".tr(), style: context.textTheme.bodyMedium), ), const SizedBox(height: 4), Divider(color: context.colorScheme.surfaceContainerHighest), @@ -165,10 +140,7 @@ class ExternalNetworkPreference extends HookConsumerWidget { ? () { entries.value = [ ...entries.value, - const AuxilaryEndpoint( - url: '', - status: AuxCheckStatus.unknown, - ), + const AuxilaryEndpoint(url: '', status: AuxCheckStatus.unknown), ]; } : null, diff --git a/mobile/lib/widgets/settings/networking_settings/local_network_preference.dart b/mobile/lib/widgets/settings/networking_settings/local_network_preference.dart index ac61019aaf..9fbc43a429 100644 --- a/mobile/lib/widgets/settings/networking_settings/local_network_preference.dart +++ b/mobile/lib/widgets/settings/networking_settings/local_network_preference.dart @@ -7,19 +7,11 @@ import 'package:immich_mobile/providers/auth.provider.dart'; import 'package:immich_mobile/providers/network.provider.dart'; class LocalNetworkPreference extends HookConsumerWidget { - const LocalNetworkPreference({ - super.key, - required this.enabled, - }); + const LocalNetworkPreference({super.key, required this.enabled}); final bool enabled; - Future _showEditDialog( - BuildContext context, - String title, - String hintText, - String initialValue, - ) { + Future _showEditDialog(BuildContext context, String title, String hintText, String initialValue) { final controller = TextEditingController(text: initialValue); return showDialog( @@ -29,23 +21,14 @@ class LocalNetworkPreference extends HookConsumerWidget { content: TextField( controller: controller, autofocus: true, - decoration: InputDecoration( - border: const OutlineInputBorder(), - hintText: hintText, - ), + decoration: InputDecoration(border: const OutlineInputBorder(), hintText: hintText), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), - child: Text( - 'cancel'.tr().toUpperCase(), - style: const TextStyle(color: Colors.red), - ), - ), - TextButton( - onPressed: () => Navigator.pop(context, controller.text), - child: Text('save'.tr().toUpperCase()), + child: Text('cancel'.tr().toUpperCase(), style: const TextStyle(color: Colors.red)), ), + TextButton(onPressed: () => Navigator.pop(context, controller.text), child: Text('save'.tr().toUpperCase())), ], ), ); @@ -56,23 +39,20 @@ class LocalNetworkPreference extends HookConsumerWidget { final wifiNameText = useState(""); final localEndpointText = useState(""); - useEffect( - () { - final wifiName = ref.read(authProvider.notifier).getSavedWifiName(); - final localEndpoint = ref.read(authProvider.notifier).getSavedLocalEndpoint(); + useEffect(() { + final wifiName = ref.read(authProvider.notifier).getSavedWifiName(); + final localEndpoint = ref.read(authProvider.notifier).getSavedLocalEndpoint(); - if (wifiName != null) { - wifiNameText.value = wifiName; - } + if (wifiName != null) { + wifiNameText.value = wifiName; + } - if (localEndpoint != null) { - localEndpointText.value = localEndpoint; - } + if (localEndpoint != null) { + localEndpointText.value = localEndpoint; + } - return null; - }, - [], - ); + return null; + }, []); saveWifiName(String wifiName) { wifiNameText.value = wifiName; @@ -85,12 +65,7 @@ class LocalNetworkPreference extends HookConsumerWidget { } handleEditWifiName() async { - final wifiName = await _showEditDialog( - context, - "wifi_name".tr(), - "your_wifi_name".tr(), - wifiNameText.value, - ); + final wifiName = await _showEditDialog(context, "wifi_name".tr(), "your_wifi_name".tr(), wifiNameText.value); if (wifiName != null) { await saveWifiName(wifiName); @@ -146,21 +121,14 @@ class LocalNetworkPreference extends HookConsumerWidget { decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(16)), color: context.colorScheme.surfaceContainerLow, - border: Border.all( - color: context.colorScheme.surfaceContainerHighest, - width: 1, - ), + border: Border.all(color: context.colorScheme.surfaceContainerHighest, width: 1), ), child: Stack( children: [ Positioned( bottom: -36, right: -36, - child: Icon( - Icons.home_outlined, - size: 120, - color: context.primaryColor.withValues(alpha: 0.05), - ), + child: Icon(Icons.home_outlined, size: 120, color: context.primaryColor.withValues(alpha: 0.05)), ), ListView( padding: const EdgeInsets.symmetric(vertical: 16.0), @@ -168,19 +136,11 @@ class LocalNetworkPreference extends HookConsumerWidget { shrinkWrap: true, children: [ Padding( - padding: const EdgeInsets.symmetric( - vertical: 4.0, - horizontal: 24, - ), - child: Text( - "local_network_sheet_info".tr(), - style: context.textTheme.bodyMedium, - ), + padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 24), + child: Text("local_network_sheet_info".tr(), style: context.textTheme.bodyMedium), ), const SizedBox(height: 4), - Divider( - color: context.colorScheme.surfaceContainerHighest, - ), + Divider(color: context.colorScheme.surfaceContainerHighest), ListTile( enabled: enabled, contentPadding: const EdgeInsets.only(left: 24, right: 8), @@ -223,9 +183,7 @@ class LocalNetworkPreference extends HookConsumerWidget { ), const SizedBox(height: 16), Padding( - padding: const EdgeInsets.symmetric( - horizontal: 24.0, - ), + padding: const EdgeInsets.symmetric(horizontal: 24.0), child: SizedBox( height: 48, child: OutlinedButton.icon( diff --git a/mobile/lib/widgets/settings/networking_settings/networking_settings.dart b/mobile/lib/widgets/settings/networking_settings/networking_settings.dart index 24d62b2663..426ea5ac0f 100644 --- a/mobile/lib/widgets/settings/networking_settings/networking_settings.dart +++ b/mobile/lib/widgets/settings/networking_settings/networking_settings.dart @@ -77,15 +77,12 @@ class NetworkingSettings extends HookConsumerWidget { } } - useEffect( - () { - if (featureEnabled.value == true) { - checkWifiReadPermission(); - } - return null; - }, - [featureEnabled.value], - ); + useEffect(() { + if (featureEnabled.value == true) { + checkWifiReadPermission(); + } + return null; + }, [featureEnabled.value]); return ListView( padding: const EdgeInsets.only(bottom: 96), @@ -104,20 +101,12 @@ class NetworkingSettings extends HookConsumerWidget { elevation: 0, shape: RoundedRectangleBorder( borderRadius: const BorderRadius.all(Radius.circular(16)), - side: BorderSide( - color: context.colorScheme.surfaceContainerHighest, - width: 1, - ), + side: BorderSide(color: context.colorScheme.surfaceContainerHighest, width: 1), ), child: ListTile( leading: currentEndpoint != null - ? const Icon( - Icons.check_circle_rounded, - color: Colors.green, - ) - : const Icon( - Icons.circle_outlined, - ), + ? const Icon(Icons.check_circle_rounded, color: Colors.green) + : const Icon(Icons.circle_outlined), title: Text( currentEndpoint ?? "--", style: TextStyle( @@ -132,9 +121,7 @@ class NetworkingSettings extends HookConsumerWidget { ), Padding( padding: const EdgeInsets.only(top: 10.0), - child: Divider( - color: context.colorScheme.surfaceContainerHighest, - ), + child: Divider(color: context.colorScheme.surfaceContainerHighest), ), SettingsSwitchListTile( enabled: true, @@ -144,35 +131,21 @@ class NetworkingSettings extends HookConsumerWidget { ), Padding( padding: const EdgeInsets.only(top: 8, left: 16, bottom: 16), - child: NetworkPreferenceTitle( - title: "local_network".tr().toUpperCase(), - icon: Icons.home_outlined, - ), - ), - LocalNetworkPreference( - enabled: featureEnabled.value, + child: NetworkPreferenceTitle(title: "local_network".tr().toUpperCase(), icon: Icons.home_outlined), ), + LocalNetworkPreference(enabled: featureEnabled.value), Padding( padding: const EdgeInsets.only(top: 32, left: 16, bottom: 16), - child: NetworkPreferenceTitle( - title: "external_network".tr().toUpperCase(), - icon: Icons.dns_outlined, - ), - ), - ExternalNetworkPreference( - enabled: featureEnabled.value, + child: NetworkPreferenceTitle(title: "external_network".tr().toUpperCase(), icon: Icons.dns_outlined), ), + ExternalNetworkPreference(enabled: featureEnabled.value), ], ); } } class NetworkPreferenceTitle extends StatelessWidget { - const NetworkPreferenceTitle({ - super.key, - required this.icon, - required this.title, - }); + const NetworkPreferenceTitle({super.key, required this.icon, required this.title}); final IconData icon; final String title; @@ -181,10 +154,7 @@ class NetworkPreferenceTitle extends StatelessWidget { Widget build(BuildContext context) { return Row( children: [ - Icon( - icon, - color: context.colorScheme.onSurface.withAlpha(150), - ), + Icon(icon, color: context.colorScheme.onSurface.withAlpha(150)), const SizedBox(width: 8), Text( title, @@ -199,58 +169,37 @@ class NetworkPreferenceTitle extends StatelessWidget { } class NetworkStatusIcon extends StatelessWidget { - const NetworkStatusIcon({ - super.key, - required this.status, - this.enabled = true, - }) : super(); + const NetworkStatusIcon({super.key, required this.status, this.enabled = true}) : super(); final AuxCheckStatus status; final bool enabled; @override Widget build(BuildContext context) { - return AnimatedSwitcher( - duration: const Duration(milliseconds: 200), - child: _buildIcon(context), - ); + return AnimatedSwitcher(duration: const Duration(milliseconds: 200), child: _buildIcon(context)); } Widget _buildIcon(BuildContext context) => switch (status) { - AuxCheckStatus.loading => Padding( - padding: const EdgeInsets.only(left: 4.0), - child: SizedBox( - width: 18, - height: 18, - child: CircularProgressIndicator( - color: context.primaryColor, - strokeWidth: 2, - key: const ValueKey('loading'), - ), + AuxCheckStatus.loading => Padding( + padding: const EdgeInsets.only(left: 4.0), + child: SizedBox( + width: 18, + height: 18, + child: CircularProgressIndicator(color: context.primaryColor, strokeWidth: 2, key: const ValueKey('loading')), + ), + ), + AuxCheckStatus.valid => + enabled + ? const Icon(Icons.check_circle_rounded, color: Colors.green, key: ValueKey('success')) + : Icon( + Icons.check_circle_rounded, + color: context.colorScheme.onSurface.withAlpha(100), + key: const ValueKey('success'), ), - ), - AuxCheckStatus.valid => enabled - ? const Icon( - Icons.check_circle_rounded, - color: Colors.green, - key: ValueKey('success'), - ) - : Icon( - Icons.check_circle_rounded, - color: context.colorScheme.onSurface.withAlpha(100), - key: const ValueKey('success'), - ), - AuxCheckStatus.error => enabled - ? const Icon( - Icons.error_rounded, - color: Colors.red, - key: ValueKey('error'), - ) - : const Icon( - Icons.error_rounded, - color: Colors.grey, - key: ValueKey('error'), - ), - _ => const Icon(Icons.circle_outlined, key: ValueKey('unknown')), - }; + AuxCheckStatus.error => + enabled + ? const Icon(Icons.error_rounded, color: Colors.red, key: ValueKey('error')) + : const Icon(Icons.error_rounded, color: Colors.grey, key: ValueKey('error')), + _ => const Icon(Icons.circle_outlined, key: ValueKey('unknown')), + }; } diff --git a/mobile/lib/widgets/settings/notification_setting.dart b/mobile/lib/widgets/settings/notification_setting.dart index f4e520f4df..d9eab26bda 100644 --- a/mobile/lib/widgets/settings/notification_setting.dart +++ b/mobile/lib/widgets/settings/notification_setting.dart @@ -12,9 +12,7 @@ import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:permission_handler/permission_handler.dart'; class NotificationSetting extends HookConsumerWidget { - const NotificationSetting({ - super.key, - }); + const NotificationSetting({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -39,14 +37,8 @@ class NotificationSetting extends HookConsumerWidget { builder: (ctx) => AlertDialog( content: const Text('notification_permission_dialog_content').tr(), actions: [ - TextButton( - child: const Text('cancel').tr(), - onPressed: () => ctx.pop(), - ), - TextButton( - onPressed: () => openAppNotificationSettings(ctx), - child: const Text('settings').tr(), - ), + TextButton(child: const Text('cancel').tr(), onPressed: () => ctx.pop()), + TextButton(onPressed: () => openAppNotificationSettings(ctx), child: const Text('settings').tr()), ], ), ); @@ -63,10 +55,10 @@ class NotificationSetting extends HookConsumerWidget { buttonText: 'notification_permission_list_tile_enable_button'.tr(), onButtonTap: () => ref.watch(notificationPermissionProvider.notifier).requestNotificationPermission().then((permission) { - if (permission == PermissionStatus.permanentlyDenied) { - showPermissionsDialog(); - } - }), + if (permission == PermissionStatus.permanentlyDenied) { + showPermissionsDialog(); + } + }), ), SettingsSwitchListTile( enabled: hasPermission, diff --git a/mobile/lib/widgets/settings/preference_settings/haptic_setting.dart b/mobile/lib/widgets/settings/preference_settings/haptic_setting.dart index fbd94b68d6..49f57a5e94 100644 --- a/mobile/lib/widgets/settings/preference_settings/haptic_setting.dart +++ b/mobile/lib/widgets/settings/preference_settings/haptic_setting.dart @@ -8,9 +8,7 @@ import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; class HapticSetting extends HookConsumerWidget { - const HapticSetting({ - super.key, - }); + const HapticSetting({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { diff --git a/mobile/lib/widgets/settings/preference_settings/preference_setting.dart b/mobile/lib/widgets/settings/preference_settings/preference_setting.dart index 8a3684e093..144fbf9758 100644 --- a/mobile/lib/widgets/settings/preference_settings/preference_setting.dart +++ b/mobile/lib/widgets/settings/preference_settings/preference_setting.dart @@ -4,20 +4,12 @@ import 'package:immich_mobile/widgets/settings/preference_settings/theme_setting import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart'; class PreferenceSetting extends StatelessWidget { - const PreferenceSetting({ - super.key, - }); + const PreferenceSetting({super.key}); @override Widget build(BuildContext context) { - const preferenceSettings = [ - ThemeSetting(), - HapticSetting(), - ]; + const preferenceSettings = [ThemeSetting(), HapticSetting()]; - return const SettingsSubPageScaffold( - settings: preferenceSettings, - showDivider: true, - ); + return const SettingsSubPageScaffold(settings: preferenceSettings, showDivider: true); } } diff --git a/mobile/lib/widgets/settings/preference_settings/primary_color_setting.dart b/mobile/lib/widgets/settings/preference_settings/primary_color_setting.dart index b4f70c5b9b..ddf2cc6215 100644 --- a/mobile/lib/widgets/settings/preference_settings/primary_color_setting.dart +++ b/mobile/lib/widgets/settings/preference_settings/primary_color_setting.dart @@ -12,9 +12,7 @@ import 'package:immich_mobile/theme/dynamic_theme.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; class PrimaryColorSetting extends HookConsumerWidget { - const PrimaryColorSetting({ - super.key, - }); + const PrimaryColorSetting({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -70,20 +68,14 @@ class PrimaryColorSetting extends HookConsumerWidget { Container( height: tileSize, width: tileSize, - decoration: BoxDecoration( - color: bottomColor, - borderRadius: const BorderRadius.all(Radius.circular(100)), - ), + decoration: BoxDecoration(color: bottomColor, borderRadius: const BorderRadius.all(Radius.circular(100))), ), Container( height: tileSize / 2, width: tileSize, decoration: BoxDecoration( color: topColor, - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(100), - topRight: Radius.circular(100), - ), + borderRadius: const BorderRadius.only(topLeft: Radius.circular(100), topRight: Radius.circular(100)), ), ), if (showSelector) @@ -99,11 +91,7 @@ class PrimaryColorSetting extends HookConsumerWidget { ), child: const Padding( padding: EdgeInsets.all(3), - child: Icon( - Icons.check_rounded, - color: Colors.white, - size: 25, - ), + child: Icon(Icons.check_rounded, color: Colors.white, size: 25), ), ), ), @@ -118,10 +106,7 @@ class PrimaryColorSetting extends HookConsumerWidget { children: [ Align( alignment: Alignment.center, - child: Text( - "theme_setting_primary_color_title".tr(), - style: context.textTheme.titleLarge, - ), + child: Text("theme_setting_primary_color_title".tr(), style: context.textTheme.titleLarge), ), if (DynamicTheme.isAvailable) Container( @@ -132,15 +117,10 @@ class PrimaryColorSetting extends HookConsumerWidget { dense: true, activeColor: context.primaryColor, tileColor: context.colorScheme.surfaceContainerHigh, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(15)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15))), title: Text( 'theme_setting_system_primary_color_title'.tr(), - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - height: 1.5, - ), + style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500, height: 1.5), ), value: systemPrimaryColorSetting.value, onChanged: onUseSystemColorChange, @@ -175,10 +155,7 @@ class PrimaryColorSetting extends HookConsumerWidget { context: context, isScrollControlled: true, builder: (BuildContext ctx) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 0), - child: bottomSheetContent(), - ); + return Padding(padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 0), child: bottomSheetContent()); }, ), contentPadding: const EdgeInsets.symmetric(horizontal: 20), @@ -190,9 +167,7 @@ class PrimaryColorSetting extends HookConsumerWidget { children: [ Text( "theme_setting_primary_color_title".tr(), - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500), ), Text( "theme_setting_primary_color_subtitle".tr(), diff --git a/mobile/lib/widgets/settings/preference_settings/theme_setting.dart b/mobile/lib/widgets/settings/preference_settings/theme_setting.dart index 6d5a50e730..123f7c9921 100644 --- a/mobile/lib/widgets/settings/preference_settings/theme_setting.dart +++ b/mobile/lib/widgets/settings/preference_settings/theme_setting.dart @@ -11,9 +11,7 @@ import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; class ThemeSetting extends HookConsumerWidget { - const ThemeSetting({ - super.key, - }); + const ThemeSetting({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { diff --git a/mobile/lib/widgets/settings/settings_button_list_tile.dart b/mobile/lib/widgets/settings/settings_button_list_tile.dart index 602bbd75cf..0e8d75b22f 100644 --- a/mobile/lib/widgets/settings/settings_button_list_tile.dart +++ b/mobile/lib/widgets/settings/settings_button_list_tile.dart @@ -31,12 +31,7 @@ class SettingsButtonListTile extends StatelessWidget { horizontalTitleGap: 20, isThreeLine: true, leading: Icon(icon, color: iconColor), - title: Text( - title, - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + title: Text(title, style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500)), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -44,9 +39,7 @@ class SettingsButtonListTile extends StatelessWidget { if (subtileText != null) Text( subtileText!, - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), if (subtitle != null) subtitle!, const SizedBox(height: 6), diff --git a/mobile/lib/widgets/settings/settings_card.dart b/mobile/lib/widgets/settings/settings_card.dart index 523add9690..36eff7bae1 100644 --- a/mobile/lib/widgets/settings/settings_card.dart +++ b/mobile/lib/widgets/settings/settings_card.dart @@ -19,21 +19,15 @@ class SettingsCard extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), + padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Card( elevation: 0, clipBehavior: Clip.antiAlias, color: context.colorScheme.surfaceContainer, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(16)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))), margin: const EdgeInsets.symmetric(vertical: 4.0), child: ListTile( - contentPadding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), + contentPadding: const EdgeInsets.symmetric(horizontal: 16.0), leading: Container( decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(16)), @@ -44,15 +38,9 @@ class SettingsCard extends StatelessWidget { ), title: Text( title, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w600, - color: context.primaryColor, - ), - ), - subtitle: Text( - subtitle, - style: context.textTheme.labelLarge, + style: context.textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor), ), + subtitle: Text(subtitle, style: context.textTheme.labelLarge), onTap: () => context.pushRoute(settingRoute), ), ), diff --git a/mobile/lib/widgets/settings/settings_radio_list_tile.dart b/mobile/lib/widgets/settings/settings_radio_list_tile.dart index 3f3a6cbe69..95224e3f59 100644 --- a/mobile/lib/widgets/settings/settings_radio_list_tile.dart +++ b/mobile/lib/widgets/settings/settings_radio_list_tile.dart @@ -13,12 +13,7 @@ class SettingsRadioListTile extends StatelessWidget { final T groupBy; final void Function(T?) onRadioChanged; - const SettingsRadioListTile({ - super.key, - required this.groups, - required this.groupBy, - required this.onRadioChanged, - }); + const SettingsRadioListTile({super.key, required this.groups, required this.groupBy, required this.onRadioChanged}); @override Widget build(BuildContext context) { @@ -29,12 +24,7 @@ class SettingsRadioListTile extends StatelessWidget { contentPadding: const EdgeInsets.symmetric(horizontal: 20), dense: true, activeColor: context.primaryColor, - title: Text( - g.title, - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + title: Text(g.title, style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500)), value: g.value, groupValue: groupBy, onChanged: onRadioChanged, diff --git a/mobile/lib/widgets/settings/settings_slider_list_tile.dart b/mobile/lib/widgets/settings/settings_slider_list_tile.dart index 386a690864..500591badb 100644 --- a/mobile/lib/widgets/settings/settings_slider_list_tile.dart +++ b/mobile/lib/widgets/settings/settings_slider_list_tile.dart @@ -28,12 +28,7 @@ class SettingsSliderListTile extends StatelessWidget { return ListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 20), dense: true, - title: Text( - text, - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + title: Text(text, style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500)), subtitle: Slider( value: valueNotifier.value.toDouble(), onChanged: (double v) => valueNotifier.value = v.toInt(), diff --git a/mobile/lib/widgets/settings/settings_sub_page_scaffold.dart b/mobile/lib/widgets/settings/settings_sub_page_scaffold.dart index 96c4678ede..b4cb67239e 100644 --- a/mobile/lib/widgets/settings/settings_sub_page_scaffold.dart +++ b/mobile/lib/widgets/settings/settings_sub_page_scaffold.dart @@ -4,11 +4,7 @@ class SettingsSubPageScaffold extends StatelessWidget { final List settings; final bool showDivider; - const SettingsSubPageScaffold({ - super.key, - required this.settings, - this.showDivider = false, - }); + const SettingsSubPageScaffold({super.key, required this.settings, this.showDivider = false}); @override Widget build(BuildContext context) { @@ -18,11 +14,7 @@ class SettingsSubPageScaffold extends StatelessWidget { itemBuilder: (ctx, index) => settings[index], separatorBuilder: (context, index) => showDivider ? const Column( - children: [ - SizedBox(height: 5), - Divider(height: 10, indent: 15, endIndent: 15), - SizedBox(height: 15), - ], + children: [SizedBox(height: 5), Divider(height: 10, indent: 15, endIndent: 15), SizedBox(height: 15)], ) : const SizedBox(height: 10), ); diff --git a/mobile/lib/widgets/settings/settings_sub_title.dart b/mobile/lib/widgets/settings/settings_sub_title.dart index 9a3fb6947d..d98f1929b2 100644 --- a/mobile/lib/widgets/settings/settings_sub_title.dart +++ b/mobile/lib/widgets/settings/settings_sub_title.dart @@ -4,10 +4,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; class SettingsSubTitle extends StatelessWidget { final String title; - const SettingsSubTitle({ - super.key, - required this.title, - }); + const SettingsSubTitle({super.key, required this.title}); @override Widget build(BuildContext context) { @@ -15,10 +12,7 @@ class SettingsSubTitle extends StatelessWidget { padding: const EdgeInsets.only(left: 20), child: Text( title, - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - fontWeight: FontWeight.w700, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor, fontWeight: FontWeight.w700), ), ); } diff --git a/mobile/lib/widgets/settings/settings_switch_list_tile.dart b/mobile/lib/widgets/settings/settings_switch_list_tile.dart index 456acd83cd..d51e2eb2ca 100644 --- a/mobile/lib/widgets/settings/settings_switch_list_tile.dart +++ b/mobile/lib/widgets/settings/settings_switch_list_tile.dart @@ -42,15 +42,11 @@ class SettingsSwitchListTile extends StatelessWidget { onChanged: onSwitchChanged, activeColor: enabled ? context.primaryColor : context.themeData.disabledColor, dense: true, - secondary: icon != null - ? Icon( - icon!, - color: valueNotifier.value ? context.primaryColor : null, - ) - : null, + secondary: icon != null ? Icon(icon!, color: valueNotifier.value ? context.primaryColor : null) : null, title: Text( title, - style: titleStyle ?? + style: + titleStyle ?? context.textTheme.bodyLarge?.copyWith( fontWeight: FontWeight.w500, color: enabled ? null : context.themeData.disabledColor, @@ -60,7 +56,8 @@ class SettingsSwitchListTile extends StatelessWidget { subtitle: subtitle != null ? Text( subtitle!, - style: subtitleStyle ?? + style: + subtitleStyle ?? context.textTheme.bodyMedium?.copyWith( color: enabled ? context.colorScheme.onSurfaceSecondary : context.themeData.disabledColor, ), diff --git a/mobile/lib/widgets/settings/ssl_client_cert_settings.dart b/mobile/lib/widgets/settings/ssl_client_cert_settings.dart index ae5b065294..dc31acf0a4 100644 --- a/mobile/lib/widgets/settings/ssl_client_cert_settings.dart +++ b/mobile/lib/widgets/settings/ssl_client_cert_settings.dart @@ -30,24 +30,15 @@ class _SslClientCertSettingsState extends State { contentPadding: const EdgeInsets.symmetric(horizontal: 20), horizontalTitleGap: 20, isThreeLine: true, - title: Text( - "client_cert_title".tr(), - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + title: Text("client_cert_title".tr(), style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500)), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "client_cert_subtitle".tr(), - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), - ), - const SizedBox( - height: 6, + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), + const SizedBox(height: 6), Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, @@ -57,9 +48,7 @@ class _SslClientCertSettingsState extends State { onPressed: widget.isLoggedIn ? null : () => importCert(context), child: Text("client_cert_import".tr()), ), - const SizedBox( - width: 15, - ), + const SizedBox(width: 15), ElevatedButton( onPressed: widget.isLoggedIn || !isCertExist ? null : () async => await removeCert(context), child: Text("remove".tr()), @@ -76,39 +65,25 @@ class _SslClientCertSettingsState extends State { context: context, builder: (ctx) => AlertDialog( content: Text(message), - actions: [ - TextButton( - onPressed: () => ctx.pop(), - child: Text("client_cert_dialog_msg_confirm".tr()), - ), - ], + actions: [TextButton(onPressed: () => ctx.pop(), child: Text("client_cert_dialog_msg_confirm".tr()))], ), ); } - Future storeCert( - BuildContext context, - Uint8List data, - String? password, - ) async { + Future storeCert(BuildContext context, Uint8List data, String? password) async { if (password != null && password.isEmpty) { password = null; } final cert = SSLClientCertStoreVal(data, password); // Test whether the certificate is valid - final isCertValid = HttpSSLCertOverride.setClientCert( - SecurityContext(withTrustedRoots: true), - cert, - ); + final isCertValid = HttpSSLCertOverride.setClientCert(SecurityContext(withTrustedRoots: true), cert); if (!isCertValid) { showMessage(context, "client_cert_invalid_msg".tr()); return; } await cert.save(); HttpSSLOptions.apply(); - setState( - () => isCertExist = true, - ); + setState(() => isCertExist = true); showMessage(context, "client_cert_import_success_msg".tr()); } @@ -122,9 +97,7 @@ class _SslClientCertSettingsState extends State { controller: password, obscureText: true, obscuringCharacter: "*", - decoration: InputDecoration( - hintText: "client_cert_enter_password".tr(), - ), + decoration: InputDecoration(hintText: "client_cert_enter_password".tr()), ), actions: [ TextButton( @@ -139,10 +112,7 @@ class _SslClientCertSettingsState extends State { Future importCert(BuildContext ctx) async { FilePickerResult? res = await FilePicker.platform.pickFiles( type: FileType.custom, - allowedExtensions: [ - 'p12', - 'pfx', - ], + allowedExtensions: ['p12', 'pfx'], ); if (res != null) { File file = File(res.files.single.path!); @@ -154,9 +124,7 @@ class _SslClientCertSettingsState extends State { Future removeCert(BuildContext context) async { await SSLClientCertStoreVal.delete(); HttpSSLOptions.apply(); - setState( - () => isCertExist = false, - ); + setState(() => isCertExist = false); showMessage(context, "client_cert_remove_msg".tr()); } } diff --git a/mobile/lib/widgets/shared_link/shared_link_item.dart b/mobile/lib/widgets/shared_link/shared_link_item.dart index 82194d2c7c..0eced33ce3 100644 --- a/mobile/lib/widgets/shared_link/shared_link_item.dart +++ b/mobile/lib/widgets/shared_link/shared_link_item.dart @@ -33,10 +33,7 @@ class SharedLinkItem extends ConsumerWidget { var expiresText = "shared_link_expires_never".tr(); if (sharedLink.expiresAt != null) { if (isExpired()) { - return Text( - "expired", - style: TextStyle(color: Colors.red[300]), - ).tr(); + return Text("expired", style: TextStyle(color: Colors.red[300])).tr(); } final difference = sharedLink.expiresAt!.difference(DateTime.now()); debugPrint("Difference: $difference"); @@ -54,10 +51,7 @@ class SharedLinkItem extends ConsumerWidget { expiresText = "shared_link_expires_seconds".tr(namedArgs: {'count': difference.inSeconds.toString()}); } } - return Text( - expiresText, - style: TextStyle(color: isDarkMode ? Colors.grey[400] : Colors.grey[600]), - ); + return Text(expiresText, style: TextStyle(color: isDarkMode ? Colors.grey[400] : Colors.grey[600])); } @override @@ -68,9 +62,7 @@ class SharedLinkItem extends ConsumerWidget { final imageSize = math.min(context.width / 4, 100.0); void copyShareLinkToClipboard() { - final externalDomain = ref.read( - serverInfoProvider.select((s) => s.serverConfig.externalDomain), - ); + final externalDomain = ref.read(serverInfoProvider.select((s) => s.serverConfig.externalDomain)); var serverUrl = externalDomain.isNotEmpty ? externalDomain : getServerUrl(); if (serverUrl != null && !serverUrl.endsWith('/')) { serverUrl += '/'; @@ -85,16 +77,12 @@ class SharedLinkItem extends ConsumerWidget { return; } - Clipboard.setData( - ClipboardData(text: "${serverUrl}share/${sharedLink.key}"), - ).then((_) { + Clipboard.setData(ClipboardData(text: "${serverUrl}share/${sharedLink.key}")).then((_) { context.scaffoldMessenger.showSnackBar( SnackBar( content: Text( "shared_link_clipboard_copied_massage", - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ).tr(), duration: const Duration(seconds: 2), ), @@ -120,14 +108,9 @@ class SharedLinkItem extends ConsumerWidget { return Container( height: imageSize * 1.2, width: imageSize, - decoration: BoxDecoration( - color: isDarkMode ? Colors.grey[800] : Colors.grey[200], - ), + decoration: BoxDecoration(color: isDarkMode ? Colors.grey[800] : Colors.grey[200]), child: Center( - child: Icon( - Icons.image_not_supported_outlined, - color: isDarkMode ? Colors.grey[100] : Colors.grey[700], - ), + child: Icon(Icons.image_not_supported_outlined, color: isDarkMode ? Colors.grey[100] : Colors.grey[700]), ), ); } @@ -160,9 +143,7 @@ class SharedLinkItem extends ConsumerWidget { color: isDarkMode ? Colors.black : Colors.white, ), ), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(25)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(25))), ), ); } @@ -228,10 +209,7 @@ class SharedLinkItem extends ConsumerWidget { color: colorScheme.primary.withValues(alpha: 0.9), borderRadius: const BorderRadius.all(Radius.circular(10)), ), - textStyle: TextStyle( - color: isDarkMode ? Colors.black : Colors.white, - fontWeight: FontWeight.bold, - ), + textStyle: TextStyle(color: isDarkMode ? Colors.black : Colors.white, fontWeight: FontWeight.bold), message: sharedLink.title, preferBelow: false, triggerMode: TooltipTriggerMode.tap, @@ -256,23 +234,14 @@ class SharedLinkItem extends ConsumerWidget { color: colorScheme.primary.withValues(alpha: 0.9), borderRadius: const BorderRadius.all(Radius.circular(10)), ), - textStyle: TextStyle( - color: isDarkMode ? Colors.black : Colors.white, - fontWeight: FontWeight.bold, - ), + textStyle: TextStyle(color: isDarkMode ? Colors.black : Colors.white, fontWeight: FontWeight.bold), message: sharedLink.description ?? "", preferBelow: false, triggerMode: TooltipTriggerMode.tap, - child: Text( - sharedLink.description ?? "", - overflow: TextOverflow.ellipsis, - ), + child: Text(sharedLink.description ?? "", overflow: TextOverflow.ellipsis), ), ), - Padding( - padding: const EdgeInsets.only(right: 15), - child: buildSharedLinkActions(), - ), + Padding(padding: const EdgeInsets.only(right: 15), child: buildSharedLinkActions()), ], ), buildBottomInfo(), @@ -286,24 +255,13 @@ class SharedLinkItem extends ConsumerWidget { Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Padding( - padding: const EdgeInsets.only(left: 15), - child: buildThumbnail(), - ), + Padding(padding: const EdgeInsets.only(left: 15), child: buildThumbnail()), Expanded( - child: Padding( - padding: const EdgeInsets.only(left: 15), - child: buildSharedLinkDetails(), - ), + child: Padding(padding: const EdgeInsets.only(left: 15), child: buildSharedLinkDetails()), ), ], ), - const Padding( - padding: EdgeInsets.all(20), - child: Divider( - height: 0, - ), - ), + const Padding(padding: EdgeInsets.all(20), child: Divider(height: 0)), ], ); } diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 20154649ea..42f37fbc85 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -2162,5 +2162,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.7.0 <4.0.0" + dart: ">=3.8.0 <4.0.0" flutter: ">=3.32.8" diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index cfec68689d..03519a0cf2 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -5,7 +5,7 @@ publish_to: 'none' version: 1.136.0+3000 environment: - sdk: '>=3.3.0 <4.0.0' + sdk: '>=3.8.0 <4.0.0' flutter: 3.32.8 isar_version: &isar_version 3.1.8 diff --git a/mobile/test/domain/services/hash_service_test.dart b/mobile/test/domain/services/hash_service_test.dart index 262766662b..1534b2e914 100644 --- a/mobile/test/domain/services/hash_service_test.dart +++ b/mobile/test/domain/services/hash_service_test.dart @@ -21,10 +21,7 @@ void main() { late MockLocalAssetRepository mockAssetRepo; late MockStorageRepository mockStorageRepo; late MockNativeSyncApi mockNativeApi; - final sortBy = { - SortLocalAlbumsBy.backupSelection, - SortLocalAlbumsBy.isIosSharedAlbum, - }; + final sortBy = {SortLocalAlbumsBy.backupSelection, SortLocalAlbumsBy.isIosSharedAlbum}; setUp(() { mockAlbumRepo = MockLocalAlbumRepository(); @@ -47,9 +44,9 @@ void main() { group('HashService hashAssets', () { test('skips albums with no assets to hash', () async { - when(() => mockAlbumRepo.getAll(sortBy: sortBy)).thenAnswer( - (_) async => [LocalAlbumStub.recent.copyWith(assetCount: 0)], - ); + when( + () => mockAlbumRepo.getAll(sortBy: sortBy), + ).thenAnswer((_) async => [LocalAlbumStub.recent.copyWith(assetCount: 0)]); when(() => mockAlbumRepo.getAssetsToHash(LocalAlbumStub.recent.id)).thenAnswer((_) async => []); await sut.hashAssets(); @@ -84,9 +81,7 @@ void main() { when(() => mockAlbumRepo.getAll(sortBy: sortBy)).thenAnswer((_) async => [album]); when(() => mockAlbumRepo.getAssetsToHash(album.id)).thenAnswer((_) async => [asset]); when(() => mockStorageRepo.getFileForAsset(asset.id)).thenAnswer((_) async => mockFile); - when(() => mockNativeApi.hashPaths(['image-path'])).thenAnswer( - (_) async => [hash], - ); + when(() => mockNativeApi.hashPaths(['image-path'])).thenAnswer((_) async => [hash]); await sut.hashAssets(); diff --git a/mobile/test/domain/services/log_service_test.dart b/mobile/test/domain/services/log_service_test.dart index ad35a018c8..87b32b8298 100644 --- a/mobile/test/domain/services/log_service_test.dart +++ b/mobile/test/domain/services/log_service_test.dart @@ -43,10 +43,7 @@ void main() { when(() => mockLogRepo.insert(any())).thenAnswer((_) async => true); when(() => mockLogRepo.insertAll(any())).thenAnswer((_) async => true); - sut = await LogService.create( - logRepository: mockLogRepo, - storeRepository: mockStoreRepo, - ); + sut = await LogService.create(logRepository: mockLogRepo, storeRepository: mockStoreRepo); }); tearDown(() async { @@ -72,9 +69,7 @@ void main() { }); test('Updates the log level in store', () { - final index = verify( - () => mockStoreRepo.insert(StoreKey.logLevel, captureAny()), - ).captured.firstOrNull; + final index = verify(() => mockStoreRepo.insert(StoreKey.logLevel, captureAny())).captured.firstOrNull; expect(index, LogLevel.shout.index); }); @@ -86,11 +81,7 @@ void main() { group("Log Service Buffer:", () { test('Buffers logs until timer elapses', () { TestUtils.fakeAsync((time) async { - sut = await LogService.create( - logRepository: mockLogRepo, - storeRepository: mockStoreRepo, - shouldBuffer: true, - ); + sut = await LogService.create(logRepository: mockLogRepo, storeRepository: mockStoreRepo, shouldBuffer: true); final logger = Logger(_kInfoLog.logger!); logger.info(_kInfoLog.message); @@ -104,11 +95,7 @@ void main() { test('Batch inserts all logs on timer', () { TestUtils.fakeAsync((time) async { - sut = await LogService.create( - logRepository: mockLogRepo, - storeRepository: mockStoreRepo, - shouldBuffer: true, - ); + sut = await LogService.create(logRepository: mockLogRepo, storeRepository: mockStoreRepo, shouldBuffer: true); final logger = Logger(_kInfoLog.logger!); logger.info(_kInfoLog.message); @@ -125,11 +112,7 @@ void main() { test('Does not buffer when off', () { TestUtils.fakeAsync((time) async { - sut = await LogService.create( - logRepository: mockLogRepo, - storeRepository: mockStoreRepo, - shouldBuffer: false, - ); + sut = await LogService.create(logRepository: mockLogRepo, storeRepository: mockStoreRepo, shouldBuffer: false); final logger = Logger(_kInfoLog.logger!); logger.info(_kInfoLog.message); @@ -159,11 +142,7 @@ void main() { test('Combines result from both DB + Buffer', () { TestUtils.fakeAsync((time) async { - sut = await LogService.create( - logRepository: mockLogRepo, - storeRepository: mockStoreRepo, - shouldBuffer: true, - ); + sut = await LogService.create(logRepository: mockLogRepo, storeRepository: mockStoreRepo, shouldBuffer: true); final logger = Logger(_kWarnLog.logger!); logger.warning(_kWarnLog.message); diff --git a/mobile/test/domain/services/store_service_test.dart b/mobile/test/domain/services/store_service_test.dart index c436a05454..d23913991c 100644 --- a/mobile/test/domain/services/store_service_test.dart +++ b/mobile/test/domain/services/store_service_test.dart @@ -73,10 +73,7 @@ void main() { }); test('Throws StoreKeyNotFoundException for nonexistent keys', () { - expect( - () => sut.get(StoreKey.currentUser), - throwsA(isA()), - ); + expect(() => sut.get(StoreKey.currentUser), throwsA(isA())); }); test('Returns the stored value for the given key or the defaultValue', () { @@ -91,17 +88,13 @@ void main() { test('Skip insert when value is not modified', () async { await sut.put(StoreKey.accessToken, _kAccessToken); - verifyNever( - () => mockStoreRepo.insert(StoreKey.accessToken, any()), - ); + verifyNever(() => mockStoreRepo.insert(StoreKey.accessToken, any())); }); test('Insert value when modified', () async { final newAccessToken = _kAccessToken.toUpperCase(); await sut.put(StoreKey.accessToken, newAccessToken); - verify( - () => mockStoreRepo.insert(StoreKey.accessToken, newAccessToken), - ).called(1); + verify(() => mockStoreRepo.insert(StoreKey.accessToken, newAccessToken)).called(1); expect(sut.tryGet(StoreKey.accessToken), newAccessToken); }); }); @@ -120,12 +113,7 @@ void main() { test('Watches a specific key for changes', () async { final stream = sut.watch(StoreKey.accessToken); - final events = [ - _kAccessToken, - _kAccessToken.toUpperCase(), - null, - _kAccessToken.toLowerCase(), - ]; + final events = [_kAccessToken, _kAccessToken.toUpperCase(), null, _kAccessToken.toLowerCase()]; expectLater(stream, emitsInOrder(events)); diff --git a/mobile/test/domain/services/sync_stream_service_test.dart b/mobile/test/domain/services/sync_stream_service_test.dart index 49ac4467d0..46e585faa0 100644 --- a/mobile/test/domain/services/sync_stream_service_test.dart +++ b/mobile/test/domain/services/sync_stream_service_test.dart @@ -54,40 +54,25 @@ void main() { when(() => mockSyncStreamRepo.deletePartnerV1(any())).thenAnswer(successHandler); when(() => mockSyncStreamRepo.updateAssetsV1(any())).thenAnswer(successHandler); when( - () => mockSyncStreamRepo.updateAssetsV1( - any(), - debugLabel: any(named: 'debugLabel'), - ), + () => mockSyncStreamRepo.updateAssetsV1(any(), debugLabel: any(named: 'debugLabel')), ).thenAnswer(successHandler); when(() => mockSyncStreamRepo.deleteAssetsV1(any())).thenAnswer(successHandler); when( - () => mockSyncStreamRepo.deleteAssetsV1( - any(), - debugLabel: any(named: 'debugLabel'), - ), + () => mockSyncStreamRepo.deleteAssetsV1(any(), debugLabel: any(named: 'debugLabel')), ).thenAnswer(successHandler); when(() => mockSyncStreamRepo.updateAssetsExifV1(any())).thenAnswer(successHandler); when( - () => mockSyncStreamRepo.updateAssetsExifV1( - any(), - debugLabel: any(named: 'debugLabel'), - ), + () => mockSyncStreamRepo.updateAssetsExifV1(any(), debugLabel: any(named: 'debugLabel')), ).thenAnswer(successHandler); when(() => mockSyncStreamRepo.updateMemoriesV1(any())).thenAnswer(successHandler); when(() => mockSyncStreamRepo.deleteMemoriesV1(any())).thenAnswer(successHandler); when(() => mockSyncStreamRepo.updateMemoryAssetsV1(any())).thenAnswer(successHandler); when(() => mockSyncStreamRepo.deleteMemoryAssetsV1(any())).thenAnswer(successHandler); when( - () => mockSyncStreamRepo.updateStacksV1( - any(), - debugLabel: any(named: 'debugLabel'), - ), + () => mockSyncStreamRepo.updateStacksV1(any(), debugLabel: any(named: 'debugLabel')), ).thenAnswer(successHandler); when( - () => mockSyncStreamRepo.deleteStacksV1( - any(), - debugLabel: any(named: 'debugLabel'), - ), + () => mockSyncStreamRepo.deleteStacksV1(any(), debugLabel: any(named: 'debugLabel')), ).thenAnswer(successHandler); when(() => mockSyncStreamRepo.updateUserMetadatasV1(any())).thenAnswer(successHandler); when(() => mockSyncStreamRepo.deleteUserMetadatasV1(any())).thenAnswer(successHandler); @@ -96,10 +81,7 @@ void main() { when(() => mockSyncStreamRepo.updateAssetFacesV1(any())).thenAnswer(successHandler); when(() => mockSyncStreamRepo.deleteAssetFacesV1(any())).thenAnswer(successHandler); - sut = SyncStreamService( - syncApiRepository: mockSyncApiRepo, - syncStreamRepository: mockSyncStreamRepo, - ); + sut = SyncStreamService(syncApiRepository: mockSyncApiRepo, syncStreamRepository: mockSyncStreamRepo); }); Future simulateEvents(List events) async { @@ -108,41 +90,35 @@ void main() { } group("SyncStreamService - _handleEvents", () { - test( - "processes events and acks successfully when handlers succeed", - () async { - final events = [ - SyncStreamStub.userDeleteV1, - SyncStreamStub.userV1Admin, - SyncStreamStub.userV1User, - SyncStreamStub.partnerDeleteV1, - SyncStreamStub.partnerV1, - ]; - - await simulateEvents(events); - - verifyInOrder([ - () => mockSyncStreamRepo.deleteUsersV1(any()), - () => mockSyncApiRepo.ack(["2"]), - () => mockSyncStreamRepo.updateUsersV1(any()), - () => mockSyncApiRepo.ack(["5"]), - () => mockSyncStreamRepo.deletePartnerV1(any()), - () => mockSyncApiRepo.ack(["4"]), - () => mockSyncStreamRepo.updatePartnerV1(any()), - () => mockSyncApiRepo.ack(["3"]), - ]); - verifyNever(() => mockAbortCallbackWrapper()); - }, - ); - - test("processes final batch correctly", () async { + test("processes events and acks successfully when handlers succeed", () async { final events = [ SyncStreamStub.userDeleteV1, SyncStreamStub.userV1Admin, + SyncStreamStub.userV1User, + SyncStreamStub.partnerDeleteV1, + SyncStreamStub.partnerV1, ]; await simulateEvents(events); + verifyInOrder([ + () => mockSyncStreamRepo.deleteUsersV1(any()), + () => mockSyncApiRepo.ack(["2"]), + () => mockSyncStreamRepo.updateUsersV1(any()), + () => mockSyncApiRepo.ack(["5"]), + () => mockSyncStreamRepo.deletePartnerV1(any()), + () => mockSyncApiRepo.ack(["4"]), + () => mockSyncStreamRepo.updatePartnerV1(any()), + () => mockSyncApiRepo.ack(["3"]), + ]); + verifyNever(() => mockAbortCallbackWrapper()); + }); + + test("processes final batch correctly", () async { + final events = [SyncStreamStub.userDeleteV1, SyncStreamStub.userV1Admin]; + + await simulateEvents(events); + verifyInOrder([ () => mockSyncStreamRepo.deleteUsersV1(any()), () => mockSyncApiRepo.ack(["2"]), @@ -174,11 +150,7 @@ void main() { ); await sut.sync(); - final events = [ - SyncStreamStub.userDeleteV1, - SyncStreamStub.userV1Admin, - SyncStreamStub.partnerDeleteV1, - ]; + final events = [SyncStreamStub.userDeleteV1, SyncStreamStub.userV1Admin, SyncStreamStub.partnerDeleteV1]; when(() => mockSyncStreamRepo.deleteUsersV1(any())).thenAnswer((_) async { when(() => cancellationChecker()).thenReturn(true); @@ -195,50 +167,43 @@ void main() { verify(() => mockSyncApiRepo.ack(["2"])).called(1); }); - test( - "aborts and stops processing if cancelled before processing batch", - () async { - final cancellationChecker = _MockCancellationWrapper(); - when(() => cancellationChecker()).thenReturn(false); + test("aborts and stops processing if cancelled before processing batch", () async { + final cancellationChecker = _MockCancellationWrapper(); + when(() => cancellationChecker()).thenReturn(false); - final processingCompleter = Completer(); - bool handler1Started = false; - when(() => mockSyncStreamRepo.deleteUsersV1(any())).thenAnswer((_) async { - handler1Started = true; - return processingCompleter.future; - }); + final processingCompleter = Completer(); + bool handler1Started = false; + when(() => mockSyncStreamRepo.deleteUsersV1(any())).thenAnswer((_) async { + handler1Started = true; + return processingCompleter.future; + }); - sut = SyncStreamService( - syncApiRepository: mockSyncApiRepo, - syncStreamRepository: mockSyncStreamRepo, - cancelChecker: cancellationChecker.call, - ); + sut = SyncStreamService( + syncApiRepository: mockSyncApiRepo, + syncStreamRepository: mockSyncStreamRepo, + cancelChecker: cancellationChecker.call, + ); - await sut.sync(); + await sut.sync(); - final events = [ - SyncStreamStub.userDeleteV1, - SyncStreamStub.userV1Admin, - SyncStreamStub.partnerDeleteV1, - ]; + final events = [SyncStreamStub.userDeleteV1, SyncStreamStub.userV1Admin, SyncStreamStub.partnerDeleteV1]; - final processingFuture = handleEventsCallback(events, mockAbortCallbackWrapper.call); - await pumpEventQueue(); + final processingFuture = handleEventsCallback(events, mockAbortCallbackWrapper.call); + await pumpEventQueue(); - expect(handler1Started, isTrue); + expect(handler1Started, isTrue); - // Signal cancellation while handler 1 is waiting - when(() => cancellationChecker()).thenReturn(true); - await pumpEventQueue(); + // Signal cancellation while handler 1 is waiting + when(() => cancellationChecker()).thenReturn(true); + await pumpEventQueue(); - processingCompleter.complete(); - await processingFuture; + processingCompleter.complete(); + await processingFuture; - verifyNever(() => mockSyncStreamRepo.updateUsersV1(any())); + verifyNever(() => mockSyncStreamRepo.updateUsersV1(any())); - verify(() => mockSyncApiRepo.ack(["2"])).called(1); - }, - ); + verify(() => mockSyncApiRepo.ack(["2"])).called(1); + }); test("processes memory sync events successfully", () async { final events = [ @@ -289,15 +254,9 @@ void main() { test("handles memory sync failure gracefully", () async { when(() => mockSyncStreamRepo.updateMemoriesV1(any())).thenThrow(Exception("Memory sync failed")); - final events = [ - SyncStreamStub.memoryV1, - SyncStreamStub.userV1Admin, - ]; + final events = [SyncStreamStub.memoryV1, SyncStreamStub.userV1Admin]; - expect( - () async => await simulateEvents(events), - throwsA(isA()), - ); + expect(() async => await simulateEvents(events), throwsA(isA())); }); test("processes memory asset events with correct data types", () async { diff --git a/mobile/test/domain/services/user_service_test.dart b/mobile/test/domain/services/user_service_test.dart index b26c243430..b3d967154c 100644 --- a/mobile/test/domain/services/user_service_test.dart +++ b/mobile/test/domain/services/user_service_test.dart @@ -89,9 +89,7 @@ void main() { when(() => mockUserApiRepo.getMyUser()).thenAnswer((_) async => null); final result = await sut.refreshMyUser(); - verifyNever( - () => mockStoreService.put(StoreKey.currentUser, UserStub.admin), - ); + verifyNever(() => mockStoreService.put(StoreKey.currentUser, UserStub.admin)); verifyNever(() => mockUserRepo.update(UserStub.admin)); expect(result, isNull); }); @@ -103,10 +101,7 @@ void main() { final updatedUser = UserStub.admin.copyWith(profileImagePath: profileImagePath); when( - () => mockUserApiRepo.createProfileImage( - name: profileImagePath, - data: Uint8List(0), - ), + () => mockUserApiRepo.createProfileImage(name: profileImagePath, data: Uint8List(0)), ).thenAnswer((_) async => profileImagePath); when(() => mockStoreService.put(StoreKey.currentUser, updatedUser)).thenAnswer((_) async => true); when(() => mockUserRepo.update(updatedUser)).thenAnswer((_) async => UserStub.admin); @@ -123,16 +118,11 @@ void main() { final updatedUser = UserStub.admin.copyWith(profileImagePath: profileImagePath); when( - () => mockUserApiRepo.createProfileImage( - name: profileImagePath, - data: Uint8List(0), - ), + () => mockUserApiRepo.createProfileImage(name: profileImagePath, data: Uint8List(0)), ).thenThrow(Exception('Failed to create profile image')); final result = await sut.createProfileImage(profileImagePath, Uint8List(0)); - verifyNever( - () => mockStoreService.put(StoreKey.currentUser, updatedUser), - ); + verifyNever(() => mockStoreService.put(StoreKey.currentUser, updatedUser)); verifyNever(() => mockUserRepo.update(updatedUser)); expect(result, isNull); }); diff --git a/mobile/test/drift/main/generated/schema_v1.dart b/mobile/test/drift/main/generated/schema_v1.dart index 75f3bdee4c..ca9e6ca1b0 100644 --- a/mobile/test/drift/main/generated/schema_v1.dart +++ b/mobile/test/drift/main/generated/schema_v1.dart @@ -8,30 +8,79 @@ class UserEntity extends Table with TableInfo { final GeneratedDatabase attachedDatabase; final String? _alias; UserEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isAdmin = GeneratedColumn('is_admin', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_admin" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn email = - GeneratedColumn('email', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn profileImagePath = GeneratedColumn('profile_image_path', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn quotaSizeInBytes = GeneratedColumn('quota_size_in_bytes', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn quotaUsageInBytes = GeneratedColumn('quota_usage_in_bytes', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: false, defaultValue: const CustomExpression('0')); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn profileImagePath = GeneratedColumn( + 'profile_image_path', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn quotaSizeInBytes = GeneratedColumn( + 'quota_size_in_bytes', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn quotaUsageInBytes = GeneratedColumn( + 'quota_usage_in_bytes', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); @override - List get $columns => - [id, name, isAdmin, email, profileImagePath, updatedAt, quotaSizeInBytes, quotaUsageInBytes]; + List get $columns => [ + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -43,17 +92,38 @@ class UserEntity extends Table with TableInfo { UserEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return UserEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - isAdmin: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_admin'])!, - email: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}email'])!, - profileImagePath: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}profile_image_path']), - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - quotaSizeInBytes: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}quota_size_in_bytes']), - quotaUsageInBytes: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}quota_usage_in_bytes'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + profileImagePath: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}profile_image_path'], + ), + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + quotaSizeInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_size_in_bytes'], + ), + quotaUsageInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_usage_in_bytes'], + )!, ); } @@ -77,15 +147,16 @@ class UserEntityData extends DataClass implements Insertable { final DateTime updatedAt; final int? quotaSizeInBytes; final int quotaUsageInBytes; - const UserEntityData( - {required this.id, - required this.name, - required this.isAdmin, - required this.email, - this.profileImagePath, - required this.updatedAt, - this.quotaSizeInBytes, - required this.quotaUsageInBytes}); + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + this.profileImagePath, + required this.updatedAt, + this.quotaSizeInBytes, + required this.quotaUsageInBytes, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -104,7 +175,10 @@ class UserEntityData extends DataClass implements Insertable { return map; } - factory UserEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return UserEntityData( id: serializer.fromJson(json['id']), @@ -132,35 +206,45 @@ class UserEntityData extends DataClass implements Insertable { }; } - UserEntityData copyWith( - {String? id, - String? name, - bool? isAdmin, - String? email, - Value profileImagePath = const Value.absent(), - DateTime? updatedAt, - Value quotaSizeInBytes = const Value.absent(), - int? quotaUsageInBytes}) => - UserEntityData( - id: id ?? this.id, - name: name ?? this.name, - isAdmin: isAdmin ?? this.isAdmin, - email: email ?? this.email, - profileImagePath: profileImagePath.present ? profileImagePath.value : this.profileImagePath, - updatedAt: updatedAt ?? this.updatedAt, - quotaSizeInBytes: quotaSizeInBytes.present ? quotaSizeInBytes.value : this.quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, - ); + UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + Value profileImagePath = const Value.absent(), + DateTime? updatedAt, + Value quotaSizeInBytes = const Value.absent(), + int? quotaUsageInBytes, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath.present + ? profileImagePath.value + : this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes.present + ? quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); UserEntityData copyWithCompanion(UserEntityCompanion data) { return UserEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, email: data.email.present ? data.email.value : this.email, - profileImagePath: data.profileImagePath.present ? data.profileImagePath.value : this.profileImagePath, + profileImagePath: data.profileImagePath.present + ? data.profileImagePath.value + : this.profileImagePath, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - quotaSizeInBytes: data.quotaSizeInBytes.present ? data.quotaSizeInBytes.value : this.quotaSizeInBytes, - quotaUsageInBytes: data.quotaUsageInBytes.present ? data.quotaUsageInBytes.value : this.quotaUsageInBytes, + quotaSizeInBytes: data.quotaSizeInBytes.present + ? data.quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: data.quotaUsageInBytes.present + ? data.quotaUsageInBytes.value + : this.quotaUsageInBytes, ); } @@ -180,8 +264,16 @@ class UserEntityData extends DataClass implements Insertable { } @override - int get hashCode => - Object.hash(id, name, isAdmin, email, profileImagePath, updatedAt, quotaSizeInBytes, quotaUsageInBytes); + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -224,9 +316,9 @@ class UserEntityCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), this.quotaSizeInBytes = const Value.absent(), this.quotaUsageInBytes = const Value.absent(), - }) : id = Value(id), - name = Value(name), - email = Value(email); + }) : id = Value(id), + name = Value(name), + email = Value(email); static Insertable custom({ Expression? id, Expression? name, @@ -249,15 +341,16 @@ class UserEntityCompanion extends UpdateCompanion { }); } - UserEntityCompanion copyWith( - {Value? id, - Value? name, - Value? isAdmin, - Value? email, - Value? profileImagePath, - Value? updatedAt, - Value? quotaSizeInBytes, - Value? quotaUsageInBytes}) { + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? profileImagePath, + Value? updatedAt, + Value? quotaSizeInBytes, + Value? quotaUsageInBytes, + }) { return UserEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -316,75 +409,161 @@ class UserEntityCompanion extends UpdateCompanion { } } -class RemoteAssetEntity extends Table with TableInfo { +class RemoteAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn type = - GeneratedColumn('type', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn width = - GeneratedColumn('width', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn height = - GeneratedColumn('height', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn durationInSeconds = GeneratedColumn('duration_in_seconds', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn checksum = - GeneratedColumn('checksum', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isFavorite = GeneratedColumn('is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn localDateTime = GeneratedColumn('local_date_time', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn thumbHash = - GeneratedColumn('thumb_hash', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn deletedAt = GeneratedColumn('deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn localDateTime = + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn livePhotoVideoId = GeneratedColumn( - 'live_photo_video_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn visibility = - GeneratedColumn('visibility', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn stackId = - GeneratedColumn('stack_id', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - ownerId, - localDateTime, - thumbHash, - deletedAt, - livePhotoVideoId, - visibility, - stackId - ]; + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -396,26 +575,74 @@ class RemoteAssetEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAssetEntityData( - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - type: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}type'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}height']), - durationInSeconds: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}checksum'])!, - isFavorite: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - localDateTime: - attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}local_date_time']), - thumbHash: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}thumb_hash']), - deletedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), - livePhotoVideoId: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}live_photo_video_id']), - visibility: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}visibility'])!, - stackId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}stack_id']), + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), ); } @@ -430,7 +657,8 @@ class RemoteAssetEntity extends Table with TableInfo true; } -class RemoteAssetEntityData extends DataClass implements Insertable { +class RemoteAssetEntityData extends DataClass + implements Insertable { final String name; final int type; final DateTime createdAt; @@ -448,24 +676,25 @@ class RemoteAssetEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -505,7 +734,10 @@ class RemoteAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAssetEntityData( name: serializer.fromJson(json['name']), @@ -551,43 +783,49 @@ class RemoteAssetEntityData extends DataClass implements Insertable width = const Value.absent(), - Value height = const Value.absent(), - Value durationInSeconds = const Value.absent(), - String? id, - String? checksum, - bool? isFavorite, - String? ownerId, - Value localDateTime = const Value.absent(), - Value thumbHash = const Value.absent(), - Value deletedAt = const Value.absent(), - Value livePhotoVideoId = const Value.absent(), - int? visibility, - Value stackId = const Value.absent()}) => - RemoteAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present ? durationInSeconds.value : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum ?? this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - ownerId: ownerId ?? this.ownerId, - localDateTime: localDateTime.present ? localDateTime.value : this.localDateTime, - thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - livePhotoVideoId: livePhotoVideoId.present ? livePhotoVideoId.value : this.livePhotoVideoId, - visibility: visibility ?? this.visibility, - stackId: stackId.present ? stackId.value : this.stackId, - ); + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + ); RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { return RemoteAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -596,16 +834,26 @@ class RemoteAssetEntityData extends DataClass implements Insertable Object.hash(name, type, createdAt, updatedAt, width, height, durationInSeconds, id, checksum, - isFavorite, ownerId, localDateTime, thumbHash, deletedAt, livePhotoVideoId, visibility, stackId); + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -660,7 +925,8 @@ class RemoteAssetEntityData extends DataClass implements Insertable { +class RemoteAssetEntityCompanion + extends UpdateCompanion { final Value name; final Value type; final Value createdAt; @@ -715,12 +981,12 @@ class RemoteAssetEntityCompanion extends UpdateCompanion this.livePhotoVideoId = const Value.absent(), required int visibility, this.stackId = const Value.absent(), - }) : name = Value(name), - type = Value(type), - id = Value(id), - checksum = Value(checksum), - ownerId = Value(ownerId), - visibility = Value(visibility); + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); static Insertable custom({ Expression? name, Expression? type, @@ -761,24 +1027,25 @@ class RemoteAssetEntityCompanion extends UpdateCompanion }); } - RemoteAssetEntityCompanion copyWith( - {Value? name, - Value? type, - Value? createdAt, - Value? updatedAt, - Value? width, - Value? height, - Value? durationInSeconds, - Value? id, - Value? checksum, - Value? isFavorite, - Value? ownerId, - Value? localDateTime, - Value? thumbHash, - Value? deletedAt, - Value? livePhotoVideoId, - Value? visibility, - Value? stackId}) { + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + }) { return RemoteAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -882,43 +1149,110 @@ class RemoteAssetEntityCompanion extends UpdateCompanion } } -class LocalAssetEntity extends Table with TableInfo { +class LocalAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; LocalAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn type = - GeneratedColumn('type', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn width = - GeneratedColumn('width', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn height = - GeneratedColumn('height', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn durationInSeconds = GeneratedColumn('duration_in_seconds', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn checksum = - GeneratedColumn('checksum', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn isFavorite = GeneratedColumn('is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn orientation = GeneratedColumn('orientation', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: false, defaultValue: const CustomExpression('0')); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); @override - List get $columns => - [name, type, createdAt, updatedAt, width, height, durationInSeconds, id, checksum, isFavorite, orientation]; + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -930,18 +1264,50 @@ class LocalAssetEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAssetEntityData( - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - type: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}type'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}height']), - durationInSeconds: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}checksum']), - isFavorite: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - orientation: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}orientation'])!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, ); } @@ -956,7 +1322,8 @@ class LocalAssetEntity extends Table with TableInfo true; } -class LocalAssetEntityData extends DataClass implements Insertable { +class LocalAssetEntityData extends DataClass + implements Insertable { final String name; final int type; final DateTime createdAt; @@ -968,18 +1335,19 @@ class LocalAssetEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -1005,7 +1373,10 @@ class LocalAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAssetEntityData( name: serializer.fromJson(json['name']), @@ -1039,31 +1410,33 @@ class LocalAssetEntityData extends DataClass implements Insertable width = const Value.absent(), - Value height = const Value.absent(), - Value durationInSeconds = const Value.absent(), - String? id, - Value checksum = const Value.absent(), - bool? isFavorite, - int? orientation}) => - LocalAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present ? durationInSeconds.value : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum.present ? checksum.value : this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - orientation: orientation ?? this.orientation, - ); + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { return LocalAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -1072,11 +1445,17 @@ class LocalAssetEntityData extends DataClass implements Insertable Object.hash( - name, type, createdAt, updatedAt, width, height, durationInSeconds, id, checksum, isFavorite, orientation); + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -1155,9 +1545,9 @@ class LocalAssetEntityCompanion extends UpdateCompanion { this.checksum = const Value.absent(), this.isFavorite = const Value.absent(), this.orientation = const Value.absent(), - }) : name = Value(name), - type = Value(type), - id = Value(id); + }) : name = Value(name), + type = Value(type), + id = Value(id); static Insertable custom({ Expression? name, Expression? type, @@ -1186,18 +1576,19 @@ class LocalAssetEntityCompanion extends UpdateCompanion { }); } - LocalAssetEntityCompanion copyWith( - {Value? name, - Value? type, - Value? createdAt, - Value? updatedAt, - Value? width, - Value? height, - Value? durationInSeconds, - Value? id, - Value? checksum, - Value? isFavorite, - Value? orientation}) { + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { return LocalAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -1276,26 +1667,57 @@ class StackEntity extends Table with TableInfo { final GeneratedDatabase attachedDatabase; final String? _alias; StackEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn primaryAssetId = GeneratedColumn('primary_asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id)')); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id)', + ), + ); @override - List get $columns => [id, createdAt, updatedAt, ownerId, primaryAssetId]; + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -1307,12 +1729,26 @@ class StackEntity extends Table with TableInfo { StackEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return StackEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - primaryAssetId: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, ); } @@ -1333,12 +1769,13 @@ class StackEntityData extends DataClass implements Insertable { final DateTime updatedAt; final String ownerId; final String primaryAssetId; - const StackEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.primaryAssetId}); + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1350,7 +1787,10 @@ class StackEntityData extends DataClass implements Insertable { return map; } - factory StackEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return StackEntityData( id: serializer.fromJson(json['id']), @@ -1372,22 +1812,28 @@ class StackEntityData extends DataClass implements Insertable { }; } - StackEntityData copyWith( - {String? id, DateTime? createdAt, DateTime? updatedAt, String? ownerId, String? primaryAssetId}) => - StackEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - primaryAssetId: primaryAssetId ?? this.primaryAssetId, - ); + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); StackEntityData copyWithCompanion(StackEntityCompanion data) { return StackEntityData( id: data.id.present ? data.id.value : this.id, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, - primaryAssetId: data.primaryAssetId.present ? data.primaryAssetId.value : this.primaryAssetId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, ); } @@ -1404,7 +1850,8 @@ class StackEntityData extends DataClass implements Insertable { } @override - int get hashCode => Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); @override bool operator ==(Object other) => identical(this, other) || @@ -1435,9 +1882,9 @@ class StackEntityCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), required String ownerId, required String primaryAssetId, - }) : id = Value(id), - ownerId = Value(ownerId), - primaryAssetId = Value(primaryAssetId); + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); static Insertable custom({ Expression? id, Expression? createdAt, @@ -1454,12 +1901,13 @@ class StackEntityCompanion extends UpdateCompanion { }); } - StackEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? primaryAssetId}) { + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { return StackEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -1503,19 +1951,36 @@ class StackEntityCompanion extends UpdateCompanion { } } -class UserMetadataEntity extends Table with TableInfo { +class UserMetadataEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; UserMetadataEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn userId = GeneratedColumn('user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn key = - GeneratedColumn('key', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn value = - GeneratedColumn('value', aliasedName, false, type: DriftSqlType.blob, requiredDuringInsert: true); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); @override List get $columns => [userId, key, value]; @override @@ -1529,9 +1994,18 @@ class UserMetadataEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return UserMetadataEntityData( - userId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - key: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}key'])!, - value: attachedDatabase.typeMapping.read(DriftSqlType.blob, data['${effectivePrefix}value'])!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, ); } @@ -1546,11 +2020,16 @@ class UserMetadataEntity extends Table with TableInfo true; } -class UserMetadataEntityData extends DataClass implements Insertable { +class UserMetadataEntityData extends DataClass + implements Insertable { final String userId; final int key; final Uint8List value; - const UserMetadataEntityData({required this.userId, required this.key, required this.value}); + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1560,7 +2039,10 @@ class UserMetadataEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return UserMetadataEntityData( userId: serializer.fromJson(json['userId']), @@ -1578,11 +2060,15 @@ class UserMetadataEntityData extends DataClass implements Insertable UserMetadataEntityData( - userId: userId ?? this.userId, - key: key ?? this.key, - value: value ?? this.value, - ); + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { return UserMetadataEntityData( userId: data.userId.present ? data.userId.value : this.userId, @@ -1612,7 +2098,8 @@ class UserMetadataEntityData extends DataClass implements Insertable { +class UserMetadataEntityCompanion + extends UpdateCompanion { final Value userId; final Value key; final Value value; @@ -1625,9 +2112,9 @@ class UserMetadataEntityCompanion extends UpdateCompanion custom({ Expression? userId, Expression? key, @@ -1640,7 +2127,11 @@ class UserMetadataEntityCompanion extends UpdateCompanion? userId, Value? key, Value? value}) { + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { return UserMetadataEntityCompanion( userId: userId ?? this.userId, key: key ?? this.key, @@ -1674,24 +2165,43 @@ class UserMetadataEntityCompanion extends UpdateCompanion { +class PartnerEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; PartnerEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn sharedById = GeneratedColumn('shared_by_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn sharedWithId = GeneratedColumn('shared_with_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn inTimeline = GeneratedColumn('in_timeline', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("in_timeline" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); @override List get $columns => [sharedById, sharedWithId, inTimeline]; @override @@ -1705,9 +2215,18 @@ class PartnerEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return PartnerEntityData( - sharedById: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}shared_by_id'])!, - sharedWithId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}shared_with_id'])!, - inTimeline: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!, + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, ); } @@ -1722,11 +2241,16 @@ class PartnerEntity extends Table with TableInfo true; } -class PartnerEntityData extends DataClass implements Insertable { +class PartnerEntityData extends DataClass + implements Insertable { final String sharedById; final String sharedWithId; final bool inTimeline; - const PartnerEntityData({required this.sharedById, required this.sharedWithId, required this.inTimeline}); + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1736,7 +2260,10 @@ class PartnerEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return PartnerEntityData( sharedById: serializer.fromJson(json['sharedById']), @@ -1754,16 +2281,26 @@ class PartnerEntityData extends DataClass implements Insertable PartnerEntityData( - sharedById: sharedById ?? this.sharedById, - sharedWithId: sharedWithId ?? this.sharedWithId, - inTimeline: inTimeline ?? this.inTimeline, - ); + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { return PartnerEntityData( - sharedById: data.sharedById.present ? data.sharedById.value : this.sharedById, - sharedWithId: data.sharedWithId.present ? data.sharedWithId.value : this.sharedWithId, - inTimeline: data.inTimeline.present ? data.inTimeline.value : this.inTimeline, + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, ); } @@ -1801,8 +2338,8 @@ class PartnerEntityCompanion extends UpdateCompanion { required String sharedById, required String sharedWithId, this.inTimeline = const Value.absent(), - }) : sharedById = Value(sharedById), - sharedWithId = Value(sharedWithId); + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); static Insertable custom({ Expression? sharedById, Expression? sharedWithId, @@ -1815,7 +2352,11 @@ class PartnerEntityCompanion extends UpdateCompanion { }); } - PartnerEntityCompanion copyWith({Value? sharedById, Value? sharedWithId, Value? inTimeline}) { + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { return PartnerEntityCompanion( sharedById: sharedById ?? this.sharedById, sharedWithId: sharedWithId ?? this.sharedWithId, @@ -1849,32 +2390,71 @@ class PartnerEntityCompanion extends UpdateCompanion { } } -class LocalAlbumEntity extends Table with TableInfo { +class LocalAlbumEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; LocalAlbumEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn backupSelection = - GeneratedColumn('backup_selection', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn isIosSharedAlbum = GeneratedColumn('is_ios_shared_album', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_ios_shared_album" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn marker_ = GeneratedColumn('marker', aliasedName, true, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("marker" IN (0, 1))')); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); @override - List get $columns => [id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_]; + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -1886,13 +2466,30 @@ class LocalAlbumEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAlbumEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - backupSelection: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}backup_selection'])!, - isIosSharedAlbum: - attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_ios_shared_album'])!, - marker_: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}marker']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), ); } @@ -1907,20 +2504,22 @@ class LocalAlbumEntity extends Table with TableInfo true; } -class LocalAlbumEntityData extends DataClass implements Insertable { +class LocalAlbumEntityData extends DataClass + implements Insertable { final String id; final String name; final DateTime updatedAt; final int backupSelection; final bool isIosSharedAlbum; final bool? marker_; - const LocalAlbumEntityData( - {required this.id, - required this.name, - required this.updatedAt, - required this.backupSelection, - required this.isIosSharedAlbum, - this.marker_}); + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1935,7 +2534,10 @@ class LocalAlbumEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAlbumEntityData( id: serializer.fromJson(json['id']), @@ -1959,28 +2561,32 @@ class LocalAlbumEntityData extends DataClass implements Insertable marker_ = const Value.absent()}) => - LocalAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - updatedAt: updatedAt ?? this.updatedAt, - backupSelection: backupSelection ?? this.backupSelection, - isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, - marker_: marker_.present ? marker_.value : this.marker_, - ); + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { return LocalAlbumEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - backupSelection: data.backupSelection.present ? data.backupSelection.value : this.backupSelection, - isIosSharedAlbum: data.isIosSharedAlbum.present ? data.isIosSharedAlbum.value : this.isIosSharedAlbum, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, marker_: data.marker_.present ? data.marker_.value : this.marker_, ); } @@ -1999,7 +2605,14 @@ class LocalAlbumEntityData extends DataClass implements Insertable Object.hash(id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_); + int get hashCode => Object.hash( + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -2034,9 +2647,9 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { required int backupSelection, this.isIosSharedAlbum = const Value.absent(), this.marker_ = const Value.absent(), - }) : id = Value(id), - name = Value(name), - backupSelection = Value(backupSelection); + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); static Insertable custom({ Expression? id, Expression? name, @@ -2055,13 +2668,14 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { }); } - LocalAlbumEntityCompanion copyWith( - {Value? id, - Value? name, - Value? updatedAt, - Value? backupSelection, - Value? isIosSharedAlbum, - Value? marker_}) { + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_, + }) { return LocalAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -2110,19 +2724,32 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { } } -class LocalAlbumAssetEntity extends Table with TableInfo { +class LocalAlbumAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES local_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn albumId = GeneratedColumn('album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES local_album_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -2133,11 +2760,20 @@ class LocalAlbumAssetEntity extends Table with TableInfo get $primaryKey => {assetId, albumId}; @override - LocalAlbumAssetEntityData map(Map data, {String? tablePrefix}) { + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -2152,10 +2788,14 @@ class LocalAlbumAssetEntity extends Table with TableInfo true; } -class LocalAlbumAssetEntityData extends DataClass implements Insertable { +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { final String assetId; final String albumId; - const LocalAlbumAssetEntityData({required this.assetId, required this.albumId}); + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -2164,7 +2804,10 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -2180,11 +2823,14 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable LocalAlbumAssetEntityData( + LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + LocalAlbumAssetEntityData( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, ); - LocalAlbumAssetEntityData copyWithCompanion(LocalAlbumAssetEntityCompanion data) { + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data, + ) { return LocalAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -2205,10 +2851,13 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable identical(this, other) || - (other is LocalAlbumAssetEntityData && other.assetId == this.assetId && other.albumId == this.albumId); + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); } -class LocalAlbumAssetEntityCompanion extends UpdateCompanion { +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { final Value assetId; final Value albumId; const LocalAlbumAssetEntityCompanion({ @@ -2218,8 +2867,8 @@ class LocalAlbumAssetEntityCompanion extends UpdateCompanion custom({ Expression? assetId, Expression? albumId, @@ -2230,7 +2879,10 @@ class LocalAlbumAssetEntityCompanion extends UpdateCompanion? assetId, Value? albumId}) { + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { return LocalAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, @@ -2259,83 +2911,195 @@ class LocalAlbumAssetEntityCompanion extends UpdateCompanion { +class RemoteExifEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteExifEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn city = - GeneratedColumn('city', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn state = - GeneratedColumn('state', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn country = - GeneratedColumn('country', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn dateTimeOriginal = GeneratedColumn( - 'date_time_original', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn description = - GeneratedColumn('description', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn height = - GeneratedColumn('height', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn width = - GeneratedColumn('width', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn exposureTime = GeneratedColumn('exposure_time', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn fNumber = - GeneratedColumn('f_number', aliasedName, true, type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn fileSize = - GeneratedColumn('file_size', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn focalLength = GeneratedColumn('focal_length', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn latitude = - GeneratedColumn('latitude', aliasedName, true, type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn longitude = - GeneratedColumn('longitude', aliasedName, true, type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn iso = - GeneratedColumn('iso', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn make = - GeneratedColumn('make', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn model = - GeneratedColumn('model', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn lens = - GeneratedColumn('lens', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn orientation = - GeneratedColumn('orientation', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn timeZone = - GeneratedColumn('time_zone', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn rating = - GeneratedColumn('rating', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn projectionType = GeneratedColumn('projection_type', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn city = GeneratedColumn( + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn state = GeneratedColumn( + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn country = GeneratedColumn( + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn make = GeneratedColumn( + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn model = GeneratedColumn( + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]; + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -2347,29 +3111,94 @@ class RemoteExifEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteExifEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - city: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}city']), - state: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}state']), - country: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}country']), - dateTimeOriginal: - attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}date_time_original']), - description: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}description']), - height: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}height']), - width: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}width']), - exposureTime: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}exposure_time']), - fNumber: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}f_number']), - fileSize: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}file_size']), - focalLength: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}focal_length']), - latitude: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}latitude']), - longitude: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}longitude']), - iso: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}iso']), - make: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}make']), - model: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}model']), - lens: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}lens']), - orientation: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}orientation']), - timeZone: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}time_zone']), - rating: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}rating']), - projectionType: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}projection_type']), + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), ); } @@ -2384,7 +3213,8 @@ class RemoteExifEntity extends Table with TableInfo true; } -class RemoteExifEntityData extends DataClass implements Insertable { +class RemoteExifEntityData extends DataClass + implements Insertable { final String assetId; final String? city; final String? state; @@ -2407,29 +3237,30 @@ class RemoteExifEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -2500,14 +3331,19 @@ class RemoteExifEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteExifEntityData( assetId: serializer.fromJson(json['assetId']), city: serializer.fromJson(json['city']), state: serializer.fromJson(json['state']), country: serializer.fromJson(json['country']), - dateTimeOriginal: serializer.fromJson(json['dateTimeOriginal']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), description: serializer.fromJson(json['description']), height: serializer.fromJson(json['height']), width: serializer.fromJson(json['width']), @@ -2556,77 +3392,93 @@ class RemoteExifEntityData extends DataClass implements Insertable city = const Value.absent(), - Value state = const Value.absent(), - Value country = const Value.absent(), - Value dateTimeOriginal = const Value.absent(), - Value description = const Value.absent(), - Value height = const Value.absent(), - Value width = const Value.absent(), - Value exposureTime = const Value.absent(), - Value fNumber = const Value.absent(), - Value fileSize = const Value.absent(), - Value focalLength = const Value.absent(), - Value latitude = const Value.absent(), - Value longitude = const Value.absent(), - Value iso = const Value.absent(), - Value make = const Value.absent(), - Value model = const Value.absent(), - Value lens = const Value.absent(), - Value orientation = const Value.absent(), - Value timeZone = const Value.absent(), - Value rating = const Value.absent(), - Value projectionType = const Value.absent()}) => - RemoteExifEntityData( - assetId: assetId ?? this.assetId, - city: city.present ? city.value : this.city, - state: state.present ? state.value : this.state, - country: country.present ? country.value : this.country, - dateTimeOriginal: dateTimeOriginal.present ? dateTimeOriginal.value : this.dateTimeOriginal, - description: description.present ? description.value : this.description, - height: height.present ? height.value : this.height, - width: width.present ? width.value : this.width, - exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, - fNumber: fNumber.present ? fNumber.value : this.fNumber, - fileSize: fileSize.present ? fileSize.value : this.fileSize, - focalLength: focalLength.present ? focalLength.value : this.focalLength, - latitude: latitude.present ? latitude.value : this.latitude, - longitude: longitude.present ? longitude.value : this.longitude, - iso: iso.present ? iso.value : this.iso, - make: make.present ? make.value : this.make, - model: model.present ? model.value : this.model, - lens: lens.present ? lens.value : this.lens, - orientation: orientation.present ? orientation.value : this.orientation, - timeZone: timeZone.present ? timeZone.value : this.timeZone, - rating: rating.present ? rating.value : this.rating, - projectionType: projectionType.present ? projectionType.value : this.projectionType, - ); + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { return RemoteExifEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, city: data.city.present ? data.city.value : this.city, state: data.state.present ? data.state.value : this.state, country: data.country.present ? data.country.value : this.country, - dateTimeOriginal: data.dateTimeOriginal.present ? data.dateTimeOriginal.value : this.dateTimeOriginal, - description: data.description.present ? data.description.value : this.description, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: data.description.present + ? data.description.value + : this.description, height: data.height.present ? data.height.value : this.height, width: data.width.present ? data.width.value : this.width, - exposureTime: data.exposureTime.present ? data.exposureTime.value : this.exposureTime, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, - focalLength: data.focalLength.present ? data.focalLength.value : this.focalLength, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, latitude: data.latitude.present ? data.latitude.value : this.latitude, longitude: data.longitude.present ? data.longitude.value : this.longitude, iso: data.iso.present ? data.iso.value : this.iso, make: data.make.present ? data.make.value : this.make, model: data.model.present ? data.model.value : this.model, lens: data.lens.present ? data.lens.value : this.lens, - orientation: data.orientation.present ? data.orientation.value : this.orientation, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, rating: data.rating.present ? data.rating.value : this.rating, - projectionType: data.projectionType.present ? data.projectionType.value : this.projectionType, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, ); } @@ -2661,29 +3513,29 @@ class RemoteExifEntityData extends DataClass implements Insertable Object.hashAll([ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]); + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); @override bool operator ==(Object other) => identical(this, other) || @@ -2833,29 +3685,30 @@ class RemoteExifEntityCompanion extends UpdateCompanion { }); } - RemoteExifEntityCompanion copyWith( - {Value? assetId, - Value? city, - Value? state, - Value? country, - Value? dateTimeOriginal, - Value? description, - Value? height, - Value? width, - Value? exposureTime, - Value? fNumber, - Value? fileSize, - Value? focalLength, - Value? latitude, - Value? longitude, - Value? iso, - Value? make, - Value? model, - Value? lens, - Value? orientation, - Value? timeZone, - Value? rating, - Value? projectionType}) { + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { return RemoteExifEntityCompanion( assetId: assetId ?? this.assetId, city: city ?? this.city, @@ -2984,43 +3837,100 @@ class RemoteExifEntityCompanion extends UpdateCompanion { } } -class RemoteAlbumEntity extends Table with TableInfo { +class RemoteAlbumEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAlbumEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn description = GeneratedColumn('description', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: false, defaultValue: const CustomExpression('\'\'')); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn thumbnailAssetId = GeneratedColumn('thumbnail_asset_id', aliasedName, true, - type: DriftSqlType.string, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE SET NULL')); - late final GeneratedColumn isActivityEnabled = GeneratedColumn('is_activity_enabled', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_activity_enabled" IN (0, 1))'), - defaultValue: const CustomExpression('1')); - late final GeneratedColumn order = - GeneratedColumn('order', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn order = GeneratedColumn( + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); @override - List get $columns => - [id, name, description, createdAt, updatedAt, ownerId, thumbnailAssetId, isActivityEnabled, order]; + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -3032,17 +3942,42 @@ class RemoteAlbumEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - description: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}description'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - thumbnailAssetId: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}thumbnail_asset_id']), - isActivityEnabled: - attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_activity_enabled'])!, - order: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}order'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, ); } @@ -3057,7 +3992,8 @@ class RemoteAlbumEntity extends Table with TableInfo true; } -class RemoteAlbumEntityData extends DataClass implements Insertable { +class RemoteAlbumEntityData extends DataClass + implements Insertable { final String id; final String name; final String description; @@ -3067,16 +4003,17 @@ class RemoteAlbumEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -3094,7 +4031,10 @@ class RemoteAlbumEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumEntityData( id: serializer.fromJson(json['id']), @@ -3124,37 +4064,45 @@ class RemoteAlbumEntityData extends DataClass implements Insertable thumbnailAssetId = const Value.absent(), - bool? isActivityEnabled, - int? order}) => - RemoteAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - description: description ?? this.description, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - thumbnailAssetId: thumbnailAssetId.present ? thumbnailAssetId.value : this.thumbnailAssetId, - isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, - order: order ?? this.order, - ); + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { return RemoteAlbumEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, - description: data.description.present ? data.description.value : this.description, + description: data.description.present + ? data.description.value + : this.description, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, - thumbnailAssetId: data.thumbnailAssetId.present ? data.thumbnailAssetId.value : this.thumbnailAssetId, - isActivityEnabled: data.isActivityEnabled.present ? data.isActivityEnabled.value : this.isActivityEnabled, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, order: data.order.present ? data.order.value : this.order, ); } @@ -3176,8 +4124,17 @@ class RemoteAlbumEntityData extends DataClass implements Insertable - Object.hash(id, name, description, createdAt, updatedAt, ownerId, thumbnailAssetId, isActivityEnabled, order); + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -3193,7 +4150,8 @@ class RemoteAlbumEntityData extends DataClass implements Insertable { +class RemoteAlbumEntityCompanion + extends UpdateCompanion { final Value id; final Value name; final Value description; @@ -3224,10 +4182,10 @@ class RemoteAlbumEntityCompanion extends UpdateCompanion this.thumbnailAssetId = const Value.absent(), this.isActivityEnabled = const Value.absent(), required int order, - }) : id = Value(id), - name = Value(name), - ownerId = Value(ownerId), - order = Value(order); + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); static Insertable custom({ Expression? id, Expression? name, @@ -3252,16 +4210,17 @@ class RemoteAlbumEntityCompanion extends UpdateCompanion }); } - RemoteAlbumEntityCompanion copyWith( - {Value? id, - Value? name, - Value? description, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? thumbnailAssetId, - Value? isActivityEnabled, - Value? order}) { + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { return RemoteAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -3325,19 +4284,32 @@ class RemoteAlbumEntityCompanion extends UpdateCompanion } } -class RemoteAlbumAssetEntity extends Table with TableInfo { +class RemoteAlbumAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn albumId = GeneratedColumn('album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -3348,11 +4320,20 @@ class RemoteAlbumAssetEntity extends Table with TableInfo get $primaryKey => {assetId, albumId}; @override - RemoteAlbumAssetEntityData map(Map data, {String? tablePrefix}) { + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -3367,10 +4348,14 @@ class RemoteAlbumAssetEntity extends Table with TableInfo true; } -class RemoteAlbumAssetEntityData extends DataClass implements Insertable { +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { final String assetId; final String albumId; - const RemoteAlbumAssetEntityData({required this.assetId, required this.albumId}); + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3379,7 +4364,10 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -3395,11 +4383,14 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable RemoteAlbumAssetEntityData( + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, ); - RemoteAlbumAssetEntityData copyWithCompanion(RemoteAlbumAssetEntityCompanion data) { + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data, + ) { return RemoteAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -3420,10 +4411,13 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable identical(this, other) || - (other is RemoteAlbumAssetEntityData && other.assetId == this.assetId && other.albumId == this.albumId); + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); } -class RemoteAlbumAssetEntityCompanion extends UpdateCompanion { +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { final Value assetId; final Value albumId; const RemoteAlbumAssetEntityCompanion({ @@ -3433,8 +4427,8 @@ class RemoteAlbumAssetEntityCompanion extends UpdateCompanion custom({ Expression? assetId, Expression? albumId, @@ -3445,7 +4439,10 @@ class RemoteAlbumAssetEntityCompanion extends UpdateCompanion? assetId, Value? albumId}) { + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { return RemoteAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, @@ -3474,21 +4471,39 @@ class RemoteAlbumAssetEntityCompanion extends UpdateCompanion { +class RemoteAlbumUserEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn albumId = GeneratedColumn('album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_album_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn userId = GeneratedColumn('user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn role = - GeneratedColumn('role', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn role = GeneratedColumn( + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); @override List get $columns => [albumId, userId, role]; @override @@ -3499,12 +4514,24 @@ class RemoteAlbumUserEntity extends Table with TableInfo get $primaryKey => {albumId, userId}; @override - RemoteAlbumUserEntityData map(Map data, {String? tablePrefix}) { + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumUserEntityData( - albumId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, - userId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - role: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}role'])!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, ); } @@ -3519,11 +4546,16 @@ class RemoteAlbumUserEntity extends Table with TableInfo true; } -class RemoteAlbumUserEntityData extends DataClass implements Insertable { +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { final String albumId; final String userId; final int role; - const RemoteAlbumUserEntityData({required this.albumId, required this.userId, required this.role}); + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3533,7 +4565,10 @@ class RemoteAlbumUserEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumUserEntityData( albumId: serializer.fromJson(json['albumId']), @@ -3551,12 +4586,18 @@ class RemoteAlbumUserEntityData extends DataClass implements Insertable RemoteAlbumUserEntityData( - albumId: albumId ?? this.albumId, - userId: userId ?? this.userId, - role: role ?? this.role, - ); - RemoteAlbumUserEntityData copyWithCompanion(RemoteAlbumUserEntityCompanion data) { + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data, + ) { return RemoteAlbumUserEntityData( albumId: data.albumId.present ? data.albumId.value : this.albumId, userId: data.userId.present ? data.userId.value : this.userId, @@ -3585,7 +4626,8 @@ class RemoteAlbumUserEntityData extends DataClass implements Insertable { +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { final Value albumId; final Value userId; final Value role; @@ -3598,9 +4640,9 @@ class RemoteAlbumUserEntityCompanion extends UpdateCompanion custom({ Expression? albumId, Expression? userId, @@ -3613,7 +4655,11 @@ class RemoteAlbumUserEntityCompanion extends UpdateCompanion? albumId, Value? userId, Value? role}) { + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { return RemoteAlbumUserEntityCompanion( albumId: albumId ?? this.albumId, userId: userId ?? this.userId, @@ -3647,47 +4693,120 @@ class RemoteAlbumUserEntityCompanion extends UpdateCompanion { +class MemoryEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; MemoryEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn deletedAt = GeneratedColumn('deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn type = - GeneratedColumn('type', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn data = - GeneratedColumn('data', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isSaved = GeneratedColumn('is_saved', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_saved" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn memoryAt = GeneratedColumn('memory_at', aliasedName, false, - type: DriftSqlType.dateTime, requiredDuringInsert: true); - late final GeneratedColumn seenAt = - GeneratedColumn('seen_at', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn showAt = - GeneratedColumn('show_at', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn hideAt = - GeneratedColumn('hide_at', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn data = GeneratedColumn( + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override - List get $columns => - [id, createdAt, updatedAt, deletedAt, ownerId, type, data, isSaved, memoryAt, seenAt, showAt, hideAt]; + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -3699,18 +4818,54 @@ class MemoryEntity extends Table with TableInfo MemoryEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return MemoryEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - deletedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - type: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}type'])!, - data: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}data'])!, - isSaved: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_saved'])!, - memoryAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}memory_at'])!, - seenAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}seen_at']), - showAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}show_at']), - hideAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}hide_at']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), ); } @@ -3725,7 +4880,8 @@ class MemoryEntity extends Table with TableInfo bool get isStrict => true; } -class MemoryEntityData extends DataClass implements Insertable { +class MemoryEntityData extends DataClass + implements Insertable { final String id; final DateTime createdAt; final DateTime updatedAt; @@ -3738,19 +4894,20 @@ class MemoryEntityData extends DataClass implements Insertable final DateTime? seenAt; final DateTime? showAt; final DateTime? hideAt; - const MemoryEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - this.deletedAt, - required this.ownerId, - required this.type, - required this.data, - required this.isSaved, - required this.memoryAt, - this.seenAt, - this.showAt, - this.hideAt}); + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3777,7 +4934,10 @@ class MemoryEntityData extends DataClass implements Insertable return map; } - factory MemoryEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return MemoryEntityData( id: serializer.fromJson(json['id']), @@ -3813,33 +4973,33 @@ class MemoryEntityData extends DataClass implements Insertable }; } - MemoryEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - Value deletedAt = const Value.absent(), - String? ownerId, - int? type, - String? data, - bool? isSaved, - DateTime? memoryAt, - Value seenAt = const Value.absent(), - Value showAt = const Value.absent(), - Value hideAt = const Value.absent()}) => - MemoryEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - ownerId: ownerId ?? this.ownerId, - type: type ?? this.type, - data: data ?? this.data, - isSaved: isSaved ?? this.isSaved, - memoryAt: memoryAt ?? this.memoryAt, - seenAt: seenAt.present ? seenAt.value : this.seenAt, - showAt: showAt.present ? showAt.value : this.showAt, - hideAt: hideAt.present ? hideAt.value : this.hideAt, - ); + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { return MemoryEntityData( id: data.id.present ? data.id.value : this.id, @@ -3877,8 +5037,20 @@ class MemoryEntityData extends DataClass implements Insertable } @override - int get hashCode => - Object.hash(id, createdAt, updatedAt, deletedAt, ownerId, type, data, isSaved, memoryAt, seenAt, showAt, hideAt); + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -3937,11 +5109,11 @@ class MemoryEntityCompanion extends UpdateCompanion { this.seenAt = const Value.absent(), this.showAt = const Value.absent(), this.hideAt = const Value.absent(), - }) : id = Value(id), - ownerId = Value(ownerId), - type = Value(type), - data = Value(data), - memoryAt = Value(memoryAt); + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); static Insertable custom({ Expression? id, Expression? createdAt, @@ -3972,19 +5144,20 @@ class MemoryEntityCompanion extends UpdateCompanion { }); } - MemoryEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? deletedAt, - Value? ownerId, - Value? type, - Value? data, - Value? isSaved, - Value? memoryAt, - Value? seenAt, - Value? showAt, - Value? hideAt}) { + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { return MemoryEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -4063,19 +5236,32 @@ class MemoryEntityCompanion extends UpdateCompanion { } } -class MemoryAssetEntity extends Table with TableInfo { +class MemoryAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; MemoryAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn memoryId = GeneratedColumn('memory_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES memory_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, memoryId]; @override @@ -4089,8 +5275,14 @@ class MemoryAssetEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return MemoryAssetEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - memoryId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}memory_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, ); } @@ -4105,7 +5297,8 @@ class MemoryAssetEntity extends Table with TableInfo true; } -class MemoryAssetEntityData extends DataClass implements Insertable { +class MemoryAssetEntityData extends DataClass + implements Insertable { final String assetId; final String memoryId; const MemoryAssetEntityData({required this.assetId, required this.memoryId}); @@ -4117,7 +5310,10 @@ class MemoryAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return MemoryAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -4133,7 +5329,8 @@ class MemoryAssetEntityData extends DataClass implements Insertable MemoryAssetEntityData( + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( assetId: assetId ?? this.assetId, memoryId: memoryId ?? this.memoryId, ); @@ -4158,10 +5355,13 @@ class MemoryAssetEntityData extends DataClass implements Insertable identical(this, other) || - (other is MemoryAssetEntityData && other.assetId == this.assetId && other.memoryId == this.memoryId); + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); } -class MemoryAssetEntityCompanion extends UpdateCompanion { +class MemoryAssetEntityCompanion + extends UpdateCompanion { final Value assetId; final Value memoryId; const MemoryAssetEntityCompanion({ @@ -4171,8 +5371,8 @@ class MemoryAssetEntityCompanion extends UpdateCompanion MemoryAssetEntityCompanion.insert({ required String assetId, required String memoryId, - }) : assetId = Value(assetId), - memoryId = Value(memoryId); + }) : assetId = Value(assetId), + memoryId = Value(memoryId); static Insertable custom({ Expression? assetId, Expression? memoryId, @@ -4183,7 +5383,10 @@ class MemoryAssetEntityCompanion extends UpdateCompanion }); } - MemoryAssetEntityCompanion copyWith({Value? assetId, Value? memoryId}) { + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { return MemoryAssetEntityCompanion( assetId: assetId ?? this.assetId, memoryId: memoryId ?? this.memoryId, @@ -4212,46 +5415,114 @@ class MemoryAssetEntityCompanion extends UpdateCompanion } } -class PersonEntity extends Table with TableInfo { +class PersonEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; PersonEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn faceAssetId = GeneratedColumn('face_asset_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn thumbnailPath = GeneratedColumn('thumbnail_path', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isFavorite = GeneratedColumn('is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))')); - late final GeneratedColumn isHidden = GeneratedColumn('is_hidden', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_hidden" IN (0, 1))')); - late final GeneratedColumn color = - GeneratedColumn('color', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn birthDate = GeneratedColumn('birth_date', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbnailPath = GeneratedColumn( + 'thumbnail_path', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override - List get $columns => - [id, createdAt, updatedAt, ownerId, name, faceAssetId, thumbnailPath, isFavorite, isHidden, color, birthDate]; + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + thumbnailPath, + isFavorite, + isHidden, + color, + birthDate, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -4263,17 +5534,50 @@ class PersonEntity extends Table with TableInfo PersonEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return PersonEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - faceAssetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}face_asset_id']), - thumbnailPath: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}thumbnail_path'])!, - isFavorite: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - isHidden: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_hidden'])!, - color: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}color']), - birthDate: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}birth_date']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + thumbnailPath: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_path'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), ); } @@ -4288,7 +5592,8 @@ class PersonEntity extends Table with TableInfo bool get isStrict => true; } -class PersonEntityData extends DataClass implements Insertable { +class PersonEntityData extends DataClass + implements Insertable { final String id; final DateTime createdAt; final DateTime updatedAt; @@ -4300,18 +5605,19 @@ class PersonEntityData extends DataClass implements Insertable final bool isHidden; final String? color; final DateTime? birthDate; - const PersonEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.name, - this.faceAssetId, - required this.thumbnailPath, - required this.isFavorite, - required this.isHidden, - this.color, - this.birthDate}); + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.thumbnailPath, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -4335,7 +5641,10 @@ class PersonEntityData extends DataClass implements Insertable return map; } - factory PersonEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return PersonEntityData( id: serializer.fromJson(json['id']), @@ -4369,31 +5678,31 @@ class PersonEntityData extends DataClass implements Insertable }; } - PersonEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? name, - Value faceAssetId = const Value.absent(), - String? thumbnailPath, - bool? isFavorite, - bool? isHidden, - Value color = const Value.absent(), - Value birthDate = const Value.absent()}) => - PersonEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - name: name ?? this.name, - faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, - thumbnailPath: thumbnailPath ?? this.thumbnailPath, - isFavorite: isFavorite ?? this.isFavorite, - isHidden: isHidden ?? this.isHidden, - color: color.present ? color.value : this.color, - birthDate: birthDate.present ? birthDate.value : this.birthDate, - ); + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + String? thumbnailPath, + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + thumbnailPath: thumbnailPath ?? this.thumbnailPath, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); PersonEntityData copyWithCompanion(PersonEntityCompanion data) { return PersonEntityData( id: data.id.present ? data.id.value : this.id, @@ -4401,9 +5710,15 @@ class PersonEntityData extends DataClass implements Insertable updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, name: data.name.present ? data.name.value : this.name, - faceAssetId: data.faceAssetId.present ? data.faceAssetId.value : this.faceAssetId, - thumbnailPath: data.thumbnailPath.present ? data.thumbnailPath.value : this.thumbnailPath, - isFavorite: data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + thumbnailPath: data.thumbnailPath.present + ? data.thumbnailPath.value + : this.thumbnailPath, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, color: data.color.present ? data.color.value : this.color, birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, @@ -4430,7 +5745,18 @@ class PersonEntityData extends DataClass implements Insertable @override int get hashCode => Object.hash( - id, createdAt, updatedAt, ownerId, name, faceAssetId, thumbnailPath, isFavorite, isHidden, color, birthDate); + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + thumbnailPath, + isFavorite, + isHidden, + color, + birthDate, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -4485,12 +5811,12 @@ class PersonEntityCompanion extends UpdateCompanion { required bool isHidden, this.color = const Value.absent(), this.birthDate = const Value.absent(), - }) : id = Value(id), - ownerId = Value(ownerId), - name = Value(name), - thumbnailPath = Value(thumbnailPath), - isFavorite = Value(isFavorite), - isHidden = Value(isHidden); + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + thumbnailPath = Value(thumbnailPath), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); static Insertable custom({ Expression? id, Expression? createdAt, @@ -4519,18 +5845,19 @@ class PersonEntityCompanion extends UpdateCompanion { }); } - PersonEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? name, - Value? faceAssetId, - Value? thumbnailPath, - Value? isFavorite, - Value? isHidden, - Value? color, - Value? birthDate}) { + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? thumbnailPath, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { return PersonEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -4610,48 +5937,59 @@ class DatabaseAtV1 extends GeneratedDatabase { late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); late final StackEntity stackEntity = StackEntity(this); - late final Index idxLocalAssetChecksum = - Index('idx_local_asset_checksum', 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); - late final Index uQRemoteAssetOwnerChecksum = Index('UQ_remote_asset_owner_checksum', - 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); - late final Index idxRemoteAssetChecksum = - Index('idx_remote_asset_checksum', 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + late final Index uQRemoteAssetOwnerChecksum = Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); late final PartnerEntity partnerEntity = PartnerEntity(this); late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); - late final LocalAlbumAssetEntity localAlbumAssetEntity = LocalAlbumAssetEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); - late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = RemoteAlbumAssetEntity(this); - late final RemoteAlbumUserEntity remoteAlbumUserEntity = RemoteAlbumUserEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); late final MemoryEntity memoryEntity = MemoryEntity(this); late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); late final PersonEntity personEntity = PersonEntity(this); @override - Iterable> get allTables => allSchemaEntities.whereType>(); + Iterable> get allTables => + allSchemaEntities.whereType>(); @override List get allSchemaEntities => [ - userEntity, - remoteAssetEntity, - localAssetEntity, - stackEntity, - idxLocalAssetChecksum, - uQRemoteAssetOwnerChecksum, - idxRemoteAssetChecksum, - userMetadataEntity, - partnerEntity, - localAlbumEntity, - localAlbumAssetEntity, - remoteExifEntity, - remoteAlbumEntity, - remoteAlbumAssetEntity, - remoteAlbumUserEntity, - memoryEntity, - memoryAssetEntity, - personEntity - ]; + userEntity, + remoteAssetEntity, + localAssetEntity, + stackEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + localAlbumEntity, + localAlbumAssetEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + ]; @override int get schemaVersion => 1; @override - DriftDatabaseOptions get options => const DriftDatabaseOptions(storeDateTimeAsText: true); + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); } diff --git a/mobile/test/drift/main/generated/schema_v2.dart b/mobile/test/drift/main/generated/schema_v2.dart index 3d705e0454..903d13b9a2 100644 --- a/mobile/test/drift/main/generated/schema_v2.dart +++ b/mobile/test/drift/main/generated/schema_v2.dart @@ -8,30 +8,79 @@ class UserEntity extends Table with TableInfo { final GeneratedDatabase attachedDatabase; final String? _alias; UserEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isAdmin = GeneratedColumn('is_admin', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_admin" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn email = - GeneratedColumn('email', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn profileImagePath = GeneratedColumn('profile_image_path', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn quotaSizeInBytes = GeneratedColumn('quota_size_in_bytes', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn quotaUsageInBytes = GeneratedColumn('quota_usage_in_bytes', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: false, defaultValue: const CustomExpression('0')); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn profileImagePath = GeneratedColumn( + 'profile_image_path', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn quotaSizeInBytes = GeneratedColumn( + 'quota_size_in_bytes', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn quotaUsageInBytes = GeneratedColumn( + 'quota_usage_in_bytes', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); @override - List get $columns => - [id, name, isAdmin, email, profileImagePath, updatedAt, quotaSizeInBytes, quotaUsageInBytes]; + List get $columns => [ + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -43,17 +92,38 @@ class UserEntity extends Table with TableInfo { UserEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return UserEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - isAdmin: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_admin'])!, - email: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}email'])!, - profileImagePath: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}profile_image_path']), - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - quotaSizeInBytes: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}quota_size_in_bytes']), - quotaUsageInBytes: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}quota_usage_in_bytes'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + profileImagePath: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}profile_image_path'], + ), + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + quotaSizeInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_size_in_bytes'], + ), + quotaUsageInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_usage_in_bytes'], + )!, ); } @@ -77,15 +147,16 @@ class UserEntityData extends DataClass implements Insertable { final DateTime updatedAt; final int? quotaSizeInBytes; final int quotaUsageInBytes; - const UserEntityData( - {required this.id, - required this.name, - required this.isAdmin, - required this.email, - this.profileImagePath, - required this.updatedAt, - this.quotaSizeInBytes, - required this.quotaUsageInBytes}); + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + this.profileImagePath, + required this.updatedAt, + this.quotaSizeInBytes, + required this.quotaUsageInBytes, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -104,7 +175,10 @@ class UserEntityData extends DataClass implements Insertable { return map; } - factory UserEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return UserEntityData( id: serializer.fromJson(json['id']), @@ -132,35 +206,45 @@ class UserEntityData extends DataClass implements Insertable { }; } - UserEntityData copyWith( - {String? id, - String? name, - bool? isAdmin, - String? email, - Value profileImagePath = const Value.absent(), - DateTime? updatedAt, - Value quotaSizeInBytes = const Value.absent(), - int? quotaUsageInBytes}) => - UserEntityData( - id: id ?? this.id, - name: name ?? this.name, - isAdmin: isAdmin ?? this.isAdmin, - email: email ?? this.email, - profileImagePath: profileImagePath.present ? profileImagePath.value : this.profileImagePath, - updatedAt: updatedAt ?? this.updatedAt, - quotaSizeInBytes: quotaSizeInBytes.present ? quotaSizeInBytes.value : this.quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, - ); + UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + Value profileImagePath = const Value.absent(), + DateTime? updatedAt, + Value quotaSizeInBytes = const Value.absent(), + int? quotaUsageInBytes, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath.present + ? profileImagePath.value + : this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes.present + ? quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); UserEntityData copyWithCompanion(UserEntityCompanion data) { return UserEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, email: data.email.present ? data.email.value : this.email, - profileImagePath: data.profileImagePath.present ? data.profileImagePath.value : this.profileImagePath, + profileImagePath: data.profileImagePath.present + ? data.profileImagePath.value + : this.profileImagePath, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - quotaSizeInBytes: data.quotaSizeInBytes.present ? data.quotaSizeInBytes.value : this.quotaSizeInBytes, - quotaUsageInBytes: data.quotaUsageInBytes.present ? data.quotaUsageInBytes.value : this.quotaUsageInBytes, + quotaSizeInBytes: data.quotaSizeInBytes.present + ? data.quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: data.quotaUsageInBytes.present + ? data.quotaUsageInBytes.value + : this.quotaUsageInBytes, ); } @@ -180,8 +264,16 @@ class UserEntityData extends DataClass implements Insertable { } @override - int get hashCode => - Object.hash(id, name, isAdmin, email, profileImagePath, updatedAt, quotaSizeInBytes, quotaUsageInBytes); + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -224,9 +316,9 @@ class UserEntityCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), this.quotaSizeInBytes = const Value.absent(), this.quotaUsageInBytes = const Value.absent(), - }) : id = Value(id), - name = Value(name), - email = Value(email); + }) : id = Value(id), + name = Value(name), + email = Value(email); static Insertable custom({ Expression? id, Expression? name, @@ -249,15 +341,16 @@ class UserEntityCompanion extends UpdateCompanion { }); } - UserEntityCompanion copyWith( - {Value? id, - Value? name, - Value? isAdmin, - Value? email, - Value? profileImagePath, - Value? updatedAt, - Value? quotaSizeInBytes, - Value? quotaUsageInBytes}) { + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? profileImagePath, + Value? updatedAt, + Value? quotaSizeInBytes, + Value? quotaUsageInBytes, + }) { return UserEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -316,75 +409,161 @@ class UserEntityCompanion extends UpdateCompanion { } } -class RemoteAssetEntity extends Table with TableInfo { +class RemoteAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn type = - GeneratedColumn('type', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn width = - GeneratedColumn('width', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn height = - GeneratedColumn('height', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn durationInSeconds = GeneratedColumn('duration_in_seconds', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn checksum = - GeneratedColumn('checksum', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isFavorite = GeneratedColumn('is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn localDateTime = GeneratedColumn('local_date_time', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn thumbHash = - GeneratedColumn('thumb_hash', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn deletedAt = GeneratedColumn('deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn localDateTime = + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn livePhotoVideoId = GeneratedColumn( - 'live_photo_video_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn visibility = - GeneratedColumn('visibility', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn stackId = - GeneratedColumn('stack_id', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - ownerId, - localDateTime, - thumbHash, - deletedAt, - livePhotoVideoId, - visibility, - stackId - ]; + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -396,26 +575,74 @@ class RemoteAssetEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAssetEntityData( - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - type: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}type'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}height']), - durationInSeconds: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}checksum'])!, - isFavorite: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - localDateTime: - attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}local_date_time']), - thumbHash: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}thumb_hash']), - deletedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), - livePhotoVideoId: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}live_photo_video_id']), - visibility: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}visibility'])!, - stackId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}stack_id']), + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), ); } @@ -430,7 +657,8 @@ class RemoteAssetEntity extends Table with TableInfo true; } -class RemoteAssetEntityData extends DataClass implements Insertable { +class RemoteAssetEntityData extends DataClass + implements Insertable { final String name; final int type; final DateTime createdAt; @@ -448,24 +676,25 @@ class RemoteAssetEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -505,7 +734,10 @@ class RemoteAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAssetEntityData( name: serializer.fromJson(json['name']), @@ -551,43 +783,49 @@ class RemoteAssetEntityData extends DataClass implements Insertable width = const Value.absent(), - Value height = const Value.absent(), - Value durationInSeconds = const Value.absent(), - String? id, - String? checksum, - bool? isFavorite, - String? ownerId, - Value localDateTime = const Value.absent(), - Value thumbHash = const Value.absent(), - Value deletedAt = const Value.absent(), - Value livePhotoVideoId = const Value.absent(), - int? visibility, - Value stackId = const Value.absent()}) => - RemoteAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present ? durationInSeconds.value : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum ?? this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - ownerId: ownerId ?? this.ownerId, - localDateTime: localDateTime.present ? localDateTime.value : this.localDateTime, - thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - livePhotoVideoId: livePhotoVideoId.present ? livePhotoVideoId.value : this.livePhotoVideoId, - visibility: visibility ?? this.visibility, - stackId: stackId.present ? stackId.value : this.stackId, - ); + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + ); RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { return RemoteAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -596,16 +834,26 @@ class RemoteAssetEntityData extends DataClass implements Insertable Object.hash(name, type, createdAt, updatedAt, width, height, durationInSeconds, id, checksum, - isFavorite, ownerId, localDateTime, thumbHash, deletedAt, livePhotoVideoId, visibility, stackId); + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -660,7 +925,8 @@ class RemoteAssetEntityData extends DataClass implements Insertable { +class RemoteAssetEntityCompanion + extends UpdateCompanion { final Value name; final Value type; final Value createdAt; @@ -715,12 +981,12 @@ class RemoteAssetEntityCompanion extends UpdateCompanion this.livePhotoVideoId = const Value.absent(), required int visibility, this.stackId = const Value.absent(), - }) : name = Value(name), - type = Value(type), - id = Value(id), - checksum = Value(checksum), - ownerId = Value(ownerId), - visibility = Value(visibility); + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); static Insertable custom({ Expression? name, Expression? type, @@ -761,24 +1027,25 @@ class RemoteAssetEntityCompanion extends UpdateCompanion }); } - RemoteAssetEntityCompanion copyWith( - {Value? name, - Value? type, - Value? createdAt, - Value? updatedAt, - Value? width, - Value? height, - Value? durationInSeconds, - Value? id, - Value? checksum, - Value? isFavorite, - Value? ownerId, - Value? localDateTime, - Value? thumbHash, - Value? deletedAt, - Value? livePhotoVideoId, - Value? visibility, - Value? stackId}) { + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + }) { return RemoteAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -882,43 +1149,110 @@ class RemoteAssetEntityCompanion extends UpdateCompanion } } -class LocalAssetEntity extends Table with TableInfo { +class LocalAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; LocalAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn type = - GeneratedColumn('type', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn width = - GeneratedColumn('width', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn height = - GeneratedColumn('height', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn durationInSeconds = GeneratedColumn('duration_in_seconds', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn checksum = - GeneratedColumn('checksum', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn isFavorite = GeneratedColumn('is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn orientation = GeneratedColumn('orientation', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: false, defaultValue: const CustomExpression('0')); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); @override - List get $columns => - [name, type, createdAt, updatedAt, width, height, durationInSeconds, id, checksum, isFavorite, orientation]; + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -930,18 +1264,50 @@ class LocalAssetEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAssetEntityData( - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - type: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}type'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}height']), - durationInSeconds: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}checksum']), - isFavorite: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - orientation: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}orientation'])!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, ); } @@ -956,7 +1322,8 @@ class LocalAssetEntity extends Table with TableInfo true; } -class LocalAssetEntityData extends DataClass implements Insertable { +class LocalAssetEntityData extends DataClass + implements Insertable { final String name; final int type; final DateTime createdAt; @@ -968,18 +1335,19 @@ class LocalAssetEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -1005,7 +1373,10 @@ class LocalAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAssetEntityData( name: serializer.fromJson(json['name']), @@ -1039,31 +1410,33 @@ class LocalAssetEntityData extends DataClass implements Insertable width = const Value.absent(), - Value height = const Value.absent(), - Value durationInSeconds = const Value.absent(), - String? id, - Value checksum = const Value.absent(), - bool? isFavorite, - int? orientation}) => - LocalAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present ? durationInSeconds.value : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum.present ? checksum.value : this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - orientation: orientation ?? this.orientation, - ); + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { return LocalAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -1072,11 +1445,17 @@ class LocalAssetEntityData extends DataClass implements Insertable Object.hash( - name, type, createdAt, updatedAt, width, height, durationInSeconds, id, checksum, isFavorite, orientation); + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -1155,9 +1545,9 @@ class LocalAssetEntityCompanion extends UpdateCompanion { this.checksum = const Value.absent(), this.isFavorite = const Value.absent(), this.orientation = const Value.absent(), - }) : name = Value(name), - type = Value(type), - id = Value(id); + }) : name = Value(name), + type = Value(type), + id = Value(id); static Insertable custom({ Expression? name, Expression? type, @@ -1186,18 +1576,19 @@ class LocalAssetEntityCompanion extends UpdateCompanion { }); } - LocalAssetEntityCompanion copyWith( - {Value? name, - Value? type, - Value? createdAt, - Value? updatedAt, - Value? width, - Value? height, - Value? durationInSeconds, - Value? id, - Value? checksum, - Value? isFavorite, - Value? orientation}) { + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { return LocalAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -1276,26 +1667,57 @@ class StackEntity extends Table with TableInfo { final GeneratedDatabase attachedDatabase; final String? _alias; StackEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn primaryAssetId = GeneratedColumn('primary_asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id)')); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id)', + ), + ); @override - List get $columns => [id, createdAt, updatedAt, ownerId, primaryAssetId]; + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -1307,12 +1729,26 @@ class StackEntity extends Table with TableInfo { StackEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return StackEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - primaryAssetId: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, ); } @@ -1333,12 +1769,13 @@ class StackEntityData extends DataClass implements Insertable { final DateTime updatedAt; final String ownerId; final String primaryAssetId; - const StackEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.primaryAssetId}); + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1350,7 +1787,10 @@ class StackEntityData extends DataClass implements Insertable { return map; } - factory StackEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return StackEntityData( id: serializer.fromJson(json['id']), @@ -1372,22 +1812,28 @@ class StackEntityData extends DataClass implements Insertable { }; } - StackEntityData copyWith( - {String? id, DateTime? createdAt, DateTime? updatedAt, String? ownerId, String? primaryAssetId}) => - StackEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - primaryAssetId: primaryAssetId ?? this.primaryAssetId, - ); + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); StackEntityData copyWithCompanion(StackEntityCompanion data) { return StackEntityData( id: data.id.present ? data.id.value : this.id, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, - primaryAssetId: data.primaryAssetId.present ? data.primaryAssetId.value : this.primaryAssetId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, ); } @@ -1404,7 +1850,8 @@ class StackEntityData extends DataClass implements Insertable { } @override - int get hashCode => Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); @override bool operator ==(Object other) => identical(this, other) || @@ -1435,9 +1882,9 @@ class StackEntityCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), required String ownerId, required String primaryAssetId, - }) : id = Value(id), - ownerId = Value(ownerId), - primaryAssetId = Value(primaryAssetId); + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); static Insertable custom({ Expression? id, Expression? createdAt, @@ -1454,12 +1901,13 @@ class StackEntityCompanion extends UpdateCompanion { }); } - StackEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? primaryAssetId}) { + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { return StackEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -1503,19 +1951,36 @@ class StackEntityCompanion extends UpdateCompanion { } } -class UserMetadataEntity extends Table with TableInfo { +class UserMetadataEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; UserMetadataEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn userId = GeneratedColumn('user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn key = - GeneratedColumn('key', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn value = - GeneratedColumn('value', aliasedName, false, type: DriftSqlType.blob, requiredDuringInsert: true); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); @override List get $columns => [userId, key, value]; @override @@ -1529,9 +1994,18 @@ class UserMetadataEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return UserMetadataEntityData( - userId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - key: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}key'])!, - value: attachedDatabase.typeMapping.read(DriftSqlType.blob, data['${effectivePrefix}value'])!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, ); } @@ -1546,11 +2020,16 @@ class UserMetadataEntity extends Table with TableInfo true; } -class UserMetadataEntityData extends DataClass implements Insertable { +class UserMetadataEntityData extends DataClass + implements Insertable { final String userId; final int key; final Uint8List value; - const UserMetadataEntityData({required this.userId, required this.key, required this.value}); + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1560,7 +2039,10 @@ class UserMetadataEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return UserMetadataEntityData( userId: serializer.fromJson(json['userId']), @@ -1578,11 +2060,15 @@ class UserMetadataEntityData extends DataClass implements Insertable UserMetadataEntityData( - userId: userId ?? this.userId, - key: key ?? this.key, - value: value ?? this.value, - ); + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { return UserMetadataEntityData( userId: data.userId.present ? data.userId.value : this.userId, @@ -1612,7 +2098,8 @@ class UserMetadataEntityData extends DataClass implements Insertable { +class UserMetadataEntityCompanion + extends UpdateCompanion { final Value userId; final Value key; final Value value; @@ -1625,9 +2112,9 @@ class UserMetadataEntityCompanion extends UpdateCompanion custom({ Expression? userId, Expression? key, @@ -1640,7 +2127,11 @@ class UserMetadataEntityCompanion extends UpdateCompanion? userId, Value? key, Value? value}) { + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { return UserMetadataEntityCompanion( userId: userId ?? this.userId, key: key ?? this.key, @@ -1674,24 +2165,43 @@ class UserMetadataEntityCompanion extends UpdateCompanion { +class PartnerEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; PartnerEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn sharedById = GeneratedColumn('shared_by_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn sharedWithId = GeneratedColumn('shared_with_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn inTimeline = GeneratedColumn('in_timeline', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("in_timeline" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); @override List get $columns => [sharedById, sharedWithId, inTimeline]; @override @@ -1705,9 +2215,18 @@ class PartnerEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return PartnerEntityData( - sharedById: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}shared_by_id'])!, - sharedWithId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}shared_with_id'])!, - inTimeline: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!, + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, ); } @@ -1722,11 +2241,16 @@ class PartnerEntity extends Table with TableInfo true; } -class PartnerEntityData extends DataClass implements Insertable { +class PartnerEntityData extends DataClass + implements Insertable { final String sharedById; final String sharedWithId; final bool inTimeline; - const PartnerEntityData({required this.sharedById, required this.sharedWithId, required this.inTimeline}); + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1736,7 +2260,10 @@ class PartnerEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return PartnerEntityData( sharedById: serializer.fromJson(json['sharedById']), @@ -1754,16 +2281,26 @@ class PartnerEntityData extends DataClass implements Insertable PartnerEntityData( - sharedById: sharedById ?? this.sharedById, - sharedWithId: sharedWithId ?? this.sharedWithId, - inTimeline: inTimeline ?? this.inTimeline, - ); + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { return PartnerEntityData( - sharedById: data.sharedById.present ? data.sharedById.value : this.sharedById, - sharedWithId: data.sharedWithId.present ? data.sharedWithId.value : this.sharedWithId, - inTimeline: data.inTimeline.present ? data.inTimeline.value : this.inTimeline, + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, ); } @@ -1801,8 +2338,8 @@ class PartnerEntityCompanion extends UpdateCompanion { required String sharedById, required String sharedWithId, this.inTimeline = const Value.absent(), - }) : sharedById = Value(sharedById), - sharedWithId = Value(sharedWithId); + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); static Insertable custom({ Expression? sharedById, Expression? sharedWithId, @@ -1815,7 +2352,11 @@ class PartnerEntityCompanion extends UpdateCompanion { }); } - PartnerEntityCompanion copyWith({Value? sharedById, Value? sharedWithId, Value? inTimeline}) { + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { return PartnerEntityCompanion( sharedById: sharedById ?? this.sharedById, sharedWithId: sharedWithId ?? this.sharedWithId, @@ -1849,32 +2390,71 @@ class PartnerEntityCompanion extends UpdateCompanion { } } -class LocalAlbumEntity extends Table with TableInfo { +class LocalAlbumEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; LocalAlbumEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn backupSelection = - GeneratedColumn('backup_selection', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn isIosSharedAlbum = GeneratedColumn('is_ios_shared_album', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_ios_shared_album" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn marker_ = GeneratedColumn('marker', aliasedName, true, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("marker" IN (0, 1))')); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); @override - List get $columns => [id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_]; + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -1886,13 +2466,30 @@ class LocalAlbumEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAlbumEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - backupSelection: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}backup_selection'])!, - isIosSharedAlbum: - attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_ios_shared_album'])!, - marker_: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}marker']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), ); } @@ -1907,20 +2504,22 @@ class LocalAlbumEntity extends Table with TableInfo true; } -class LocalAlbumEntityData extends DataClass implements Insertable { +class LocalAlbumEntityData extends DataClass + implements Insertable { final String id; final String name; final DateTime updatedAt; final int backupSelection; final bool isIosSharedAlbum; final bool? marker_; - const LocalAlbumEntityData( - {required this.id, - required this.name, - required this.updatedAt, - required this.backupSelection, - required this.isIosSharedAlbum, - this.marker_}); + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1935,7 +2534,10 @@ class LocalAlbumEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAlbumEntityData( id: serializer.fromJson(json['id']), @@ -1959,28 +2561,32 @@ class LocalAlbumEntityData extends DataClass implements Insertable marker_ = const Value.absent()}) => - LocalAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - updatedAt: updatedAt ?? this.updatedAt, - backupSelection: backupSelection ?? this.backupSelection, - isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, - marker_: marker_.present ? marker_.value : this.marker_, - ); + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { return LocalAlbumEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - backupSelection: data.backupSelection.present ? data.backupSelection.value : this.backupSelection, - isIosSharedAlbum: data.isIosSharedAlbum.present ? data.isIosSharedAlbum.value : this.isIosSharedAlbum, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, marker_: data.marker_.present ? data.marker_.value : this.marker_, ); } @@ -1999,7 +2605,14 @@ class LocalAlbumEntityData extends DataClass implements Insertable Object.hash(id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_); + int get hashCode => Object.hash( + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -2034,9 +2647,9 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { required int backupSelection, this.isIosSharedAlbum = const Value.absent(), this.marker_ = const Value.absent(), - }) : id = Value(id), - name = Value(name), - backupSelection = Value(backupSelection); + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); static Insertable custom({ Expression? id, Expression? name, @@ -2055,13 +2668,14 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { }); } - LocalAlbumEntityCompanion copyWith( - {Value? id, - Value? name, - Value? updatedAt, - Value? backupSelection, - Value? isIosSharedAlbum, - Value? marker_}) { + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_, + }) { return LocalAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -2110,19 +2724,32 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { } } -class LocalAlbumAssetEntity extends Table with TableInfo { +class LocalAlbumAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES local_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn albumId = GeneratedColumn('album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES local_album_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -2133,11 +2760,20 @@ class LocalAlbumAssetEntity extends Table with TableInfo get $primaryKey => {assetId, albumId}; @override - LocalAlbumAssetEntityData map(Map data, {String? tablePrefix}) { + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -2152,10 +2788,14 @@ class LocalAlbumAssetEntity extends Table with TableInfo true; } -class LocalAlbumAssetEntityData extends DataClass implements Insertable { +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { final String assetId; final String albumId; - const LocalAlbumAssetEntityData({required this.assetId, required this.albumId}); + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -2164,7 +2804,10 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -2180,11 +2823,14 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable LocalAlbumAssetEntityData( + LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + LocalAlbumAssetEntityData( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, ); - LocalAlbumAssetEntityData copyWithCompanion(LocalAlbumAssetEntityCompanion data) { + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data, + ) { return LocalAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -2205,10 +2851,13 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable identical(this, other) || - (other is LocalAlbumAssetEntityData && other.assetId == this.assetId && other.albumId == this.albumId); + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); } -class LocalAlbumAssetEntityCompanion extends UpdateCompanion { +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { final Value assetId; final Value albumId; const LocalAlbumAssetEntityCompanion({ @@ -2218,8 +2867,8 @@ class LocalAlbumAssetEntityCompanion extends UpdateCompanion custom({ Expression? assetId, Expression? albumId, @@ -2230,7 +2879,10 @@ class LocalAlbumAssetEntityCompanion extends UpdateCompanion? assetId, Value? albumId}) { + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { return LocalAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, @@ -2259,83 +2911,195 @@ class LocalAlbumAssetEntityCompanion extends UpdateCompanion { +class RemoteExifEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteExifEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn city = - GeneratedColumn('city', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn state = - GeneratedColumn('state', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn country = - GeneratedColumn('country', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn dateTimeOriginal = GeneratedColumn( - 'date_time_original', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn description = - GeneratedColumn('description', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn height = - GeneratedColumn('height', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn width = - GeneratedColumn('width', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn exposureTime = GeneratedColumn('exposure_time', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn fNumber = - GeneratedColumn('f_number', aliasedName, true, type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn fileSize = - GeneratedColumn('file_size', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn focalLength = GeneratedColumn('focal_length', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn latitude = - GeneratedColumn('latitude', aliasedName, true, type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn longitude = - GeneratedColumn('longitude', aliasedName, true, type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn iso = - GeneratedColumn('iso', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn make = - GeneratedColumn('make', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn model = - GeneratedColumn('model', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn lens = - GeneratedColumn('lens', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn orientation = - GeneratedColumn('orientation', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn timeZone = - GeneratedColumn('time_zone', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn rating = - GeneratedColumn('rating', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn projectionType = GeneratedColumn('projection_type', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn city = GeneratedColumn( + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn state = GeneratedColumn( + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn country = GeneratedColumn( + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn make = GeneratedColumn( + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn model = GeneratedColumn( + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]; + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -2347,29 +3111,94 @@ class RemoteExifEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteExifEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - city: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}city']), - state: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}state']), - country: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}country']), - dateTimeOriginal: - attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}date_time_original']), - description: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}description']), - height: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}height']), - width: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}width']), - exposureTime: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}exposure_time']), - fNumber: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}f_number']), - fileSize: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}file_size']), - focalLength: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}focal_length']), - latitude: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}latitude']), - longitude: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}longitude']), - iso: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}iso']), - make: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}make']), - model: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}model']), - lens: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}lens']), - orientation: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}orientation']), - timeZone: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}time_zone']), - rating: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}rating']), - projectionType: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}projection_type']), + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), ); } @@ -2384,7 +3213,8 @@ class RemoteExifEntity extends Table with TableInfo true; } -class RemoteExifEntityData extends DataClass implements Insertable { +class RemoteExifEntityData extends DataClass + implements Insertable { final String assetId; final String? city; final String? state; @@ -2407,29 +3237,30 @@ class RemoteExifEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -2500,14 +3331,19 @@ class RemoteExifEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteExifEntityData( assetId: serializer.fromJson(json['assetId']), city: serializer.fromJson(json['city']), state: serializer.fromJson(json['state']), country: serializer.fromJson(json['country']), - dateTimeOriginal: serializer.fromJson(json['dateTimeOriginal']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), description: serializer.fromJson(json['description']), height: serializer.fromJson(json['height']), width: serializer.fromJson(json['width']), @@ -2556,77 +3392,93 @@ class RemoteExifEntityData extends DataClass implements Insertable city = const Value.absent(), - Value state = const Value.absent(), - Value country = const Value.absent(), - Value dateTimeOriginal = const Value.absent(), - Value description = const Value.absent(), - Value height = const Value.absent(), - Value width = const Value.absent(), - Value exposureTime = const Value.absent(), - Value fNumber = const Value.absent(), - Value fileSize = const Value.absent(), - Value focalLength = const Value.absent(), - Value latitude = const Value.absent(), - Value longitude = const Value.absent(), - Value iso = const Value.absent(), - Value make = const Value.absent(), - Value model = const Value.absent(), - Value lens = const Value.absent(), - Value orientation = const Value.absent(), - Value timeZone = const Value.absent(), - Value rating = const Value.absent(), - Value projectionType = const Value.absent()}) => - RemoteExifEntityData( - assetId: assetId ?? this.assetId, - city: city.present ? city.value : this.city, - state: state.present ? state.value : this.state, - country: country.present ? country.value : this.country, - dateTimeOriginal: dateTimeOriginal.present ? dateTimeOriginal.value : this.dateTimeOriginal, - description: description.present ? description.value : this.description, - height: height.present ? height.value : this.height, - width: width.present ? width.value : this.width, - exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, - fNumber: fNumber.present ? fNumber.value : this.fNumber, - fileSize: fileSize.present ? fileSize.value : this.fileSize, - focalLength: focalLength.present ? focalLength.value : this.focalLength, - latitude: latitude.present ? latitude.value : this.latitude, - longitude: longitude.present ? longitude.value : this.longitude, - iso: iso.present ? iso.value : this.iso, - make: make.present ? make.value : this.make, - model: model.present ? model.value : this.model, - lens: lens.present ? lens.value : this.lens, - orientation: orientation.present ? orientation.value : this.orientation, - timeZone: timeZone.present ? timeZone.value : this.timeZone, - rating: rating.present ? rating.value : this.rating, - projectionType: projectionType.present ? projectionType.value : this.projectionType, - ); + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { return RemoteExifEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, city: data.city.present ? data.city.value : this.city, state: data.state.present ? data.state.value : this.state, country: data.country.present ? data.country.value : this.country, - dateTimeOriginal: data.dateTimeOriginal.present ? data.dateTimeOriginal.value : this.dateTimeOriginal, - description: data.description.present ? data.description.value : this.description, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: data.description.present + ? data.description.value + : this.description, height: data.height.present ? data.height.value : this.height, width: data.width.present ? data.width.value : this.width, - exposureTime: data.exposureTime.present ? data.exposureTime.value : this.exposureTime, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, - focalLength: data.focalLength.present ? data.focalLength.value : this.focalLength, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, latitude: data.latitude.present ? data.latitude.value : this.latitude, longitude: data.longitude.present ? data.longitude.value : this.longitude, iso: data.iso.present ? data.iso.value : this.iso, make: data.make.present ? data.make.value : this.make, model: data.model.present ? data.model.value : this.model, lens: data.lens.present ? data.lens.value : this.lens, - orientation: data.orientation.present ? data.orientation.value : this.orientation, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, rating: data.rating.present ? data.rating.value : this.rating, - projectionType: data.projectionType.present ? data.projectionType.value : this.projectionType, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, ); } @@ -2661,29 +3513,29 @@ class RemoteExifEntityData extends DataClass implements Insertable Object.hashAll([ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]); + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); @override bool operator ==(Object other) => identical(this, other) || @@ -2833,29 +3685,30 @@ class RemoteExifEntityCompanion extends UpdateCompanion { }); } - RemoteExifEntityCompanion copyWith( - {Value? assetId, - Value? city, - Value? state, - Value? country, - Value? dateTimeOriginal, - Value? description, - Value? height, - Value? width, - Value? exposureTime, - Value? fNumber, - Value? fileSize, - Value? focalLength, - Value? latitude, - Value? longitude, - Value? iso, - Value? make, - Value? model, - Value? lens, - Value? orientation, - Value? timeZone, - Value? rating, - Value? projectionType}) { + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { return RemoteExifEntityCompanion( assetId: assetId ?? this.assetId, city: city ?? this.city, @@ -2984,43 +3837,100 @@ class RemoteExifEntityCompanion extends UpdateCompanion { } } -class RemoteAlbumEntity extends Table with TableInfo { +class RemoteAlbumEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAlbumEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn description = GeneratedColumn('description', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: false, defaultValue: const CustomExpression('\'\'')); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn thumbnailAssetId = GeneratedColumn('thumbnail_asset_id', aliasedName, true, - type: DriftSqlType.string, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE SET NULL')); - late final GeneratedColumn isActivityEnabled = GeneratedColumn('is_activity_enabled', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_activity_enabled" IN (0, 1))'), - defaultValue: const CustomExpression('1')); - late final GeneratedColumn order = - GeneratedColumn('order', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn order = GeneratedColumn( + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); @override - List get $columns => - [id, name, description, createdAt, updatedAt, ownerId, thumbnailAssetId, isActivityEnabled, order]; + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -3032,17 +3942,42 @@ class RemoteAlbumEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - description: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}description'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - thumbnailAssetId: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}thumbnail_asset_id']), - isActivityEnabled: - attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_activity_enabled'])!, - order: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}order'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, ); } @@ -3057,7 +3992,8 @@ class RemoteAlbumEntity extends Table with TableInfo true; } -class RemoteAlbumEntityData extends DataClass implements Insertable { +class RemoteAlbumEntityData extends DataClass + implements Insertable { final String id; final String name; final String description; @@ -3067,16 +4003,17 @@ class RemoteAlbumEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -3094,7 +4031,10 @@ class RemoteAlbumEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumEntityData( id: serializer.fromJson(json['id']), @@ -3124,37 +4064,45 @@ class RemoteAlbumEntityData extends DataClass implements Insertable thumbnailAssetId = const Value.absent(), - bool? isActivityEnabled, - int? order}) => - RemoteAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - description: description ?? this.description, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - thumbnailAssetId: thumbnailAssetId.present ? thumbnailAssetId.value : this.thumbnailAssetId, - isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, - order: order ?? this.order, - ); + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { return RemoteAlbumEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, - description: data.description.present ? data.description.value : this.description, + description: data.description.present + ? data.description.value + : this.description, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, - thumbnailAssetId: data.thumbnailAssetId.present ? data.thumbnailAssetId.value : this.thumbnailAssetId, - isActivityEnabled: data.isActivityEnabled.present ? data.isActivityEnabled.value : this.isActivityEnabled, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, order: data.order.present ? data.order.value : this.order, ); } @@ -3176,8 +4124,17 @@ class RemoteAlbumEntityData extends DataClass implements Insertable - Object.hash(id, name, description, createdAt, updatedAt, ownerId, thumbnailAssetId, isActivityEnabled, order); + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -3193,7 +4150,8 @@ class RemoteAlbumEntityData extends DataClass implements Insertable { +class RemoteAlbumEntityCompanion + extends UpdateCompanion { final Value id; final Value name; final Value description; @@ -3224,10 +4182,10 @@ class RemoteAlbumEntityCompanion extends UpdateCompanion this.thumbnailAssetId = const Value.absent(), this.isActivityEnabled = const Value.absent(), required int order, - }) : id = Value(id), - name = Value(name), - ownerId = Value(ownerId), - order = Value(order); + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); static Insertable custom({ Expression? id, Expression? name, @@ -3252,16 +4210,17 @@ class RemoteAlbumEntityCompanion extends UpdateCompanion }); } - RemoteAlbumEntityCompanion copyWith( - {Value? id, - Value? name, - Value? description, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? thumbnailAssetId, - Value? isActivityEnabled, - Value? order}) { + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { return RemoteAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -3325,19 +4284,32 @@ class RemoteAlbumEntityCompanion extends UpdateCompanion } } -class RemoteAlbumAssetEntity extends Table with TableInfo { +class RemoteAlbumAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn albumId = GeneratedColumn('album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -3348,11 +4320,20 @@ class RemoteAlbumAssetEntity extends Table with TableInfo get $primaryKey => {assetId, albumId}; @override - RemoteAlbumAssetEntityData map(Map data, {String? tablePrefix}) { + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -3367,10 +4348,14 @@ class RemoteAlbumAssetEntity extends Table with TableInfo true; } -class RemoteAlbumAssetEntityData extends DataClass implements Insertable { +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { final String assetId; final String albumId; - const RemoteAlbumAssetEntityData({required this.assetId, required this.albumId}); + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3379,7 +4364,10 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -3395,11 +4383,14 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable RemoteAlbumAssetEntityData( + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, ); - RemoteAlbumAssetEntityData copyWithCompanion(RemoteAlbumAssetEntityCompanion data) { + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data, + ) { return RemoteAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -3420,10 +4411,13 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable identical(this, other) || - (other is RemoteAlbumAssetEntityData && other.assetId == this.assetId && other.albumId == this.albumId); + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); } -class RemoteAlbumAssetEntityCompanion extends UpdateCompanion { +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { final Value assetId; final Value albumId; const RemoteAlbumAssetEntityCompanion({ @@ -3433,8 +4427,8 @@ class RemoteAlbumAssetEntityCompanion extends UpdateCompanion custom({ Expression? assetId, Expression? albumId, @@ -3445,7 +4439,10 @@ class RemoteAlbumAssetEntityCompanion extends UpdateCompanion? assetId, Value? albumId}) { + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { return RemoteAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, @@ -3474,21 +4471,39 @@ class RemoteAlbumAssetEntityCompanion extends UpdateCompanion { +class RemoteAlbumUserEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn albumId = GeneratedColumn('album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_album_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn userId = GeneratedColumn('user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn role = - GeneratedColumn('role', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn role = GeneratedColumn( + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); @override List get $columns => [albumId, userId, role]; @override @@ -3499,12 +4514,24 @@ class RemoteAlbumUserEntity extends Table with TableInfo get $primaryKey => {albumId, userId}; @override - RemoteAlbumUserEntityData map(Map data, {String? tablePrefix}) { + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumUserEntityData( - albumId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, - userId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - role: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}role'])!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, ); } @@ -3519,11 +4546,16 @@ class RemoteAlbumUserEntity extends Table with TableInfo true; } -class RemoteAlbumUserEntityData extends DataClass implements Insertable { +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { final String albumId; final String userId; final int role; - const RemoteAlbumUserEntityData({required this.albumId, required this.userId, required this.role}); + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3533,7 +4565,10 @@ class RemoteAlbumUserEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumUserEntityData( albumId: serializer.fromJson(json['albumId']), @@ -3551,12 +4586,18 @@ class RemoteAlbumUserEntityData extends DataClass implements Insertable RemoteAlbumUserEntityData( - albumId: albumId ?? this.albumId, - userId: userId ?? this.userId, - role: role ?? this.role, - ); - RemoteAlbumUserEntityData copyWithCompanion(RemoteAlbumUserEntityCompanion data) { + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data, + ) { return RemoteAlbumUserEntityData( albumId: data.albumId.present ? data.albumId.value : this.albumId, userId: data.userId.present ? data.userId.value : this.userId, @@ -3585,7 +4626,8 @@ class RemoteAlbumUserEntityData extends DataClass implements Insertable { +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { final Value albumId; final Value userId; final Value role; @@ -3598,9 +4640,9 @@ class RemoteAlbumUserEntityCompanion extends UpdateCompanion custom({ Expression? albumId, Expression? userId, @@ -3613,7 +4655,11 @@ class RemoteAlbumUserEntityCompanion extends UpdateCompanion? albumId, Value? userId, Value? role}) { + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { return RemoteAlbumUserEntityCompanion( albumId: albumId ?? this.albumId, userId: userId ?? this.userId, @@ -3647,47 +4693,120 @@ class RemoteAlbumUserEntityCompanion extends UpdateCompanion { +class MemoryEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; MemoryEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn deletedAt = GeneratedColumn('deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn type = - GeneratedColumn('type', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn data = - GeneratedColumn('data', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isSaved = GeneratedColumn('is_saved', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_saved" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn memoryAt = GeneratedColumn('memory_at', aliasedName, false, - type: DriftSqlType.dateTime, requiredDuringInsert: true); - late final GeneratedColumn seenAt = - GeneratedColumn('seen_at', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn showAt = - GeneratedColumn('show_at', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn hideAt = - GeneratedColumn('hide_at', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn data = GeneratedColumn( + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override - List get $columns => - [id, createdAt, updatedAt, deletedAt, ownerId, type, data, isSaved, memoryAt, seenAt, showAt, hideAt]; + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -3699,18 +4818,54 @@ class MemoryEntity extends Table with TableInfo MemoryEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return MemoryEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - deletedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - type: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}type'])!, - data: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}data'])!, - isSaved: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_saved'])!, - memoryAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}memory_at'])!, - seenAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}seen_at']), - showAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}show_at']), - hideAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}hide_at']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), ); } @@ -3725,7 +4880,8 @@ class MemoryEntity extends Table with TableInfo bool get isStrict => true; } -class MemoryEntityData extends DataClass implements Insertable { +class MemoryEntityData extends DataClass + implements Insertable { final String id; final DateTime createdAt; final DateTime updatedAt; @@ -3738,19 +4894,20 @@ class MemoryEntityData extends DataClass implements Insertable final DateTime? seenAt; final DateTime? showAt; final DateTime? hideAt; - const MemoryEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - this.deletedAt, - required this.ownerId, - required this.type, - required this.data, - required this.isSaved, - required this.memoryAt, - this.seenAt, - this.showAt, - this.hideAt}); + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3777,7 +4934,10 @@ class MemoryEntityData extends DataClass implements Insertable return map; } - factory MemoryEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return MemoryEntityData( id: serializer.fromJson(json['id']), @@ -3813,33 +4973,33 @@ class MemoryEntityData extends DataClass implements Insertable }; } - MemoryEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - Value deletedAt = const Value.absent(), - String? ownerId, - int? type, - String? data, - bool? isSaved, - DateTime? memoryAt, - Value seenAt = const Value.absent(), - Value showAt = const Value.absent(), - Value hideAt = const Value.absent()}) => - MemoryEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - ownerId: ownerId ?? this.ownerId, - type: type ?? this.type, - data: data ?? this.data, - isSaved: isSaved ?? this.isSaved, - memoryAt: memoryAt ?? this.memoryAt, - seenAt: seenAt.present ? seenAt.value : this.seenAt, - showAt: showAt.present ? showAt.value : this.showAt, - hideAt: hideAt.present ? hideAt.value : this.hideAt, - ); + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { return MemoryEntityData( id: data.id.present ? data.id.value : this.id, @@ -3877,8 +5037,20 @@ class MemoryEntityData extends DataClass implements Insertable } @override - int get hashCode => - Object.hash(id, createdAt, updatedAt, deletedAt, ownerId, type, data, isSaved, memoryAt, seenAt, showAt, hideAt); + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -3937,11 +5109,11 @@ class MemoryEntityCompanion extends UpdateCompanion { this.seenAt = const Value.absent(), this.showAt = const Value.absent(), this.hideAt = const Value.absent(), - }) : id = Value(id), - ownerId = Value(ownerId), - type = Value(type), - data = Value(data), - memoryAt = Value(memoryAt); + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); static Insertable custom({ Expression? id, Expression? createdAt, @@ -3972,19 +5144,20 @@ class MemoryEntityCompanion extends UpdateCompanion { }); } - MemoryEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? deletedAt, - Value? ownerId, - Value? type, - Value? data, - Value? isSaved, - Value? memoryAt, - Value? seenAt, - Value? showAt, - Value? hideAt}) { + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { return MemoryEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -4063,19 +5236,32 @@ class MemoryEntityCompanion extends UpdateCompanion { } } -class MemoryAssetEntity extends Table with TableInfo { +class MemoryAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; MemoryAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn memoryId = GeneratedColumn('memory_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES memory_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, memoryId]; @override @@ -4089,8 +5275,14 @@ class MemoryAssetEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return MemoryAssetEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - memoryId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}memory_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, ); } @@ -4105,7 +5297,8 @@ class MemoryAssetEntity extends Table with TableInfo true; } -class MemoryAssetEntityData extends DataClass implements Insertable { +class MemoryAssetEntityData extends DataClass + implements Insertable { final String assetId; final String memoryId; const MemoryAssetEntityData({required this.assetId, required this.memoryId}); @@ -4117,7 +5310,10 @@ class MemoryAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return MemoryAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -4133,7 +5329,8 @@ class MemoryAssetEntityData extends DataClass implements Insertable MemoryAssetEntityData( + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( assetId: assetId ?? this.assetId, memoryId: memoryId ?? this.memoryId, ); @@ -4158,10 +5355,13 @@ class MemoryAssetEntityData extends DataClass implements Insertable identical(this, other) || - (other is MemoryAssetEntityData && other.assetId == this.assetId && other.memoryId == this.memoryId); + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); } -class MemoryAssetEntityCompanion extends UpdateCompanion { +class MemoryAssetEntityCompanion + extends UpdateCompanion { final Value assetId; final Value memoryId; const MemoryAssetEntityCompanion({ @@ -4171,8 +5371,8 @@ class MemoryAssetEntityCompanion extends UpdateCompanion MemoryAssetEntityCompanion.insert({ required String assetId, required String memoryId, - }) : assetId = Value(assetId), - memoryId = Value(memoryId); + }) : assetId = Value(assetId), + memoryId = Value(memoryId); static Insertable custom({ Expression? assetId, Expression? memoryId, @@ -4183,7 +5383,10 @@ class MemoryAssetEntityCompanion extends UpdateCompanion }); } - MemoryAssetEntityCompanion copyWith({Value? assetId, Value? memoryId}) { + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { return MemoryAssetEntityCompanion( assetId: assetId ?? this.assetId, memoryId: memoryId ?? this.memoryId, @@ -4212,46 +5415,114 @@ class MemoryAssetEntityCompanion extends UpdateCompanion } } -class PersonEntity extends Table with TableInfo { +class PersonEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; PersonEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn faceAssetId = GeneratedColumn('face_asset_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn thumbnailPath = GeneratedColumn('thumbnail_path', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isFavorite = GeneratedColumn('is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))')); - late final GeneratedColumn isHidden = GeneratedColumn('is_hidden', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_hidden" IN (0, 1))')); - late final GeneratedColumn color = - GeneratedColumn('color', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn birthDate = GeneratedColumn('birth_date', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbnailPath = GeneratedColumn( + 'thumbnail_path', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override - List get $columns => - [id, createdAt, updatedAt, ownerId, name, faceAssetId, thumbnailPath, isFavorite, isHidden, color, birthDate]; + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + thumbnailPath, + isFavorite, + isHidden, + color, + birthDate, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -4263,17 +5534,50 @@ class PersonEntity extends Table with TableInfo PersonEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return PersonEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - faceAssetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}face_asset_id']), - thumbnailPath: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}thumbnail_path'])!, - isFavorite: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - isHidden: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_hidden'])!, - color: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}color']), - birthDate: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}birth_date']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + thumbnailPath: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_path'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), ); } @@ -4288,7 +5592,8 @@ class PersonEntity extends Table with TableInfo bool get isStrict => true; } -class PersonEntityData extends DataClass implements Insertable { +class PersonEntityData extends DataClass + implements Insertable { final String id; final DateTime createdAt; final DateTime updatedAt; @@ -4300,18 +5605,19 @@ class PersonEntityData extends DataClass implements Insertable final bool isHidden; final String? color; final DateTime? birthDate; - const PersonEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.name, - this.faceAssetId, - required this.thumbnailPath, - required this.isFavorite, - required this.isHidden, - this.color, - this.birthDate}); + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.thumbnailPath, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -4335,7 +5641,10 @@ class PersonEntityData extends DataClass implements Insertable return map; } - factory PersonEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return PersonEntityData( id: serializer.fromJson(json['id']), @@ -4369,31 +5678,31 @@ class PersonEntityData extends DataClass implements Insertable }; } - PersonEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? name, - Value faceAssetId = const Value.absent(), - String? thumbnailPath, - bool? isFavorite, - bool? isHidden, - Value color = const Value.absent(), - Value birthDate = const Value.absent()}) => - PersonEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - name: name ?? this.name, - faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, - thumbnailPath: thumbnailPath ?? this.thumbnailPath, - isFavorite: isFavorite ?? this.isFavorite, - isHidden: isHidden ?? this.isHidden, - color: color.present ? color.value : this.color, - birthDate: birthDate.present ? birthDate.value : this.birthDate, - ); + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + String? thumbnailPath, + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + thumbnailPath: thumbnailPath ?? this.thumbnailPath, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); PersonEntityData copyWithCompanion(PersonEntityCompanion data) { return PersonEntityData( id: data.id.present ? data.id.value : this.id, @@ -4401,9 +5710,15 @@ class PersonEntityData extends DataClass implements Insertable updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, name: data.name.present ? data.name.value : this.name, - faceAssetId: data.faceAssetId.present ? data.faceAssetId.value : this.faceAssetId, - thumbnailPath: data.thumbnailPath.present ? data.thumbnailPath.value : this.thumbnailPath, - isFavorite: data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + thumbnailPath: data.thumbnailPath.present + ? data.thumbnailPath.value + : this.thumbnailPath, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, color: data.color.present ? data.color.value : this.color, birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, @@ -4430,7 +5745,18 @@ class PersonEntityData extends DataClass implements Insertable @override int get hashCode => Object.hash( - id, createdAt, updatedAt, ownerId, name, faceAssetId, thumbnailPath, isFavorite, isHidden, color, birthDate); + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + thumbnailPath, + isFavorite, + isHidden, + color, + birthDate, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -4485,12 +5811,12 @@ class PersonEntityCompanion extends UpdateCompanion { required bool isHidden, this.color = const Value.absent(), this.birthDate = const Value.absent(), - }) : id = Value(id), - ownerId = Value(ownerId), - name = Value(name), - thumbnailPath = Value(thumbnailPath), - isFavorite = Value(isFavorite), - isHidden = Value(isHidden); + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + thumbnailPath = Value(thumbnailPath), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); static Insertable custom({ Expression? id, Expression? createdAt, @@ -4519,18 +5845,19 @@ class PersonEntityCompanion extends UpdateCompanion { }); } - PersonEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? name, - Value? faceAssetId, - Value? thumbnailPath, - Value? isFavorite, - Value? isHidden, - Value? color, - Value? birthDate}) { + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? thumbnailPath, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { return PersonEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -4610,48 +5937,59 @@ class DatabaseAtV2 extends GeneratedDatabase { late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); late final StackEntity stackEntity = StackEntity(this); - late final Index idxLocalAssetChecksum = - Index('idx_local_asset_checksum', 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); - late final Index uQRemoteAssetOwnerChecksum = Index('UQ_remote_asset_owner_checksum', - 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); - late final Index idxRemoteAssetChecksum = - Index('idx_remote_asset_checksum', 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + late final Index uQRemoteAssetOwnerChecksum = Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); late final PartnerEntity partnerEntity = PartnerEntity(this); late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); - late final LocalAlbumAssetEntity localAlbumAssetEntity = LocalAlbumAssetEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); - late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = RemoteAlbumAssetEntity(this); - late final RemoteAlbumUserEntity remoteAlbumUserEntity = RemoteAlbumUserEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); late final MemoryEntity memoryEntity = MemoryEntity(this); late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); late final PersonEntity personEntity = PersonEntity(this); @override - Iterable> get allTables => allSchemaEntities.whereType>(); + Iterable> get allTables => + allSchemaEntities.whereType>(); @override List get allSchemaEntities => [ - userEntity, - remoteAssetEntity, - localAssetEntity, - stackEntity, - idxLocalAssetChecksum, - uQRemoteAssetOwnerChecksum, - idxRemoteAssetChecksum, - userMetadataEntity, - partnerEntity, - localAlbumEntity, - localAlbumAssetEntity, - remoteExifEntity, - remoteAlbumEntity, - remoteAlbumAssetEntity, - remoteAlbumUserEntity, - memoryEntity, - memoryAssetEntity, - personEntity - ]; + userEntity, + remoteAssetEntity, + localAssetEntity, + stackEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + localAlbumEntity, + localAlbumAssetEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + ]; @override int get schemaVersion => 2; @override - DriftDatabaseOptions get options => const DriftDatabaseOptions(storeDateTimeAsText: true); + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); } diff --git a/mobile/test/drift/main/generated/schema_v3.dart b/mobile/test/drift/main/generated/schema_v3.dart index 711cf85253..e4382a9fb9 100644 --- a/mobile/test/drift/main/generated/schema_v3.dart +++ b/mobile/test/drift/main/generated/schema_v3.dart @@ -8,30 +8,79 @@ class UserEntity extends Table with TableInfo { final GeneratedDatabase attachedDatabase; final String? _alias; UserEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isAdmin = GeneratedColumn('is_admin', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_admin" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn email = - GeneratedColumn('email', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn profileImagePath = GeneratedColumn('profile_image_path', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn quotaSizeInBytes = GeneratedColumn('quota_size_in_bytes', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn quotaUsageInBytes = GeneratedColumn('quota_usage_in_bytes', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: false, defaultValue: const CustomExpression('0')); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn profileImagePath = GeneratedColumn( + 'profile_image_path', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn quotaSizeInBytes = GeneratedColumn( + 'quota_size_in_bytes', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn quotaUsageInBytes = GeneratedColumn( + 'quota_usage_in_bytes', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); @override - List get $columns => - [id, name, isAdmin, email, profileImagePath, updatedAt, quotaSizeInBytes, quotaUsageInBytes]; + List get $columns => [ + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -43,17 +92,38 @@ class UserEntity extends Table with TableInfo { UserEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return UserEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - isAdmin: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_admin'])!, - email: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}email'])!, - profileImagePath: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}profile_image_path']), - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - quotaSizeInBytes: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}quota_size_in_bytes']), - quotaUsageInBytes: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}quota_usage_in_bytes'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + profileImagePath: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}profile_image_path'], + ), + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + quotaSizeInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_size_in_bytes'], + ), + quotaUsageInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_usage_in_bytes'], + )!, ); } @@ -77,15 +147,16 @@ class UserEntityData extends DataClass implements Insertable { final DateTime updatedAt; final int? quotaSizeInBytes; final int quotaUsageInBytes; - const UserEntityData( - {required this.id, - required this.name, - required this.isAdmin, - required this.email, - this.profileImagePath, - required this.updatedAt, - this.quotaSizeInBytes, - required this.quotaUsageInBytes}); + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + this.profileImagePath, + required this.updatedAt, + this.quotaSizeInBytes, + required this.quotaUsageInBytes, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -104,7 +175,10 @@ class UserEntityData extends DataClass implements Insertable { return map; } - factory UserEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return UserEntityData( id: serializer.fromJson(json['id']), @@ -132,35 +206,45 @@ class UserEntityData extends DataClass implements Insertable { }; } - UserEntityData copyWith( - {String? id, - String? name, - bool? isAdmin, - String? email, - Value profileImagePath = const Value.absent(), - DateTime? updatedAt, - Value quotaSizeInBytes = const Value.absent(), - int? quotaUsageInBytes}) => - UserEntityData( - id: id ?? this.id, - name: name ?? this.name, - isAdmin: isAdmin ?? this.isAdmin, - email: email ?? this.email, - profileImagePath: profileImagePath.present ? profileImagePath.value : this.profileImagePath, - updatedAt: updatedAt ?? this.updatedAt, - quotaSizeInBytes: quotaSizeInBytes.present ? quotaSizeInBytes.value : this.quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, - ); + UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + Value profileImagePath = const Value.absent(), + DateTime? updatedAt, + Value quotaSizeInBytes = const Value.absent(), + int? quotaUsageInBytes, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath.present + ? profileImagePath.value + : this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes.present + ? quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); UserEntityData copyWithCompanion(UserEntityCompanion data) { return UserEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, email: data.email.present ? data.email.value : this.email, - profileImagePath: data.profileImagePath.present ? data.profileImagePath.value : this.profileImagePath, + profileImagePath: data.profileImagePath.present + ? data.profileImagePath.value + : this.profileImagePath, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - quotaSizeInBytes: data.quotaSizeInBytes.present ? data.quotaSizeInBytes.value : this.quotaSizeInBytes, - quotaUsageInBytes: data.quotaUsageInBytes.present ? data.quotaUsageInBytes.value : this.quotaUsageInBytes, + quotaSizeInBytes: data.quotaSizeInBytes.present + ? data.quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: data.quotaUsageInBytes.present + ? data.quotaUsageInBytes.value + : this.quotaUsageInBytes, ); } @@ -180,8 +264,16 @@ class UserEntityData extends DataClass implements Insertable { } @override - int get hashCode => - Object.hash(id, name, isAdmin, email, profileImagePath, updatedAt, quotaSizeInBytes, quotaUsageInBytes); + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -224,9 +316,9 @@ class UserEntityCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), this.quotaSizeInBytes = const Value.absent(), this.quotaUsageInBytes = const Value.absent(), - }) : id = Value(id), - name = Value(name), - email = Value(email); + }) : id = Value(id), + name = Value(name), + email = Value(email); static Insertable custom({ Expression? id, Expression? name, @@ -249,15 +341,16 @@ class UserEntityCompanion extends UpdateCompanion { }); } - UserEntityCompanion copyWith( - {Value? id, - Value? name, - Value? isAdmin, - Value? email, - Value? profileImagePath, - Value? updatedAt, - Value? quotaSizeInBytes, - Value? quotaUsageInBytes}) { + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? profileImagePath, + Value? updatedAt, + Value? quotaSizeInBytes, + Value? quotaUsageInBytes, + }) { return UserEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -316,75 +409,161 @@ class UserEntityCompanion extends UpdateCompanion { } } -class RemoteAssetEntity extends Table with TableInfo { +class RemoteAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn type = - GeneratedColumn('type', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn width = - GeneratedColumn('width', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn height = - GeneratedColumn('height', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn durationInSeconds = GeneratedColumn('duration_in_seconds', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn checksum = - GeneratedColumn('checksum', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isFavorite = GeneratedColumn('is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn localDateTime = GeneratedColumn('local_date_time', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn thumbHash = - GeneratedColumn('thumb_hash', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn deletedAt = GeneratedColumn('deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn localDateTime = + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn livePhotoVideoId = GeneratedColumn( - 'live_photo_video_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn visibility = - GeneratedColumn('visibility', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn stackId = - GeneratedColumn('stack_id', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - ownerId, - localDateTime, - thumbHash, - deletedAt, - livePhotoVideoId, - visibility, - stackId - ]; + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -396,26 +575,74 @@ class RemoteAssetEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAssetEntityData( - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - type: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}type'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}height']), - durationInSeconds: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}checksum'])!, - isFavorite: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - localDateTime: - attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}local_date_time']), - thumbHash: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}thumb_hash']), - deletedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), - livePhotoVideoId: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}live_photo_video_id']), - visibility: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}visibility'])!, - stackId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}stack_id']), + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), ); } @@ -430,7 +657,8 @@ class RemoteAssetEntity extends Table with TableInfo true; } -class RemoteAssetEntityData extends DataClass implements Insertable { +class RemoteAssetEntityData extends DataClass + implements Insertable { final String name; final int type; final DateTime createdAt; @@ -448,24 +676,25 @@ class RemoteAssetEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -505,7 +734,10 @@ class RemoteAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAssetEntityData( name: serializer.fromJson(json['name']), @@ -551,43 +783,49 @@ class RemoteAssetEntityData extends DataClass implements Insertable width = const Value.absent(), - Value height = const Value.absent(), - Value durationInSeconds = const Value.absent(), - String? id, - String? checksum, - bool? isFavorite, - String? ownerId, - Value localDateTime = const Value.absent(), - Value thumbHash = const Value.absent(), - Value deletedAt = const Value.absent(), - Value livePhotoVideoId = const Value.absent(), - int? visibility, - Value stackId = const Value.absent()}) => - RemoteAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present ? durationInSeconds.value : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum ?? this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - ownerId: ownerId ?? this.ownerId, - localDateTime: localDateTime.present ? localDateTime.value : this.localDateTime, - thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - livePhotoVideoId: livePhotoVideoId.present ? livePhotoVideoId.value : this.livePhotoVideoId, - visibility: visibility ?? this.visibility, - stackId: stackId.present ? stackId.value : this.stackId, - ); + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + ); RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { return RemoteAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -596,16 +834,26 @@ class RemoteAssetEntityData extends DataClass implements Insertable Object.hash(name, type, createdAt, updatedAt, width, height, durationInSeconds, id, checksum, - isFavorite, ownerId, localDateTime, thumbHash, deletedAt, livePhotoVideoId, visibility, stackId); + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -660,7 +925,8 @@ class RemoteAssetEntityData extends DataClass implements Insertable { +class RemoteAssetEntityCompanion + extends UpdateCompanion { final Value name; final Value type; final Value createdAt; @@ -715,12 +981,12 @@ class RemoteAssetEntityCompanion extends UpdateCompanion this.livePhotoVideoId = const Value.absent(), required int visibility, this.stackId = const Value.absent(), - }) : name = Value(name), - type = Value(type), - id = Value(id), - checksum = Value(checksum), - ownerId = Value(ownerId), - visibility = Value(visibility); + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); static Insertable custom({ Expression? name, Expression? type, @@ -761,24 +1027,25 @@ class RemoteAssetEntityCompanion extends UpdateCompanion }); } - RemoteAssetEntityCompanion copyWith( - {Value? name, - Value? type, - Value? createdAt, - Value? updatedAt, - Value? width, - Value? height, - Value? durationInSeconds, - Value? id, - Value? checksum, - Value? isFavorite, - Value? ownerId, - Value? localDateTime, - Value? thumbHash, - Value? deletedAt, - Value? livePhotoVideoId, - Value? visibility, - Value? stackId}) { + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + }) { return RemoteAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -882,43 +1149,110 @@ class RemoteAssetEntityCompanion extends UpdateCompanion } } -class LocalAssetEntity extends Table with TableInfo { +class LocalAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; LocalAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn type = - GeneratedColumn('type', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn width = - GeneratedColumn('width', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn height = - GeneratedColumn('height', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn durationInSeconds = GeneratedColumn('duration_in_seconds', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn checksum = - GeneratedColumn('checksum', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn isFavorite = GeneratedColumn('is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn orientation = GeneratedColumn('orientation', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: false, defaultValue: const CustomExpression('0')); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); @override - List get $columns => - [name, type, createdAt, updatedAt, width, height, durationInSeconds, id, checksum, isFavorite, orientation]; + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -930,18 +1264,50 @@ class LocalAssetEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAssetEntityData( - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - type: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}type'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}height']), - durationInSeconds: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}checksum']), - isFavorite: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - orientation: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}orientation'])!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, ); } @@ -956,7 +1322,8 @@ class LocalAssetEntity extends Table with TableInfo true; } -class LocalAssetEntityData extends DataClass implements Insertable { +class LocalAssetEntityData extends DataClass + implements Insertable { final String name; final int type; final DateTime createdAt; @@ -968,18 +1335,19 @@ class LocalAssetEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -1005,7 +1373,10 @@ class LocalAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAssetEntityData( name: serializer.fromJson(json['name']), @@ -1039,31 +1410,33 @@ class LocalAssetEntityData extends DataClass implements Insertable width = const Value.absent(), - Value height = const Value.absent(), - Value durationInSeconds = const Value.absent(), - String? id, - Value checksum = const Value.absent(), - bool? isFavorite, - int? orientation}) => - LocalAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present ? durationInSeconds.value : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum.present ? checksum.value : this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - orientation: orientation ?? this.orientation, - ); + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { return LocalAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -1072,11 +1445,17 @@ class LocalAssetEntityData extends DataClass implements Insertable Object.hash( - name, type, createdAt, updatedAt, width, height, durationInSeconds, id, checksum, isFavorite, orientation); + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -1155,9 +1545,9 @@ class LocalAssetEntityCompanion extends UpdateCompanion { this.checksum = const Value.absent(), this.isFavorite = const Value.absent(), this.orientation = const Value.absent(), - }) : name = Value(name), - type = Value(type), - id = Value(id); + }) : name = Value(name), + type = Value(type), + id = Value(id); static Insertable custom({ Expression? name, Expression? type, @@ -1186,18 +1576,19 @@ class LocalAssetEntityCompanion extends UpdateCompanion { }); } - LocalAssetEntityCompanion copyWith( - {Value? name, - Value? type, - Value? createdAt, - Value? updatedAt, - Value? width, - Value? height, - Value? durationInSeconds, - Value? id, - Value? checksum, - Value? isFavorite, - Value? orientation}) { + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { return LocalAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -1276,24 +1667,54 @@ class StackEntity extends Table with TableInfo { final GeneratedDatabase attachedDatabase; final String? _alias; StackEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn primaryAssetId = GeneratedColumn('primary_asset_id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); @override - List get $columns => [id, createdAt, updatedAt, ownerId, primaryAssetId]; + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -1305,12 +1726,26 @@ class StackEntity extends Table with TableInfo { StackEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return StackEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - primaryAssetId: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, ); } @@ -1331,12 +1766,13 @@ class StackEntityData extends DataClass implements Insertable { final DateTime updatedAt; final String ownerId; final String primaryAssetId; - const StackEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.primaryAssetId}); + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1348,7 +1784,10 @@ class StackEntityData extends DataClass implements Insertable { return map; } - factory StackEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return StackEntityData( id: serializer.fromJson(json['id']), @@ -1370,22 +1809,28 @@ class StackEntityData extends DataClass implements Insertable { }; } - StackEntityData copyWith( - {String? id, DateTime? createdAt, DateTime? updatedAt, String? ownerId, String? primaryAssetId}) => - StackEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - primaryAssetId: primaryAssetId ?? this.primaryAssetId, - ); + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); StackEntityData copyWithCompanion(StackEntityCompanion data) { return StackEntityData( id: data.id.present ? data.id.value : this.id, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, - primaryAssetId: data.primaryAssetId.present ? data.primaryAssetId.value : this.primaryAssetId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, ); } @@ -1402,7 +1847,8 @@ class StackEntityData extends DataClass implements Insertable { } @override - int get hashCode => Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); @override bool operator ==(Object other) => identical(this, other) || @@ -1433,9 +1879,9 @@ class StackEntityCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), required String ownerId, required String primaryAssetId, - }) : id = Value(id), - ownerId = Value(ownerId), - primaryAssetId = Value(primaryAssetId); + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); static Insertable custom({ Expression? id, Expression? createdAt, @@ -1452,12 +1898,13 @@ class StackEntityCompanion extends UpdateCompanion { }); } - StackEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? primaryAssetId}) { + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { return StackEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -1501,19 +1948,36 @@ class StackEntityCompanion extends UpdateCompanion { } } -class UserMetadataEntity extends Table with TableInfo { +class UserMetadataEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; UserMetadataEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn userId = GeneratedColumn('user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn key = - GeneratedColumn('key', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn value = - GeneratedColumn('value', aliasedName, false, type: DriftSqlType.blob, requiredDuringInsert: true); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); @override List get $columns => [userId, key, value]; @override @@ -1527,9 +1991,18 @@ class UserMetadataEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return UserMetadataEntityData( - userId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - key: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}key'])!, - value: attachedDatabase.typeMapping.read(DriftSqlType.blob, data['${effectivePrefix}value'])!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, ); } @@ -1544,11 +2017,16 @@ class UserMetadataEntity extends Table with TableInfo true; } -class UserMetadataEntityData extends DataClass implements Insertable { +class UserMetadataEntityData extends DataClass + implements Insertable { final String userId; final int key; final Uint8List value; - const UserMetadataEntityData({required this.userId, required this.key, required this.value}); + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1558,7 +2036,10 @@ class UserMetadataEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return UserMetadataEntityData( userId: serializer.fromJson(json['userId']), @@ -1576,11 +2057,15 @@ class UserMetadataEntityData extends DataClass implements Insertable UserMetadataEntityData( - userId: userId ?? this.userId, - key: key ?? this.key, - value: value ?? this.value, - ); + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { return UserMetadataEntityData( userId: data.userId.present ? data.userId.value : this.userId, @@ -1610,7 +2095,8 @@ class UserMetadataEntityData extends DataClass implements Insertable { +class UserMetadataEntityCompanion + extends UpdateCompanion { final Value userId; final Value key; final Value value; @@ -1623,9 +2109,9 @@ class UserMetadataEntityCompanion extends UpdateCompanion custom({ Expression? userId, Expression? key, @@ -1638,7 +2124,11 @@ class UserMetadataEntityCompanion extends UpdateCompanion? userId, Value? key, Value? value}) { + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { return UserMetadataEntityCompanion( userId: userId ?? this.userId, key: key ?? this.key, @@ -1672,24 +2162,43 @@ class UserMetadataEntityCompanion extends UpdateCompanion { +class PartnerEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; PartnerEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn sharedById = GeneratedColumn('shared_by_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn sharedWithId = GeneratedColumn('shared_with_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn inTimeline = GeneratedColumn('in_timeline', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("in_timeline" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); @override List get $columns => [sharedById, sharedWithId, inTimeline]; @override @@ -1703,9 +2212,18 @@ class PartnerEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return PartnerEntityData( - sharedById: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}shared_by_id'])!, - sharedWithId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}shared_with_id'])!, - inTimeline: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!, + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, ); } @@ -1720,11 +2238,16 @@ class PartnerEntity extends Table with TableInfo true; } -class PartnerEntityData extends DataClass implements Insertable { +class PartnerEntityData extends DataClass + implements Insertable { final String sharedById; final String sharedWithId; final bool inTimeline; - const PartnerEntityData({required this.sharedById, required this.sharedWithId, required this.inTimeline}); + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1734,7 +2257,10 @@ class PartnerEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return PartnerEntityData( sharedById: serializer.fromJson(json['sharedById']), @@ -1752,16 +2278,26 @@ class PartnerEntityData extends DataClass implements Insertable PartnerEntityData( - sharedById: sharedById ?? this.sharedById, - sharedWithId: sharedWithId ?? this.sharedWithId, - inTimeline: inTimeline ?? this.inTimeline, - ); + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { return PartnerEntityData( - sharedById: data.sharedById.present ? data.sharedById.value : this.sharedById, - sharedWithId: data.sharedWithId.present ? data.sharedWithId.value : this.sharedWithId, - inTimeline: data.inTimeline.present ? data.inTimeline.value : this.inTimeline, + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, ); } @@ -1799,8 +2335,8 @@ class PartnerEntityCompanion extends UpdateCompanion { required String sharedById, required String sharedWithId, this.inTimeline = const Value.absent(), - }) : sharedById = Value(sharedById), - sharedWithId = Value(sharedWithId); + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); static Insertable custom({ Expression? sharedById, Expression? sharedWithId, @@ -1813,7 +2349,11 @@ class PartnerEntityCompanion extends UpdateCompanion { }); } - PartnerEntityCompanion copyWith({Value? sharedById, Value? sharedWithId, Value? inTimeline}) { + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { return PartnerEntityCompanion( sharedById: sharedById ?? this.sharedById, sharedWithId: sharedWithId ?? this.sharedWithId, @@ -1847,32 +2387,71 @@ class PartnerEntityCompanion extends UpdateCompanion { } } -class LocalAlbumEntity extends Table with TableInfo { +class LocalAlbumEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; LocalAlbumEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn backupSelection = - GeneratedColumn('backup_selection', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn isIosSharedAlbum = GeneratedColumn('is_ios_shared_album', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_ios_shared_album" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn marker_ = GeneratedColumn('marker', aliasedName, true, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("marker" IN (0, 1))')); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); @override - List get $columns => [id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_]; + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -1884,13 +2463,30 @@ class LocalAlbumEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAlbumEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - backupSelection: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}backup_selection'])!, - isIosSharedAlbum: - attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_ios_shared_album'])!, - marker_: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}marker']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), ); } @@ -1905,20 +2501,22 @@ class LocalAlbumEntity extends Table with TableInfo true; } -class LocalAlbumEntityData extends DataClass implements Insertable { +class LocalAlbumEntityData extends DataClass + implements Insertable { final String id; final String name; final DateTime updatedAt; final int backupSelection; final bool isIosSharedAlbum; final bool? marker_; - const LocalAlbumEntityData( - {required this.id, - required this.name, - required this.updatedAt, - required this.backupSelection, - required this.isIosSharedAlbum, - this.marker_}); + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1933,7 +2531,10 @@ class LocalAlbumEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAlbumEntityData( id: serializer.fromJson(json['id']), @@ -1957,28 +2558,32 @@ class LocalAlbumEntityData extends DataClass implements Insertable marker_ = const Value.absent()}) => - LocalAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - updatedAt: updatedAt ?? this.updatedAt, - backupSelection: backupSelection ?? this.backupSelection, - isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, - marker_: marker_.present ? marker_.value : this.marker_, - ); + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { return LocalAlbumEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - backupSelection: data.backupSelection.present ? data.backupSelection.value : this.backupSelection, - isIosSharedAlbum: data.isIosSharedAlbum.present ? data.isIosSharedAlbum.value : this.isIosSharedAlbum, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, marker_: data.marker_.present ? data.marker_.value : this.marker_, ); } @@ -1997,7 +2602,14 @@ class LocalAlbumEntityData extends DataClass implements Insertable Object.hash(id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_); + int get hashCode => Object.hash( + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -2032,9 +2644,9 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { required int backupSelection, this.isIosSharedAlbum = const Value.absent(), this.marker_ = const Value.absent(), - }) : id = Value(id), - name = Value(name), - backupSelection = Value(backupSelection); + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); static Insertable custom({ Expression? id, Expression? name, @@ -2053,13 +2665,14 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { }); } - LocalAlbumEntityCompanion copyWith( - {Value? id, - Value? name, - Value? updatedAt, - Value? backupSelection, - Value? isIosSharedAlbum, - Value? marker_}) { + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_, + }) { return LocalAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -2108,19 +2721,32 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { } } -class LocalAlbumAssetEntity extends Table with TableInfo { +class LocalAlbumAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES local_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn albumId = GeneratedColumn('album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES local_album_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -2131,11 +2757,20 @@ class LocalAlbumAssetEntity extends Table with TableInfo get $primaryKey => {assetId, albumId}; @override - LocalAlbumAssetEntityData map(Map data, {String? tablePrefix}) { + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -2150,10 +2785,14 @@ class LocalAlbumAssetEntity extends Table with TableInfo true; } -class LocalAlbumAssetEntityData extends DataClass implements Insertable { +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { final String assetId; final String albumId; - const LocalAlbumAssetEntityData({required this.assetId, required this.albumId}); + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -2162,7 +2801,10 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -2178,11 +2820,14 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable LocalAlbumAssetEntityData( + LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + LocalAlbumAssetEntityData( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, ); - LocalAlbumAssetEntityData copyWithCompanion(LocalAlbumAssetEntityCompanion data) { + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data, + ) { return LocalAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -2203,10 +2848,13 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable identical(this, other) || - (other is LocalAlbumAssetEntityData && other.assetId == this.assetId && other.albumId == this.albumId); + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); } -class LocalAlbumAssetEntityCompanion extends UpdateCompanion { +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { final Value assetId; final Value albumId; const LocalAlbumAssetEntityCompanion({ @@ -2216,8 +2864,8 @@ class LocalAlbumAssetEntityCompanion extends UpdateCompanion custom({ Expression? assetId, Expression? albumId, @@ -2228,7 +2876,10 @@ class LocalAlbumAssetEntityCompanion extends UpdateCompanion? assetId, Value? albumId}) { + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { return LocalAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, @@ -2257,83 +2908,195 @@ class LocalAlbumAssetEntityCompanion extends UpdateCompanion { +class RemoteExifEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteExifEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn city = - GeneratedColumn('city', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn state = - GeneratedColumn('state', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn country = - GeneratedColumn('country', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn dateTimeOriginal = GeneratedColumn( - 'date_time_original', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn description = - GeneratedColumn('description', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn height = - GeneratedColumn('height', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn width = - GeneratedColumn('width', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn exposureTime = GeneratedColumn('exposure_time', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn fNumber = - GeneratedColumn('f_number', aliasedName, true, type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn fileSize = - GeneratedColumn('file_size', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn focalLength = GeneratedColumn('focal_length', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn latitude = - GeneratedColumn('latitude', aliasedName, true, type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn longitude = - GeneratedColumn('longitude', aliasedName, true, type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn iso = - GeneratedColumn('iso', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn make = - GeneratedColumn('make', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn model = - GeneratedColumn('model', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn lens = - GeneratedColumn('lens', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn orientation = - GeneratedColumn('orientation', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn timeZone = - GeneratedColumn('time_zone', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn rating = - GeneratedColumn('rating', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn projectionType = GeneratedColumn('projection_type', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn city = GeneratedColumn( + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn state = GeneratedColumn( + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn country = GeneratedColumn( + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn make = GeneratedColumn( + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn model = GeneratedColumn( + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]; + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -2345,29 +3108,94 @@ class RemoteExifEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteExifEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - city: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}city']), - state: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}state']), - country: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}country']), - dateTimeOriginal: - attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}date_time_original']), - description: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}description']), - height: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}height']), - width: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}width']), - exposureTime: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}exposure_time']), - fNumber: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}f_number']), - fileSize: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}file_size']), - focalLength: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}focal_length']), - latitude: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}latitude']), - longitude: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}longitude']), - iso: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}iso']), - make: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}make']), - model: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}model']), - lens: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}lens']), - orientation: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}orientation']), - timeZone: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}time_zone']), - rating: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}rating']), - projectionType: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}projection_type']), + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), ); } @@ -2382,7 +3210,8 @@ class RemoteExifEntity extends Table with TableInfo true; } -class RemoteExifEntityData extends DataClass implements Insertable { +class RemoteExifEntityData extends DataClass + implements Insertable { final String assetId; final String? city; final String? state; @@ -2405,29 +3234,30 @@ class RemoteExifEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -2498,14 +3328,19 @@ class RemoteExifEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteExifEntityData( assetId: serializer.fromJson(json['assetId']), city: serializer.fromJson(json['city']), state: serializer.fromJson(json['state']), country: serializer.fromJson(json['country']), - dateTimeOriginal: serializer.fromJson(json['dateTimeOriginal']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), description: serializer.fromJson(json['description']), height: serializer.fromJson(json['height']), width: serializer.fromJson(json['width']), @@ -2554,77 +3389,93 @@ class RemoteExifEntityData extends DataClass implements Insertable city = const Value.absent(), - Value state = const Value.absent(), - Value country = const Value.absent(), - Value dateTimeOriginal = const Value.absent(), - Value description = const Value.absent(), - Value height = const Value.absent(), - Value width = const Value.absent(), - Value exposureTime = const Value.absent(), - Value fNumber = const Value.absent(), - Value fileSize = const Value.absent(), - Value focalLength = const Value.absent(), - Value latitude = const Value.absent(), - Value longitude = const Value.absent(), - Value iso = const Value.absent(), - Value make = const Value.absent(), - Value model = const Value.absent(), - Value lens = const Value.absent(), - Value orientation = const Value.absent(), - Value timeZone = const Value.absent(), - Value rating = const Value.absent(), - Value projectionType = const Value.absent()}) => - RemoteExifEntityData( - assetId: assetId ?? this.assetId, - city: city.present ? city.value : this.city, - state: state.present ? state.value : this.state, - country: country.present ? country.value : this.country, - dateTimeOriginal: dateTimeOriginal.present ? dateTimeOriginal.value : this.dateTimeOriginal, - description: description.present ? description.value : this.description, - height: height.present ? height.value : this.height, - width: width.present ? width.value : this.width, - exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, - fNumber: fNumber.present ? fNumber.value : this.fNumber, - fileSize: fileSize.present ? fileSize.value : this.fileSize, - focalLength: focalLength.present ? focalLength.value : this.focalLength, - latitude: latitude.present ? latitude.value : this.latitude, - longitude: longitude.present ? longitude.value : this.longitude, - iso: iso.present ? iso.value : this.iso, - make: make.present ? make.value : this.make, - model: model.present ? model.value : this.model, - lens: lens.present ? lens.value : this.lens, - orientation: orientation.present ? orientation.value : this.orientation, - timeZone: timeZone.present ? timeZone.value : this.timeZone, - rating: rating.present ? rating.value : this.rating, - projectionType: projectionType.present ? projectionType.value : this.projectionType, - ); + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { return RemoteExifEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, city: data.city.present ? data.city.value : this.city, state: data.state.present ? data.state.value : this.state, country: data.country.present ? data.country.value : this.country, - dateTimeOriginal: data.dateTimeOriginal.present ? data.dateTimeOriginal.value : this.dateTimeOriginal, - description: data.description.present ? data.description.value : this.description, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: data.description.present + ? data.description.value + : this.description, height: data.height.present ? data.height.value : this.height, width: data.width.present ? data.width.value : this.width, - exposureTime: data.exposureTime.present ? data.exposureTime.value : this.exposureTime, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, - focalLength: data.focalLength.present ? data.focalLength.value : this.focalLength, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, latitude: data.latitude.present ? data.latitude.value : this.latitude, longitude: data.longitude.present ? data.longitude.value : this.longitude, iso: data.iso.present ? data.iso.value : this.iso, make: data.make.present ? data.make.value : this.make, model: data.model.present ? data.model.value : this.model, lens: data.lens.present ? data.lens.value : this.lens, - orientation: data.orientation.present ? data.orientation.value : this.orientation, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, rating: data.rating.present ? data.rating.value : this.rating, - projectionType: data.projectionType.present ? data.projectionType.value : this.projectionType, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, ); } @@ -2659,29 +3510,29 @@ class RemoteExifEntityData extends DataClass implements Insertable Object.hashAll([ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]); + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); @override bool operator ==(Object other) => identical(this, other) || @@ -2831,29 +3682,30 @@ class RemoteExifEntityCompanion extends UpdateCompanion { }); } - RemoteExifEntityCompanion copyWith( - {Value? assetId, - Value? city, - Value? state, - Value? country, - Value? dateTimeOriginal, - Value? description, - Value? height, - Value? width, - Value? exposureTime, - Value? fNumber, - Value? fileSize, - Value? focalLength, - Value? latitude, - Value? longitude, - Value? iso, - Value? make, - Value? model, - Value? lens, - Value? orientation, - Value? timeZone, - Value? rating, - Value? projectionType}) { + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { return RemoteExifEntityCompanion( assetId: assetId ?? this.assetId, city: city ?? this.city, @@ -2982,43 +3834,100 @@ class RemoteExifEntityCompanion extends UpdateCompanion { } } -class RemoteAlbumEntity extends Table with TableInfo { +class RemoteAlbumEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAlbumEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn description = GeneratedColumn('description', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: false, defaultValue: const CustomExpression('\'\'')); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn thumbnailAssetId = GeneratedColumn('thumbnail_asset_id', aliasedName, true, - type: DriftSqlType.string, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE SET NULL')); - late final GeneratedColumn isActivityEnabled = GeneratedColumn('is_activity_enabled', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_activity_enabled" IN (0, 1))'), - defaultValue: const CustomExpression('1')); - late final GeneratedColumn order = - GeneratedColumn('order', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn order = GeneratedColumn( + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); @override - List get $columns => - [id, name, description, createdAt, updatedAt, ownerId, thumbnailAssetId, isActivityEnabled, order]; + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -3030,17 +3939,42 @@ class RemoteAlbumEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - description: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}description'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - thumbnailAssetId: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}thumbnail_asset_id']), - isActivityEnabled: - attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_activity_enabled'])!, - order: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}order'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, ); } @@ -3055,7 +3989,8 @@ class RemoteAlbumEntity extends Table with TableInfo true; } -class RemoteAlbumEntityData extends DataClass implements Insertable { +class RemoteAlbumEntityData extends DataClass + implements Insertable { final String id; final String name; final String description; @@ -3065,16 +4000,17 @@ class RemoteAlbumEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -3092,7 +4028,10 @@ class RemoteAlbumEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumEntityData( id: serializer.fromJson(json['id']), @@ -3122,37 +4061,45 @@ class RemoteAlbumEntityData extends DataClass implements Insertable thumbnailAssetId = const Value.absent(), - bool? isActivityEnabled, - int? order}) => - RemoteAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - description: description ?? this.description, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - thumbnailAssetId: thumbnailAssetId.present ? thumbnailAssetId.value : this.thumbnailAssetId, - isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, - order: order ?? this.order, - ); + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { return RemoteAlbumEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, - description: data.description.present ? data.description.value : this.description, + description: data.description.present + ? data.description.value + : this.description, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, - thumbnailAssetId: data.thumbnailAssetId.present ? data.thumbnailAssetId.value : this.thumbnailAssetId, - isActivityEnabled: data.isActivityEnabled.present ? data.isActivityEnabled.value : this.isActivityEnabled, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, order: data.order.present ? data.order.value : this.order, ); } @@ -3174,8 +4121,17 @@ class RemoteAlbumEntityData extends DataClass implements Insertable - Object.hash(id, name, description, createdAt, updatedAt, ownerId, thumbnailAssetId, isActivityEnabled, order); + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -3191,7 +4147,8 @@ class RemoteAlbumEntityData extends DataClass implements Insertable { +class RemoteAlbumEntityCompanion + extends UpdateCompanion { final Value id; final Value name; final Value description; @@ -3222,10 +4179,10 @@ class RemoteAlbumEntityCompanion extends UpdateCompanion this.thumbnailAssetId = const Value.absent(), this.isActivityEnabled = const Value.absent(), required int order, - }) : id = Value(id), - name = Value(name), - ownerId = Value(ownerId), - order = Value(order); + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); static Insertable custom({ Expression? id, Expression? name, @@ -3250,16 +4207,17 @@ class RemoteAlbumEntityCompanion extends UpdateCompanion }); } - RemoteAlbumEntityCompanion copyWith( - {Value? id, - Value? name, - Value? description, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? thumbnailAssetId, - Value? isActivityEnabled, - Value? order}) { + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { return RemoteAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -3323,19 +4281,32 @@ class RemoteAlbumEntityCompanion extends UpdateCompanion } } -class RemoteAlbumAssetEntity extends Table with TableInfo { +class RemoteAlbumAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn albumId = GeneratedColumn('album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -3346,11 +4317,20 @@ class RemoteAlbumAssetEntity extends Table with TableInfo get $primaryKey => {assetId, albumId}; @override - RemoteAlbumAssetEntityData map(Map data, {String? tablePrefix}) { + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -3365,10 +4345,14 @@ class RemoteAlbumAssetEntity extends Table with TableInfo true; } -class RemoteAlbumAssetEntityData extends DataClass implements Insertable { +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { final String assetId; final String albumId; - const RemoteAlbumAssetEntityData({required this.assetId, required this.albumId}); + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3377,7 +4361,10 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -3393,11 +4380,14 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable RemoteAlbumAssetEntityData( + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, ); - RemoteAlbumAssetEntityData copyWithCompanion(RemoteAlbumAssetEntityCompanion data) { + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data, + ) { return RemoteAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -3418,10 +4408,13 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable identical(this, other) || - (other is RemoteAlbumAssetEntityData && other.assetId == this.assetId && other.albumId == this.albumId); + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); } -class RemoteAlbumAssetEntityCompanion extends UpdateCompanion { +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { final Value assetId; final Value albumId; const RemoteAlbumAssetEntityCompanion({ @@ -3431,8 +4424,8 @@ class RemoteAlbumAssetEntityCompanion extends UpdateCompanion custom({ Expression? assetId, Expression? albumId, @@ -3443,7 +4436,10 @@ class RemoteAlbumAssetEntityCompanion extends UpdateCompanion? assetId, Value? albumId}) { + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { return RemoteAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, @@ -3472,21 +4468,39 @@ class RemoteAlbumAssetEntityCompanion extends UpdateCompanion { +class RemoteAlbumUserEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn albumId = GeneratedColumn('album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_album_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn userId = GeneratedColumn('user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn role = - GeneratedColumn('role', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn role = GeneratedColumn( + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); @override List get $columns => [albumId, userId, role]; @override @@ -3497,12 +4511,24 @@ class RemoteAlbumUserEntity extends Table with TableInfo get $primaryKey => {albumId, userId}; @override - RemoteAlbumUserEntityData map(Map data, {String? tablePrefix}) { + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumUserEntityData( - albumId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, - userId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - role: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}role'])!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, ); } @@ -3517,11 +4543,16 @@ class RemoteAlbumUserEntity extends Table with TableInfo true; } -class RemoteAlbumUserEntityData extends DataClass implements Insertable { +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { final String albumId; final String userId; final int role; - const RemoteAlbumUserEntityData({required this.albumId, required this.userId, required this.role}); + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3531,7 +4562,10 @@ class RemoteAlbumUserEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumUserEntityData( albumId: serializer.fromJson(json['albumId']), @@ -3549,12 +4583,18 @@ class RemoteAlbumUserEntityData extends DataClass implements Insertable RemoteAlbumUserEntityData( - albumId: albumId ?? this.albumId, - userId: userId ?? this.userId, - role: role ?? this.role, - ); - RemoteAlbumUserEntityData copyWithCompanion(RemoteAlbumUserEntityCompanion data) { + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data, + ) { return RemoteAlbumUserEntityData( albumId: data.albumId.present ? data.albumId.value : this.albumId, userId: data.userId.present ? data.userId.value : this.userId, @@ -3583,7 +4623,8 @@ class RemoteAlbumUserEntityData extends DataClass implements Insertable { +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { final Value albumId; final Value userId; final Value role; @@ -3596,9 +4637,9 @@ class RemoteAlbumUserEntityCompanion extends UpdateCompanion custom({ Expression? albumId, Expression? userId, @@ -3611,7 +4652,11 @@ class RemoteAlbumUserEntityCompanion extends UpdateCompanion? albumId, Value? userId, Value? role}) { + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { return RemoteAlbumUserEntityCompanion( albumId: albumId ?? this.albumId, userId: userId ?? this.userId, @@ -3645,47 +4690,120 @@ class RemoteAlbumUserEntityCompanion extends UpdateCompanion { +class MemoryEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; MemoryEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn deletedAt = GeneratedColumn('deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn type = - GeneratedColumn('type', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn data = - GeneratedColumn('data', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isSaved = GeneratedColumn('is_saved', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_saved" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn memoryAt = GeneratedColumn('memory_at', aliasedName, false, - type: DriftSqlType.dateTime, requiredDuringInsert: true); - late final GeneratedColumn seenAt = - GeneratedColumn('seen_at', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn showAt = - GeneratedColumn('show_at', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn hideAt = - GeneratedColumn('hide_at', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn data = GeneratedColumn( + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override - List get $columns => - [id, createdAt, updatedAt, deletedAt, ownerId, type, data, isSaved, memoryAt, seenAt, showAt, hideAt]; + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -3697,18 +4815,54 @@ class MemoryEntity extends Table with TableInfo MemoryEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return MemoryEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - deletedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - type: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}type'])!, - data: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}data'])!, - isSaved: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_saved'])!, - memoryAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}memory_at'])!, - seenAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}seen_at']), - showAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}show_at']), - hideAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}hide_at']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), ); } @@ -3723,7 +4877,8 @@ class MemoryEntity extends Table with TableInfo bool get isStrict => true; } -class MemoryEntityData extends DataClass implements Insertable { +class MemoryEntityData extends DataClass + implements Insertable { final String id; final DateTime createdAt; final DateTime updatedAt; @@ -3736,19 +4891,20 @@ class MemoryEntityData extends DataClass implements Insertable final DateTime? seenAt; final DateTime? showAt; final DateTime? hideAt; - const MemoryEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - this.deletedAt, - required this.ownerId, - required this.type, - required this.data, - required this.isSaved, - required this.memoryAt, - this.seenAt, - this.showAt, - this.hideAt}); + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3775,7 +4931,10 @@ class MemoryEntityData extends DataClass implements Insertable return map; } - factory MemoryEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return MemoryEntityData( id: serializer.fromJson(json['id']), @@ -3811,33 +4970,33 @@ class MemoryEntityData extends DataClass implements Insertable }; } - MemoryEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - Value deletedAt = const Value.absent(), - String? ownerId, - int? type, - String? data, - bool? isSaved, - DateTime? memoryAt, - Value seenAt = const Value.absent(), - Value showAt = const Value.absent(), - Value hideAt = const Value.absent()}) => - MemoryEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - ownerId: ownerId ?? this.ownerId, - type: type ?? this.type, - data: data ?? this.data, - isSaved: isSaved ?? this.isSaved, - memoryAt: memoryAt ?? this.memoryAt, - seenAt: seenAt.present ? seenAt.value : this.seenAt, - showAt: showAt.present ? showAt.value : this.showAt, - hideAt: hideAt.present ? hideAt.value : this.hideAt, - ); + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { return MemoryEntityData( id: data.id.present ? data.id.value : this.id, @@ -3875,8 +5034,20 @@ class MemoryEntityData extends DataClass implements Insertable } @override - int get hashCode => - Object.hash(id, createdAt, updatedAt, deletedAt, ownerId, type, data, isSaved, memoryAt, seenAt, showAt, hideAt); + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -3935,11 +5106,11 @@ class MemoryEntityCompanion extends UpdateCompanion { this.seenAt = const Value.absent(), this.showAt = const Value.absent(), this.hideAt = const Value.absent(), - }) : id = Value(id), - ownerId = Value(ownerId), - type = Value(type), - data = Value(data), - memoryAt = Value(memoryAt); + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); static Insertable custom({ Expression? id, Expression? createdAt, @@ -3970,19 +5141,20 @@ class MemoryEntityCompanion extends UpdateCompanion { }); } - MemoryEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? deletedAt, - Value? ownerId, - Value? type, - Value? data, - Value? isSaved, - Value? memoryAt, - Value? seenAt, - Value? showAt, - Value? hideAt}) { + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { return MemoryEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -4061,19 +5233,32 @@ class MemoryEntityCompanion extends UpdateCompanion { } } -class MemoryAssetEntity extends Table with TableInfo { +class MemoryAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; MemoryAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn memoryId = GeneratedColumn('memory_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES memory_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, memoryId]; @override @@ -4087,8 +5272,14 @@ class MemoryAssetEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return MemoryAssetEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - memoryId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}memory_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, ); } @@ -4103,7 +5294,8 @@ class MemoryAssetEntity extends Table with TableInfo true; } -class MemoryAssetEntityData extends DataClass implements Insertable { +class MemoryAssetEntityData extends DataClass + implements Insertable { final String assetId; final String memoryId; const MemoryAssetEntityData({required this.assetId, required this.memoryId}); @@ -4115,7 +5307,10 @@ class MemoryAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return MemoryAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -4131,7 +5326,8 @@ class MemoryAssetEntityData extends DataClass implements Insertable MemoryAssetEntityData( + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( assetId: assetId ?? this.assetId, memoryId: memoryId ?? this.memoryId, ); @@ -4156,10 +5352,13 @@ class MemoryAssetEntityData extends DataClass implements Insertable identical(this, other) || - (other is MemoryAssetEntityData && other.assetId == this.assetId && other.memoryId == this.memoryId); + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); } -class MemoryAssetEntityCompanion extends UpdateCompanion { +class MemoryAssetEntityCompanion + extends UpdateCompanion { final Value assetId; final Value memoryId; const MemoryAssetEntityCompanion({ @@ -4169,8 +5368,8 @@ class MemoryAssetEntityCompanion extends UpdateCompanion MemoryAssetEntityCompanion.insert({ required String assetId, required String memoryId, - }) : assetId = Value(assetId), - memoryId = Value(memoryId); + }) : assetId = Value(assetId), + memoryId = Value(memoryId); static Insertable custom({ Expression? assetId, Expression? memoryId, @@ -4181,7 +5380,10 @@ class MemoryAssetEntityCompanion extends UpdateCompanion }); } - MemoryAssetEntityCompanion copyWith({Value? assetId, Value? memoryId}) { + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { return MemoryAssetEntityCompanion( assetId: assetId ?? this.assetId, memoryId: memoryId ?? this.memoryId, @@ -4210,46 +5412,114 @@ class MemoryAssetEntityCompanion extends UpdateCompanion } } -class PersonEntity extends Table with TableInfo { +class PersonEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; PersonEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn faceAssetId = GeneratedColumn('face_asset_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn thumbnailPath = GeneratedColumn('thumbnail_path', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isFavorite = GeneratedColumn('is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))')); - late final GeneratedColumn isHidden = GeneratedColumn('is_hidden', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_hidden" IN (0, 1))')); - late final GeneratedColumn color = - GeneratedColumn('color', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn birthDate = GeneratedColumn('birth_date', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbnailPath = GeneratedColumn( + 'thumbnail_path', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override - List get $columns => - [id, createdAt, updatedAt, ownerId, name, faceAssetId, thumbnailPath, isFavorite, isHidden, color, birthDate]; + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + thumbnailPath, + isFavorite, + isHidden, + color, + birthDate, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -4261,17 +5531,50 @@ class PersonEntity extends Table with TableInfo PersonEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return PersonEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - faceAssetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}face_asset_id']), - thumbnailPath: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}thumbnail_path'])!, - isFavorite: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - isHidden: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_hidden'])!, - color: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}color']), - birthDate: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}birth_date']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + thumbnailPath: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_path'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), ); } @@ -4286,7 +5589,8 @@ class PersonEntity extends Table with TableInfo bool get isStrict => true; } -class PersonEntityData extends DataClass implements Insertable { +class PersonEntityData extends DataClass + implements Insertable { final String id; final DateTime createdAt; final DateTime updatedAt; @@ -4298,18 +5602,19 @@ class PersonEntityData extends DataClass implements Insertable final bool isHidden; final String? color; final DateTime? birthDate; - const PersonEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.name, - this.faceAssetId, - required this.thumbnailPath, - required this.isFavorite, - required this.isHidden, - this.color, - this.birthDate}); + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.thumbnailPath, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -4333,7 +5638,10 @@ class PersonEntityData extends DataClass implements Insertable return map; } - factory PersonEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return PersonEntityData( id: serializer.fromJson(json['id']), @@ -4367,31 +5675,31 @@ class PersonEntityData extends DataClass implements Insertable }; } - PersonEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? name, - Value faceAssetId = const Value.absent(), - String? thumbnailPath, - bool? isFavorite, - bool? isHidden, - Value color = const Value.absent(), - Value birthDate = const Value.absent()}) => - PersonEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - name: name ?? this.name, - faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, - thumbnailPath: thumbnailPath ?? this.thumbnailPath, - isFavorite: isFavorite ?? this.isFavorite, - isHidden: isHidden ?? this.isHidden, - color: color.present ? color.value : this.color, - birthDate: birthDate.present ? birthDate.value : this.birthDate, - ); + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + String? thumbnailPath, + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + thumbnailPath: thumbnailPath ?? this.thumbnailPath, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); PersonEntityData copyWithCompanion(PersonEntityCompanion data) { return PersonEntityData( id: data.id.present ? data.id.value : this.id, @@ -4399,9 +5707,15 @@ class PersonEntityData extends DataClass implements Insertable updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, name: data.name.present ? data.name.value : this.name, - faceAssetId: data.faceAssetId.present ? data.faceAssetId.value : this.faceAssetId, - thumbnailPath: data.thumbnailPath.present ? data.thumbnailPath.value : this.thumbnailPath, - isFavorite: data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + thumbnailPath: data.thumbnailPath.present + ? data.thumbnailPath.value + : this.thumbnailPath, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, color: data.color.present ? data.color.value : this.color, birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, @@ -4428,7 +5742,18 @@ class PersonEntityData extends DataClass implements Insertable @override int get hashCode => Object.hash( - id, createdAt, updatedAt, ownerId, name, faceAssetId, thumbnailPath, isFavorite, isHidden, color, birthDate); + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + thumbnailPath, + isFavorite, + isHidden, + color, + birthDate, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -4483,12 +5808,12 @@ class PersonEntityCompanion extends UpdateCompanion { required bool isHidden, this.color = const Value.absent(), this.birthDate = const Value.absent(), - }) : id = Value(id), - ownerId = Value(ownerId), - name = Value(name), - thumbnailPath = Value(thumbnailPath), - isFavorite = Value(isFavorite), - isHidden = Value(isHidden); + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + thumbnailPath = Value(thumbnailPath), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); static Insertable custom({ Expression? id, Expression? createdAt, @@ -4517,18 +5842,19 @@ class PersonEntityCompanion extends UpdateCompanion { }); } - PersonEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? name, - Value? faceAssetId, - Value? thumbnailPath, - Value? isFavorite, - Value? isHidden, - Value? color, - Value? birthDate}) { + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? thumbnailPath, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { return PersonEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -4608,48 +5934,59 @@ class DatabaseAtV3 extends GeneratedDatabase { late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); late final StackEntity stackEntity = StackEntity(this); - late final Index idxLocalAssetChecksum = - Index('idx_local_asset_checksum', 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); - late final Index uQRemoteAssetOwnerChecksum = Index('UQ_remote_asset_owner_checksum', - 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); - late final Index idxRemoteAssetChecksum = - Index('idx_remote_asset_checksum', 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + late final Index uQRemoteAssetOwnerChecksum = Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); late final PartnerEntity partnerEntity = PartnerEntity(this); late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); - late final LocalAlbumAssetEntity localAlbumAssetEntity = LocalAlbumAssetEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); - late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = RemoteAlbumAssetEntity(this); - late final RemoteAlbumUserEntity remoteAlbumUserEntity = RemoteAlbumUserEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); late final MemoryEntity memoryEntity = MemoryEntity(this); late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); late final PersonEntity personEntity = PersonEntity(this); @override - Iterable> get allTables => allSchemaEntities.whereType>(); + Iterable> get allTables => + allSchemaEntities.whereType>(); @override List get allSchemaEntities => [ - userEntity, - remoteAssetEntity, - localAssetEntity, - stackEntity, - idxLocalAssetChecksum, - uQRemoteAssetOwnerChecksum, - idxRemoteAssetChecksum, - userMetadataEntity, - partnerEntity, - localAlbumEntity, - localAlbumAssetEntity, - remoteExifEntity, - remoteAlbumEntity, - remoteAlbumAssetEntity, - remoteAlbumUserEntity, - memoryEntity, - memoryAssetEntity, - personEntity - ]; + userEntity, + remoteAssetEntity, + localAssetEntity, + stackEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + localAlbumEntity, + localAlbumAssetEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + ]; @override int get schemaVersion => 3; @override - DriftDatabaseOptions get options => const DriftDatabaseOptions(storeDateTimeAsText: true); + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); } diff --git a/mobile/test/drift/main/generated/schema_v4.dart b/mobile/test/drift/main/generated/schema_v4.dart index f54e5e9644..9eabea714f 100644 --- a/mobile/test/drift/main/generated/schema_v4.dart +++ b/mobile/test/drift/main/generated/schema_v4.dart @@ -8,30 +8,79 @@ class UserEntity extends Table with TableInfo { final GeneratedDatabase attachedDatabase; final String? _alias; UserEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isAdmin = GeneratedColumn('is_admin', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_admin" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn email = - GeneratedColumn('email', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn profileImagePath = GeneratedColumn('profile_image_path', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn quotaSizeInBytes = GeneratedColumn('quota_size_in_bytes', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn quotaUsageInBytes = GeneratedColumn('quota_usage_in_bytes', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: false, defaultValue: const CustomExpression('0')); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn profileImagePath = GeneratedColumn( + 'profile_image_path', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn quotaSizeInBytes = GeneratedColumn( + 'quota_size_in_bytes', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn quotaUsageInBytes = GeneratedColumn( + 'quota_usage_in_bytes', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); @override - List get $columns => - [id, name, isAdmin, email, profileImagePath, updatedAt, quotaSizeInBytes, quotaUsageInBytes]; + List get $columns => [ + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -43,17 +92,38 @@ class UserEntity extends Table with TableInfo { UserEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return UserEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - isAdmin: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_admin'])!, - email: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}email'])!, - profileImagePath: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}profile_image_path']), - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - quotaSizeInBytes: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}quota_size_in_bytes']), - quotaUsageInBytes: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}quota_usage_in_bytes'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + profileImagePath: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}profile_image_path'], + ), + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + quotaSizeInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_size_in_bytes'], + ), + quotaUsageInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_usage_in_bytes'], + )!, ); } @@ -77,15 +147,16 @@ class UserEntityData extends DataClass implements Insertable { final DateTime updatedAt; final int? quotaSizeInBytes; final int quotaUsageInBytes; - const UserEntityData( - {required this.id, - required this.name, - required this.isAdmin, - required this.email, - this.profileImagePath, - required this.updatedAt, - this.quotaSizeInBytes, - required this.quotaUsageInBytes}); + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + this.profileImagePath, + required this.updatedAt, + this.quotaSizeInBytes, + required this.quotaUsageInBytes, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -104,7 +175,10 @@ class UserEntityData extends DataClass implements Insertable { return map; } - factory UserEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return UserEntityData( id: serializer.fromJson(json['id']), @@ -132,35 +206,45 @@ class UserEntityData extends DataClass implements Insertable { }; } - UserEntityData copyWith( - {String? id, - String? name, - bool? isAdmin, - String? email, - Value profileImagePath = const Value.absent(), - DateTime? updatedAt, - Value quotaSizeInBytes = const Value.absent(), - int? quotaUsageInBytes}) => - UserEntityData( - id: id ?? this.id, - name: name ?? this.name, - isAdmin: isAdmin ?? this.isAdmin, - email: email ?? this.email, - profileImagePath: profileImagePath.present ? profileImagePath.value : this.profileImagePath, - updatedAt: updatedAt ?? this.updatedAt, - quotaSizeInBytes: quotaSizeInBytes.present ? quotaSizeInBytes.value : this.quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, - ); + UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + Value profileImagePath = const Value.absent(), + DateTime? updatedAt, + Value quotaSizeInBytes = const Value.absent(), + int? quotaUsageInBytes, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath.present + ? profileImagePath.value + : this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes.present + ? quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); UserEntityData copyWithCompanion(UserEntityCompanion data) { return UserEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, email: data.email.present ? data.email.value : this.email, - profileImagePath: data.profileImagePath.present ? data.profileImagePath.value : this.profileImagePath, + profileImagePath: data.profileImagePath.present + ? data.profileImagePath.value + : this.profileImagePath, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - quotaSizeInBytes: data.quotaSizeInBytes.present ? data.quotaSizeInBytes.value : this.quotaSizeInBytes, - quotaUsageInBytes: data.quotaUsageInBytes.present ? data.quotaUsageInBytes.value : this.quotaUsageInBytes, + quotaSizeInBytes: data.quotaSizeInBytes.present + ? data.quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: data.quotaUsageInBytes.present + ? data.quotaUsageInBytes.value + : this.quotaUsageInBytes, ); } @@ -180,8 +264,16 @@ class UserEntityData extends DataClass implements Insertable { } @override - int get hashCode => - Object.hash(id, name, isAdmin, email, profileImagePath, updatedAt, quotaSizeInBytes, quotaUsageInBytes); + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -224,9 +316,9 @@ class UserEntityCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), this.quotaSizeInBytes = const Value.absent(), this.quotaUsageInBytes = const Value.absent(), - }) : id = Value(id), - name = Value(name), - email = Value(email); + }) : id = Value(id), + name = Value(name), + email = Value(email); static Insertable custom({ Expression? id, Expression? name, @@ -249,15 +341,16 @@ class UserEntityCompanion extends UpdateCompanion { }); } - UserEntityCompanion copyWith( - {Value? id, - Value? name, - Value? isAdmin, - Value? email, - Value? profileImagePath, - Value? updatedAt, - Value? quotaSizeInBytes, - Value? quotaUsageInBytes}) { + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? profileImagePath, + Value? updatedAt, + Value? quotaSizeInBytes, + Value? quotaUsageInBytes, + }) { return UserEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -316,75 +409,161 @@ class UserEntityCompanion extends UpdateCompanion { } } -class RemoteAssetEntity extends Table with TableInfo { +class RemoteAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn type = - GeneratedColumn('type', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn width = - GeneratedColumn('width', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn height = - GeneratedColumn('height', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn durationInSeconds = GeneratedColumn('duration_in_seconds', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn checksum = - GeneratedColumn('checksum', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isFavorite = GeneratedColumn('is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn localDateTime = GeneratedColumn('local_date_time', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn thumbHash = - GeneratedColumn('thumb_hash', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn deletedAt = GeneratedColumn('deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn localDateTime = + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn livePhotoVideoId = GeneratedColumn( - 'live_photo_video_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn visibility = - GeneratedColumn('visibility', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn stackId = - GeneratedColumn('stack_id', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - ownerId, - localDateTime, - thumbHash, - deletedAt, - livePhotoVideoId, - visibility, - stackId - ]; + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -396,26 +575,74 @@ class RemoteAssetEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAssetEntityData( - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - type: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}type'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}height']), - durationInSeconds: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}checksum'])!, - isFavorite: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - localDateTime: - attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}local_date_time']), - thumbHash: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}thumb_hash']), - deletedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), - livePhotoVideoId: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}live_photo_video_id']), - visibility: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}visibility'])!, - stackId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}stack_id']), + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), ); } @@ -430,7 +657,8 @@ class RemoteAssetEntity extends Table with TableInfo true; } -class RemoteAssetEntityData extends DataClass implements Insertable { +class RemoteAssetEntityData extends DataClass + implements Insertable { final String name; final int type; final DateTime createdAt; @@ -448,24 +676,25 @@ class RemoteAssetEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -505,7 +734,10 @@ class RemoteAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAssetEntityData( name: serializer.fromJson(json['name']), @@ -551,43 +783,49 @@ class RemoteAssetEntityData extends DataClass implements Insertable width = const Value.absent(), - Value height = const Value.absent(), - Value durationInSeconds = const Value.absent(), - String? id, - String? checksum, - bool? isFavorite, - String? ownerId, - Value localDateTime = const Value.absent(), - Value thumbHash = const Value.absent(), - Value deletedAt = const Value.absent(), - Value livePhotoVideoId = const Value.absent(), - int? visibility, - Value stackId = const Value.absent()}) => - RemoteAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present ? durationInSeconds.value : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum ?? this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - ownerId: ownerId ?? this.ownerId, - localDateTime: localDateTime.present ? localDateTime.value : this.localDateTime, - thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - livePhotoVideoId: livePhotoVideoId.present ? livePhotoVideoId.value : this.livePhotoVideoId, - visibility: visibility ?? this.visibility, - stackId: stackId.present ? stackId.value : this.stackId, - ); + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + ); RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { return RemoteAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -596,16 +834,26 @@ class RemoteAssetEntityData extends DataClass implements Insertable Object.hash(name, type, createdAt, updatedAt, width, height, durationInSeconds, id, checksum, - isFavorite, ownerId, localDateTime, thumbHash, deletedAt, livePhotoVideoId, visibility, stackId); + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -660,7 +925,8 @@ class RemoteAssetEntityData extends DataClass implements Insertable { +class RemoteAssetEntityCompanion + extends UpdateCompanion { final Value name; final Value type; final Value createdAt; @@ -715,12 +981,12 @@ class RemoteAssetEntityCompanion extends UpdateCompanion this.livePhotoVideoId = const Value.absent(), required int visibility, this.stackId = const Value.absent(), - }) : name = Value(name), - type = Value(type), - id = Value(id), - checksum = Value(checksum), - ownerId = Value(ownerId), - visibility = Value(visibility); + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); static Insertable custom({ Expression? name, Expression? type, @@ -761,24 +1027,25 @@ class RemoteAssetEntityCompanion extends UpdateCompanion }); } - RemoteAssetEntityCompanion copyWith( - {Value? name, - Value? type, - Value? createdAt, - Value? updatedAt, - Value? width, - Value? height, - Value? durationInSeconds, - Value? id, - Value? checksum, - Value? isFavorite, - Value? ownerId, - Value? localDateTime, - Value? thumbHash, - Value? deletedAt, - Value? livePhotoVideoId, - Value? visibility, - Value? stackId}) { + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + }) { return RemoteAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -887,24 +1154,54 @@ class StackEntity extends Table with TableInfo { final GeneratedDatabase attachedDatabase; final String? _alias; StackEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn primaryAssetId = GeneratedColumn('primary_asset_id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); @override - List get $columns => [id, createdAt, updatedAt, ownerId, primaryAssetId]; + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -916,12 +1213,26 @@ class StackEntity extends Table with TableInfo { StackEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return StackEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - primaryAssetId: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, ); } @@ -942,12 +1253,13 @@ class StackEntityData extends DataClass implements Insertable { final DateTime updatedAt; final String ownerId; final String primaryAssetId; - const StackEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.primaryAssetId}); + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -959,7 +1271,10 @@ class StackEntityData extends DataClass implements Insertable { return map; } - factory StackEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return StackEntityData( id: serializer.fromJson(json['id']), @@ -981,22 +1296,28 @@ class StackEntityData extends DataClass implements Insertable { }; } - StackEntityData copyWith( - {String? id, DateTime? createdAt, DateTime? updatedAt, String? ownerId, String? primaryAssetId}) => - StackEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - primaryAssetId: primaryAssetId ?? this.primaryAssetId, - ); + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); StackEntityData copyWithCompanion(StackEntityCompanion data) { return StackEntityData( id: data.id.present ? data.id.value : this.id, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, - primaryAssetId: data.primaryAssetId.present ? data.primaryAssetId.value : this.primaryAssetId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, ); } @@ -1013,7 +1334,8 @@ class StackEntityData extends DataClass implements Insertable { } @override - int get hashCode => Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); @override bool operator ==(Object other) => identical(this, other) || @@ -1044,9 +1366,9 @@ class StackEntityCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), required String ownerId, required String primaryAssetId, - }) : id = Value(id), - ownerId = Value(ownerId), - primaryAssetId = Value(primaryAssetId); + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); static Insertable custom({ Expression? id, Expression? createdAt, @@ -1063,12 +1385,13 @@ class StackEntityCompanion extends UpdateCompanion { }); } - StackEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? primaryAssetId}) { + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { return StackEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -1112,43 +1435,110 @@ class StackEntityCompanion extends UpdateCompanion { } } -class LocalAssetEntity extends Table with TableInfo { +class LocalAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; LocalAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn type = - GeneratedColumn('type', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn width = - GeneratedColumn('width', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn height = - GeneratedColumn('height', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn durationInSeconds = GeneratedColumn('duration_in_seconds', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn checksum = - GeneratedColumn('checksum', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn isFavorite = GeneratedColumn('is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn orientation = GeneratedColumn('orientation', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: false, defaultValue: const CustomExpression('0')); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); @override - List get $columns => - [name, type, createdAt, updatedAt, width, height, durationInSeconds, id, checksum, isFavorite, orientation]; + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -1160,18 +1550,50 @@ class LocalAssetEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAssetEntityData( - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - type: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}type'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}height']), - durationInSeconds: - attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}checksum']), - isFavorite: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - orientation: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}orientation'])!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, ); } @@ -1186,7 +1608,8 @@ class LocalAssetEntity extends Table with TableInfo true; } -class LocalAssetEntityData extends DataClass implements Insertable { +class LocalAssetEntityData extends DataClass + implements Insertable { final String name; final int type; final DateTime createdAt; @@ -1198,18 +1621,19 @@ class LocalAssetEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -1235,7 +1659,10 @@ class LocalAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAssetEntityData( name: serializer.fromJson(json['name']), @@ -1269,31 +1696,33 @@ class LocalAssetEntityData extends DataClass implements Insertable width = const Value.absent(), - Value height = const Value.absent(), - Value durationInSeconds = const Value.absent(), - String? id, - Value checksum = const Value.absent(), - bool? isFavorite, - int? orientation}) => - LocalAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present ? durationInSeconds.value : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum.present ? checksum.value : this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - orientation: orientation ?? this.orientation, - ); + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { return LocalAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -1302,11 +1731,17 @@ class LocalAssetEntityData extends DataClass implements Insertable Object.hash( - name, type, createdAt, updatedAt, width, height, durationInSeconds, id, checksum, isFavorite, orientation); + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -1385,9 +1831,9 @@ class LocalAssetEntityCompanion extends UpdateCompanion { this.checksum = const Value.absent(), this.isFavorite = const Value.absent(), this.orientation = const Value.absent(), - }) : name = Value(name), - type = Value(type), - id = Value(id); + }) : name = Value(name), + type = Value(type), + id = Value(id); static Insertable custom({ Expression? name, Expression? type, @@ -1416,18 +1862,19 @@ class LocalAssetEntityCompanion extends UpdateCompanion { }); } - LocalAssetEntityCompanion copyWith( - {Value? name, - Value? type, - Value? createdAt, - Value? updatedAt, - Value? width, - Value? height, - Value? durationInSeconds, - Value? id, - Value? checksum, - Value? isFavorite, - Value? orientation}) { + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { return LocalAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -1501,32 +1948,71 @@ class LocalAssetEntityCompanion extends UpdateCompanion { } } -class LocalAlbumEntity extends Table with TableInfo { +class LocalAlbumEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; LocalAlbumEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn backupSelection = - GeneratedColumn('backup_selection', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn isIosSharedAlbum = GeneratedColumn('is_ios_shared_album', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_ios_shared_album" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn marker_ = GeneratedColumn('marker', aliasedName, true, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("marker" IN (0, 1))')); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); @override - List get $columns => [id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_]; + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -1538,13 +2024,30 @@ class LocalAlbumEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAlbumEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - backupSelection: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}backup_selection'])!, - isIosSharedAlbum: - attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_ios_shared_album'])!, - marker_: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}marker']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), ); } @@ -1559,20 +2062,22 @@ class LocalAlbumEntity extends Table with TableInfo true; } -class LocalAlbumEntityData extends DataClass implements Insertable { +class LocalAlbumEntityData extends DataClass + implements Insertable { final String id; final String name; final DateTime updatedAt; final int backupSelection; final bool isIosSharedAlbum; final bool? marker_; - const LocalAlbumEntityData( - {required this.id, - required this.name, - required this.updatedAt, - required this.backupSelection, - required this.isIosSharedAlbum, - this.marker_}); + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1587,7 +2092,10 @@ class LocalAlbumEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAlbumEntityData( id: serializer.fromJson(json['id']), @@ -1611,28 +2119,32 @@ class LocalAlbumEntityData extends DataClass implements Insertable marker_ = const Value.absent()}) => - LocalAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - updatedAt: updatedAt ?? this.updatedAt, - backupSelection: backupSelection ?? this.backupSelection, - isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, - marker_: marker_.present ? marker_.value : this.marker_, - ); + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { return LocalAlbumEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - backupSelection: data.backupSelection.present ? data.backupSelection.value : this.backupSelection, - isIosSharedAlbum: data.isIosSharedAlbum.present ? data.isIosSharedAlbum.value : this.isIosSharedAlbum, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, marker_: data.marker_.present ? data.marker_.value : this.marker_, ); } @@ -1651,7 +2163,14 @@ class LocalAlbumEntityData extends DataClass implements Insertable Object.hash(id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_); + int get hashCode => Object.hash( + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -1686,9 +2205,9 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { required int backupSelection, this.isIosSharedAlbum = const Value.absent(), this.marker_ = const Value.absent(), - }) : id = Value(id), - name = Value(name), - backupSelection = Value(backupSelection); + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); static Insertable custom({ Expression? id, Expression? name, @@ -1707,13 +2226,14 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { }); } - LocalAlbumEntityCompanion copyWith( - {Value? id, - Value? name, - Value? updatedAt, - Value? backupSelection, - Value? isIosSharedAlbum, - Value? marker_}) { + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_, + }) { return LocalAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -1762,19 +2282,32 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { } } -class LocalAlbumAssetEntity extends Table with TableInfo { +class LocalAlbumAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES local_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn albumId = GeneratedColumn('album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES local_album_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -1785,11 +2318,20 @@ class LocalAlbumAssetEntity extends Table with TableInfo get $primaryKey => {assetId, albumId}; @override - LocalAlbumAssetEntityData map(Map data, {String? tablePrefix}) { + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -1804,10 +2346,14 @@ class LocalAlbumAssetEntity extends Table with TableInfo true; } -class LocalAlbumAssetEntityData extends DataClass implements Insertable { +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { final String assetId; final String albumId; - const LocalAlbumAssetEntityData({required this.assetId, required this.albumId}); + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1816,7 +2362,10 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -1832,11 +2381,14 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable LocalAlbumAssetEntityData( + LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + LocalAlbumAssetEntityData( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, ); - LocalAlbumAssetEntityData copyWithCompanion(LocalAlbumAssetEntityCompanion data) { + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data, + ) { return LocalAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -1857,10 +2409,13 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable identical(this, other) || - (other is LocalAlbumAssetEntityData && other.assetId == this.assetId && other.albumId == this.albumId); + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); } -class LocalAlbumAssetEntityCompanion extends UpdateCompanion { +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { final Value assetId; final Value albumId; const LocalAlbumAssetEntityCompanion({ @@ -1870,8 +2425,8 @@ class LocalAlbumAssetEntityCompanion extends UpdateCompanion custom({ Expression? assetId, Expression? albumId, @@ -1882,7 +2437,10 @@ class LocalAlbumAssetEntityCompanion extends UpdateCompanion? assetId, Value? albumId}) { + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { return LocalAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, @@ -1911,19 +2469,36 @@ class LocalAlbumAssetEntityCompanion extends UpdateCompanion { +class UserMetadataEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; UserMetadataEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn userId = GeneratedColumn('user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn key = - GeneratedColumn('key', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn value = - GeneratedColumn('value', aliasedName, false, type: DriftSqlType.blob, requiredDuringInsert: true); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); @override List get $columns => [userId, key, value]; @override @@ -1937,9 +2512,18 @@ class UserMetadataEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return UserMetadataEntityData( - userId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - key: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}key'])!, - value: attachedDatabase.typeMapping.read(DriftSqlType.blob, data['${effectivePrefix}value'])!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, ); } @@ -1954,11 +2538,16 @@ class UserMetadataEntity extends Table with TableInfo true; } -class UserMetadataEntityData extends DataClass implements Insertable { +class UserMetadataEntityData extends DataClass + implements Insertable { final String userId; final int key; final Uint8List value; - const UserMetadataEntityData({required this.userId, required this.key, required this.value}); + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1968,7 +2557,10 @@ class UserMetadataEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return UserMetadataEntityData( userId: serializer.fromJson(json['userId']), @@ -1986,11 +2578,15 @@ class UserMetadataEntityData extends DataClass implements Insertable UserMetadataEntityData( - userId: userId ?? this.userId, - key: key ?? this.key, - value: value ?? this.value, - ); + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { return UserMetadataEntityData( userId: data.userId.present ? data.userId.value : this.userId, @@ -2020,7 +2616,8 @@ class UserMetadataEntityData extends DataClass implements Insertable { +class UserMetadataEntityCompanion + extends UpdateCompanion { final Value userId; final Value key; final Value value; @@ -2033,9 +2630,9 @@ class UserMetadataEntityCompanion extends UpdateCompanion custom({ Expression? userId, Expression? key, @@ -2048,7 +2645,11 @@ class UserMetadataEntityCompanion extends UpdateCompanion? userId, Value? key, Value? value}) { + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { return UserMetadataEntityCompanion( userId: userId ?? this.userId, key: key ?? this.key, @@ -2082,24 +2683,43 @@ class UserMetadataEntityCompanion extends UpdateCompanion { +class PartnerEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; PartnerEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn sharedById = GeneratedColumn('shared_by_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn sharedWithId = GeneratedColumn('shared_with_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn inTimeline = GeneratedColumn('in_timeline', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("in_timeline" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); @override List get $columns => [sharedById, sharedWithId, inTimeline]; @override @@ -2113,9 +2733,18 @@ class PartnerEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return PartnerEntityData( - sharedById: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}shared_by_id'])!, - sharedWithId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}shared_with_id'])!, - inTimeline: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!, + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, ); } @@ -2130,11 +2759,16 @@ class PartnerEntity extends Table with TableInfo true; } -class PartnerEntityData extends DataClass implements Insertable { +class PartnerEntityData extends DataClass + implements Insertable { final String sharedById; final String sharedWithId; final bool inTimeline; - const PartnerEntityData({required this.sharedById, required this.sharedWithId, required this.inTimeline}); + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -2144,7 +2778,10 @@ class PartnerEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return PartnerEntityData( sharedById: serializer.fromJson(json['sharedById']), @@ -2162,16 +2799,26 @@ class PartnerEntityData extends DataClass implements Insertable PartnerEntityData( - sharedById: sharedById ?? this.sharedById, - sharedWithId: sharedWithId ?? this.sharedWithId, - inTimeline: inTimeline ?? this.inTimeline, - ); + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { return PartnerEntityData( - sharedById: data.sharedById.present ? data.sharedById.value : this.sharedById, - sharedWithId: data.sharedWithId.present ? data.sharedWithId.value : this.sharedWithId, - inTimeline: data.inTimeline.present ? data.inTimeline.value : this.inTimeline, + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, ); } @@ -2209,8 +2856,8 @@ class PartnerEntityCompanion extends UpdateCompanion { required String sharedById, required String sharedWithId, this.inTimeline = const Value.absent(), - }) : sharedById = Value(sharedById), - sharedWithId = Value(sharedWithId); + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); static Insertable custom({ Expression? sharedById, Expression? sharedWithId, @@ -2223,7 +2870,11 @@ class PartnerEntityCompanion extends UpdateCompanion { }); } - PartnerEntityCompanion copyWith({Value? sharedById, Value? sharedWithId, Value? inTimeline}) { + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { return PartnerEntityCompanion( sharedById: sharedById ?? this.sharedById, sharedWithId: sharedWithId ?? this.sharedWithId, @@ -2257,83 +2908,195 @@ class PartnerEntityCompanion extends UpdateCompanion { } } -class RemoteExifEntity extends Table with TableInfo { +class RemoteExifEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteExifEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn city = - GeneratedColumn('city', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn state = - GeneratedColumn('state', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn country = - GeneratedColumn('country', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn dateTimeOriginal = GeneratedColumn( - 'date_time_original', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn description = - GeneratedColumn('description', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn height = - GeneratedColumn('height', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn width = - GeneratedColumn('width', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn exposureTime = GeneratedColumn('exposure_time', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn fNumber = - GeneratedColumn('f_number', aliasedName, true, type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn fileSize = - GeneratedColumn('file_size', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn focalLength = GeneratedColumn('focal_length', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn latitude = - GeneratedColumn('latitude', aliasedName, true, type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn longitude = - GeneratedColumn('longitude', aliasedName, true, type: DriftSqlType.double, requiredDuringInsert: false); - late final GeneratedColumn iso = - GeneratedColumn('iso', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn make = - GeneratedColumn('make', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn model = - GeneratedColumn('model', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn lens = - GeneratedColumn('lens', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn orientation = - GeneratedColumn('orientation', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn timeZone = - GeneratedColumn('time_zone', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn rating = - GeneratedColumn('rating', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); - late final GeneratedColumn projectionType = GeneratedColumn('projection_type', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn city = GeneratedColumn( + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn state = GeneratedColumn( + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn country = GeneratedColumn( + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn make = GeneratedColumn( + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn model = GeneratedColumn( + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]; + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -2345,29 +3108,94 @@ class RemoteExifEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteExifEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - city: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}city']), - state: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}state']), - country: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}country']), - dateTimeOriginal: - attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}date_time_original']), - description: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}description']), - height: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}height']), - width: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}width']), - exposureTime: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}exposure_time']), - fNumber: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}f_number']), - fileSize: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}file_size']), - focalLength: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}focal_length']), - latitude: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}latitude']), - longitude: attachedDatabase.typeMapping.read(DriftSqlType.double, data['${effectivePrefix}longitude']), - iso: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}iso']), - make: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}make']), - model: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}model']), - lens: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}lens']), - orientation: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}orientation']), - timeZone: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}time_zone']), - rating: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}rating']), - projectionType: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}projection_type']), + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), ); } @@ -2382,7 +3210,8 @@ class RemoteExifEntity extends Table with TableInfo true; } -class RemoteExifEntityData extends DataClass implements Insertable { +class RemoteExifEntityData extends DataClass + implements Insertable { final String assetId; final String? city; final String? state; @@ -2405,29 +3234,30 @@ class RemoteExifEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -2498,14 +3328,19 @@ class RemoteExifEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteExifEntityData( assetId: serializer.fromJson(json['assetId']), city: serializer.fromJson(json['city']), state: serializer.fromJson(json['state']), country: serializer.fromJson(json['country']), - dateTimeOriginal: serializer.fromJson(json['dateTimeOriginal']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), description: serializer.fromJson(json['description']), height: serializer.fromJson(json['height']), width: serializer.fromJson(json['width']), @@ -2554,77 +3389,93 @@ class RemoteExifEntityData extends DataClass implements Insertable city = const Value.absent(), - Value state = const Value.absent(), - Value country = const Value.absent(), - Value dateTimeOriginal = const Value.absent(), - Value description = const Value.absent(), - Value height = const Value.absent(), - Value width = const Value.absent(), - Value exposureTime = const Value.absent(), - Value fNumber = const Value.absent(), - Value fileSize = const Value.absent(), - Value focalLength = const Value.absent(), - Value latitude = const Value.absent(), - Value longitude = const Value.absent(), - Value iso = const Value.absent(), - Value make = const Value.absent(), - Value model = const Value.absent(), - Value lens = const Value.absent(), - Value orientation = const Value.absent(), - Value timeZone = const Value.absent(), - Value rating = const Value.absent(), - Value projectionType = const Value.absent()}) => - RemoteExifEntityData( - assetId: assetId ?? this.assetId, - city: city.present ? city.value : this.city, - state: state.present ? state.value : this.state, - country: country.present ? country.value : this.country, - dateTimeOriginal: dateTimeOriginal.present ? dateTimeOriginal.value : this.dateTimeOriginal, - description: description.present ? description.value : this.description, - height: height.present ? height.value : this.height, - width: width.present ? width.value : this.width, - exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, - fNumber: fNumber.present ? fNumber.value : this.fNumber, - fileSize: fileSize.present ? fileSize.value : this.fileSize, - focalLength: focalLength.present ? focalLength.value : this.focalLength, - latitude: latitude.present ? latitude.value : this.latitude, - longitude: longitude.present ? longitude.value : this.longitude, - iso: iso.present ? iso.value : this.iso, - make: make.present ? make.value : this.make, - model: model.present ? model.value : this.model, - lens: lens.present ? lens.value : this.lens, - orientation: orientation.present ? orientation.value : this.orientation, - timeZone: timeZone.present ? timeZone.value : this.timeZone, - rating: rating.present ? rating.value : this.rating, - projectionType: projectionType.present ? projectionType.value : this.projectionType, - ); + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { return RemoteExifEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, city: data.city.present ? data.city.value : this.city, state: data.state.present ? data.state.value : this.state, country: data.country.present ? data.country.value : this.country, - dateTimeOriginal: data.dateTimeOriginal.present ? data.dateTimeOriginal.value : this.dateTimeOriginal, - description: data.description.present ? data.description.value : this.description, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: data.description.present + ? data.description.value + : this.description, height: data.height.present ? data.height.value : this.height, width: data.width.present ? data.width.value : this.width, - exposureTime: data.exposureTime.present ? data.exposureTime.value : this.exposureTime, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, - focalLength: data.focalLength.present ? data.focalLength.value : this.focalLength, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, latitude: data.latitude.present ? data.latitude.value : this.latitude, longitude: data.longitude.present ? data.longitude.value : this.longitude, iso: data.iso.present ? data.iso.value : this.iso, make: data.make.present ? data.make.value : this.make, model: data.model.present ? data.model.value : this.model, lens: data.lens.present ? data.lens.value : this.lens, - orientation: data.orientation.present ? data.orientation.value : this.orientation, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, rating: data.rating.present ? data.rating.value : this.rating, - projectionType: data.projectionType.present ? data.projectionType.value : this.projectionType, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, ); } @@ -2659,29 +3510,29 @@ class RemoteExifEntityData extends DataClass implements Insertable Object.hashAll([ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]); + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); @override bool operator ==(Object other) => identical(this, other) || @@ -2831,29 +3682,30 @@ class RemoteExifEntityCompanion extends UpdateCompanion { }); } - RemoteExifEntityCompanion copyWith( - {Value? assetId, - Value? city, - Value? state, - Value? country, - Value? dateTimeOriginal, - Value? description, - Value? height, - Value? width, - Value? exposureTime, - Value? fNumber, - Value? fileSize, - Value? focalLength, - Value? latitude, - Value? longitude, - Value? iso, - Value? make, - Value? model, - Value? lens, - Value? orientation, - Value? timeZone, - Value? rating, - Value? projectionType}) { + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { return RemoteExifEntityCompanion( assetId: assetId ?? this.assetId, city: city ?? this.city, @@ -2982,43 +3834,100 @@ class RemoteExifEntityCompanion extends UpdateCompanion { } } -class RemoteAlbumEntity extends Table with TableInfo { +class RemoteAlbumEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAlbumEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn description = GeneratedColumn('description', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: false, defaultValue: const CustomExpression('\'\'')); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn thumbnailAssetId = GeneratedColumn('thumbnail_asset_id', aliasedName, true, - type: DriftSqlType.string, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE SET NULL')); - late final GeneratedColumn isActivityEnabled = GeneratedColumn('is_activity_enabled', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_activity_enabled" IN (0, 1))'), - defaultValue: const CustomExpression('1')); - late final GeneratedColumn order = - GeneratedColumn('order', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn order = GeneratedColumn( + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); @override - List get $columns => - [id, name, description, createdAt, updatedAt, ownerId, thumbnailAssetId, isActivityEnabled, order]; + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -3030,17 +3939,42 @@ class RemoteAlbumEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - description: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}description'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - thumbnailAssetId: - attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}thumbnail_asset_id']), - isActivityEnabled: - attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_activity_enabled'])!, - order: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}order'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, ); } @@ -3055,7 +3989,8 @@ class RemoteAlbumEntity extends Table with TableInfo true; } -class RemoteAlbumEntityData extends DataClass implements Insertable { +class RemoteAlbumEntityData extends DataClass + implements Insertable { final String id; final String name; final String description; @@ -3065,16 +4000,17 @@ class RemoteAlbumEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -3092,7 +4028,10 @@ class RemoteAlbumEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumEntityData( id: serializer.fromJson(json['id']), @@ -3122,37 +4061,45 @@ class RemoteAlbumEntityData extends DataClass implements Insertable thumbnailAssetId = const Value.absent(), - bool? isActivityEnabled, - int? order}) => - RemoteAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - description: description ?? this.description, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - thumbnailAssetId: thumbnailAssetId.present ? thumbnailAssetId.value : this.thumbnailAssetId, - isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, - order: order ?? this.order, - ); + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { return RemoteAlbumEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, - description: data.description.present ? data.description.value : this.description, + description: data.description.present + ? data.description.value + : this.description, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, - thumbnailAssetId: data.thumbnailAssetId.present ? data.thumbnailAssetId.value : this.thumbnailAssetId, - isActivityEnabled: data.isActivityEnabled.present ? data.isActivityEnabled.value : this.isActivityEnabled, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, order: data.order.present ? data.order.value : this.order, ); } @@ -3174,8 +4121,17 @@ class RemoteAlbumEntityData extends DataClass implements Insertable - Object.hash(id, name, description, createdAt, updatedAt, ownerId, thumbnailAssetId, isActivityEnabled, order); + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -3191,7 +4147,8 @@ class RemoteAlbumEntityData extends DataClass implements Insertable { +class RemoteAlbumEntityCompanion + extends UpdateCompanion { final Value id; final Value name; final Value description; @@ -3222,10 +4179,10 @@ class RemoteAlbumEntityCompanion extends UpdateCompanion this.thumbnailAssetId = const Value.absent(), this.isActivityEnabled = const Value.absent(), required int order, - }) : id = Value(id), - name = Value(name), - ownerId = Value(ownerId), - order = Value(order); + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); static Insertable custom({ Expression? id, Expression? name, @@ -3250,16 +4207,17 @@ class RemoteAlbumEntityCompanion extends UpdateCompanion }); } - RemoteAlbumEntityCompanion copyWith( - {Value? id, - Value? name, - Value? description, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? thumbnailAssetId, - Value? isActivityEnabled, - Value? order}) { + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { return RemoteAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -3323,19 +4281,32 @@ class RemoteAlbumEntityCompanion extends UpdateCompanion } } -class RemoteAlbumAssetEntity extends Table with TableInfo { +class RemoteAlbumAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn albumId = GeneratedColumn('album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -3346,11 +4317,20 @@ class RemoteAlbumAssetEntity extends Table with TableInfo get $primaryKey => {assetId, albumId}; @override - RemoteAlbumAssetEntityData map(Map data, {String? tablePrefix}) { + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -3365,10 +4345,14 @@ class RemoteAlbumAssetEntity extends Table with TableInfo true; } -class RemoteAlbumAssetEntityData extends DataClass implements Insertable { +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { final String assetId; final String albumId; - const RemoteAlbumAssetEntityData({required this.assetId, required this.albumId}); + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3377,7 +4361,10 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -3393,11 +4380,14 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable RemoteAlbumAssetEntityData( + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, ); - RemoteAlbumAssetEntityData copyWithCompanion(RemoteAlbumAssetEntityCompanion data) { + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data, + ) { return RemoteAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -3418,10 +4408,13 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable identical(this, other) || - (other is RemoteAlbumAssetEntityData && other.assetId == this.assetId && other.albumId == this.albumId); + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); } -class RemoteAlbumAssetEntityCompanion extends UpdateCompanion { +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { final Value assetId; final Value albumId; const RemoteAlbumAssetEntityCompanion({ @@ -3431,8 +4424,8 @@ class RemoteAlbumAssetEntityCompanion extends UpdateCompanion custom({ Expression? assetId, Expression? albumId, @@ -3443,7 +4436,10 @@ class RemoteAlbumAssetEntityCompanion extends UpdateCompanion? assetId, Value? albumId}) { + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { return RemoteAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, @@ -3472,21 +4468,39 @@ class RemoteAlbumAssetEntityCompanion extends UpdateCompanion { +class RemoteAlbumUserEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn albumId = GeneratedColumn('album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_album_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn userId = GeneratedColumn('user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn role = - GeneratedColumn('role', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn role = GeneratedColumn( + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); @override List get $columns => [albumId, userId, role]; @override @@ -3497,12 +4511,24 @@ class RemoteAlbumUserEntity extends Table with TableInfo get $primaryKey => {albumId, userId}; @override - RemoteAlbumUserEntityData map(Map data, {String? tablePrefix}) { + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumUserEntityData( - albumId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, - userId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - role: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}role'])!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, ); } @@ -3517,11 +4543,16 @@ class RemoteAlbumUserEntity extends Table with TableInfo true; } -class RemoteAlbumUserEntityData extends DataClass implements Insertable { +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { final String albumId; final String userId; final int role; - const RemoteAlbumUserEntityData({required this.albumId, required this.userId, required this.role}); + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3531,7 +4562,10 @@ class RemoteAlbumUserEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumUserEntityData( albumId: serializer.fromJson(json['albumId']), @@ -3549,12 +4583,18 @@ class RemoteAlbumUserEntityData extends DataClass implements Insertable RemoteAlbumUserEntityData( - albumId: albumId ?? this.albumId, - userId: userId ?? this.userId, - role: role ?? this.role, - ); - RemoteAlbumUserEntityData copyWithCompanion(RemoteAlbumUserEntityCompanion data) { + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data, + ) { return RemoteAlbumUserEntityData( albumId: data.albumId.present ? data.albumId.value : this.albumId, userId: data.userId.present ? data.userId.value : this.userId, @@ -3583,7 +4623,8 @@ class RemoteAlbumUserEntityData extends DataClass implements Insertable { +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { final Value albumId; final Value userId; final Value role; @@ -3596,9 +4637,9 @@ class RemoteAlbumUserEntityCompanion extends UpdateCompanion custom({ Expression? albumId, Expression? userId, @@ -3611,7 +4652,11 @@ class RemoteAlbumUserEntityCompanion extends UpdateCompanion? albumId, Value? userId, Value? role}) { + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { return RemoteAlbumUserEntityCompanion( albumId: albumId ?? this.albumId, userId: userId ?? this.userId, @@ -3645,47 +4690,120 @@ class RemoteAlbumUserEntityCompanion extends UpdateCompanion { +class MemoryEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; MemoryEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn deletedAt = GeneratedColumn('deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn type = - GeneratedColumn('type', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn data = - GeneratedColumn('data', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn isSaved = GeneratedColumn('is_saved', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_saved" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - late final GeneratedColumn memoryAt = GeneratedColumn('memory_at', aliasedName, false, - type: DriftSqlType.dateTime, requiredDuringInsert: true); - late final GeneratedColumn seenAt = - GeneratedColumn('seen_at', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn showAt = - GeneratedColumn('show_at', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false); - late final GeneratedColumn hideAt = - GeneratedColumn('hide_at', aliasedName, true, type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn data = GeneratedColumn( + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override - List get $columns => - [id, createdAt, updatedAt, deletedAt, ownerId, type, data, isSaved, memoryAt, seenAt, showAt, hideAt]; + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -3697,18 +4815,54 @@ class MemoryEntity extends Table with TableInfo MemoryEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return MemoryEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - deletedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - type: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}type'])!, - data: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}data'])!, - isSaved: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_saved'])!, - memoryAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}memory_at'])!, - seenAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}seen_at']), - showAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}show_at']), - hideAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}hide_at']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), ); } @@ -3723,7 +4877,8 @@ class MemoryEntity extends Table with TableInfo bool get isStrict => true; } -class MemoryEntityData extends DataClass implements Insertable { +class MemoryEntityData extends DataClass + implements Insertable { final String id; final DateTime createdAt; final DateTime updatedAt; @@ -3736,19 +4891,20 @@ class MemoryEntityData extends DataClass implements Insertable final DateTime? seenAt; final DateTime? showAt; final DateTime? hideAt; - const MemoryEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - this.deletedAt, - required this.ownerId, - required this.type, - required this.data, - required this.isSaved, - required this.memoryAt, - this.seenAt, - this.showAt, - this.hideAt}); + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3775,7 +4931,10 @@ class MemoryEntityData extends DataClass implements Insertable return map; } - factory MemoryEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return MemoryEntityData( id: serializer.fromJson(json['id']), @@ -3811,33 +4970,33 @@ class MemoryEntityData extends DataClass implements Insertable }; } - MemoryEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - Value deletedAt = const Value.absent(), - String? ownerId, - int? type, - String? data, - bool? isSaved, - DateTime? memoryAt, - Value seenAt = const Value.absent(), - Value showAt = const Value.absent(), - Value hideAt = const Value.absent()}) => - MemoryEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - ownerId: ownerId ?? this.ownerId, - type: type ?? this.type, - data: data ?? this.data, - isSaved: isSaved ?? this.isSaved, - memoryAt: memoryAt ?? this.memoryAt, - seenAt: seenAt.present ? seenAt.value : this.seenAt, - showAt: showAt.present ? showAt.value : this.showAt, - hideAt: hideAt.present ? hideAt.value : this.hideAt, - ); + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { return MemoryEntityData( id: data.id.present ? data.id.value : this.id, @@ -3875,8 +5034,20 @@ class MemoryEntityData extends DataClass implements Insertable } @override - int get hashCode => - Object.hash(id, createdAt, updatedAt, deletedAt, ownerId, type, data, isSaved, memoryAt, seenAt, showAt, hideAt); + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -3935,11 +5106,11 @@ class MemoryEntityCompanion extends UpdateCompanion { this.seenAt = const Value.absent(), this.showAt = const Value.absent(), this.hideAt = const Value.absent(), - }) : id = Value(id), - ownerId = Value(ownerId), - type = Value(type), - data = Value(data), - memoryAt = Value(memoryAt); + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); static Insertable custom({ Expression? id, Expression? createdAt, @@ -3970,19 +5141,20 @@ class MemoryEntityCompanion extends UpdateCompanion { }); } - MemoryEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? deletedAt, - Value? ownerId, - Value? type, - Value? data, - Value? isSaved, - Value? memoryAt, - Value? seenAt, - Value? showAt, - Value? hideAt}) { + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { return MemoryEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -4061,19 +5233,32 @@ class MemoryEntityCompanion extends UpdateCompanion { } } -class MemoryAssetEntity extends Table with TableInfo { +class MemoryAssetEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; MemoryAssetEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn memoryId = GeneratedColumn('memory_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES memory_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, memoryId]; @override @@ -4087,8 +5272,14 @@ class MemoryAssetEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return MemoryAssetEntityData( - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - memoryId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}memory_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, ); } @@ -4103,7 +5294,8 @@ class MemoryAssetEntity extends Table with TableInfo true; } -class MemoryAssetEntityData extends DataClass implements Insertable { +class MemoryAssetEntityData extends DataClass + implements Insertable { final String assetId; final String memoryId; const MemoryAssetEntityData({required this.assetId, required this.memoryId}); @@ -4115,7 +5307,10 @@ class MemoryAssetEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return MemoryAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -4131,7 +5326,8 @@ class MemoryAssetEntityData extends DataClass implements Insertable MemoryAssetEntityData( + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( assetId: assetId ?? this.assetId, memoryId: memoryId ?? this.memoryId, ); @@ -4156,10 +5352,13 @@ class MemoryAssetEntityData extends DataClass implements Insertable identical(this, other) || - (other is MemoryAssetEntityData && other.assetId == this.assetId && other.memoryId == this.memoryId); + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); } -class MemoryAssetEntityCompanion extends UpdateCompanion { +class MemoryAssetEntityCompanion + extends UpdateCompanion { final Value assetId; final Value memoryId; const MemoryAssetEntityCompanion({ @@ -4169,8 +5368,8 @@ class MemoryAssetEntityCompanion extends UpdateCompanion MemoryAssetEntityCompanion.insert({ required String assetId, required String memoryId, - }) : assetId = Value(assetId), - memoryId = Value(memoryId); + }) : assetId = Value(assetId), + memoryId = Value(memoryId); static Insertable custom({ Expression? assetId, Expression? memoryId, @@ -4181,7 +5380,10 @@ class MemoryAssetEntityCompanion extends UpdateCompanion }); } - MemoryAssetEntityCompanion copyWith({Value? assetId, Value? memoryId}) { + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { return MemoryAssetEntityCompanion( assetId: assetId ?? this.assetId, memoryId: memoryId ?? this.memoryId, @@ -4210,44 +5412,106 @@ class MemoryAssetEntityCompanion extends UpdateCompanion } } -class PersonEntity extends Table with TableInfo { +class PersonEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; PersonEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn('created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn('updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn ownerId = GeneratedColumn('owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn name = - GeneratedColumn('name', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn faceAssetId = GeneratedColumn('face_asset_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn isFavorite = GeneratedColumn('is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))')); - late final GeneratedColumn isHidden = GeneratedColumn('is_hidden', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('CHECK ("is_hidden" IN (0, 1))')); - late final GeneratedColumn color = - GeneratedColumn('color', aliasedName, true, type: DriftSqlType.string, requiredDuringInsert: false); - late final GeneratedColumn birthDate = GeneratedColumn('birth_date', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override - List get $columns => - [id, createdAt, updatedAt, ownerId, name, faceAssetId, isFavorite, isHidden, color, birthDate]; + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -4259,16 +5523,46 @@ class PersonEntity extends Table with TableInfo PersonEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return PersonEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - name: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}name'])!, - faceAssetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}face_asset_id']), - isFavorite: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - isHidden: attachedDatabase.typeMapping.read(DriftSqlType.bool, data['${effectivePrefix}is_hidden'])!, - color: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}color']), - birthDate: attachedDatabase.typeMapping.read(DriftSqlType.dateTime, data['${effectivePrefix}birth_date']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), ); } @@ -4283,7 +5577,8 @@ class PersonEntity extends Table with TableInfo bool get isStrict => true; } -class PersonEntityData extends DataClass implements Insertable { +class PersonEntityData extends DataClass + implements Insertable { final String id; final DateTime createdAt; final DateTime updatedAt; @@ -4294,17 +5589,18 @@ class PersonEntityData extends DataClass implements Insertable final bool isHidden; final String? color; final DateTime? birthDate; - const PersonEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.name, - this.faceAssetId, - required this.isFavorite, - required this.isHidden, - this.color, - this.birthDate}); + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -4327,7 +5623,10 @@ class PersonEntityData extends DataClass implements Insertable return map; } - factory PersonEntityData.fromJson(Map json, {ValueSerializer? serializer}) { + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return PersonEntityData( id: serializer.fromJson(json['id']), @@ -4359,29 +5658,29 @@ class PersonEntityData extends DataClass implements Insertable }; } - PersonEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? name, - Value faceAssetId = const Value.absent(), - bool? isFavorite, - bool? isHidden, - Value color = const Value.absent(), - Value birthDate = const Value.absent()}) => - PersonEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - name: name ?? this.name, - faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, - isFavorite: isFavorite ?? this.isFavorite, - isHidden: isHidden ?? this.isHidden, - color: color.present ? color.value : this.color, - birthDate: birthDate.present ? birthDate.value : this.birthDate, - ); + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); PersonEntityData copyWithCompanion(PersonEntityCompanion data) { return PersonEntityData( id: data.id.present ? data.id.value : this.id, @@ -4389,8 +5688,12 @@ class PersonEntityData extends DataClass implements Insertable updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, name: data.name.present ? data.name.value : this.name, - faceAssetId: data.faceAssetId.present ? data.faceAssetId.value : this.faceAssetId, - isFavorite: data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, color: data.color.present ? data.color.value : this.color, birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, @@ -4415,8 +5718,18 @@ class PersonEntityData extends DataClass implements Insertable } @override - int get hashCode => - Object.hash(id, createdAt, updatedAt, ownerId, name, faceAssetId, isFavorite, isHidden, color, birthDate); + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -4467,11 +5780,11 @@ class PersonEntityCompanion extends UpdateCompanion { required bool isHidden, this.color = const Value.absent(), this.birthDate = const Value.absent(), - }) : id = Value(id), - ownerId = Value(ownerId), - name = Value(name), - isFavorite = Value(isFavorite), - isHidden = Value(isHidden); + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); static Insertable custom({ Expression? id, Expression? createdAt, @@ -4498,17 +5811,18 @@ class PersonEntityCompanion extends UpdateCompanion { }); } - PersonEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? name, - Value? faceAssetId, - Value? isFavorite, - Value? isHidden, - Value? color, - Value? birthDate}) { + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { return PersonEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -4577,48 +5891,101 @@ class PersonEntityCompanion extends UpdateCompanion { } } -class AssetFaceEntity extends Table with TableInfo { +class AssetFaceEntity extends Table + with TableInfo { @override final GeneratedDatabase attachedDatabase; final String? _alias; AssetFaceEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn assetId = GeneratedColumn('asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn personId = GeneratedColumn('person_id', aliasedName, true, - type: DriftSqlType.string, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways('REFERENCES person_entity (id) ON DELETE SET NULL')); - late final GeneratedColumn imageWidth = - GeneratedColumn('image_width', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn imageHeight = - GeneratedColumn('image_height', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn boundingBoxX1 = - GeneratedColumn('bounding_box_x1', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn boundingBoxY1 = - GeneratedColumn('bounding_box_y1', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn boundingBoxX2 = - GeneratedColumn('bounding_box_x2', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn boundingBoxY2 = - GeneratedColumn('bounding_box_y2', aliasedName, false, type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn sourceType = - GeneratedColumn('source_type', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn personId = GeneratedColumn( + 'person_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn imageWidth = GeneratedColumn( + 'image_width', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn imageHeight = GeneratedColumn( + 'image_height', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX1 = GeneratedColumn( + 'bounding_box_x1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY1 = GeneratedColumn( + 'bounding_box_y1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX2 = GeneratedColumn( + 'bounding_box_x2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY2 = GeneratedColumn( + 'bounding_box_y2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn sourceType = GeneratedColumn( + 'source_type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); @override List get $columns => [ - id, - assetId, - personId, - imageWidth, - imageHeight, - boundingBoxX1, - boundingBoxY1, - boundingBoxX2, - boundingBoxY2, - sourceType - ]; + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -4630,16 +5997,46 @@ class AssetFaceEntity extends Table with TableInfo data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return AssetFaceEntityData( - id: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}id'])!, - assetId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - personId: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}person_id']), - imageWidth: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}image_width'])!, - imageHeight: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}image_height'])!, - boundingBoxX1: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}bounding_box_x1'])!, - boundingBoxY1: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}bounding_box_y1'])!, - boundingBoxX2: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}bounding_box_x2'])!, - boundingBoxY2: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}bounding_box_y2'])!, - sourceType: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}source_type'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + personId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}person_id'], + ), + imageWidth: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_width'], + )!, + imageHeight: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_height'], + )!, + boundingBoxX1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x1'], + )!, + boundingBoxY1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y1'], + )!, + boundingBoxX2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x2'], + )!, + boundingBoxY2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y2'], + )!, + sourceType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}source_type'], + )!, ); } @@ -4654,7 +6051,8 @@ class AssetFaceEntity extends Table with TableInfo true; } -class AssetFaceEntityData extends DataClass implements Insertable { +class AssetFaceEntityData extends DataClass + implements Insertable { final String id; final String assetId; final String? personId; @@ -4665,17 +6063,18 @@ class AssetFaceEntityData extends DataClass implements Insertable toColumns(bool nullToAbsent) { final map = {}; @@ -4694,7 +6093,10 @@ class AssetFaceEntityData extends DataClass implements Insertable json, {ValueSerializer? serializer}) { + factory AssetFaceEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return AssetFaceEntityData( id: serializer.fromJson(json['id']), @@ -4726,41 +6128,55 @@ class AssetFaceEntityData extends DataClass implements Insertable personId = const Value.absent(), - int? imageWidth, - int? imageHeight, - int? boundingBoxX1, - int? boundingBoxY1, - int? boundingBoxX2, - int? boundingBoxY2, - String? sourceType}) => - AssetFaceEntityData( - id: id ?? this.id, - assetId: assetId ?? this.assetId, - personId: personId.present ? personId.value : this.personId, - imageWidth: imageWidth ?? this.imageWidth, - imageHeight: imageHeight ?? this.imageHeight, - boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, - boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, - boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, - boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, - sourceType: sourceType ?? this.sourceType, - ); + AssetFaceEntityData copyWith({ + String? id, + String? assetId, + Value personId = const Value.absent(), + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType, + }) => AssetFaceEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId.present ? personId.value : this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); AssetFaceEntityData copyWithCompanion(AssetFaceEntityCompanion data) { return AssetFaceEntityData( id: data.id.present ? data.id.value : this.id, assetId: data.assetId.present ? data.assetId.value : this.assetId, personId: data.personId.present ? data.personId.value : this.personId, - imageWidth: data.imageWidth.present ? data.imageWidth.value : this.imageWidth, - imageHeight: data.imageHeight.present ? data.imageHeight.value : this.imageHeight, - boundingBoxX1: data.boundingBoxX1.present ? data.boundingBoxX1.value : this.boundingBoxX1, - boundingBoxY1: data.boundingBoxY1.present ? data.boundingBoxY1.value : this.boundingBoxY1, - boundingBoxX2: data.boundingBoxX2.present ? data.boundingBoxX2.value : this.boundingBoxX2, - boundingBoxY2: data.boundingBoxY2.present ? data.boundingBoxY2.value : this.boundingBoxY2, - sourceType: data.sourceType.present ? data.sourceType.value : this.sourceType, + imageWidth: data.imageWidth.present + ? data.imageWidth.value + : this.imageWidth, + imageHeight: data.imageHeight.present + ? data.imageHeight.value + : this.imageHeight, + boundingBoxX1: data.boundingBoxX1.present + ? data.boundingBoxX1.value + : this.boundingBoxX1, + boundingBoxY1: data.boundingBoxY1.present + ? data.boundingBoxY1.value + : this.boundingBoxY1, + boundingBoxX2: data.boundingBoxX2.present + ? data.boundingBoxX2.value + : this.boundingBoxX2, + boundingBoxY2: data.boundingBoxY2.present + ? data.boundingBoxY2.value + : this.boundingBoxY2, + sourceType: data.sourceType.present + ? data.sourceType.value + : this.sourceType, ); } @@ -4782,8 +6198,18 @@ class AssetFaceEntityData extends DataClass implements Insertable Object.hash(id, assetId, personId, imageWidth, imageHeight, boundingBoxX1, boundingBoxY1, - boundingBoxX2, boundingBoxY2, sourceType); + int get hashCode => Object.hash( + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -4834,15 +6260,15 @@ class AssetFaceEntityCompanion extends UpdateCompanion { required int boundingBoxX2, required int boundingBoxY2, required String sourceType, - }) : id = Value(id), - assetId = Value(assetId), - imageWidth = Value(imageWidth), - imageHeight = Value(imageHeight), - boundingBoxX1 = Value(boundingBoxX1), - boundingBoxY1 = Value(boundingBoxY1), - boundingBoxX2 = Value(boundingBoxX2), - boundingBoxY2 = Value(boundingBoxY2), - sourceType = Value(sourceType); + }) : id = Value(id), + assetId = Value(assetId), + imageWidth = Value(imageWidth), + imageHeight = Value(imageHeight), + boundingBoxX1 = Value(boundingBoxX1), + boundingBoxY1 = Value(boundingBoxY1), + boundingBoxX2 = Value(boundingBoxX2), + boundingBoxY2 = Value(boundingBoxY2), + sourceType = Value(sourceType); static Insertable custom({ Expression? id, Expression? assetId, @@ -4869,17 +6295,18 @@ class AssetFaceEntityCompanion extends UpdateCompanion { }); } - AssetFaceEntityCompanion copyWith( - {Value? id, - Value? assetId, - Value? personId, - Value? imageWidth, - Value? imageHeight, - Value? boundingBoxX1, - Value? boundingBoxY1, - Value? boundingBoxX2, - Value? boundingBoxY2, - Value? sourceType}) { + AssetFaceEntityCompanion copyWith({ + Value? id, + Value? assetId, + Value? personId, + Value? imageWidth, + Value? imageHeight, + Value? boundingBoxX1, + Value? boundingBoxY1, + Value? boundingBoxX2, + Value? boundingBoxY2, + Value? sourceType, + }) { return AssetFaceEntityCompanion( id: id ?? this.id, assetId: assetId ?? this.assetId, @@ -4955,49 +6382,60 @@ class DatabaseAtV4 extends GeneratedDatabase { late final StackEntity stackEntity = StackEntity(this); late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); - late final LocalAlbumAssetEntity localAlbumAssetEntity = LocalAlbumAssetEntity(this); - late final Index idxLocalAssetChecksum = - Index('idx_local_asset_checksum', 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); - late final Index uQRemoteAssetOwnerChecksum = Index('UQ_remote_asset_owner_checksum', - 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); - late final Index idxRemoteAssetChecksum = - Index('idx_remote_asset_checksum', 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + late final Index uQRemoteAssetOwnerChecksum = Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); late final PartnerEntity partnerEntity = PartnerEntity(this); late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); - late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = RemoteAlbumAssetEntity(this); - late final RemoteAlbumUserEntity remoteAlbumUserEntity = RemoteAlbumUserEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); late final MemoryEntity memoryEntity = MemoryEntity(this); late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); late final PersonEntity personEntity = PersonEntity(this); late final AssetFaceEntity assetFaceEntity = AssetFaceEntity(this); @override - Iterable> get allTables => allSchemaEntities.whereType>(); + Iterable> get allTables => + allSchemaEntities.whereType>(); @override List get allSchemaEntities => [ - userEntity, - remoteAssetEntity, - stackEntity, - localAssetEntity, - localAlbumEntity, - localAlbumAssetEntity, - idxLocalAssetChecksum, - uQRemoteAssetOwnerChecksum, - idxRemoteAssetChecksum, - userMetadataEntity, - partnerEntity, - remoteExifEntity, - remoteAlbumEntity, - remoteAlbumAssetEntity, - remoteAlbumUserEntity, - memoryEntity, - memoryAssetEntity, - personEntity, - assetFaceEntity - ]; + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + ]; @override int get schemaVersion => 4; @override - DriftDatabaseOptions get options => const DriftDatabaseOptions(storeDateTimeAsText: true); + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); } diff --git a/mobile/test/fixtures/album.stub.dart b/mobile/test/fixtures/album.stub.dart index 1e79f62faf..a22a4b72ab 100644 --- a/mobile/test/fixtures/album.stub.dart +++ b/mobile/test/fixtures/album.stub.dart @@ -42,20 +42,21 @@ final class AlbumStub { endDate: DateTime(2023), )..assets.addAll([AssetStub.image1]); - static final twoAsset = Album( - name: "album-with-two-assets", - localId: "album-with-two-assets-local", - remoteId: "album-with-two-assets-remote", - createdAt: DateTime(2001), - modifiedAt: DateTime(2010), - shared: false, - activityEnabled: false, - startDate: DateTime(2019), - endDate: DateTime(2020), - ) - ..assets.addAll([AssetStub.image1, AssetStub.image2]) - ..activityEnabled = true - ..owner.value = User.fromDto(UserStub.admin); + static final twoAsset = + Album( + name: "album-with-two-assets", + localId: "album-with-two-assets-local", + remoteId: "album-with-two-assets-remote", + createdAt: DateTime(2001), + modifiedAt: DateTime(2010), + shared: false, + activityEnabled: false, + startDate: DateTime(2019), + endDate: DateTime(2020), + ) + ..assets.addAll([AssetStub.image1, AssetStub.image2]) + ..activityEnabled = true + ..owner.value = User.fromDto(UserStub.admin); static final create2020end2020Album = Album( name: "create2020update2020Album", diff --git a/mobile/test/fixtures/sync_stream.stub.dart b/mobile/test/fixtures/sync_stream.stub.dart index 6c47b9172e..a89be5ad24 100644 --- a/mobile/test/fixtures/sync_stream.stub.dart +++ b/mobile/test/fixtures/sync_stream.stub.dart @@ -4,24 +4,12 @@ import 'package:openapi/api.dart'; abstract final class SyncStreamStub { static final userV1Admin = SyncEvent( type: SyncEntityType.userV1, - data: SyncUserV1( - deletedAt: DateTime(2020), - email: "admin@admin", - id: "1", - name: "Admin", - avatarColor: null, - ), + data: SyncUserV1(deletedAt: DateTime(2020), email: "admin@admin", id: "1", name: "Admin", avatarColor: null), ack: "1", ); static final userV1User = SyncEvent( type: SyncEntityType.userV1, - data: SyncUserV1( - deletedAt: DateTime(2021), - email: "user@user", - id: "5", - name: "User", - avatarColor: null, - ), + data: SyncUserV1(deletedAt: DateTime(2021), email: "user@user", id: "5", name: "User", avatarColor: null), ack: "5", ); static final userDeleteV1 = SyncEvent( @@ -32,11 +20,7 @@ abstract final class SyncStreamStub { static final partnerV1 = SyncEvent( type: SyncEntityType.partnerV1, - data: SyncPartnerV1( - inTimeline: true, - sharedById: "1", - sharedWithId: "2", - ), + data: SyncPartnerV1(inTimeline: true, sharedById: "1", sharedWithId: "2"), ack: "3", ); static final partnerDeleteV1 = SyncEvent( @@ -72,19 +56,13 @@ abstract final class SyncStreamStub { static final memoryToAssetV1 = SyncEvent( type: SyncEntityType.memoryToAssetV1, - data: SyncMemoryAssetV1( - assetId: "asset-1", - memoryId: "memory-1", - ), + data: SyncMemoryAssetV1(assetId: "asset-1", memoryId: "memory-1"), ack: "7", ); static final memoryToAssetDeleteV1 = SyncEvent( type: SyncEntityType.memoryToAssetDeleteV1, - data: SyncMemoryAssetDeleteV1( - assetId: "asset-2", - memoryId: "memory-1", - ), + data: SyncMemoryAssetDeleteV1(assetId: "asset-2", memoryId: "memory-1"), ack: "8", ); } diff --git a/mobile/test/infrastructure/repositories/local_album_repository_test.dart b/mobile/test/infrastructure/repositories/local_album_repository_test.dart index bb4292be07..fae0e09171 100644 --- a/mobile/test/infrastructure/repositories/local_album_repository_test.dart +++ b/mobile/test/infrastructure/repositories/local_album_repository_test.dart @@ -12,48 +12,21 @@ void main() { late MediumFactory mediumFactory; setUp(() { - db = Drift( - DatabaseConnection( - NativeDatabase.memory(), - closeStreamsSynchronously: true, - ), - ); + db = Drift(DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true)); mediumFactory = MediumFactory(db); }); group('getAll', () { test('sorts albums by backupSelection & isIosSharedAlbum', () async { final localAlbumRepo = mediumFactory.getRepository(); + await localAlbumRepo.upsert(mediumFactory.localAlbum(id: '1', backupSelection: BackupSelection.none)); + await localAlbumRepo.upsert(mediumFactory.localAlbum(id: '2', backupSelection: BackupSelection.excluded)); await localAlbumRepo.upsert( - mediumFactory.localAlbum( - id: '1', - backupSelection: BackupSelection.none, - ), - ); - await localAlbumRepo.upsert( - mediumFactory.localAlbum( - id: '2', - backupSelection: BackupSelection.excluded, - ), - ); - await localAlbumRepo.upsert( - mediumFactory.localAlbum( - id: '3', - backupSelection: BackupSelection.selected, - isIosSharedAlbum: true, - ), - ); - await localAlbumRepo.upsert( - mediumFactory.localAlbum( - id: '4', - backupSelection: BackupSelection.selected, - ), + mediumFactory.localAlbum(id: '3', backupSelection: BackupSelection.selected, isIosSharedAlbum: true), ); + await localAlbumRepo.upsert(mediumFactory.localAlbum(id: '4', backupSelection: BackupSelection.selected)); final albums = await localAlbumRepo.getAll( - sortBy: { - SortLocalAlbumsBy.backupSelection, - SortLocalAlbumsBy.isIosSharedAlbum, - }, + sortBy: {SortLocalAlbumsBy.backupSelection, SortLocalAlbumsBy.isIosSharedAlbum}, ); expect(albums.length, 4); expect(albums[0].id, '4'); // selected diff --git a/mobile/test/infrastructure/repositories/store_repository_test.dart b/mobile/test/infrastructure/repositories/store_repository_test.dart index 6d75fbc765..84d18ad955 100644 --- a/mobile/test/infrastructure/repositories/store_repository_test.dart +++ b/mobile/test/infrastructure/repositories/store_repository_test.dart @@ -24,16 +24,8 @@ Future _addStrStoreValue(Isar db, StoreKey key, String? value) async { Future _populateStore(Isar db) async { await db.writeTxn(() async { - await _addIntStoreValue( - db, - StoreKey.colorfulInterface, - _kTestColorfulInterface ? 1 : 0, - ); - await _addIntStoreValue( - db, - StoreKey.backupFailedSince, - _kTestBackupFailed.millisecondsSinceEpoch, - ); + await _addIntStoreValue(db, StoreKey.colorfulInterface, _kTestColorfulInterface ? 1 : 0); + await _addIntStoreValue(db, StoreKey.backupFailedSince, _kTestBackupFailed.millisecondsSinceEpoch); await _addStrStoreValue(db, StoreKey.accessToken, _kTestAccessToken); await _addIntStoreValue(db, StoreKey.version, _kTestVersion); }); @@ -143,21 +135,10 @@ void main() { stream, emitsInAnyOrder([ emits(const StoreDto(StoreKey.version, _kTestVersion)), - emits( - StoreDto(StoreKey.backupFailedSince, _kTestBackupFailed), - ), - emits( - const StoreDto(StoreKey.accessToken, _kTestAccessToken), - ), - emits( - const StoreDto( - StoreKey.colorfulInterface, - _kTestColorfulInterface, - ), - ), - emits( - const StoreDto(StoreKey.version, _kTestVersion + 10), - ), + emits(StoreDto(StoreKey.backupFailedSince, _kTestBackupFailed)), + emits(const StoreDto(StoreKey.accessToken, _kTestAccessToken)), + emits(const StoreDto(StoreKey.colorfulInterface, _kTestColorfulInterface)), + emits(const StoreDto(StoreKey.version, _kTestVersion + 10)), ]), ); await sut.update(StoreKey.version, _kTestVersion + 10); diff --git a/mobile/test/infrastructure/repositories/sync_api_repository_test.dart b/mobile/test/infrastructure/repositories/sync_api_repository_test.dart index 3348cc7c04..d456b06f7c 100644 --- a/mobile/test/infrastructure/repositories/sync_api_repository_test.dart +++ b/mobile/test/infrastructure/repositories/sync_api_repository_test.dart @@ -63,14 +63,8 @@ void main() { } }); - Future streamChanges( - Function(List, Function() abort) onDataCallback, - ) { - return sut.streamChanges( - onDataCallback, - batchSize: testBatchSize, - httpClient: mockHttpClient, - ); + Future streamChanges(Function(List, Function() abort) onDataCallback) { + return sut.streamChanges(onDataCallback, batchSize: testBatchSize, httpClient: mockHttpClient); } test('streamChanges stops processing stream when abort is called', () async { @@ -96,11 +90,7 @@ void main() { for (int i = 0; i < testBatchSize; i++) { responseStreamController.add( utf8.encode( - _createJsonLine( - SyncEntityType.userDeleteV1.toString(), - SyncUserDeleteV1(userId: "user$i").toJson(), - 'ack$i', - ), + _createJsonLine(SyncEntityType.userDeleteV1.toString(), SyncUserDeleteV1(userId: "user$i").toJson(), 'ack$i'), ), ); } @@ -108,11 +98,7 @@ void main() { for (int i = testBatchSize; i < testBatchSize * 2; i++) { responseStreamController.add( utf8.encode( - _createJsonLine( - SyncEntityType.userDeleteV1.toString(), - SyncUserDeleteV1(userId: "user$i").toJson(), - 'ack$i', - ), + _createJsonLine(SyncEntityType.userDeleteV1.toString(), SyncUserDeleteV1(userId: "user$i").toJson(), 'ack$i'), ), ); } @@ -126,113 +112,91 @@ void main() { verify(() => mockHttpClient.close()).called(1); }); - test( - 'streamChanges does not process remaining lines in finally block if aborted', - () async { - int onDataCallCount = 0; - bool abortWasCalledInCallback = false; + test('streamChanges does not process remaining lines in finally block if aborted', () async { + int onDataCallCount = 0; + bool abortWasCalledInCallback = false; - onDataCallback(List events, Function() abort) { - onDataCallCount++; - if (onDataCallCount == 1) { - abort(); - abortWasCalledInCallback = true; - } else { - fail("onData called more than once after abort was invoked"); - } + onDataCallback(List events, Function() abort) { + onDataCallCount++; + if (onDataCallCount == 1) { + abort(); + abortWasCalledInCallback = true; + } else { + fail("onData called more than once after abort was invoked"); } + } - final streamChangesFuture = streamChanges(onDataCallback); + final streamChangesFuture = streamChanges(onDataCallback); - await pumpEventQueue(); + await pumpEventQueue(); - for (int i = 0; i < testBatchSize; i++) { - responseStreamController.add( - utf8.encode( - _createJsonLine( - SyncEntityType.userDeleteV1.toString(), - SyncUserDeleteV1(userId: "user$i").toJson(), - 'ack$i', - ), - ), - ); - } - - // emit a single event to skip batching and trigger finally + for (int i = 0; i < testBatchSize; i++) { responseStreamController.add( utf8.encode( - _createJsonLine( - SyncEntityType.userDeleteV1.toString(), - SyncUserDeleteV1(userId: "user100").toJson(), - 'ack100', - ), + _createJsonLine(SyncEntityType.userDeleteV1.toString(), SyncUserDeleteV1(userId: "user$i").toJson(), 'ack$i'), ), ); + } - await responseStreamController.close(); - await expectLater(streamChangesFuture, completes); + // emit a single event to skip batching and trigger finally + responseStreamController.add( + utf8.encode( + _createJsonLine(SyncEntityType.userDeleteV1.toString(), SyncUserDeleteV1(userId: "user100").toJson(), 'ack100'), + ), + ); - expect(onDataCallCount, 1); - expect(abortWasCalledInCallback, isTrue); - verify(() => mockHttpClient.close()).called(1); - }, - ); + await responseStreamController.close(); + await expectLater(streamChangesFuture, completes); - test( - 'streamChanges processes remaining lines in finally block if not aborted', - () async { - int onDataCallCount = 0; - List receivedEventsBatch1 = []; - List receivedEventsBatch2 = []; + expect(onDataCallCount, 1); + expect(abortWasCalledInCallback, isTrue); + verify(() => mockHttpClient.close()).called(1); + }); - onDataCallback(List events, Function() _) { - onDataCallCount++; - if (onDataCallCount == 1) { - receivedEventsBatch1 = events; - } else if (onDataCallCount == 2) { - receivedEventsBatch2 = events; - } else { - fail("onData called more than expected"); - } + test('streamChanges processes remaining lines in finally block if not aborted', () async { + int onDataCallCount = 0; + List receivedEventsBatch1 = []; + List receivedEventsBatch2 = []; + + onDataCallback(List events, Function() _) { + onDataCallCount++; + if (onDataCallCount == 1) { + receivedEventsBatch1 = events; + } else if (onDataCallCount == 2) { + receivedEventsBatch2 = events; + } else { + fail("onData called more than expected"); } + } - final streamChangesFuture = streamChanges(onDataCallback); + final streamChangesFuture = streamChanges(onDataCallback); - await pumpEventQueue(); + await pumpEventQueue(); - // Batch 1 - for (int i = 0; i < testBatchSize; i++) { - responseStreamController.add( - utf8.encode( - _createJsonLine( - SyncEntityType.userDeleteV1.toString(), - SyncUserDeleteV1(userId: "user$i").toJson(), - 'ack$i', - ), - ), - ); - } - - // Partial Batch 2 + // Batch 1 + for (int i = 0; i < testBatchSize; i++) { responseStreamController.add( utf8.encode( - _createJsonLine( - SyncEntityType.userDeleteV1.toString(), - SyncUserDeleteV1(userId: "user100").toJson(), - 'ack100', - ), + _createJsonLine(SyncEntityType.userDeleteV1.toString(), SyncUserDeleteV1(userId: "user$i").toJson(), 'ack$i'), ), ); + } - await responseStreamController.close(); - await expectLater(streamChangesFuture, completes); + // Partial Batch 2 + responseStreamController.add( + utf8.encode( + _createJsonLine(SyncEntityType.userDeleteV1.toString(), SyncUserDeleteV1(userId: "user100").toJson(), 'ack100'), + ), + ); - expect(onDataCallCount, 2); - expect(receivedEventsBatch1.length, testBatchSize); - expect(receivedEventsBatch2.length, 1); - verify(() => mockHttpClient.close()).called(1); - }, - ); + await responseStreamController.close(); + await expectLater(streamChangesFuture, completes); + + expect(onDataCallCount, 2); + expect(receivedEventsBatch1.length, testBatchSize); + expect(receivedEventsBatch2.length, 1); + verify(() => mockHttpClient.close()).called(1); + }); test('streamChanges handles stream error gracefully', () async { final streamError = Exception("Network Error"); @@ -248,11 +212,7 @@ void main() { responseStreamController.add( utf8.encode( - _createJsonLine( - SyncEntityType.userDeleteV1.toString(), - SyncUserDeleteV1(userId: "user1").toJson(), - 'ack1', - ), + _createJsonLine(SyncEntityType.userDeleteV1.toString(), SyncUserDeleteV1(userId: "user1").toJson(), 'ack1'), ), ); diff --git a/mobile/test/mock_http_override.dart b/mobile/test/mock_http_override.dart index ae19d932c0..877d6f8726 100644 --- a/mobile/test/mock_http_override.dart +++ b/mobile/test/mock_http_override.dart @@ -40,12 +40,9 @@ class MockHttpOverrides extends HttpOverrides { final cancelOnError = invocation.namedArguments[#cancelOnError] as bool; - return Stream>.fromIterable([kTransparentImage.toList()]).listen( - onData, - onDone: onDone, - onError: onError, - cancelOnError: cancelOnError, - ); + return Stream>.fromIterable([ + kTransparentImage.toList(), + ]).listen(onData, onDone: onDone, onError: onError, cancelOnError: cancelOnError); }); return client; diff --git a/mobile/test/modules/activity/activities_page_test.dart b/mobile/test/modules/activity/activities_page_test.dart index c3279e9b58..05eac98111 100644 --- a/mobile/test/modules/activity/activities_page_test.dart +++ b/mobile/test/modules/activity/activities_page_test.dart @@ -49,19 +49,8 @@ final _activities = [ comment: 'Second Activity', user: UserStub.user1, ), - Activity( - id: '3', - createdAt: DateTime(300), - type: ActivityType.like, - assetId: 'asset-1', - user: UserStub.user2, - ), - Activity( - id: '4', - createdAt: DateTime(400), - type: ActivityType.like, - user: UserStub.user1, - ), + Activity(id: '3', createdAt: DateTime(300), type: ActivityType.like, assetId: 'asset-1', user: UserStub.user2), + Activity(id: '4', createdAt: DateTime(400), type: ActivityType.like, user: UserStub.user1), ]; void main() { @@ -85,10 +74,7 @@ void main() { mockCurrentAssetProvider = MockCurrentAssetProvider(AssetStub.image1); activityMock = MockAlbumActivity(_activities); overrides = [ - albumActivityProvider( - AlbumStub.twoAsset.remoteId!, - AssetStub.image1.remoteId!, - ).overrideWith(() => activityMock), + albumActivityProvider(AlbumStub.twoAsset.remoteId!, AssetStub.image1.remoteId!).overrideWith(() => activityMock), currentAlbumProvider.overrideWith(() => mockCurrentAlbumProvider), currentAssetProvider.overrideWith(() => mockCurrentAssetProvider), ]; @@ -108,147 +94,82 @@ void main() { }); group("App bar", () { - testWidgets( - "No title when currentAsset != null", - (tester) async { - await tester.pumpConsumerWidget( - const ActivitiesPage(), - overrides: overrides, - ); + testWidgets("No title when currentAsset != null", (tester) async { + await tester.pumpConsumerWidget(const ActivitiesPage(), overrides: overrides); - final listTile = tester.widget(find.byType(AppBar)); - expect(listTile.title, isNull); - }, - ); + final listTile = tester.widget(find.byType(AppBar)); + expect(listTile.title, isNull); + }); - testWidgets( - "Album name as title when currentAsset == null", - (tester) async { - await tester.pumpConsumerWidget( - const ActivitiesPage(), - overrides: overrides, - ); - await tester.pumpAndSettle(); + testWidgets("Album name as title when currentAsset == null", (tester) async { + await tester.pumpConsumerWidget(const ActivitiesPage(), overrides: overrides); + await tester.pumpAndSettle(); - mockCurrentAssetProvider.state = null; - await tester.pumpAndSettle(); + mockCurrentAssetProvider.state = null; + await tester.pumpAndSettle(); - expect(find.text(AlbumStub.twoAsset.name), findsOneWidget); - final listTile = tester.widget(find.byType(AppBar)); - expect(listTile.title, isNotNull); - }, - ); + expect(find.text(AlbumStub.twoAsset.name), findsOneWidget); + final listTile = tester.widget(find.byType(AppBar)); + expect(listTile.title, isNotNull); + }); }); group("Body", () { - testWidgets( - "Contains a stack with Activity List and Activity Input", - (tester) async { - await tester.pumpConsumerWidget( - const ActivitiesPage(), - overrides: overrides, - ); - await tester.pumpAndSettle(); + testWidgets("Contains a stack with Activity List and Activity Input", (tester) async { + await tester.pumpConsumerWidget(const ActivitiesPage(), overrides: overrides); + await tester.pumpAndSettle(); - expect( - find.descendant( - of: find.byType(Stack), - matching: find.byType(ActivityTextField), - ), - findsOneWidget, - ); + expect(find.descendant(of: find.byType(Stack), matching: find.byType(ActivityTextField)), findsOneWidget); - expect( - find.descendant( - of: find.byType(Stack), - matching: find.byType(ListView), - ), - findsOneWidget, - ); - }, - ); + expect(find.descendant(of: find.byType(Stack), matching: find.byType(ListView)), findsOneWidget); + }); - testWidgets( - "List Contains all dismissible activities", - (tester) async { - await tester.pumpConsumerWidget( - const ActivitiesPage(), - overrides: overrides, - ); - await tester.pumpAndSettle(); + testWidgets("List Contains all dismissible activities", (tester) async { + await tester.pumpConsumerWidget(const ActivitiesPage(), overrides: overrides); + await tester.pumpAndSettle(); - final listFinder = find.descendant( - of: find.byType(Stack), - matching: find.byType(ListView), - ); - final listChildren = find.descendant( - of: listFinder, - matching: find.byType(DismissibleActivity), - ); - expect(listChildren, findsNWidgets(_activities.length)); - }, - ); + final listFinder = find.descendant(of: find.byType(Stack), matching: find.byType(ListView)); + final listChildren = find.descendant(of: listFinder, matching: find.byType(DismissibleActivity)); + expect(listChildren, findsNWidgets(_activities.length)); + }); - testWidgets( - "Submitting text input adds a comment with the text", - (tester) async { - await tester.pumpConsumerWidget( - const ActivitiesPage(), - overrides: overrides, - ); - await tester.pumpAndSettle(); + testWidgets("Submitting text input adds a comment with the text", (tester) async { + await tester.pumpConsumerWidget(const ActivitiesPage(), overrides: overrides); + await tester.pumpAndSettle(); - when(() => activityMock.addComment(any())).thenAnswer((_) => Future.value()); + when(() => activityMock.addComment(any())).thenAnswer((_) => Future.value()); - final textField = find.byType(TextField); - await tester.enterText(textField, 'Test comment'); - await tester.testTextInput.receiveAction(TextInputAction.done); + final textField = find.byType(TextField); + await tester.enterText(textField, 'Test comment'); + await tester.testTextInput.receiveAction(TextInputAction.done); - verify(() => activityMock.addComment('Test comment')); - }, - ); + verify(() => activityMock.addComment('Test comment')); + }); - testWidgets( - "Owner can remove all activities", - (tester) async { - await tester.pumpConsumerWidget( - const ActivitiesPage(), - overrides: overrides, - ); - await tester.pumpAndSettle(); + testWidgets("Owner can remove all activities", (tester) async { + await tester.pumpConsumerWidget(const ActivitiesPage(), overrides: overrides); + await tester.pumpAndSettle(); - final deletableActivityFinder = find.byWidgetPredicate( - (widget) => widget is DismissibleActivity && widget.onDismiss != null, - ); - expect(deletableActivityFinder, findsNWidgets(_activities.length)); - }, - ); + final deletableActivityFinder = find.byWidgetPredicate( + (widget) => widget is DismissibleActivity && widget.onDismiss != null, + ); + expect(deletableActivityFinder, findsNWidgets(_activities.length)); + }); - testWidgets( - "Non-Owner can remove only their activities", - (tester) async { - final mockCurrentUser = MockCurrentUserProvider(); + testWidgets("Non-Owner can remove only their activities", (tester) async { + final mockCurrentUser = MockCurrentUserProvider(); - await tester.pumpConsumerWidget( - const ActivitiesPage(), - overrides: [ - ...overrides, - currentUserProvider.overrideWith((ref) => mockCurrentUser), - ], - ); - mockCurrentUser.state = UserStub.user1; - await tester.pumpAndSettle(); + await tester.pumpConsumerWidget( + const ActivitiesPage(), + overrides: [...overrides, currentUserProvider.overrideWith((ref) => mockCurrentUser)], + ); + mockCurrentUser.state = UserStub.user1; + await tester.pumpAndSettle(); - final deletableActivityFinder = find.byWidgetPredicate( - (widget) => widget is DismissibleActivity && widget.onDismiss != null, - ); - expect( - deletableActivityFinder, - findsNWidgets( - _activities.where((a) => a.user == UserStub.user1).length, - ), - ); - }, - ); + final deletableActivityFinder = find.byWidgetPredicate( + (widget) => widget is DismissibleActivity && widget.onDismiss != null, + ); + expect(deletableActivityFinder, findsNWidgets(_activities.where((a) => a.user == UserStub.user1).length)); + }); }); } diff --git a/mobile/test/modules/activity/activity_provider_test.dart b/mobile/test/modules/activity/activity_provider_test.dart index 9bac84bab9..7964b43cad 100644 --- a/mobile/test/modules/activity/activity_provider_test.dart +++ b/mobile/test/modules/activity/activity_provider_test.dart @@ -26,19 +26,8 @@ final _activities = [ comment: 'Second Activity', user: UserStub.user1, ), - Activity( - id: '3', - createdAt: DateTime(300), - type: ActivityType.like, - assetId: 'asset-1', - user: UserStub.admin, - ), - Activity( - id: '4', - createdAt: DateTime(400), - type: ActivityType.like, - user: UserStub.user1, - ), + Activity(id: '3', createdAt: DateTime(300), type: ActivityType.like, assetId: 'asset-1', user: UserStub.admin), + Activity(id: '4', createdAt: DateTime(400), type: ActivityType.like, user: UserStub.user1), ]; void main() { @@ -70,11 +59,7 @@ void main() { // Init and wait for providers future to complete provider = albumActivityProvider('test-album', 'test-asset'); listener = ListenerMock(); - container.listen( - provider, - listener.call, - fireImmediately: true, - ); + container.listen(provider, listener.call, fireImmediately: true); await container.read(provider.future); }); @@ -83,18 +68,14 @@ void main() { verifyInOrder([ () => listener.call(null, const AsyncLoading()), () => listener.call( - const AsyncLoading(), - any( - that: allOf( - [ - isA>>(), - predicate( - (AsyncData> ad) => ad.requireValue.every((e) => _activities.contains(e)), - ), - ], - ), - ), - ), + const AsyncLoading(), + any( + that: allOf([ + isA>>(), + predicate((AsyncData> ad) => ad.requireValue.every((e) => _activities.contains(e))), + ]), + ), + ), ]); verifyNoMoreInteractions(listener); @@ -102,30 +83,15 @@ void main() { group('addLike()', () { test('Like successfully added', () async { - final like = Activity( - id: '5', - createdAt: DateTime(2023), - type: ActivityType.like, - user: UserStub.admin, - ); + final like = Activity(id: '5', createdAt: DateTime(2023), type: ActivityType.like, user: UserStub.admin); when( - () => activityMock.addActivity( - 'test-album', - ActivityType.like, - assetId: 'test-asset', - ), + () => activityMock.addActivity('test-album', ActivityType.like, assetId: 'test-asset'), ).thenAnswer((_) async => AsyncData(like)); await container.read(provider.notifier).addLike(); - verify( - () => activityMock.addActivity( - 'test-album', - ActivityType.like, - assetId: 'test-asset', - ), - ); + verify(() => activityMock.addActivity('test-album', ActivityType.like, assetId: 'test-asset')); final activities = await container.read(provider.future); expect(activities, hasLength(5)); @@ -136,31 +102,14 @@ void main() { }); test('Like failed', () async { - final like = Activity( - id: '5', - createdAt: DateTime(2023), - type: ActivityType.like, - user: UserStub.admin, - ); + final like = Activity(id: '5', createdAt: DateTime(2023), type: ActivityType.like, user: UserStub.admin); when( - () => activityMock.addActivity( - 'test-album', - ActivityType.like, - assetId: 'test-asset', - ), - ).thenAnswer( - (_) async => AsyncError(Exception('Mock'), StackTrace.current), - ); + () => activityMock.addActivity('test-album', ActivityType.like, assetId: 'test-asset'), + ).thenAnswer((_) async => AsyncError(Exception('Mock'), StackTrace.current)); await container.read(provider.notifier).addLike(); - verify( - () => activityMock.addActivity( - 'test-album', - ActivityType.like, - assetId: 'test-asset', - ), - ); + verify(() => activityMock.addActivity('test-album', ActivityType.like, assetId: 'test-asset')); final activities = await container.read(provider.future); expect(activities, hasLength(4)); @@ -174,16 +123,11 @@ void main() { await container.read(provider.notifier).removeActivity('3'); - verify( - () => activityMock.removeActivity('3'), - ); + verify(() => activityMock.removeActivity('3')); final activities = await container.read(provider.future); expect(activities, hasLength(3)); - expect( - activities, - isNot(anyElement(predicate((Activity a) => a.id == '3'))), - ); + expect(activities, isNot(anyElement(predicate((Activity a) => a.id == '3')))); verifyNever(() => activityStatisticsMock.removeActivity()); }); @@ -195,10 +139,7 @@ void main() { final activities = await container.read(provider.future); expect(activities, hasLength(4)); - expect( - activities, - anyElement(predicate((Activity a) => a.id == '3')), - ); + expect(activities, anyElement(predicate((Activity a) => a.id == '3'))); }); test('Comment successfully removed', () async { @@ -207,10 +148,7 @@ void main() { await container.read(provider.notifier).removeActivity('1'); final activities = await container.read(provider.future); - expect( - activities, - isNot(anyElement(predicate((Activity a) => a.id == '1'))), - ); + expect(activities, isNot(anyElement(predicate((Activity a) => a.id == '1')))); verify(() => activityStatisticsMock.removeActivity()); }); @@ -281,11 +219,7 @@ void main() { ); when( - () => activityMock.addActivity( - 'test-album', - ActivityType.comment, - comment: 'Test-Comment', - ), + () => activityMock.addActivity('test-album', ActivityType.comment, comment: 'Test-Comment'), ).thenAnswer((_) async => AsyncData(comment)); when(() => albumActivityStatisticsMock.build('test-album')).thenReturn(2); when(() => activityMock.getAllActivities('test-album')).thenAnswer((_) async => [..._activities]); @@ -294,12 +228,7 @@ void main() { await container.read(albumProvider.notifier).addComment('Test-Comment'); verify( - () => activityMock.addActivity( - 'test-album', - ActivityType.comment, - assetId: null, - comment: 'Test-Comment', - ), + () => activityMock.addActivity('test-album', ActivityType.comment, assetId: null, comment: 'Test-Comment'), ); final activities = await container.read(albumProvider.future); @@ -327,9 +256,7 @@ void main() { assetId: 'test-asset', comment: 'Test-Comment', ), - ).thenAnswer( - (_) async => AsyncError(Exception('Error'), StackTrace.current), - ); + ).thenAnswer((_) async => AsyncError(Exception('Error'), StackTrace.current)); await container.read(provider.notifier).addComment('Test-Comment'); diff --git a/mobile/test/modules/activity/activity_statistics_provider_test.dart b/mobile/test/modules/activity/activity_statistics_provider_test.dart index 0216528ddd..7fe73868f5 100644 --- a/mobile/test/modules/activity/activity_statistics_provider_test.dart +++ b/mobile/test/modules/activity/activity_statistics_provider_test.dart @@ -15,11 +15,7 @@ void main() { setUp(() async { activityMock = ActivityServiceMock(); - container = TestUtils.createContainer( - overrides: [ - activityServiceProvider.overrideWith((ref) => activityMock), - ], - ); + container = TestUtils.createContainer(overrides: [activityServiceProvider.overrideWith((ref) => activityMock)]); listener = ListenerMock(); }); @@ -31,34 +27,21 @@ void main() { // Read here to make the getStatistics call container.read(activityStatisticsProvider('test-album', 'test-asset')); - container.listen( - activityStatisticsProvider('test-album', 'test-asset'), - listener.call, - fireImmediately: true, - ); + container.listen(activityStatisticsProvider('test-album', 'test-asset'), listener.call, fireImmediately: true); // Sleep for the getStatistics future to resolve await Future.delayed(const Duration(milliseconds: 1)); - verifyInOrder([ - () => listener.call(null, 0), - () => listener.call(0, 5), - ]); + verifyInOrder([() => listener.call(null, 0), () => listener.call(0, 5)]); verifyNoMoreInteractions(listener); }); test('Adds activity', () async { - when( - () => activityMock.getStatistics('test-album'), - ).thenAnswer((_) async => const ActivityStats(comments: 10)); + when(() => activityMock.getStatistics('test-album')).thenAnswer((_) async => const ActivityStats(comments: 10)); final provider = activityStatisticsProvider('test-album'); - container.listen( - provider, - listener.call, - fireImmediately: true, - ); + container.listen(provider, listener.call, fireImmediately: true); // Sleep for the getStatistics future to resolve await Future.delayed(const Duration(milliseconds: 1)); @@ -75,11 +58,7 @@ void main() { ).thenAnswer((_) async => const ActivityStats(comments: 10)); final provider = activityStatisticsProvider('new-album', 'test-asset'); - container.listen( - provider, - listener.call, - fireImmediately: true, - ); + container.listen(provider, listener.call, fireImmediately: true); // Sleep for the getStatistics future to resolve await Future.delayed(const Duration(milliseconds: 1)); diff --git a/mobile/test/modules/activity/activity_text_field_test.dart b/mobile/test/modules/activity/activity_text_field_test.dart index e74099cdcd..1163330c54 100644 --- a/mobile/test/modules/activity/activity_text_field_test.dart +++ b/mobile/test/modules/activity/activity_text_field_test.dart @@ -49,12 +49,7 @@ void main() { }); testWidgets('Returns an Input text field', (tester) async { - await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (_) {}, - ), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTextField(onSubmit: (_) {}), overrides: overrides); expect(find.byType(TextField), findsOneWidget); }); @@ -63,76 +58,38 @@ void main() { final userProvider = MockCurrentUserProvider(); await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (_) {}, - ), - overrides: [ - currentUserProvider.overrideWith((ref) => userProvider), - ...overrides, - ], + ActivityTextField(onSubmit: (_) {}), + overrides: [currentUserProvider.overrideWith((ref) => userProvider), ...overrides], ); expect(find.byType(UserCircleAvatar), findsNothing); }); testWidgets('UserCircleAvatar displayed when user != null', (tester) async { - await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (_) {}, - ), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTextField(onSubmit: (_) {}), overrides: overrides); expect(find.byType(UserCircleAvatar), findsOneWidget); }); - testWidgets( - 'Filled icon if likedId != null', - (tester) async { - await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (_) {}, - likeId: '1', - ), - overrides: overrides, - ); - - expect( - find.widgetWithIcon(IconButton, Icons.favorite_rounded), - findsOneWidget, - ); - expect( - find.widgetWithIcon(IconButton, Icons.favorite_border_rounded), - findsNothing, - ); - }, - ); - - testWidgets('Bordered icon if likedId == null', (tester) async { + testWidgets('Filled icon if likedId != null', (tester) async { await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (_) {}, - ), + ActivityTextField(onSubmit: (_) {}, likeId: '1'), overrides: overrides, ); - expect( - find.widgetWithIcon(IconButton, Icons.favorite_border_rounded), - findsOneWidget, - ); - expect( - find.widgetWithIcon(IconButton, Icons.favorite_rounded), - findsNothing, - ); + expect(find.widgetWithIcon(IconButton, Icons.favorite_rounded), findsOneWidget); + expect(find.widgetWithIcon(IconButton, Icons.favorite_border_rounded), findsNothing); + }); + + testWidgets('Bordered icon if likedId == null', (tester) async { + await tester.pumpConsumerWidget(ActivityTextField(onSubmit: (_) {}), overrides: overrides); + + expect(find.widgetWithIcon(IconButton, Icons.favorite_border_rounded), findsOneWidget); + expect(find.widgetWithIcon(IconButton, Icons.favorite_rounded), findsNothing); }); testWidgets('Adds new like', (tester) async { - await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (_) {}, - ), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTextField(onSubmit: (_) {}), overrides: overrides); when(() => activityMock.addLike()).thenAnswer((_) => Future.value()); @@ -144,10 +101,7 @@ void main() { testWidgets('Removes like if already liked', (tester) async { await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (_) {}, - likeId: 'test-suffix', - ), + ActivityTextField(onSubmit: (_) {}, likeId: 'test-suffix'), overrides: overrides, ); @@ -163,10 +117,7 @@ void main() { String? receivedText; await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (text) => receivedText = text, - likeId: 'test-suffix', - ), + ActivityTextField(onSubmit: (text) => receivedText = text, likeId: 'test-suffix'), overrides: overrides, ); @@ -180,11 +131,7 @@ void main() { String? receviedText; await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (text) => receviedText = text, - isEnabled: false, - likeId: 'test-suffix', - ), + ActivityTextField(onSubmit: (text) => receviedText = text, isEnabled: false, likeId: 'test-suffix'), overrides: overrides, ); diff --git a/mobile/test/modules/activity/activity_tile_test.dart b/mobile/test/modules/activity/activity_tile_test.dart index fddbb6269c..eb4bb25848 100644 --- a/mobile/test/modules/activity/activity_tile_test.dart +++ b/mobile/test/modules/activity/activity_tile_test.dart @@ -43,14 +43,7 @@ void main() { testWidgets('Returns a ListTile', (tester) async { await tester.pumpConsumerWidget( - ActivityTile( - Activity( - id: '1', - createdAt: DateTime(100), - type: ActivityType.like, - user: UserStub.admin, - ), - ), + ActivityTile(Activity(id: '1', createdAt: DateTime(100), type: ActivityType.like, user: UserStub.admin)), overrides: overrides, ); @@ -59,14 +52,7 @@ void main() { testWidgets('No trailing widget when activity assetId == null', (tester) async { await tester.pumpConsumerWidget( - ActivityTile( - Activity( - id: '1', - createdAt: DateTime(100), - type: ActivityType.like, - user: UserStub.admin, - ), - ), + ActivityTile(Activity(id: '1', createdAt: DateTime(100), type: ActivityType.like, user: UserStub.admin)), overrides: overrides, ); @@ -77,13 +63,7 @@ void main() { testWidgets('Asset Thumbanil as trailing widget when activity assetId != null', (tester) async { await tester.pumpConsumerWidget( ActivityTile( - Activity( - id: '1', - createdAt: DateTime(100), - type: ActivityType.like, - user: UserStub.admin, - assetId: '1', - ), + Activity(id: '1', createdAt: DateTime(100), type: ActivityType.like, user: UserStub.admin, assetId: '1'), ), overrides: overrides, ); @@ -96,13 +76,7 @@ void main() { testWidgets('No trailing widget when current asset != null', (tester) async { await tester.pumpConsumerWidget( ActivityTile( - Activity( - id: '1', - createdAt: DateTime(100), - type: ActivityType.like, - user: UserStub.admin, - assetId: '1', - ), + Activity(id: '1', createdAt: DateTime(100), type: ActivityType.like, user: UserStub.admin, assetId: '1'), ), overrides: overrides, ); @@ -115,37 +89,23 @@ void main() { }); group('Like Activity', () { - final activity = Activity( - id: '1', - createdAt: DateTime(100), - type: ActivityType.like, - user: UserStub.admin, - ); + final activity = Activity(id: '1', createdAt: DateTime(100), type: ActivityType.like, user: UserStub.admin); testWidgets('Like contains filled heart as leading', (tester) async { - await tester.pumpConsumerWidget( - ActivityTile(activity), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTile(activity), overrides: overrides); // Leading widget should not be null final listTile = tester.widget(find.byType(ListTile)); expect(listTile.leading, isNotNull); // And should have a favorite icon - final favoIconFinder = find.widgetWithIcon( - listTile.leading!.runtimeType, - Icons.favorite_rounded, - ); + final favoIconFinder = find.widgetWithIcon(listTile.leading!.runtimeType, Icons.favorite_rounded); expect(favoIconFinder, findsOneWidget); }); testWidgets('Like title is center aligned', (tester) async { - await tester.pumpConsumerWidget( - ActivityTile(activity), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTile(activity), overrides: overrides); final listTile = tester.widget(find.byType(ListTile)); @@ -153,10 +113,7 @@ void main() { }); testWidgets('No subtitle for likes', (tester) async { - await tester.pumpConsumerWidget( - ActivityTile(activity), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTile(activity), overrides: overrides); final listTile = tester.widget(find.byType(ListTile)); @@ -174,10 +131,7 @@ void main() { ); testWidgets('Comment contains User Circle Avatar as leading', (tester) async { - await tester.pumpConsumerWidget( - ActivityTile(activity), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTile(activity), overrides: overrides); final userAvatarFinder = find.byType(UserCircleAvatar); expect(userAvatarFinder, findsOneWidget); @@ -192,10 +146,7 @@ void main() { }); testWidgets('Comment title is top aligned', (tester) async { - await tester.pumpConsumerWidget( - ActivityTile(activity), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTile(activity), overrides: overrides); final listTile = tester.widget(find.byType(ListTile)); @@ -203,21 +154,12 @@ void main() { }); testWidgets('Contains comment text as subtitle', (tester) async { - await tester.pumpConsumerWidget( - ActivityTile(activity), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTile(activity), overrides: overrides); final listTile = tester.widget(find.byType(ListTile)); expect(listTile.subtitle, isNotNull); - expect( - find.descendant( - of: find.byType(ListTile), - matching: find.text(activity.comment!), - ), - findsOneWidget, - ); + expect(find.descendant(of: find.byType(ListTile), matching: find.text(activity.comment!)), findsOneWidget); }); }); } diff --git a/mobile/test/modules/activity/dismissible_activity_test.dart b/mobile/test/modules/activity/dismissible_activity_test.dart index 8cc81a69cf..e5f6258ee9 100644 --- a/mobile/test/modules/activity/dismissible_activity_test.dart +++ b/mobile/test/modules/activity/dismissible_activity_test.dart @@ -15,12 +15,7 @@ import '../../test_utils.dart'; import '../../widget_tester_extensions.dart'; import '../asset_viewer/asset_viewer_mocks.dart'; -final activity = Activity( - id: '1', - createdAt: DateTime(100), - type: ActivityType.like, - user: UserStub.admin, -); +final activity = Activity(id: '1', createdAt: DateTime(100), type: ActivityType.like, user: UserStub.admin); void main() { late MockCurrentAssetProvider assetProvider; @@ -34,10 +29,7 @@ void main() { }); testWidgets('Returns a Dismissible', (tester) async { - await tester.pumpConsumerWidget( - DismissibleActivity('1', ActivityTile(activity)), - overrides: overrides, - ); + await tester.pumpConsumerWidget(DismissibleActivity('1', ActivityTile(activity)), overrides: overrides); expect(find.byType(Dismissible), findsOneWidget); }); @@ -58,11 +50,7 @@ void main() { testWidgets('Ok action in ConfirmDialog should call onDismiss with activityId', (tester) async { String? receivedActivityId; await tester.pumpConsumerWidget( - DismissibleActivity( - '1', - ActivityTile(activity), - onDismiss: (id) => receivedActivityId = id, - ), + DismissibleActivity('1', ActivityTile(activity), onDismiss: (id) => receivedActivityId = id), overrides: overrides, ); @@ -91,10 +79,7 @@ void main() { }); testWidgets('No delete dialog if onDismiss is not set', (tester) async { - await tester.pumpConsumerWidget( - DismissibleActivity('1', ActivityTile(activity)), - overrides: overrides, - ); + await tester.pumpConsumerWidget(DismissibleActivity('1', ActivityTile(activity)), overrides: overrides); final dismissible = find.byType(Dismissible); await tester.drag(dismissible, const Offset(500, 0)); @@ -104,10 +89,7 @@ void main() { }); testWidgets('No icon for background if onDismiss is not set', (tester) async { - await tester.pumpConsumerWidget( - DismissibleActivity('1', ActivityTile(activity)), - overrides: overrides, - ); + await tester.pumpConsumerWidget(DismissibleActivity('1', ActivityTile(activity)), overrides: overrides); final dismissible = find.byType(Dismissible); await tester.drag(dismissible, const Offset(-500, 0)); diff --git a/mobile/test/modules/album/album_sort_by_options_provider_test.dart b/mobile/test/modules/album/album_sort_by_options_provider_test.dart index bf3163f00d..a35255bc21 100644 --- a/mobile/test/modules/album/album_sort_by_options_provider_test.dart +++ b/mobile/test/modules/album/album_sort_by_options_provider_test.dart @@ -22,12 +22,7 @@ void main() { db = await TestUtils.initIsar(); }); - final albums = [ - AlbumStub.emptyAlbum, - AlbumStub.sharedWithUser, - AlbumStub.oneAsset, - AlbumStub.twoAsset, - ]; + final albums = [AlbumStub.emptyAlbum, AlbumStub.sharedWithUser, AlbumStub.oneAsset, AlbumStub.twoAsset]; setUp(() { db.writeTxnSync(() { @@ -48,23 +43,13 @@ void main() { const created = AlbumSortMode.created; test("Created time - ASC", () { final sorted = created.sortFn(albums, false); - final sortedList = [ - AlbumStub.emptyAlbum, - AlbumStub.twoAsset, - AlbumStub.oneAsset, - AlbumStub.sharedWithUser, - ]; + final sortedList = [AlbumStub.emptyAlbum, AlbumStub.twoAsset, AlbumStub.oneAsset, AlbumStub.sharedWithUser]; expect(sorted, orderedEquals(sortedList)); }); test("Created time - DESC", () { final sorted = created.sortFn(albums, true); - final sortedList = [ - AlbumStub.sharedWithUser, - AlbumStub.oneAsset, - AlbumStub.twoAsset, - AlbumStub.emptyAlbum, - ]; + final sortedList = [AlbumStub.sharedWithUser, AlbumStub.oneAsset, AlbumStub.twoAsset, AlbumStub.emptyAlbum]; expect(sorted, orderedEquals(sortedList)); }); }); @@ -73,23 +58,13 @@ void main() { const assetCount = AlbumSortMode.assetCount; test("Asset Count - ASC", () { final sorted = assetCount.sortFn(albums, false); - final sortedList = [ - AlbumStub.emptyAlbum, - AlbumStub.sharedWithUser, - AlbumStub.oneAsset, - AlbumStub.twoAsset, - ]; + final sortedList = [AlbumStub.emptyAlbum, AlbumStub.sharedWithUser, AlbumStub.oneAsset, AlbumStub.twoAsset]; expect(sorted, orderedEquals(sortedList)); }); test("Asset Count - DESC", () { final sorted = assetCount.sortFn(albums, true); - final sortedList = [ - AlbumStub.twoAsset, - AlbumStub.oneAsset, - AlbumStub.sharedWithUser, - AlbumStub.emptyAlbum, - ]; + final sortedList = [AlbumStub.twoAsset, AlbumStub.oneAsset, AlbumStub.sharedWithUser, AlbumStub.emptyAlbum]; expect(sorted, orderedEquals(sortedList)); }); }); @@ -98,23 +73,13 @@ void main() { const lastModified = AlbumSortMode.lastModified; test("Last modified - ASC", () { final sorted = lastModified.sortFn(albums, false); - final sortedList = [ - AlbumStub.twoAsset, - AlbumStub.emptyAlbum, - AlbumStub.sharedWithUser, - AlbumStub.oneAsset, - ]; + final sortedList = [AlbumStub.twoAsset, AlbumStub.emptyAlbum, AlbumStub.sharedWithUser, AlbumStub.oneAsset]; expect(sorted, orderedEquals(sortedList)); }); test("Last modified - DESC", () { final sorted = lastModified.sortFn(albums, true); - final sortedList = [ - AlbumStub.oneAsset, - AlbumStub.sharedWithUser, - AlbumStub.emptyAlbum, - AlbumStub.twoAsset, - ]; + final sortedList = [AlbumStub.oneAsset, AlbumStub.sharedWithUser, AlbumStub.emptyAlbum, AlbumStub.twoAsset]; expect(sorted, orderedEquals(sortedList)); }); }); @@ -123,23 +88,13 @@ void main() { const created = AlbumSortMode.created; test("Created - ASC", () { final sorted = created.sortFn(albums, false); - final sortedList = [ - AlbumStub.emptyAlbum, - AlbumStub.twoAsset, - AlbumStub.oneAsset, - AlbumStub.sharedWithUser, - ]; + final sortedList = [AlbumStub.emptyAlbum, AlbumStub.twoAsset, AlbumStub.oneAsset, AlbumStub.sharedWithUser]; expect(sorted, orderedEquals(sortedList)); }); test("Created - DESC", () { final sorted = created.sortFn(albums, true); - final sortedList = [ - AlbumStub.sharedWithUser, - AlbumStub.oneAsset, - AlbumStub.twoAsset, - AlbumStub.emptyAlbum, - ]; + final sortedList = [AlbumStub.sharedWithUser, AlbumStub.oneAsset, AlbumStub.twoAsset, AlbumStub.emptyAlbum]; expect(sorted, orderedEquals(sortedList)); }); }); @@ -148,15 +103,12 @@ void main() { const mostRecent = AlbumSortMode.mostRecent; test("Most Recent - DESC", () { - final sorted = mostRecent.sortFn( - [ - AlbumStub.create2020end2020Album, - AlbumStub.create2020end2022Album, - AlbumStub.create2020end2024Album, - AlbumStub.create2020end2026Album, - ], - false, - ); + final sorted = mostRecent.sortFn([ + AlbumStub.create2020end2020Album, + AlbumStub.create2020end2022Album, + AlbumStub.create2020end2024Album, + AlbumStub.create2020end2026Album, + ], false); final sortedList = [ AlbumStub.create2020end2026Album, AlbumStub.create2020end2024Album, @@ -167,15 +119,12 @@ void main() { }); test("Most Recent - ASC", () { - final sorted = mostRecent.sortFn( - [ - AlbumStub.create2020end2020Album, - AlbumStub.create2020end2022Album, - AlbumStub.create2020end2024Album, - AlbumStub.create2020end2026Album, - ], - true, - ); + final sorted = mostRecent.sortFn([ + AlbumStub.create2020end2020Album, + AlbumStub.create2020end2022Album, + AlbumStub.create2020end2024Album, + AlbumStub.create2020end2026Album, + ], true); final sortedList = [ AlbumStub.create2020end2020Album, AlbumStub.create2020end2022Album, @@ -191,23 +140,13 @@ void main() { test("Most Oldest - ASC", () { final sorted = mostOldest.sortFn(albums, false); - final sortedList = [ - AlbumStub.twoAsset, - AlbumStub.emptyAlbum, - AlbumStub.oneAsset, - AlbumStub.sharedWithUser, - ]; + final sortedList = [AlbumStub.twoAsset, AlbumStub.emptyAlbum, AlbumStub.oneAsset, AlbumStub.sharedWithUser]; expect(sorted, orderedEquals(sortedList)); }); test("Most Oldest - DESC", () { final sorted = mostOldest.sortFn(albums, true); - final sortedList = [ - AlbumStub.sharedWithUser, - AlbumStub.oneAsset, - AlbumStub.emptyAlbum, - AlbumStub.twoAsset, - ]; + final sortedList = [AlbumStub.sharedWithUser, AlbumStub.oneAsset, AlbumStub.emptyAlbum, AlbumStub.twoAsset]; expect(sorted, orderedEquals(sortedList)); }); }); @@ -221,67 +160,43 @@ void main() { setUp(() async { settingsMock = MockAppSettingsService(); container = TestUtils.createContainer( - overrides: [ - appSettingsServiceProvider.overrideWith((ref) => settingsMock), - ], + overrides: [appSettingsServiceProvider.overrideWith((ref) => settingsMock)], ); when( - () => settingsMock.setSetting( - AppSettingsEnum.selectedAlbumSortReverse, - any(), - ), + () => settingsMock.setSetting(AppSettingsEnum.selectedAlbumSortReverse, any()), ).thenAnswer((_) async => {}); when( - () => settingsMock.setSetting( - AppSettingsEnum.selectedAlbumSortOrder, - any(), - ), + () => settingsMock.setSetting(AppSettingsEnum.selectedAlbumSortOrder, any()), ).thenAnswer((_) async => {}); }); test('Returns the default sort mode when none set', () { // Returns the default value when nothing is set - when( - () => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortOrder), - ).thenReturn(0); + when(() => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortOrder)).thenReturn(0); expect(container.read(albumSortByOptionsProvider), AlbumSortMode.created); }); test('Returns the correct sort mode with index from Store', () { // Returns the default value when nothing is set - when( - () => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortOrder), - ).thenReturn(3); + when(() => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortOrder)).thenReturn(3); - expect( - container.read(albumSortByOptionsProvider), - AlbumSortMode.lastModified, - ); + expect(container.read(albumSortByOptionsProvider), AlbumSortMode.lastModified); }); test('Properly saves the correct store index of sort mode', () { container.read(albumSortByOptionsProvider.notifier).changeSortMode(AlbumSortMode.mostOldest); verify( - () => settingsMock.setSetting( - AppSettingsEnum.selectedAlbumSortOrder, - AlbumSortMode.mostOldest.storeIndex, - ), + () => settingsMock.setSetting(AppSettingsEnum.selectedAlbumSortOrder, AlbumSortMode.mostOldest.storeIndex), ); }); test('Notifies listeners on state change', () { - when( - () => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortOrder), - ).thenReturn(0); + when(() => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortOrder)).thenReturn(0); final listener = ListenerMock(); - container.listen( - albumSortByOptionsProvider, - listener.call, - fireImmediately: true, - ); + container.listen(albumSortByOptionsProvider, listener.call, fireImmediately: true); // Created -> Most Oldest container.read(albumSortByOptionsProvider.notifier).changeSortMode(AlbumSortMode.mostOldest); @@ -309,28 +224,18 @@ void main() { setUp(() async { settingsMock = MockAppSettingsService(); container = TestUtils.createContainer( - overrides: [ - appSettingsServiceProvider.overrideWith((ref) => settingsMock), - ], + overrides: [appSettingsServiceProvider.overrideWith((ref) => settingsMock)], ); when( - () => settingsMock.setSetting( - AppSettingsEnum.selectedAlbumSortReverse, - any(), - ), + () => settingsMock.setSetting(AppSettingsEnum.selectedAlbumSortReverse, any()), ).thenAnswer((_) async => {}); when( - () => settingsMock.setSetting( - AppSettingsEnum.selectedAlbumSortOrder, - any(), - ), + () => settingsMock.setSetting(AppSettingsEnum.selectedAlbumSortOrder, any()), ).thenAnswer((_) async => {}); }); test('Returns the default sort order when none set - false', () { - when( - () => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortReverse), - ).thenReturn(false); + when(() => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortReverse)).thenReturn(false); expect(container.read(albumSortOrderProvider), isFalse); }); @@ -338,25 +243,14 @@ void main() { test('Properly saves the correct order', () { container.read(albumSortOrderProvider.notifier).changeSortDirection(true); - verify( - () => settingsMock.setSetting( - AppSettingsEnum.selectedAlbumSortReverse, - true, - ), - ); + verify(() => settingsMock.setSetting(AppSettingsEnum.selectedAlbumSortReverse, true)); }); test('Notifies listeners on state change', () { - when( - () => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortReverse), - ).thenReturn(false); + when(() => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortReverse)).thenReturn(false); final listener = ListenerMock(); - container.listen( - albumSortOrderProvider, - listener.call, - fireImmediately: true, - ); + container.listen(albumSortOrderProvider, listener.call, fireImmediately: true); // false -> true container.read(albumSortOrderProvider.notifier).changeSortDirection(true); diff --git a/mobile/test/modules/extensions/asset_extensions_test.dart b/mobile/test/modules/extensions/asset_extensions_test.dart index a164b9ddce..2b9b740ca7 100644 --- a/mobile/test/modules/extensions/asset_extensions_test.dart +++ b/mobile/test/modules/extensions/asset_extensions_test.dart @@ -5,21 +5,11 @@ import 'package:immich_mobile/extensions/asset_extensions.dart'; import 'package:timezone/data/latest.dart'; import 'package:timezone/timezone.dart'; -ExifInfo makeExif({ - DateTime? dateTimeOriginal, - String? timeZone, -}) { - return ExifInfo( - dateTimeOriginal: dateTimeOriginal, - timeZone: timeZone, - ); +ExifInfo makeExif({DateTime? dateTimeOriginal, String? timeZone}) { + return ExifInfo(dateTimeOriginal: dateTimeOriginal, timeZone: timeZone); } -Asset makeAsset({ - required String id, - required DateTime createdAt, - ExifInfo? exifInfo, -}) { +Asset makeAsset({required String id, required DateTime createdAt, ExifInfo? exifInfo}) { return Asset( checksum: '', localId: id, @@ -79,10 +69,7 @@ void main() { test('Returns dateTimeOriginal in UTC from exifInfo with invalid timezone', () { final createdAt = DateTime.parse("2023-01-27T14:00:00-0500"); final dateTimeOriginal = DateTime.parse("2022-01-27T14:00:00+0530"); - final e = makeExif( - dateTimeOriginal: dateTimeOriginal, - timeZone: "#_#", - ); // Invalid timezone + final e = makeExif(dateTimeOriginal: dateTimeOriginal, timeZone: "#_#"); // Invalid timezone final a = makeAsset(id: '1', createdAt: createdAt, exifInfo: e); final (dt, tz) = a.getTZAdjustedTimeAndOffset(); diff --git a/mobile/test/modules/extensions/builtin_extensions_test.dart b/mobile/test/modules/extensions/builtin_extensions_test.dart index 2de450a952..e52362f3d3 100644 --- a/mobile/test/modules/extensions/builtin_extensions_test.dart +++ b/mobile/test/modules/extensions/builtin_extensions_test.dart @@ -5,10 +5,7 @@ import 'package:immich_mobile/extensions/string_extensions.dart'; void main() { group('Test toDuration', () { test('ok', () { - expect( - "1:02:33".toDuration(), - const Duration(hours: 1, minutes: 2, seconds: 33), - ); + expect("1:02:33".toDuration(), const Duration(hours: 1, minutes: 2, seconds: 33)); }); test('malformed', () { expect("".toDuration(), isNull); @@ -45,9 +42,7 @@ void main() { test('withKey', () { final a = ["a", "bb", "cc", "ddd"]; expect( - a.uniqueConsecutive( - compare: (s1, s2) => s1.length.compareTo(s2.length), - ), + a.uniqueConsecutive(compare: (s1, s2) => s1.length.compareTo(s2.length)), orderedEquals(["a", "bb", "ddd"]), ); }); diff --git a/mobile/test/modules/home/asset_grid_data_structure_test.dart b/mobile/test/modules/home/asset_grid_data_structure_test.dart index b4ee851969..3e1fe06c68 100644 --- a/mobile/test/modules/home/asset_grid_data_structure_test.dart +++ b/mobile/test/modules/home/asset_grid_data_structure_test.dart @@ -58,10 +58,7 @@ void main() { group('Test grouped', () { test('test grouped check months', () async { - final renderList = await RenderList.fromAssets( - assets, - GroupAssetsBy.day, - ); + final renderList = await RenderList.fromAssets(assets, GroupAssetsBy.day); // Oct // Day 1 @@ -75,33 +72,18 @@ void main() { // Day 1 // 5 Assets => 2 Rows expect(renderList.elements, hasLength(4)); - expect( - renderList.elements[0].type, - RenderAssetGridElementType.monthTitle, - ); + expect(renderList.elements[0].type, RenderAssetGridElementType.monthTitle); expect(renderList.elements[0].date.month, 1); - expect( - renderList.elements[1].type, - RenderAssetGridElementType.groupDividerTitle, - ); + expect(renderList.elements[1].type, RenderAssetGridElementType.groupDividerTitle); expect(renderList.elements[1].date.month, 1); - expect( - renderList.elements[2].type, - RenderAssetGridElementType.monthTitle, - ); + expect(renderList.elements[2].type, RenderAssetGridElementType.monthTitle); expect(renderList.elements[2].date.month, 2); - expect( - renderList.elements[3].type, - RenderAssetGridElementType.monthTitle, - ); + expect(renderList.elements[3].type, RenderAssetGridElementType.monthTitle); expect(renderList.elements[3].date.month, 10); }); test('test grouped check types', () async { - final renderList = await RenderList.fromAssets( - assets, - GroupAssetsBy.day, - ); + final renderList = await RenderList.fromAssets(assets, GroupAssetsBy.day); // Oct // Day 1 diff --git a/mobile/test/modules/map/map_theme_override_test.dart b/mobile/test/modules/map/map_theme_override_test.dart index 78fa6b0043..de16b7f24f 100644 --- a/mobile/test/modules/map/map_theme_override_test.dart +++ b/mobile/test/modules/map/map_theme_override_test.dart @@ -67,9 +67,7 @@ void main() { overrides: overrides, ); - mapStateNotifier.state = mapState.copyWith( - darkStyleFetched: const AsyncError("Error", StackTrace.empty), - ); + mapStateNotifier.state = mapState.copyWith(darkStyleFetched: const AsyncError("Error", StackTrace.empty)); await tester.pumpAndSettle(); expect(mapStyle?.hasError, isTrue); }); @@ -86,10 +84,7 @@ void main() { overrides: overrides, ); - mapStateNotifier.state = mapState.copyWith( - themeMode: ThemeMode.light, - lightStyleFetched: const AsyncData("light"), - ); + mapStateNotifier.state = mapState.copyWith(themeMode: ThemeMode.light, lightStyleFetched: const AsyncData("light")); await tester.pumpAndSettle(); expect(mapStyle?.valueOrNull, "light"); }); diff --git a/mobile/test/modules/shared/sync_service_test.dart b/mobile/test/modules/shared/sync_service_test.dart index 2858e6a9e7..b51a4d67fd 100644 --- a/mobile/test/modules/shared/sync_service_test.dart +++ b/mobile/test/modules/shared/sync_service_test.dart @@ -66,13 +66,7 @@ void main() { final MockPartnerRepository partnerRepository = MockPartnerRepository(); final MockUserService userService = MockUserService(); - final owner = UserDto( - id: "1", - updatedAt: DateTime.now(), - email: "a@b.c", - name: "first last", - isAdmin: false, - ); + final owner = UserDto(id: "1", updatedAt: DateTime.now(), email: "a@b.c", name: "first last", isAdmin: false); late SyncService s; setUpAll(() async { WidgetsFlutterBinding.ensureInitialized(); @@ -81,10 +75,7 @@ void main() { db.writeTxnSync(() => db.clearSync()); await StoreService.init(storeRepository: IsarStoreRepository(db)); await Store.put(StoreKey.currentUser, owner); - await LogService.init( - logRepository: IsarLogRepository(db), - storeRepository: IsarStoreRepository(db), - ); + await LogService.init(logRepository: IsarLogRepository(db), storeRepository: IsarStoreRepository(db)); }); final List initialAssets = [ makeAsset(checksum: "a", remoteId: "0-1"), @@ -119,22 +110,20 @@ void main() { when(() => userRepository.getAll(sortBy: SortUserBy.id)).thenAnswer((_) async => [owner]); when(() => userRepository.getAll()).thenAnswer((_) async => [owner]); when( - () => assetRepository.getAll( - ownerId: owner.id, - sortBy: AssetSort.checksum, - ), + () => assetRepository.getAll(ownerId: owner.id, sortBy: AssetSort.checksum), ).thenAnswer((_) async => initialAssets); - when(() => assetRepository.getAllByOwnerIdChecksum(any(), any())) - .thenAnswer((_) async => [initialAssets[3], null, null]); + when( + () => assetRepository.getAllByOwnerIdChecksum(any(), any()), + ).thenAnswer((_) async => [initialAssets[3], null, null]); when(() => assetRepository.updateAll(any())).thenAnswer((_) async => []); when(() => assetRepository.deleteByIds(any())).thenAnswer((_) async {}); when(() => exifInfoRepository.updateAll(any())).thenAnswer((_) async => []); - when(() => assetRepository.transaction(any())).thenAnswer( - (call) => (call.positionalArguments.first as Function).call(), - ); - when(() => assetRepository.transaction(any())).thenAnswer( - (call) => (call.positionalArguments.first as Function).call(), - ); + when( + () => assetRepository.transaction(any()), + ).thenAnswer((call) => (call.positionalArguments.first as Function).call()); + when( + () => assetRepository.transaction(any()), + ).thenAnswer((call) => (call.positionalArguments.first as Function).call()); when(() => userApiRepository.getAll()).thenAnswer((_) async => [owner]); registerFallbackValue(Direction.sharedByMe); when(() => partnerApiRepository.getAll(any())).thenAnswer((_) async => []); @@ -170,9 +159,7 @@ void main() { ); expect(c1, isTrue); final updatedAsset = initialAssets[3].updatedCopy(remoteAssets[3]); - verify( - () => assetRepository.updateAll([remoteAssets[4], remoteAssets[5], updatedAsset]), - ); + verify(() => assetRepository.updateAll([remoteAssets[4], remoteAssets[5], updatedAsset])); }); test('test syncing duplicate assets', () async { @@ -191,10 +178,7 @@ void main() { ); expect(c1, isTrue); when( - () => assetRepository.getAll( - ownerId: owner.id, - sortBy: AssetSort.checksum, - ), + () => assetRepository.getAll(ownerId: owner.id, sortBy: AssetSort.checksum), ).thenAnswer((_) async => remoteAssets); final bool c2 = await s.syncRemoteAssetsToDb( users: [owner], @@ -204,10 +188,7 @@ void main() { expect(c2, isFalse); final currentState = [...remoteAssets]; when( - () => assetRepository.getAll( - ownerId: owner.id, - sortBy: AssetSort.checksum, - ), + () => assetRepository.getAll(ownerId: owner.id, sortBy: AssetSort.checksum), ).thenAnswer((_) async => currentState); remoteAssets.removeAt(4); final bool c3 = await s.syncRemoteAssetsToDb( @@ -228,18 +209,19 @@ void main() { test('test efficient sync', () async { when( - () => assetRepository.deleteAllByRemoteId( - [initialAssets[1].remoteId!, initialAssets[2].remoteId!], - state: AssetState.remote, - ), + () => assetRepository.deleteAllByRemoteId([ + initialAssets[1].remoteId!, + initialAssets[2].remoteId!, + ], state: AssetState.remote), ).thenAnswer((_) async { return; }); when( () => assetRepository.getAllByRemoteId(["2-1", "1-1"], state: AssetState.merged), ).thenAnswer((_) async => [initialAssets[2]]); - when(() => assetRepository.getAllByOwnerIdChecksum(any(), any())) - .thenAnswer((_) async => [initialAssets[0], null, null]); //afg + when( + () => assetRepository.getAllByOwnerIdChecksum(any(), any()), + ).thenAnswer((_) async => [initialAssets[0], null, null]); //afg final List toUpsert = [ makeAsset(checksum: "a", remoteId: "0-1"), // changed makeAsset(checksum: "f", remoteId: "0-2"), // new @@ -262,18 +244,11 @@ void main() { test('test upsert with EXIF data', () async { final assets = [AssetStub.image1, AssetStub.image2]; - expect( - assets.map((a) => a.exifInfo?.assetId), - List.filled(assets.length, null), - ); + expect(assets.map((a) => a.exifInfo?.assetId), List.filled(assets.length, null)); await s.upsertAssetsWithExif(assets); verify( () => exifInfoRepository.updateAll( - any( - that: containsAll( - assets.map((a) => a.exifInfo!.copyWith(assetId: a.id)), - ), - ), + any(that: containsAll(assets.map((a) => a.exifInfo!.copyWith(assetId: a.id)))), ), ); expect(assets.map((a) => a.exifInfo?.assetId), assets.map((a) => a.id)); @@ -282,8 +257,4 @@ void main() { }); } -Future<(List?, List?)> _failDiff( - List user, - DateTime time, -) => - Future.value((null, null)); +Future<(List?, List?)> _failDiff(List user, DateTime time) => Future.value((null, null)); diff --git a/mobile/test/modules/utils/async_mutex_test.dart b/mobile/test/modules/utils/async_mutex_test.dart index c1b39035b4..d50567721b 100644 --- a/mobile/test/modules/utils/async_mutex_test.dart +++ b/mobile/test/modules/utils/async_mutex_test.dart @@ -7,33 +7,13 @@ void main() { AsyncMutex lock = AsyncMutex(); List events = []; expect(0, lock.enqueued); - lock.run( - () => Future.delayed( - const Duration(milliseconds: 10), - () => events.add(1), - ), - ); + lock.run(() => Future.delayed(const Duration(milliseconds: 10), () => events.add(1))); expect(1, lock.enqueued); - lock.run( - () => Future.delayed( - const Duration(milliseconds: 3), - () => events.add(2), - ), - ); + lock.run(() => Future.delayed(const Duration(milliseconds: 3), () => events.add(2))); expect(2, lock.enqueued); - lock.run( - () => Future.delayed( - const Duration(milliseconds: 1), - () => events.add(3), - ), - ); + lock.run(() => Future.delayed(const Duration(milliseconds: 1), () => events.add(3))); expect(3, lock.enqueued); - await lock.run( - () => Future.delayed( - const Duration(milliseconds: 10), - () => events.add(4), - ), - ); + await lock.run(() => Future.delayed(const Duration(milliseconds: 10), () => events.add(4))); expect(0, lock.enqueued); expect(events, [1, 2, 3, 4]); }); diff --git a/mobile/test/modules/utils/thumbnail_utils_test.dart b/mobile/test/modules/utils/thumbnail_utils_test.dart index 6fa0127f16..dd4588fc80 100644 --- a/mobile/test/modules/utils/thumbnail_utils_test.dart +++ b/mobile/test/modules/utils/thumbnail_utils_test.dart @@ -9,22 +9,12 @@ void main() { final dateTimeString = DateFormat.yMMMMd().format(dateTime); test('returns description if it has one', () { - final result = getAltText( - const ExifInfo(description: 'description'), - dateTime, - AssetType.image, - [], - ); + final result = getAltText(const ExifInfo(description: 'description'), dateTime, AssetType.image, []); expect(result, 'description'); }); test('returns image alt text with date if no location', () { - final (template, args) = getAltTextTemplate( - const ExifInfo(), - dateTime, - AssetType.image, - [], - ); + final (template, args) = getAltTextTemplate(const ExifInfo(), dateTime, AssetType.image, []); expect(template, "image_alt_text_date"); expect(args["isVideo"], "false"); expect(args["date"], dateTimeString); @@ -45,12 +35,7 @@ void main() { }); test('returns image alt text with date and some people', () { - final (template, args) = getAltTextTemplate( - const ExifInfo(), - dateTime, - AssetType.image, - ["Alice", "Bob"], - ); + final (template, args) = getAltTextTemplate(const ExifInfo(), dateTime, AssetType.image, ["Alice", "Bob"]); expect(template, "image_alt_text_date_2_people"); expect(args["isVideo"], "false"); expect(args["date"], dateTimeString); diff --git a/mobile/test/modules/utils/version_compatibility_test.dart b/mobile/test/modules/utils/version_compatibility_test.dart index bdcc0c8fce..82f89237c0 100644 --- a/mobile/test/modules/utils/version_compatibility_test.dart +++ b/mobile/test/modules/utils/version_compatibility_test.dart @@ -6,10 +6,7 @@ void main() { String? result; result = getVersionCompatibilityMessage(1, 0, 2, 0); - expect( - result, - 'Your app major version is not compatible with the server!', - ); + expect(result, 'Your app major version is not compatible with the server!'); result = getVersionCompatibilityMessage(1, 106, 1, 105); expect( diff --git a/mobile/test/pages/search/search.page_test.dart b/mobile/test/pages/search/search.page_test.dart index 1c1a410150..9592623a28 100644 --- a/mobile/test/pages/search/search.page_test.dart +++ b/mobile/test/pages/search/search.page_test.dart @@ -45,18 +45,11 @@ void main() { final emptyTextSearch = isA().having((s) => s.originalFileName, 'originalFileName', null); testWidgets('contextual search with/without text', (tester) async { - await tester.pumpConsumerWidget( - const SearchPage(), - overrides: overrides, - ); + await tester.pumpConsumerWidget(const SearchPage(), overrides: overrides); await tester.pumpAndSettle(); - expect( - find.byIcon(Icons.abc_rounded), - findsOneWidget, - reason: 'Should have contextual search icon', - ); + expect(find.byIcon(Icons.abc_rounded), findsOneWidget, reason: 'Should have contextual search icon'); final searchField = find.byKey(const Key('search_text_field')); expect(searchField, findsOneWidget); @@ -64,14 +57,9 @@ void main() { await tester.enterText(searchField, 'test'); await tester.testTextInput.receiveAction(TextInputAction.search); - var captured = verify( - () => mockSearchApi.searchSmart(captureAny()), - ).captured; + var captured = verify(() => mockSearchApi.searchSmart(captureAny())).captured; - expect( - captured.first, - isA().having((s) => s.query, 'query', 'test'), - ); + expect(captured.first, isA().having((s) => s.query, 'query', 'test')); await tester.enterText(searchField, ''); await tester.testTextInput.receiveAction(TextInputAction.search); @@ -81,10 +69,7 @@ void main() { }); testWidgets('not contextual search with/without text', (tester) async { - await tester.pumpConsumerWidget( - const SearchPage(), - overrides: overrides, - ); + await tester.pumpConsumerWidget(const SearchPage(), overrides: overrides); await tester.pumpAndSettle(); @@ -92,11 +77,7 @@ void main() { await tester.pumpAndSettle(); - expect( - find.byIcon(Icons.image_search_rounded), - findsOneWidget, - reason: 'Should not have contextual search icon', - ); + expect(find.byIcon(Icons.image_search_rounded), findsOneWidget, reason: 'Should not have contextual search icon'); final searchField = find.byKey(const Key('search_text_field')); expect(searchField, findsOneWidget); @@ -104,14 +85,9 @@ void main() { await tester.enterText(searchField, 'test'); await tester.testTextInput.receiveAction(TextInputAction.search); - var captured = verify( - () => mockSearchApi.searchAssets(captureAny()), - ).captured; + var captured = verify(() => mockSearchApi.searchAssets(captureAny())).captured; - expect( - captured.first, - isA().having((s) => s.originalFileName, 'originalFileName', 'test'), - ); + expect(captured.first, isA().having((s) => s.originalFileName, 'originalFileName', 'test')); await tester.enterText(searchField, ''); await tester.testTextInput.receiveAction(TextInputAction.search); diff --git a/mobile/test/services/album.service_test.dart b/mobile/test/services/album.service_test.dart index 547b049593..97683cdab1 100644 --- a/mobile/test/services/album.service_test.dart +++ b/mobile/test/services/album.service_test.dart @@ -33,12 +33,12 @@ void main() { when(() => userService.getMyUser()).thenReturn(UserStub.user1); - when(() => albumRepository.transaction(any())).thenAnswer( - (call) => (call.positionalArguments.first as Function).call(), - ); - when(() => assetRepository.transaction(any())).thenAnswer( - (call) => (call.positionalArguments.first as Function).call(), - ); + when( + () => albumRepository.transaction(any()), + ).thenAnswer((call) => (call.positionalArguments.first as Function).call()); + when( + () => assetRepository.transaction(any()), + ).thenAnswer((call) => (call.positionalArguments.first as Function).call()); sut = AlbumService( syncService, @@ -66,15 +66,14 @@ void main() { test('one selected albums, two on device', () async { when(() => backupRepository.getIdsBySelection(BackupSelection.exclude)).thenAnswer((_) async => []); - when(() => backupRepository.getIdsBySelection(BackupSelection.select)) - .thenAnswer((_) async => [AlbumStub.oneAsset.localId!]); + when( + () => backupRepository.getIdsBySelection(BackupSelection.select), + ).thenAnswer((_) async => [AlbumStub.oneAsset.localId!]); when(() => albumMediaRepository.getAll()).thenAnswer((_) async => [AlbumStub.oneAsset, AlbumStub.twoAsset]); when(() => syncService.syncLocalAlbumAssetsToDb(any(), any())).thenAnswer((_) async => true); final result = await sut.refreshDeviceAlbums(); expect(result, true); - verify( - () => syncService.syncLocalAlbumAssetsToDb([AlbumStub.oneAsset], null), - ).called(1); + verify(() => syncService.syncLocalAlbumAssetsToDb([AlbumStub.oneAsset], null)).called(1); verifyNoMoreInteractions(syncService); }); }); @@ -85,15 +84,12 @@ void main() { when(() => syncService.syncUsersFromServer(any())).thenAnswer((_) async => true); when(() => albumApiRepository.getAll(shared: true)).thenAnswer((_) async => [AlbumStub.sharedWithUser]); - when(() => albumApiRepository.getAll(shared: null)) - .thenAnswer((_) async => [AlbumStub.oneAsset, AlbumStub.twoAsset]); + when( + () => albumApiRepository.getAll(shared: null), + ).thenAnswer((_) async => [AlbumStub.oneAsset, AlbumStub.twoAsset]); when( - () => syncService.syncRemoteAlbumsToDb([ - AlbumStub.twoAsset, - AlbumStub.oneAsset, - AlbumStub.sharedWithUser, - ]), + () => syncService.syncRemoteAlbumsToDb([AlbumStub.twoAsset, AlbumStub.oneAsset, AlbumStub.sharedWithUser]), ).thenAnswer((_) async => true); final result = await sut.refreshRemoteAlbums(); expect(result, true); @@ -102,13 +98,7 @@ void main() { verify(() => albumApiRepository.getAll(shared: true)).called(1); verify(() => albumApiRepository.getAll(shared: null)).called(1); verify( - () => syncService.syncRemoteAlbumsToDb( - [ - AlbumStub.twoAsset, - AlbumStub.oneAsset, - AlbumStub.sharedWithUser, - ], - ), + () => syncService.syncRemoteAlbumsToDb([AlbumStub.twoAsset, AlbumStub.oneAsset, AlbumStub.sharedWithUser]), ).called(1); verifyNoMoreInteractions(userService); verifyNoMoreInteractions(albumApiRepository); @@ -130,9 +120,7 @@ void main() { () => entityService.fillAlbumWithDatabaseEntities(AlbumStub.oneAsset), ).thenAnswer((_) async => AlbumStub.oneAsset); - when( - () => albumRepository.create(AlbumStub.oneAsset), - ).thenAnswer((_) async => AlbumStub.twoAsset); + when(() => albumRepository.create(AlbumStub.oneAsset)).thenAnswer((_) async => AlbumStub.twoAsset); final result = await sut.createAlbum("name", [AssetStub.image1], [UserStub.user1]); expect(result, AlbumStub.twoAsset); @@ -143,9 +131,7 @@ void main() { sharedUserIds: [UserStub.user1.id], ), ).called(1); - verify( - () => entityService.fillAlbumWithDatabaseEntities(AlbumStub.oneAsset), - ).called(1); + verify(() => entityService.fillAlbumWithDatabaseEntities(AlbumStub.oneAsset)).called(1); }); }); @@ -153,29 +139,14 @@ void main() { test('one added, one duplicate', () async { when( () => albumApiRepository.addAssets(AlbumStub.oneAsset.remoteId!, any()), - ).thenAnswer( - (_) async => (added: [AssetStub.image2.remoteId!], duplicates: [AssetStub.image1.remoteId!]), - ); - when( - () => albumRepository.get(AlbumStub.oneAsset.id), - ).thenAnswer((_) async => AlbumStub.oneAsset); - when( - () => albumRepository.addAssets(AlbumStub.oneAsset, [AssetStub.image2]), - ).thenAnswer((_) async {}); - when( - () => albumRepository.removeAssets(AlbumStub.oneAsset, []), - ).thenAnswer((_) async {}); - when( - () => albumRepository.recalculateMetadata(AlbumStub.oneAsset), - ).thenAnswer((_) async => AlbumStub.oneAsset); - when( - () => albumRepository.update(AlbumStub.oneAsset), - ).thenAnswer((_) async => AlbumStub.oneAsset); + ).thenAnswer((_) async => (added: [AssetStub.image2.remoteId!], duplicates: [AssetStub.image1.remoteId!])); + when(() => albumRepository.get(AlbumStub.oneAsset.id)).thenAnswer((_) async => AlbumStub.oneAsset); + when(() => albumRepository.addAssets(AlbumStub.oneAsset, [AssetStub.image2])).thenAnswer((_) async {}); + when(() => albumRepository.removeAssets(AlbumStub.oneAsset, [])).thenAnswer((_) async {}); + when(() => albumRepository.recalculateMetadata(AlbumStub.oneAsset)).thenAnswer((_) async => AlbumStub.oneAsset); + when(() => albumRepository.update(AlbumStub.oneAsset)).thenAnswer((_) async => AlbumStub.oneAsset); - final result = await sut.addAssets( - AlbumStub.oneAsset, - [AssetStub.image1, AssetStub.image2], - ); + final result = await sut.addAssets(AlbumStub.oneAsset, [AssetStub.image1, AssetStub.image2]); expect(result != null, true); expect(result!.alreadyInAlbum, [AssetStub.image1.remoteId!]); @@ -187,9 +158,7 @@ void main() { test('one added', () async { when( () => albumApiRepository.addUsers(AlbumStub.emptyAlbum.remoteId!, any()), - ).thenAnswer( - (_) async => AlbumStub.sharedWithUser, - ); + ).thenAnswer((_) async => AlbumStub.sharedWithUser); when( () => albumRepository.addUsers( @@ -198,14 +167,9 @@ void main() { ), ).thenAnswer((_) async => AlbumStub.emptyAlbum); - when( - () => albumRepository.update(AlbumStub.emptyAlbum), - ).thenAnswer((_) async => AlbumStub.emptyAlbum); + when(() => albumRepository.update(AlbumStub.emptyAlbum)).thenAnswer((_) async => AlbumStub.emptyAlbum); - final result = await sut.addUsers( - AlbumStub.emptyAlbum, - [UserStub.user2.id], - ); + final result = await sut.addUsers(AlbumStub.emptyAlbum, [UserStub.user2.id]); expect(result, true); }); diff --git a/mobile/test/services/asset.service_test.dart b/mobile/test/services/asset.service_test.dart index 5077764f26..b741150165 100644 --- a/mobile/test/services/asset.service_test.dart +++ b/mobile/test/services/asset.service_test.dart @@ -81,9 +81,7 @@ void main() { final upsertExifCallback = verify(() => syncService.upsertAssetsWithExif(captureAny())); upsertExifCallback.called(1); final receivedAssets = upsertExifCallback.captured.firstOrNull as List? ?? []; - final receivedDatetime = receivedAssets.cast().map( - (a) => a.exifInfo?.dateTimeOriginal ?? DateTime(0), - ); + final receivedDatetime = receivedAssets.cast().map((a) => a.exifInfo?.dateTimeOriginal ?? DateTime(0)); expect(receivedDatetime.every((d) => d == dateTime), isTrue); }); @@ -97,8 +95,8 @@ void main() { upsertExifCallback.called(1); final receivedAssets = upsertExifCallback.captured.firstOrNull as List? ?? []; final receivedCoords = receivedAssets.cast().map( - (a) => LatLng(a.exifInfo?.latitude ?? 0, a.exifInfo?.longitude ?? 0), - ); + (a) => LatLng(a.exifInfo?.latitude ?? 0, a.exifInfo?.longitude ?? 0), + ); expect(receivedCoords.every((l) => l == latLng), isTrue); }); }); diff --git a/mobile/test/services/auth.service_test.dart b/mobile/test/services/auth.service_test.dart index c9b44fe28b..1bad780ca7 100644 --- a/mobile/test/services/auth.service_test.dart +++ b/mobile/test/services/auth.service_test.dart @@ -95,10 +95,7 @@ void main() { when(() => apiService.resolveAndSetEndpoint(testUrl)).thenThrow(Exception('Invalid URL')); - expect( - () async => await sut.validateServerUrl(testUrl), - throwsA(isA()), - ); + expect(() async => await sut.validateServerUrl(testUrl), throwsA(isA())); verify(() => apiService.resolveAndSetEndpoint(testUrl)).called(1); verifyNever(() => apiService.setDeviceInfoHeader()); @@ -109,10 +106,7 @@ void main() { when(() => apiService.resolveAndSetEndpoint(testUrl)).thenThrow(Exception('Server is not reachable')); - expect( - () async => await sut.validateServerUrl(testUrl), - throwsA(isA()), - ); + expect(() async => await sut.validateServerUrl(testUrl), throwsA(isA())); verify(() => apiService.resolveAndSetEndpoint(testUrl)).called(1); verifyNever(() => apiService.setDeviceInfoHeader()); @@ -126,10 +120,7 @@ void main() { when(() => authRepository.clearLocalData()).thenAnswer((_) => Future.value(null)); when(() => uploadService.cancelBackup()).thenAnswer((_) => Future.value(1)); when( - () => appSettingsService.setSetting( - AppSettingsEnum.enableBackup, - false, - ), + () => appSettingsService.setSetting(AppSettingsEnum.enableBackup, false), ).thenAnswer((_) => Future.value(null)); await sut.logout(); @@ -144,10 +135,7 @@ void main() { when(() => authRepository.clearLocalData()).thenAnswer((_) => Future.value(null)); when(() => uploadService.cancelBackup()).thenAnswer((_) => Future.value(1)); when( - () => appSettingsService.setSetting( - AppSettingsEnum.enableBackup, - false, - ), + () => appSettingsService.setSetting(AppSettingsEnum.enableBackup, false), ).thenAnswer((_) => Future.value(null)); await sut.logout(); @@ -176,8 +164,9 @@ void main() { when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true); when(() => authRepository.getPreferredWifiName()).thenReturn('TestWifi'); when(() => authRepository.getLocalEndpoint()).thenReturn('http://local.endpoint'); - when(() => apiService.resolveAndSetEndpoint('http://local.endpoint')) - .thenAnswer((_) async => 'http://local.endpoint'); + when( + () => apiService.resolveAndSetEndpoint('http://local.endpoint'), + ).thenAnswer((_) async => 'http://local.endpoint'); final result = await sut.setOpenApiServiceEndpoint(); @@ -192,12 +181,9 @@ void main() { test('Should set external endpoint if wifi name not matching', () async { when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true); when(() => authRepository.getPreferredWifiName()).thenReturn('DifferentWifi'); - when(() => authRepository.getExternalEndpointList()).thenReturn([ - const AuxilaryEndpoint( - url: 'https://external.endpoint', - status: AuxCheckStatus.valid, - ), - ]); + when( + () => authRepository.getExternalEndpointList(), + ).thenReturn([const AuxilaryEndpoint(url: 'https://external.endpoint', status: AuxCheckStatus.valid)]); when( () => apiService.resolveAndSetEndpoint('https://external.endpoint'), ).thenAnswer((_) async => 'https://external.endpoint/api'); @@ -209,23 +195,15 @@ void main() { verify(() => networkService.getWifiName()).called(1); verify(() => authRepository.getPreferredWifiName()).called(1); verify(() => authRepository.getExternalEndpointList()).called(1); - verify( - () => apiService.resolveAndSetEndpoint('https://external.endpoint'), - ).called(1); + verify(() => apiService.resolveAndSetEndpoint('https://external.endpoint')).called(1); }); test('Should set second external endpoint if the first throw any error', () async { when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true); when(() => authRepository.getPreferredWifiName()).thenReturn('DifferentWifi'); when(() => authRepository.getExternalEndpointList()).thenReturn([ - const AuxilaryEndpoint( - url: 'https://external.endpoint', - status: AuxCheckStatus.valid, - ), - const AuxilaryEndpoint( - url: 'https://external.endpoint2', - status: AuxCheckStatus.valid, - ), + const AuxilaryEndpoint(url: 'https://external.endpoint', status: AuxCheckStatus.valid), + const AuxilaryEndpoint(url: 'https://external.endpoint2', status: AuxCheckStatus.valid), ]); when( @@ -242,23 +220,15 @@ void main() { verify(() => networkService.getWifiName()).called(1); verify(() => authRepository.getPreferredWifiName()).called(1); verify(() => authRepository.getExternalEndpointList()).called(1); - verify( - () => apiService.resolveAndSetEndpoint('https://external.endpoint2'), - ).called(1); + verify(() => apiService.resolveAndSetEndpoint('https://external.endpoint2')).called(1); }); test('Should set second external endpoint if the first throw ApiException', () async { when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true); when(() => authRepository.getPreferredWifiName()).thenReturn('DifferentWifi'); when(() => authRepository.getExternalEndpointList()).thenReturn([ - const AuxilaryEndpoint( - url: 'https://external.endpoint', - status: AuxCheckStatus.valid, - ), - const AuxilaryEndpoint( - url: 'https://external.endpoint2', - status: AuxCheckStatus.valid, - ), + const AuxilaryEndpoint(url: 'https://external.endpoint', status: AuxCheckStatus.valid), + const AuxilaryEndpoint(url: 'https://external.endpoint2', status: AuxCheckStatus.valid), ]); when( @@ -275,17 +245,16 @@ void main() { verify(() => networkService.getWifiName()).called(1); verify(() => authRepository.getPreferredWifiName()).called(1); verify(() => authRepository.getExternalEndpointList()).called(1); - verify( - () => apiService.resolveAndSetEndpoint('https://external.endpoint2'), - ).called(1); + verify(() => apiService.resolveAndSetEndpoint('https://external.endpoint2')).called(1); }); test('Should handle error when setting local connection', () async { when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true); when(() => authRepository.getPreferredWifiName()).thenReturn('TestWifi'); when(() => authRepository.getLocalEndpoint()).thenReturn('http://local.endpoint'); - when(() => apiService.resolveAndSetEndpoint('http://local.endpoint')) - .thenThrow(Exception('Local endpoint error')); + when( + () => apiService.resolveAndSetEndpoint('http://local.endpoint'), + ).thenThrow(Exception('Local endpoint error')); final result = await sut.setOpenApiServiceEndpoint(); @@ -300,12 +269,9 @@ void main() { test('Should handle error when setting external connection', () async { when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true); when(() => authRepository.getPreferredWifiName()).thenReturn('DifferentWifi'); - when(() => authRepository.getExternalEndpointList()).thenReturn([ - const AuxilaryEndpoint( - url: 'https://external.endpoint', - status: AuxCheckStatus.valid, - ), - ]); + when( + () => authRepository.getExternalEndpointList(), + ).thenReturn([const AuxilaryEndpoint(url: 'https://external.endpoint', status: AuxCheckStatus.valid)]); when( () => apiService.resolveAndSetEndpoint('https://external.endpoint'), ).thenThrow(Exception('External endpoint error')); @@ -317,9 +283,7 @@ void main() { verify(() => networkService.getWifiName()).called(1); verify(() => authRepository.getPreferredWifiName()).called(1); verify(() => authRepository.getExternalEndpointList()).called(1); - verify( - () => apiService.resolveAndSetEndpoint('https://external.endpoint'), - ).called(1); + verify(() => apiService.resolveAndSetEndpoint('https://external.endpoint')).called(1); }); }); } diff --git a/mobile/test/services/entity.service_test.dart b/mobile/test/services/entity.service_test.dart index 7aab7f9428..64b9fc604b 100644 --- a/mobile/test/services/entity.service_test.dart +++ b/mobile/test/services/entity.service_test.dart @@ -22,23 +22,22 @@ void main() { group('fillAlbumWithDatabaseEntities', () { test('remote album with owner, thumbnail, sharedUsers and assets', () async { - final Album album = Album( - name: "album-with-two-assets-and-two-users", - localId: "album-with-two-assets-and-two-users-local", - remoteId: "album-with-two-assets-and-two-users-remote", - createdAt: DateTime(2001), - modifiedAt: DateTime(2010), - shared: true, - activityEnabled: true, - startDate: DateTime(2019), - endDate: DateTime(2020), - ) - ..remoteThumbnailAssetId = AssetStub.image1.remoteId - ..assets.addAll([AssetStub.image1, AssetStub.image1]) - ..owner.value = User.fromDto(UserStub.user1) - ..sharedUsers.addAll( - [User.fromDto(UserStub.admin), User.fromDto(UserStub.admin)], - ); + final Album album = + Album( + name: "album-with-two-assets-and-two-users", + localId: "album-with-two-assets-and-two-users-local", + remoteId: "album-with-two-assets-and-two-users-remote", + createdAt: DateTime(2001), + modifiedAt: DateTime(2010), + shared: true, + activityEnabled: true, + startDate: DateTime(2019), + endDate: DateTime(2020), + ) + ..remoteThumbnailAssetId = AssetStub.image1.remoteId + ..assets.addAll([AssetStub.image1, AssetStub.image1]) + ..owner.value = User.fromDto(UserStub.user1) + ..sharedUsers.addAll([User.fromDto(UserStub.admin), User.fromDto(UserStub.admin)]); when(() => userRepository.getByUserId(any())).thenAnswer((_) async => UserStub.admin); when(() => userRepository.getByUserId(any())).thenAnswer((_) async => UserStub.admin); @@ -52,23 +51,20 @@ void main() { await sut.fillAlbumWithDatabaseEntities(album); expect(album.owner.value?.toDto(), UserStub.admin); expect(album.thumbnail.value, AssetStub.image1); - expect( - album.remoteUsers.map((u) => u.toDto()).toSet(), - {UserStub.user1, UserStub.user2}, - ); + expect(album.remoteUsers.map((u) => u.toDto()).toSet(), {UserStub.user1, UserStub.user2}); expect(album.remoteAssets.toSet(), {AssetStub.image1, AssetStub.image2}); }); test('remote album without any info', () async { makeEmptyAlbum() => Album( - name: "album-without-info", - localId: "album-without-info-local", - remoteId: "album-without-info-remote", - createdAt: DateTime(2001), - modifiedAt: DateTime(2010), - shared: false, - activityEnabled: false, - ); + name: "album-without-info", + localId: "album-without-info-local", + remoteId: "album-without-info-remote", + createdAt: DateTime(2001), + modifiedAt: DateTime(2010), + shared: false, + activityEnabled: false, + ); final album = makeEmptyAlbum(); await sut.fillAlbumWithDatabaseEntities(album); diff --git a/mobile/test/services/hash_service_test.dart b/mobile/test/services/hash_service_test.dart index 5360e30341..74b8575e40 100644 --- a/mobile/test/services/hash_service_test.dart +++ b/mobile/test/services/hash_service_test.dart @@ -31,15 +31,10 @@ void main() { mockBackgroundService = MockBackgroundService(); mockDeviceAssetRepository = MockDeviceAssetRepository(); - sut = HashService( - deviceAssetRepository: mockDeviceAssetRepository, - backgroundService: mockBackgroundService, - ); + sut = HashService(deviceAssetRepository: mockDeviceAssetRepository, backgroundService: mockBackgroundService); when(() => mockDeviceAssetRepository.transaction(any())).thenAnswer((_) async { - final capturedCallback = verify( - () => mockDeviceAssetRepository.transaction(captureAny()), - ).captured; + final capturedCallback = verify(() => mockDeviceAssetRepository.transaction(captureAny())).captured; // Invoke the transaction callback await (capturedCallback.firstOrNull as Future Function()?)?.call(); }); @@ -53,17 +48,13 @@ void main() { when(() => mockBackgroundService.digestFiles([file.path])).thenAnswer((_) async => [hash]); // No DB entries for this asset - when( - () => mockDeviceAssetRepository.getByIds([AssetStub.image1.localId!]), - ).thenAnswer((_) async => []); + when(() => mockDeviceAssetRepository.getByIds([AssetStub.image1.localId!])).thenAnswer((_) async => []); final result = await sut.hashAssets([mockAsset]); // Verify we stored the new hash in DB when(() => mockDeviceAssetRepository.transaction(any())).thenAnswer((_) async { - final capturedCallback = verify( - () => mockDeviceAssetRepository.transaction(captureAny()), - ).captured; + final capturedCallback = verify(() => mockDeviceAssetRepository.transaction(captureAny())).captured; // Invoke the transaction callback await (capturedCallback.firstOrNull as Future Function()?)?.call(); verify( @@ -73,10 +64,7 @@ void main() { ).called(1); verify(() => mockDeviceAssetRepository.deleteIds([])).called(1); }); - expect( - result, - [AssetStub.image1.copyWith(checksum: base64.encode(hash))], - ); + expect(result, [AssetStub.image1.copyWith(checksum: base64.encode(hash))]); }); }); @@ -84,15 +72,9 @@ void main() { test("when the asset is not modified", () async { final hash = utf8.encode("image1-hash"); - when( - () => mockDeviceAssetRepository.getByIds([AssetStub.image1.localId!]), - ).thenAnswer( + when(() => mockDeviceAssetRepository.getByIds([AssetStub.image1.localId!])).thenAnswer( (_) async => [ - DeviceAsset( - assetId: AssetStub.image1.localId!, - hash: hash, - modifiedTime: AssetStub.image1.fileModifiedAt, - ), + DeviceAsset(assetId: AssetStub.image1.localId!, hash: hash, modifiedTime: AssetStub.image1.fileModifiedAt), ], ); final result = await sut.hashAssets([AssetStub.image1]); @@ -102,9 +84,7 @@ void main() { verifyNever(() => mockDeviceAssetRepository.updateAll(any())); verifyNever(() => mockDeviceAssetRepository.deleteIds(any())); - expect(result, [ - AssetStub.image1.copyWith(checksum: base64.encode(hash)), - ]); + expect(result, [AssetStub.image1.copyWith(checksum: base64.encode(hash))]); }); test("hashed successful when asset is modified", () async { @@ -118,9 +98,7 @@ void main() { final result = await sut.hashAssets([mockAsset]); when(() => mockDeviceAssetRepository.transaction(any())).thenAnswer((_) async { - final capturedCallback = verify( - () => mockDeviceAssetRepository.transaction(captureAny()), - ).captured; + final capturedCallback = verify(() => mockDeviceAssetRepository.transaction(captureAny())).captured; // Invoke the transaction callback await (capturedCallback.firstOrNull as Future Function()?)?.call(); verify( @@ -133,9 +111,7 @@ void main() { verify(() => mockBackgroundService.digestFiles([file.path])).called(1); - expect(result, [ - AssetStub.image1.copyWith(checksum: base64.encode(hash)), - ]); + expect(result, [AssetStub.image1.copyWith(checksum: base64.encode(hash))]); }); }); @@ -161,18 +137,14 @@ void main() { verifyNever(() => mockBackgroundService.digestFiles(any())); verifyNever(() => mockBackgroundService.digestFile(any())); verifyNever(() => mockDeviceAssetRepository.updateAll(any())); - verify( - () => mockDeviceAssetRepository.deleteIds([AssetStub.image1.localId!]), - ).called(1); + verify(() => mockDeviceAssetRepository.deleteIds([AssetStub.image1.localId!])).called(1); expect(result, isEmpty); }); test("cleanups DeviceAsset when hashing failed", () async { when(() => mockDeviceAssetRepository.transaction(any())).thenAnswer((_) async { - final capturedCallback = verify( - () => mockDeviceAssetRepository.transaction(captureAny()), - ).captured; + final capturedCallback = verify(() => mockDeviceAssetRepository.transaction(captureAny())).captured; // Invoke the transaction callback await (capturedCallback.firstOrNull as Future Function()?)?.call(); @@ -194,9 +166,7 @@ void main() { // To avoid this, we capture the callback and execute it within the transaction stub itself // and verify the results inside the transaction stub verify(() => mockDeviceAssetRepository.updateAll([])).called(1); - verify( - () => mockDeviceAssetRepository.deleteIds([AssetStub.image1.localId!]), - ).called(1); + verify(() => mockDeviceAssetRepository.deleteIds([AssetStub.image1.localId!])).called(1); }); when(() => mockBackgroundService.digestFiles([file.path])).thenAnswer( @@ -243,14 +213,11 @@ void main() { verify(() => mockBackgroundService.digestFiles([file1.path, file2.path])).called(1); verify(() => mockBackgroundService.digestFiles([file3.path])).called(1); - expect( - result, - [ - AssetStub.image1.copyWith(checksum: base64.encode(hash1)), - AssetStub.image2.copyWith(checksum: base64.encode(hash2)), - AssetStub.image3.copyWith(checksum: base64.encode(hash3)), - ], - ); + expect(result, [ + AssetStub.image1.copyWith(checksum: base64.encode(hash1)), + AssetStub.image2.copyWith(checksum: base64.encode(hash2)), + AssetStub.image3.copyWith(checksum: base64.encode(hash3)), + ]); }); test("processes assets in batches when file limit is reached", () async { @@ -283,14 +250,11 @@ void main() { verify(() => mockBackgroundService.digestFiles([file2.path])).called(1); verify(() => mockBackgroundService.digestFiles([file3.path])).called(1); - expect( - result, - [ - AssetStub.image1.copyWith(checksum: base64.encode(hash1)), - AssetStub.image2.copyWith(checksum: base64.encode(hash2)), - AssetStub.image3.copyWith(checksum: base64.encode(hash3)), - ], - ); + expect(result, [ + AssetStub.image1.copyWith(checksum: base64.encode(hash1)), + AssetStub.image2.copyWith(checksum: base64.encode(hash2)), + AssetStub.image3.copyWith(checksum: base64.encode(hash3)), + ]); }); test("HashService: Sort & Process different states", () async { @@ -345,15 +309,10 @@ void main() { test("handles all file access failures", () async { // No DB entries when( - () => mockDeviceAssetRepository.getByIds( - [AssetStub.image1.localId!, AssetStub.image2.localId!], - ), + () => mockDeviceAssetRepository.getByIds([AssetStub.image1.localId!, AssetStub.image2.localId!]), ).thenAnswer((_) async => []); - final result = await sut.hashAssets([ - AssetStub.image1, - AssetStub.image2, - ]); + final result = await sut.hashAssets([AssetStub.image1, AssetStub.image2]); verifyNever(() => mockBackgroundService.digestFiles(any())); verifyNever(() => mockDeviceAssetRepository.updateAll(any())); @@ -363,9 +322,7 @@ void main() { }); } -Future<(Asset, File, DeviceAsset, Uint8List)> _createAssetMock( - Asset asset, -) async { +Future<(Asset, File, DeviceAsset, Uint8List)> _createAssetMock(Asset asset) async { final random = Random(); final hash = Uint8List.fromList(List.generate(20, (i) => random.nextInt(255))); final mockAsset = MockAsset(); @@ -384,8 +341,9 @@ Future<(Asset, File, DeviceAsset, Uint8List)> _createAssetMock( when(() => mockAsset.fileName).thenReturn(asset.fileName); when(() => mockAsset.fileCreatedAt).thenReturn(asset.fileCreatedAt); when(() => mockAsset.fileModifiedAt).thenReturn(asset.fileModifiedAt); - when(() => mockAsset.copyWith(checksum: any(named: "checksum"))) - .thenReturn(asset.copyWith(checksum: base64.encode(hash))); + when( + () => mockAsset.copyWith(checksum: any(named: "checksum")), + ).thenReturn(asset.copyWith(checksum: base64.encode(hash))); when(() => mockAsset.local).thenAnswer((_) => mockAssetEntity); when(() => mockAssetEntity.originFile).thenAnswer((_) async => file); diff --git a/mobile/test/test_utils.dart b/mobile/test/test_utils.dart index 596d3bcd1c..d932e2ffc7 100644 --- a/mobile/test/test_utils.dart +++ b/mobile/test/test_utils.dart @@ -73,11 +73,7 @@ abstract final class TestUtils { List overrides = const [], List? observers, }) { - final container = ProviderContainer( - parent: parent, - overrides: overrides, - observers: observers, - ); + final container = ProviderContainer(parent: parent, overrides: overrides, observers: observers); // Dispose on test end addTearDown(container.dispose); @@ -94,23 +90,22 @@ abstract final class TestUtils { // Workaround till the following issue is resolved // https://github.com/dart-lang/test/issues/2307 - static T fakeAsync( - Future Function(FakeAsync _) callback, { - DateTime? initialTime, - }) { + static T fakeAsync(Future Function(FakeAsync _) callback, {DateTime? initialTime}) { late final T result; Object? error; StackTrace? stack; FakeAsync(initialTime: initialTime).run((FakeAsync async) { bool shouldPump = true; unawaited( - callback(async).then( - (value) => result = value, - onError: (e, s) { - error = e; - stack = s; - }, - ).whenComplete(() => shouldPump = false), + callback(async) + .then( + (value) => result = value, + onError: (e, s) { + error = e; + stack = s; + }, + ) + .whenComplete(() => shouldPump = false), ); while (shouldPump) { diff --git a/mobile/test/widget_tester_extensions.dart b/mobile/test/widget_tester_extensions.dart index 7d5b266224..bb3fc3f418 100644 --- a/mobile/test/widget_tester_extensions.dart +++ b/mobile/test/widget_tester_extensions.dart @@ -18,10 +18,7 @@ extension PumpConsumerWidget on WidgetTester { return pumpWidget( ProviderScope( overrides: overrides, - child: MaterialApp( - debugShowCheckedModeBanner: false, - home: Material(child: widget), - ), + child: MaterialApp(debugShowCheckedModeBanner: false, home: Material(child: widget)), ), duration: duration, phase: phase, From 34974b036c5bc79f899c44c058b618412110362a Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Tue, 29 Jul 2025 00:52:50 +0530 Subject: [PATCH 090/748] fix: handle back gesture in multi selection mode (#20356) * fix: handle back gesture in multi selection mode # Conflicts: # mobile/lib/presentation/widgets/timeline/timeline.widget.dart * remove null-aware element because Isar * chore: set sqlite busy_timeout to 500ms (#20358) fix: add busy_timeout pragma Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- .../repositories/db.repository.dart | 1 + .../widgets/timeline/timeline.widget.dart | 132 ++++++++---------- 2 files changed, 62 insertions(+), 71 deletions(-) diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index 7f6374ed24..6e574afa8c 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -109,6 +109,7 @@ class Drift extends $Drift implements IDatabaseRepository { await customStatement('PRAGMA foreign_keys = ON'); await customStatement('PRAGMA synchronous = NORMAL'); await customStatement('PRAGMA journal_mode = WAL'); + await customStatement('PRAGMA busy_timeout = 500'); }, ); } diff --git a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart index 26799580a2..d946872781 100644 --- a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart @@ -155,79 +155,69 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { final asyncSegments = ref.watch(timelineSegmentProvider); final maxHeight = ref.watch(timelineArgsProvider.select((args) => args.maxHeight)); final isSelectionMode = ref.watch(multiSelectProvider.select((s) => s.forceEnable)); + final isMultiSelectEnabled = ref.watch(multiSelectProvider.select((s) => s.isEnabled)); - return asyncSegments.widgetWhen( - onData: (segments) { - final childCount = (segments.lastOrNull?.lastIndex ?? -1) + 1; - final double appBarExpandedHeight = widget.appBar != null && widget.appBar is MesmerizingSliverAppBar ? 200 : 0; - final topPadding = context.padding.top + (widget.appBar == null ? 0 : kToolbarHeight) + 10; - - const scrubberBottomPadding = 100.0; - final bottomPadding = context.padding.bottom + (widget.appBar == null ? 0 : scrubberBottomPadding); - - return PrimaryScrollController( - controller: _scrollController, - child: Stack( - children: [ - Scrubber( - layoutSegments: segments, - timelineHeight: maxHeight, - topPadding: topPadding, - bottomPadding: bottomPadding, - monthSegmentSnappingOffset: widget.topSliverWidgetHeight ?? 0 + appBarExpandedHeight, - child: CustomScrollView( - primary: true, - cacheExtent: maxHeight * 2, - slivers: [ - if (isSelectionMode) const SelectionSliverAppBar() else if (widget.appBar != null) widget.appBar!, - if (widget.topSliverWidget != null) widget.topSliverWidget!, - _SliverSegmentedList( - segments: segments, - delegate: SliverChildBuilderDelegate( - (ctx, index) { - if (index >= childCount) return null; - final segment = segments.findByIndex(index); - return segment?.builder(ctx, index) ?? const SizedBox.shrink(); - }, - childCount: childCount, - addAutomaticKeepAlives: false, - // We add repaint boundary around tiles, so skip the auto boundaries - addRepaintBoundaries: false, - ), - ), - const SliverPadding(padding: EdgeInsets.only(bottom: scrubberBottomPadding)), - ], - ), - ), - if (!isSelectionMode) ...[ - Consumer( - builder: (_, consumerRef, child) { - final isMultiSelectEnabled = consumerRef.watch(multiSelectProvider.select((s) => s.isEnabled)); - - if (isMultiSelectEnabled) { - return child!; - } - return const SizedBox.shrink(); - }, - child: const Positioned(top: 60, left: 25, child: _MultiSelectStatusButton()), - ), - if (widget.bottomSheet != null) - Consumer( - builder: (_, consumerRef, child) { - final isMultiSelectEnabled = consumerRef.watch(multiSelectProvider.select((s) => s.isEnabled)); - - if (isMultiSelectEnabled) { - return child!; - } - return const SizedBox.shrink(); - }, - child: widget.bottomSheet, - ), - ], - ], - ), - ); + return PopScope( + canPop: !isMultiSelectEnabled, + onPopInvokedWithResult: (_, __) { + if (isMultiSelectEnabled) { + ref.read(multiSelectProvider.notifier).reset(); + } }, + child: asyncSegments.widgetWhen( + onData: (segments) { + final childCount = (segments.lastOrNull?.lastIndex ?? -1) + 1; + final double appBarExpandedHeight = widget.appBar != null && widget.appBar is MesmerizingSliverAppBar + ? 200 + : 0; + final topPadding = context.padding.top + (widget.appBar == null ? 0 : kToolbarHeight) + 10; + + const scrubberBottomPadding = 100.0; + final bottomPadding = context.padding.bottom + (widget.appBar == null ? 0 : scrubberBottomPadding); + + return PrimaryScrollController( + controller: _scrollController, + child: Stack( + children: [ + Scrubber( + layoutSegments: segments, + timelineHeight: maxHeight, + topPadding: topPadding, + bottomPadding: bottomPadding, + monthSegmentSnappingOffset: widget.topSliverWidgetHeight ?? 0 + appBarExpandedHeight, + child: CustomScrollView( + primary: true, + cacheExtent: maxHeight * 2, + slivers: [ + if (isSelectionMode) const SelectionSliverAppBar() else if (widget.appBar != null) widget.appBar!, + if (widget.topSliverWidget != null) widget.topSliverWidget!, + _SliverSegmentedList( + segments: segments, + delegate: SliverChildBuilderDelegate( + (ctx, index) { + if (index >= childCount) return null; + final segment = segments.findByIndex(index); + return segment?.builder(ctx, index) ?? const SizedBox.shrink(); + }, + childCount: childCount, + addAutomaticKeepAlives: false, + // We add repaint boundary around tiles, so skip the auto boundaries + addRepaintBoundaries: false, + ), + ), + const SliverPadding(padding: EdgeInsets.only(bottom: scrubberBottomPadding)), + ], + ), + ), + if (!isSelectionMode && isMultiSelectEnabled) ...[ + const Positioned(top: 60, left: 25, child: _MultiSelectStatusButton()), + if (widget.bottomSheet != null) widget.bottomSheet!, + ], + ], + ), + ); + }, + ), ); } } From 7d759edfcc9842b4ddfa411d5f0bad8ea185c8b2 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Mon, 28 Jul 2025 18:40:34 -0400 Subject: [PATCH 091/748] chore: add permission metadata to open-api document (#20373) --- open-api/immich-openapi-specs.json | 501 ++++++++++++++++++---------- server/src/middleware/auth.guard.ts | 6 +- 2 files changed, 339 insertions(+), 168 deletions(-) diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 5000eaa5c7..8b305aa8b5 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -77,7 +77,8 @@ ], "tags": [ "Activities" - ] + ], + "x-immich-permission": "activity.read" }, "post": { "operationId": "createActivity", @@ -117,7 +118,8 @@ ], "tags": [ "Activities" - ] + ], + "x-immich-permission": "activity.create" } }, "/activities/statistics": { @@ -168,7 +170,8 @@ ], "tags": [ "Activities" - ] + ], + "x-immich-permission": "activity.statistics" } }, "/activities/{id}": { @@ -203,7 +206,8 @@ ], "tags": [ "Activities" - ] + ], + "x-immich-permission": "activity.delete" } }, "/admin/notifications": { @@ -391,7 +395,8 @@ ], "tags": [ "Users (admin)" - ] + ], + "x-immich-permission": "adminUser.read" }, "post": { "operationId": "createUserAdmin", @@ -431,7 +436,8 @@ ], "tags": [ "Users (admin)" - ] + ], + "x-immich-permission": "adminUser.create" } }, "/admin/users/{id}": { @@ -483,7 +489,8 @@ ], "tags": [ "Users (admin)" - ] + ], + "x-immich-permission": "adminUser.delete" }, "get": { "operationId": "getUserAdmin", @@ -523,7 +530,8 @@ ], "tags": [ "Users (admin)" - ] + ], + "x-immich-permission": "adminUser.read" }, "put": { "operationId": "updateUserAdmin", @@ -573,7 +581,8 @@ ], "tags": [ "Users (admin)" - ] + ], + "x-immich-permission": "adminUser.update" } }, "/admin/users/{id}/preferences": { @@ -615,7 +624,8 @@ ], "tags": [ "Users (admin)" - ] + ], + "x-immich-permission": "adminUser.read" }, "put": { "operationId": "updateUserPreferencesAdmin", @@ -665,7 +675,8 @@ ], "tags": [ "Users (admin)" - ] + ], + "x-immich-permission": "adminUser.update" } }, "/admin/users/{id}/restore": { @@ -707,7 +718,8 @@ ], "tags": [ "Users (admin)" - ] + ], + "x-immich-permission": "adminUser.delete" } }, "/admin/users/{id}/statistics": { @@ -773,7 +785,8 @@ ], "tags": [ "Users (admin)" - ] + ], + "x-immich-permission": "adminUser.read" } }, "/albums": { @@ -827,7 +840,8 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "album.read" }, "post": { "operationId": "createAlbum", @@ -867,7 +881,8 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "album.create" } }, "/albums/statistics": { @@ -899,7 +914,8 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "album.statistics" } }, "/albums/{id}": { @@ -934,7 +950,8 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "album.delete" }, "get": { "operationId": "getAlbumInfo", @@ -998,7 +1015,8 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "album.read" }, "patch": { "operationId": "updateAlbumInfo", @@ -1048,7 +1066,8 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "album.update" } }, "/albums/{id}/assets": { @@ -1103,7 +1122,8 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "albumAsset.delete" }, "put": { "operationId": "addAssetsToAlbum", @@ -1172,7 +1192,8 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "albumAsset.create" } }, "/albums/{id}/user/{userId}": { @@ -1215,7 +1236,8 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "albumUser.delete" }, "put": { "operationId": "updateAlbumUser", @@ -1266,7 +1288,8 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "albumUser.update" } }, "/albums/{id}/users": { @@ -1318,7 +1341,8 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "albumUser.create" } }, "/api-keys": { @@ -1353,7 +1377,8 @@ ], "tags": [ "API Keys" - ] + ], + "x-immich-permission": "apiKey.read" }, "post": { "operationId": "createApiKey", @@ -1393,7 +1418,8 @@ ], "tags": [ "API Keys" - ] + ], + "x-immich-permission": "apiKey.create" } }, "/api-keys/{id}": { @@ -1428,7 +1454,8 @@ ], "tags": [ "API Keys" - ] + ], + "x-immich-permission": "apiKey.delete" }, "get": { "operationId": "getApiKey", @@ -1468,7 +1495,8 @@ ], "tags": [ "API Keys" - ] + ], + "x-immich-permission": "apiKey.read" }, "put": { "operationId": "updateApiKey", @@ -1518,7 +1546,8 @@ ], "tags": [ "API Keys" - ] + ], + "x-immich-permission": "apiKey.update" } }, "/assets": { @@ -1553,7 +1582,8 @@ ], "tags": [ "Assets" - ] + ], + "x-immich-permission": "asset.delete" }, "post": { "operationId": "uploadAsset", @@ -1620,7 +1650,8 @@ ], "tags": [ "Assets" - ] + ], + "x-immich-permission": "asset.upload" }, "put": { "operationId": "updateAssets", @@ -1653,7 +1684,8 @@ ], "tags": [ "Assets" - ] + ], + "x-immich-permission": "asset.update" } }, "/assets/bulk-upload-check": { @@ -1873,7 +1905,8 @@ ], "x-immich-lifecycle": { "deprecatedAt": "v1.116.0" - } + }, + "x-immich-permission": "asset.read" } }, "/assets/statistics": { @@ -1930,7 +1963,8 @@ ], "tags": [ "Assets" - ] + ], + "x-immich-permission": "asset.statistics" } }, "/assets/{id}": { @@ -1988,7 +2022,8 @@ ], "tags": [ "Assets" - ] + ], + "x-immich-permission": "asset.read" }, "put": { "operationId": "updateAsset", @@ -2038,7 +2073,8 @@ ], "tags": [ "Assets" - ] + ], + "x-immich-permission": "asset.update" } }, "/assets/{id}/original": { @@ -2097,7 +2133,8 @@ ], "tags": [ "Assets" - ] + ], + "x-immich-permission": "asset.download" }, "put": { "description": "Replace the asset with new file, without changing its id", @@ -2168,7 +2205,8 @@ ], "x-immich-lifecycle": { "addedAt": "v1.106.0" - } + }, + "x-immich-permission": "asset.replace" } }, "/assets/{id}/thumbnail": { @@ -2235,7 +2273,8 @@ ], "tags": [ "Assets" - ] + ], + "x-immich-permission": "asset.view" } }, "/assets/{id}/video/playback": { @@ -2294,7 +2333,8 @@ ], "tags": [ "Assets" - ] + ], + "x-immich-permission": "asset.view" } }, "/auth/admin-sign-up": { @@ -2367,7 +2407,8 @@ ], "tags": [ "Authentication" - ] + ], + "x-immich-permission": "auth.changePassword" } }, "/auth/login": { @@ -2465,7 +2506,8 @@ ], "tags": [ "Authentication" - ] + ], + "x-immich-permission": "pinCode.delete" }, "post": { "operationId": "setupPinCode", @@ -2498,7 +2540,8 @@ ], "tags": [ "Authentication" - ] + ], + "x-immich-permission": "pinCode.create" }, "put": { "operationId": "changePinCode", @@ -2531,7 +2574,8 @@ ], "tags": [ "Authentication" - ] + ], + "x-immich-permission": "pinCode.update" } }, "/auth/session/lock": { @@ -2715,7 +2759,8 @@ ], "tags": [ "Download" - ] + ], + "x-immich-permission": "asset.download" } }, "/download/info": { @@ -2774,7 +2819,8 @@ ], "tags": [ "Download" - ] + ], + "x-immich-permission": "asset.download" } }, "/duplicates": { @@ -2809,7 +2855,8 @@ ], "tags": [ "Duplicates" - ] + ], + "x-immich-permission": "duplicate.delete" }, "get": { "operationId": "getAssetDuplicates", @@ -2842,7 +2889,8 @@ ], "tags": [ "Duplicates" - ] + ], + "x-immich-permission": "duplicate.read" } }, "/duplicates/{id}": { @@ -2877,7 +2925,8 @@ ], "tags": [ "Duplicates" - ] + ], + "x-immich-permission": "duplicate.delete" } }, "/faces": { @@ -2922,7 +2971,8 @@ ], "tags": [ "Faces" - ] + ], + "x-immich-permission": "face.read" }, "post": { "operationId": "createFace", @@ -2955,7 +3005,8 @@ ], "tags": [ "Faces" - ] + ], + "x-immich-permission": "face.create" } }, "/faces/{id}": { @@ -3000,7 +3051,8 @@ ], "tags": [ "Faces" - ] + ], + "x-immich-permission": "face.delete" }, "put": { "operationId": "reassignFacesById", @@ -3050,7 +3102,8 @@ ], "tags": [ "Faces" - ] + ], + "x-immich-permission": "face.update" } }, "/jobs": { @@ -3082,7 +3135,8 @@ ], "tags": [ "Jobs" - ] + ], + "x-immich-permission": "job.read" }, "post": { "operationId": "createJob", @@ -3115,7 +3169,8 @@ ], "tags": [ "Jobs" - ] + ], + "x-immich-permission": "job.create" } }, "/jobs/{id}": { @@ -3166,7 +3221,8 @@ ], "tags": [ "Jobs" - ] + ], + "x-immich-permission": "job.create" } }, "/libraries": { @@ -3201,7 +3257,8 @@ ], "tags": [ "Libraries" - ] + ], + "x-immich-permission": "library.read" }, "post": { "operationId": "createLibrary", @@ -3241,7 +3298,8 @@ ], "tags": [ "Libraries" - ] + ], + "x-immich-permission": "library.create" } }, "/libraries/{id}": { @@ -3276,7 +3334,8 @@ ], "tags": [ "Libraries" - ] + ], + "x-immich-permission": "library.delete" }, "get": { "operationId": "getLibrary", @@ -3316,7 +3375,8 @@ ], "tags": [ "Libraries" - ] + ], + "x-immich-permission": "library.read" }, "put": { "operationId": "updateLibrary", @@ -3366,7 +3426,8 @@ ], "tags": [ "Libraries" - ] + ], + "x-immich-permission": "library.update" } }, "/libraries/{id}/scan": { @@ -3401,7 +3462,8 @@ ], "tags": [ "Libraries" - ] + ], + "x-immich-permission": "library.update" } }, "/libraries/{id}/statistics": { @@ -3443,7 +3505,8 @@ ], "tags": [ "Libraries" - ] + ], + "x-immich-permission": "library.statistics" } }, "/libraries/{id}/validate": { @@ -3704,7 +3767,8 @@ ], "tags": [ "Memories" - ] + ], + "x-immich-permission": "memory.read" }, "post": { "operationId": "createMemory", @@ -3744,7 +3808,8 @@ ], "tags": [ "Memories" - ] + ], + "x-immich-permission": "memory.create" } }, "/memories/statistics": { @@ -3810,7 +3875,8 @@ ], "tags": [ "Memories" - ] + ], + "x-immich-permission": "memory.statistics" } }, "/memories/{id}": { @@ -3845,7 +3911,8 @@ ], "tags": [ "Memories" - ] + ], + "x-immich-permission": "memory.delete" }, "get": { "operationId": "getMemory", @@ -3885,7 +3952,8 @@ ], "tags": [ "Memories" - ] + ], + "x-immich-permission": "memory.read" }, "put": { "operationId": "updateMemory", @@ -3935,7 +4003,8 @@ ], "tags": [ "Memories" - ] + ], + "x-immich-permission": "memory.update" } }, "/memories/{id}/assets": { @@ -3990,7 +4059,8 @@ ], "tags": [ "Memories" - ] + ], + "x-immich-permission": "memoryAsset.delete" }, "put": { "operationId": "addMemoryAssets", @@ -4043,7 +4113,8 @@ ], "tags": [ "Memories" - ] + ], + "x-immich-permission": "memoryAsset.create" } }, "/notifications": { @@ -4078,7 +4149,8 @@ ], "tags": [ "Notifications" - ] + ], + "x-immich-permission": "notification.delete" }, "get": { "operationId": "getNotifications", @@ -4145,7 +4217,8 @@ ], "tags": [ "Notifications" - ] + ], + "x-immich-permission": "notification.read" }, "put": { "operationId": "updateNotifications", @@ -4178,7 +4251,8 @@ ], "tags": [ "Notifications" - ] + ], + "x-immich-permission": "notification.update" } }, "/notifications/{id}": { @@ -4213,7 +4287,8 @@ ], "tags": [ "Notifications" - ] + ], + "x-immich-permission": "notification.delete" }, "get": { "operationId": "getNotification", @@ -4253,7 +4328,8 @@ ], "tags": [ "Notifications" - ] + ], + "x-immich-permission": "notification.read" }, "put": { "operationId": "updateNotification", @@ -4303,7 +4379,8 @@ ], "tags": [ "Notifications" - ] + ], + "x-immich-permission": "notification.update" } }, "/oauth/authorize": { @@ -4497,7 +4574,8 @@ ], "tags": [ "Partners" - ] + ], + "x-immich-permission": "partner.read" } }, "/partners/{id}": { @@ -4532,7 +4610,8 @@ ], "tags": [ "Partners" - ] + ], + "x-immich-permission": "partner.delete" }, "post": { "operationId": "createPartner", @@ -4572,7 +4651,8 @@ ], "tags": [ "Partners" - ] + ], + "x-immich-permission": "partner.create" }, "put": { "operationId": "updatePartner", @@ -4622,7 +4702,8 @@ ], "tags": [ "Partners" - ] + ], + "x-immich-permission": "partner.update" } }, "/people": { @@ -4657,7 +4738,8 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.delete" }, "get": { "operationId": "getAllPeople", @@ -4737,7 +4819,8 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.read" }, "post": { "operationId": "createPerson", @@ -4777,7 +4860,8 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.create" }, "put": { "operationId": "updatePeople", @@ -4820,7 +4904,8 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.update" } }, "/people/{id}": { @@ -4855,7 +4940,8 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.delete" }, "get": { "operationId": "getPerson", @@ -4895,7 +4981,8 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.read" }, "put": { "operationId": "updatePerson", @@ -4945,7 +5032,8 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.update" } }, "/people/{id}/merge": { @@ -5000,7 +5088,8 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.merge" } }, "/people/{id}/reassign": { @@ -5055,7 +5144,8 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.reassign" } }, "/people/{id}/statistics": { @@ -5097,7 +5187,8 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.statistics" } }, "/people/{id}/thumbnail": { @@ -5140,7 +5231,8 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.read" } }, "/search/cities": { @@ -5175,7 +5267,8 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "asset.read" } }, "/search/explore": { @@ -5210,7 +5303,8 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "asset.read" } }, "/search/metadata": { @@ -5252,7 +5346,8 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "asset.read" } }, "/search/person": { @@ -5304,7 +5399,8 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "person.read" } }, "/search/places": { @@ -5348,7 +5444,8 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "asset.read" } }, "/search/random": { @@ -5393,7 +5490,8 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "asset.read" } }, "/search/smart": { @@ -5435,7 +5533,8 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "asset.read" } }, "/search/statistics": { @@ -5477,7 +5576,8 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "asset.statistics" } }, "/search/suggestions": { @@ -5562,7 +5662,8 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "asset.read" } }, "/server/about": { @@ -5594,7 +5695,8 @@ ], "tags": [ "Server" - ] + ], + "x-immich-permission": "server.about" } }, "/server/apk-links": { @@ -5626,7 +5728,8 @@ ], "tags": [ "Server" - ] + ], + "x-immich-permission": "server.apkLinks" } }, "/server/config": { @@ -5693,7 +5796,8 @@ ], "tags": [ "Server" - ] + ], + "x-immich-permission": "serverLicense.delete" }, "get": { "operationId": "getServerLicense", @@ -5726,7 +5830,8 @@ ], "tags": [ "Server" - ] + ], + "x-immich-permission": "serverLicense.read" }, "put": { "operationId": "setServerLicense", @@ -5766,7 +5871,8 @@ ], "tags": [ "Server" - ] + ], + "x-immich-permission": "serverLicense.update" } }, "/server/media-types": { @@ -5840,7 +5946,8 @@ ], "tags": [ "Server" - ] + ], + "x-immich-permission": "server.statistics" } }, "/server/storage": { @@ -5872,7 +5979,8 @@ ], "tags": [ "Server" - ] + ], + "x-immich-permission": "server.storage" } }, "/server/theme": { @@ -5995,7 +6103,8 @@ ], "tags": [ "Sessions" - ] + ], + "x-immich-permission": "session.delete" }, "get": { "operationId": "getSessions", @@ -6028,7 +6137,8 @@ ], "tags": [ "Sessions" - ] + ], + "x-immich-permission": "session.read" }, "post": { "operationId": "createSession", @@ -6068,7 +6178,8 @@ ], "tags": [ "Sessions" - ] + ], + "x-immich-permission": "session.create" } }, "/sessions/{id}": { @@ -6103,7 +6214,8 @@ ], "tags": [ "Sessions" - ] + ], + "x-immich-permission": "session.delete" }, "put": { "operationId": "updateSession", @@ -6153,7 +6265,8 @@ ], "tags": [ "Sessions" - ] + ], + "x-immich-permission": "session.update" } }, "/sessions/{id}/lock": { @@ -6188,7 +6301,8 @@ ], "tags": [ "Sessions" - ] + ], + "x-immich-permission": "session.lock" } }, "/shared-links": { @@ -6233,7 +6347,8 @@ ], "tags": [ "Shared Links" - ] + ], + "x-immich-permission": "sharedLink.read" }, "post": { "operationId": "createSharedLink", @@ -6273,7 +6388,8 @@ ], "tags": [ "Shared Links" - ] + ], + "x-immich-permission": "sharedLink.create" } }, "/shared-links/me": { @@ -6374,7 +6490,8 @@ ], "tags": [ "Shared Links" - ] + ], + "x-immich-permission": "sharedLink.delete" }, "get": { "operationId": "getSharedLinkById", @@ -6414,7 +6531,8 @@ ], "tags": [ "Shared Links" - ] + ], + "x-immich-permission": "sharedLink.read" }, "patch": { "operationId": "updateSharedLink", @@ -6464,7 +6582,8 @@ ], "tags": [ "Shared Links" - ] + ], + "x-immich-permission": "sharedLink.update" } }, "/shared-links/{id}/assets": { @@ -6639,7 +6758,8 @@ ], "tags": [ "Stacks" - ] + ], + "x-immich-permission": "stack.delete" }, "get": { "operationId": "searchStacks", @@ -6682,7 +6802,8 @@ ], "tags": [ "Stacks" - ] + ], + "x-immich-permission": "stack.read" }, "post": { "operationId": "createStack", @@ -6722,7 +6843,8 @@ ], "tags": [ "Stacks" - ] + ], + "x-immich-permission": "stack.create" } }, "/stacks/{id}": { @@ -6757,7 +6879,8 @@ ], "tags": [ "Stacks" - ] + ], + "x-immich-permission": "stack.delete" }, "get": { "operationId": "getStack", @@ -6797,7 +6920,8 @@ ], "tags": [ "Stacks" - ] + ], + "x-immich-permission": "stack.read" }, "put": { "operationId": "updateStack", @@ -6847,7 +6971,8 @@ ], "tags": [ "Stacks" - ] + ], + "x-immich-permission": "stack.update" } }, "/stacks/{id}/assets/{assetId}": { @@ -6891,7 +7016,8 @@ ], "tags": [ "Stacks" - ] + ], + "x-immich-permission": "stack.update" } }, "/sync/ack": { @@ -6926,7 +7052,8 @@ ], "tags": [ "Sync" - ] + ], + "x-immich-permission": "syncCheckpoint.delete" }, "get": { "operationId": "getSyncAck", @@ -6959,7 +7086,8 @@ ], "tags": [ "Sync" - ] + ], + "x-immich-permission": "syncCheckpoint.read" }, "post": { "operationId": "sendSyncAck", @@ -6992,7 +7120,8 @@ ], "tags": [ "Sync" - ] + ], + "x-immich-permission": "syncCheckpoint.update" } }, "/sync/delta-sync": { @@ -7114,7 +7243,8 @@ ], "tags": [ "Sync" - ] + ], + "x-immich-permission": "sync.stream" } }, "/system-config": { @@ -7146,7 +7276,8 @@ ], "tags": [ "System Config" - ] + ], + "x-immich-permission": "systemConfig.read" }, "put": { "operationId": "updateConfig", @@ -7186,7 +7317,8 @@ ], "tags": [ "System Config" - ] + ], + "x-immich-permission": "systemConfig.update" } }, "/system-config/defaults": { @@ -7218,7 +7350,8 @@ ], "tags": [ "System Config" - ] + ], + "x-immich-permission": "systemConfig.read" } }, "/system-config/storage-template-options": { @@ -7250,7 +7383,8 @@ ], "tags": [ "System Config" - ] + ], + "x-immich-permission": "systemConfig.read" } }, "/system-metadata/admin-onboarding": { @@ -7282,7 +7416,8 @@ ], "tags": [ "System Metadata" - ] + ], + "x-immich-permission": "systemMetadata.read" }, "post": { "operationId": "updateAdminOnboarding", @@ -7315,7 +7450,8 @@ ], "tags": [ "System Metadata" - ] + ], + "x-immich-permission": "systemMetadata.update" } }, "/system-metadata/reverse-geocoding-state": { @@ -7347,7 +7483,8 @@ ], "tags": [ "System Metadata" - ] + ], + "x-immich-permission": "systemMetadata.read" } }, "/system-metadata/version-check-state": { @@ -7379,7 +7516,8 @@ ], "tags": [ "System Metadata" - ] + ], + "x-immich-permission": "systemMetadata.read" } }, "/tags": { @@ -7414,7 +7552,8 @@ ], "tags": [ "Tags" - ] + ], + "x-immich-permission": "tag.read" }, "post": { "operationId": "createTag", @@ -7454,7 +7593,8 @@ ], "tags": [ "Tags" - ] + ], + "x-immich-permission": "tag.create" }, "put": { "operationId": "upsertTags", @@ -7497,7 +7637,8 @@ ], "tags": [ "Tags" - ] + ], + "x-immich-permission": "tag.create" } }, "/tags/assets": { @@ -7539,7 +7680,8 @@ ], "tags": [ "Tags" - ] + ], + "x-immich-permission": "tag.asset" } }, "/tags/{id}": { @@ -7574,7 +7716,8 @@ ], "tags": [ "Tags" - ] + ], + "x-immich-permission": "tag.delete" }, "get": { "operationId": "getTagById", @@ -7614,7 +7757,8 @@ ], "tags": [ "Tags" - ] + ], + "x-immich-permission": "tag.read" }, "put": { "operationId": "updateTag", @@ -7664,7 +7808,8 @@ ], "tags": [ "Tags" - ] + ], + "x-immich-permission": "tag.update" } }, "/tags/{id}/assets": { @@ -7719,7 +7864,8 @@ ], "tags": [ "Tags" - ] + ], + "x-immich-permission": "tag.asset" }, "put": { "operationId": "tagAssets", @@ -7772,7 +7918,8 @@ ], "tags": [ "Tags" - ] + ], + "x-immich-permission": "tag.asset" } }, "/timeline/bucket": { @@ -7925,7 +8072,8 @@ ], "tags": [ "Timeline" - ] + ], + "x-immich-permission": "asset.read" } }, "/timeline/buckets": { @@ -8071,7 +8219,8 @@ ], "tags": [ "Timeline" - ] + ], + "x-immich-permission": "asset.read" } }, "/trash/empty": { @@ -8103,7 +8252,8 @@ ], "tags": [ "Trash" - ] + ], + "x-immich-permission": "asset.delete" } }, "/trash/restore": { @@ -8135,7 +8285,8 @@ ], "tags": [ "Trash" - ] + ], + "x-immich-permission": "asset.delete" } }, "/trash/restore/assets": { @@ -8177,7 +8328,8 @@ ], "tags": [ "Trash" - ] + ], + "x-immich-permission": "asset.delete" } }, "/users": { @@ -8212,7 +8364,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "user.read" } }, "/users/me": { @@ -8244,7 +8397,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "user.read" }, "put": { "operationId": "updateMyUser", @@ -8284,7 +8438,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "user.update" } }, "/users/me/license": { @@ -8309,7 +8464,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userLicense.delete" }, "get": { "operationId": "getUserLicense", @@ -8339,7 +8495,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userLicense.read" }, "put": { "operationId": "setUserLicense", @@ -8379,7 +8536,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userLicense.update" } }, "/users/me/onboarding": { @@ -8404,7 +8562,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userOnboarding.delete" }, "get": { "operationId": "getUserOnboarding", @@ -8434,7 +8593,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userOnboarding.read" }, "put": { "operationId": "setUserOnboarding", @@ -8474,7 +8634,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userOnboarding.update" } }, "/users/me/preferences": { @@ -8506,7 +8667,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userPreference.read" }, "put": { "operationId": "updateMyPreferences", @@ -8546,7 +8708,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userPreference.update" } }, "/users/profile-image": { @@ -8571,7 +8734,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userProfileImage.delete" }, "post": { "operationId": "createProfileImage", @@ -8612,7 +8776,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userProfileImage.update" } }, "/users/{id}": { @@ -8654,7 +8819,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "user.read" } }, "/users/{id}/profile-image": { @@ -8697,7 +8863,8 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userProfileImage.read" } }, "/view/folder": { diff --git a/server/src/middleware/auth.guard.ts b/server/src/middleware/auth.guard.ts index 69b3cb5ecc..38ff1c373f 100644 --- a/server/src/middleware/auth.guard.ts +++ b/server/src/middleware/auth.guard.ts @@ -7,7 +7,7 @@ import { createParamDecorator, } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; -import { ApiBearerAuth, ApiCookieAuth, ApiOkResponse, ApiQuery, ApiSecurity } from '@nestjs/swagger'; +import { ApiBearerAuth, ApiCookieAuth, ApiExtension, ApiOkResponse, ApiQuery, ApiSecurity } from '@nestjs/swagger'; import { Request } from 'express'; import { AuthDto } from 'src/dtos/auth.dto'; import { ImmichQuery, MetadataKey, Permission } from 'src/enum'; @@ -27,6 +27,10 @@ export const Authenticated = (options?: AuthenticatedOptions): MethodDecorator = SetMetadata(MetadataKey.AuthRoute, options || {}), ]; + if (options?.permission) { + decorators.push(ApiExtension('x-immich-permission', options.permission)); + } + if ((options as SharedLinkRoute)?.sharedLink) { decorators.push( ApiQuery({ name: ImmichQuery.SharedLinkKey, type: String, required: false }), From ae1d60e259aa0d913b0395eca24bb94234b35f11 Mon Sep 17 00:00:00 2001 From: Alwin Lohrie <46248939+niwla23@users.noreply.github.com> Date: Tue, 29 Jul 2025 00:48:39 +0200 Subject: [PATCH 092/748] feat: find large files utility (#18040) feat: large asset utility Co-authored-by: Jason Rasmussen --- i18n/en.json | 2 + mobile/openapi/README.md | 1 + mobile/openapi/lib/api/search_api.dart | 264 +++++++++++++++ open-api/immich-openapi-specs.json | 317 ++++++++++++++++++ open-api/typescript-sdk/src/fetch-client.ts | 73 ++++ server/src/controllers/search.controller.ts | 8 + server/src/dtos/search.dto.ts | 9 + server/src/queries/search.repository.sql | 21 ++ server/src/repositories/search.repository.ts | 27 +- server/src/services/search.service.ts | 11 + .../specs/services/search.service.spec.ts | 55 +++ .../large-assets/large-asset-data.svelte | 58 ++++ .../utilities-page/utilities-menu.svelte | 11 +- web/src/lib/constants.ts | 1 + web/src/lib/utils/asset-utils.ts | 4 +- .../[[assetId=id]]/+page.svelte | 89 +++++ .../[[photos=photos]]/[[assetId=id]]/+page.ts | 17 + 17 files changed, 964 insertions(+), 4 deletions(-) create mode 100644 server/test/medium/specs/services/search.service.spec.ts create mode 100644 web/src/lib/components/utilities-page/large-assets/large-asset-data.svelte create mode 100644 web/src/routes/(user)/utilities/large-files/[[photos=photos]]/[[assetId=id]]/+page.svelte create mode 100644 web/src/routes/(user)/utilities/large-files/[[photos=photos]]/[[assetId=id]]/+page.ts diff --git a/i18n/en.json b/i18n/en.json index 39baff3381..5f7f166222 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1154,6 +1154,7 @@ "language_no_results_title": "No languages found", "language_search_hint": "Search languages...", "language_setting_description": "Select your preferred language", + "large_files": "Large Files", "last_seen": "Last seen", "latest_version": "Latest Version", "latitude": "Latitude", @@ -1588,6 +1589,7 @@ "resume": "Resume", "retry_upload": "Retry upload", "review_duplicates": "Review duplicates", + "review_large_files": "Review large files", "role": "Role", "role_editor": "Editor", "role_viewer": "Viewer", diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 3181b03a47..058524479c 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -184,6 +184,7 @@ Class | Method | HTTP request | Description *SearchApi* | [**getSearchSuggestions**](doc//SearchApi.md#getsearchsuggestions) | **GET** /search/suggestions | *SearchApi* | [**searchAssetStatistics**](doc//SearchApi.md#searchassetstatistics) | **POST** /search/statistics | *SearchApi* | [**searchAssets**](doc//SearchApi.md#searchassets) | **POST** /search/metadata | +*SearchApi* | [**searchLargeAssets**](doc//SearchApi.md#searchlargeassets) | **POST** /search/large-assets | *SearchApi* | [**searchPerson**](doc//SearchApi.md#searchperson) | **GET** /search/person | *SearchApi* | [**searchPlaces**](doc//SearchApi.md#searchplaces) | **GET** /search/places | *SearchApi* | [**searchRandom**](doc//SearchApi.md#searchrandom) | **POST** /search/random | diff --git a/mobile/openapi/lib/api/search_api.dart b/mobile/openapi/lib/api/search_api.dart index 5c7a8de59d..1b58702c40 100644 --- a/mobile/openapi/lib/api/search_api.dart +++ b/mobile/openapi/lib/api/search_api.dart @@ -287,6 +287,270 @@ class SearchApi { return null; } + /// Performs an HTTP 'POST /search/large-assets' operation and returns the [Response]. + /// Parameters: + /// + /// * [List] albumIds: + /// + /// * [String] city: + /// + /// * [String] country: + /// + /// * [DateTime] createdAfter: + /// + /// * [DateTime] createdBefore: + /// + /// * [String] deviceId: + /// + /// * [bool] isEncoded: + /// + /// * [bool] isFavorite: + /// + /// * [bool] isMotion: + /// + /// * [bool] isNotInAlbum: + /// + /// * [bool] isOffline: + /// + /// * [String] lensModel: + /// + /// * [String] libraryId: + /// + /// * [String] make: + /// + /// * [int] minFileSize: + /// + /// * [String] model: + /// + /// * [List] personIds: + /// + /// * [num] rating: + /// + /// * [num] size: + /// + /// * [String] state: + /// + /// * [List] tagIds: + /// + /// * [DateTime] takenAfter: + /// + /// * [DateTime] takenBefore: + /// + /// * [DateTime] trashedAfter: + /// + /// * [DateTime] trashedBefore: + /// + /// * [AssetTypeEnum] type: + /// + /// * [DateTime] updatedAfter: + /// + /// * [DateTime] updatedBefore: + /// + /// * [AssetVisibility] visibility: + /// + /// * [bool] withDeleted: + /// + /// * [bool] withExif: + Future searchLargeAssetsWithHttpInfo({ List? albumIds, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceId, bool? isEncoded, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, String? lensModel, String? libraryId, String? make, int? minFileSize, String? model, List? personIds, num? rating, num? size, String? state, List? tagIds, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, AssetVisibility? visibility, bool? withDeleted, bool? withExif, }) async { + // ignore: prefer_const_declarations + final apiPath = r'/search/large-assets'; + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + if (albumIds != null) { + queryParams.addAll(_queryParams('multi', 'albumIds', albumIds)); + } + if (city != null) { + queryParams.addAll(_queryParams('', 'city', city)); + } + if (country != null) { + queryParams.addAll(_queryParams('', 'country', country)); + } + if (createdAfter != null) { + queryParams.addAll(_queryParams('', 'createdAfter', createdAfter)); + } + if (createdBefore != null) { + queryParams.addAll(_queryParams('', 'createdBefore', createdBefore)); + } + if (deviceId != null) { + queryParams.addAll(_queryParams('', 'deviceId', deviceId)); + } + if (isEncoded != null) { + queryParams.addAll(_queryParams('', 'isEncoded', isEncoded)); + } + if (isFavorite != null) { + queryParams.addAll(_queryParams('', 'isFavorite', isFavorite)); + } + if (isMotion != null) { + queryParams.addAll(_queryParams('', 'isMotion', isMotion)); + } + if (isNotInAlbum != null) { + queryParams.addAll(_queryParams('', 'isNotInAlbum', isNotInAlbum)); + } + if (isOffline != null) { + queryParams.addAll(_queryParams('', 'isOffline', isOffline)); + } + if (lensModel != null) { + queryParams.addAll(_queryParams('', 'lensModel', lensModel)); + } + if (libraryId != null) { + queryParams.addAll(_queryParams('', 'libraryId', libraryId)); + } + if (make != null) { + queryParams.addAll(_queryParams('', 'make', make)); + } + if (minFileSize != null) { + queryParams.addAll(_queryParams('', 'minFileSize', minFileSize)); + } + if (model != null) { + queryParams.addAll(_queryParams('', 'model', model)); + } + if (personIds != null) { + queryParams.addAll(_queryParams('multi', 'personIds', personIds)); + } + if (rating != null) { + queryParams.addAll(_queryParams('', 'rating', rating)); + } + if (size != null) { + queryParams.addAll(_queryParams('', 'size', size)); + } + if (state != null) { + queryParams.addAll(_queryParams('', 'state', state)); + } + if (tagIds != null) { + queryParams.addAll(_queryParams('multi', 'tagIds', tagIds)); + } + if (takenAfter != null) { + queryParams.addAll(_queryParams('', 'takenAfter', takenAfter)); + } + if (takenBefore != null) { + queryParams.addAll(_queryParams('', 'takenBefore', takenBefore)); + } + if (trashedAfter != null) { + queryParams.addAll(_queryParams('', 'trashedAfter', trashedAfter)); + } + if (trashedBefore != null) { + queryParams.addAll(_queryParams('', 'trashedBefore', trashedBefore)); + } + if (type != null) { + queryParams.addAll(_queryParams('', 'type', type)); + } + if (updatedAfter != null) { + queryParams.addAll(_queryParams('', 'updatedAfter', updatedAfter)); + } + if (updatedBefore != null) { + queryParams.addAll(_queryParams('', 'updatedBefore', updatedBefore)); + } + if (visibility != null) { + queryParams.addAll(_queryParams('', 'visibility', visibility)); + } + if (withDeleted != null) { + queryParams.addAll(_queryParams('', 'withDeleted', withDeleted)); + } + if (withExif != null) { + queryParams.addAll(_queryParams('', 'withExif', withExif)); + } + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'POST', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [List] albumIds: + /// + /// * [String] city: + /// + /// * [String] country: + /// + /// * [DateTime] createdAfter: + /// + /// * [DateTime] createdBefore: + /// + /// * [String] deviceId: + /// + /// * [bool] isEncoded: + /// + /// * [bool] isFavorite: + /// + /// * [bool] isMotion: + /// + /// * [bool] isNotInAlbum: + /// + /// * [bool] isOffline: + /// + /// * [String] lensModel: + /// + /// * [String] libraryId: + /// + /// * [String] make: + /// + /// * [int] minFileSize: + /// + /// * [String] model: + /// + /// * [List] personIds: + /// + /// * [num] rating: + /// + /// * [num] size: + /// + /// * [String] state: + /// + /// * [List] tagIds: + /// + /// * [DateTime] takenAfter: + /// + /// * [DateTime] takenBefore: + /// + /// * [DateTime] trashedAfter: + /// + /// * [DateTime] trashedBefore: + /// + /// * [AssetTypeEnum] type: + /// + /// * [DateTime] updatedAfter: + /// + /// * [DateTime] updatedBefore: + /// + /// * [AssetVisibility] visibility: + /// + /// * [bool] withDeleted: + /// + /// * [bool] withExif: + Future?> searchLargeAssets({ List? albumIds, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceId, bool? isEncoded, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, String? lensModel, String? libraryId, String? make, int? minFileSize, String? model, List? personIds, num? rating, num? size, String? state, List? tagIds, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, AssetVisibility? visibility, bool? withDeleted, bool? withExif, }) async { + final response = await searchLargeAssetsWithHttpInfo( albumIds: albumIds, city: city, country: country, createdAfter: createdAfter, createdBefore: createdBefore, deviceId: deviceId, isEncoded: isEncoded, isFavorite: isFavorite, isMotion: isMotion, isNotInAlbum: isNotInAlbum, isOffline: isOffline, lensModel: lensModel, libraryId: libraryId, make: make, minFileSize: minFileSize, model: model, personIds: personIds, rating: rating, size: size, state: state, tagIds: tagIds, takenAfter: takenAfter, takenBefore: takenBefore, trashedAfter: trashedAfter, trashedBefore: trashedBefore, type: type, updatedAfter: updatedAfter, updatedBefore: updatedBefore, visibility: visibility, withDeleted: withDeleted, withExif: withExif, ); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + // When a remote server returns no body with a status of 204, we shall not decode it. + // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" + // FormatException when trying to decode an empty string. + if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { + final responseBody = await _decodeBodyBytes(response); + return (await apiClient.deserializeAsync(responseBody, 'List') as List) + .cast() + .toList(growable: false); + + } + return null; + } + /// Performs an HTTP 'GET /search/person' operation and returns the [Response]. /// Parameters: /// diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 8b305aa8b5..b8089083d1 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -5307,6 +5307,323 @@ "x-immich-permission": "asset.read" } }, + "/search/large-assets": { + "post": { + "operationId": "searchLargeAssets", + "parameters": [ + { + "name": "albumIds", + "required": false, + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + }, + { + "name": "city", + "required": false, + "in": "query", + "schema": { + "nullable": true, + "type": "string" + } + }, + { + "name": "country", + "required": false, + "in": "query", + "schema": { + "nullable": true, + "type": "string" + } + }, + { + "name": "createdAfter", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "createdBefore", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "deviceId", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "isEncoded", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "isFavorite", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "isMotion", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "isNotInAlbum", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "isOffline", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "lensModel", + "required": false, + "in": "query", + "schema": { + "nullable": true, + "type": "string" + } + }, + { + "name": "libraryId", + "required": false, + "in": "query", + "schema": { + "format": "uuid", + "nullable": true, + "type": "string" + } + }, + { + "name": "make", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "minFileSize", + "required": false, + "in": "query", + "schema": { + "minimum": 0, + "type": "integer" + } + }, + { + "name": "model", + "required": false, + "in": "query", + "schema": { + "nullable": true, + "type": "string" + } + }, + { + "name": "personIds", + "required": false, + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + }, + { + "name": "rating", + "required": false, + "in": "query", + "schema": { + "minimum": -1, + "maximum": 5, + "type": "number" + } + }, + { + "name": "size", + "required": false, + "in": "query", + "schema": { + "minimum": 1, + "maximum": 1000, + "type": "number" + } + }, + { + "name": "state", + "required": false, + "in": "query", + "schema": { + "nullable": true, + "type": "string" + } + }, + { + "name": "tagIds", + "required": false, + "in": "query", + "schema": { + "nullable": true, + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + }, + { + "name": "takenAfter", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "takenBefore", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "trashedAfter", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "trashedBefore", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "type", + "required": false, + "in": "query", + "schema": { + "$ref": "#/components/schemas/AssetTypeEnum" + } + }, + { + "name": "updatedAfter", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "updatedBefore", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "visibility", + "required": false, + "in": "query", + "schema": { + "$ref": "#/components/schemas/AssetVisibility" + } + }, + { + "name": "withDeleted", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "withExif", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/AssetResponseDto" + }, + "type": "array" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Search" + ] + } + }, "/search/metadata": { "post": { "operationId": "searchAssets", diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 063fc86782..33f0da8a08 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -2954,6 +2954,79 @@ export function getExploreData(opts?: Oazapfts.RequestOpts) { ...opts })); } +export function searchLargeAssets({ albumIds, city, country, createdAfter, createdBefore, deviceId, isEncoded, isFavorite, isMotion, isNotInAlbum, isOffline, lensModel, libraryId, make, minFileSize, model, personIds, rating, size, state, tagIds, takenAfter, takenBefore, trashedAfter, trashedBefore, $type, updatedAfter, updatedBefore, visibility, withDeleted, withExif }: { + albumIds?: string[]; + city?: string | null; + country?: string | null; + createdAfter?: string; + createdBefore?: string; + deviceId?: string; + isEncoded?: boolean; + isFavorite?: boolean; + isMotion?: boolean; + isNotInAlbum?: boolean; + isOffline?: boolean; + lensModel?: string | null; + libraryId?: string | null; + make?: string; + minFileSize?: number; + model?: string | null; + personIds?: string[]; + rating?: number; + size?: number; + state?: string | null; + tagIds?: string[] | null; + takenAfter?: string; + takenBefore?: string; + trashedAfter?: string; + trashedBefore?: string; + $type?: AssetTypeEnum; + updatedAfter?: string; + updatedBefore?: string; + visibility?: AssetVisibility; + withDeleted?: boolean; + withExif?: boolean; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: AssetResponseDto[]; + }>(`/search/large-assets${QS.query(QS.explode({ + albumIds, + city, + country, + createdAfter, + createdBefore, + deviceId, + isEncoded, + isFavorite, + isMotion, + isNotInAlbum, + isOffline, + lensModel, + libraryId, + make, + minFileSize, + model, + personIds, + rating, + size, + state, + tagIds, + takenAfter, + takenBefore, + trashedAfter, + trashedBefore, + "type": $type, + updatedAfter, + updatedBefore, + visibility, + withDeleted, + withExif + }))}`, { + ...opts, + method: "POST" + })); +} export function searchAssets({ metadataSearchDto }: { metadataSearchDto: MetadataSearchDto; }, opts?: Oazapfts.RequestOpts) { diff --git a/server/src/controllers/search.controller.ts b/server/src/controllers/search.controller.ts index fefa916fe7..15f8bc3a5a 100644 --- a/server/src/controllers/search.controller.ts +++ b/server/src/controllers/search.controller.ts @@ -4,6 +4,7 @@ import { AssetResponseDto } from 'src/dtos/asset-response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { PersonResponseDto } from 'src/dtos/person.dto'; import { + LargeAssetSearchDto, MetadataSearchDto, PlacesResponseDto, RandomSearchDto, @@ -46,6 +47,13 @@ export class SearchController { return this.service.searchRandom(auth, dto); } + @Post('large-assets') + @HttpCode(HttpStatus.OK) + @Authenticated({ permission: Permission.AssetRead }) + searchLargeAssets(@Auth() auth: AuthDto, @Query() dto: LargeAssetSearchDto): Promise { + return this.service.searchLargeAssets(auth, dto); + } + @Post('smart') @HttpCode(HttpStatus.OK) @Authenticated({ permission: Permission.AssetRead }) diff --git a/server/src/dtos/search.dto.ts b/server/src/dtos/search.dto.ts index aef78e51ea..f709ad94ab 100644 --- a/server/src/dtos/search.dto.ts +++ b/server/src/dtos/search.dto.ts @@ -126,6 +126,15 @@ export class RandomSearchDto extends BaseSearchWithResultsDto { withPeople?: boolean; } +export class LargeAssetSearchDto extends BaseSearchWithResultsDto { + @Optional() + @IsInt() + @Min(0) + @Type(() => Number) + @ApiProperty({ type: 'integer' }) + minFileSize?: number; +} + export class MetadataSearchDto extends RandomSearchDto { @ValidateUUID({ optional: true }) id?: string; diff --git a/server/src/queries/search.repository.sql b/server/src/queries/search.repository.sql index ef5363126f..be2245a74e 100644 --- a/server/src/queries/search.repository.sql +++ b/server/src/queries/search.repository.sql @@ -77,6 +77,27 @@ union all limit $15 +-- SearchRepository.searchLargeAssets +select + "asset".*, + to_json("asset_exif") as "exifInfo" +from + "asset" + inner join "asset_exif" on "asset"."id" = "asset_exif"."assetId" + left join "asset_exif" on "asset"."id" = "asset_exif"."assetId" +where + "asset"."visibility" = $1 + and "asset"."fileCreatedAt" >= $2 + and "asset_exif"."lensModel" = $3 + and "asset"."ownerId" = any ($4::uuid[]) + and "asset"."isFavorite" = $5 + and "asset"."deletedAt" is null + and "asset_exif"."fileSizeInByte" > $6 +order by + "asset_exif"."fileSizeInByte" desc +limit + $7 + -- SearchRepository.searchSmart begin set diff --git a/server/src/repositories/search.repository.ts b/server/src/repositories/search.repository.ts index 61e0cc1e29..36ef7a27f1 100644 --- a/server/src/repositories/search.repository.ts +++ b/server/src/repositories/search.repository.ts @@ -8,7 +8,7 @@ import { AssetStatus, AssetType, AssetVisibility, VectorIndex } from 'src/enum'; import { probes } from 'src/repositories/database.repository'; import { DB } from 'src/schema'; import { AssetExifTable } from 'src/schema/tables/asset-exif.table'; -import { anyUuid, searchAssetBuilder } from 'src/utils/database'; +import { anyUuid, searchAssetBuilder, withExif } from 'src/utils/database'; import { paginationHelper } from 'src/utils/pagination'; import { isValidInteger } from 'src/validation'; @@ -129,6 +129,8 @@ export type SmartSearchOptions = SearchDateOptions & SearchPeopleOptions & SearchTagOptions; +export type LargeAssetSearchOptions = AssetSearchOptions & { minFileSize?: number }; + export interface FaceEmbeddingSearch extends SearchEmbeddingOptions { hasPerson?: boolean; numResults: number; @@ -237,6 +239,29 @@ export class SearchRepository { return rows; } + @GenerateSql({ + params: [ + 100, + { + takenAfter: DummyValue.DATE, + lensModel: DummyValue.STRING, + withStacked: true, + isFavorite: true, + userIds: [DummyValue.UUID], + }, + ], + }) + searchLargeAssets(size: number, options: LargeAssetSearchOptions) { + const orderDirection = (options.orderDirection?.toLowerCase() || 'desc') as OrderByDirection; + return searchAssetBuilder(this.db, options) + .selectAll('asset') + .$call(withExif) + .where('asset_exif.fileSizeInByte', '>', options.minFileSize || 0) + .orderBy('asset_exif.fileSizeInByte', orderDirection) + .limit(size) + .execute(); + } + @GenerateSql({ params: [ { page: 1, size: 200 }, diff --git a/server/src/services/search.service.ts b/server/src/services/search.service.ts index 1c75c4a434..b9391fed90 100644 --- a/server/src/services/search.service.ts +++ b/server/src/services/search.service.ts @@ -4,6 +4,7 @@ import { AssetMapOptions, AssetResponseDto, MapAsset, mapAsset } from 'src/dtos/ import { AuthDto } from 'src/dtos/auth.dto'; import { mapPerson, PersonResponseDto } from 'src/dtos/person.dto'; import { + LargeAssetSearchDto, mapPlaces, MetadataSearchDto, PlacesResponseDto, @@ -91,6 +92,16 @@ export class SearchService extends BaseService { return items.map((item) => mapAsset(item, { auth })); } + async searchLargeAssets(auth: AuthDto, dto: LargeAssetSearchDto): Promise { + if (dto.visibility === AssetVisibility.Locked) { + requireElevatedPermission(auth); + } + + const userIds = await this.getUserIdsToSearch(auth); + const items = await this.searchRepository.searchLargeAssets(dto.size || 250, { ...dto, userIds }); + return items.map((item) => mapAsset(item, { auth })); + } + async searchSmart(auth: AuthDto, dto: SmartSearchDto): Promise { if (dto.visibility === AssetVisibility.Locked) { requireElevatedPermission(auth); diff --git a/server/test/medium/specs/services/search.service.spec.ts b/server/test/medium/specs/services/search.service.spec.ts new file mode 100644 index 0000000000..517e6cc277 --- /dev/null +++ b/server/test/medium/specs/services/search.service.spec.ts @@ -0,0 +1,55 @@ +import { Kysely } from 'kysely'; +import { AccessRepository } from 'src/repositories/access.repository'; +import { DatabaseRepository } from 'src/repositories/database.repository'; +import { LoggingRepository } from 'src/repositories/logging.repository'; +import { PartnerRepository } from 'src/repositories/partner.repository'; +import { PersonRepository } from 'src/repositories/person.repository'; +import { SearchRepository } from 'src/repositories/search.repository'; +import { DB } from 'src/schema'; +import { SearchService } from 'src/services/search.service'; +import { newMediumService } from 'test/medium.factory'; +import { factory } from 'test/small.factory'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = (db?: Kysely) => { + return newMediumService(SearchService, { + database: db || defaultDatabase, + real: [AccessRepository, DatabaseRepository, SearchRepository, PartnerRepository, PersonRepository], + mock: [LoggingRepository], + }); +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe(SearchService.name, () => { + it('should work', () => { + const { sut } = setup(); + expect(sut).toBeDefined(); + }); + + it('should return assets', async () => { + const { sut, ctx } = setup(); + const { user } = await ctx.newUser(); + + const assets = []; + const sizes = [12_334, 599, 123_456]; + + for (let i = 0; i < sizes.length; i++) { + const { asset } = await ctx.newAsset({ ownerId: user.id }); + await ctx.newExif({ assetId: asset.id, fileSizeInByte: sizes[i] }); + assets.push(asset); + } + + const auth = factory.auth({ user: { id: user.id } }); + + await expect(sut.searchLargeAssets(auth, {})).resolves.toEqual([ + expect.objectContaining({ id: assets[2].id }), + expect.objectContaining({ id: assets[0].id }), + expect.objectContaining({ id: assets[1].id }), + ]); + }); +}); diff --git a/web/src/lib/components/utilities-page/large-assets/large-asset-data.svelte b/web/src/lib/components/utilities-page/large-assets/large-asset-data.svelte new file mode 100644 index 0000000000..71f3dbb5c4 --- /dev/null +++ b/web/src/lib/components/utilities-page/large-assets/large-asset-data.svelte @@ -0,0 +1,58 @@ + + +
+
+ +
+ +
+
+
{asset.originalFileName}
+ {getAssetResolution(asset)} +
+
+ {getFileSize(asset, 1)} +
+
+
diff --git a/web/src/lib/components/utilities-page/utilities-menu.svelte b/web/src/lib/components/utilities-page/utilities-menu.svelte index 7deddc6bee..5484ce4ea0 100644 --- a/web/src/lib/components/utilities-page/utilities-menu.svelte +++ b/web/src/lib/components/utilities-page/utilities-menu.svelte @@ -1,7 +1,7 @@ @@ -17,4 +17,13 @@ {$t('review_duplicates')} + + + + {$t('review_large_files')} + diff --git a/web/src/lib/constants.ts b/web/src/lib/constants.ts index b354989e17..f2de6d5deb 100644 --- a/web/src/lib/constants.ts +++ b/web/src/lib/constants.ts @@ -51,6 +51,7 @@ export enum AppRoute { UTILITIES = '/utilities', DUPLICATES = '/utilities/duplicates', + LARGE_FILES = '/utilities/large-files', FOLDERS = '/folders', TAGS = '/tags', diff --git a/web/src/lib/utils/asset-utils.ts b/web/src/lib/utils/asset-utils.ts index d3feff8302..7267520549 100644 --- a/web/src/lib/utils/asset-utils.ts +++ b/web/src/lib/utils/asset-utils.ts @@ -275,9 +275,9 @@ export function isFlipped(orientation?: string | null) { return value && (isRotated270CW(value) || isRotated90CW(value)); } -export function getFileSize(asset: AssetResponseDto): string { +export function getFileSize(asset: AssetResponseDto, maxPrecision = 4): string { const size = asset.exifInfo?.fileSizeInByte || 0; - return size > 0 ? getByteUnitString(size, undefined, 4) : 'Invalid Data'; + return size > 0 ? getByteUnitString(size, undefined, maxPrecision) : 'Invalid Data'; } export function getAssetResolution(asset: AssetResponseDto): string { diff --git a/web/src/routes/(user)/utilities/large-files/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/utilities/large-files/[[photos=photos]]/[[assetId=id]]/+page.svelte new file mode 100644 index 0000000000..75ac4fab93 --- /dev/null +++ b/web/src/routes/(user)/utilities/large-files/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -0,0 +1,89 @@ + + + +
+ {#if assets && data.assets.length > 0} + {#each assets as asset (asset.id)} + setAsset(asset)} /> + {/each} + {:else} +

+ {$t('no_assets_to_show')} +

+ {/if} +
+
+ +{#if $showAssetViewer} + {#await import('$lib/components/asset-viewer/asset-viewer.svelte') then { default: AssetViewer }} + + 1} + {onNext} + {onPrevious} + {onRandom} + {onAction} + onClose={() => { + assetViewingStore.showAssetViewer(false); + handlePromiseError(navigate({ targetRoute: 'current', assetId: null })); + }} + /> + + {/await} +{/if} diff --git a/web/src/routes/(user)/utilities/large-files/[[photos=photos]]/[[assetId=id]]/+page.ts b/web/src/routes/(user)/utilities/large-files/[[photos=photos]]/[[assetId=id]]/+page.ts new file mode 100644 index 0000000000..6780fdb023 --- /dev/null +++ b/web/src/routes/(user)/utilities/large-files/[[photos=photos]]/[[assetId=id]]/+page.ts @@ -0,0 +1,17 @@ +import { authenticate } from '$lib/utils/auth'; +import { getFormatter } from '$lib/utils/i18n'; +import { searchLargeAssets } from '@immich/sdk'; +import type { PageLoad } from './$types'; + +export const load = (async ({ url }) => { + await authenticate(url); + const assets = await searchLargeAssets({ minFileSize: 0 }); + const $t = await getFormatter(); + + return { + assets, + meta: { + title: $t('large_files'), + }, + }; +}) satisfies PageLoad; From 1804a8fe586b05dc0c5565110d58b37f88110382 Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Mon, 28 Jul 2025 18:46:34 -0500 Subject: [PATCH 093/748] fix: openapi spec (#20378) --- open-api/immich-openapi-specs.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index b8089083d1..1e9fddf79d 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -5621,7 +5621,8 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "asset.read" } }, "/search/metadata": { From fbbb6af27a0e03fa38bb41d423f47ae23d88619a Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Mon, 28 Jul 2025 20:56:22 -0400 Subject: [PATCH 094/748] chore: update open-api (#20376) From cfae134ecfbe98e598c670a9f5843db05bc92d8a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 09:47:30 +0000 Subject: [PATCH 095/748] fix(deps): update typescript-projects (#20388) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Daniel Dietzler --- cli/package-lock.json | 142 +++---- e2e/package-lock.json | 171 ++++----- server/package-lock.json | 807 ++++++++------------------------------- server/package.json | 2 +- web/package-lock.json | 632 +++--------------------------- 5 files changed, 367 insertions(+), 1387 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index 3cd4da0480..85e17e6c40 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1365,17 +1365,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", - "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", + "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/type-utils": "8.37.0", - "@typescript-eslint/utils": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/type-utils": "8.38.0", + "@typescript-eslint/utils": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -1389,7 +1389,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.37.0", + "@typescript-eslint/parser": "^8.38.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } @@ -1405,16 +1405,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", - "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", + "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4" }, "engines": { @@ -1430,14 +1430,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", - "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", + "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.37.0", - "@typescript-eslint/types": "^8.37.0", + "@typescript-eslint/tsconfig-utils": "^8.38.0", + "@typescript-eslint/types": "^8.38.0", "debug": "^4.3.4" }, "engines": { @@ -1452,14 +1452,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", - "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", + "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0" + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1470,9 +1470,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", - "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", + "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", "dev": true, "license": "MIT", "engines": { @@ -1487,15 +1487,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", - "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", + "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -1512,9 +1512,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", - "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", + "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", "dev": true, "license": "MIT", "engines": { @@ -1526,16 +1526,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", - "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", + "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.37.0", - "@typescript-eslint/tsconfig-utils": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/project-service": "8.38.0", + "@typescript-eslint/tsconfig-utils": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1581,16 +1581,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", - "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", + "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0" + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1605,13 +1605,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", - "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", + "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/types": "8.38.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -2367,9 +2367,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", - "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", "bin": { @@ -2383,9 +2383,9 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.1.tgz", - "integrity": "sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw==", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.3.tgz", + "integrity": "sha512-NAdMYww51ehKfDyDhv59/eIItUVzU0Io9H2E8nHNGKEeeqlnci+1gCvrHib6EmZdf6GxF+LCV5K7UC65Ezvw7w==", "dev": true, "license": "MIT", "dependencies": { @@ -3548,15 +3548,15 @@ } }, "node_modules/prettier-plugin-organize-imports": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.1.0.tgz", - "integrity": "sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.2.0.tgz", + "integrity": "sha512-Zdy27UhlmyvATZi67BTnLcKTo8fm6Oik59Sz6H64PgZJVs6NJpPD1mT240mmJn62c98/QaL+r3kx9Q3gRpDajg==", "dev": true, "license": "MIT", "peerDependencies": { "prettier": ">=2.0", "typescript": ">=2.9", - "vue-tsc": "^2.1.0" + "vue-tsc": "^2.1.0 || 3" }, "peerDependenciesMeta": { "vue-tsc": { @@ -4139,16 +4139,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.37.0.tgz", - "integrity": "sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.38.0.tgz", + "integrity": "sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.37.0", - "@typescript-eslint/parser": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0" + "@typescript-eslint/eslint-plugin": "8.38.0", + "@typescript-eslint/parser": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 5da041b37b..0d65d3d77f 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -2125,17 +2125,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", - "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", + "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/type-utils": "8.37.0", - "@typescript-eslint/utils": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/type-utils": "8.38.0", + "@typescript-eslint/utils": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -2149,7 +2149,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.37.0", + "@typescript-eslint/parser": "^8.38.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } @@ -2165,16 +2165,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", - "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", + "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4" }, "engines": { @@ -2190,14 +2190,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", - "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", + "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.37.0", - "@typescript-eslint/types": "^8.37.0", + "@typescript-eslint/tsconfig-utils": "^8.38.0", + "@typescript-eslint/types": "^8.38.0", "debug": "^4.3.4" }, "engines": { @@ -2212,14 +2212,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", - "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", + "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0" + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2230,9 +2230,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", - "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", + "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", "dev": true, "license": "MIT", "engines": { @@ -2247,15 +2247,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", - "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", + "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -2272,9 +2272,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", - "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", + "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", "dev": true, "license": "MIT", "engines": { @@ -2286,16 +2286,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", - "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", + "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.37.0", - "@typescript-eslint/tsconfig-utils": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/project-service": "8.38.0", + "@typescript-eslint/tsconfig-utils": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -2341,16 +2341,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", - "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", + "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0" + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2365,13 +2365,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", - "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", + "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/types": "8.38.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -3525,9 +3525,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", - "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", "bin": { @@ -3541,9 +3541,9 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.1.tgz", - "integrity": "sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw==", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.3.tgz", + "integrity": "sha512-NAdMYww51ehKfDyDhv59/eIItUVzU0Io9H2E8nHNGKEeeqlnci+1gCvrHib6EmZdf6GxF+LCV5K7UC65Ezvw7w==", "dev": true, "license": "MIT", "dependencies": { @@ -3983,15 +3983,16 @@ } }, "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -5708,15 +5709,15 @@ } }, "node_modules/prettier-plugin-organize-imports": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.1.0.tgz", - "integrity": "sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.2.0.tgz", + "integrity": "sha512-Zdy27UhlmyvATZi67BTnLcKTo8fm6Oik59Sz6H64PgZJVs6NJpPD1mT240mmJn62c98/QaL+r3kx9Q3gRpDajg==", "dev": true, "license": "MIT", "peerDependencies": { "prettier": ">=2.0", "typescript": ">=2.9", - "vue-tsc": "^2.1.0" + "vue-tsc": "^2.1.0 || 3" }, "peerDependenciesMeta": { "vue-tsc": { @@ -6469,35 +6470,35 @@ } }, "node_modules/superagent": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.2.tgz", - "integrity": "sha512-vWMq11OwWCC84pQaFPzF/VO3BrjkCeewuvJgt1jfV0499Z1QSAWN4EqfMM5WlFDDX9/oP8JjlDKpblrmEoyu4Q==", + "version": "10.2.3", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.3.tgz", + "integrity": "sha512-y/hkYGeXAj7wUMjxRbB21g/l6aAEituGXM9Rwl4o20+SX3e8YOSV6BxFXl+dL3Uk0mjSL3kCbNkwURm8/gEDig==", "dev": true, "license": "MIT", "dependencies": { - "component-emitter": "^1.3.0", + "component-emitter": "^1.3.1", "cookiejar": "^2.1.4", - "debug": "^4.3.4", + "debug": "^4.3.7", "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", + "form-data": "^4.0.4", "formidable": "^3.5.4", "methods": "^1.1.2", "mime": "2.6.0", - "qs": "^6.11.0" + "qs": "^6.11.2" }, "engines": { "node": ">=14.18.0" } }, "node_modules/supertest": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.3.tgz", - "integrity": "sha512-ORY0gPa6ojmg/C74P/bDoS21WL6FMXq5I8mawkEz30/zkwdu0gOeqstFy316vHG6OKxqQ+IbGneRemHI8WraEw==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.4.tgz", + "integrity": "sha512-tjLPs7dVyqgItVFirHYqe2T+MfWc2VOBQ8QFKKbWTA3PU7liZR8zoSpAi/C1k1ilm9RsXIKYf197oap9wXGVYg==", "dev": true, "license": "MIT", "dependencies": { "methods": "^1.1.2", - "superagent": "^10.2.2" + "superagent": "^10.2.3" }, "engines": { "node": ">=14.18.0" @@ -6817,16 +6818,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.37.0.tgz", - "integrity": "sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.38.0.tgz", + "integrity": "sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.37.0", - "@typescript-eslint/parser": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0" + "@typescript-eslint/eslint-plugin": "8.38.0", + "@typescript-eslint/parser": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/server/package-lock.json b/server/package-lock.json index 5b7ffcdcaf..b5f5889346 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -54,7 +54,7 @@ "i18n-iso-countries": "^7.6.0", "ioredis": "^5.3.2", "js-yaml": "^4.1.0", - "kysely": "^0.28.2", + "kysely": "0.28.2", "kysely-postgres-js": "^2.0.0", "lodash": "^4.17.21", "luxon": "^3.4.2", @@ -415,22 +415,6 @@ "node": ">= 8" } }, - "node_modules/@asamuzakjp/css-color": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", - "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@csstools/css-calc": "^2.1.3", - "@csstools/css-color-parser": "^3.0.9", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "lru-cache": "^10.4.3" - } - }, "node_modules/@babel/code-frame": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", @@ -582,131 +566,6 @@ "node": ">=0.1.90" } }, - "node_modules/@csstools/color-helpers": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", - "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@csstools/css-calc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", - "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-color-parser": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.10.tgz", - "integrity": "sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@csstools/color-helpers": "^5.0.2", - "@csstools/css-calc": "^2.1.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-parser-algorithms": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", - "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-tokenizer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", - "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - } - }, "node_modules/@emnapi/runtime": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", @@ -6605,9 +6464,9 @@ "license": "MIT" }, "node_modules/@swc/core": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.0.tgz", - "integrity": "sha512-7Fh16ZH/Rj3Di720if+sw9BictD4N5kbTpsyDC+URXhvsZ7qRt1lH7PaeIQYyJJQHwFhoKpwwGxfGU9SHgPLdw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.2.tgz", + "integrity": "sha512-YWqn+0IKXDhqVLKoac4v2tV6hJqB/wOh8/Br8zjqeqBkKa77Qb0Kw2i7LOFzjFNZbZaPH6AlMGlBwNrxaauaAg==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -6623,16 +6482,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.13.0", - "@swc/core-darwin-x64": "1.13.0", - "@swc/core-linux-arm-gnueabihf": "1.13.0", - "@swc/core-linux-arm64-gnu": "1.13.0", - "@swc/core-linux-arm64-musl": "1.13.0", - "@swc/core-linux-x64-gnu": "1.13.0", - "@swc/core-linux-x64-musl": "1.13.0", - "@swc/core-win32-arm64-msvc": "1.13.0", - "@swc/core-win32-ia32-msvc": "1.13.0", - "@swc/core-win32-x64-msvc": "1.13.0" + "@swc/core-darwin-arm64": "1.13.2", + "@swc/core-darwin-x64": "1.13.2", + "@swc/core-linux-arm-gnueabihf": "1.13.2", + "@swc/core-linux-arm64-gnu": "1.13.2", + "@swc/core-linux-arm64-musl": "1.13.2", + "@swc/core-linux-x64-gnu": "1.13.2", + "@swc/core-linux-x64-musl": "1.13.2", + "@swc/core-win32-arm64-msvc": "1.13.2", + "@swc/core-win32-ia32-msvc": "1.13.2", + "@swc/core-win32-x64-msvc": "1.13.2" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" @@ -6644,9 +6503,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.0.tgz", - "integrity": "sha512-SkmR9u7MHDu2X8hf7SjZTmsAfQTmel0mi+TJ7AGtufLwGySv6pwQfJ/CIJpcPxYENVqDJAFnDrHaKV8mgA6kxQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.2.tgz", + "integrity": "sha512-44p7ivuLSGFJ15Vly4ivLJjg3ARo4879LtEBAabcHhSZygpmkP8eyjyWxrH3OxkY1eRZSIJe8yRZPFw4kPXFPw==", "cpu": [ "arm64" ], @@ -6661,9 +6520,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.13.0.tgz", - "integrity": "sha512-15/SyDjXRtFJ09fYHBXUXrj4tpiSpCkjgsF1z3/sSpHH1POWpQUQzxmFyomPQVZ/SsDqP18WGH09Vph4Qriuiw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.13.2.tgz", + "integrity": "sha512-Lb9EZi7X2XDAVmuUlBm2UvVAgSCbD3qKqDCxSI4jEOddzVOpNCnyZ/xEampdngUIyDDhhJLYU9duC+Mcsv5Y+A==", "cpu": [ "x64" ], @@ -6678,9 +6537,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.13.0.tgz", - "integrity": "sha512-AHauVHZQEJI/dCZQg6VYNNQ6HROz8dSOnCSheXzzBw1DGWo77BlcxRP0fF0jaAXM9WNqtCUOY1HiJ9ohkAE61Q==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.13.2.tgz", + "integrity": "sha512-9TDe/92ee1x57x+0OqL1huG4BeljVx0nWW4QOOxp8CCK67Rpc/HHl2wciJ0Kl9Dxf2NvpNtkPvqj9+BUmM9WVA==", "cpu": [ "arm" ], @@ -6695,9 +6554,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.0.tgz", - "integrity": "sha512-qyZmBZF7asF6954/x7yn6R7Bzd45KRG05rK2atIF9J3MTa8az7vubP1Q3BWmmss1j8699DELpbuoJucGuhsNXw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.2.tgz", + "integrity": "sha512-KJUSl56DBk7AWMAIEcU83zl5mg3vlQYhLELhjwRFkGFMvghQvdqQ3zFOYa4TexKA7noBZa3C8fb24rI5sw9Exg==", "cpu": [ "arm64" ], @@ -6712,9 +6571,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.13.0.tgz", - "integrity": "sha512-whskQCOUlLQT7MjnronpHmyHegBka5ig9JkQvecbqhWzRfdwN+c2xTJs3kQsWy2Vc2f1hcL3D8hGIwY5TwPxMQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.13.2.tgz", + "integrity": "sha512-teU27iG1oyWpNh9CzcGQ48ClDRt/RCem7mYO7ehd2FY102UeTws2+OzLESS1TS1tEZipq/5xwx3FzbVgiolCiQ==", "cpu": [ "arm64" ], @@ -6729,9 +6588,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.13.0.tgz", - "integrity": "sha512-51n4P4nv6rblXyH3zCEktvmR9uSAZ7+zbfeby0sxbj8LS/IKuVd7iCwD5dwMj4CxG9Fs+HgjN73dLQF/OerHhg==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.13.2.tgz", + "integrity": "sha512-dRPsyPyqpLD0HMRCRpYALIh4kdOir8pPg4AhNQZLehKowigRd30RcLXGNVZcc31Ua8CiPI4QSgjOIxK+EQe4LQ==", "cpu": [ "x64" ], @@ -6746,9 +6605,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.13.0.tgz", - "integrity": "sha512-VMqelgvnXs27eQyhDf1S2O2MxSdchIH7c1tkxODRtu9eotcAeniNNgqqLjZ5ML0MGeRk/WpbsAY/GWi7eSpiHw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.13.2.tgz", + "integrity": "sha512-CCxETW+KkYEQDqz1SYC15YIWYheqFC+PJVOW76Maa/8yu8Biw+HTAcblKf2isrlUtK8RvrQN94v3UXkC2NzCEw==", "cpu": [ "x64" ], @@ -6763,9 +6622,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.13.0.tgz", - "integrity": "sha512-NLJmseWJngWeENgat+O/WB4ptNxtx2X4OfPnSG5a/A4sxcn2E4jq91OPvbeUQwDkH+ZQWKXmbXFzt7Nn661QYA==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.13.2.tgz", + "integrity": "sha512-Wv/QTA6PjyRLlmKcN6AmSI4jwSMRl0VTLGs57PHTqYRwwfwd7y4s2fIPJVBNbAlXd795dOEP6d/bGSQSyhOX3A==", "cpu": [ "arm64" ], @@ -6780,9 +6639,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.13.0.tgz", - "integrity": "sha512-UBfwrp0xW37KQGTA08mwrCLIm1ZKy6pXK8IVwou7BvhMgrItRNweTGyUrCnvDLUfyYFuJCmzcEaJ3NudtctD6g==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.13.2.tgz", + "integrity": "sha512-PuCdtNynEkUNbUXX/wsyUC+t4mamIU5y00lT5vJcAvco3/r16Iaxl5UCzhXYaWZSNVZMzPp9qN8NlSL8M5pPxw==", "cpu": [ "ia32" ], @@ -6797,9 +6656,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.13.0.tgz", - "integrity": "sha512-BAB1P7Z/y2EENsfsPytPnjIyBVRZN2WULY+s3ozW4QkGmYHde6XXG28n0ABTHhcIOmmR2VzM+uaW1x48laSimw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.13.2.tgz", + "integrity": "sha512-qlmMkFZJus8cYuBURx1a3YAG2G7IW44i+FEYV5/32ylKkzGNAr9tDJSA53XNnNXkAB5EXSPsOz7bn5C3JlEtdQ==", "cpu": [ "x64" ], @@ -6820,18 +6679,6 @@ "dev": true, "license": "Apache-2.0" }, - "node_modules/@swc/helpers": { - "version": "0.5.17", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", - "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.8.0" - } - }, "node_modules/@swc/types": { "version": "0.1.23", "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.23.tgz", @@ -6843,23 +6690,23 @@ } }, "node_modules/@testcontainers/postgresql": { - "version": "11.2.1", - "resolved": "https://registry.npmjs.org/@testcontainers/postgresql/-/postgresql-11.2.1.tgz", - "integrity": "sha512-u0XLsjUmAHaUmB9Q1bitBu8uoxRKteDI65S5/zpJ6TeZabx9qB4EENwKqzuqEwOCzzlko9at7ZY4frwRcchgvA==", + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/@testcontainers/postgresql/-/postgresql-11.4.0.tgz", + "integrity": "sha512-WiKsz3Np5twNZGp2kgatqGaE/KqNR271CPwvIgAvFyN7E581P34glQljM4iLfxdv1XpzVYGWRO6PbQAVDbehBQ==", "dev": true, "license": "MIT", "dependencies": { - "testcontainers": "^11.2.1" + "testcontainers": "^11.4.0" } }, "node_modules/@testcontainers/redis": { - "version": "11.2.1", - "resolved": "https://registry.npmjs.org/@testcontainers/redis/-/redis-11.2.1.tgz", - "integrity": "sha512-Q5j+irNw0BLec3he30s2E0fhE06Zr9ROVutkyKUgcwQoZxEVW3xV69ke2AFCT5teEcIvTKqevObN4UDkq33Qow==", + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/@testcontainers/redis/-/redis-11.4.0.tgz", + "integrity": "sha512-w+2VpYt5xAEYbsdhITgwDMif+5Atae+q0ifG/ZrSUZXK528CzqsfnxIgwrZWFnLDCqk1mVNgG4mXdD8VDGd38w==", "dev": true, "license": "MIT", "dependencies": { - "testcontainers": "^11.2.1" + "testcontainers": "^11.4.0" } }, "node_modules/@tokenizer/inflate": { @@ -7294,9 +7141,9 @@ } }, "node_modules/@types/picomatch": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-4.0.0.tgz", - "integrity": "sha512-J1Bng+wlyEERWSgJQU1Pi0HObCLVcr994xT/M+1wcl/yNRTGBupsCxthgkdYG+GCOMaQH7iSVUY3LJVBBqG7MQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-4.0.1.tgz", + "integrity": "sha512-dLqxmi5VJRC9XTvc/oaTtk+bDb4RRqxLZPZ3jIpYBHEnDXX8lu02w2yWI6NsPPsELuVK298Z2iR8jgoWKRdUVQ==", "dev": true, "license": "MIT" }, @@ -7478,17 +7325,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", - "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", + "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/type-utils": "8.37.0", - "@typescript-eslint/utils": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/type-utils": "8.38.0", + "@typescript-eslint/utils": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -7502,7 +7349,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.37.0", + "@typescript-eslint/parser": "^8.38.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } @@ -7518,16 +7365,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", - "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", + "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4" }, "engines": { @@ -7543,14 +7390,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", - "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", + "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.37.0", - "@typescript-eslint/types": "^8.37.0", + "@typescript-eslint/tsconfig-utils": "^8.38.0", + "@typescript-eslint/types": "^8.38.0", "debug": "^4.3.4" }, "engines": { @@ -7565,14 +7412,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", - "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", + "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0" + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7583,9 +7430,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", - "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", + "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", "dev": true, "license": "MIT", "engines": { @@ -7600,15 +7447,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", - "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", + "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -7625,9 +7472,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", - "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", + "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", "dev": true, "license": "MIT", "engines": { @@ -7639,16 +7486,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", - "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", + "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.37.0", - "@typescript-eslint/tsconfig-utils": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/project-service": "8.38.0", + "@typescript-eslint/tsconfig-utils": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -7694,16 +7541,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", - "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", + "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0" + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7718,13 +7565,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", - "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", + "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/types": "8.38.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -8884,9 +8731,9 @@ } }, "node_modules/bullmq": { - "version": "5.56.4", - "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.56.4.tgz", - "integrity": "sha512-5wSHd0oXs2jS6P+6tay/01Iz0cWRK8iYcscKtpS/GewEq0bJZwbkMZc77GJVOT9SP+UQuXA2y+pQTdCQJel7kQ==", + "version": "5.56.5", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.56.5.tgz", + "integrity": "sha512-nhcVxoE9Y0YUuNYtvaD+N0Bk2kqcU+rXzJwdQIr8i8qC/fxoghwUYb9a+CidTv24pi1eqstLnBoa8xkR/P7Mdw==", "license": "MIT", "dependencies": { "cron-parser": "^4.9.0", @@ -9858,22 +9705,6 @@ "node": ">=4" } }, - "node_modules/cssstyle": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.5.0.tgz", - "integrity": "sha512-/7gw8TGrvH/0g564EnhgFZogTMVe+lifpB7LWU+PEsiq5o83TUXR3fDbzTRXOJhoJwck5IS9ez3Em5LNMMO2aw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@asamuzakjp/css-color": "^3.2.0", - "rrweb-cssom": "^0.8.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -9881,22 +9712,6 @@ "dev": true, "license": "MIT" }, - "node_modules/data-urls": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", - "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/dayjs": { "version": "1.11.13", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", @@ -9932,15 +9747,6 @@ } } }, - "node_modules/decimal.js": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", - "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -10734,9 +10540,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", - "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", "bin": { @@ -10750,9 +10556,9 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.1.tgz", - "integrity": "sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw==", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.3.tgz", + "integrity": "sha512-NAdMYww51ehKfDyDhv59/eIItUVzU0Io9H2E8nHNGKEeeqlnci+1gCvrHib6EmZdf6GxF+LCV5K7UC65Ezvw7w==", "dev": true, "license": "MIT", "dependencies": { @@ -11491,9 +11297,9 @@ } }, "node_modules/form-data": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", - "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -12041,21 +11847,6 @@ "he": "bin/he" } }, - "node_modules/html-encoding-sniffer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", - "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "whatwg-encoding": "^3.1.1" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -12502,15 +12293,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/is-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", @@ -12720,48 +12502,6 @@ "dev": true, "license": "MIT" }, - "node_modules/jsdom": { - "version": "26.1.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", - "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "cssstyle": "^4.2.1", - "data-urls": "^5.0.0", - "decimal.js": "^10.5.0", - "html-encoding-sniffer": "^4.0.0", - "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.6", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.16", - "parse5": "^7.2.1", - "rrweb-cssom": "^0.8.0", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^5.1.1", - "w3c-xmlserializer": "^5.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^3.1.1", - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.1.1", - "ws": "^8.18.0", - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "canvas": "^3.0.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -14112,15 +13852,6 @@ "set-blocking": "^2.0.0" } }, - "node_modules/nwsapi": { - "version": "2.2.20", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.20.tgz", - "integrity": "sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/nypm": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.0.tgz", @@ -14401,36 +14132,6 @@ "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==", "license": "MIT" }, - "node_modules/parse5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "entities": "^6.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5/node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/parseley": { "version": "0.12.1", "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.1.tgz", @@ -15057,15 +14758,15 @@ } }, "node_modules/prettier-plugin-organize-imports": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.1.0.tgz", - "integrity": "sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.2.0.tgz", + "integrity": "sha512-Zdy27UhlmyvATZi67BTnLcKTo8fm6Oik59Sz6H64PgZJVs6NJpPD1mT240mmJn62c98/QaL+r3kx9Q3gRpDajg==", "dev": true, "license": "MIT", "peerDependencies": { "prettier": ">=2.0", "typescript": ">=2.9", - "vue-tsc": "^2.1.0" + "vue-tsc": "^2.1.0 || 3" }, "peerDependenciesMeta": { "vue-tsc": { @@ -15385,9 +15086,9 @@ } }, "node_modules/react-email": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/react-email/-/react-email-4.2.3.tgz", - "integrity": "sha512-LUKyk9nNVFuTqAyp4yCEQFQjBe+s8nl3VauMWuOhBZ4VhGnimbrnv01U8yD2YwzaHKtytS0U659x5dc/0+xu+Q==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/react-email/-/react-email-4.2.4.tgz", + "integrity": "sha512-r5x1nlWUXKZWoIU7l9jx5jkq43RuDUlroH0FRA5MMrCOaLqAfg3vOsAxAadNkG47L0iTeDkkqTKzaV6dTaYf/A==", "license": "MIT", "dependencies": { "@babel/parser": "^7.27.0", @@ -16000,15 +15701,6 @@ "node": ">= 18" } }, - "node_modules/rrweb-cssom": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", - "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -16099,21 +15791,6 @@ "postcss": "^8.3.11" } }, - "node_modules/saxes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true, - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=v12.22.7" - } - }, "node_modules/scheduler": { "version": "0.26.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", @@ -17183,35 +16860,35 @@ } }, "node_modules/superagent": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.2.tgz", - "integrity": "sha512-vWMq11OwWCC84pQaFPzF/VO3BrjkCeewuvJgt1jfV0499Z1QSAWN4EqfMM5WlFDDX9/oP8JjlDKpblrmEoyu4Q==", + "version": "10.2.3", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.3.tgz", + "integrity": "sha512-y/hkYGeXAj7wUMjxRbB21g/l6aAEituGXM9Rwl4o20+SX3e8YOSV6BxFXl+dL3Uk0mjSL3kCbNkwURm8/gEDig==", "dev": true, "license": "MIT", "dependencies": { - "component-emitter": "^1.3.0", + "component-emitter": "^1.3.1", "cookiejar": "^2.1.4", - "debug": "^4.3.4", + "debug": "^4.3.7", "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", + "form-data": "^4.0.4", "formidable": "^3.5.4", "methods": "^1.1.2", "mime": "2.6.0", - "qs": "^6.11.0" + "qs": "^6.11.2" }, "engines": { "node": ">=14.18.0" } }, "node_modules/supertest": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.3.tgz", - "integrity": "sha512-ORY0gPa6ojmg/C74P/bDoS21WL6FMXq5I8mawkEz30/zkwdu0gOeqstFy316vHG6OKxqQ+IbGneRemHI8WraEw==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.4.tgz", + "integrity": "sha512-tjLPs7dVyqgItVFirHYqe2T+MfWc2VOBQ8QFKKbWTA3PU7liZR8zoSpAi/C1k1ilm9RsXIKYf197oap9wXGVYg==", "dev": true, "license": "MIT", "dependencies": { "methods": "^1.1.2", - "superagent": "^10.2.2" + "superagent": "^10.2.3" }, "engines": { "node": ">=14.18.0" @@ -17260,15 +16937,6 @@ "node": ">=0.10" } }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/synckit": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.8.tgz", @@ -17787,9 +17455,9 @@ } }, "node_modules/testcontainers": { - "version": "11.2.1", - "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-11.2.1.tgz", - "integrity": "sha512-KJALGi8ButKDZgzHr0PtJUVNBOSlSFncumZ34MCQTN4VEU9AK4tWTn9gCcAFzG4zBmzzC2aEbHMFUujqkbDvBg==", + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-11.4.0.tgz", + "integrity": "sha512-eX5nc/Fi5I0LHqwxw6BuUvWNfdl+M2sKX6fX/47RP89Xs5nU6smd0iD7dpFogxy8/wACjlucLoutJc7b5mtq7w==", "dev": true, "license": "MIT", "dependencies": { @@ -17807,7 +17475,7 @@ "ssh-remote-port-forward": "^1.0.4", "tar-fs": "^3.1.0", "tmp": "^0.2.3", - "undici": "^7.11.0" + "undici": "^7.12.0" } }, "node_modules/testcontainers/node_modules/tmp": { @@ -17929,30 +17597,6 @@ "node": ">=14.0.0" } }, - "node_modules/tldts": { - "version": "6.1.86", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", - "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "tldts-core": "^6.1.86" - }, - "bin": { - "tldts": "bin/cli.js" - } - }, - "node_modules/tldts-core": { - "version": "6.1.86", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", - "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -18012,36 +17656,6 @@ "node": ">=6" } }, - "node_modules/tough-cookie": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", - "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", - "dev": true, - "license": "BSD-3-Clause", - "optional": true, - "peer": true, - "dependencies": { - "tldts": "^6.1.32" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/tr46": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", - "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -18435,16 +18049,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.37.0.tgz", - "integrity": "sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.38.0.tgz", + "integrity": "sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.37.0", - "@typescript-eslint/parser": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0" + "@typescript-eslint/eslint-plugin": "8.38.0", + "@typescript-eslint/parser": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -18558,9 +18172,9 @@ } }, "node_modules/undici": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.11.0.tgz", - "integrity": "sha512-heTSIac3iLhsmZhUCjyS3JQEkZELateufzZuBaVM5RHXdSBMb1LPMQf5x+FH7qjsZYDP0ttAc3nnVpUB+wYbOg==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.12.0.tgz", + "integrity": "sha512-GrKEsc3ughskmGA9jevVlIOPMiiAHJ4OFUtaAH+NhfTUSiZ1wMPIQqQvAJUrJspFXJt3EBWgpAeoHEDVT1IBug==", "dev": true, "license": "MIT", "engines": { @@ -18974,21 +18588,6 @@ } } }, - "node_modules/w3c-xmlserializer": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", - "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/watchpack": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", @@ -19012,18 +18611,6 @@ "defaults": "^1.0.3" } }, - "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "peer": true, - "engines": { - "node": ">=12" - } - }, "node_modules/webpack": { "version": "5.99.6", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.6.tgz", @@ -19220,49 +18807,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/whatwg-encoding": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", - "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/whatwg-mimetype": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", - "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/whatwg-url": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", - "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "tr46": "^5.1.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -19401,51 +18945,6 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, - "node_modules/ws": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", - "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xml-name-validator": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", - "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/server/package.json b/server/package.json index 67945b3dbe..38d4f7b5f7 100644 --- a/server/package.json +++ b/server/package.json @@ -79,7 +79,7 @@ "i18n-iso-countries": "^7.6.0", "ioredis": "^5.3.2", "js-yaml": "^4.1.0", - "kysely": "^0.28.2", + "kysely": "0.28.2", "kysely-postgres-js": "^2.0.0", "lodash": "^4.17.21", "luxon": "^3.4.2", diff --git a/web/package-lock.json b/web/package-lock.json index ab515030cc..65a016ed02 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1791,50 +1791,50 @@ } }, "node_modules/@photo-sphere-viewer/core": { - "version": "5.13.3", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/core/-/core-5.13.3.tgz", - "integrity": "sha512-6Fjkx2fTUFSmoy6jJxXn+FrwNlMtTi6M8ErEZkQgn7ULenMpIUx6tdFz3l85NM7Br/Rj4CK8IyUo8Le1eI1Fdg==", + "version": "5.13.4", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/core/-/core-5.13.4.tgz", + "integrity": "sha512-leVQL6gG9wTF+uvCFarHUcr8mzafCZ/GLzauksYQJfiqDVRFSAJNXnTOy7RH9otToluEdjN1hsN1f9HQy+rLYg==", "license": "MIT", "dependencies": { "three": "^0.175.0" } }, "node_modules/@photo-sphere-viewer/equirectangular-video-adapter": { - "version": "5.13.3", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/equirectangular-video-adapter/-/equirectangular-video-adapter-5.13.3.tgz", - "integrity": "sha512-Gl63vt9ztRHrw2lxDF4KTPsndDbIByg+hUsaMtEAuc304wLUKj6GIapAh71eCyDqvPBhLoTZ5+4RX/TPafnCGg==", + "version": "5.13.4", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/equirectangular-video-adapter/-/equirectangular-video-adapter-5.13.4.tgz", + "integrity": "sha512-OdTOKxFunP56FNoPR47mQp7V1WHvV4eiow3qtyJjAgLeU8T2q3kivLuH1kMZN2yTAJaXab+VBXzA/YChiHZ6mQ==", "license": "MIT", "peerDependencies": { - "@photo-sphere-viewer/core": "5.13.3", - "@photo-sphere-viewer/video-plugin": "5.13.3" + "@photo-sphere-viewer/core": "5.13.4", + "@photo-sphere-viewer/video-plugin": "5.13.4" } }, "node_modules/@photo-sphere-viewer/resolution-plugin": { - "version": "5.13.3", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/resolution-plugin/-/resolution-plugin-5.13.3.tgz", - "integrity": "sha512-spSBm7OXDisnw5Fx4+JPR510nb+nGFZf2aecPRhP23GZyxKPg82i1PYoDJgRCWKTlvz8Yq2eilKdAtncmHWgSA==", + "version": "5.13.4", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/resolution-plugin/-/resolution-plugin-5.13.4.tgz", + "integrity": "sha512-HRBC5zYmpNoo/joKZzXbxn7jwoh3tdtTJFXzHxYPV51ELDclRNmzhmqEaZeVkrFHr4bRF5ow3AOjxiMtu1xQxA==", "license": "MIT", "peerDependencies": { - "@photo-sphere-viewer/core": "5.13.3", - "@photo-sphere-viewer/settings-plugin": "5.13.3" + "@photo-sphere-viewer/core": "5.13.4", + "@photo-sphere-viewer/settings-plugin": "5.13.4" } }, "node_modules/@photo-sphere-viewer/settings-plugin": { - "version": "5.13.3", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/settings-plugin/-/settings-plugin-5.13.3.tgz", - "integrity": "sha512-x/KulP3UxoawDS8MuGrZU3DznrzpaYqQ2BQnxlaOVItewrgCdS8WRG18E7nBo+2lfZGDDwGhRsSp/IRUOwScRg==", + "version": "5.13.4", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/settings-plugin/-/settings-plugin-5.13.4.tgz", + "integrity": "sha512-As1nmlsfnjKBFQOWPVQLH1+dJ+s62MdEb6Jvlm16+3fUVHF4CBWRTJZyBKejLiu4xjbDxrE8v5ZHDLvG6ButiQ==", "license": "MIT", "peerDependencies": { - "@photo-sphere-viewer/core": "5.13.3" + "@photo-sphere-viewer/core": "5.13.4" } }, "node_modules/@photo-sphere-viewer/video-plugin": { - "version": "5.13.3", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/video-plugin/-/video-plugin-5.13.3.tgz", - "integrity": "sha512-npNeknhV3jLJLKbbFzaLRVRROiN2POSdHLnV0R6QNiF7rQURC0Cs7Yuztl2nFJWUBOS3MiO13t5Wyz1wwfKfSQ==", + "version": "5.13.4", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/video-plugin/-/video-plugin-5.13.4.tgz", + "integrity": "sha512-QWbHMVAJHukLbFNn0irND/nEPtmzjbXth1ckBkT1bg8aRilFw50+IIB0Zfdl6X919R2GfGo8P0u+I/Mwxf7yfg==", "license": "MIT", "peerDependencies": { - "@photo-sphere-viewer/core": "5.13.3" + "@photo-sphere-viewer/core": "5.13.4" } }, "node_modules/@pkgjs/parseargs": { @@ -2203,9 +2203,9 @@ } }, "node_modules/@sveltejs/kit": { - "version": "2.25.1", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.25.1.tgz", - "integrity": "sha512-8H+fxDEp7Xq6tLFdrGdS5fLu6ONDQQ9DgyjboXpChubuFdfH9QoFX09ypssBpyNkJNZFt9eW3yLmXIc9CesPCA==", + "version": "2.25.2", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.25.2.tgz", + "integrity": "sha512-aKfj82vqEINedoH9Pw4Ip16jj3w8soNq9F3nJqc56kxXW74TcEu/gdTAuLUI+gsl8i+KXfetRqg1F+gG/AZRVQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3021,17 +3021,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", - "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", + "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/type-utils": "8.37.0", - "@typescript-eslint/utils": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/type-utils": "8.38.0", + "@typescript-eslint/utils": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -3045,188 +3045,11 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.37.0", + "@typescript-eslint/parser": "^8.38.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/project-service": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", - "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.37.0", - "@typescript-eslint/types": "^8.37.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", - "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", - "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", - "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", - "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", - "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.37.0", - "@typescript-eslint/tsconfig-utils": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", - "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", - "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.37.0", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", @@ -3237,33 +3060,17 @@ "node": ">= 4" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@typescript-eslint/parser": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", - "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", + "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4" }, "engines": { @@ -3278,150 +3085,6 @@ "typescript": ">=4.8.4 <5.9.0" } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/project-service": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", - "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.37.0", - "@typescript-eslint/types": "^8.37.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", - "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", - "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", - "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", - "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.37.0", - "@typescript-eslint/tsconfig-utils": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", - "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.37.0", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@typescript-eslint/project-service": { "version": "8.38.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", @@ -5195,9 +4858,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", - "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", "bin": { @@ -5619,9 +5282,9 @@ } }, "node_modules/fabric": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/fabric/-/fabric-6.7.0.tgz", - "integrity": "sha512-+yKumsh1MvJ44Um2eOhb4Q6CyZ6e2XKBV3IfQvzuGKhl2UkRFQtIKPUi6f06m3gd0r5zspgMUl5iwxtT1dmFAQ==", + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/fabric/-/fabric-6.7.1.tgz", + "integrity": "sha512-dLxSmIvN4InJf4xOjbl1LFWh8WGOUIYtcuDIGs2IN0Z9lI0zGobfesDauyEhI1+owMLTPCCiEv01rpYXm7g2EQ==", "license": "MIT", "engines": { "node": ">=16.20.0" @@ -8187,15 +7850,15 @@ } }, "node_modules/prettier-plugin-organize-imports": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.1.0.tgz", - "integrity": "sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.2.0.tgz", + "integrity": "sha512-Zdy27UhlmyvATZi67BTnLcKTo8fm6Oik59Sz6H64PgZJVs6NJpPD1mT240mmJn62c98/QaL+r3kx9Q3gRpDajg==", "dev": true, "license": "MIT", "peerDependencies": { "prettier": ">=2.0", "typescript": ">=2.9", - "vue-tsc": "^2.1.0" + "vue-tsc": "^2.1.0 || 3" }, "peerDependenciesMeta": { "vue-tsc": { @@ -9773,16 +9436,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.37.0.tgz", - "integrity": "sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.38.0.tgz", + "integrity": "sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.37.0", - "@typescript-eslint/parser": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0" + "@typescript-eslint/eslint-plugin": "8.38.0", + "@typescript-eslint/parser": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -9796,174 +9459,6 @@ "typescript": ">=4.8.4 <5.9.0" } }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/project-service": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", - "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.37.0", - "@typescript-eslint/types": "^8.37.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", - "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", - "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", - "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", - "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.37.0", - "@typescript-eslint/tsconfig-utils": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", - "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", - "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.37.0", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/typescript-eslint/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/typescript-eslint/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/uglify-js": { "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", @@ -10928,21 +10423,6 @@ "license": "ISC", "optional": true }, - "node_modules/yaml": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", - "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true, - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/yargs": { "version": "15.4.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", From 056b262cba30107181d92354c5c99a4bd9660e3f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 11:48:26 +0200 Subject: [PATCH 096/748] chore(deps): update dependency @types/node to ^22.16.5 (#20385) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- cli/package-lock.json | 4 ++-- cli/package.json | 2 +- e2e/package-lock.json | 6 +++--- e2e/package.json | 2 +- open-api/typescript-sdk/package-lock.json | 2 +- open-api/typescript-sdk/package.json | 2 +- server/package-lock.json | 2 +- server/package.json | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index 85e17e6c40..3659cb835f 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -27,7 +27,7 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.16.4", + "@types/node": "^22.16.5", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", @@ -61,7 +61,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.16.4", + "@types/node": "^22.16.5", "typescript": "^5.3.3" } }, diff --git a/cli/package.json b/cli/package.json index 6475a91465..2f8147ceef 100644 --- a/cli/package.json +++ b/cli/package.json @@ -21,7 +21,7 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.16.4", + "@types/node": "^22.16.5", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 0d65d3d77f..fe6ba17d4c 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -16,7 +16,7 @@ "@playwright/test": "^1.44.1", "@socket.io/component-emitter": "^3.1.2", "@types/luxon": "^3.4.2", - "@types/node": "^22.16.4", + "@types/node": "^22.16.5", "@types/oidc-provider": "^9.0.0", "@types/pg": "^8.15.1", "@types/pngjs": "^6.0.4", @@ -68,7 +68,7 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.16.4", + "@types/node": "^22.16.5", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", @@ -102,7 +102,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.16.4", + "@types/node": "^22.16.5", "typescript": "^5.3.3" } }, diff --git a/e2e/package.json b/e2e/package.json index 580cf4982c..228332aaa8 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -26,7 +26,7 @@ "@playwright/test": "^1.44.1", "@socket.io/component-emitter": "^3.1.2", "@types/luxon": "^3.4.2", - "@types/node": "^22.16.4", + "@types/node": "^22.16.5", "@types/oidc-provider": "^9.0.0", "@types/pg": "^8.15.1", "@types/pngjs": "^6.0.4", diff --git a/open-api/typescript-sdk/package-lock.json b/open-api/typescript-sdk/package-lock.json index 61c8807fc5..e906674878 100644 --- a/open-api/typescript-sdk/package-lock.json +++ b/open-api/typescript-sdk/package-lock.json @@ -12,7 +12,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.16.4", + "@types/node": "^22.16.5", "typescript": "^5.3.3" } }, diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index fceb764f82..a5582ee60a 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -19,7 +19,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.16.4", + "@types/node": "^22.16.5", "typescript": "^5.3.3" }, "repository": { diff --git a/server/package-lock.json b/server/package-lock.json index b5f5889346..655b0eaf5b 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -109,7 +109,7 @@ "@types/luxon": "^3.6.2", "@types/mock-fs": "^4.13.1", "@types/multer": "^2.0.0", - "@types/node": "^22.16.4", + "@types/node": "^22.16.5", "@types/nodemailer": "^6.4.14", "@types/picomatch": "^4.0.0", "@types/pngjs": "^6.0.5", diff --git a/server/package.json b/server/package.json index 38d4f7b5f7..8641e8168c 100644 --- a/server/package.json +++ b/server/package.json @@ -134,7 +134,7 @@ "@types/luxon": "^3.6.2", "@types/mock-fs": "^4.13.1", "@types/multer": "^2.0.0", - "@types/node": "^22.16.4", + "@types/node": "^22.16.5", "@types/nodemailer": "^6.4.14", "@types/picomatch": "^4.0.0", "@types/pngjs": "^6.0.5", From ad6f7f8089f8016bc0291533ba295e3700973ad5 Mon Sep 17 00:00:00 2001 From: Andreas Petersson Date: Tue, 29 Jul 2025 13:02:37 +0200 Subject: [PATCH 097/748] docs: add immich_ml_balancer to community projects (#20399) --- docs/src/components/community-projects.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/src/components/community-projects.tsx b/docs/src/components/community-projects.tsx index 03a384162b..46e28b3b76 100644 --- a/docs/src/components/community-projects.tsx +++ b/docs/src/components/community-projects.tsx @@ -100,6 +100,11 @@ const projects: CommunityProjectProps[] = [ description: 'Automatically optimize files uploaded to Immich in order to save storage space', url: 'https://github.com/miguelangel-nubla/immich-upload-optimizer', }, + { + title: 'Immich Machine Learning Load Balancer', + description: 'Speed up your machine learning by load balancing your requests to multiple computers', + url: 'https://github.com/apetersson/immich_ml_balancer', + }, ]; function CommunityProject({ title, description, url }: CommunityProjectProps): JSX.Element { From 90eac40e0234d3c9406227c9a05e597318c7c2fe Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 12:06:52 +0100 Subject: [PATCH 098/748] chore(deps): update terraform cloudflare to v4.52.1 (#20387) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .../docs-release/.terraform.lock.hcl | 60 +++++++++---------- .../modules/cloudflare/docs-release/config.tf | 2 +- .../cloudflare/docs/.terraform.lock.hcl | 60 +++++++++---------- deployment/modules/cloudflare/docs/config.tf | 2 +- 4 files changed, 62 insertions(+), 62 deletions(-) diff --git a/deployment/modules/cloudflare/docs-release/.terraform.lock.hcl b/deployment/modules/cloudflare/docs-release/.terraform.lock.hcl index 417f262157..90a7bd6259 100644 --- a/deployment/modules/cloudflare/docs-release/.terraform.lock.hcl +++ b/deployment/modules/cloudflare/docs-release/.terraform.lock.hcl @@ -2,37 +2,37 @@ # Manual edits may be lost in future updates. provider "registry.opentofu.org/cloudflare/cloudflare" { - version = "4.52.0" - constraints = "4.52.0" + version = "4.52.1" + constraints = "4.52.1" hashes = [ - "h1:2BEJyXJtYC4B4nda/WCYUmuJYDaYk88F8t1pwPzr0iQ=", - "h1:4IASk5SESeWKQ7JU0+M7KApuF5mZyklvwMXPBabim3c=", - "h1:5ImZxxALSnWfH/4EXw/wFirSmk5Tr0ACmcysy51AafE=", - "h1:6TJ3dxLSin4ZKBJLsZDn95H2ZYnGm8S7GGHvvXuuMQU=", - "h1:IzTUjg9kQ4N3qizP9CjYLeHwjsuGgtxwXvfUQWyOLcA=", - "h1:NTaOQfYINA0YTG/V1/9+SYtgX1it63+cBugj4WK4FWc=", - "h1:PXH48LuJn329sCfMXprdMDk51EZaWFyajVvS03qhQLs=", - "h1:Pi5M+GeoMSN2eJ6QnIeXjBf19O+rby/74CfB2ocpv20=", - "h1:ShXZ2ZjBvm3thfoPPzPT8+OhyismnydQVkUAfI8X12w=", - "h1:WQ9hu0Wge2msBbODfottCSKgu8oKUrw4Opz+fDPVVHk=", - "h1:Z5yXML2DE0uH9UU+M0ut9JMQAORcwVZz1CxBHzeBmao=", - "h1:jqI2qKknpleS3JDSplyGYHMu0u9K/tor1ZOjFwDgEMk=", - "h1:kgfutDh14Q5nw4eg6qGFamFxIiY8Ae0FPKRBLDOzpcI=", - "h1:zCAO7GZmfYhWb+i6TfqlqhMeDyPZWGio2IzEzAh3YTs=", - "zh:19be1a91c982b902c42aba47766860dfa5dc151eed1e95fd39ca642229381ef0", - "zh:1de451c4d1ecf7efbe67b6dace3426ba810711afdd644b0f1b870364c8ae91f8", - "zh:352b4a2120173298622e669258744554339d959ac3a95607b117a48ee4a83238", - "zh:3c6f1346d9154afbd2d558fabb4b0150fc8d559aa961254144fe1bc17fe6032f", - "zh:4c4c92d53fb535b1e0eff26f222bbd627b97d3b4c891ec9c321268676d06152f", - "zh:53276f68006c9ceb7cdb10a6ccf91a5c1eadd1407a28edb5741e84e88d7e29e8", - "zh:7925a97773948171a63d4f65bb81ee92fd6d07a447e36012977313293a5435c9", - "zh:7dfb0a4496cfe032437386d0a2cd9229a1956e9c30bd920923c141b0f0440060", + "h1:2lHvafwGbLdmc9lYkuJFw3nsInaQjRpjX/JfIRKmq/M=", + "h1:596JomwjrtUrOSreq9NNCS+rj70+jOV+0pfja5MXiTI=", + "h1:7mBOA5TVAIt3qAwPXKCtE0RSYeqij9v30mnksuBbpEg=", + "h1:ELVgzh4kHKBCYdL+2A8JjWS0E1snLUN3Mmz3Vo6qSfw=", + "h1:FGGM5yLFf72g3kSXM3LAN64Gf/AkXr5WCmhixgnP+l4=", + "h1:JupkJbQALcIVoMhHImrLeLDsQR1ET7VJLGC7ONxjqGU=", + "h1:KsaE4JNq+1uV1nJsuTcYar/8lyY6zKS5UBEpfYg3wvc=", + "h1:NHZ5RJIzQDLhie/ykl3uI6UPfNQR9Lu5Ti7JPR6X904=", + "h1:NfAuMbn6LQPLDtJhbzO1MX9JMIGLMa8K6CpekvtsuX8=", + "h1:e+vNKokamDsp/kJvFr2pRudzwEz2r49iZ/oSggw+1LY=", + "h1:jnb4VdfNZ79I3yj7Q8x+JmOT+FxbfjjRfrF0dL0yCW8=", + "h1:kmF//O539d7NuHU7qIxDj7Wz4eJmLKFiI5glwQivldU=", + "h1:s6XriaKwOgV4jvKAGPXkrxhhOQxpNU5dceZwi9Z/1k8=", + "h1:wt3WBEBAeSGTlC9OlnTlAALxRiK4SQgLy0KgBIS7qzs=", + "zh:2fb95e1d3229b9b6c704e1a413c7481c60f139780d9641f657b6eb9b633b90f2", + "zh:379c7680983383862236e9e6e720c3114195c40526172188e88d0ffcf50dfe2e", + "zh:55533beb6cfc02d22ffda8cba8027bc2c841bb172cd637ed0d28323d41395f8f", + "zh:5abd70760e4eb1f37a1c307cbd2989ea7c9ba0afb93818c67c1d363a31f75703", + "zh:699f1c8cd66129176fe659ebf0e6337632a8967a28d2630b6ae5948665c0c2ae", + "zh:69c15acd73c451e89de6477059cda2f3ec200b48ae4b9ff3646c4d389fd3205e", + "zh:6e02b687de21b844f8266dff99e93e7c61fc8eb688f4bbb23803caceb251839e", + "zh:7a51d17b87ed87b7bebf2ad9fc7c3a74f16a1b44eee92c779c08eb89258c0496", + "zh:88ad84436837b0f55302f22748505972634e87400d6902260fd6b7ba1610f937", "zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f", - "zh:8d4aa79f0a414bb4163d771063c70cd991c8fac6c766e685bac2ee12903c5bd6", - "zh:a67540c13565616a7e7e51ee9366e88b0dc60046e1d75c72680e150bd02725bb", - "zh:a936383a4767f5393f38f622e92bf2d0c03fe04b69c284951f27345766c7b31b", - "zh:d4887d73c466ff036eecf50ad6404ba38fd82ea4855296b1846d244b0f13c380", - "zh:e9093c8bd5b6cd99c81666e315197791781b8f93afa14fc2e0f732d1bb2a44b7", - "zh:efd3b3f1ec59a37f635aa1d4efcf178734c2fcf8ddb0d56ea690bec342da8672", + "zh:8d46c3d9f4f7ad20ac6ef01daa63f4e30a2d16dcb1bb5c7c7ee3dc6be38e9ca1", + "zh:913d64e72a4929dae1d4793e2004f4f9a58b138ea337d9d94fa35cafbf06550a", + "zh:c8d93cf86e2e49f6cec665cfe78b82c144cce15a8b2e30f343385fadd1251849", + "zh:cc4f69397d9bc34a528a5609a024c3a48f54f21616c0008792dd417297add955", + "zh:df99cdb8b064aad35ffea77e645cf6541d0b1b2ebc51b6d26c42031de60ab69e", ] } diff --git a/deployment/modules/cloudflare/docs-release/config.tf b/deployment/modules/cloudflare/docs-release/config.tf index cd370f9353..9dd16d5982 100644 --- a/deployment/modules/cloudflare/docs-release/config.tf +++ b/deployment/modules/cloudflare/docs-release/config.tf @@ -5,7 +5,7 @@ terraform { required_providers { cloudflare = { source = "cloudflare/cloudflare" - version = "4.52.0" + version = "4.52.1" } } } diff --git a/deployment/modules/cloudflare/docs/.terraform.lock.hcl b/deployment/modules/cloudflare/docs/.terraform.lock.hcl index 417f262157..90a7bd6259 100644 --- a/deployment/modules/cloudflare/docs/.terraform.lock.hcl +++ b/deployment/modules/cloudflare/docs/.terraform.lock.hcl @@ -2,37 +2,37 @@ # Manual edits may be lost in future updates. provider "registry.opentofu.org/cloudflare/cloudflare" { - version = "4.52.0" - constraints = "4.52.0" + version = "4.52.1" + constraints = "4.52.1" hashes = [ - "h1:2BEJyXJtYC4B4nda/WCYUmuJYDaYk88F8t1pwPzr0iQ=", - "h1:4IASk5SESeWKQ7JU0+M7KApuF5mZyklvwMXPBabim3c=", - "h1:5ImZxxALSnWfH/4EXw/wFirSmk5Tr0ACmcysy51AafE=", - "h1:6TJ3dxLSin4ZKBJLsZDn95H2ZYnGm8S7GGHvvXuuMQU=", - "h1:IzTUjg9kQ4N3qizP9CjYLeHwjsuGgtxwXvfUQWyOLcA=", - "h1:NTaOQfYINA0YTG/V1/9+SYtgX1it63+cBugj4WK4FWc=", - "h1:PXH48LuJn329sCfMXprdMDk51EZaWFyajVvS03qhQLs=", - "h1:Pi5M+GeoMSN2eJ6QnIeXjBf19O+rby/74CfB2ocpv20=", - "h1:ShXZ2ZjBvm3thfoPPzPT8+OhyismnydQVkUAfI8X12w=", - "h1:WQ9hu0Wge2msBbODfottCSKgu8oKUrw4Opz+fDPVVHk=", - "h1:Z5yXML2DE0uH9UU+M0ut9JMQAORcwVZz1CxBHzeBmao=", - "h1:jqI2qKknpleS3JDSplyGYHMu0u9K/tor1ZOjFwDgEMk=", - "h1:kgfutDh14Q5nw4eg6qGFamFxIiY8Ae0FPKRBLDOzpcI=", - "h1:zCAO7GZmfYhWb+i6TfqlqhMeDyPZWGio2IzEzAh3YTs=", - "zh:19be1a91c982b902c42aba47766860dfa5dc151eed1e95fd39ca642229381ef0", - "zh:1de451c4d1ecf7efbe67b6dace3426ba810711afdd644b0f1b870364c8ae91f8", - "zh:352b4a2120173298622e669258744554339d959ac3a95607b117a48ee4a83238", - "zh:3c6f1346d9154afbd2d558fabb4b0150fc8d559aa961254144fe1bc17fe6032f", - "zh:4c4c92d53fb535b1e0eff26f222bbd627b97d3b4c891ec9c321268676d06152f", - "zh:53276f68006c9ceb7cdb10a6ccf91a5c1eadd1407a28edb5741e84e88d7e29e8", - "zh:7925a97773948171a63d4f65bb81ee92fd6d07a447e36012977313293a5435c9", - "zh:7dfb0a4496cfe032437386d0a2cd9229a1956e9c30bd920923c141b0f0440060", + "h1:2lHvafwGbLdmc9lYkuJFw3nsInaQjRpjX/JfIRKmq/M=", + "h1:596JomwjrtUrOSreq9NNCS+rj70+jOV+0pfja5MXiTI=", + "h1:7mBOA5TVAIt3qAwPXKCtE0RSYeqij9v30mnksuBbpEg=", + "h1:ELVgzh4kHKBCYdL+2A8JjWS0E1snLUN3Mmz3Vo6qSfw=", + "h1:FGGM5yLFf72g3kSXM3LAN64Gf/AkXr5WCmhixgnP+l4=", + "h1:JupkJbQALcIVoMhHImrLeLDsQR1ET7VJLGC7ONxjqGU=", + "h1:KsaE4JNq+1uV1nJsuTcYar/8lyY6zKS5UBEpfYg3wvc=", + "h1:NHZ5RJIzQDLhie/ykl3uI6UPfNQR9Lu5Ti7JPR6X904=", + "h1:NfAuMbn6LQPLDtJhbzO1MX9JMIGLMa8K6CpekvtsuX8=", + "h1:e+vNKokamDsp/kJvFr2pRudzwEz2r49iZ/oSggw+1LY=", + "h1:jnb4VdfNZ79I3yj7Q8x+JmOT+FxbfjjRfrF0dL0yCW8=", + "h1:kmF//O539d7NuHU7qIxDj7Wz4eJmLKFiI5glwQivldU=", + "h1:s6XriaKwOgV4jvKAGPXkrxhhOQxpNU5dceZwi9Z/1k8=", + "h1:wt3WBEBAeSGTlC9OlnTlAALxRiK4SQgLy0KgBIS7qzs=", + "zh:2fb95e1d3229b9b6c704e1a413c7481c60f139780d9641f657b6eb9b633b90f2", + "zh:379c7680983383862236e9e6e720c3114195c40526172188e88d0ffcf50dfe2e", + "zh:55533beb6cfc02d22ffda8cba8027bc2c841bb172cd637ed0d28323d41395f8f", + "zh:5abd70760e4eb1f37a1c307cbd2989ea7c9ba0afb93818c67c1d363a31f75703", + "zh:699f1c8cd66129176fe659ebf0e6337632a8967a28d2630b6ae5948665c0c2ae", + "zh:69c15acd73c451e89de6477059cda2f3ec200b48ae4b9ff3646c4d389fd3205e", + "zh:6e02b687de21b844f8266dff99e93e7c61fc8eb688f4bbb23803caceb251839e", + "zh:7a51d17b87ed87b7bebf2ad9fc7c3a74f16a1b44eee92c779c08eb89258c0496", + "zh:88ad84436837b0f55302f22748505972634e87400d6902260fd6b7ba1610f937", "zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f", - "zh:8d4aa79f0a414bb4163d771063c70cd991c8fac6c766e685bac2ee12903c5bd6", - "zh:a67540c13565616a7e7e51ee9366e88b0dc60046e1d75c72680e150bd02725bb", - "zh:a936383a4767f5393f38f622e92bf2d0c03fe04b69c284951f27345766c7b31b", - "zh:d4887d73c466ff036eecf50ad6404ba38fd82ea4855296b1846d244b0f13c380", - "zh:e9093c8bd5b6cd99c81666e315197791781b8f93afa14fc2e0f732d1bb2a44b7", - "zh:efd3b3f1ec59a37f635aa1d4efcf178734c2fcf8ddb0d56ea690bec342da8672", + "zh:8d46c3d9f4f7ad20ac6ef01daa63f4e30a2d16dcb1bb5c7c7ee3dc6be38e9ca1", + "zh:913d64e72a4929dae1d4793e2004f4f9a58b138ea337d9d94fa35cafbf06550a", + "zh:c8d93cf86e2e49f6cec665cfe78b82c144cce15a8b2e30f343385fadd1251849", + "zh:cc4f69397d9bc34a528a5609a024c3a48f54f21616c0008792dd417297add955", + "zh:df99cdb8b064aad35ffea77e645cf6541d0b1b2ebc51b6d26c42031de60ab69e", ] } diff --git a/deployment/modules/cloudflare/docs/config.tf b/deployment/modules/cloudflare/docs/config.tf index cd370f9353..9dd16d5982 100644 --- a/deployment/modules/cloudflare/docs/config.tf +++ b/deployment/modules/cloudflare/docs/config.tf @@ -5,7 +5,7 @@ terraform { required_providers { cloudflare = { source = "cloudflare/cloudflare" - version = "4.52.0" + version = "4.52.1" } } } From 59a50b86973a9df51cf30f07841965dc22571b63 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 12:07:14 +0100 Subject: [PATCH 099/748] chore(deps): update github-actions (#20384) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/static_analysis.yml | 2 +- .github/workflows/test.yml | 2 +- .github/workflows/weblate-lock.yml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 6f1e68afce..b10bda81f8 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -50,7 +50,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 + uses: github/codeql-action/init@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3.29.4 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -63,7 +63,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 + uses: github/codeql-action/autobuild@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3.29.4 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -76,6 +76,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 + uses: github/codeql-action/analyze@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3.29.4 with: category: '/language:${{matrix.language}}' diff --git a/.github/workflows/static_analysis.yml b/.github/workflows/static_analysis.yml index 573c908526..0f7b0a7a56 100644 --- a/.github/workflows/static_analysis.yml +++ b/.github/workflows/static_analysis.yml @@ -129,7 +129,7 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload SARIF file - uses: github/codeql-action/upload-sarif@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 + uses: github/codeql-action/upload-sarif@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3.29.4 with: sarif_file: results.sarif category: zizmor diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 47a11c8232..baaafccbd1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -668,7 +668,7 @@ jobs: contents: read services: postgres: - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3@sha256:1f5583fe3397210a0fbc7f11b0cec18bacc4a99e3e8ea0548e9bd6bcf26ec37a + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3@sha256:ec713143dca1a426eba2e03707c319e2ec3cc9d304ef767f777f8e297dee820c env: POSTGRES_PASSWORD: postgres POSTGRES_USER: postgres diff --git a/.github/workflows/weblate-lock.yml b/.github/workflows/weblate-lock.yml index 084e1a97d6..77bd2b7830 100644 --- a/.github/workflows/weblate-lock.yml +++ b/.github/workflows/weblate-lock.yml @@ -38,7 +38,7 @@ jobs: exit 1 fi - name: Find Pull Request - uses: juliangruber/find-pull-request-action@48b6133aa6c826f267ebd33aa2d29470f9d9e7d0 # v1.9.0 + uses: juliangruber/find-pull-request-action@952b3bb1ddb2dcc0aa3479e98bb1c2d1a922f096 # v1.10.0 id: find-pr with: branch: chore/translations From 2a005629a0069ed0f474cb22f84569855f75c03f Mon Sep 17 00:00:00 2001 From: Zack Pollard Date: Tue, 29 Jul 2025 12:23:52 +0100 Subject: [PATCH 100/748] chore: bump minimum eslint-config-prettier version due to MAL-2025-6022 (#20400) --- cli/package-lock.json | 2 +- cli/package.json | 2 +- e2e/package-lock.json | 4 ++-- e2e/package.json | 2 +- server/package-lock.json | 2 +- server/package.json | 2 +- web/package-lock.json | 4 ++-- web/package.json | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index 3659cb835f..2b20af801b 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -33,7 +33,7 @@ "cli-progress": "^3.12.0", "commander": "^12.0.0", "eslint": "^9.14.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^59.0.0", "globals": "^16.0.0", diff --git a/cli/package.json b/cli/package.json index 2f8147ceef..a20ae145b4 100644 --- a/cli/package.json +++ b/cli/package.json @@ -27,7 +27,7 @@ "cli-progress": "^3.12.0", "commander": "^12.0.0", "eslint": "^9.14.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^59.0.0", "globals": "^16.0.0", diff --git a/e2e/package-lock.json b/e2e/package-lock.json index fe6ba17d4c..8c1e6e2ba1 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -23,7 +23,7 @@ "@types/supertest": "^6.0.2", "@vitest/coverage-v8": "^3.0.0", "eslint": "^9.14.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^59.0.0", "exiftool-vendored": "^28.3.1", @@ -74,7 +74,7 @@ "cli-progress": "^3.12.0", "commander": "^12.0.0", "eslint": "^9.14.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^59.0.0", "globals": "^16.0.0", diff --git a/e2e/package.json b/e2e/package.json index 228332aaa8..988f1dfd96 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -33,7 +33,7 @@ "@types/supertest": "^6.0.2", "@vitest/coverage-v8": "^3.0.0", "eslint": "^9.14.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^59.0.0", "exiftool-vendored": "^28.3.1", diff --git a/server/package-lock.json b/server/package-lock.json index 655b0eaf5b..bac0540e38 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -122,7 +122,7 @@ "@vitest/coverage-v8": "^3.0.0", "canvas": "^3.1.0", "eslint": "^9.14.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^59.0.0", "globals": "^16.0.0", diff --git a/server/package.json b/server/package.json index 8641e8168c..2a5fa5ea8f 100644 --- a/server/package.json +++ b/server/package.json @@ -147,7 +147,7 @@ "@vitest/coverage-v8": "^3.0.0", "canvas": "^3.1.0", "eslint": "^9.14.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^59.0.0", "globals": "^16.0.0", diff --git a/web/package-lock.json b/web/package-lock.json index 65a016ed02..eb4aae708b 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -68,7 +68,7 @@ "autoprefixer": "^10.4.17", "dotenv": "^17.0.0", "eslint": "^9.18.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-p": "^0.25.0", "eslint-plugin-compat": "^6.0.2", "eslint-plugin-svelte": "^3.9.0", @@ -100,7 +100,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.16.4", + "@types/node": "^22.16.5", "typescript": "^5.3.3" } }, diff --git a/web/package.json b/web/package.json index b9fb5b0400..a8d338d547 100644 --- a/web/package.json +++ b/web/package.json @@ -85,7 +85,7 @@ "autoprefixer": "^10.4.17", "dotenv": "^17.0.0", "eslint": "^9.18.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-p": "^0.25.0", "eslint-plugin-compat": "^6.0.2", "eslint-plugin-svelte": "^3.9.0", From a0fa7318edc58c1e4f988ee79c3c71ec1ec87768 Mon Sep 17 00:00:00 2001 From: Zack Pollard Date: Tue, 29 Jul 2025 13:28:10 +0100 Subject: [PATCH 101/748] fix: handle cleanup of new backups alongside old backups (#20402) --- server/src/services/backup.service.spec.ts | 30 ++++++++++++++-------- server/src/services/backup.service.ts | 9 +++++-- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/server/src/services/backup.service.spec.ts b/server/src/services/backup.service.spec.ts index e36f699f53..ad60e30425 100644 --- a/server/src/services/backup.service.spec.ts +++ b/server/src/services/backup.service.spec.ts @@ -1,3 +1,4 @@ +import { DateTime } from 'luxon'; import { PassThrough } from 'node:stream'; import { defaults, SystemConfig } from 'src/config'; import { StorageCore } from 'src/cores/storage.core'; @@ -90,18 +91,23 @@ describe(BackupService.name, () => { it('should remove failed backup files', async () => { mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.backupEnabled); + //`immich-db-backup-${DateTime.now().toFormat("yyyyLLdd'T'HHmmss")}-v${serverVersion.toString()}-pg${databaseVersion.split(' ')[0]}.sql.gz.tmp`, mocks.storage.readdir.mockResolvedValue([ 'immich-db-backup-123.sql.gz.tmp', - 'immich-db-backup-234.sql.gz', - 'immich-db-backup-345.sql.gz.tmp', + `immich-db-backup-${DateTime.fromISO('2025-07-25T11:02:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz.tmp`, + `immich-db-backup-${DateTime.fromISO('2025-07-27T11:01:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz`, + `immich-db-backup-${DateTime.fromISO('2025-07-29T11:01:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz.tmp`, ]); await sut.cleanupDatabaseBackups(); - expect(mocks.storage.unlink).toHaveBeenCalledTimes(2); + expect(mocks.storage.unlink).toHaveBeenCalledTimes(3); expect(mocks.storage.unlink).toHaveBeenCalledWith( `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-123.sql.gz.tmp`, ); expect(mocks.storage.unlink).toHaveBeenCalledWith( - `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-345.sql.gz.tmp`, + `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-20250725T110216-v1.234.5-pg14.5.sql.gz.tmp`, + ); + expect(mocks.storage.unlink).toHaveBeenCalledWith( + `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-20250729T110116-v1.234.5-pg14.5.sql.gz.tmp`, ); }); @@ -118,17 +124,21 @@ describe(BackupService.name, () => { it('should remove old backup files over keepLastAmount and failed backups', async () => { mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.backupEnabled); mocks.storage.readdir.mockResolvedValue([ - 'immich-db-backup-1.sql.gz.tmp', - 'immich-db-backup-2.sql.gz', - 'immich-db-backup-3.sql.gz', + `immich-db-backup-${DateTime.fromISO('2025-07-25T11:02:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz.tmp`, + `immich-db-backup-${DateTime.fromISO('2025-07-27T11:01:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz`, + 'immich-db-backup-1753789649000.sql.gz', + `immich-db-backup-${DateTime.fromISO('2025-07-29T11:01:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz`, ]); await sut.cleanupDatabaseBackups(); - expect(mocks.storage.unlink).toHaveBeenCalledTimes(2); + expect(mocks.storage.unlink).toHaveBeenCalledTimes(3); expect(mocks.storage.unlink).toHaveBeenCalledWith( - `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-1.sql.gz.tmp`, + `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-1753789649000.sql.gz`, ); expect(mocks.storage.unlink).toHaveBeenCalledWith( - `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-2.sql.gz`, + `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-20250725T110216-v1.234.5-pg14.5.sql.gz.tmp`, + ); + expect(mocks.storage.unlink).toHaveBeenCalledWith( + `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-20250727T110116-v1.234.5-pg14.5.sql.gz`, ); }); }); diff --git a/server/src/services/backup.service.ts b/server/src/services/backup.service.ts index 9daa2c3aea..0948965f1c 100644 --- a/server/src/services/backup.service.ts +++ b/server/src/services/backup.service.ts @@ -53,9 +53,14 @@ export class BackupService extends BaseService { const backupsFolder = StorageCore.getBaseFolder(StorageFolder.Backups); const files = await this.storageRepository.readdir(backupsFolder); - const failedBackups = files.filter((file) => file.match(/immich-db-backup-\d+\.sql\.gz\.tmp$/)); + const failedBackups = files.filter((file) => file.match(/immich-db-backup-.*\.sql\.gz\.tmp$/)); const backups = files - .filter((file) => file.match(/immich-db-backup-\d+\.sql\.gz$/)) + .filter((file) => { + const oldBackupStyle = file.match(/immich-db-backup-\d+\.sql\.gz$/); + //immich-db-backup-20250729T114018-v1.136.0-pg14.17.sql.gz + const newBackupStyle = file.match(/immich-db-backup-\d{8}T\d{6}-v.*-pg.*\.sql\.gz$/); + return oldBackupStyle || newBackupStyle; + }) .sort() .reverse(); From 3b5e00131bf6dd5a36a2a1120f23ab1947e31ede Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Tue, 29 Jul 2025 08:59:10 -0500 Subject: [PATCH 102/748] fix: android widget periodic updates (#20389) * fix: android widget updates * ensure periodic work is queued when we receive an update This will not "reset the clock" on the periodic work since we are using ExistingPeriodicWorkPolicy.UPDATE. This is needed since existing widgets have already been asked to queue their workers. If those periodic workers were overwritten by a widget update request from the app, there is no way to queue them again. onReceive gets run when the app requests a widget update so the periodic workers will get queued again. --- .../immich/widget/ImageDownloadWorker.kt | 2 +- .../app/alextran/immich/widget/MemoryReceiver.kt | 16 +++++++++------- .../app/alextran/immich/widget/RandomReceiver.kt | 16 +++++++++------- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImageDownloadWorker.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImageDownloadWorker.kt index 3915f291f8..25a7ed99f1 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImageDownloadWorker.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImageDownloadWorker.kt @@ -69,7 +69,7 @@ class ImageDownloadWorker( .build() manager.enqueueUniqueWork( - "$uniqueWorkName-$appWidgetId", + "$uniqueWorkName-$appWidgetId-singleShot", ExistingWorkPolicy.REPLACE, workRequest ) diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/MemoryReceiver.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/MemoryReceiver.kt index 7721af7d6f..63b32eb6f0 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/MemoryReceiver.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/MemoryReceiver.kt @@ -28,19 +28,21 @@ class MemoryReceiver : GlanceAppWidgetReceiver() { override fun onReceive(context: Context, intent: Intent) { val fromMainApp = intent.getBooleanExtra(HomeWidgetPlugin.TRIGGERED_FROM_HOME_WIDGET, false) + val provider = ComponentName(context, MemoryReceiver::class.java) + val glanceIds = AppWidgetManager.getInstance(context).getAppWidgetIds(provider) // Launch coroutine to setup a single shot if the app requested the update if (fromMainApp) { - CoroutineScope(Dispatchers.Default).launch { - val provider = ComponentName(context, MemoryReceiver::class.java) - val glanceIds = AppWidgetManager.getInstance(context).getAppWidgetIds(provider) - - glanceIds.forEach { widgetID -> - ImageDownloadWorker.singleShot(context, widgetID, WidgetType.MEMORIES) - } + glanceIds.forEach { widgetID -> + ImageDownloadWorker.singleShot(context, widgetID, WidgetType.MEMORIES) } } + // make sure the periodic jobs are running + glanceIds.forEach { widgetID -> + ImageDownloadWorker.enqueuePeriodic(context, widgetID, WidgetType.MEMORIES) + } + super.onReceive(context, intent) } diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomReceiver.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomReceiver.kt index 39afd76c35..a7662181bc 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomReceiver.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomReceiver.kt @@ -28,19 +28,21 @@ class RandomReceiver : GlanceAppWidgetReceiver() { override fun onReceive(context: Context, intent: Intent) { val fromMainApp = intent.getBooleanExtra(HomeWidgetPlugin.TRIGGERED_FROM_HOME_WIDGET, false) + val provider = ComponentName(context, RandomReceiver::class.java) + val glanceIds = AppWidgetManager.getInstance(context).getAppWidgetIds(provider) // Launch coroutine to setup a single shot if the app requested the update if (fromMainApp) { - CoroutineScope(Dispatchers.Default).launch { - val provider = ComponentName(context, RandomReceiver::class.java) - val glanceIds = AppWidgetManager.getInstance(context).getAppWidgetIds(provider) - - glanceIds.forEach { widgetID -> - ImageDownloadWorker.singleShot(context, widgetID, WidgetType.RANDOM) - } + glanceIds.forEach { widgetID -> + ImageDownloadWorker.singleShot(context, widgetID, WidgetType.RANDOM) } } + // make sure the periodic jobs are running + glanceIds.forEach { widgetID -> + ImageDownloadWorker.enqueuePeriodic(context, widgetID, WidgetType.RANDOM) + } + super.onReceive(context, intent) } From e6ec01985285f47bd38fe025f43bd7865306e28f Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Tue, 29 Jul 2025 20:35:04 +0530 Subject: [PATCH 103/748] fix: show missing local assets only in timeline with partner sharing (#20298) fix: show missing local assets in timeline with partner sharing Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Co-authored-by: Alex --- .../entities/merged_asset.drift | 7 +++--- .../entities/merged_asset.drift.dart | 24 +++++++++---------- .../repositories/timeline.repository.dart | 4 ++-- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/mobile/lib/infrastructure/entities/merged_asset.drift b/mobile/lib/infrastructure/entities/merged_asset.drift index 9778ba723b..579323d641 100644 --- a/mobile/lib/infrastructure/entities/merged_asset.drift +++ b/mobile/lib/infrastructure/entities/merged_asset.drift @@ -29,7 +29,7 @@ LEFT JOIN WHERE rae.deleted_at IS NULL AND rae.visibility = 0 -- timeline visibility - AND rae.owner_id in ? + AND rae.owner_id IN :user_ids AND ( rae.stack_id IS NULL OR rae.id = se.primary_asset_id @@ -57,7 +57,7 @@ SELECT FROM local_asset_entity lae WHERE NOT EXISTS ( - SELECT 1 FROM remote_asset_entity rae WHERE rae.checksum = lae.checksum + SELECT 1 FROM remote_asset_entity rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN :user_ids ) AND EXISTS ( SELECT 1 FROM local_album_asset_entity laa @@ -85,7 +85,7 @@ FROM WHERE rae.deleted_at IS NULL AND rae.visibility = 0 -- timeline visibility - AND rae.owner_id in ? + AND rae.owner_id in :user_ids AND ( rae.stack_id IS NULL OR rae.id = se.primary_asset_id @@ -103,6 +103,7 @@ FROM local_album_entity la ON la.id = laa.album_id WHERE rae.id IS NULL + AND rae.owner_id IN :user_ids AND la.backup_selection = 0 -- selected ) GROUP BY bucket_date diff --git a/mobile/lib/infrastructure/entities/merged_asset.drift.dart b/mobile/lib/infrastructure/entities/merged_asset.drift.dart index 75f8de2de0..9916ec13bb 100644 --- a/mobile/lib/infrastructure/entities/merged_asset.drift.dart +++ b/mobile/lib/infrastructure/entities/merged_asset.drift.dart @@ -16,22 +16,22 @@ import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.d class MergedAssetDrift extends i1.ModularAccessor { MergedAssetDrift(i0.GeneratedDatabase db) : super(db); - i0.Selectable mergedAsset( - List var1, { + i0.Selectable mergedAsset({ + required List userIds, required MergedAsset$limit limit, }) { var $arrayStartIndex = 1; - final expandedvar1 = $expandVar($arrayStartIndex, var1.length); - $arrayStartIndex += var1.length; + final expandeduserIds = $expandVar($arrayStartIndex, userIds.length); + $arrayStartIndex += userIds.length; final generatedlimit = $write( limit(alias(this.localAssetEntity, 'lae')), startIndex: $arrayStartIndex, ); $arrayStartIndex += generatedlimit.amountOfVariables; return customSelect( - 'SELECT rae.id AS remote_id, (SELECT lae.id FROM local_asset_entity AS lae WHERE lae.checksum = rae.checksum LIMIT 1) AS local_id, rae.name, rae.type, rae.created_at AS created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar1) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at AS created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) ORDER BY created_at DESC ${generatedlimit.sql}', + 'SELECT rae.id AS remote_id, (SELECT lae.id FROM local_asset_entity AS lae WHERE lae.checksum = rae.checksum LIMIT 1) AS local_id, rae.name, rae.type, rae.created_at AS created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandeduserIds) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at AS created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN ($expandeduserIds)) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) ORDER BY created_at DESC ${generatedlimit.sql}', variables: [ - for (var $ in var1) i0.Variable($), + for (var $ in userIds) i0.Variable($), ...generatedlimit.introducedVariables, ], readsFrom: { @@ -66,18 +66,18 @@ class MergedAssetDrift extends i1.ModularAccessor { ); } - i0.Selectable mergedBucket( - List var2, { + i0.Selectable mergedBucket({ required int groupBy, + required List userIds, }) { var $arrayStartIndex = 2; - final expandedvar2 = $expandVar($arrayStartIndex, var2.length); - $arrayStartIndex += var2.length; + final expandeduserIds = $expandVar($arrayStartIndex, userIds.length); + $arrayStartIndex += userIds.length; return customSelect( - 'SELECT COUNT(*) AS asset_count, CASE WHEN ?1 = 0 THEN STRFTIME(\'%Y-%m-%d\', created_at, \'localtime\') WHEN ?1 = 1 THEN STRFTIME(\'%Y-%m\', created_at, \'localtime\') END AS bucket_date FROM (SELECT rae.created_at FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar2) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT lae.created_at FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum LEFT JOIN local_album_asset_entity AS laa ON laa.asset_id = lae.id LEFT JOIN local_album_entity AS la ON la.id = laa.album_id WHERE rae.id IS NULL AND la.backup_selection = 0) GROUP BY bucket_date ORDER BY bucket_date DESC', + 'SELECT COUNT(*) AS asset_count, CASE WHEN ?1 = 0 THEN STRFTIME(\'%Y-%m-%d\', created_at, \'localtime\') WHEN ?1 = 1 THEN STRFTIME(\'%Y-%m\', created_at, \'localtime\') END AS bucket_date FROM (SELECT rae.created_at FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandeduserIds) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT lae.created_at FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum LEFT JOIN local_album_asset_entity AS laa ON laa.asset_id = lae.id LEFT JOIN local_album_entity AS la ON la.id = laa.album_id WHERE rae.id IS NULL AND rae.owner_id IN ($expandeduserIds) AND la.backup_selection = 0) GROUP BY bucket_date ORDER BY bucket_date DESC', variables: [ i0.Variable(groupBy), - for (var $ in var2) i0.Variable($), + for (var $ in userIds) i0.Variable($), ], readsFrom: { remoteAssetEntity, diff --git a/mobile/lib/infrastructure/repositories/timeline.repository.dart b/mobile/lib/infrastructure/repositories/timeline.repository.dart index a2c14a363f..dcd10faa64 100644 --- a/mobile/lib/infrastructure/repositories/timeline.repository.dart +++ b/mobile/lib/infrastructure/repositories/timeline.repository.dart @@ -41,7 +41,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { } return _db.mergedAssetDrift - .mergedBucket(userIds, groupBy: groupBy.index) + .mergedBucket(userIds: userIds, groupBy: groupBy.index) .map((row) { final date = row.bucketDate.dateFmt(groupBy); return TimeBucket(date: date, assetCount: row.assetCount); @@ -52,7 +52,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { Future> _getMainBucketAssets(List userIds, {required int offset, required int count}) { return _db.mergedAssetDrift - .mergedAsset(userIds, limit: (_) => Limit(count, offset)) + .mergedAsset(userIds: userIds, limit: (_) => Limit(count, offset)) .map( (row) => row.remoteId != null && row.ownerId != null ? RemoteAsset( From 4cae15f28da78b772f469d6066a4f5720d2bb929 Mon Sep 17 00:00:00 2001 From: Andrew Marshall Date: Tue, 29 Jul 2025 16:43:11 -0400 Subject: [PATCH 104/748] feat: support config via systemd credentials (#20406) feat: Support config via Systemd Credentials See https://systemd.io/CREDENTIALS/. This is used as a fallback, so will only be used if the `$*_FILE` var is empty. This could also be used to implicitly use Docker Secrets by settings `CREDENTIALS_DIRECTORY=/run/secrets` rather than setting individual `$_*FILE` environment variables. --- docs/docs/install/environment-variables.md | 8 ++++---- server/bin/start.sh | 8 ++++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/docs/install/environment-variables.md b/docs/docs/install/environment-variables.md index 939c42439d..d070e3485a 100644 --- a/docs/docs/install/environment-variables.md +++ b/docs/docs/install/environment-variables.md @@ -199,12 +199,11 @@ Additional machine learning parameters can be tuned from the admin UI. | `IMMICH_TELEMETRY_INCLUDE` | Collect these telemetries. List of `host`, `api`, `io`, `repo`, `job`. Note: You can also specify `all` to enable all | | server | api, microservices | | `IMMICH_TELEMETRY_EXCLUDE` | Do not collect these telemetries. List of `host`, `api`, `io`, `repo`, `job` | | server | api, microservices | -## Docker Secrets +## Secrets -The following variables support the use of [Docker secrets][docker-secrets] for additional security. +The following variables support reading from files, either via [Systemd Credentials][systemd-creds] or [Docker secrets][docker-secrets] for additional security. -To use any of these, replace the regular environment variable with the equivalent `_FILE` environment variable. The value of -the `_FILE` variable should be set to the path of a file containing the variable value. +To use any of these, either set `CREDENTIALS_DIRECTORY` to a directory that contains files whose name is the “regular variable” name, and whose content is the secret. If using Docker Secrets, setting `CREDENTIALS_DIRECTORY=/run/secrets` will cause all secrets present to be used. Alternatively, replace the regular variable with the equivalent `_FILE` environment variable as below. The value of the `_FILE` variable should be set to the path of a file containing the variable value. | Regular Variable | Equivalent Docker Secrets '\_FILE' Variable | | :----------------- | :------------------------------------------ | @@ -226,3 +225,4 @@ to use a Docker secret for the password in the Redis container. [docker-secrets-docs]: https://github.com/docker-library/docs/tree/master/postgres#docker-secrets [docker-secrets]: https://docs.docker.com/engine/swarm/secrets/ [ioredis]: https://ioredis.readthedocs.io/en/latest/README/#connect-to-redis +[systemd-creds]: https://systemd.io/CREDENTIALS/ diff --git a/server/bin/start.sh b/server/bin/start.sh index 2b4351a6bc..10f897dd8e 100755 --- a/server/bin/start.sh +++ b/server/bin/start.sh @@ -11,8 +11,12 @@ export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/lib/jellyfin-ffmpeg/lib" SERVER_HOME=/usr/src/app/server read_file_and_export() { - if [ -n "${!1}" ]; then - content="$(cat "${!1}")" + fname="${!1}" + if [[ -z $fname ]] && [[ -e "$CREDENTIALS_DIRECTORY/$2" ]]; then + fname="${CREDENTIALS_DIRECTORY}/$2" + fi + if [[ -n $fname ]]; then + content="$(< "$fname")" export "$2"="${content}" unset "$1" fi From 58521c9efb1fd142f555d54a7d9496961209a9a7 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Tue, 29 Jul 2025 16:58:50 -0400 Subject: [PATCH 105/748] feat: change default media location to /data (#20367) * feat!: change default media location to /data * feat: dynamically detect media location --- .../mobile/container-compose-overrides.yml | 1 - .../server/container-compose-overrides.yml | 1 - docker/docker-compose.dev.yml | 1 - docker/docker-compose.prod.yml | 2 - docker/docker-compose.yml | 2 +- docs/docs/FAQ.mdx | 2 +- docs/docs/administration/server-commands.md | 17 ++- docs/docs/features/libraries.md | 2 +- docs/docs/guides/custom-locations.md | 12 +-- docs/docs/guides/external-library.md | 2 +- docs/docs/install/environment-variables.md | 2 +- docs/src/pages/errors.md | 19 ++++ e2e/docker-compose.yml | 1 - e2e/src/utils.ts | 12 --- server/Dockerfile | 2 +- server/src/commands/media-location.command.ts | 2 +- server/src/constants.ts | 2 - server/src/cores/storage.core.spec.ts | 5 +- server/src/cores/storage.core.ts | 19 +++- server/src/enum.ts | 2 + server/src/repositories/config.repository.ts | 2 + server/src/repositories/storage.repository.ts | 4 + .../src/services/asset-media.service.spec.ts | 16 +-- server/src/services/auth.service.spec.ts | 2 +- server/src/services/download.service.spec.ts | 21 ++-- server/src/services/job.service.spec.ts | 2 +- server/src/services/library.service.spec.ts | 26 ++--- server/src/services/media.service.spec.ts | 17 ++- server/src/services/metadata.service.spec.ts | 6 +- server/src/services/server.service.spec.ts | 12 +-- .../services/storage-template.service.spec.ts | 100 ++++++++---------- server/src/services/storage.service.spec.ts | 48 ++++++--- server/src/services/storage.service.ts | 83 ++++++++++----- server/src/services/user.service.spec.ts | 12 +-- server/test/fixtures/asset.stub.ts | 4 +- .../specs/services/storage.service.spec.ts | 46 ++++++++ .../repositories/storage.repository.mock.ts | 8 +- server/test/small.factory.ts | 2 +- web/src/lib/utils/asset-utils.spec.ts | 6 +- 39 files changed, 316 insertions(+), 209 deletions(-) create mode 100644 server/test/medium/specs/services/storage.service.spec.ts diff --git a/.devcontainer/mobile/container-compose-overrides.yml b/.devcontainer/mobile/container-compose-overrides.yml index 9fc7486fea..c0655e50e7 100644 --- a/.devcontainer/mobile/container-compose-overrides.yml +++ b/.devcontainer/mobile/container-compose-overrides.yml @@ -4,7 +4,6 @@ services: target: dev-container-mobile environment: - IMMICH_SERVER_URL=http://127.0.0.1:2283/ - - IMMICH_MEDIA_LOCATION=/data volumes: !override # bind mount host to /workspaces/immich - ..:/workspaces/immich - cli_node_modules:/workspaces/immich/cli/node_modules diff --git a/.devcontainer/server/container-compose-overrides.yml b/.devcontainer/server/container-compose-overrides.yml index 951d763b4b..c4745ad199 100644 --- a/.devcontainer/server/container-compose-overrides.yml +++ b/.devcontainer/server/container-compose-overrides.yml @@ -6,7 +6,6 @@ services: hostname: immich-dev environment: - IMMICH_SERVER_URL=http://127.0.0.1:2283/ - - IMMICH_MEDIA_LOCATION=/data volumes: !override - ..:/workspaces/immich - cli_node_modules:/workspaces/immich/cli/node_modules diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index 6db9623b1a..0d918a3dbe 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -36,7 +36,6 @@ services: env_file: - .env environment: - IMMICH_MEDIA_LOCATION: /data IMMICH_REPOSITORY: immich-app/immich IMMICH_REPOSITORY_URL: https://github.com/immich-app/immich IMMICH_SOURCE_REF: local diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml index fdeb6ef7a8..838aea4458 100644 --- a/docker/docker-compose.prod.yml +++ b/docker/docker-compose.prod.yml @@ -19,8 +19,6 @@ services: build: context: ../ dockerfile: server/Dockerfile - environment: - - IMMICH_MEDIA_LOCATION=/data volumes: - ${UPLOAD_LOCATION}/photos:/data - /etc/localtime:/etc/localtime:ro diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 6af0e0aa2a..2c0895a57a 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -18,7 +18,7 @@ services: # service: cpu # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding volumes: # Do not edit the next line. If you want to change the media storage location on your system, edit the value of UPLOAD_LOCATION in the .env file - - ${UPLOAD_LOCATION}:/usr/src/app/upload + - ${UPLOAD_LOCATION}:/data - /etc/localtime:/etc/localtime:ro env_file: - .env diff --git a/docs/docs/FAQ.mdx b/docs/docs/FAQ.mdx index 9e14d10a0e..e57039a420 100644 --- a/docs/docs/FAQ.mdx +++ b/docs/docs/FAQ.mdx @@ -180,7 +180,7 @@ services: ... volumes: # Do not edit the next line. If you want to change the media storage location on your system, edit the value of UPLOAD_LOCATION in the .env file - - ${UPLOAD_LOCATION}:/usr/src/app/upload + - ${UPLOAD_LOCATION}:/data - /etc/localtime:/etc/localtime:ro + - originals:/usr/src/app/originals ... diff --git a/docs/docs/administration/server-commands.md b/docs/docs/administration/server-commands.md index b275d8fede..a25673abf2 100644 --- a/docs/docs/administration/server-commands.md +++ b/docs/docs/administration/server-commands.md @@ -94,19 +94,16 @@ Change media location ``` immich-admin change-media-location -? Enter the previous value of IMMICH_MEDIA_LOCATION: /usr/src/app/upload -? Enter the new value of IMMICH_MEDIA_LOCATION: /data +? Enter the previous value of IMMICH_MEDIA_LOCATION: /data +? Enter the new value of IMMICH_MEDIA_LOCATION: /my-data +... + Previous value: /data + Current value: /my-data - Previous value: /usr/src/app/upload - Current value: /data - - Changing database paths from "/usr/src/app/upload/*" to "/data/*" + Changing database paths from "/data/*" to "/my-data/*" ? Do you want to proceed? [Y/n] y Database file paths updated successfully! 🎉 - -You may now set IMMICH_MEDIA_LOCATION=/data and restart! - -(please remember to update applicable volume mounts e.g. ${UPLOAD_LOCATION}:/data) +... ``` diff --git a/docs/docs/features/libraries.md b/docs/docs/features/libraries.md index caf7ab5e55..f274ca3c70 100644 --- a/docs/docs/features/libraries.md +++ b/docs/docs/features/libraries.md @@ -93,7 +93,7 @@ The `immich-server` container will need access to the gallery. Modify your docke ```diff title="docker-compose.yml" immich-server: volumes: - - ${UPLOAD_LOCATION}:/usr/src/app/upload + - ${UPLOAD_LOCATION}:/data + - /mnt/nas/christmas-trip:/mnt/media/christmas-trip:ro + - /home/user/old-pics:/mnt/media/old-pics:ro + - /mnt/media/videos:/mnt/media/videos:ro diff --git a/docs/docs/guides/custom-locations.md b/docs/docs/guides/custom-locations.md index ba5caf4b26..af8ca438e7 100644 --- a/docs/docs/guides/custom-locations.md +++ b/docs/docs/guides/custom-locations.md @@ -27,11 +27,11 @@ After defining the locations of these files, we will edit the `docker-compose.ym services: immich-server: volumes: - - ${UPLOAD_LOCATION}:/usr/src/app/upload -+ - ${THUMB_LOCATION}:/usr/src/app/upload/thumbs -+ - ${ENCODED_VIDEO_LOCATION}:/usr/src/app/upload/encoded-video -+ - ${PROFILE_LOCATION}:/usr/src/app/upload/profile -+ - ${BACKUP_LOCATION}:/usr/src/app/upload/backups + - ${UPLOAD_LOCATION}:/data ++ - ${THUMB_LOCATION}:/data/thumbs ++ - ${ENCODED_VIDEO_LOCATION}:/data/encoded-video ++ - ${PROFILE_LOCATION}:/data/profile ++ - ${BACKUP_LOCATION}:/data/backups - /etc/localtime:/etc/localtime:ro ``` @@ -44,7 +44,7 @@ docker compose up -d :::note Because of the underlying properties of docker bind mounts, it is not recommended to mount the `upload/` and `library/` folders as separate bind mounts if they are on the same device. -For this reason, we mount the HDD or the network storage (NAS) to `/usr/src/app/upload` and then mount the folders we want to access under that folder. +For this reason, we mount the HDD or the network storage (NAS) to `/data` and then mount the folders we want to access under that folder. The `thumbs/` folder contains both the small thumbnails displayed in the timeline and the larger previews shown when clicking into an image. These cannot be separated. diff --git a/docs/docs/guides/external-library.md b/docs/docs/guides/external-library.md index 3ad1679423..7921843297 100644 --- a/docs/docs/guides/external-library.md +++ b/docs/docs/guides/external-library.md @@ -12,7 +12,7 @@ If you want Immich to be able to delete the images in the external library or ad ```diff immich-server: volumes: - - ${UPLOAD_LOCATION}:/usr/src/app/upload + - ${UPLOAD_LOCATION}:/data + - /home/user/photos1:/home/user/photos1:ro + - /mnt/photos2:/mnt/photos2:ro # you can delete this line if you only have one mount point, or you can add more lines if you have more than two ``` diff --git a/docs/docs/install/environment-variables.md b/docs/docs/install/environment-variables.md index d070e3485a..928e0b26e5 100644 --- a/docs/docs/install/environment-variables.md +++ b/docs/docs/install/environment-variables.md @@ -34,7 +34,7 @@ These environment variables are used by the `docker-compose.yml` file and do **N | `TZ` | Timezone | \*1 | server | microservices | | `IMMICH_ENV` | Environment (production, development) | `production` | server, machine learning | api, microservices | | `IMMICH_LOG_LEVEL` | Log level (verbose, debug, log, warn, error) | `log` | server, machine learning | api, microservices | -| `IMMICH_MEDIA_LOCATION` | Media location inside the container ⚠️**You probably shouldn't set this**\*2⚠️ | `/usr/src/app/upload` | server | api, microservices | +| `IMMICH_MEDIA_LOCATION` | Media location inside the container ⚠️**You probably shouldn't set this**\*2⚠️ | `/data` | server | api, microservices | | `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices | | `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | | | `CPU_CORES` | Number of cores available to the Immich server | auto-detected CPU core count | server | | diff --git a/docs/src/pages/errors.md b/docs/src/pages/errors.md index e9bf09770c..4a745877dc 100644 --- a/docs/src/pages/errors.md +++ b/docs/src/pages/errors.md @@ -3,3 +3,22 @@ ## TypeORM Upgrade The upgrade to Immich `v2.x.x` has a required upgrade path to `v1.132.0+`. This means it is required to start up the application at least once on version `1.132.0` (or later). Doing so will complete database schema upgrades that are required for `v2.0.0`. After Immich has successfully booted on this version, shut the system down and try the `v2.x.x` upgrade again. + +## Inconsistent Media Location + +:::caution +This error is related to the location of media files _inside the container_. Never move files on the host system when you run into this error message. +::: + +Immich automatically tries to detect where your Immich data is located. On start up, it compares the detected media location with the file paths in the database and throws an Inconsistent Media Location error when they do not match. + +To fix this issue, verify that the `IMMICH_MEDIA_LOCATION` environment variable and `UPLOAD_LOCATION` volume mount are in sync with the database paths. + +If you would like to migrate from one media location to another, simply successfully start Immich on `v1.136.0` or later, then do the following steps: + +1. Stop Immich +2. Update `IMMICH_MEDIA_LOCATION` to the new location +3. Update the right-hand side of the `UPLOAD_LOCATION` volume mount to the new location +4. Start up Immich + +After version `1.136.0`, Immich can detect when a media location has moved and will automatically update the database paths to keep them in sync. diff --git a/e2e/docker-compose.yml b/e2e/docker-compose.yml index ed57f423bd..ccb21e4587 100644 --- a/e2e/docker-compose.yml +++ b/e2e/docker-compose.yml @@ -22,7 +22,6 @@ services: - IMMICH_ENV=testing - IMMICH_PORT=2285 - IMMICH_IGNORE_MOUNT_CHECK_ERRORS=true - - IMMICH_MEDIA_LOCATION=/data volumes: - ./test-assets:/test-assets extra_hosts: diff --git a/e2e/src/utils.ts b/e2e/src/utils.ts index 3fcc4ab552..0f8ceeabfc 100644 --- a/e2e/src/utils.ts +++ b/e2e/src/utils.ts @@ -186,18 +186,6 @@ export const utils = { } }, - resetFilesystem: async () => { - const mediaInternal = '/usr/src/app/upload'; - const dirs = [ - `"${mediaInternal}/thumbs"`, - `"${mediaInternal}/upload"`, - `"${mediaInternal}/library"`, - `"${mediaInternal}/encoded-video"`, - ].join(' '); - - await execPromise(`docker exec -i "immich-e2e-server" /bin/bash -c "rm -rf ${dirs} && mkdir ${dirs}"`); - }, - unzip: async (input: string, output: string) => { await execPromise(`unzip -o -d "${output}" "${input}"`); }, diff --git a/server/Dockerfile b/server/Dockerfile index 9110d46f7d..c922c5b291 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -130,7 +130,7 @@ ENV IMMICH_SOURCE_REF=${BUILD_SOURCE_REF} ENV IMMICH_SOURCE_COMMIT=${BUILD_SOURCE_COMMIT} ENV IMMICH_SOURCE_URL=https://github.com/immich-app/immich/commit/${BUILD_SOURCE_COMMIT} -VOLUME /usr/src/app/upload +VOLUME /data EXPOSE 2283 ENTRYPOINT ["tini", "--", "/bin/bash", "-c"] CMD ["start.sh"] diff --git a/server/src/commands/media-location.command.ts b/server/src/commands/media-location.command.ts index 0935fe202d..0d32749c02 100644 --- a/server/src/commands/media-location.command.ts +++ b/server/src/commands/media-location.command.ts @@ -61,7 +61,7 @@ export class ChangeMediaLocationCommand extends CommandRunner { immich-server: ... volumes: - - \${UPLOAD_LOCATION}:/usr/src/app/upload + - \${UPLOAD_LOCATION}:/data ... )`; diff --git a/server/src/constants.ts b/server/src/constants.ts index 67d50a65f8..3215a58291 100644 --- a/server/src/constants.ts +++ b/server/src/constants.ts @@ -47,8 +47,6 @@ export const serverVersion = new SemVer(version); export const AUDIT_LOG_MAX_DURATION = Duration.fromObject({ days: 100 }); export const ONE_HOUR = Duration.fromObject({ hours: 1 }); -export const APP_MEDIA_LOCATION = process.env.IMMICH_MEDIA_LOCATION || '/usr/src/app/upload'; - export const MACHINE_LEARNING_PING_TIMEOUT = Number(process.env.MACHINE_LEARNING_PING_TIMEOUT || 2000); export const MACHINE_LEARNING_AVAILABILITY_BACKOFF_TIME = Number( process.env.MACHINE_LEARNING_AVAILABILITY_BACKOFF_TIME || 30_000, diff --git a/server/src/cores/storage.core.spec.ts b/server/src/cores/storage.core.spec.ts index 7bb2cdb1be..ed446f9259 100644 --- a/server/src/cores/storage.core.spec.ts +++ b/server/src/cores/storage.core.spec.ts @@ -2,7 +2,6 @@ import { StorageCore } from 'src/cores/storage.core'; import { vitest } from 'vitest'; vitest.mock('src/constants', () => ({ - APP_MEDIA_LOCATION: '/photos', ADDED_IN_PREFIX: 'This property was added in ', DEPRECATED_IN_PREFIX: 'This property was deprecated in ', IWorker: 'IWorker', @@ -10,6 +9,10 @@ vitest.mock('src/constants', () => ({ describe('StorageCore', () => { describe('isImmichPath', () => { + beforeAll(() => { + StorageCore.setMediaLocation('/photos'); + }); + it('should return true for APP_MEDIA_LOCATION path', () => { const immichPath = '/photos'; expect(StorageCore.isImmichPath(immichPath)).toBe(true); diff --git a/server/src/cores/storage.core.ts b/server/src/cores/storage.core.ts index 6576b397e3..06dde4644c 100644 --- a/server/src/cores/storage.core.ts +++ b/server/src/cores/storage.core.ts @@ -1,6 +1,5 @@ import { randomUUID } from 'node:crypto'; import { dirname, join, resolve } from 'node:path'; -import { APP_MEDIA_LOCATION } from 'src/constants'; import { StorageAsset } from 'src/database'; import { AssetFileType, AssetPathType, ImageFormat, PathType, PersonPathType, StorageFolder } from 'src/enum'; import { AssetRepository } from 'src/repositories/asset.repository'; @@ -32,6 +31,8 @@ export type ThumbnailPathEntity = { id: string; ownerId: string }; let instance: StorageCore | null; +let mediaLocation: string | undefined; + export class StorageCore { private constructor( private assetRepository: AssetRepository, @@ -74,6 +75,18 @@ export class StorageCore { instance = null; } + static getMediaLocation(): string { + if (mediaLocation === undefined) { + throw new Error('Media location is not set.'); + } + + return mediaLocation; + } + + static setMediaLocation(location: string) { + mediaLocation = location; + } + static getFolderLocation(folder: StorageFolder, userId: string) { return join(StorageCore.getBaseFolder(folder), userId); } @@ -83,7 +96,7 @@ export class StorageCore { } static getBaseFolder(folder: StorageFolder) { - return join(APP_MEDIA_LOCATION, folder); + return join(StorageCore.getMediaLocation(), folder); } static getPersonThumbnailPath(person: ThumbnailPathEntity) { @@ -108,7 +121,7 @@ export class StorageCore { static isImmichPath(path: string) { const resolvedPath = resolve(path); - const resolvedAppMediaLocation = resolve(APP_MEDIA_LOCATION); + const resolvedAppMediaLocation = StorageCore.getMediaLocation(); const normalizedPath = resolvedPath.endsWith('/') ? resolvedPath : resolvedPath + '/'; const normalizedAppMediaLocation = resolvedAppMediaLocation.endsWith('/') ? resolvedAppMediaLocation diff --git a/server/src/enum.ts b/server/src/enum.ts index d17b5dc901..666b9fc505 100644 --- a/server/src/enum.ts +++ b/server/src/enum.ts @@ -475,6 +475,8 @@ export enum DatabaseExtension { export enum BootstrapEventPriority { // Database service should be initialized before anything else, most other services need database access DatabaseService = -200, + // Detect and configure the media location before jobs are queued which may use it + StorageService = -195, // Other services may need to queue jobs on bootstrap. JobService = -190, // Initialise config after other bootstrap services, stop other services from using config on bootstrap diff --git a/server/src/repositories/config.repository.ts b/server/src/repositories/config.repository.ts index c9e96a1803..d5c279099c 100644 --- a/server/src/repositories/config.repository.ts +++ b/server/src/repositories/config.repository.ts @@ -97,6 +97,7 @@ export interface EnvData { storage: { ignoreMountCheckErrors: boolean; + mediaLocation?: string; }; workers: ImmichWorker[]; @@ -307,6 +308,7 @@ const getEnv = (): EnvData => { storage: { ignoreMountCheckErrors: !!dto.IMMICH_IGNORE_MOUNT_CHECK_ERRORS, + mediaLocation: dto.IMMICH_MEDIA_LOCATION, }, telemetry: { diff --git a/server/src/repositories/storage.repository.ts b/server/src/repositories/storage.repository.ts index 4d89b02a50..7d6b634845 100644 --- a/server/src/repositories/storage.repository.ts +++ b/server/src/repositories/storage.repository.ts @@ -162,6 +162,10 @@ export class StorageRepository { } } + existsSync(filepath: string) { + return existsSync(filepath); + } + async checkDiskUsage(folder: string): Promise { const stats = await fs.statfs(folder); return { diff --git a/server/src/services/asset-media.service.spec.ts b/server/src/services/asset-media.service.spec.ts index 0585a159ac..91bddeb6e9 100644 --- a/server/src/services/asset-media.service.spec.ts +++ b/server/src/services/asset-media.service.spec.ts @@ -29,7 +29,7 @@ const uploadFile = { file: { uuid: 'random-uuid', checksum: Buffer.from('checksum', 'utf8'), - originalPath: 'upload/admin/image.jpeg', + originalPath: '/data/library/admin/image.jpeg', originalName: 'image.jpeg', size: 1000, }, @@ -42,7 +42,7 @@ const uploadFile = { uuid: 'random-uuid', mimeType: 'image/jpeg', checksum: Buffer.from('checksum', 'utf8'), - originalPath: `upload/admin/${filename}`, + originalPath: `/data/admin/${filename}`, originalName: filename, size: 1000, }, @@ -294,16 +294,16 @@ describe(AssetMediaService.name, () => { it('should return profile for profile uploads', () => { expect(sut.getUploadFolder(uploadFile.filename(UploadFieldName.PROFILE_DATA, 'image.jpg'))).toEqual( - expect.stringContaining('upload/profile/admin_id'), + expect.stringContaining('/data/profile/admin_id'), ); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/profile/admin_id')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/profile/admin_id')); }); it('should return upload for everything else', () => { expect(sut.getUploadFolder(uploadFile.filename(UploadFieldName.ASSET_DATA, 'image.jpg'))).toEqual( - expect.stringContaining('upload/upload/admin_id/ra/nd'), + expect.stringContaining('/data/upload/admin_id/ra/nd'), ); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/upload/admin_id/ra/nd')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/upload/admin_id/ra/nd')); }); }); @@ -907,14 +907,14 @@ describe(AssetMediaService.name, () => { size: 1000, uuid: 'random-uuid', checksum: Buffer.from('checksum', 'utf8'), - originalPath: 'upload/upload/user-id/ra/nd/random-uuid.jpg', + originalPath: '/data/upload/user-id/ra/nd/random-uuid.jpg', } as unknown as Express.Multer.File; await sut.onUploadError(request, file); expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.FileDelete, - data: { files: [expect.stringContaining('upload/upload/user-id/ra/nd/random-uuid.jpg')] }, + data: { files: [expect.stringContaining('/data/upload/user-id/ra/nd/random-uuid.jpg')] }, }); }); }); diff --git a/server/src/services/auth.service.spec.ts b/server/src/services/auth.service.spec.ts index 58c2542310..6e19292f71 100644 --- a/server/src/services/auth.service.spec.ts +++ b/server/src/services/auth.service.spec.ts @@ -843,7 +843,7 @@ describe(AuthService.name, () => { ).resolves.toEqual(oauthResponse(user)); expect(mocks.user.update).toHaveBeenCalledWith(user.id, { - profileImagePath: expect.stringContaining(`upload/profile/${user.id}/${fileId}.jpg`), + profileImagePath: expect.stringContaining(`/data/profile/${user.id}/${fileId}.jpg`), profileChangedAt: expect.any(Date), }); expect(mocks.oauth.getProfilePicture).toHaveBeenCalledWith(pictureUrl); diff --git a/server/src/services/download.service.spec.ts b/server/src/services/download.service.spec.ts index 940767ff67..a85fd74c72 100644 --- a/server/src/services/download.service.spec.ts +++ b/server/src/services/download.service.spec.ts @@ -1,5 +1,4 @@ import { BadRequestException } from '@nestjs/common'; -import { APP_MEDIA_LOCATION } from 'src/constants'; import { DownloadResponseDto } from 'src/dtos/download.dto'; import { DownloadService } from 'src/services/download.service'; import { assetStub } from 'test/fixtures/asset.stub'; @@ -49,7 +48,7 @@ describe(DownloadService.name, () => { expect(archiveMock.addFile).toHaveBeenCalledTimes(1); expect(archiveMock.addFile).toHaveBeenNthCalledWith( 1, - expect.stringContaining('upload/library/IMG_123.jpg'), + expect.stringContaining('/data/library/IMG_123.jpg'), 'IMG_123.jpg', ); }); @@ -75,8 +74,8 @@ describe(DownloadService.name, () => { expect(mocks.logger.warn).toHaveBeenCalledTimes(2); expect(archiveMock.addFile).toHaveBeenCalledTimes(2); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, 'upload/library/IMG_123.jpg', 'IMG_123.jpg'); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, 'upload/library/IMG_456.jpg', 'IMG_456.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, '/data/library/IMG_123.jpg', 'IMG_123.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, '/data/library/IMG_456.jpg', 'IMG_456.jpg'); }); it('should download an archive', async () => { @@ -98,8 +97,8 @@ describe(DownloadService.name, () => { }); expect(archiveMock.addFile).toHaveBeenCalledTimes(2); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, 'upload/library/IMG_123.jpg', 'IMG_123.jpg'); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, 'upload/library/IMG_456.jpg', 'IMG_456.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, '/data/library/IMG_123.jpg', 'IMG_123.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, '/data/library/IMG_456.jpg', 'IMG_456.jpg'); }); it('should handle duplicate file names', async () => { @@ -121,8 +120,8 @@ describe(DownloadService.name, () => { }); expect(archiveMock.addFile).toHaveBeenCalledTimes(2); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, 'upload/library/IMG_123.jpg', 'IMG_123.jpg'); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, 'upload/library/IMG_123.jpg', 'IMG_123+1.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, '/data/library/IMG_123.jpg', 'IMG_123.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, '/data/library/IMG_123.jpg', 'IMG_123+1.jpg'); }); it('should be deterministic', async () => { @@ -144,8 +143,8 @@ describe(DownloadService.name, () => { }); expect(archiveMock.addFile).toHaveBeenCalledTimes(2); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, 'upload/library/IMG_123.jpg', 'IMG_123.jpg'); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, 'upload/library/IMG_123.jpg', 'IMG_123+1.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, '/data/library/IMG_123.jpg', 'IMG_123.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, '/data/library/IMG_123.jpg', 'IMG_123+1.jpg'); }); it('should resolve symlinks', async () => { @@ -291,7 +290,7 @@ describe(DownloadService.name, () => { id: 'asset-2', livePhotoVideoId: null, size: 23_456, - originalPath: APP_MEDIA_LOCATION + '/encoded-video/uuid-MP.mp4', + originalPath: '/data/encoded-video/uuid-MP.mp4', }, ]), ); diff --git a/server/src/services/job.service.spec.ts b/server/src/services/job.service.spec.ts index a57db736af..63d5fb2d06 100644 --- a/server/src/services/job.service.spec.ts +++ b/server/src/services/job.service.spec.ts @@ -11,7 +11,7 @@ describe(JobService.name, () => { let mocks: ServiceMocks; beforeEach(() => { - ({ sut, mocks } = newTestService(JobService, {})); + ({ sut, mocks } = newTestService(JobService)); mocks.config.getWorker.mockReturnValue(ImmichWorker.Microservices); }); diff --git a/server/src/services/library.service.spec.ts b/server/src/services/library.service.spec.ts index 308c80fb37..edd0d46bce 100644 --- a/server/src/services/library.service.spec.ts +++ b/server/src/services/library.service.spec.ts @@ -1,7 +1,7 @@ import { BadRequestException } from '@nestjs/common'; import { Stats } from 'node:fs'; import { defaults, SystemConfig } from 'src/config'; -import { APP_MEDIA_LOCATION, JOBS_LIBRARY_PAGINATION_SIZE } from 'src/constants'; +import { JOBS_LIBRARY_PAGINATION_SIZE } from 'src/constants'; import { mapLibrary } from 'src/dtos/library.dto'; import { AssetType, CronJob, ImmichWorker, JobName, JobStatus } from 'src/enum'; import { LibraryService } from 'src/services/library.service'; @@ -24,7 +24,7 @@ describe(LibraryService.name, () => { let mocks: ServiceMocks; beforeEach(() => { - ({ sut, mocks } = newTestService(LibraryService, {})); + ({ sut, mocks } = newTestService(LibraryService)); mocks.database.tryLock.mockResolvedValue(true); mocks.config.getWorker.mockReturnValue(ImmichWorker.Microservices); @@ -1171,10 +1171,10 @@ describe(LibraryService.name, () => { mocks.storage.checkFileExists.mockResolvedValue(true); - await expect(sut.validate('library-id', { importPaths: ['/data/user1/'] })).resolves.toEqual({ + await expect(sut.validate('library-id', { importPaths: ['/external/user1/'] })).resolves.toEqual({ importPaths: [ { - importPath: '/data/user1/', + importPath: '/external/user1/', isValid: true, message: undefined, }, @@ -1188,10 +1188,10 @@ describe(LibraryService.name, () => { throw error; }); - await expect(sut.validate('library-id', { importPaths: ['/data/user1/'] })).resolves.toEqual({ + await expect(sut.validate('library-id', { importPaths: ['/external/user1/'] })).resolves.toEqual({ importPaths: [ { - importPath: '/data/user1/', + importPath: '/external/user1/', isValid: false, message: 'Path does not exist (ENOENT)', }, @@ -1204,10 +1204,10 @@ describe(LibraryService.name, () => { isDirectory: () => false, } as Stats); - await expect(sut.validate('library-id', { importPaths: ['/data/user1/file'] })).resolves.toEqual({ + await expect(sut.validate('library-id', { importPaths: ['/external/user1/file'] })).resolves.toEqual({ importPaths: [ { - importPath: '/data/user1/file', + importPath: '/external/user1/file', isValid: false, message: 'Not a directory', }, @@ -1220,10 +1220,10 @@ describe(LibraryService.name, () => { throw new Error('Unknown error'); }); - await expect(sut.validate('library-id', { importPaths: ['/data/user1/'] })).resolves.toEqual({ + await expect(sut.validate('library-id', { importPaths: ['/external/user1/'] })).resolves.toEqual({ importPaths: [ { - importPath: '/data/user1/', + importPath: '/external/user1/', isValid: false, message: 'Error: Unknown error', }, @@ -1238,10 +1238,10 @@ describe(LibraryService.name, () => { mocks.storage.checkFileExists.mockResolvedValue(false); - await expect(sut.validate('library-id', { importPaths: ['/data/user1/'] })).resolves.toEqual({ + await expect(sut.validate('library-id', { importPaths: ['/external/user1/'] })).resolves.toEqual({ importPaths: [ { - importPath: '/data/user1/', + importPath: '/external/user1/', isValid: false, message: 'Lacking read permission for folder', }, @@ -1264,7 +1264,7 @@ describe(LibraryService.name, () => { }); it('should detect when import path is in immich media folder', async () => { - const importPaths = [APP_MEDIA_LOCATION + '/thumbs', `${process.cwd()}/xyz`, APP_MEDIA_LOCATION + '/library']; + const importPaths = ['/data/thumbs', `${process.cwd()}/xyz`, '/data/library']; const library = factory.library({ importPaths }); mocks.storage.stat.mockResolvedValue({ isDirectory: () => true } as Stats); diff --git a/server/src/services/media.service.spec.ts b/server/src/services/media.service.spec.ts index 0f4ba769c0..97351af37b 100644 --- a/server/src/services/media.service.spec.ts +++ b/server/src/services/media.service.spec.ts @@ -1,6 +1,5 @@ import { OutputInfo } from 'sharp'; import { SystemConfig } from 'src/config'; -import { APP_MEDIA_LOCATION } from 'src/constants'; import { Exif } from 'src/database'; import { AssetFileType, @@ -205,19 +204,19 @@ describe(MediaService.name, () => { entityId: assetStub.image.id, pathType: AssetPathType.FullSize, oldPath: '/uploads/user-id/fullsize/path.webp', - newPath: expect.stringContaining('upload/thumbs/user-id/as/se/asset-id-fullsize.jpeg'), + newPath: expect.stringContaining('/data/thumbs/user-id/as/se/asset-id-fullsize.jpeg'), }); expect(mocks.move.create).toHaveBeenCalledWith({ entityId: assetStub.image.id, pathType: AssetPathType.Preview, oldPath: '/uploads/user-id/thumbs/path.jpg', - newPath: expect.stringContaining('upload/thumbs/user-id/as/se/asset-id-preview.jpeg'), + newPath: expect.stringContaining('/data/thumbs/user-id/as/se/asset-id-preview.jpeg'), }); expect(mocks.move.create).toHaveBeenCalledWith({ entityId: assetStub.image.id, pathType: AssetPathType.Thumbnail, oldPath: '/uploads/user-id/webp/path.ext', - newPath: expect.stringContaining('upload/thumbs/user-id/as/se/asset-id-thumbnail.webp'), + newPath: expect.stringContaining('/data/thumbs/user-id/as/se/asset-id-thumbnail.webp'), }); expect(mocks.move.create).toHaveBeenCalledTimes(3); }); @@ -486,8 +485,8 @@ describe(MediaService.name, () => { mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.image); const thumbhashBuffer = Buffer.from('a thumbhash', 'utf8'); mocks.media.generateThumbhash.mockResolvedValue(thumbhashBuffer); - const previewPath = APP_MEDIA_LOCATION + `/thumbs/user-id/as/se/asset-id-preview.${format}`; - const thumbnailPath = APP_MEDIA_LOCATION + `/thumbs/user-id/as/se/asset-id-thumbnail.webp`; + const previewPath = `/data/thumbs/user-id/as/se/asset-id-preview.${format}`; + const thumbnailPath = `/data/thumbs/user-id/as/se/asset-id-thumbnail.webp`; await sut.handleGenerateThumbnails({ id: assetStub.image.id }); @@ -531,8 +530,8 @@ describe(MediaService.name, () => { mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.image); const thumbhashBuffer = Buffer.from('a thumbhash', 'utf8'); mocks.media.generateThumbhash.mockResolvedValue(thumbhashBuffer); - const previewPath = expect.stringContaining(`upload/thumbs/user-id/as/se/asset-id-preview.jpeg`); - const thumbnailPath = expect.stringContaining(`upload/thumbs/user-id/as/se/asset-id-thumbnail.${format}`); + const previewPath = expect.stringContaining(`/data/thumbs/user-id/as/se/asset-id-preview.jpeg`); + const thumbnailPath = expect.stringContaining(`/data/thumbs/user-id/as/se/asset-id-thumbnail.${format}`); await sut.handleGenerateThumbnails({ id: assetStub.image.id }); @@ -2895,7 +2894,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - APP_MEDIA_LOCATION + '/encoded-video/user-id/as/se/asset-id.mp4', + '/data/encoded-video/user-id/as/se/asset-id.mp4', expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:a copy']), diff --git a/server/src/services/metadata.service.spec.ts b/server/src/services/metadata.service.spec.ts index cc0956b9a8..1e304137e4 100644 --- a/server/src/services/metadata.service.spec.ts +++ b/server/src/services/metadata.service.spec.ts @@ -587,7 +587,7 @@ describe(MetadataService.name, () => { libraryId: assetStub.livePhotoWithOriginalFileName.libraryId, localDateTime: assetStub.livePhotoWithOriginalFileName.fileCreatedAt, originalFileName: 'asset_1.mp4', - originalPath: expect.stringContaining('upload/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4'), + originalPath: expect.stringContaining('/data/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4'), ownerId: assetStub.livePhotoWithOriginalFileName.ownerId, type: AssetType.Video, }); @@ -645,7 +645,7 @@ describe(MetadataService.name, () => { libraryId: assetStub.livePhotoWithOriginalFileName.libraryId, localDateTime: assetStub.livePhotoWithOriginalFileName.fileCreatedAt, originalFileName: 'asset_1.mp4', - originalPath: expect.stringContaining('upload/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4'), + originalPath: expect.stringContaining('/data/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4'), ownerId: assetStub.livePhotoWithOriginalFileName.ownerId, type: AssetType.Video, }); @@ -703,7 +703,7 @@ describe(MetadataService.name, () => { libraryId: assetStub.livePhotoWithOriginalFileName.libraryId, localDateTime: assetStub.livePhotoWithOriginalFileName.fileCreatedAt, originalFileName: 'asset_1.mp4', - originalPath: expect.stringContaining('upload/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4'), + originalPath: expect.stringContaining('/data/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4'), ownerId: assetStub.livePhotoWithOriginalFileName.ownerId, type: AssetType.Video, }); diff --git a/server/src/services/server.service.spec.ts b/server/src/services/server.service.spec.ts index 06ddd32601..a96a9925db 100644 --- a/server/src/services/server.service.spec.ts +++ b/server/src/services/server.service.spec.ts @@ -28,7 +28,7 @@ describe(ServerService.name, () => { diskUseRaw: 300, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('upload/library')); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('/data/library')); }); it('should return the disk space as KiB', async () => { @@ -44,7 +44,7 @@ describe(ServerService.name, () => { diskUseRaw: 300_000, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('upload/library')); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('/data/library')); }); it('should return the disk space as MiB', async () => { @@ -60,7 +60,7 @@ describe(ServerService.name, () => { diskUseRaw: 300_000_000, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('upload/library')); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('/data/library')); }); it('should return the disk space as GiB', async () => { @@ -80,7 +80,7 @@ describe(ServerService.name, () => { diskUseRaw: 300_000_000_000, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('upload/library')); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('/data/library')); }); it('should return the disk space as TiB', async () => { @@ -100,7 +100,7 @@ describe(ServerService.name, () => { diskUseRaw: 300_000_000_000_000, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('upload/library')); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('/data/library')); }); it('should return the disk space as PiB', async () => { @@ -120,7 +120,7 @@ describe(ServerService.name, () => { diskUseRaw: 300_000_000_000_000_000, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('upload/library')); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('/data/library')); }); }); diff --git a/server/src/services/storage-template.service.spec.ts b/server/src/services/storage-template.service.spec.ts index 882ffcd328..d0d7ea3a3c 100644 --- a/server/src/services/storage-template.service.spec.ts +++ b/server/src/services/storage-template.service.spec.ts @@ -1,6 +1,5 @@ import { Stats } from 'node:fs'; import { defaults, SystemConfig } from 'src/config'; -import { APP_MEDIA_LOCATION } from 'src/constants'; import { AssetPathType, JobStatus } from 'src/enum'; import { StorageTemplateService } from 'src/services/storage-template.service'; import { albumStub } from 'test/fixtures/album.stub'; @@ -111,10 +110,8 @@ describe(StorageTemplateService.name, () => { it('should migrate single moving picture', async () => { mocks.user.get.mockResolvedValue(userStub.user1); - const newMotionPicturePath = - APP_MEDIA_LOCATION + `/library/${motionAsset.ownerId}/2022/2022-06-19/${motionAsset.originalFileName}`; - const newStillPicturePath = - APP_MEDIA_LOCATION + `/library/${stillAsset.ownerId}/2022/2022-06-19/${stillAsset.originalFileName}`; + const newMotionPicturePath = `/data/library/${motionAsset.ownerId}/2022/2022-06-19/${motionAsset.originalFileName}`; + const newStillPicturePath = `/data/library/${stillAsset.ownerId}/2022/2022-06-19/${stillAsset.originalFileName}`; mocks.assetJob.getForStorageTemplateJob.mockResolvedValueOnce(stillAsset); mocks.assetJob.getForStorageTemplateJob.mockResolvedValueOnce(motionAsset); @@ -160,7 +157,7 @@ describe(StorageTemplateService.name, () => { expect(mocks.move.create).toHaveBeenCalledWith({ entityId: asset.id, newPath: expect.stringContaining( - `upload/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${album.albumName}/${asset.originalFileName}`, + `/data/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${album.albumName}/${asset.originalFileName}`, ), oldPath: asset.originalPath, pathType: AssetPathType.Original, @@ -183,7 +180,7 @@ describe(StorageTemplateService.name, () => { expect(mocks.move.create).toHaveBeenCalledWith({ entityId: asset.id, newPath: expect.stringContaining( - `upload/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/other/${month}/${asset.originalFileName}`, + `/data/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/other/${month}/${asset.originalFileName}`, ), oldPath: asset.originalPath, pathType: AssetPathType.Original, @@ -219,7 +216,7 @@ describe(StorageTemplateService.name, () => { expect(mocks.move.create).toHaveBeenCalledWith({ entityId: asset.id, newPath: expect.stringContaining( - `upload/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${month} - ${album.albumName}/${asset.originalFileName}`, + `/data/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${month} - ${album.albumName}/${asset.originalFileName}`, ), oldPath: asset.originalPath, pathType: AssetPathType.Original, @@ -243,9 +240,7 @@ describe(StorageTemplateService.name, () => { const month = (asset.fileCreatedAt.getMonth() + 1).toString().padStart(2, '0'); expect(mocks.move.create).toHaveBeenCalledWith({ entityId: asset.id, - newPath: - APP_MEDIA_LOCATION + - `/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${month}/${asset.originalFileName}`, + newPath: `/data/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${month}/${asset.originalFileName}`, oldPath: asset.originalPath, pathType: AssetPathType.Original, }); @@ -255,9 +250,8 @@ describe(StorageTemplateService.name, () => { mocks.user.get.mockResolvedValue(userStub.user1); const asset = assetStub.storageAsset(); - const previousFailedNewPath = - APP_MEDIA_LOCATION + `/library/${userStub.user1.id}/2023/Feb/${asset.originalFileName}`; - const newPath = APP_MEDIA_LOCATION + `/library/${userStub.user1.id}/2022/2022-06-19/${asset.originalFileName}`; + const previousFailedNewPath = `/data/library/${userStub.user1.id}/2023/Feb/${asset.originalFileName}`; + const newPath = `/data/library/${userStub.user1.id}/2022/2022-06-19/${asset.originalFileName}`; mocks.storage.checkFileExists.mockImplementation((path) => Promise.resolve(path === asset.originalPath)); mocks.move.getByEntity.mockResolvedValue({ @@ -296,9 +290,8 @@ describe(StorageTemplateService.name, () => { mocks.user.get.mockResolvedValue(userStub.user1); const asset = assetStub.storageAsset({ fileSizeInByte: 5000 }); - const previousFailedNewPath = - APP_MEDIA_LOCATION + `/library/${asset.ownerId}/2022/June/${asset.originalFileName}`; - const newPath = APP_MEDIA_LOCATION + `/library/${asset.ownerId}/2022/2022-06-19/${asset.originalFileName}`; + const previousFailedNewPath = `/data/library/${asset.ownerId}/2022/June/${asset.originalFileName}`; + const newPath = `/data/library/${asset.ownerId}/2022/2022-06-19/${asset.originalFileName}`; mocks.storage.checkFileExists.mockImplementation((path) => Promise.resolve(path === previousFailedNewPath)); mocks.storage.stat.mockResolvedValue({ size: 5000 } as Stats); @@ -332,8 +325,7 @@ describe(StorageTemplateService.name, () => { it('should fail move if copying and hash of asset and the new file do not match', async () => { mocks.user.get.mockResolvedValue(userStub.user1); - const newPath = - APP_MEDIA_LOCATION + `/library/${userStub.user1.id}/2022/2022-06-19/${testAsset.originalFileName}`; + const newPath = `/data/library/${userStub.user1.id}/2022/2022-06-19/${testAsset.originalFileName}`; mocks.storage.rename.mockRejectedValue({ code: 'EXDEV' }); mocks.storage.stat.mockResolvedValue({ size: 5000 } as Stats); @@ -375,8 +367,8 @@ describe(StorageTemplateService.name, () => { 'should fail to migrate previously failed move from previous new path when old path no longer exists if $reason validation fails', async ({ failedPathChecksum, failedPathSize }) => { mocks.user.get.mockResolvedValue(userStub.user1); - const previousFailedNewPath = `upload/library/${userStub.user1.id}/2023/Feb/${testAsset.originalFileName}`; - const newPath = `upload/library/${userStub.user1.id}/2023/2023-02-23/${testAsset.originalFileName}`; + const previousFailedNewPath = `/data/library/${userStub.user1.id}/2023/Feb/${testAsset.originalFileName}`; + const newPath = `/data/library/${userStub.user1.id}/2023/2023-02-23/${testAsset.originalFileName}`; mocks.storage.checkFileExists.mockImplementation((path) => Promise.resolve(previousFailedNewPath === path)); mocks.storage.stat.mockResolvedValue({ size: failedPathSize } as Stats); @@ -423,7 +415,7 @@ describe(StorageTemplateService.name, () => { it('should handle an asset with a duplicate destination', async () => { const asset = assetStub.storageAsset(); const oldPath = asset.originalPath; - const newPath = APP_MEDIA_LOCATION + `/library/user-id/2022/2022-06-19/${asset.originalFileName}`; + const newPath = `/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`; const newPath2 = newPath.replace('.jpg', '+1.jpg'); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); @@ -448,7 +440,7 @@ describe(StorageTemplateService.name, () => { }); it('should skip when an asset already matches the template', async () => { - const asset = assetStub.storageAsset({ originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id.jpg' }); + const asset = assetStub.storageAsset({ originalPath: '/data/library/user-id/2023/2023-02-23/asset-id.jpg' }); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); mocks.user.getList.mockResolvedValue([userStub.user1]); @@ -463,7 +455,7 @@ describe(StorageTemplateService.name, () => { }); it('should skip when an asset is probably a duplicate', async () => { - const asset = assetStub.storageAsset({ originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id+1.jpg' }); + const asset = assetStub.storageAsset({ originalPath: '/data/library/user-id/2023/2023-02-23/asset-id+1.jpg' }); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); mocks.user.getList.mockResolvedValue([userStub.user1]); @@ -480,7 +472,7 @@ describe(StorageTemplateService.name, () => { it('should move an asset', async () => { const asset = assetStub.storageAsset(); const oldPath = asset.originalPath; - const newPath = APP_MEDIA_LOCATION + `/library/user-id/2022/2022-06-19/${asset.originalFileName}`; + const newPath = `/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`; mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); mocks.user.getList.mockResolvedValue([userStub.user1]); mocks.move.create.mockResolvedValue({ @@ -508,7 +500,7 @@ describe(StorageTemplateService.name, () => { entityId: asset.id, pathType: AssetPathType.Original, oldPath: asset.originalPath, - newPath: `upload/library/${user.storageLabel}/2023/2023-02-23/${asset.originalFileName}`, + newPath: `/data/library/${user.storageLabel}/2023/2023-02-23/${asset.originalFileName}`, }); await sut.handleMigration(); @@ -516,12 +508,12 @@ describe(StorageTemplateService.name, () => { expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( '/original/path.jpg', - expect.stringContaining(`upload/library/${user.storageLabel}/2022/2022-06-19/${asset.originalFileName}`), + expect.stringContaining(`/data/library/${user.storageLabel}/2022/2022-06-19/${asset.originalFileName}`), ); expect(mocks.asset.update).toHaveBeenCalledWith({ id: asset.id, originalPath: expect.stringContaining( - `upload/library/${user.storageLabel}/2022/2022-06-19/${asset.originalFileName}`, + `/data/library/${user.storageLabel}/2022/2022-06-19/${asset.originalFileName}`, ), }); }); @@ -529,7 +521,7 @@ describe(StorageTemplateService.name, () => { it('should copy the file if rename fails due to EXDEV (rename across filesystems)', async () => { const asset = assetStub.storageAsset({ originalPath: '/path/to/original.jpg', fileSizeInByte: 5000 }); const oldPath = asset.originalPath; - const newPath = APP_MEDIA_LOCATION + `/library/user-id/2022/2022-06-19/${asset.originalFileName}`; + const newPath = `/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`; mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); mocks.storage.rename.mockRejectedValue({ code: 'EXDEV' }); mocks.user.getList.mockResolvedValue([userStub.user1]); @@ -577,7 +569,7 @@ describe(StorageTemplateService.name, () => { entityId: asset.id, pathType: AssetPathType.Original, oldPath: asset.originalPath, - newPath: `upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`, + newPath: `/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`, }); mocks.storage.stat.mockResolvedValue({ size: 100, @@ -588,14 +580,14 @@ describe(StorageTemplateService.name, () => { expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( '/original/path.jpg', - expect.stringContaining(`upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`), + expect.stringContaining(`/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`), ); expect(mocks.storage.copyFile).toHaveBeenCalledWith( '/original/path.jpg', - expect.stringContaining(`upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`), + expect.stringContaining(`/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`), ); expect(mocks.storage.stat).toHaveBeenCalledWith( - expect.stringContaining(`upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`), + expect.stringContaining(`/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`), ); expect(mocks.asset.update).not.toHaveBeenCalled(); }); @@ -619,7 +611,7 @@ describe(StorageTemplateService.name, () => { expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( '/original/path.jpg', - expect.stringContaining(`upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`), + expect.stringContaining(`/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`), ); expect(mocks.asset.update).not.toHaveBeenCalled(); }); @@ -630,7 +622,7 @@ describe(StorageTemplateService.name, () => { const user = factory.userAdmin({ storageLabel: 'label-1' }); const asset = assetStub.storageAsset({ ownerId: user.id, - originalPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.heic`, + originalPath: `/data/library/${user.id}/2022/2022-06-19/IMG_7065.heic`, originalFileName: 'IMG_7065.HEIC', }); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); @@ -639,16 +631,16 @@ describe(StorageTemplateService.name, () => { id: '123', entityId: asset.id, pathType: AssetPathType.Original, - oldPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.heic`, - newPath: `upload/library/${user.id}/2023/2023-02-23/IMG_7065.heic`, + oldPath: `/data/library/${user.id}/2022/2022-06-19/IMG_7065.heic`, + newPath: `/data/library/${user.id}/2023/2023-02-23/IMG_7065.heic`, }); await sut.handleMigration(); expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( - expect.stringContaining(`upload/library/${user.id}/2022/2022-06-19/IMG_7065.heic`), - expect.stringContaining(`upload/library/${user.storageLabel}/2022/2022-06-19/IMG_7065.heic`), + expect.stringContaining(`/data/library/${user.id}/2022/2022-06-19/IMG_7065.heic`), + expect.stringContaining(`/data/library/${user.storageLabel}/2022/2022-06-19/IMG_7065.heic`), ); }); @@ -656,7 +648,7 @@ describe(StorageTemplateService.name, () => { const user = factory.userAdmin(); const asset = assetStub.storageAsset({ ownerId: user.id, - originalPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`, + originalPath: `/data/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`, originalFileName: 'IMG_7065.HEIC', }); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); @@ -665,16 +657,16 @@ describe(StorageTemplateService.name, () => { id: '123', entityId: asset.id, pathType: AssetPathType.Original, - oldPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`, - newPath: `upload/library/${user.id}/2023/2023-02-23/IMG_7065.heic`, + oldPath: `/data/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`, + newPath: `/data/library/${user.id}/2023/2023-02-23/IMG_7065.heic`, }); await sut.handleMigration(); expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( - expect.stringContaining(`upload/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`), - expect.stringContaining(`upload/library/${user.id}/2022/2022-06-19/IMG_7065.heic`), + expect.stringContaining(`/data/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`), + expect.stringContaining(`/data/library/${user.id}/2022/2022-06-19/IMG_7065.heic`), ); }); @@ -682,7 +674,7 @@ describe(StorageTemplateService.name, () => { const user = factory.userAdmin(); const asset = assetStub.storageAsset({ ownerId: user.id, - originalPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`, + originalPath: `/data/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`, originalFileName: 'IMG_7065.JPEG', }); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); @@ -691,16 +683,16 @@ describe(StorageTemplateService.name, () => { id: '123', entityId: asset.id, pathType: AssetPathType.Original, - oldPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`, - newPath: `upload/library/${user.id}/2023/2023-02-23/IMG_7065.jpg`, + oldPath: `/data/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`, + newPath: `/data/library/${user.id}/2023/2023-02-23/IMG_7065.jpg`, }); await sut.handleMigration(); expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( - expect.stringContaining(`upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`), - expect.stringContaining(`upload/library/${user.id}/2022/2022-06-19/IMG_7065.jpg`), + expect.stringContaining(`/data/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`), + expect.stringContaining(`/data/library/${user.id}/2022/2022-06-19/IMG_7065.jpg`), ); }); @@ -708,7 +700,7 @@ describe(StorageTemplateService.name, () => { const user = factory.userAdmin(); const asset = assetStub.storageAsset({ ownerId: user.id, - originalPath: 'upload/library/user-id/2022/2022-06-19/IMG_7065.JPG', + originalPath: '/data/library/user-id/2022/2022-06-19/IMG_7065.JPG', originalFileName: 'IMG_7065.JPG', }); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); @@ -717,16 +709,16 @@ describe(StorageTemplateService.name, () => { id: '123', entityId: asset.id, pathType: AssetPathType.Original, - oldPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPG`, - newPath: `upload/library/${user.id}/2023/2023-02-23/IMG_7065.jpg`, + oldPath: `/data/library/${user.id}/2022/2022-06-19/IMG_7065.JPG`, + newPath: `/data/library/${user.id}/2023/2023-02-23/IMG_7065.jpg`, }); await sut.handleMigration(); expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( - expect.stringContaining(`upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPG`), - expect.stringContaining(`upload/library/${user.id}/2022/2022-06-19/IMG_7065.jpg`), + expect.stringContaining(`/data/library/${user.id}/2022/2022-06-19/IMG_7065.JPG`), + expect.stringContaining(`/data/library/${user.id}/2022/2022-06-19/IMG_7065.jpg`), ); }); }); diff --git a/server/src/services/storage.service.spec.ts b/server/src/services/storage.service.spec.ts index 0303c11649..3ca9cd7ce2 100644 --- a/server/src/services/storage.service.spec.ts +++ b/server/src/services/storage.service.spec.ts @@ -20,6 +20,14 @@ describe(StorageService.name, () => { it('should enable mount folder checking', async () => { mocks.systemMetadata.get.mockResolvedValue(null); mocks.asset.getFileSamples.mockResolvedValue([]); + mocks.config.getEnv.mockReturnValue( + mockEnvData({ + storage: { + ignoreMountCheckErrors: false, + mediaLocation: '/data', + }, + }), + ); await expect(sut.onBootstrap()).resolves.toBeUndefined(); @@ -33,34 +41,34 @@ describe(StorageService.name, () => { upload: true, }, }); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/encoded-video')); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/library')); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/profile')); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/thumbs')); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/upload')); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/backups')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/encoded-video')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/library')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/profile')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/thumbs')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/upload')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/backups')); expect(mocks.storage.createFile).toHaveBeenCalledWith( - expect.stringContaining('upload/encoded-video/.immich'), + expect.stringContaining('/data/encoded-video/.immich'), expect.any(Buffer), ); expect(mocks.storage.createFile).toHaveBeenCalledWith( - expect.stringContaining('upload/library/.immich'), + expect.stringContaining('/data/library/.immich'), expect.any(Buffer), ); expect(mocks.storage.createFile).toHaveBeenCalledWith( - expect.stringContaining('upload/profile/.immich'), + expect.stringContaining('/data/profile/.immich'), expect.any(Buffer), ); expect(mocks.storage.createFile).toHaveBeenCalledWith( - expect.stringContaining('upload/thumbs/.immich'), + expect.stringContaining('/data/thumbs/.immich'), expect.any(Buffer), ); expect(mocks.storage.createFile).toHaveBeenCalledWith( - expect.stringContaining('upload/upload/.immich'), + expect.stringContaining('/data/upload/.immich'), expect.any(Buffer), ); expect(mocks.storage.createFile).toHaveBeenCalledWith( - expect.stringContaining('upload/backups/.immich'), + expect.stringContaining('/data/backups/.immich'), expect.any(Buffer), ); }); @@ -77,6 +85,14 @@ describe(StorageService.name, () => { }, }); mocks.asset.getFileSamples.mockResolvedValue([]); + mocks.config.getEnv.mockReturnValue( + mockEnvData({ + storage: { + ignoreMountCheckErrors: false, + mediaLocation: '/data', + }, + }), + ); await expect(sut.onBootstrap()).resolves.toBeUndefined(); @@ -91,15 +107,15 @@ describe(StorageService.name, () => { }, }); expect(mocks.storage.mkdirSync).toHaveBeenCalledTimes(2); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/library')); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/backups')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/library')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/backups')); expect(mocks.storage.createFile).toHaveBeenCalledTimes(2); expect(mocks.storage.createFile).toHaveBeenCalledWith( - expect.stringContaining('upload/library/.immich'), + expect.stringContaining('/data/library/.immich'), expect.any(Buffer), ); expect(mocks.storage.createFile).toHaveBeenCalledWith( - expect.stringContaining('upload/backups/.immich'), + expect.stringContaining('/data/backups/.immich'), expect.any(Buffer), ); }); diff --git a/server/src/services/storage.service.ts b/server/src/services/storage.service.ts index 6c99194abd..cd8910305a 100644 --- a/server/src/services/storage.service.ts +++ b/server/src/services/storage.service.ts @@ -1,9 +1,16 @@ import { Injectable } from '@nestjs/common'; import { join } from 'node:path'; -import { APP_MEDIA_LOCATION } from 'src/constants'; import { StorageCore } from 'src/cores/storage.core'; import { OnEvent, OnJob } from 'src/decorators'; -import { DatabaseLock, JobName, JobStatus, QueueName, StorageFolder, SystemMetadataKey } from 'src/enum'; +import { + BootstrapEventPriority, + DatabaseLock, + JobName, + JobStatus, + QueueName, + StorageFolder, + SystemMetadataKey, +} from 'src/enum'; import { BaseService } from 'src/services/base.service'; import { JobOf, SystemFlags } from 'src/types'; import { ImmichStartupError } from 'src/utils/misc'; @@ -12,9 +19,32 @@ const docsMessage = `Please see https://immich.app/docs/administration/system-in @Injectable() export class StorageService extends BaseService { - @OnEvent({ name: 'AppBootstrap' }) - async onBootstrap() { + private detectMediaLocation(): string { const envData = this.configRepository.getEnv(); + if (envData.storage.mediaLocation) { + return envData.storage.mediaLocation; + } + + const targets: string[] = []; + const candidates = ['/data', '/usr/src/app/upload']; + + for (const candidate of candidates) { + const exists = this.storageRepository.existsSync(candidate); + if (exists) { + targets.push(candidate); + } + } + + if (targets.length === 1) { + return targets[0]; + } + + return '/usr/src/app/upload'; + } + + @OnEvent({ name: 'AppBootstrap', priority: BootstrapEventPriority.StorageService }) + async onBootstrap() { + StorageCore.setMediaLocation(this.detectMediaLocation()); await this.databaseRepository.withLock(DatabaseLock.SystemFileMounts, async () => { const flags = @@ -53,6 +83,7 @@ export class StorageService extends BaseService { this.logger.log('Successfully verified system mount folder checks'); } catch (error) { + const envData = this.configRepository.getEnv(); if (envData.storage.ignoreMountCheckErrors) { this.logger.error(error as Error); this.logger.warn('Ignoring mount folder errors'); @@ -63,30 +94,34 @@ export class StorageService extends BaseService { }); await this.databaseRepository.withLock(DatabaseLock.MediaLocation, async () => { - const current = APP_MEDIA_LOCATION; - const savedValue = await this.systemMetadataRepository.get(SystemMetadataKey.MediaLocation); - let previous = savedValue?.location || ''; + const current = StorageCore.getMediaLocation(); + const samples = await this.assetRepository.getFileSamples(); + if (samples.length > 0) { + const originalPath = samples[0].originalPath; + const savedValue = await this.systemMetadataRepository.get(SystemMetadataKey.MediaLocation); + let previous = savedValue?.location || ''; - if (previous !== current) { - this.logger.log(`Media location changed (from=${previous}, to=${current})`); - - const samples = await this.assetRepository.getFileSamples(); - if (samples.length > 0) { - const originalPath = samples[0].originalPath; - if (!previous) { - previous = originalPath.startsWith('upload/') ? 'upload' : '/usr/src/app/upload'; - } - - if (previous && originalPath.startsWith(previous)) { - this.logger.warn( - `Detected a change to IMMICH_MEDIA_LOCATION, performing an automatic migration of file paths from ${previous} to ${current}, this may take awhile`, - ); - await this.databaseRepository.migrateFilePaths(previous, current); - } + if (!previous) { + previous = originalPath.startsWith('upload/') ? 'upload' : '/usr/src/app/upload'; } - await this.systemMetadataRepository.set(SystemMetadataKey.MediaLocation, { location: current }); + if (previous !== current) { + this.logger.log(`Media location changed (from=${previous}, to=${current})`); + + if (!originalPath.startsWith(previous)) { + throw new Error( + 'Detected an inconsistent media location. For more information, see https://immich.app/errors#inconsistent-media-location', + ); + } + + this.logger.warn( + `Detected a change to media location, performing an automatic migration of file paths from ${previous} to ${current}, this may take awhile`, + ); + await this.databaseRepository.migrateFilePaths(previous, current); + } } + + await this.systemMetadataRepository.set(SystemMetadataKey.MediaLocation, { location: current }); }); } diff --git a/server/src/services/user.service.spec.ts b/server/src/services/user.service.spec.ts index b4e616974e..bd896ffc24 100644 --- a/server/src/services/user.service.spec.ts +++ b/server/src/services/user.service.spec.ts @@ -236,23 +236,23 @@ describe(UserService.name, () => { await sut.handleUserDelete({ id: user.id }); expect(mocks.storage.unlinkDir).toHaveBeenCalledWith( - expect.stringContaining('upload/library/deleted-user'), + expect.stringContaining('/data/library/deleted-user'), options, ); expect(mocks.storage.unlinkDir).toHaveBeenCalledWith( - expect.stringContaining('upload/upload/deleted-user'), + expect.stringContaining('/data/upload/deleted-user'), options, ); expect(mocks.storage.unlinkDir).toHaveBeenCalledWith( - expect.stringContaining('upload/profile/deleted-user'), + expect.stringContaining('/data/profile/deleted-user'), options, ); expect(mocks.storage.unlinkDir).toHaveBeenCalledWith( - expect.stringContaining('upload/thumbs/deleted-user'), + expect.stringContaining('/data/thumbs/deleted-user'), options, ); expect(mocks.storage.unlinkDir).toHaveBeenCalledWith( - expect.stringContaining('upload/encoded-video/deleted-user'), + expect.stringContaining('/data/encoded-video/deleted-user'), options, ); expect(mocks.album.deleteAll).toHaveBeenCalledWith(user.id); @@ -268,7 +268,7 @@ describe(UserService.name, () => { const options = { force: true, recursive: true }; - expect(mocks.storage.unlinkDir).toHaveBeenCalledWith(expect.stringContaining('upload/library/admin'), options); + expect(mocks.storage.unlinkDir).toHaveBeenCalledWith(expect.stringContaining('data/library/admin'), options); }); }); diff --git a/server/test/fixtures/asset.stub.ts b/server/test/fixtures/asset.stub.ts index 76cf71d34d..066996ead5 100644 --- a/server/test/fixtures/asset.stub.ts +++ b/server/test/fixtures/asset.stub.ts @@ -65,7 +65,7 @@ export const assetStub = { owner: userStub.user1, ownerId: 'user-id', deviceId: 'device-id', - originalPath: 'upload/library/IMG_123.jpg', + originalPath: '/data/library/IMG_123.jpg', files: [thumbnailFile], checksum: Buffer.from('file hash', 'utf8'), type: AssetType.Image, @@ -101,7 +101,7 @@ export const assetStub = { owner: userStub.user1, ownerId: 'user-id', deviceId: 'device-id', - originalPath: 'upload/library/IMG_456.jpg', + originalPath: '/data/library/IMG_456.jpg', files: [previewFile], checksum: Buffer.from('file hash', 'utf8'), type: AssetType.Image, diff --git a/server/test/medium/specs/services/storage.service.spec.ts b/server/test/medium/specs/services/storage.service.spec.ts new file mode 100644 index 0000000000..6c3fc487d4 --- /dev/null +++ b/server/test/medium/specs/services/storage.service.spec.ts @@ -0,0 +1,46 @@ +import { Kysely } from 'kysely'; +import { AssetRepository } from 'src/repositories/asset.repository'; +import { ConfigRepository } from 'src/repositories/config.repository'; +import { DatabaseRepository } from 'src/repositories/database.repository'; +import { LoggingRepository } from 'src/repositories/logging.repository'; +import { StorageRepository } from 'src/repositories/storage.repository'; +import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository'; +import { DB } from 'src/schema'; +import { StorageService } from 'src/services/storage.service'; +import { newMediumService } from 'test/medium.factory'; +import { mockEnvData } from 'test/repositories/config.repository.mock'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = (db?: Kysely) => { + return newMediumService(StorageService, { + database: db || defaultDatabase, + real: [AssetRepository, DatabaseRepository, SystemMetadataRepository], + mock: [StorageRepository, ConfigRepository, LoggingRepository], + }); +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe(StorageService.name, () => { + describe('onBoostrap', () => { + it('should work', async () => { + const { sut, ctx } = setup(); + + const configMock = ctx.getMock(ConfigRepository); + configMock.getEnv.mockReturnValue(mockEnvData({})); + + const storageMock = ctx.getMock(StorageRepository); + storageMock.mkdirSync.mockReturnValue(void 0); + storageMock.existsSync.mockReturnValue(true); + storageMock.createFile.mockResolvedValue(void 0); + storageMock.overwriteFile.mockResolvedValue(void 0); + storageMock.readFile.mockResolvedValue(Buffer.from('test content')); + + await expect(sut.onBootstrap()).resolves.toBeUndefined(); + }); + }); +}); diff --git a/server/test/repositories/storage.repository.mock.ts b/server/test/repositories/storage.repository.mock.ts index dc3fb3e16c..9752a39441 100644 --- a/server/test/repositories/storage.repository.mock.ts +++ b/server/test/repositories/storage.repository.mock.ts @@ -41,10 +41,9 @@ export const makeMockWatcher = return () => Promise.resolve(); }; -export const newStorageRepositoryMock = (reset = true): Mocked> => { - if (reset) { - StorageCore.reset(); - } +export const newStorageRepositoryMock = (): Mocked> => { + StorageCore.reset(); + StorageCore.setMediaLocation('/data'); return { createZipStream: vitest.fn(), @@ -53,6 +52,7 @@ export const newStorageRepositoryMock = (reset = true): Mocked = {}) => ({ livePhotoVideoId: null, localDateTime: newDate(), originalFileName: 'IMG_123.jpg', - originalPath: `upload/12/34/IMG_123.jpg`, + originalPath: `/data/12/34/IMG_123.jpg`, ownerId: newUuid(), sidecarPath: null, stackId: null, diff --git a/web/src/lib/utils/asset-utils.spec.ts b/web/src/lib/utils/asset-utils.spec.ts index b0f76f3bc2..52517975e2 100644 --- a/web/src/lib/utils/asset-utils.spec.ts +++ b/web/src/lib/utils/asset-utils.spec.ts @@ -33,21 +33,21 @@ describe('get asset filename', () => { { asset: { originalFileName: 'filename', - originalPath: 'upload/library/test/2016/2016-08-30/filename.jpg', + originalPath: '/data/library/test/2016/2016-08-30/filename.jpg', }, result: 'filename.jpg', }, { asset: { originalFileName: 'new-filename', - originalPath: 'upload/library/89d14e47-a40d-4cae-a347-a914cdef1f22/2016/2016-08-30/filename.jpg', + originalPath: '/data/library/89d14e47-a40d-4cae-a347-a914cdef1f22/2016/2016-08-30/filename.jpg', }, result: 'new-filename.jpg', }, { asset: { originalFileName: 'new-filename.txt', - originalPath: 'upload/library/test/2016/2016-08-30/filename.txt.jpg', + originalPath: '/data/library/test/2016/2016-08-30/filename.txt.jpg', }, result: 'new-filename.txt.jpg', }, From 290e325c5cd07e30fe484272cc55eda0a3d152f2 Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Tue, 29 Jul 2025 16:17:33 -0500 Subject: [PATCH 106/748] feat: drift description editor (#20383) * feat: drift description editor * chore: use focus node * chore: code review fixes * chore: move description update to action.service * refactor * refactor --------- Co-authored-by: Alex --- i18n/en.json | 1 + .../repositories/remote_asset.repository.dart | 6 ++ .../asset_viewer/bottom_sheet.widget.dart | 78 +++++++++++++++++++ .../infrastructure/action.provider.dart | 16 ++++ .../repositories/asset_api.repository.dart | 4 + mobile/lib/services/action.service.dart | 8 ++ 6 files changed, 113 insertions(+) diff --git a/i18n/en.json b/i18n/en.json index 5f7f166222..524466f4e3 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -988,6 +988,7 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Add Description...", + "exif_bottom_sheet_description_error": "Error updating description", "exif_bottom_sheet_details": "DETAILS", "exif_bottom_sheet_location": "LOCATION", "exif_bottom_sheet_people": "PEOPLE", diff --git a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart index 1ab62b3442..33735f1709 100644 --- a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart @@ -226,6 +226,12 @@ class RemoteAssetRepository extends DriftDatabaseRepository { }); } + Future updateDescription(String assetId, String description) async { + await (_db.remoteExifEntity.update()..where((row) => row.assetId.equals(assetId))).write( + RemoteExifEntityCompanion(description: Value(description)), + ); + } + Future getCount() { return _db.managers.remoteAssetEntity.count(); } diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart index 73ec6b456a..f27261a357 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart @@ -18,10 +18,12 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_b import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart'; import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; import 'package:immich_mobile/providers/routes.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/utils/bytes_units.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; const _kSeparator = ' • '; @@ -147,6 +149,7 @@ class _AssetDetailBottomSheet extends ConsumerWidget { title: _getDateTime(context, asset), titleStyle: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600), ), + if (exifInfo != null) _SheetAssetDescription(exif: exifInfo), const SheetLocationDetails(), // Details header _SheetTile( @@ -234,3 +237,78 @@ class _SheetTile extends StatelessWidget { ); } } + +class _SheetAssetDescription extends ConsumerStatefulWidget { + final ExifInfo exif; + + const _SheetAssetDescription({required this.exif}); + + @override + ConsumerState<_SheetAssetDescription> createState() => _SheetAssetDescriptionState(); +} + +class _SheetAssetDescriptionState extends ConsumerState<_SheetAssetDescription> { + late TextEditingController _controller; + final _descriptionFocus = FocusNode(); + + @override + void initState() { + super.initState(); + _controller = TextEditingController(text: widget.exif.description ?? ''); + } + + Future saveDescription(String? previousDescription) async { + final newDescription = _controller.text.trim(); + + if (newDescription == previousDescription) { + _descriptionFocus.unfocus(); + return; + } + + final editAction = await ref.read(actionProvider.notifier).updateDescription(ActionSource.viewer, newDescription); + + if (!editAction.success) { + _controller.text = previousDescription ?? ''; + + ImmichToast.show( + context: context, + msg: 'exif_bottom_sheet_description_error'.t(context: context), + toastType: ToastType.error, + ); + } + + _descriptionFocus.unfocus(); + } + + @override + Widget build(BuildContext context) { + // Watch the current asset EXIF provider to get updates + final currentExifInfo = ref.watch(currentAssetExifProvider).valueOrNull; + + // Update controller text when EXIF data changes + final currentDescription = currentExifInfo?.description ?? ''; + if (_controller.text != currentDescription && !_descriptionFocus.hasFocus) { + _controller.text = currentDescription; + } + + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8), + child: TextField( + controller: _controller, + keyboardType: TextInputType.multiline, + focusNode: _descriptionFocus, + maxLines: null, // makes it grow as text is added + decoration: InputDecoration( + hintText: 'exif_bottom_sheet_description'.t(context: context), + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + disabledBorder: InputBorder.none, + errorBorder: InputBorder.none, + focusedErrorBorder: InputBorder.none, + ), + onTapOutside: (_) => saveDescription(currentExifInfo?.description), + ), + ); + } +} diff --git a/mobile/lib/providers/infrastructure/action.provider.dart b/mobile/lib/providers/infrastructure/action.provider.dart index 9d05a6ecab..69c0532303 100644 --- a/mobile/lib/providers/infrastructure/action.provider.dart +++ b/mobile/lib/providers/infrastructure/action.provider.dart @@ -250,6 +250,22 @@ class ActionNotifier extends Notifier { } } + Future updateDescription(ActionSource source, String description) async { + final ids = _getRemoteIdsForSource(source); + if (ids.length != 1) { + _logger.warning('updateDescription called with multiple assets, expected single asset'); + return ActionResult(count: ids.length, success: false, error: 'Expected single asset for description update'); + } + + try { + final isUpdated = await _service.updateDescription(ids.first, description); + return ActionResult(count: 1, success: isUpdated); + } catch (error, stack) { + _logger.severe('Failed to update description for asset', error, stack); + return ActionResult(count: 1, success: false, error: error.toString()); + } + } + Future stack(String userId, ActionSource source) async { final ids = _getOwnedRemoteIdsForSource(source); try { diff --git a/mobile/lib/repositories/asset_api.repository.dart b/mobile/lib/repositories/asset_api.repository.dart index 26147292d7..bbb176ffa7 100644 --- a/mobile/lib/repositories/asset_api.repository.dart +++ b/mobile/lib/repositories/asset_api.repository.dart @@ -93,6 +93,10 @@ class AssetApiRepository extends ApiRepository { // we need to get the MIME of the thumbnail once that gets added to the API return response.originalMimeType; } + + Future updateDescription(String assetId, String description) { + return _api.updateAsset(assetId, UpdateAssetDto(description: description)); + } } extension on StackResponseDto { diff --git a/mobile/lib/services/action.service.dart b/mobile/lib/services/action.service.dart index 5a23f16534..f45071e7f7 100644 --- a/mobile/lib/services/action.service.dart +++ b/mobile/lib/services/action.service.dart @@ -170,6 +170,14 @@ class ActionService { return removedCount; } + Future updateDescription(String assetId, String description) async { + // update remote first, then local to ensure consistency + await _assetApiRepository.updateDescription(assetId, description); + await _remoteAssetRepository.updateDescription(assetId, description); + + return true; + } + Future stack(String userId, List remoteIds) async { final stack = await _assetApiRepository.stack(remoteIds); await _remoteAssetRepository.stack(userId, stack); From 9b65cd4d7b2de166d8835df59f928ab100ae17a5 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Tue, 29 Jul 2025 17:28:02 -0400 Subject: [PATCH 107/748] feat!: remove typeorm (#20366) feat: remove typeorm --- docs/src/pages/errors.md | 2 +- server/src/bin/migrations.ts | 27 +--- .../controllers/download.controller.spec.ts | 2 +- .../1645130759468-CreateUserTable.ts | 23 --- .../1645130777674-CreateDeviceInfoTable.ts | 26 ---- .../1645130805273-CreateAssetsTable.ts | 31 ---- .../1645130817965-CreateExifTable.ts | 42 ------ .../1645130870184-CreateSmartInfoTable.ts | 30 ---- .../1646249209023-AddExifTextSearchColumn.ts | 25 ---- ...1646249734844-CreateExifTextSearchIndex.ts | 17 --- .../1646709533213-AddRegionCityToExIf.ts | 29 ---- ...46710459852-AddLocationToExifTextSearch.ts | 37 ----- ...648317474768-AddObjectColumnToSmartInfo.ts | 18 --- ...16111-CreateSharedAlbumAndRelatedTables.ts | 70 --------- ...3525943-UpdateUserTableWithAdminAndName.ts | 36 ----- ...3214255670-UpdateAssetTableWithWebpPath.ts | 17 --- ...583-UpdateAssetTableWithEncodeVideoPath.ts | 17 --- .../1655401127251-RenameSharedAlbums.ts | 19 --- ...56338626260-RenameIsFirstLoggedInColumn.ts | 17 --- ...656888591977-RenameAssetAlbumIdSequence.ts | 17 --- ...6888918620-DropExifTextSearchableColumn.ts | 32 ----- ...1566-MatchMigrationsWithTypeORMEntities.ts | 53 ------- ...470248-AddExifImageNameAsSearchableText.ts | 22 --- .../migrations/1661011331242-AddCaption.ts | 15 -- ...919411-ChangeExifFileSizeInByteToBigInt.ts | 19 --- .../1661881837496-AddAssetChecksum.ts | 17 --- ...UpdateAssetTableWithNewUniqueConstraint.ts | 17 --- ...365521-FixTimestampDataTypeInAssetTable.ts | 21 --- .../1665540663419-CreateSystemConfigTable.ts | 15 -- ...60744-AddingDeletedAtColumnInUserEntity.ts | 13 -- ...-AddLivePhotosRelatedColumnToAssetTable.ts | 16 --- .../1668835311083-UpdateUserTableForOIDC.ts | 16 --- .../src/migrations/1670104716264-OAuthId.ts | 14 -- .../1670257571385-CreateTagsTable.ts | 26 ---- .../1670607437008-TruncateOldConfigItems.ts | 11 -- ...0633210032-AddUserEmailUniqueConstraint.ts | 14 -- .../1672109862870-DropSaltColumn.ts | 14 -- .../migrations/1672502270115-AddAPIKeys.ts | 16 --- .../1673150490490-AddSharedLinkTable.ts | 28 ---- ...907194740-AddMorePermissionToSharedLink.ts | 15 -- ...4263302005-RemoveVideoCodecConfigOption.ts | 12 -- .../1674342044239-CreateUserTokenEntity.ts | 16 --- ...757936889-AlterExifExposureTimeToString.ts | 16 --- .../1674774248319-TruncateAPIKeys.ts | 14 -- ...9-AddSharedLinkUserForeignKeyConstraint.ts | 18 --- ...2-AddUpdatedAtColumnToAlbumsUsersAssets.ts | 17 --- ...909594-AddAlbumUserForeignKeyConstraint.ts | 19 --- .../1675808874445-APIKeyUUIDPrimaryKey.ts | 20 --- .../1675812532822-FixAlbumEntityTypeORM.ts | 82 ----------- .../1676437878377-AppleContentIdentifier.ts | 15 -- .../1676680127415-FixAssetRelations.ts | 30 ---- .../1676721296440-AssetCreatedAtField.ts | 14 -- ...1676848629119-ExifEntityDefinitionFixes.ts | 28 ---- ...8694786-SharedLinkEntityDefinitionFixes.ts | 14 -- ...52143506-SmartInfoEntityDefinitionFixes.ts | 24 ---- .../1677497925328-AddExifTimeZone.ts | 14 -- ...43119-AddIndexForAlbumInSharedLinkTable.ts | 14 -- .../1677613712565-AlbumThumbnailRelation.ts | 60 -------- .../1677971458822-AddCLIPEncodeDataColumn.ts | 13 -- .../1679751316282-UpdateTranscodeOption.ts | 27 ---- .../1679901204458-ClipEmbeddingFloat4.ts | 17 --- .../1680632845740-AddIsArchivedColumn.ts | 14 -- ...680694465853-RemoveRedundantConstraints.ts | 16 --- ...4628393-AddOriginalFileNameToAssetTable.ts | 30 ---- ...1159594469-RemoveImageNameFromEXIFTable.ts | 32 ----- .../1682371561743-FixNullableRelations.ts | 21 --- .../1682371791038-AddDeviceInfoToUserToken.ts | 16 --- .../1682710252424-DropDeviceInfoTable.ts | 26 ---- .../1683808254676-AddPartnersTable.ts | 18 --- .../1684255168091-AddFacialTables.ts | 22 --- .../1684273840676-AddSidecarFile.ts | 14 -- .../1684328185099-RequireChecksumNotNull.ts | 19 --- .../1684410565398-AddStorageLabel.ts | 16 --- ...867360825-AddUserTokenAndAPIKeyCascades.ts | 20 --- .../1685044328272-AddSharedLinkCascade.ts | 16 --- .../1685370430343-UserDatesTimestamptz.ts | 16 --- .../1685731372040-RemoveInvalidCoordinates.ts | 16 --- .../migrations/1686584273471-ImportAsset.ts | 18 --- .../1686762895180-AddThumbhashColumn.ts | 13 -- .../1688241394489-AddDetectFaceResultInfo.ts | 23 --- .../1688392120838-AddLibraryTable.ts | 58 -------- .../1689001889950-DropMimeTypeColumn.ts | 14 -- .../1689281196844-AddHiddenFaces.ts | 14 -- .../src/migrations/1690469489288-Panoramas.ts | 13 -- .../1691209138541-AddAlbumDescription.ts | 13 -- .../1691600216749-UserMemoryPreference.ts | 13 -- .../1692057328660-fixGPSNullIsland.ts | 13 -- .../1692112147855-AddPersonBirthDate.ts | 13 -- .../migrations/1692804658140-AddAuditTable.ts | 16 --- .../1693236627291-RenameMLEnableFlags.ts | 25 ---- .../1693833336881-AddPersonFaceAssetId.ts | 15 -- .../1694204416744-AddAssetDeletedAtColumn.ts | 14 -- .../1694525143117-AddLocalDateTime.ts | 23 --- .../1694638413248-AddDeletedAtToAlbums.ts | 13 -- .../1694750975773-AddExifColorSpace.ts | 18 --- .../1694758412194-UpdateOpusCodecToLibopus.ts | 21 --- .../1695354433573-AddStackParentIdToAssets.ts | 16 --- .../1695660378655-RemoveInvalidCoordinates.ts | 16 --- .../1696888644031-AddOriginalPathIndex.ts | 13 -- .../migrations/1696968880063-AddMoveTable.ts | 14 -- .../migrations/1697272818851-UnassignFace.ts | 23 --- .../1698290827089-AddPasswordToSharedLinks.ts | 14 -- .../migrations/1698693294632-AddActivity.ts | 22 --- .../1699268680508-DisableActivity.ts | 14 -- .../1699322864544-UserNameConsolidation.ts | 21 --- .../migrations/1699345863886-AddJobStatus.ts | 16 --- ...562570201-AdddInTimelineToPartnersTable.ts | 14 -- .../1699727044012-EditFaceAssetForeignKey.ts | 18 --- .../1699889987493-AddAvatarColor.ts | 14 -- .../1700345818045-SystemMetadata.ts | 14 -- .../src/migrations/1700362016675-Geodata.ts | 25 ---- .../migrations/1700713871511-UsePgVectors.ts | 55 ------- .../1700713994428-AddCLIPEmbeddingIndex.ts | 18 --- .../1700714033632-AddFaceEmbeddingIndex.ts | 18 --- .../1700714072055-AddSmartInfoTagsIndex.ts | 13 -- ...14140297-CreateSmartInfoTextSearchIndex.ts | 37 ----- .../1700752078178-AddAssetFaceIndicies.ts | 16 --- .../1701665867595-AddExifCityIndex.ts | 14 -- ...02084989965-AddWebSocketAttachmentTable.ts | 13 -- .../1702257380990-DropNullIslandLatLong.ts | 14 -- ...fyFutureBirthDatesAndAddCheckConstraint.ts | 16 --- ...702942303661-FixRemovedAssetsSharedLink.ts | 16 --- .../1703035138085-AddAutoStackId.ts | 16 --- ...orageTemplateOnForExistingInstallations.ts | 16 --- .../1704382918223-AddQuotaColumnsToUser.ts | 16 --- ...faultOnboardingForExistingInstallations.ts | 17 --- ...43345360-SetAssetFaceNullOnPersonDelete.ts | 24 ---- .../1705094221536-AddMetadataExtractedAt.ts | 19 --- .../1705306747072-AddOriginalFileNameIndex.ts | 13 -- .../1705363967169-CreateAssetStackTable.ts | 83 ----------- .../1707000751533-AddVectorsToSearchPath.ts | 14 -- .../1708059341865-GeodataLocationSearch.ts | 108 -------------- .../1708116312820-GeonamesEnhancement.ts | 18 --- .../1708227417898-AddFileCreatedAtIndex.ts | 12 -- .../1708425975121-RemoveExternalPath.ts | 13 -- ...0004123-RemoveLibraryWatchPollingOption.ts | 12 -- ...140355-AddAssetOriginalPathTrigramIndex.ts | 14 -- ...63765506-AddExtensionToOriginalFileName.ts | 20 --- .../1709825430031-CascadeSharedLinksDelete.ts | 16 --- .../migrations/1709870213078-AddUserStatus.ts | 14 -- .../1710182081326-AscendingOrderAlbum.ts | 14 -- .../1710293990203-AddAssetRelationIndices.ts | 15 -- .../1711257900274-RenameWebpJpegPaths.ts | 51 ------- .../1711637874206-AddMemoryTable.ts | 26 ---- .../1711989989911-AddAssetDuplicateColumns.ts | 14 -- .../1713337511945-AddAlbumUserRole.ts | 14 -- .../1713490844785-RenameSessionsTable.ts | 15 -- .../1714698592332-RemoveIsReadOnly.ts | 14 -- .../1715435221124-MotionAssetExtensionMP4.ts | 11 -- .../1715623169039-RemoveTextSearchColumn.ts | 21 --- .../1715787369686-RemoveSystemConfigTable.ts | 31 ---- .../1715798702876-RemoveLibraryIsVisible.ts | 14 -- .../1715804005643-RemoveLibraryType.ts | 29 ---- .../src/migrations/1715890481637-FixJsonB.ts | 24 ---- .../migrations/1716312279245-UserMetadata.ts | 60 -------- .../1718486162779-AddFaceSearchRelation.ts | 71 ---------- ...1719359859887-FixLivePhotoVideoRelation.ts | 18 --- .../migrations/1720207981949-AddStackOwner.ts | 22 --- .../1720375641148-natural-earth-countries.ts | 14 -- ...721249222549-AddSourceColumnToAssetFace.ts | 16 --- .../migrations/1722753178937-AddExifRating.ts | 14 -- .../1723719333525-AddApiKeyPermissions.ts | 14 -- .../1724080823160-AddThumbnailJobStatus.ts | 17 --- .../1724101822106-AddAssetFilesTable.ts | 34 ----- .../1724790460210-NestedTagTable.ts | 57 -------- .../1725023079109-FixTagUniqueness.ts | 16 --- ...25258039306-UpsertMissingAssetJobStatus.ts | 21 --- ...80-RemoveThumbailAtForMissingThumbnails.ts | 13 -- ...5730782681-RemoveHiddenAssetsFromAlbums.ts | 13 -- .../1726491047923-AddprofileChangedAt.ts | 14 -- .../1726593009549-AddAssetStatus.ts | 16 --- ...7-SeparateQualityForThumbnailAndPreview.ts | 37 ----- .../1727781844613-IsOfflineSetDeletedAt.ts | 16 --- .../1727797340951-AddVersionHistory.ts | 14 -- .../1729793521993-AddAlbumAssetCreatedAt.ts | 13 -- ...1730227312171-RemoveNplFromSystemConfig.ts | 12 -- .../1730989238718-DropSmartInfoTable.ts | 11 -- ...943-NaturalEarthCountriesIdentityColumn.ts | 29 ---- ...39482860-RenameMachineLearningUrlToUrls.ts | 19 --- .../1734574016301-AddTimeBucketIndices.ts | 25 ---- .../1734879118272-AddIsFavoritePerson.ts | 14 -- .../1737672307560-AddUpdatedAtTriggers.ts | 102 ------------- .../migrations/1737845696644-NullableDates.ts | 18 --- .../1738889177573-AddPersonColor.ts | 14 -- ...036-AddDeletedAtColumnToAssetFacesTable.ts | 17 --- .../1739824470990-AddMemoryShowHideDates.ts | 16 --- ...001232576-AddSessionSyncCheckpointTable.ts | 22 --- .../1740064899123-AddUsersAuditTable.ts | 34 ----- .../1740586617223-AddUpdateIdColumns.ts | 134 ------------------ ...740595460866-UsersAuditUuidv7PrimaryKey.ts | 26 ---- .../1740619600996-AddManualSourceType.ts | 27 ---- ...9-UnsetStackedAssetsFromDuplicateStatus.ts | 14 -- .../1740739778549-CreatePartnersAuditTable.ts | 38 ----- .../migrations/1741027685381-ResetMemories.ts | 14 -- .../1741179334403-MoveHistoryUuidEntityId.ts | 26 ---- .../1741191762113-AssetAuditTable.ts | 37 ----- ...328985-FixAssetAndUserCascadeConditions.ts | 50 ------- .../1741281344519-AddExifUpdateId.ts | 25 ---- .../migrations/1743595393000-TableCleanup.ts | 12 -- .../1743611339000-GeodataCleanup.ts | 19 --- ...44662638410-MakeFileMetadataNonNullable.ts | 22 --- .../1744900200559-AddForeignKeyIndexes.ts | 51 ------- .../1744910873956-AddMissingIndex.ts | 13 -- .../src/repositories/database.repository.ts | 57 +------- server/src/services/download.service.spec.ts | 2 +- server/test/utils.ts | 3 +- 206 files changed, 13 insertions(+), 4664 deletions(-) delete mode 100644 server/src/migrations/1645130759468-CreateUserTable.ts delete mode 100644 server/src/migrations/1645130777674-CreateDeviceInfoTable.ts delete mode 100644 server/src/migrations/1645130805273-CreateAssetsTable.ts delete mode 100644 server/src/migrations/1645130817965-CreateExifTable.ts delete mode 100644 server/src/migrations/1645130870184-CreateSmartInfoTable.ts delete mode 100644 server/src/migrations/1646249209023-AddExifTextSearchColumn.ts delete mode 100644 server/src/migrations/1646249734844-CreateExifTextSearchIndex.ts delete mode 100644 server/src/migrations/1646709533213-AddRegionCityToExIf.ts delete mode 100644 server/src/migrations/1646710459852-AddLocationToExifTextSearch.ts delete mode 100644 server/src/migrations/1648317474768-AddObjectColumnToSmartInfo.ts delete mode 100644 server/src/migrations/1649643216111-CreateSharedAlbumAndRelatedTables.ts delete mode 100644 server/src/migrations/1652633525943-UpdateUserTableWithAdminAndName.ts delete mode 100644 server/src/migrations/1653214255670-UpdateAssetTableWithWebpPath.ts delete mode 100644 server/src/migrations/1654299904583-UpdateAssetTableWithEncodeVideoPath.ts delete mode 100644 server/src/migrations/1655401127251-RenameSharedAlbums.ts delete mode 100644 server/src/migrations/1656338626260-RenameIsFirstLoggedInColumn.ts delete mode 100644 server/src/migrations/1656888591977-RenameAssetAlbumIdSequence.ts delete mode 100644 server/src/migrations/1656888918620-DropExifTextSearchableColumn.ts delete mode 100644 server/src/migrations/1656889061566-MatchMigrationsWithTypeORMEntities.ts delete mode 100644 server/src/migrations/1658860470248-AddExifImageNameAsSearchableText.ts delete mode 100644 server/src/migrations/1661011331242-AddCaption.ts delete mode 100644 server/src/migrations/1661528919411-ChangeExifFileSizeInByteToBigInt.ts delete mode 100644 server/src/migrations/1661881837496-AddAssetChecksum.ts delete mode 100644 server/src/migrations/1661971370662-UpdateAssetTableWithNewUniqueConstraint.ts delete mode 100644 server/src/migrations/1662427365521-FixTimestampDataTypeInAssetTable.ts delete mode 100644 server/src/migrations/1665540663419-CreateSystemConfigTable.ts delete mode 100644 server/src/migrations/1667762360744-AddingDeletedAtColumnInUserEntity.ts delete mode 100644 server/src/migrations/1668383120461-AddLivePhotosRelatedColumnToAssetTable.ts delete mode 100644 server/src/migrations/1668835311083-UpdateUserTableForOIDC.ts delete mode 100644 server/src/migrations/1670104716264-OAuthId.ts delete mode 100644 server/src/migrations/1670257571385-CreateTagsTable.ts delete mode 100644 server/src/migrations/1670607437008-TruncateOldConfigItems.ts delete mode 100644 server/src/migrations/1670633210032-AddUserEmailUniqueConstraint.ts delete mode 100644 server/src/migrations/1672109862870-DropSaltColumn.ts delete mode 100644 server/src/migrations/1672502270115-AddAPIKeys.ts delete mode 100644 server/src/migrations/1673150490490-AddSharedLinkTable.ts delete mode 100644 server/src/migrations/1673907194740-AddMorePermissionToSharedLink.ts delete mode 100644 server/src/migrations/1674263302005-RemoveVideoCodecConfigOption.ts delete mode 100644 server/src/migrations/1674342044239-CreateUserTokenEntity.ts delete mode 100644 server/src/migrations/1674757936889-AlterExifExposureTimeToString.ts delete mode 100644 server/src/migrations/1674774248319-TruncateAPIKeys.ts delete mode 100644 server/src/migrations/1674939383309-AddSharedLinkUserForeignKeyConstraint.ts delete mode 100644 server/src/migrations/1675667878312-AddUpdatedAtColumnToAlbumsUsersAssets.ts delete mode 100644 server/src/migrations/1675701909594-AddAlbumUserForeignKeyConstraint.ts delete mode 100644 server/src/migrations/1675808874445-APIKeyUUIDPrimaryKey.ts delete mode 100644 server/src/migrations/1675812532822-FixAlbumEntityTypeORM.ts delete mode 100644 server/src/migrations/1676437878377-AppleContentIdentifier.ts delete mode 100644 server/src/migrations/1676680127415-FixAssetRelations.ts delete mode 100644 server/src/migrations/1676721296440-AssetCreatedAtField.ts delete mode 100644 server/src/migrations/1676848629119-ExifEntityDefinitionFixes.ts delete mode 100644 server/src/migrations/1676848694786-SharedLinkEntityDefinitionFixes.ts delete mode 100644 server/src/migrations/1676852143506-SmartInfoEntityDefinitionFixes.ts delete mode 100644 server/src/migrations/1677497925328-AddExifTimeZone.ts delete mode 100644 server/src/migrations/1677535643119-AddIndexForAlbumInSharedLinkTable.ts delete mode 100644 server/src/migrations/1677613712565-AlbumThumbnailRelation.ts delete mode 100644 server/src/migrations/1677971458822-AddCLIPEncodeDataColumn.ts delete mode 100644 server/src/migrations/1679751316282-UpdateTranscodeOption.ts delete mode 100644 server/src/migrations/1679901204458-ClipEmbeddingFloat4.ts delete mode 100644 server/src/migrations/1680632845740-AddIsArchivedColumn.ts delete mode 100644 server/src/migrations/1680694465853-RemoveRedundantConstraints.ts delete mode 100644 server/src/migrations/1681144628393-AddOriginalFileNameToAssetTable.ts delete mode 100644 server/src/migrations/1681159594469-RemoveImageNameFromEXIFTable.ts delete mode 100644 server/src/migrations/1682371561743-FixNullableRelations.ts delete mode 100644 server/src/migrations/1682371791038-AddDeviceInfoToUserToken.ts delete mode 100644 server/src/migrations/1682710252424-DropDeviceInfoTable.ts delete mode 100644 server/src/migrations/1683808254676-AddPartnersTable.ts delete mode 100644 server/src/migrations/1684255168091-AddFacialTables.ts delete mode 100644 server/src/migrations/1684273840676-AddSidecarFile.ts delete mode 100644 server/src/migrations/1684328185099-RequireChecksumNotNull.ts delete mode 100644 server/src/migrations/1684410565398-AddStorageLabel.ts delete mode 100644 server/src/migrations/1684867360825-AddUserTokenAndAPIKeyCascades.ts delete mode 100644 server/src/migrations/1685044328272-AddSharedLinkCascade.ts delete mode 100644 server/src/migrations/1685370430343-UserDatesTimestamptz.ts delete mode 100644 server/src/migrations/1685731372040-RemoveInvalidCoordinates.ts delete mode 100644 server/src/migrations/1686584273471-ImportAsset.ts delete mode 100644 server/src/migrations/1686762895180-AddThumbhashColumn.ts delete mode 100644 server/src/migrations/1688241394489-AddDetectFaceResultInfo.ts delete mode 100644 server/src/migrations/1688392120838-AddLibraryTable.ts delete mode 100644 server/src/migrations/1689001889950-DropMimeTypeColumn.ts delete mode 100644 server/src/migrations/1689281196844-AddHiddenFaces.ts delete mode 100644 server/src/migrations/1690469489288-Panoramas.ts delete mode 100644 server/src/migrations/1691209138541-AddAlbumDescription.ts delete mode 100644 server/src/migrations/1691600216749-UserMemoryPreference.ts delete mode 100644 server/src/migrations/1692057328660-fixGPSNullIsland.ts delete mode 100644 server/src/migrations/1692112147855-AddPersonBirthDate.ts delete mode 100644 server/src/migrations/1692804658140-AddAuditTable.ts delete mode 100644 server/src/migrations/1693236627291-RenameMLEnableFlags.ts delete mode 100644 server/src/migrations/1693833336881-AddPersonFaceAssetId.ts delete mode 100644 server/src/migrations/1694204416744-AddAssetDeletedAtColumn.ts delete mode 100644 server/src/migrations/1694525143117-AddLocalDateTime.ts delete mode 100644 server/src/migrations/1694638413248-AddDeletedAtToAlbums.ts delete mode 100644 server/src/migrations/1694750975773-AddExifColorSpace.ts delete mode 100644 server/src/migrations/1694758412194-UpdateOpusCodecToLibopus.ts delete mode 100644 server/src/migrations/1695354433573-AddStackParentIdToAssets.ts delete mode 100644 server/src/migrations/1695660378655-RemoveInvalidCoordinates.ts delete mode 100644 server/src/migrations/1696888644031-AddOriginalPathIndex.ts delete mode 100644 server/src/migrations/1696968880063-AddMoveTable.ts delete mode 100644 server/src/migrations/1697272818851-UnassignFace.ts delete mode 100644 server/src/migrations/1698290827089-AddPasswordToSharedLinks.ts delete mode 100644 server/src/migrations/1698693294632-AddActivity.ts delete mode 100644 server/src/migrations/1699268680508-DisableActivity.ts delete mode 100644 server/src/migrations/1699322864544-UserNameConsolidation.ts delete mode 100644 server/src/migrations/1699345863886-AddJobStatus.ts delete mode 100644 server/src/migrations/1699562570201-AdddInTimelineToPartnersTable.ts delete mode 100644 server/src/migrations/1699727044012-EditFaceAssetForeignKey.ts delete mode 100644 server/src/migrations/1699889987493-AddAvatarColor.ts delete mode 100644 server/src/migrations/1700345818045-SystemMetadata.ts delete mode 100644 server/src/migrations/1700362016675-Geodata.ts delete mode 100644 server/src/migrations/1700713871511-UsePgVectors.ts delete mode 100644 server/src/migrations/1700713994428-AddCLIPEmbeddingIndex.ts delete mode 100644 server/src/migrations/1700714033632-AddFaceEmbeddingIndex.ts delete mode 100644 server/src/migrations/1700714072055-AddSmartInfoTagsIndex.ts delete mode 100644 server/src/migrations/1700714140297-CreateSmartInfoTextSearchIndex.ts delete mode 100644 server/src/migrations/1700752078178-AddAssetFaceIndicies.ts delete mode 100644 server/src/migrations/1701665867595-AddExifCityIndex.ts delete mode 100644 server/src/migrations/1702084989965-AddWebSocketAttachmentTable.ts delete mode 100644 server/src/migrations/1702257380990-DropNullIslandLatLong.ts delete mode 100644 server/src/migrations/1702938928766-NullifyFutureBirthDatesAndAddCheckConstraint.ts delete mode 100644 server/src/migrations/1702942303661-FixRemovedAssetsSharedLink.ts delete mode 100644 server/src/migrations/1703035138085-AddAutoStackId.ts delete mode 100644 server/src/migrations/1703288449127-DefaultStorageTemplateOnForExistingInstallations.ts delete mode 100644 server/src/migrations/1704382918223-AddQuotaColumnsToUser.ts delete mode 100644 server/src/migrations/1704571051932-DefaultOnboardingForExistingInstallations.ts delete mode 100644 server/src/migrations/1704943345360-SetAssetFaceNullOnPersonDelete.ts delete mode 100644 server/src/migrations/1705094221536-AddMetadataExtractedAt.ts delete mode 100644 server/src/migrations/1705306747072-AddOriginalFileNameIndex.ts delete mode 100644 server/src/migrations/1705363967169-CreateAssetStackTable.ts delete mode 100644 server/src/migrations/1707000751533-AddVectorsToSearchPath.ts delete mode 100644 server/src/migrations/1708059341865-GeodataLocationSearch.ts delete mode 100644 server/src/migrations/1708116312820-GeonamesEnhancement.ts delete mode 100644 server/src/migrations/1708227417898-AddFileCreatedAtIndex.ts delete mode 100644 server/src/migrations/1708425975121-RemoveExternalPath.ts delete mode 100644 server/src/migrations/1709150004123-RemoveLibraryWatchPollingOption.ts delete mode 100644 server/src/migrations/1709608140355-AddAssetOriginalPathTrigramIndex.ts delete mode 100644 server/src/migrations/1709763765506-AddExtensionToOriginalFileName.ts delete mode 100644 server/src/migrations/1709825430031-CascadeSharedLinksDelete.ts delete mode 100644 server/src/migrations/1709870213078-AddUserStatus.ts delete mode 100644 server/src/migrations/1710182081326-AscendingOrderAlbum.ts delete mode 100644 server/src/migrations/1710293990203-AddAssetRelationIndices.ts delete mode 100644 server/src/migrations/1711257900274-RenameWebpJpegPaths.ts delete mode 100644 server/src/migrations/1711637874206-AddMemoryTable.ts delete mode 100644 server/src/migrations/1711989989911-AddAssetDuplicateColumns.ts delete mode 100644 server/src/migrations/1713337511945-AddAlbumUserRole.ts delete mode 100644 server/src/migrations/1713490844785-RenameSessionsTable.ts delete mode 100644 server/src/migrations/1714698592332-RemoveIsReadOnly.ts delete mode 100644 server/src/migrations/1715435221124-MotionAssetExtensionMP4.ts delete mode 100644 server/src/migrations/1715623169039-RemoveTextSearchColumn.ts delete mode 100644 server/src/migrations/1715787369686-RemoveSystemConfigTable.ts delete mode 100644 server/src/migrations/1715798702876-RemoveLibraryIsVisible.ts delete mode 100644 server/src/migrations/1715804005643-RemoveLibraryType.ts delete mode 100644 server/src/migrations/1715890481637-FixJsonB.ts delete mode 100644 server/src/migrations/1716312279245-UserMetadata.ts delete mode 100644 server/src/migrations/1718486162779-AddFaceSearchRelation.ts delete mode 100644 server/src/migrations/1719359859887-FixLivePhotoVideoRelation.ts delete mode 100644 server/src/migrations/1720207981949-AddStackOwner.ts delete mode 100644 server/src/migrations/1720375641148-natural-earth-countries.ts delete mode 100644 server/src/migrations/1721249222549-AddSourceColumnToAssetFace.ts delete mode 100644 server/src/migrations/1722753178937-AddExifRating.ts delete mode 100644 server/src/migrations/1723719333525-AddApiKeyPermissions.ts delete mode 100644 server/src/migrations/1724080823160-AddThumbnailJobStatus.ts delete mode 100644 server/src/migrations/1724101822106-AddAssetFilesTable.ts delete mode 100644 server/src/migrations/1724790460210-NestedTagTable.ts delete mode 100644 server/src/migrations/1725023079109-FixTagUniqueness.ts delete mode 100644 server/src/migrations/1725258039306-UpsertMissingAssetJobStatus.ts delete mode 100644 server/src/migrations/1725327902980-RemoveThumbailAtForMissingThumbnails.ts delete mode 100644 server/src/migrations/1725730782681-RemoveHiddenAssetsFromAlbums.ts delete mode 100644 server/src/migrations/1726491047923-AddprofileChangedAt.ts delete mode 100644 server/src/migrations/1726593009549-AddAssetStatus.ts delete mode 100644 server/src/migrations/1727471863507-SeparateQualityForThumbnailAndPreview.ts delete mode 100644 server/src/migrations/1727781844613-IsOfflineSetDeletedAt.ts delete mode 100644 server/src/migrations/1727797340951-AddVersionHistory.ts delete mode 100644 server/src/migrations/1729793521993-AddAlbumAssetCreatedAt.ts delete mode 100644 server/src/migrations/1730227312171-RemoveNplFromSystemConfig.ts delete mode 100644 server/src/migrations/1730989238718-DropSmartInfoTable.ts delete mode 100644 server/src/migrations/1732072134943-NaturalEarthCountriesIdentityColumn.ts delete mode 100644 server/src/migrations/1733339482860-RenameMachineLearningUrlToUrls.ts delete mode 100644 server/src/migrations/1734574016301-AddTimeBucketIndices.ts delete mode 100644 server/src/migrations/1734879118272-AddIsFavoritePerson.ts delete mode 100644 server/src/migrations/1737672307560-AddUpdatedAtTriggers.ts delete mode 100644 server/src/migrations/1737845696644-NullableDates.ts delete mode 100644 server/src/migrations/1738889177573-AddPersonColor.ts delete mode 100644 server/src/migrations/1739466714036-AddDeletedAtColumnToAssetFacesTable.ts delete mode 100644 server/src/migrations/1739824470990-AddMemoryShowHideDates.ts delete mode 100644 server/src/migrations/1740001232576-AddSessionSyncCheckpointTable.ts delete mode 100644 server/src/migrations/1740064899123-AddUsersAuditTable.ts delete mode 100644 server/src/migrations/1740586617223-AddUpdateIdColumns.ts delete mode 100644 server/src/migrations/1740595460866-UsersAuditUuidv7PrimaryKey.ts delete mode 100644 server/src/migrations/1740619600996-AddManualSourceType.ts delete mode 100644 server/src/migrations/1740654480319-UnsetStackedAssetsFromDuplicateStatus.ts delete mode 100644 server/src/migrations/1740739778549-CreatePartnersAuditTable.ts delete mode 100644 server/src/migrations/1741027685381-ResetMemories.ts delete mode 100644 server/src/migrations/1741179334403-MoveHistoryUuidEntityId.ts delete mode 100644 server/src/migrations/1741191762113-AssetAuditTable.ts delete mode 100644 server/src/migrations/1741280328985-FixAssetAndUserCascadeConditions.ts delete mode 100644 server/src/migrations/1741281344519-AddExifUpdateId.ts delete mode 100644 server/src/migrations/1743595393000-TableCleanup.ts delete mode 100644 server/src/migrations/1743611339000-GeodataCleanup.ts delete mode 100644 server/src/migrations/1744662638410-MakeFileMetadataNonNullable.ts delete mode 100644 server/src/migrations/1744900200559-AddForeignKeyIndexes.ts delete mode 100644 server/src/migrations/1744910873956-AddMissingIndex.ts diff --git a/docs/src/pages/errors.md b/docs/src/pages/errors.md index 4a745877dc..c5af7e2575 100644 --- a/docs/src/pages/errors.md +++ b/docs/src/pages/errors.md @@ -2,7 +2,7 @@ ## TypeORM Upgrade -The upgrade to Immich `v2.x.x` has a required upgrade path to `v1.132.0+`. This means it is required to start up the application at least once on version `1.132.0` (or later). Doing so will complete database schema upgrades that are required for `v2.0.0`. After Immich has successfully booted on this version, shut the system down and try the `v2.x.x` upgrade again. +In order to update to Immich to `v1.137.0` (or above), the application must be started at least once on a version in the range between `1.132.0` and `1.136.0`. Doing so will complete database schema upgrades that are required for `v1.137.0` (and above). After Immich has successfully started on this version, shut the system down and try the update again. We recommend users upgrade to `1.132.0` since it does not have any other breaking changes. ## Inconsistent Media Location diff --git a/server/src/bin/migrations.ts b/server/src/bin/migrations.ts index 3bdfb3bbc6..ebb07af442 100644 --- a/server/src/bin/migrations.ts +++ b/server/src/bin/migrations.ts @@ -98,7 +98,7 @@ const create = (path: string, up: string[], down: string[]) => { const folder = dirname(path); const fullPath = join(folder, filename); mkdirSync(folder, { recursive: true }); - writeFileSync(fullPath, asMigration('kysely', { name, timestamp, up, down })); + writeFileSync(fullPath, asMigration({ up, down })); console.log(`Wrote ${fullPath}`); }; @@ -128,34 +128,11 @@ const compare = async () => { }; type MigrationProps = { - name: string; - timestamp: number; up: string[]; down: string[]; }; -const asMigration = (type: 'kysely' | 'typeorm', options: MigrationProps) => - type === 'typeorm' ? asTypeOrmMigration(options) : asKyselyMigration(options); - -const asTypeOrmMigration = ({ timestamp, name, up, down }: MigrationProps) => { - const upSql = up.map((sql) => ` await queryRunner.query(\`${sql}\`);`).join('\n'); - const downSql = down.map((sql) => ` await queryRunner.query(\`${sql}\`);`).join('\n'); - - return `import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class ${name}${timestamp} implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { -${upSql} - } - - public async down(queryRunner: QueryRunner): Promise { -${downSql} - } -} -`; -}; - -const asKyselyMigration = ({ up, down }: MigrationProps) => { +const asMigration = ({ up, down }: MigrationProps) => { const upSql = up.map((sql) => ` await sql\`${sql}\`.execute(db);`).join('\n'); const downSql = down.map((sql) => ` await sql\`${sql}\`.execute(db);`).join('\n'); diff --git a/server/src/controllers/download.controller.spec.ts b/server/src/controllers/download.controller.spec.ts index 9385c445b5..00d03fc46f 100644 --- a/server/src/controllers/download.controller.spec.ts +++ b/server/src/controllers/download.controller.spec.ts @@ -1,9 +1,9 @@ +import { Readable } from 'node:stream'; import { DownloadController } from 'src/controllers/download.controller'; import { DownloadService } from 'src/services/download.service'; import request from 'supertest'; import { factory } from 'test/small.factory'; import { ControllerContext, controllerSetup, mockBaseService } from 'test/utils'; -import { Readable } from 'typeorm/platform/PlatformTools.js'; describe(DownloadController.name, () => { let ctx: ControllerContext; diff --git a/server/src/migrations/1645130759468-CreateUserTable.ts b/server/src/migrations/1645130759468-CreateUserTable.ts deleted file mode 100644 index 1aedfb67d4..0000000000 --- a/server/src/migrations/1645130759468-CreateUserTable.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateUserTable1645130759468 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`); - await queryRunner.query(` - create table if not exists users - ( - id uuid default uuid_generate_v4() not null - constraint "PK_a3ffb1c0c8416b9fc6f907b7433" - primary key, - email varchar not null, - password varchar not null, - salt varchar not null, - "createdAt" timestamp default now() not null - ); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`drop table users`); - } -} diff --git a/server/src/migrations/1645130777674-CreateDeviceInfoTable.ts b/server/src/migrations/1645130777674-CreateDeviceInfoTable.ts deleted file mode 100644 index bf53d7910b..0000000000 --- a/server/src/migrations/1645130777674-CreateDeviceInfoTable.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateDeviceInfoTable1645130777674 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - create table if not exists device_info - ( - id serial - constraint "PK_b1c15a80b0a4e5f4eebadbdd92c" - primary key, - "userId" varchar not null, - "deviceId" varchar not null, - "deviceType" varchar not null, - "notificationToken" varchar, - "createdAt" timestamp default now() not null, - "isAutoBackup" boolean default false not null, - constraint "UQ_ebad78f36b10d15fbea8560e107" - unique ("userId", "deviceId") - ); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`drop table device_info`); - } -} diff --git a/server/src/migrations/1645130805273-CreateAssetsTable.ts b/server/src/migrations/1645130805273-CreateAssetsTable.ts deleted file mode 100644 index 82727e18a5..0000000000 --- a/server/src/migrations/1645130805273-CreateAssetsTable.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateAssetsTable1645130805273 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - create table if not exists assets - ( - id uuid default uuid_generate_v4() not null - constraint "PK_da96729a8b113377cfb6a62439c" - primary key, - "deviceAssetId" varchar not null, - "userId" varchar not null, - "deviceId" varchar not null, - type varchar not null, - "originalPath" varchar not null, - "resizePath" varchar, - "createdAt" varchar not null, - "modifiedAt" varchar not null, - "isFavorite" boolean default false not null, - "mimeType" varchar, - duration varchar, - constraint "UQ_b599ab0bd9574958acb0b30a90e" - unique ("deviceAssetId", "userId", "deviceId") - ); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`drop table assets`); - } -} diff --git a/server/src/migrations/1645130817965-CreateExifTable.ts b/server/src/migrations/1645130817965-CreateExifTable.ts deleted file mode 100644 index af46b86507..0000000000 --- a/server/src/migrations/1645130817965-CreateExifTable.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateExifTable1645130817965 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - create table if not exists exif - ( - id serial - constraint "PK_28663352d85078ad0046dafafaa" - primary key, - "assetId" uuid not null - constraint "REL_c0117fdbc50b917ef9067740c4" - unique - constraint "FK_c0117fdbc50b917ef9067740c44" - references assets - on delete cascade, - make varchar, - model varchar, - "imageName" varchar, - "exifImageWidth" integer, - "exifImageHeight" integer, - "fileSizeInByte" integer, - orientation varchar, - "dateTimeOriginal" timestamp with time zone, - "modifyDate" timestamp with time zone, - "lensModel" varchar, - "fNumber" double precision, - "focalLength" double precision, - iso integer, - "exposureTime" double precision, - latitude double precision, - longitude double precision - ); - - create unique index if not exists "IDX_c0117fdbc50b917ef9067740c4" on exif ("assetId"); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`drop table exif`); - } -} diff --git a/server/src/migrations/1645130870184-CreateSmartInfoTable.ts b/server/src/migrations/1645130870184-CreateSmartInfoTable.ts deleted file mode 100644 index 9c81f6099a..0000000000 --- a/server/src/migrations/1645130870184-CreateSmartInfoTable.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateSmartInfoTable1645130870184 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - create table if not exists smart_info - ( - id serial - constraint "PK_0beace66440e9713f5c40470e46" - primary key, - "assetId" uuid not null - constraint "UQ_5e3753aadd956110bf3ec0244ac" - unique - constraint "FK_5e3753aadd956110bf3ec0244ac" - references assets - on delete cascade, - tags text[] - ); - - create unique index if not exists "IDX_5e3753aadd956110bf3ec0244a" - on smart_info ("assetId"); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - drop table smart_info; - `); - } -} diff --git a/server/src/migrations/1646249209023-AddExifTextSearchColumn.ts b/server/src/migrations/1646249209023-AddExifTextSearchColumn.ts deleted file mode 100644 index 071d4bd40d..0000000000 --- a/server/src/migrations/1646249209023-AddExifTextSearchColumn.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddExifTextSearchColumn1646249209023 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - ADD COLUMN IF NOT EXISTS exif_text_searchable_column tsvector - GENERATED ALWAYS AS ( - TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') - ) - ) STORED; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - DROP COLUMN IF EXISTS exif_text_searchable_column; - `); - } -} diff --git a/server/src/migrations/1646249734844-CreateExifTextSearchIndex.ts b/server/src/migrations/1646249734844-CreateExifTextSearchIndex.ts deleted file mode 100644 index 664d06c4bc..0000000000 --- a/server/src/migrations/1646249734844-CreateExifTextSearchIndex.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateExifTextSearchIndex1646249734844 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - CREATE INDEX exif_text_searchable_idx - ON exif - USING GIN (exif_text_searchable_column); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - DROP INDEX IF EXISTS exif_text_searchable_idx ON exif; - `); - } -} diff --git a/server/src/migrations/1646709533213-AddRegionCityToExIf.ts b/server/src/migrations/1646709533213-AddRegionCityToExIf.ts deleted file mode 100644 index e2d226cfa4..0000000000 --- a/server/src/migrations/1646709533213-AddRegionCityToExIf.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddRegionCityToExIf1646709533213 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - ADD COLUMN if not exists city varchar; - - ALTER TABLE exif - ADD COLUMN if not exists state varchar; - - ALTER TABLE exif - ADD COLUMN if not exists country varchar; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - DROP COLUMN city; - - ALTER TABLE exif - DROP COLUMN state; - - ALTER TABLE exif - DROP COLUMN country; - `); - } -} diff --git a/server/src/migrations/1646710459852-AddLocationToExifTextSearch.ts b/server/src/migrations/1646710459852-AddLocationToExifTextSearch.ts deleted file mode 100644 index 9116bf2866..0000000000 --- a/server/src/migrations/1646710459852-AddLocationToExifTextSearch.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddLocationToExifTextSearch1646710459852 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - DROP COLUMN IF EXISTS exif_text_searchable_column; - - ALTER TABLE exif - ADD COLUMN IF NOT EXISTS exif_text_searchable_column tsvector - GENERATED ALWAYS AS ( - TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') || ' ' || - COALESCE("city", '') || ' ' || - COALESCE("state", '') || ' ' || - COALESCE("country", '') - ) - ) STORED; - - CREATE INDEX exif_text_searchable_idx - ON exif - USING GIN (exif_text_searchable_column); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - DROP COLUMN IF EXISTS exif_text_searchable_column; - - DROP INDEX IF EXISTS exif_text_searchable_idx ON exif; - `); - } -} diff --git a/server/src/migrations/1648317474768-AddObjectColumnToSmartInfo.ts b/server/src/migrations/1648317474768-AddObjectColumnToSmartInfo.ts deleted file mode 100644 index bdf3dff5df..0000000000 --- a/server/src/migrations/1648317474768-AddObjectColumnToSmartInfo.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddObjectColumnToSmartInfo1648317474768 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE smart_info - ADD COLUMN if not exists objects text[]; - - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE smart_info - DROP COLUMN objects; - `); - } -} diff --git a/server/src/migrations/1649643216111-CreateSharedAlbumAndRelatedTables.ts b/server/src/migrations/1649643216111-CreateSharedAlbumAndRelatedTables.ts deleted file mode 100644 index ef633d6f12..0000000000 --- a/server/src/migrations/1649643216111-CreateSharedAlbumAndRelatedTables.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateSharedAlbumAndRelatedTables1649643216111 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - // Create shared_albums - await queryRunner.query(` - create table if not exists shared_albums - ( - id uuid default uuid_generate_v4() not null - constraint "PK_7f71c7b5bc7c87b8f94c9a93a00" - primary key, - "ownerId" varchar not null, - "albumName" varchar default 'Untitled Album'::character varying not null, - "createdAt" timestamp with time zone default now() not null, - "albumThumbnailAssetId" varchar - ); - - comment on column shared_albums."albumThumbnailAssetId" is 'Asset ID to be used as thumbnail'; - `); - - // Create user_shared_album - await queryRunner.query(` - create table if not exists user_shared_album - ( - id serial - constraint "PK_b6562316a98845a7b3e9a25cdd0" - primary key, - "albumId" uuid not null - constraint "FK_7b3bf0f5f8da59af30519c25f18" - references shared_albums - on delete cascade, - "sharedUserId" uuid not null - constraint "FK_543c31211653e63e080ba882eb5" - references users, - constraint "PK_unique_user_in_album" - unique ("albumId", "sharedUserId") - ); - `); - - // Create asset_shared_album - await queryRunner.query( - ` - create table if not exists asset_shared_album - ( - id serial - constraint "PK_a34e076afbc601d81938e2c2277" - primary key, - "albumId" uuid not null - constraint "FK_a8b79a84996cef6ba6a3662825d" - references shared_albums - on delete cascade, - "assetId" uuid not null - constraint "FK_64f2e7d68d1d1d8417acc844a4a" - references assets - on delete cascade, - constraint "UQ_a1e2734a1ce361e7a26f6b28288" - unique ("albumId", "assetId") - ); - `, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - drop table asset_shared_album; - drop table user_shared_album; - drop table shared_albums; - `); - } -} diff --git a/server/src/migrations/1652633525943-UpdateUserTableWithAdminAndName.ts b/server/src/migrations/1652633525943-UpdateUserTableWithAdminAndName.ts deleted file mode 100644 index af5082ebb2..0000000000 --- a/server/src/migrations/1652633525943-UpdateUserTableWithAdminAndName.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UpdateUserTableWithAdminAndName1652633525943 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - alter table users - add column if not exists "firstName" varchar default ''; - - alter table users - add column if not exists "lastName" varchar default ''; - - alter table users - add column if not exists "profileImagePath" varchar default ''; - - alter table users - add column if not exists "isAdmin" bool default false; - - alter table users - add column if not exists "isFirstLoggedIn" bool default true; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - alter table users - drop column "firstName"; - - alter table users - drop column "lastName"; - - alter table users - drop column "isAdmin"; - - `); - } -} diff --git a/server/src/migrations/1653214255670-UpdateAssetTableWithWebpPath.ts b/server/src/migrations/1653214255670-UpdateAssetTableWithWebpPath.ts deleted file mode 100644 index 4de9684f18..0000000000 --- a/server/src/migrations/1653214255670-UpdateAssetTableWithWebpPath.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UpdateAssetTableWithWebpPath1653214255670 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - alter table assets - add column if not exists "webpPath" varchar default ''; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - alter table assets - drop column if exists "webpPath"; - `); - } -} diff --git a/server/src/migrations/1654299904583-UpdateAssetTableWithEncodeVideoPath.ts b/server/src/migrations/1654299904583-UpdateAssetTableWithEncodeVideoPath.ts deleted file mode 100644 index 169f7db171..0000000000 --- a/server/src/migrations/1654299904583-UpdateAssetTableWithEncodeVideoPath.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UpdateAssetTableWithEncodeVideoPath1654299904583 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - alter table assets - add column if not exists "encodedVideoPath" varchar default ''; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - alter table assets - drop column if exists "encodedVideoPath"; - `); - } -} diff --git a/server/src/migrations/1655401127251-RenameSharedAlbums.ts b/server/src/migrations/1655401127251-RenameSharedAlbums.ts deleted file mode 100644 index 9bb71fb08c..0000000000 --- a/server/src/migrations/1655401127251-RenameSharedAlbums.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RenameSharedAlbums1655401127251 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE shared_albums RENAME TO albums; - - ALTER TABLE asset_shared_album RENAME TO asset_album; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE asset_album RENAME TO asset_shared_album; - - ALTER TABLE albums RENAME TO shared_albums; - `); - } -} diff --git a/server/src/migrations/1656338626260-RenameIsFirstLoggedInColumn.ts b/server/src/migrations/1656338626260-RenameIsFirstLoggedInColumn.ts deleted file mode 100644 index c4e4d7cd63..0000000000 --- a/server/src/migrations/1656338626260-RenameIsFirstLoggedInColumn.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RenameIsFirstLoggedInColumn1656338626260 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE users - RENAME COLUMN "isFirstLoggedIn" to "shouldChangePassword"; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE users - RENAME COLUMN "shouldChangePassword" to "isFirstLoggedIn"; - `); - } -} diff --git a/server/src/migrations/1656888591977-RenameAssetAlbumIdSequence.ts b/server/src/migrations/1656888591977-RenameAssetAlbumIdSequence.ts deleted file mode 100644 index 07d5592f53..0000000000 --- a/server/src/migrations/1656888591977-RenameAssetAlbumIdSequence.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RenameAssetAlbumIdSequence1656888591977 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`alter sequence asset_shared_album_id_seq rename to asset_album_id_seq;`); - await queryRunner.query( - `alter table asset_album alter column id set default nextval('asset_album_id_seq'::regclass);`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`alter sequence asset_album_id_seq rename to asset_shared_album_id_seq;`); - await queryRunner.query( - `alter table asset_album alter column id set default nextval('asset_shared_album_id_seq'::regclass);`, - ); - } -} diff --git a/server/src/migrations/1656888918620-DropExifTextSearchableColumn.ts b/server/src/migrations/1656888918620-DropExifTextSearchableColumn.ts deleted file mode 100644 index 305b67ee84..0000000000 --- a/server/src/migrations/1656888918620-DropExifTextSearchableColumn.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class DropExifTextSearchableColumns1656888918620 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exif_text_searchable_column"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - DROP COLUMN IF EXISTS exif_text_searchable_column; - - ALTER TABLE exif - ADD COLUMN IF NOT EXISTS exif_text_searchable_column tsvector - GENERATED ALWAYS AS ( - TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') || ' ' || - COALESCE("city", '') || ' ' || - COALESCE("state", '') || ' ' || - COALESCE("country", '') - ) - ) STORED; - - CREATE INDEX exif_text_searchable_idx - ON exif - USING GIN (exif_text_searchable_column); - `); - } -} diff --git a/server/src/migrations/1656889061566-MatchMigrationsWithTypeORMEntities.ts b/server/src/migrations/1656889061566-MatchMigrationsWithTypeORMEntities.ts deleted file mode 100644 index 00a66d78e9..0000000000 --- a/server/src/migrations/1656889061566-MatchMigrationsWithTypeORMEntities.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class MatchMigrationsWithTypeORMEntities1656889061566 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "exifTextSearchableColumn" tsvector GENERATED ALWAYS AS (TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') || ' ' || - COALESCE("city", '') || ' ' || - COALESCE("state", '') || ' ' || - COALESCE("country", ''))) STORED`); - await queryRunner.query(`ALTER TABLE "exif" ALTER COLUMN "exifTextSearchableColumn" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "firstName" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "lastName" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "isAdmin" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "profileImagePath" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "shouldChangePassword" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "FK_a8b79a84996cef6ba6a3662825d"`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "FK_64f2e7d68d1d1d8417acc844a4a"`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "UQ_a1e2734a1ce361e7a26f6b28288"`); - await queryRunner.query( - `ALTER TABLE "asset_album" ADD CONSTRAINT "UQ_unique_asset_in_album" UNIQUE ("albumId", "assetId")`, - ); - await queryRunner.query( - `ALTER TABLE "asset_album" ADD CONSTRAINT "FK_256a30a03a4a0aff0394051397d" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `ALTER TABLE "asset_album" ADD CONSTRAINT "FK_7ae4e03729895bf87e056d7b598" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "shouldChangePassword" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "profileImagePath" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "isAdmin" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "lastName" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "firstName" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exifTextSearchableColumn"`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "FK_7ae4e03729895bf87e056d7b598"`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "FK_256a30a03a4a0aff0394051397d"`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "UQ_unique_asset_in_album"`); - await queryRunner.query( - `ALTER TABLE "asset_album" ADD CONSTRAINT "UQ_a1e2734a1ce361e7a26f6b28288" UNIQUE ("albumId", "assetId")`, - ); - await queryRunner.query( - `ALTER TABLE "asset_album" ADD CONSTRAINT "FK_64f2e7d68d1d1d8417acc844a4a" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `ALTER TABLE "asset_album" ADD CONSTRAINT "FK_a8b79a84996cef6ba6a3662825d" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, - ); - } -} diff --git a/server/src/migrations/1658860470248-AddExifImageNameAsSearchableText.ts b/server/src/migrations/1658860470248-AddExifImageNameAsSearchableText.ts deleted file mode 100644 index 3b175be3e5..0000000000 --- a/server/src/migrations/1658860470248-AddExifImageNameAsSearchableText.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddExifImageNameAsSearchableText1658860470248 implements MigrationInterface { - name = 'AddExifImageNameAsSearchableText1658860470248'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exifTextSearchableColumn"`); - await queryRunner.query(`ALTER TABLE "exif" ADD "exifTextSearchableColumn" tsvector GENERATED ALWAYS AS (TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') || ' ' || - COALESCE("imageName", '') || ' ' || - COALESCE("city", '') || ' ' || - COALESCE("state", '') || ' ' || - COALESCE("country", ''))) STORED`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "exifTextSearchableColumn" tsvector NOT NULL`); - } -} diff --git a/server/src/migrations/1661011331242-AddCaption.ts b/server/src/migrations/1661011331242-AddCaption.ts deleted file mode 100644 index f6370a7b66..0000000000 --- a/server/src/migrations/1661011331242-AddCaption.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddCaption1661011331242 implements MigrationInterface { - name = 'AddCaption1661011331242'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "description" text DEFAULT ''`); - await queryRunner.query(`ALTER TABLE "exif" ADD "fps" double precision`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "fps"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "description"`); - } -} diff --git a/server/src/migrations/1661528919411-ChangeExifFileSizeInByteToBigInt.ts b/server/src/migrations/1661528919411-ChangeExifFileSizeInByteToBigInt.ts deleted file mode 100644 index da614e7f9c..0000000000 --- a/server/src/migrations/1661528919411-ChangeExifFileSizeInByteToBigInt.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class ChangeExifFileSizeInByteToBigInt1661528919411 implements MigrationInterface { - name = 'ChangeExifFileSizeInByteToBigInt1661528919411'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - ALTER COLUMN "fileSizeInByte" type bigint using "fileSizeInByte"::bigint; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - ALTER COLUMN "fileSizeInByte" type integer using "fileSizeInByte"::integer; - `); - } -} diff --git a/server/src/migrations/1661881837496-AddAssetChecksum.ts b/server/src/migrations/1661881837496-AddAssetChecksum.ts deleted file mode 100644 index 2901b4f554..0000000000 --- a/server/src/migrations/1661881837496-AddAssetChecksum.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddAssetChecksum1661881837496 implements MigrationInterface { - name = 'AddAssetChecksum1661881837496'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "checksum" bytea`); - await queryRunner.query( - `CREATE INDEX "IDX_64c507300988dd1764f9a6530c" ON "assets" ("checksum") WHERE 'checksum' IS NOT NULL`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_64c507300988dd1764f9a6530c"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "checksum"`); - } -} diff --git a/server/src/migrations/1661971370662-UpdateAssetTableWithNewUniqueConstraint.ts b/server/src/migrations/1661971370662-UpdateAssetTableWithNewUniqueConstraint.ts deleted file mode 100644 index 15fa467878..0000000000 --- a/server/src/migrations/1661971370662-UpdateAssetTableWithNewUniqueConstraint.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UpdateAssetTableWithNewUniqueConstraint1661971370662 implements MigrationInterface { - name = 'UpdateAssetTableWithNewUniqueConstraint1661971370662'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_b599ab0bd9574958acb0b30a90e"`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "UQ_userid_checksum" UNIQUE ("userId", "checksum")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_userid_checksum"`); - await queryRunner.query( - `ALTER TABLE "assets" ADD CONSTRAINT "UQ_b599ab0bd9574958acb0b30a90e" UNIQUE ("deviceAssetId", "userId", "deviceId")`, - ); - } -} diff --git a/server/src/migrations/1662427365521-FixTimestampDataTypeInAssetTable.ts b/server/src/migrations/1662427365521-FixTimestampDataTypeInAssetTable.ts deleted file mode 100644 index a0ce4dc8c6..0000000000 --- a/server/src/migrations/1662427365521-FixTimestampDataTypeInAssetTable.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class FixTimestampDataTypeInAssetTable1662427365521 implements MigrationInterface { - name = 'FixTimestampDataTypeInAssetTable1662427365521'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ALTER COLUMN "exifTextSearchableColumn" SET NOT NULL`); - await queryRunner.query( - `ALTER TABLE "assets" ALTER COLUMN "createdAt" TYPE timestamptz USING "createdAt"::timestamptz`, - ); - await queryRunner.query( - `ALTER TABLE "assets" ALTER COLUMN "modifiedAt" TYPE timestamptz USING "createdAt"::timestamptz`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "createdAt" TYPE varchar USING "createdAt"::varchar`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "modifiedAt" TYPE varchar USING "createdAt"::varchar`); - await queryRunner.query(`ALTER TABLE "exif" ALTER COLUMN "exifTextSearchableColumn" DROP NOT NULL`); - } -} diff --git a/server/src/migrations/1665540663419-CreateSystemConfigTable.ts b/server/src/migrations/1665540663419-CreateSystemConfigTable.ts deleted file mode 100644 index 40dd87c644..0000000000 --- a/server/src/migrations/1665540663419-CreateSystemConfigTable.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateSystemConfigTable1665540663419 implements MigrationInterface { - name = 'CreateSystemConfigTable1665540663419'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TABLE "system_config" ("key" character varying NOT NULL, "value" character varying, CONSTRAINT "PK_aab69295b445016f56731f4d535" PRIMARY KEY ("key"))`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "system_config"`); - } -} diff --git a/server/src/migrations/1667762360744-AddingDeletedAtColumnInUserEntity.ts b/server/src/migrations/1667762360744-AddingDeletedAtColumnInUserEntity.ts deleted file mode 100644 index 1e80fc089a..0000000000 --- a/server/src/migrations/1667762360744-AddingDeletedAtColumnInUserEntity.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddingDeletedAtColumnInUserEntity1667762360744 implements MigrationInterface { - name = 'AddingDeletedAtColumnInUserEntity1667762360744'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "deletedAt" TIMESTAMP`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "deletedAt"`); - } -} diff --git a/server/src/migrations/1668383120461-AddLivePhotosRelatedColumnToAssetTable.ts b/server/src/migrations/1668383120461-AddLivePhotosRelatedColumnToAssetTable.ts deleted file mode 100644 index 62ce314f30..0000000000 --- a/server/src/migrations/1668383120461-AddLivePhotosRelatedColumnToAssetTable.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddLivePhotosRelatedColumnToAssetTable1668383120461 implements MigrationInterface { - name = 'AddLivePhotosRelatedColumnToAssetTable1668383120461' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "isVisible" boolean NOT NULL DEFAULT true`); - await queryRunner.query(`ALTER TABLE "assets" ADD "livePhotoVideoId" uuid`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "livePhotoVideoId"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "isVisible"`); - } - -} diff --git a/server/src/migrations/1668835311083-UpdateUserTableForOIDC.ts b/server/src/migrations/1668835311083-UpdateUserTableForOIDC.ts deleted file mode 100644 index 044b79c808..0000000000 --- a/server/src/migrations/1668835311083-UpdateUserTableForOIDC.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class UpdateUserTableForOIDC1668835311083 implements MigrationInterface { - name = 'UpdateUserTableForOIDC1668835311083' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "password" SET DEFAULT ''`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "salt" SET DEFAULT ''`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "salt" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "password" DROP DEFAULT`); - } - -} diff --git a/server/src/migrations/1670104716264-OAuthId.ts b/server/src/migrations/1670104716264-OAuthId.ts deleted file mode 100644 index 46b99a79d5..0000000000 --- a/server/src/migrations/1670104716264-OAuthId.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class OAuthId1670104716264 implements MigrationInterface { - name = 'OAuthId1670104716264' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "oauthId" character varying NOT NULL DEFAULT ''`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "oauthId"`); - } - -} diff --git a/server/src/migrations/1670257571385-CreateTagsTable.ts b/server/src/migrations/1670257571385-CreateTagsTable.ts deleted file mode 100644 index 75fba9249c..0000000000 --- a/server/src/migrations/1670257571385-CreateTagsTable.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class CreateTagsTable1670257571385 implements MigrationInterface { - name = 'CreateTagsTable1670257571385' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "tags" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "type" character varying NOT NULL, "name" character varying NOT NULL, "userId" uuid NOT NULL, "renameTagId" uuid, CONSTRAINT "UQ_tag_name_userId" UNIQUE ("name", "userId"), CONSTRAINT "PK_e7dc17249a1148a1970748eda99" PRIMARY KEY ("id")); COMMENT ON COLUMN "tags"."renameTagId" IS 'The new renamed tagId'`); - await queryRunner.query(`CREATE TABLE "tag_asset" ("assetsId" uuid NOT NULL, "tagsId" uuid NOT NULL, CONSTRAINT "PK_ef5346fe522b5fb3bc96454747e" PRIMARY KEY ("assetsId", "tagsId"))`); - await queryRunner.query(`CREATE INDEX "IDX_f8e8a9e893cb5c54907f1b798e" ON "tag_asset" ("assetsId") `); - await queryRunner.query(`CREATE INDEX "IDX_e99f31ea4cdf3a2c35c7287eb4" ON "tag_asset" ("tagsId") `); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "FK_92e67dc508c705dd66c94615576" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9" FOREIGN KEY ("assetsId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42" FOREIGN KEY ("tagsId") REFERENCES "tags"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42"`); - await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9"`); - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "FK_92e67dc508c705dd66c94615576"`); - await queryRunner.query(`DROP INDEX "IDX_e99f31ea4cdf3a2c35c7287eb4"`); - await queryRunner.query(`DROP INDEX "IDX_f8e8a9e893cb5c54907f1b798e"`); - await queryRunner.query(`DROP TABLE "tag_asset"`); - await queryRunner.query(`DROP TABLE "tags"`); - } - -} diff --git a/server/src/migrations/1670607437008-TruncateOldConfigItems.ts b/server/src/migrations/1670607437008-TruncateOldConfigItems.ts deleted file mode 100644 index 0a82783f89..0000000000 --- a/server/src/migrations/1670607437008-TruncateOldConfigItems.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class TruncateOldConfigItems1670607437008 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`TRUNCATE TABLE "system_config"`); - } - - public async down(): Promise { - // noop - } -} diff --git a/server/src/migrations/1670633210032-AddUserEmailUniqueConstraint.ts b/server/src/migrations/1670633210032-AddUserEmailUniqueConstraint.ts deleted file mode 100644 index 50a67ae94f..0000000000 --- a/server/src/migrations/1670633210032-AddUserEmailUniqueConstraint.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddUserEmailUniqueConstraint1670633210032 implements MigrationInterface { - name = 'AddUserEmailUniqueConstraint1670633210032' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD CONSTRAINT "UQ_97672ac88f789774dd47f7c8be3" UNIQUE ("email")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP CONSTRAINT "UQ_97672ac88f789774dd47f7c8be3"`); - } - -} diff --git a/server/src/migrations/1672109862870-DropSaltColumn.ts b/server/src/migrations/1672109862870-DropSaltColumn.ts deleted file mode 100644 index 91ca5ade11..0000000000 --- a/server/src/migrations/1672109862870-DropSaltColumn.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class DropSaltColumn1672109862870 implements MigrationInterface { - name = 'DropSaltColumn1672109862870' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "salt"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "salt" character varying NOT NULL DEFAULT ''`); - } - -} diff --git a/server/src/migrations/1672502270115-AddAPIKeys.ts b/server/src/migrations/1672502270115-AddAPIKeys.ts deleted file mode 100644 index e72b3dc2fe..0000000000 --- a/server/src/migrations/1672502270115-AddAPIKeys.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAPIKeys1672502270115 implements MigrationInterface { - name = 'AddAPIKeys1672502270115' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "api_keys" ("id" SERIAL NOT NULL, "name" character varying NOT NULL, "key" character varying NOT NULL, "userId" uuid NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_5c8a79801b44bd27b79228e1dad" PRIMARY KEY ("id"))`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD CONSTRAINT "FK_6c2e267ae764a9413b863a29342" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "api_keys" DROP CONSTRAINT "FK_6c2e267ae764a9413b863a29342"`); - await queryRunner.query(`DROP TABLE "api_keys"`); - } - -} diff --git a/server/src/migrations/1673150490490-AddSharedLinkTable.ts b/server/src/migrations/1673150490490-AddSharedLinkTable.ts deleted file mode 100644 index 8d5bd2f5a5..0000000000 --- a/server/src/migrations/1673150490490-AddSharedLinkTable.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddSharedLinkTable1673150490490 implements MigrationInterface { - name = 'AddSharedLinkTable1673150490490' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "shared_links" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "description" character varying, "userId" character varying NOT NULL, "key" bytea NOT NULL, "type" character varying NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "expiresAt" TIMESTAMP WITH TIME ZONE, "allowUpload" boolean NOT NULL DEFAULT false, "albumId" uuid, CONSTRAINT "UQ_sharedlink_key" UNIQUE ("key"), CONSTRAINT "PK_642e2b0f619e4876e5f90a43465" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_sharedlink_key" ON "shared_links" ("key") `); - await queryRunner.query(`CREATE TABLE "shared_link__asset" ("assetsId" uuid NOT NULL, "sharedLinksId" uuid NOT NULL, CONSTRAINT "PK_9b4f3687f9b31d1e311336b05e3" PRIMARY KEY ("assetsId", "sharedLinksId"))`); - await queryRunner.query(`CREATE INDEX "IDX_5b7decce6c8d3db9593d6111a6" ON "shared_link__asset" ("assetsId") `); - await queryRunner.query(`CREATE INDEX "IDX_c9fab4aa97ffd1b034f3d6581a" ON "shared_link__asset" ("sharedLinksId") `); - await queryRunner.query(`ALTER TABLE "shared_links" ADD CONSTRAINT "FK_0c6ce9058c29f07cdf7014eac66" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "shared_link__asset" ADD CONSTRAINT "FK_5b7decce6c8d3db9593d6111a66" FOREIGN KEY ("assetsId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "shared_link__asset" ADD CONSTRAINT "FK_c9fab4aa97ffd1b034f3d6581ab" FOREIGN KEY ("sharedLinksId") REFERENCES "shared_links"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_link__asset" DROP CONSTRAINT "FK_c9fab4aa97ffd1b034f3d6581ab"`); - await queryRunner.query(`ALTER TABLE "shared_link__asset" DROP CONSTRAINT "FK_5b7decce6c8d3db9593d6111a66"`); - await queryRunner.query(`ALTER TABLE "shared_links" DROP CONSTRAINT "FK_0c6ce9058c29f07cdf7014eac66"`); - await queryRunner.query(`DROP INDEX "IDX_c9fab4aa97ffd1b034f3d6581a"`); - await queryRunner.query(`DROP INDEX "IDX_5b7decce6c8d3db9593d6111a6"`); - await queryRunner.query(`DROP TABLE "shared_link__asset"`); - await queryRunner.query(`DROP INDEX "IDX_sharedlink_key"`); - await queryRunner.query(`DROP TABLE "shared_links"`); - } - -} diff --git a/server/src/migrations/1673907194740-AddMorePermissionToSharedLink.ts b/server/src/migrations/1673907194740-AddMorePermissionToSharedLink.ts deleted file mode 100644 index af0a0280a8..0000000000 --- a/server/src/migrations/1673907194740-AddMorePermissionToSharedLink.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddMorePermissionToSharedLink1673907194740 implements MigrationInterface { - name = 'AddMorePermissionToSharedLink1673907194740'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" ADD "allowDownload" boolean NOT NULL DEFAULT true`); - await queryRunner.query(`ALTER TABLE "shared_links" ADD "showExif" boolean NOT NULL DEFAULT true`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" DROP COLUMN "showExif"`); - await queryRunner.query(`ALTER TABLE "shared_links" DROP COLUMN "allowDownload"`); - } -} diff --git a/server/src/migrations/1674263302005-RemoveVideoCodecConfigOption.ts b/server/src/migrations/1674263302005-RemoveVideoCodecConfigOption.ts deleted file mode 100644 index 5f64b11559..0000000000 --- a/server/src/migrations/1674263302005-RemoveVideoCodecConfigOption.ts +++ /dev/null @@ -1,12 +0,0 @@ -import {MigrationInterface, QueryRunner} from 'typeorm'; - -export class RemoveVideoCodecConfigOption1674263302006 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DELETE FROM "system_config" WHERE key = 'ffmpeg.targetVideoCodec'`); - await queryRunner.query(`DELETE FROM "system_config" WHERE key = 'ffmpeg.targetAudioCodec'`); - } - - public async down(): Promise { - // noop - } -} diff --git a/server/src/migrations/1674342044239-CreateUserTokenEntity.ts b/server/src/migrations/1674342044239-CreateUserTokenEntity.ts deleted file mode 100644 index e289787f91..0000000000 --- a/server/src/migrations/1674342044239-CreateUserTokenEntity.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class CreateUserTokenEntity1674342044239 implements MigrationInterface { - name = 'CreateUserTokenEntity1674342044239' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "user_token" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "token" character varying NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "userId" uuid, CONSTRAINT "PK_48cb6b5c20faa63157b3c1baf7f" PRIMARY KEY ("id"))`); - await queryRunner.query(`ALTER TABLE "user_token" ADD CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_token" DROP CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918"`); - await queryRunner.query(`DROP TABLE "user_token"`); - } - -} diff --git a/server/src/migrations/1674757936889-AlterExifExposureTimeToString.ts b/server/src/migrations/1674757936889-AlterExifExposureTimeToString.ts deleted file mode 100644 index de21e180b7..0000000000 --- a/server/src/migrations/1674757936889-AlterExifExposureTimeToString.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AlterExifExposureTimeToString1674757936889 implements MigrationInterface { - name = 'AlterExifExposureTimeToString1674757936889' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exposureTime"`); - await queryRunner.query(`ALTER TABLE "exif" ADD "exposureTime" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exposureTime"`); - await queryRunner.query(`ALTER TABLE "exif" ADD "exposureTime" double precision`); - } - -} diff --git a/server/src/migrations/1674774248319-TruncateAPIKeys.ts b/server/src/migrations/1674774248319-TruncateAPIKeys.ts deleted file mode 100644 index efbb5c41af..0000000000 --- a/server/src/migrations/1674774248319-TruncateAPIKeys.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class TruncateAPIKeys1674774248319 implements MigrationInterface { - name = 'TruncateAPIKeys1674774248319' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`TRUNCATE TABLE "api_keys"`); - } - - public async down(): Promise { - //noop - } - -} diff --git a/server/src/migrations/1674939383309-AddSharedLinkUserForeignKeyConstraint.ts b/server/src/migrations/1674939383309-AddSharedLinkUserForeignKeyConstraint.ts deleted file mode 100644 index 9119c57065..0000000000 --- a/server/src/migrations/1674939383309-AddSharedLinkUserForeignKeyConstraint.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddSharedLinkUserForeignKeyConstraint1674939383309 implements MigrationInterface { - name = 'AddSharedLinkUserForeignKeyConstraint1674939383309'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" ALTER COLUMN "userId" TYPE varchar(36)`); - await queryRunner.query(`ALTER TABLE "shared_links" ALTER COLUMN "userId" TYPE uuid using "userId"::uuid`); - await queryRunner.query( - `ALTER TABLE "shared_links" ADD CONSTRAINT "FK_66fe3837414c5a9f1c33ca49340" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" DROP CONSTRAINT "FK_66fe3837414c5a9f1c33ca49340"`); - await queryRunner.query(`ALTER TABLE "shared_links" ALTER COLUMN "userId" TYPE character varying`); - } -} diff --git a/server/src/migrations/1675667878312-AddUpdatedAtColumnToAlbumsUsersAssets.ts b/server/src/migrations/1675667878312-AddUpdatedAtColumnToAlbumsUsersAssets.ts deleted file mode 100644 index 90c00b38e9..0000000000 --- a/server/src/migrations/1675667878312-AddUpdatedAtColumnToAlbumsUsersAssets.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddUpdatedAtColumnToAlbumsUsersAssets1675667878312 implements MigrationInterface { - name = 'AddUpdatedAtColumnToAlbumsUsersAssets1675667878312'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" ADD "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); - await queryRunner.query(`ALTER TABLE "users" ADD "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); - await queryRunner.query(`ALTER TABLE "assets" ADD "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" DROP COLUMN "updatedAt"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "updatedAt"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "updatedAt"`); - } -} diff --git a/server/src/migrations/1675701909594-AddAlbumUserForeignKeyConstraint.ts b/server/src/migrations/1675701909594-AddAlbumUserForeignKeyConstraint.ts deleted file mode 100644 index 0898accfb7..0000000000 --- a/server/src/migrations/1675701909594-AddAlbumUserForeignKeyConstraint.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddAlbumUserForeignKeyConstraint1675701909594 implements MigrationInterface { - name = 'AddAlbumUserForeignKeyConstraint1675701909594'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DELETE FROM "albums" WHERE "ownerId"::uuid NOT IN (SELECT id FROM "users") `) - await queryRunner.query(`ALTER TABLE "albums" ALTER COLUMN "ownerId" TYPE varchar(36)`); - await queryRunner.query(`ALTER TABLE "albums" ALTER COLUMN "ownerId" TYPE uuid using "ownerId"::uuid`); - await queryRunner.query( - `ALTER TABLE "albums" ADD CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" DROP CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4"`); - await queryRunner.query(`ALTER TABLE "albums" ALTER COLUMN "ownerId" TYPE character varying`); - } -} diff --git a/server/src/migrations/1675808874445-APIKeyUUIDPrimaryKey.ts b/server/src/migrations/1675808874445-APIKeyUUIDPrimaryKey.ts deleted file mode 100644 index 368a9ca9c5..0000000000 --- a/server/src/migrations/1675808874445-APIKeyUUIDPrimaryKey.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class APIKeyUUIDPrimaryKey1675808874445 implements MigrationInterface { - name = 'APIKeyUUIDPrimaryKey1675808874445' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "api_keys" DROP CONSTRAINT "PK_5c8a79801b44bd27b79228e1dad"`); - await queryRunner.query(`ALTER TABLE "api_keys" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD "id" uuid NOT NULL DEFAULT uuid_generate_v4()`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD CONSTRAINT "PK_5c8a79801b44bd27b79228e1dad" PRIMARY KEY ("id")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "api_keys" DROP CONSTRAINT "PK_5c8a79801b44bd27b79228e1dad"`); - await queryRunner.query(`ALTER TABLE "api_keys" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD "id" SERIAL NOT NULL`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD CONSTRAINT "PK_5c8a79801b44bd27b79228e1dad" PRIMARY KEY ("id")`); - } - -} diff --git a/server/src/migrations/1675812532822-FixAlbumEntityTypeORM.ts b/server/src/migrations/1675812532822-FixAlbumEntityTypeORM.ts deleted file mode 100644 index 6f48ac736d..0000000000 --- a/server/src/migrations/1675812532822-FixAlbumEntityTypeORM.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class FixAlbumEntityTypeORM1675812532822 implements MigrationInterface { - name = 'FixAlbumEntityTypeORM1675812532822' - - public async up(queryRunner: QueryRunner): Promise { - - await queryRunner.query(`ALTER TABLE "asset_album" RENAME TO "albums_assets_assets"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP CONSTRAINT "FK_7ae4e03729895bf87e056d7b598"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP CONSTRAINT "FK_256a30a03a4a0aff0394051397d"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP CONSTRAINT "UQ_unique_asset_in_album"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP CONSTRAINT "PK_a34e076afbc601d81938e2c2277"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" RENAME COLUMN "albumId" TO "albumsId"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" RENAME COLUMN "assetId" TO "assetsId"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" ADD CONSTRAINT "PK_c67bc36fa845fb7b18e0e398180" PRIMARY KEY ("albumsId", "assetsId")`); - await queryRunner.query(`CREATE INDEX "IDX_e590fa396c6898fcd4a50e4092" ON "albums_assets_assets" ("albumsId") `); - await queryRunner.query(`CREATE INDEX "IDX_4bd1303d199f4e72ccdf998c62" ON "albums_assets_assets" ("assetsId") `); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" ADD CONSTRAINT "FK_e590fa396c6898fcd4a50e40927" FOREIGN KEY ("albumsId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" ADD CONSTRAINT "FK_4bd1303d199f4e72ccdf998c621" FOREIGN KEY ("assetsId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - - await queryRunner.query(`ALTER TABLE "user_shared_album" RENAME TO "albums_shared_users_users"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP CONSTRAINT "FK_543c31211653e63e080ba882eb5"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP CONSTRAINT "FK_7b3bf0f5f8da59af30519c25f18"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP CONSTRAINT "PK_unique_user_in_album"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP CONSTRAINT "PK_b6562316a98845a7b3e9a25cdd0"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" RENAME COLUMN "albumId" TO "albumsId"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" RENAME COLUMN "sharedUserId" TO "usersId"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ADD CONSTRAINT "PK_7df55657e0b2e8b626330a0ebc8" PRIMARY KEY ("albumsId", "usersId")`); - await queryRunner.query(`CREATE INDEX "IDX_427c350ad49bd3935a50baab73" ON "albums_shared_users_users" ("albumsId") `); - await queryRunner.query(`CREATE INDEX "IDX_f48513bf9bccefd6ff3ad30bd0" ON "albums_shared_users_users" ("usersId") `); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ADD CONSTRAINT "FK_427c350ad49bd3935a50baab737" FOREIGN KEY ("albumsId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ADD CONSTRAINT "FK_f48513bf9bccefd6ff3ad30bd06" FOREIGN KEY ("usersId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - - await queryRunner.query(`ALTER TABLE "albums" DROP CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4"`) - await queryRunner.query(`ALTER TABLE "albums" ADD CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums_assets_assets" RENAME TO "asset_album"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" RENAME TO "user_shared_album"`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "FK_e590fa396c6898fcd4a50e40927"`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "FK_4bd1303d199f4e72ccdf998c621"`); - await queryRunner.query(`ALTER TABLE "user_shared_album" DROP CONSTRAINT "FK_427c350ad49bd3935a50baab737"`); - await queryRunner.query(`ALTER TABLE "user_shared_album" DROP CONSTRAINT "FK_f48513bf9bccefd6ff3ad30bd06"`); - await queryRunner.query(`DROP INDEX "IDX_427c350ad49bd3935a50baab73"`); - await queryRunner.query(`DROP INDEX "IDX_f48513bf9bccefd6ff3ad30bd0"`); - await queryRunner.query(`DROP INDEX "IDX_e590fa396c6898fcd4a50e4092"`); - await queryRunner.query(`DROP INDEX "IDX_4bd1303d199f4e72ccdf998c62"`); - - await queryRunner.query(`ALTER TABLE "albums" DROP CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4"`); - await queryRunner.query( - `ALTER TABLE "albums" ADD CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - - await queryRunner.query(`ALTER TABLE "user_shared_album" DROP CONSTRAINT "PK_7df55657e0b2e8b626330a0ebc8"`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD CONSTRAINT "PK_323f8dcbe85373722886940f143" PRIMARY KEY ("albumsId")`); - await queryRunner.query(`ALTER TABLE "user_shared_album" DROP COLUMN "usersId"`); - await queryRunner.query(`ALTER TABLE "user_shared_album" DROP CONSTRAINT "PK_323f8dcbe85373722886940f143"`); - await queryRunner.query(`ALTER TABLE "user_shared_album" DROP COLUMN "albumsId"`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD "sharedUserId" uuid NOT NULL`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD "albumId" uuid NOT NULL`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD "id" SERIAL NOT NULL`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD CONSTRAINT "PK_b6562316a98845a7b3e9a25cdd0" PRIMARY KEY ("id")`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD CONSTRAINT "PK_unique_user_in_album" UNIQUE ("albumId", "sharedUserId")`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD CONSTRAINT "FK_7b3bf0f5f8da59af30519c25f18" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD CONSTRAINT "FK_543c31211653e63e080ba882eb5" FOREIGN KEY ("sharedUserId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "PK_c67bc36fa845fb7b18e0e398180"`); - await queryRunner.query(`ALTER TABLE "asset_album" ADD CONSTRAINT "PK_b4f2e5b96efc25cbccd80a04f7a" PRIMARY KEY ("albumsId")`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "PK_b4f2e5b96efc25cbccd80a04f7a"`); - await queryRunner.query(`ALTER TABLE "asset_album" RENAME COLUMN "albumsId" TO "albumId"`); - await queryRunner.query(`ALTER TABLE "asset_album" RENAME COLUMN "assetsId" TO "assetId"`); - await queryRunner.query(`ALTER TABLE "asset_album" ADD "id" SERIAL NOT NULL`); - await queryRunner.query(`ALTER TABLE "asset_album" ADD CONSTRAINT "PK_a34e076afbc601d81938e2c2277" PRIMARY KEY ("id")`); - await queryRunner.query(`ALTER TABLE "asset_album" ADD CONSTRAINT "UQ_unique_asset_in_album" UNIQUE ("albumId", "assetId")`); - await queryRunner.query(`ALTER TABLE "asset_album" ADD CONSTRAINT "FK_256a30a03a4a0aff0394051397d" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "asset_album" ADD CONSTRAINT "FK_7ae4e03729895bf87e056d7b598" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - -} diff --git a/server/src/migrations/1676437878377-AppleContentIdentifier.ts b/server/src/migrations/1676437878377-AppleContentIdentifier.ts deleted file mode 100644 index 8d11139878..0000000000 --- a/server/src/migrations/1676437878377-AppleContentIdentifier.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AppleContentIdentifier1676437878377 implements MigrationInterface { - name = 'AppleContentIdentifier1676437878377'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "livePhotoCID" character varying`); - await queryRunner.query(`CREATE INDEX "IDX_live_photo_cid" ON "exif" ("livePhotoCID") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_live_photo_cid"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "livePhotoCID"`); - } -} diff --git a/server/src/migrations/1676680127415-FixAssetRelations.ts b/server/src/migrations/1676680127415-FixAssetRelations.ts deleted file mode 100644 index 439e86a78a..0000000000 --- a/server/src/migrations/1676680127415-FixAssetRelations.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class FixAssetRelations1676680127415 implements MigrationInterface { - name = 'FixAssetRelations1676680127415' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "modifiedAt" TO "fileModifiedAt"`); - await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "createdAt" TO "fileCreatedAt"`); - - await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "userId" TO "ownerId"`); - await queryRunner.query(`ALTER TABLE assets ALTER COLUMN "ownerId" TYPE uuid USING "ownerId"::uuid;`); - - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "UQ_16294b83fa8c0149719a1f631ef" UNIQUE ("livePhotoVideoId")`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "FK_2c5ac0d6fb58b238fd2068de67d" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "FK_16294b83fa8c0149719a1f631ef" FOREIGN KEY ("livePhotoVideoId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_16294b83fa8c0149719a1f631ef"`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_2c5ac0d6fb58b238fd2068de67d"`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_16294b83fa8c0149719a1f631ef"`); - - await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "fileCreatedAt" TO "createdAt"`); - await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "fileModifiedAt" TO "modifiedAt"`); - - await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "ownerId" TO "userId"`); - await queryRunner.query(`ALTER TABLE assets ALTER COLUMN "userId" TYPE varchar`); - } - -} diff --git a/server/src/migrations/1676721296440-AssetCreatedAtField.ts b/server/src/migrations/1676721296440-AssetCreatedAtField.ts deleted file mode 100644 index 304c7b2190..0000000000 --- a/server/src/migrations/1676721296440-AssetCreatedAtField.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AssetCreatedAtField1676721296440 implements MigrationInterface { - name = 'AssetCreatedAtField1676721296440' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "createdAt"`); - } - -} diff --git a/server/src/migrations/1676848629119-ExifEntityDefinitionFixes.ts b/server/src/migrations/1676848629119-ExifEntityDefinitionFixes.ts deleted file mode 100644 index 947559ed2d..0000000000 --- a/server/src/migrations/1676848629119-ExifEntityDefinitionFixes.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class ExifEntityDefinitionFixes1676848629119 implements MigrationInterface { - name = 'ExifEntityDefinitionFixes1676848629119' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ALTER COLUMN "description" SET NOT NULL`); - - await queryRunner.query(`DROP INDEX "IDX_c0117fdbc50b917ef9067740c4"`); - await queryRunner.query(`ALTER TABLE "exif" DROP CONSTRAINT "PK_28663352d85078ad0046dafafaa"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "exif" DROP CONSTRAINT "FK_c0117fdbc50b917ef9067740c44"`); - await queryRunner.query(`ALTER TABLE "exif" ADD CONSTRAINT "PK_c0117fdbc50b917ef9067740c44" PRIMARY KEY ("assetId")`); - await queryRunner.query(`ALTER TABLE "exif" ADD CONSTRAINT "FK_c0117fdbc50b917ef9067740c44" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ALTER COLUMN "description" DROP NOT NULL`); - - await queryRunner.query(`ALTER TABLE "exif" DROP CONSTRAINT "FK_c0117fdbc50b917ef9067740c44"`); - await queryRunner.query(`ALTER TABLE "exif" DROP CONSTRAINT "PK_c0117fdbc50b917ef9067740c44"`); - await queryRunner.query(`ALTER TABLE "exif" ADD CONSTRAINT "FK_c0117fdbc50b917ef9067740c44" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "exif" ADD "id" SERIAL NOT NULL`); - await queryRunner.query(`ALTER TABLE "exif" ADD CONSTRAINT "PK_28663352d85078ad0046dafafaa" PRIMARY KEY ("id")`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_c0117fdbc50b917ef9067740c4" ON "exif" ("assetId") `); - } - -} diff --git a/server/src/migrations/1676848694786-SharedLinkEntityDefinitionFixes.ts b/server/src/migrations/1676848694786-SharedLinkEntityDefinitionFixes.ts deleted file mode 100644 index d48f543fef..0000000000 --- a/server/src/migrations/1676848694786-SharedLinkEntityDefinitionFixes.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class SharedLinkEntityDefinitionFixes1676848694786 implements MigrationInterface { - name = 'SharedLinkEntityDefinitionFixes1676848694786' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" ALTER COLUMN "createdAt" SET DEFAULT now()`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" ALTER COLUMN "createdAt" DROP DEFAULT`); - } - -} diff --git a/server/src/migrations/1676852143506-SmartInfoEntityDefinitionFixes.ts b/server/src/migrations/1676852143506-SmartInfoEntityDefinitionFixes.ts deleted file mode 100644 index e089619c6d..0000000000 --- a/server/src/migrations/1676852143506-SmartInfoEntityDefinitionFixes.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class SmartInfoEntityDefinitionFixes1676852143506 implements MigrationInterface { - name = 'SmartInfoEntityDefinitionFixes1676852143506' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_5e3753aadd956110bf3ec0244a"`); - await queryRunner.query(`ALTER TABLE "smart_info" DROP CONSTRAINT "PK_0beace66440e9713f5c40470e46"`); - await queryRunner.query(`ALTER TABLE "smart_info" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "smart_info" DROP CONSTRAINT "FK_5e3753aadd956110bf3ec0244ac"`); - await queryRunner.query(`ALTER TABLE "smart_info" ADD CONSTRAINT "PK_5e3753aadd956110bf3ec0244ac" PRIMARY KEY ("assetId")`); - await queryRunner.query(`ALTER TABLE "smart_info" ADD CONSTRAINT "FK_5e3753aadd956110bf3ec0244ac" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "smart_info" DROP CONSTRAINT "FK_5e3753aadd956110bf3ec0244ac"`); - await queryRunner.query(`ALTER TABLE "smart_info" DROP CONSTRAINT "PK_5e3753aadd956110bf3ec0244ac"`); - await queryRunner.query(`ALTER TABLE "smart_info" ADD CONSTRAINT "FK_5e3753aadd956110bf3ec0244ac" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "smart_info" ADD "id" SERIAL NOT NULL`); - await queryRunner.query(`ALTER TABLE "smart_info" ADD CONSTRAINT "PK_0beace66440e9713f5c40470e46" PRIMARY KEY ("id")`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_5e3753aadd956110bf3ec0244a" ON "smart_info" ("assetId") `); - } - -} diff --git a/server/src/migrations/1677497925328-AddExifTimeZone.ts b/server/src/migrations/1677497925328-AddExifTimeZone.ts deleted file mode 100644 index 33f958336e..0000000000 --- a/server/src/migrations/1677497925328-AddExifTimeZone.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddExifTimeZone1677497925328 implements MigrationInterface { - name = 'AddExifTimeZone1677497925328' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "timeZone" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "timeZone"`); - } - -} diff --git a/server/src/migrations/1677535643119-AddIndexForAlbumInSharedLinkTable.ts b/server/src/migrations/1677535643119-AddIndexForAlbumInSharedLinkTable.ts deleted file mode 100644 index 986b5ebd20..0000000000 --- a/server/src/migrations/1677535643119-AddIndexForAlbumInSharedLinkTable.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddIndexForAlbumInSharedLinkTable1677535643119 implements MigrationInterface { - name = 'AddIndexForAlbumInSharedLinkTable1677535643119' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX "IDX_sharedlink_albumId" ON "shared_links" ("albumId") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_sharedlink_albumId"`); - } - -} diff --git a/server/src/migrations/1677613712565-AlbumThumbnailRelation.ts b/server/src/migrations/1677613712565-AlbumThumbnailRelation.ts deleted file mode 100644 index 71f022dcf6..0000000000 --- a/server/src/migrations/1677613712565-AlbumThumbnailRelation.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AlbumThumbnailRelation1677613712565 implements MigrationInterface { - name = 'AlbumThumbnailRelation1677613712565'; - - public async up(queryRunner: QueryRunner): Promise { - // Make sure all albums have a valid albumThumbnailAssetId UUID or NULL. - await queryRunner.query(` - UPDATE "albums" - SET - "albumThumbnailAssetId" = ( - SELECT - "albums_assets2"."assetsId" - FROM - "assets" "assets", - "albums_assets_assets" "albums_assets2" - WHERE - "albums_assets2"."assetsId" = "assets"."id" - AND "albums_assets2"."albumsId" = "albums"."id" - ORDER BY - "assets"."fileCreatedAt" DESC - LIMIT 1 - ), - "updatedAt" = CURRENT_TIMESTAMP - WHERE - "albums"."albumThumbnailAssetId" IS NULL - AND EXISTS ( - SELECT 1 - FROM "albums_assets_assets" "albums_assets" - WHERE "albums"."id" = "albums_assets"."albumsId" - ) - OR "albums"."albumThumbnailAssetId" IS NOT NULL - AND NOT EXISTS ( - SELECT 1 - FROM "albums_assets_assets" "albums_assets" - WHERE - "albums"."id" = "albums_assets"."albumsId" - AND "albums"."albumThumbnailAssetId" = "albums_assets"."assetsId"::varchar - ) - `); - - await queryRunner.query(` - ALTER TABLE "albums" - ALTER COLUMN "albumThumbnailAssetId" - TYPE uuid USING "albumThumbnailAssetId"::uuid - `); - - await queryRunner.query(` - ALTER TABLE "albums" ADD CONSTRAINT "FK_05895aa505a670300d4816debce" FOREIGN KEY ("albumThumbnailAssetId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE CASCADE - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" DROP CONSTRAINT "FK_05895aa505a670300d4816debce"`); - - await queryRunner.query(` - ALTER TABLE "albums" ALTER COLUMN "albumThumbnailAssetId" TYPE varchar USING "albumThumbnailAssetId"::varchar - `); - } -} diff --git a/server/src/migrations/1677971458822-AddCLIPEncodeDataColumn.ts b/server/src/migrations/1677971458822-AddCLIPEncodeDataColumn.ts deleted file mode 100644 index 82f8176b0d..0000000000 --- a/server/src/migrations/1677971458822-AddCLIPEncodeDataColumn.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddCLIPEncodeDataColumn1677971458822 implements MigrationInterface { - name = 'AddCLIPEncodeDataColumn1677971458822'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "smart_info" ADD "clipEmbedding" numeric(20,19) array`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "smart_info" DROP COLUMN "clipEmbedding"`); - } -} diff --git a/server/src/migrations/1679751316282-UpdateTranscodeOption.ts b/server/src/migrations/1679751316282-UpdateTranscodeOption.ts deleted file mode 100644 index 989622e831..0000000000 --- a/server/src/migrations/1679751316282-UpdateTranscodeOption.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UpdateTranscodeOption1679751316282 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_config - SET - key = 'ffmpeg.transcode', - value = '"all"' - WHERE - key = 'ffmpeg.transcodeAll' AND value = 'true' - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_config - SET - key = 'ffmpeg.transcodeAll', - value = 'true' - WHERE - key = 'ffmpeg.transcode' AND value = '"all"' - `); - - await queryRunner.query(`DELETE FROM "system_config" WHERE key = 'ffmpeg.transcode'`); - } -} diff --git a/server/src/migrations/1679901204458-ClipEmbeddingFloat4.ts b/server/src/migrations/1679901204458-ClipEmbeddingFloat4.ts deleted file mode 100644 index 3afa8c6d10..0000000000 --- a/server/src/migrations/1679901204458-ClipEmbeddingFloat4.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class ClipEmbeddingFloat41679901204458 implements MigrationInterface { - name = 'ClipEmbeddingFloat41679901204458'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "smart_info" ALTER COLUMN "clipEmbedding" TYPE real array USING "clipEmbedding"::real array`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "smart_info" ALTER COLUMN "clipEmbedding" TYPE numeric(20,19) array USING "clipEmbedding"::numeric(20,19) array`, - ); - } -} diff --git a/server/src/migrations/1680632845740-AddIsArchivedColumn.ts b/server/src/migrations/1680632845740-AddIsArchivedColumn.ts deleted file mode 100644 index 325e37f489..0000000000 --- a/server/src/migrations/1680632845740-AddIsArchivedColumn.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddIsArchivedColumn1680632845740 implements MigrationInterface { - name = 'AddIsArchivedColumn1680632845740' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "isArchived" boolean NOT NULL DEFAULT false`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "isArchived"`); - } - -} diff --git a/server/src/migrations/1680694465853-RemoveRedundantConstraints.ts b/server/src/migrations/1680694465853-RemoveRedundantConstraints.ts deleted file mode 100644 index 1c3b051b02..0000000000 --- a/server/src/migrations/1680694465853-RemoveRedundantConstraints.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class RemoveRedundantConstraints1680694465853 implements MigrationInterface { - name = 'RemoveRedundantConstraints1680694465853' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP CONSTRAINT "REL_c0117fdbc50b917ef9067740c4"`); - await queryRunner.query(`ALTER TABLE "smart_info" DROP CONSTRAINT "UQ_5e3753aadd956110bf3ec0244ac"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "smart_info" ADD CONSTRAINT "UQ_5e3753aadd956110bf3ec0244ac" UNIQUE ("assetId")`); - await queryRunner.query(`ALTER TABLE "exif" ADD CONSTRAINT "REL_c0117fdbc50b917ef9067740c4" UNIQUE ("assetId")`); - } - -} diff --git a/server/src/migrations/1681144628393-AddOriginalFileNameToAssetTable.ts b/server/src/migrations/1681144628393-AddOriginalFileNameToAssetTable.ts deleted file mode 100644 index 7c547108b5..0000000000 --- a/server/src/migrations/1681144628393-AddOriginalFileNameToAssetTable.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddOriginalFileNameToAssetTable1681144628393 implements MigrationInterface { - name = 'AddOriginalFileNameToAssetTable1681144628393'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "originalFileName" character varying`); - - await queryRunner.query(` - UPDATE assets a - SET "originalFileName" = ( - select e."imageName" - from exif e - where e."assetId" = a.id - ) - `); - - await queryRunner.query(` - UPDATE assets a - SET "originalFileName" = a.id - where a."originalFileName" IS NULL or a."originalFileName" = '' - `); - - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "originalFileName" SET NOT NULL`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "originalFileName"`); - } -} diff --git a/server/src/migrations/1681159594469-RemoveImageNameFromEXIFTable.ts b/server/src/migrations/1681159594469-RemoveImageNameFromEXIFTable.ts deleted file mode 100644 index e188ea3506..0000000000 --- a/server/src/migrations/1681159594469-RemoveImageNameFromEXIFTable.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveImageNameFromEXIFTable1681159594469 implements MigrationInterface { - name = 'RemoveImageNameFromEXIFTable1681159594469'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN IF EXISTS "exifTextSearchableColumn"`); - await queryRunner.query(`ALTER TABLE "exif" ADD "exifTextSearchableColumn" tsvector GENERATED ALWAYS AS (TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') || ' ' || - COALESCE("city", '') || ' ' || - COALESCE("state", '') || ' ' || - COALESCE("country", ''))) STORED NOT NULL`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "imageName"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exifTextSearchableColumn"`); - await queryRunner.query(`ALTER TABLE "exif" ADD "exifTextSearchableColumn" tsvector GENERATED ALWAYS AS (TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') || ' ' || - COALESCE("imageName", '') || ' ' || - COALESCE("city", '') || ' ' || - COALESCE("state", '') || ' ' || - COALESCE("country", ''))) STORED NOT NULL`); - await queryRunner.query(`ALTER TABLE "exif" ADD "imageName" character varying`); - } -} diff --git a/server/src/migrations/1682371561743-FixNullableRelations.ts b/server/src/migrations/1682371561743-FixNullableRelations.ts deleted file mode 100644 index 42c34f9399..0000000000 --- a/server/src/migrations/1682371561743-FixNullableRelations.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class FixNullableRelations1682371561743 implements MigrationInterface { - name = 'FixNullableRelations1682371561743'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_token" DROP CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918"`); - await queryRunner.query(`ALTER TABLE "user_token" ALTER COLUMN "userId" SET NOT NULL`); - await queryRunner.query( - `ALTER TABLE "user_token" ADD CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_token" DROP CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918"`); - await queryRunner.query(`ALTER TABLE "user_token" ALTER COLUMN "userId" DROP NOT NULL`); - await queryRunner.query( - `ALTER TABLE "user_token" ADD CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - } -} diff --git a/server/src/migrations/1682371791038-AddDeviceInfoToUserToken.ts b/server/src/migrations/1682371791038-AddDeviceInfoToUserToken.ts deleted file mode 100644 index bb60e452ef..0000000000 --- a/server/src/migrations/1682371791038-AddDeviceInfoToUserToken.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddDeviceInfoToUserToken1682371791038 implements MigrationInterface { - name = 'AddDeviceInfoToUserToken1682371791038' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_token" ADD "deviceType" character varying NOT NULL DEFAULT ''`); - await queryRunner.query(`ALTER TABLE "user_token" ADD "deviceOS" character varying NOT NULL DEFAULT ''`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_token" DROP COLUMN "deviceOS"`); - await queryRunner.query(`ALTER TABLE "user_token" DROP COLUMN "deviceType"`); - } - -} diff --git a/server/src/migrations/1682710252424-DropDeviceInfoTable.ts b/server/src/migrations/1682710252424-DropDeviceInfoTable.ts deleted file mode 100644 index 9a07676351..0000000000 --- a/server/src/migrations/1682710252424-DropDeviceInfoTable.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class DropDeviceInfoTable1682710252424 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`drop table device_info`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - create table if not exists device_info - ( - id serial - constraint "PK_b1c15a80b0a4e5f4eebadbdd92c" - primary key, - "userId" varchar not null, - "deviceId" varchar not null, - "deviceType" varchar not null, - "notificationToken" varchar, - "createdAt" timestamp default now() not null, - "isAutoBackup" boolean default false not null, - constraint "UQ_ebad78f36b10d15fbea8560e107" - unique ("userId", "deviceId") - ); - `); - } -} diff --git a/server/src/migrations/1683808254676-AddPartnersTable.ts b/server/src/migrations/1683808254676-AddPartnersTable.ts deleted file mode 100644 index 64afb0b76c..0000000000 --- a/server/src/migrations/1683808254676-AddPartnersTable.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddPartnersTable1683808254676 implements MigrationInterface { - name = 'AddPartnersTable1683808254676' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "partners" ("sharedById" uuid NOT NULL, "sharedWithId" uuid NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_f1cc8f73d16b367f426261a8736" PRIMARY KEY ("sharedById", "sharedWithId"))`); - await queryRunner.query(`ALTER TABLE "partners" ADD CONSTRAINT "FK_7e077a8b70b3530138610ff5e04" FOREIGN KEY ("sharedById") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "partners" ADD CONSTRAINT "FK_d7e875c6c60e661723dbf372fd3" FOREIGN KEY ("sharedWithId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "partners" DROP CONSTRAINT "FK_d7e875c6c60e661723dbf372fd3"`); - await queryRunner.query(`ALTER TABLE "partners" DROP CONSTRAINT "FK_7e077a8b70b3530138610ff5e04"`); - await queryRunner.query(`DROP TABLE "partners"`); - } - -} diff --git a/server/src/migrations/1684255168091-AddFacialTables.ts b/server/src/migrations/1684255168091-AddFacialTables.ts deleted file mode 100644 index 1f2426bb0c..0000000000 --- a/server/src/migrations/1684255168091-AddFacialTables.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddFacialTables1684255168091 implements MigrationInterface { - name = 'AddFacialTables1684255168091' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "person" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "ownerId" uuid NOT NULL, "name" character varying NOT NULL DEFAULT '', "thumbnailPath" character varying NOT NULL DEFAULT '', CONSTRAINT "PK_5fdaf670315c4b7e70cce85daa3" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE TABLE "asset_faces" ("assetId" uuid NOT NULL, "personId" uuid NOT NULL, "embedding" real array, CONSTRAINT "PK_bf339a24070dac7e71304ec530a" PRIMARY KEY ("assetId", "personId"))`); - await queryRunner.query(`ALTER TABLE "person" ADD CONSTRAINT "FK_5527cc99f530a547093f9e577b6" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD CONSTRAINT "FK_02a43fd0b3c50fb6d7f0cb7282c" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD CONSTRAINT "FK_95ad7106dd7b484275443f580f9" FOREIGN KEY ("personId") REFERENCES "person"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_faces" DROP CONSTRAINT "FK_95ad7106dd7b484275443f580f9"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP CONSTRAINT "FK_02a43fd0b3c50fb6d7f0cb7282c"`); - await queryRunner.query(`ALTER TABLE "person" DROP CONSTRAINT "FK_5527cc99f530a547093f9e577b6"`); - await queryRunner.query(`DROP TABLE "asset_faces"`); - await queryRunner.query(`DROP TABLE "person"`); - } - -} diff --git a/server/src/migrations/1684273840676-AddSidecarFile.ts b/server/src/migrations/1684273840676-AddSidecarFile.ts deleted file mode 100644 index 46c4b5d375..0000000000 --- a/server/src/migrations/1684273840676-AddSidecarFile.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddSidecarFile1684273840676 implements MigrationInterface { - name = 'AddSidecarFile1684273840676' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "sidecarPath" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "sidecarPath"`); - } - -} diff --git a/server/src/migrations/1684328185099-RequireChecksumNotNull.ts b/server/src/migrations/1684328185099-RequireChecksumNotNull.ts deleted file mode 100644 index e691fff2b1..0000000000 --- a/server/src/migrations/1684328185099-RequireChecksumNotNull.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RequireChecksumNotNull1684328185099 implements MigrationInterface { - name = 'removeNotNullFromChecksumIndex1684328185099'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_64c507300988dd1764f9a6530c"`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "checksum" SET NOT NULL`); - await queryRunner.query(`CREATE INDEX "IDX_8d3efe36c0755849395e6ea866" ON "assets" ("checksum") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_8d3efe36c0755849395e6ea866"`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "checksum" DROP NOT NULL`); - await queryRunner.query( - `CREATE INDEX "IDX_64c507300988dd1764f9a6530c" ON "assets" ("checksum") WHERE ('checksum' IS NOT NULL)`, - ); - } -} diff --git a/server/src/migrations/1684410565398-AddStorageLabel.ts b/server/src/migrations/1684410565398-AddStorageLabel.ts deleted file mode 100644 index 6c6ea9702f..0000000000 --- a/server/src/migrations/1684410565398-AddStorageLabel.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddStorageLabel1684410565398 implements MigrationInterface { - name = 'AddStorageLabel1684410565398' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "storageLabel" character varying`); - await queryRunner.query(`ALTER TABLE "users" ADD CONSTRAINT "UQ_b309cf34fa58137c416b32cea3a" UNIQUE ("storageLabel")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP CONSTRAINT "UQ_b309cf34fa58137c416b32cea3a"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "storageLabel"`); - } - -} diff --git a/server/src/migrations/1684867360825-AddUserTokenAndAPIKeyCascades.ts b/server/src/migrations/1684867360825-AddUserTokenAndAPIKeyCascades.ts deleted file mode 100644 index 273c6b830f..0000000000 --- a/server/src/migrations/1684867360825-AddUserTokenAndAPIKeyCascades.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddUserTokenAndAPIKeyCascades1684867360825 implements MigrationInterface { - name = 'AddUserTokenAndAPIKeyCascades1684867360825' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "api_keys" DROP CONSTRAINT "FK_6c2e267ae764a9413b863a29342"`); - await queryRunner.query(`ALTER TABLE "user_token" DROP CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918"`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD CONSTRAINT "FK_6c2e267ae764a9413b863a29342" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "user_token" ADD CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_token" DROP CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918"`); - await queryRunner.query(`ALTER TABLE "api_keys" DROP CONSTRAINT "FK_6c2e267ae764a9413b863a29342"`); - await queryRunner.query(`ALTER TABLE "user_token" ADD CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD CONSTRAINT "FK_6c2e267ae764a9413b863a29342" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - -} diff --git a/server/src/migrations/1685044328272-AddSharedLinkCascade.ts b/server/src/migrations/1685044328272-AddSharedLinkCascade.ts deleted file mode 100644 index 3aefd3989e..0000000000 --- a/server/src/migrations/1685044328272-AddSharedLinkCascade.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddSharedLinkCascade1685044328272 implements MigrationInterface { - name = 'AddSharedLinkCascade1685044328272' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" DROP CONSTRAINT "FK_0c6ce9058c29f07cdf7014eac66"`); - await queryRunner.query(`ALTER TABLE "shared_links" ADD CONSTRAINT "FK_0c6ce9058c29f07cdf7014eac66" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" DROP CONSTRAINT "FK_0c6ce9058c29f07cdf7014eac66"`); - await queryRunner.query(`ALTER TABLE "shared_links" ADD CONSTRAINT "FK_0c6ce9058c29f07cdf7014eac66" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - -} diff --git a/server/src/migrations/1685370430343-UserDatesTimestamptz.ts b/server/src/migrations/1685370430343-UserDatesTimestamptz.ts deleted file mode 100644 index 0a70e012d8..0000000000 --- a/server/src/migrations/1685370430343-UserDatesTimestamptz.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class UserDatesTimestamptz1685370430343 implements MigrationInterface { - name = 'UserDatesTimestamptz1685370430343' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "deletedAt" TYPE TIMESTAMP WITH TIME ZONE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "deletedAt" TYPE TIMESTAMP`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "createdAt" TYPE TIMESTAMP`); - } - -} diff --git a/server/src/migrations/1685731372040-RemoveInvalidCoordinates.ts b/server/src/migrations/1685731372040-RemoveInvalidCoordinates.ts deleted file mode 100644 index 9a9b00a366..0000000000 --- a/server/src/migrations/1685731372040-RemoveInvalidCoordinates.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveInvalidCoordinates1685731372040 implements MigrationInterface { - name = 'RemoveInvalidCoordinates1685731372040'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`UPDATE "exif" SET "latitude" = NULL WHERE "latitude" IN ('NaN', 'Infinity', '-Infinity')`); - await queryRunner.query( - `UPDATE "exif" SET "longitude" = NULL WHERE "longitude" IN ('NaN', 'Infinity', '-Infinity')`, - ); - } - - public async down(): Promise { - // Empty, data cannot be restored - } -} diff --git a/server/src/migrations/1686584273471-ImportAsset.ts b/server/src/migrations/1686584273471-ImportAsset.ts deleted file mode 100644 index d9f5819a8d..0000000000 --- a/server/src/migrations/1686584273471-ImportAsset.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class ImportAsset1686584273471 implements MigrationInterface { - name = 'ImportAsset1686584273471' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "isReadOnly" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "UQ_4ed4f8052685ff5b1e7ca1058ba" UNIQUE ("originalPath")`); - await queryRunner.query(`ALTER TABLE "users" ADD "externalPath" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_4ed4f8052685ff5b1e7ca1058ba"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "isReadOnly"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "externalPath"`); - } - -} diff --git a/server/src/migrations/1686762895180-AddThumbhashColumn.ts b/server/src/migrations/1686762895180-AddThumbhashColumn.ts deleted file mode 100644 index 4ad73163db..0000000000 --- a/server/src/migrations/1686762895180-AddThumbhashColumn.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddThumbhashColumn1685546571785 implements MigrationInterface { - name = 'AddThumbhashColumn1686762895180'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "thumbhash" bytea NULL`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "thumbhash"`); - } -} diff --git a/server/src/migrations/1688241394489-AddDetectFaceResultInfo.ts b/server/src/migrations/1688241394489-AddDetectFaceResultInfo.ts deleted file mode 100644 index b026685bfb..0000000000 --- a/server/src/migrations/1688241394489-AddDetectFaceResultInfo.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddDetectFaceResultInfo1688241394489 implements MigrationInterface { - name = 'AddDetectFaceResultInfo1688241394489'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_faces" ADD "imageWidth" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD "imageHeight" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD "boundingBoxX1" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD "boundingBoxY1" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD "boundingBoxX2" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD "boundingBoxY2" integer NOT NULL DEFAULT '0'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "boundingBoxY2"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "boundingBoxX2"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "boundingBoxY1"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "boundingBoxX1"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "imageHeight"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "imageWidth"`); - } -} diff --git a/server/src/migrations/1688392120838-AddLibraryTable.ts b/server/src/migrations/1688392120838-AddLibraryTable.ts deleted file mode 100644 index 4d394adaf1..0000000000 --- a/server/src/migrations/1688392120838-AddLibraryTable.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddLibraries1688392120838 implements MigrationInterface { - name = 'AddLibraryTable1688392120838'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_userid_checksum"`); - await queryRunner.query( - `CREATE TABLE "libraries" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, "ownerId" uuid NOT NULL, "type" character varying NOT NULL, "importPaths" text array NOT NULL, "exclusionPatterns" text array NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "refreshedAt" TIMESTAMP WITH TIME ZONE, "isVisible" boolean NOT NULL DEFAULT true, CONSTRAINT "PK_505fedfcad00a09b3734b4223de" PRIMARY KEY ("id"))`, - ); - await queryRunner.query(`ALTER TABLE "assets" ADD "isOffline" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`ALTER TABLE "assets" ADD "libraryId" uuid`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_4ed4f8052685ff5b1e7ca1058ba"`); - await queryRunner.query(`ALTER TABLE "assets" ADD "isExternal" boolean NOT NULL DEFAULT false`); - - await queryRunner.query( - `CREATE UNIQUE INDEX "UQ_assets_owner_library_checksum" on "assets" ("ownerId", "libraryId", checksum)`, - ); - await queryRunner.query( - `ALTER TABLE "libraries" ADD CONSTRAINT "FK_0f6fc2fb195f24d19b0fb0d57c1" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`, - ); - await queryRunner.query( - `ALTER TABLE "assets" ADD CONSTRAINT "FK_9977c3c1de01c3d848039a6b90c" FOREIGN KEY ("libraryId") REFERENCES "libraries"("id") ON DELETE CASCADE ON UPDATE CASCADE`, - ); - - // Create default library for each user and assign all assets to it - const users = await queryRunner.query(`SELECT id FROM "users"`); - const userIds: string[] = users.map((user: any) => user.id); - - for (const userId of userIds) { - await queryRunner.query( - `INSERT INTO "libraries" ("name", "ownerId", "type", "importPaths", "exclusionPatterns") VALUES ('Default Library', '${userId}', 'UPLOAD', '{}', '{}')`, - ); - - await queryRunner.query( - `UPDATE "assets" SET "libraryId" = (SELECT id FROM "libraries" WHERE "ownerId" = '${userId}' LIMIT 1) WHERE "ownerId" = '${userId}'`, - ); - } - - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "libraryId" SET NOT NULL`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "libraryId" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_9977c3c1de01c3d848039a6b90c"`); - await queryRunner.query(`ALTER TABLE "libraries" DROP CONSTRAINT "FK_0f6fc2fb195f24d19b0fb0d57c1"`); - await queryRunner.query(`DROP INDEX "UQ_assets_owner_library_checksum"`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_owner_library_originalpath"`); - await queryRunner.query( - `ALTER TABLE "assets" ADD CONSTRAINT "UQ_4ed4f8052685ff5b1e7ca1058ba" UNIQUE ("originalPath")`, - ); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "libraryId"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "isOffline"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "isExternal"`); - await queryRunner.query(`DROP TABLE "libraries"`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "UQ_userid_checksum" UNIQUE ("ownerId", "checksum")`); - } -} diff --git a/server/src/migrations/1689001889950-DropMimeTypeColumn.ts b/server/src/migrations/1689001889950-DropMimeTypeColumn.ts deleted file mode 100644 index 45559313a1..0000000000 --- a/server/src/migrations/1689001889950-DropMimeTypeColumn.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class DropMimeTypeColumn1689001889950 implements MigrationInterface { - name = 'DropMimeTypeColumn1689001889950' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "mimeType"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "mimeType" character varying`); - } - -} diff --git a/server/src/migrations/1689281196844-AddHiddenFaces.ts b/server/src/migrations/1689281196844-AddHiddenFaces.ts deleted file mode 100644 index 234b77dd34..0000000000 --- a/server/src/migrations/1689281196844-AddHiddenFaces.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class Infra1689281196844 implements MigrationInterface { - name = 'Infra1689281196844' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" ADD "isHidden" boolean NOT NULL DEFAULT false`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" DROP COLUMN "isHidden"`); - } - -} diff --git a/server/src/migrations/1690469489288-Panoramas.ts b/server/src/migrations/1690469489288-Panoramas.ts deleted file mode 100644 index ee0934b43a..0000000000 --- a/server/src/migrations/1690469489288-Panoramas.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class Panoramas1690217088596 implements MigrationInterface { - name = 'Panoramas1690217088596'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "projectionType" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "projectionType"`); - } -} diff --git a/server/src/migrations/1691209138541-AddAlbumDescription.ts b/server/src/migrations/1691209138541-AddAlbumDescription.ts deleted file mode 100644 index f4167598af..0000000000 --- a/server/src/migrations/1691209138541-AddAlbumDescription.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddAlbumDescription1691209138541 implements MigrationInterface { - name = 'AddAlbumDescription1691209138541'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" ADD "description" text NOT NULL DEFAULT ''`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" DROP COLUMN "description"`); - } -} diff --git a/server/src/migrations/1691600216749-UserMemoryPreference.ts b/server/src/migrations/1691600216749-UserMemoryPreference.ts deleted file mode 100644 index 7238749080..0000000000 --- a/server/src/migrations/1691600216749-UserMemoryPreference.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UserMemoryPreference1691600216749 implements MigrationInterface { - name = 'UserMemoryPreference1691600216749'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "memoriesEnabled" boolean NOT NULL DEFAULT true`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "memoriesEnabled"`); - } -} diff --git a/server/src/migrations/1692057328660-fixGPSNullIsland.ts b/server/src/migrations/1692057328660-fixGPSNullIsland.ts deleted file mode 100644 index 74dc40a474..0000000000 --- a/server/src/migrations/1692057328660-fixGPSNullIsland.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class FixGPSNullIsland1692057328660 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`UPDATE "exif" SET latitude = NULL, longitude = NULL WHERE latitude = 0 AND longitude = 0;`); - } - - public async down(): Promise { - // Setting lat,lon to 0 not necessary - } - -} diff --git a/server/src/migrations/1692112147855-AddPersonBirthDate.ts b/server/src/migrations/1692112147855-AddPersonBirthDate.ts deleted file mode 100644 index db2ba35dad..0000000000 --- a/server/src/migrations/1692112147855-AddPersonBirthDate.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class AddPersonBirthDate1692112147855 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" ADD "birthDate" date`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" DROP COLUMN "birthDate"`); - } - -} diff --git a/server/src/migrations/1692804658140-AddAuditTable.ts b/server/src/migrations/1692804658140-AddAuditTable.ts deleted file mode 100644 index d398051a79..0000000000 --- a/server/src/migrations/1692804658140-AddAuditTable.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAuditTable1692804658140 implements MigrationInterface { - name = 'AddAuditTable1692804658140' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "audit" ("id" SERIAL NOT NULL, "entityType" character varying NOT NULL, "entityId" uuid NOT NULL, "action" character varying NOT NULL, "ownerId" uuid NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_1d3d120ddaf7bc9b1ed68ed463a" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_ownerId_createdAt" ON "audit" ("ownerId", "createdAt") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_ownerId_createdAt"`); - await queryRunner.query(`DROP TABLE "audit"`); - } - -} diff --git a/server/src/migrations/1693236627291-RenameMLEnableFlags.ts b/server/src/migrations/1693236627291-RenameMLEnableFlags.ts deleted file mode 100644 index 096356f6e0..0000000000 --- a/server/src/migrations/1693236627291-RenameMLEnableFlags.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class RenameMLEnableFlags1693236627291 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_config SET key = CASE - WHEN key = 'ffmpeg.classificationEnabled' THEN 'ffmpeg.classification.enabled' - WHEN key = 'ffmpeg.clipEnabled' THEN 'ffmpeg.clip.enabled' - WHEN key = 'ffmpeg.facialRecognitionEnabled' THEN 'ffmpeg.facialRecognition.enabled' - ELSE key - END - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_config SET key = CASE - WHEN key = 'ffmpeg.classification.enabled' THEN 'ffmpeg.classificationEnabled' - WHEN key = 'ffmpeg.clip.enabled' THEN 'ffmpeg.clipEnabled' - WHEN key = 'ffmpeg.facialRecognition.enabled' THEN 'ffmpeg.facialRecognitionEnabled' - ELSE key - END - `); - } -} diff --git a/server/src/migrations/1693833336881-AddPersonFaceAssetId.ts b/server/src/migrations/1693833336881-AddPersonFaceAssetId.ts deleted file mode 100644 index 2e3c914eec..0000000000 --- a/server/src/migrations/1693833336881-AddPersonFaceAssetId.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class AddPersonFaceAssetId1693833336881 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" ADD "faceAssetId" uuid`); - await queryRunner.query(`ALTER TABLE "person" ADD CONSTRAINT "FK_2bbabe31656b6778c6b87b61023" FOREIGN KEY ("faceAssetId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" DROP CONSTRAINT "FK_2bbabe31656b6778c6b87b61023"`); - await queryRunner.query(`ALTER TABLE "person" DROP COLUMN "faceAssetId"`); - } - -} diff --git a/server/src/migrations/1694204416744-AddAssetDeletedAtColumn.ts b/server/src/migrations/1694204416744-AddAssetDeletedAtColumn.ts deleted file mode 100644 index 3b213b9f04..0000000000 --- a/server/src/migrations/1694204416744-AddAssetDeletedAtColumn.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAssetDeletedAtColumn1694204416744 implements MigrationInterface { - name = 'AddAssetDeletedAtColumn1694204416744' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "deletedAt" TIMESTAMP WITH TIME ZONE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "deletedAt"`); - } - -} diff --git a/server/src/migrations/1694525143117-AddLocalDateTime.ts b/server/src/migrations/1694525143117-AddLocalDateTime.ts deleted file mode 100644 index dd24c07c0b..0000000000 --- a/server/src/migrations/1694525143117-AddLocalDateTime.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddLocalDateTime1694525143117 implements MigrationInterface { - name = 'AddLocalDateTime1694525143117'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "localDateTime" TIMESTAMP WITH TIME ZONE`); - await queryRunner.query(`UPDATE "assets" SET "localDateTime" = "fileCreatedAt"`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "localDateTime" SET NOT NULL`); - await queryRunner.query( - `CREATE INDEX "IDX_day_of_month" ON assets (EXTRACT(DAY FROM "localDateTime" AT TIME ZONE 'UTC'))`, - ); - await queryRunner.query( - `CREATE INDEX "IDX_month" ON assets (EXTRACT(MONTH FROM "localDateTime" AT TIME ZONE 'UTC'))`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "localDateTime"`); - await queryRunner.query(`DROP INDEX "IDX_day_of_month"`); - await queryRunner.query(`DROP INDEX "IDX_month"`); - } -} diff --git a/server/src/migrations/1694638413248-AddDeletedAtToAlbums.ts b/server/src/migrations/1694638413248-AddDeletedAtToAlbums.ts deleted file mode 100644 index 64a34c3e88..0000000000 --- a/server/src/migrations/1694638413248-AddDeletedAtToAlbums.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddDeletedAtToAlbums1694638413248 implements MigrationInterface { - name = 'AddDeletedAtToAlbums1694638413248'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" ADD "deletedAt" TIMESTAMP WITH TIME ZONE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" DROP COLUMN "deletedAt"`); - } -} diff --git a/server/src/migrations/1694750975773-AddExifColorSpace.ts b/server/src/migrations/1694750975773-AddExifColorSpace.ts deleted file mode 100644 index d6b3a6b2b0..0000000000 --- a/server/src/migrations/1694750975773-AddExifColorSpace.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddExifColorSpace1694750975773 implements MigrationInterface { - name = 'AddExifColorSpace1694750975773' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "profileDescription" character varying`); - await queryRunner.query(`ALTER TABLE "exif" ADD "colorspace" character varying`); - await queryRunner.query(`ALTER TABLE "exif" ADD "bitsPerSample" integer`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "bitsPerSample"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "colorspace"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "profileDescription"`); - } - -} diff --git a/server/src/migrations/1694758412194-UpdateOpusCodecToLibopus.ts b/server/src/migrations/1694758412194-UpdateOpusCodecToLibopus.ts deleted file mode 100644 index 715b46550a..0000000000 --- a/server/src/migrations/1694758412194-UpdateOpusCodecToLibopus.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class UpdateOpusCodecToLibopus1694758412194 implements MigrationInterface { - name = 'UpdateOpusCodecToLibopus1694758412194' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_config - SET value = '"libopus"' - WHERE key = 'ffmpeg.targetAudioCodec' AND value = '"opus"' - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_config - SET value = '"opus"' - WHERE key = 'ffmpeg.targetAudioCodec' AND value = '"libopus"' - `); - } -} diff --git a/server/src/migrations/1695354433573-AddStackParentIdToAssets.ts b/server/src/migrations/1695354433573-AddStackParentIdToAssets.ts deleted file mode 100644 index d5150d3a81..0000000000 --- a/server/src/migrations/1695354433573-AddStackParentIdToAssets.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddStackParentIdToAssets1695354433573 implements MigrationInterface { - name = 'AddStackParentIdToAssets1695354433573' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "stackParentId" uuid`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "FK_b463c8edb01364bf2beba08ef19" FOREIGN KEY ("stackParentId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_b463c8edb01364bf2beba08ef19"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "stackParentId"`); - } - -} diff --git a/server/src/migrations/1695660378655-RemoveInvalidCoordinates.ts b/server/src/migrations/1695660378655-RemoveInvalidCoordinates.ts deleted file mode 100644 index 20b179ceb6..0000000000 --- a/server/src/migrations/1695660378655-RemoveInvalidCoordinates.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveInvalidCoordinates1695660378655 implements MigrationInterface { - name = 'RemoveInvalidCoordinates1695660378655'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`UPDATE "exif" SET "latitude" = NULL WHERE "latitude" IN ('NaN', 'Infinity', '-Infinity')`); - await queryRunner.query( - `UPDATE "exif" SET "longitude" = NULL WHERE "longitude" IN ('NaN', 'Infinity', '-Infinity')`, - ); - } - - public async down(): Promise { - // Empty, data cannot be restored - } -} diff --git a/server/src/migrations/1696888644031-AddOriginalPathIndex.ts b/server/src/migrations/1696888644031-AddOriginalPathIndex.ts deleted file mode 100644 index 78e1c92ecb..0000000000 --- a/server/src/migrations/1696888644031-AddOriginalPathIndex.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddOriginalPathIndex1696888644031 implements MigrationInterface { - name = 'AddOriginalPathIndex1696888644031'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX "IDX_originalPath_libraryId" ON "assets" ("originalPath", "libraryId")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_originalPath_libraryId"`); - } -} diff --git a/server/src/migrations/1696968880063-AddMoveTable.ts b/server/src/migrations/1696968880063-AddMoveTable.ts deleted file mode 100644 index 7ba140d05b..0000000000 --- a/server/src/migrations/1696968880063-AddMoveTable.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddMoveTable1696968880063 implements MigrationInterface { - name = 'AddMoveTable1696968880063' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "move_history" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "entityId" character varying NOT NULL, "pathType" character varying NOT NULL, "oldPath" character varying NOT NULL, "newPath" character varying NOT NULL, CONSTRAINT "UQ_newPath" UNIQUE ("newPath"), CONSTRAINT "UQ_entityId_pathType" UNIQUE ("entityId", "pathType"), CONSTRAINT "PK_af608f132233acf123f2949678d" PRIMARY KEY ("id"))`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "move_history"`); - } - -} diff --git a/server/src/migrations/1697272818851-UnassignFace.ts b/server/src/migrations/1697272818851-UnassignFace.ts deleted file mode 100644 index 49eebf4cc1..0000000000 --- a/server/src/migrations/1697272818851-UnassignFace.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UnassignFace1697272818851 implements MigrationInterface { - name = 'UnassignFace1697272818851'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_faces" DROP CONSTRAINT "PK_bf339a24070dac7e71304ec530a"`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD COLUMN "id" UUID DEFAULT uuid_generate_v4() NOT NULL`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD CONSTRAINT "PK_6df76ab2eb6f5b57b7c2f1fc684" PRIMARY KEY ("id")`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP CONSTRAINT "FK_95ad7106dd7b484275443f580f9"`); - await queryRunner.query(`ALTER TABLE "asset_faces" ALTER COLUMN "personId" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD CONSTRAINT "FK_95ad7106dd7b484275443f580f9" FOREIGN KEY ("personId") REFERENCES "person"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_faces" DROP CONSTRAINT "PK_6df76ab2eb6f5b57b7c2f1fc684"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP CONSTRAINT "FK_95ad7106dd7b484275443f580f9"`); - await queryRunner.query(`ALTER TABLE "asset_faces" ALTER COLUMN "personId" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD CONSTRAINT "FK_95ad7106dd7b484275443f580f9" FOREIGN KEY ("personId") REFERENCES "person"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD CONSTRAINT "PK_bf339a24070dac7e71304ec530a" PRIMARY KEY ("assetId", "personId")`); - } -} diff --git a/server/src/migrations/1698290827089-AddPasswordToSharedLinks.ts b/server/src/migrations/1698290827089-AddPasswordToSharedLinks.ts deleted file mode 100644 index b6906e3d05..0000000000 --- a/server/src/migrations/1698290827089-AddPasswordToSharedLinks.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddPasswordToSharedLinks1698290827089 implements MigrationInterface { - name = 'AddPasswordToSharedLinks1698290827089' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" ADD "password" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" DROP COLUMN "password"`); - } - -} diff --git a/server/src/migrations/1698693294632-AddActivity.ts b/server/src/migrations/1698693294632-AddActivity.ts deleted file mode 100644 index 5556ef2b20..0000000000 --- a/server/src/migrations/1698693294632-AddActivity.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddActivity1698693294632 implements MigrationInterface { - name = 'AddActivity1698693294632' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "activity" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "albumId" uuid NOT NULL, "userId" uuid NOT NULL, "assetId" uuid, "comment" text, "isLiked" boolean NOT NULL DEFAULT false, CONSTRAINT "CHK_2ab1e70f113f450eb40c1e3ec8" CHECK (("comment" IS NULL AND "isLiked" = true) OR ("comment" IS NOT NULL AND "isLiked" = false)), CONSTRAINT "PK_24625a1d6b1b089c8ae206fe467" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_activity_like" ON "activity" ("assetId", "userId", "albumId") WHERE ("isLiked" = true)`); - await queryRunner.query(`ALTER TABLE "activity" ADD CONSTRAINT "FK_8091ea76b12338cb4428d33d782" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "activity" ADD CONSTRAINT "FK_3571467bcbe021f66e2bdce96ea" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "activity" ADD CONSTRAINT "FK_1af8519996fbfb3684b58df280b" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "activity" DROP CONSTRAINT "FK_1af8519996fbfb3684b58df280b"`); - await queryRunner.query(`ALTER TABLE "activity" DROP CONSTRAINT "FK_3571467bcbe021f66e2bdce96ea"`); - await queryRunner.query(`ALTER TABLE "activity" DROP CONSTRAINT "FK_8091ea76b12338cb4428d33d782"`); - await queryRunner.query(`DROP INDEX "IDX_activity_like"`); - await queryRunner.query(`DROP TABLE "activity"`); - } - -} diff --git a/server/src/migrations/1699268680508-DisableActivity.ts b/server/src/migrations/1699268680508-DisableActivity.ts deleted file mode 100644 index d860244f6d..0000000000 --- a/server/src/migrations/1699268680508-DisableActivity.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class DisableActivity1699268680508 implements MigrationInterface { - name = 'DisableActivity1699268680508' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" ADD "isActivityEnabled" boolean NOT NULL DEFAULT true`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" DROP COLUMN "isActivityEnabled"`); - } - -} diff --git a/server/src/migrations/1699322864544-UserNameConsolidation.ts b/server/src/migrations/1699322864544-UserNameConsolidation.ts deleted file mode 100644 index 431a2a92fc..0000000000 --- a/server/src/migrations/1699322864544-UserNameConsolidation.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddUsername1699322864544 implements MigrationInterface { - name = 'AddUsername1699322864544' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "name" character varying NOT NULL DEFAULT ''`); - await queryRunner.query(`UPDATE "users" SET "name" = CONCAT(COALESCE("firstName", ''), ' ', COALESCE("lastName", ''))`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "firstName"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "lastName"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "name"`); - await queryRunner.query(`ALTER TABLE "users" ADD "lastName" character varying NOT NULL DEFAULT ''`); - await queryRunner.query(`ALTER TABLE "users" ADD "firstName" character varying NOT NULL DEFAULT ''`); - await queryRunner.query(`UPDATE "users" SET "lastName" = COALESCE("email", '')`); - await queryRunner.query(`UPDATE "users" SET "firstName" = COALESCE("email", '')`); - } - -} diff --git a/server/src/migrations/1699345863886-AddJobStatus.ts b/server/src/migrations/1699345863886-AddJobStatus.ts deleted file mode 100644 index c7df6387c0..0000000000 --- a/server/src/migrations/1699345863886-AddJobStatus.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddJobStatus1699345863886 implements MigrationInterface { - name = 'AddJobStatus1699345863886' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "asset_job_status" ("assetId" uuid NOT NULL, "facesRecognizedAt" TIMESTAMP WITH TIME ZONE, CONSTRAINT "PK_420bec36fc02813bddf5c8b73d4" PRIMARY KEY ("assetId"))`); - await queryRunner.query(`ALTER TABLE "asset_job_status" ADD CONSTRAINT "FK_420bec36fc02813bddf5c8b73d4" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_job_status" DROP CONSTRAINT "FK_420bec36fc02813bddf5c8b73d4"`); - await queryRunner.query(`DROP TABLE "asset_job_status"`); - } - -} diff --git a/server/src/migrations/1699562570201-AdddInTimelineToPartnersTable.ts b/server/src/migrations/1699562570201-AdddInTimelineToPartnersTable.ts deleted file mode 100644 index 59c2f229eb..0000000000 --- a/server/src/migrations/1699562570201-AdddInTimelineToPartnersTable.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AdddInTimelineToPartnersTable1699562570201 implements MigrationInterface { - name = 'AdddInTimelineToPartnersTable1699562570201' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "partners" ADD "inTimeline" boolean NOT NULL DEFAULT false`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "partners" DROP COLUMN "inTimeline"`); - } - -} diff --git a/server/src/migrations/1699727044012-EditFaceAssetForeignKey.ts b/server/src/migrations/1699727044012-EditFaceAssetForeignKey.ts deleted file mode 100644 index fdff7ea062..0000000000 --- a/server/src/migrations/1699727044012-EditFaceAssetForeignKey.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class EditFaceAssetForeignKey1699727044012 implements MigrationInterface { - name = 'EditFaceAssetForeignKey1699727044012' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" DROP CONSTRAINT "FK_2bbabe31656b6778c6b87b61023"`); - await queryRunner.query(`UPDATE person SET "faceAssetId" = asset_faces."id" FROM asset_faces WHERE person."faceAssetId" = asset_faces."assetId" AND person."id" = asset_faces."personId"`) - await queryRunner.query(`ALTER TABLE "person" ADD CONSTRAINT "FK_2bbabe31656b6778c6b87b61023" FOREIGN KEY ("faceAssetId") REFERENCES "asset_faces"("id") ON DELETE SET NULL ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" DROP CONSTRAINT "FK_2bbabe31656b6778c6b87b61023"`); - await queryRunner.query(`UPDATE person SET "faceAssetId" = assets."id" FROM assets, asset_faces WHERE person."faceAssetId" = asset_faces."id" AND asset_faces."assetId" = assets."id"`); - await queryRunner.query(`ALTER TABLE "person" ADD CONSTRAINT "FK_2bbabe31656b6778c6b87b61023" FOREIGN KEY ("faceAssetId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE NO ACTION`); - } - -} diff --git a/server/src/migrations/1699889987493-AddAvatarColor.ts b/server/src/migrations/1699889987493-AddAvatarColor.ts deleted file mode 100644 index b075a5d2af..0000000000 --- a/server/src/migrations/1699889987493-AddAvatarColor.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAvatarColor1699889987493 implements MigrationInterface { - name = 'AddAvatarColor1699889987493' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "avatarColor" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "avatarColor"`); - } - -} diff --git a/server/src/migrations/1700345818045-SystemMetadata.ts b/server/src/migrations/1700345818045-SystemMetadata.ts deleted file mode 100644 index 0bd9162db7..0000000000 --- a/server/src/migrations/1700345818045-SystemMetadata.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class SystemMetadata1700345818045 implements MigrationInterface { - name = 'SystemMetadata1700345818045' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "system_metadata" ("key" character varying NOT NULL, "value" jsonb NOT NULL DEFAULT '{}', CONSTRAINT "PK_fa94f6857470fb5b81ec6084465" PRIMARY KEY ("key"))`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "system_metadata"`); - } - -} diff --git a/server/src/migrations/1700362016675-Geodata.ts b/server/src/migrations/1700362016675-Geodata.ts deleted file mode 100644 index fa948e0896..0000000000 --- a/server/src/migrations/1700362016675-Geodata.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class Geodata1700362016675 implements MigrationInterface { - name = 'Geodata1700362016675' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS cube`) - await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS earthdistance`) - await queryRunner.query(`CREATE TABLE "geodata_admin2" ("key" character varying NOT NULL, "name" character varying NOT NULL, CONSTRAINT "PK_1e3886455dbb684d6f6b4756726" PRIMARY KEY ("key"))`); - await queryRunner.query(`CREATE TABLE "geodata_admin1" ("key" character varying NOT NULL, "name" character varying NOT NULL, CONSTRAINT "PK_3fe3a89c5aac789d365871cb172" PRIMARY KEY ("key"))`); - await queryRunner.query(`CREATE TABLE "geodata_places" ("id" integer NOT NULL, "name" character varying(200) NOT NULL, "longitude" double precision NOT NULL, "latitude" double precision NOT NULL, "countryCode" character(2) NOT NULL, "admin1Code" character varying(20), "admin2Code" character varying(80), "admin1Key" character varying GENERATED ALWAYS AS ("countryCode" || '.' || "admin1Code") STORED, "admin2Key" character varying GENERATED ALWAYS AS ("countryCode" || '.' || "admin1Code" || '.' || "admin2Code") STORED, "modificationDate" date NOT NULL, CONSTRAINT "PK_c29918988912ef4036f3d7fbff4" PRIMARY KEY ("id"))`); - await queryRunner.query(`ALTER TABLE "geodata_places" ADD "earthCoord" earth GENERATED ALWAYS AS (ll_to_earth(latitude, longitude)) STORED`) - await queryRunner.query(`CREATE INDEX "IDX_geodata_gist_earthcoord" ON "geodata_places" USING gist ("earthCoord");`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_geodata_gist_earthcoord"`); - await queryRunner.query(`DROP TABLE "geodata_places"`); - await queryRunner.query(`DROP TABLE "geodata_admin1"`); - await queryRunner.query(`DROP TABLE "geodata_admin2"`); - await queryRunner.query(`DROP EXTENSION cube`); - await queryRunner.query(`DROP EXTENSION earthdistance`); - } - -} diff --git a/server/src/migrations/1700713871511-UsePgVectors.ts b/server/src/migrations/1700713871511-UsePgVectors.ts deleted file mode 100644 index 4511e1001b..0000000000 --- a/server/src/migrations/1700713871511-UsePgVectors.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { getVectorExtension } from 'src/repositories/database.repository'; -import { getCLIPModelInfo } from 'src/utils/misc'; -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UsePgVectors1700713871511 implements MigrationInterface { - name = 'UsePgVectors1700713871511'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`SET search_path TO "$user", public, vectors`); - await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS ${await getVectorExtension(queryRunner)}`); - const faceDimQuery = await queryRunner.query(` - SELECT CARDINALITY(embedding::real[]) as dimsize - FROM asset_faces - LIMIT 1`); - const faceDimSize = faceDimQuery?.[0]?.['dimsize'] ?? 512; - - const clipModelNameQuery = await queryRunner.query( - `SELECT value FROM system_config WHERE key = 'machineLearning.clip.modelName'`, - ); - const clipModelName: string = clipModelNameQuery?.[0]?.['value'] ?? 'ViT-B-32__openai'; - const clipDimSize = getCLIPModelInfo(clipModelName.replaceAll('"', '')).dimSize; - - await queryRunner.query(` - ALTER TABLE asset_faces - ALTER COLUMN embedding SET NOT NULL, - ALTER COLUMN embedding TYPE vector(${faceDimSize})`); - - await queryRunner.query(` - CREATE TABLE smart_search ( - "assetId" uuid PRIMARY KEY NOT NULL REFERENCES assets(id) ON DELETE CASCADE, - embedding vector(${clipDimSize}) NOT NULL )`); - - await queryRunner.query(` - INSERT INTO smart_search("assetId", embedding) - SELECT si."assetId", si."clipEmbedding" - FROM smart_info si - WHERE "clipEmbedding" IS NOT NULL - AND CARDINALITY("clipEmbedding"::real[]) = ${clipDimSize} - AND array_position(si."clipEmbedding", NULL) IS NULL`); - - await queryRunner.query(`ALTER TABLE smart_info DROP COLUMN IF EXISTS "clipEmbedding"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE asset_faces ALTER COLUMN embedding TYPE real array`); - await queryRunner.query(`ALTER TABLE smart_info ADD COLUMN IF NOT EXISTS "clipEmbedding" TYPE real array`); - await queryRunner.query(` - INSERT INTO smart_info - ("assetId", "clipEmbedding") - SELECT s."assetId", s.embedding - FROM smart_search s - ON CONFLICT (s."assetId") DO UPDATE SET "clipEmbedding" = s.embedding`); - await queryRunner.query(`DROP TABLE IF EXISTS smart_search`); - } -} diff --git a/server/src/migrations/1700713994428-AddCLIPEmbeddingIndex.ts b/server/src/migrations/1700713994428-AddCLIPEmbeddingIndex.ts deleted file mode 100644 index 43809d6364..0000000000 --- a/server/src/migrations/1700713994428-AddCLIPEmbeddingIndex.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { getVectorExtension } from 'src/repositories/database.repository'; -import { vectorIndexQuery } from 'src/utils/database'; -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddCLIPEmbeddingIndex1700713994428 implements MigrationInterface { - name = 'AddCLIPEmbeddingIndex1700713994428'; - - public async up(queryRunner: QueryRunner): Promise { - const vectorExtension = await getVectorExtension(queryRunner); - await queryRunner.query(`SET search_path TO "$user", public, vectors`); - - await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'smart_search', indexName: 'clip_index' })); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX IF EXISTS clip_index`); - } -} diff --git a/server/src/migrations/1700714033632-AddFaceEmbeddingIndex.ts b/server/src/migrations/1700714033632-AddFaceEmbeddingIndex.ts deleted file mode 100644 index 5ee91afbcc..0000000000 --- a/server/src/migrations/1700714033632-AddFaceEmbeddingIndex.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { getVectorExtension } from 'src/repositories/database.repository'; -import { vectorIndexQuery } from 'src/utils/database'; -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddFaceEmbeddingIndex1700714033632 implements MigrationInterface { - name = 'AddFaceEmbeddingIndex1700714033632'; - - public async up(queryRunner: QueryRunner): Promise { - const vectorExtension = await getVectorExtension(queryRunner); - await queryRunner.query(`SET search_path TO "$user", public, vectors`); - - await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'asset_faces', indexName: 'face_index' })); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX IF EXISTS face_index`); - } -} diff --git a/server/src/migrations/1700714072055-AddSmartInfoTagsIndex.ts b/server/src/migrations/1700714072055-AddSmartInfoTagsIndex.ts deleted file mode 100644 index b850d3da09..0000000000 --- a/server/src/migrations/1700714072055-AddSmartInfoTagsIndex.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddSmartInfoTagsIndex1700714072055 implements MigrationInterface { - name = 'AddSmartInfoTagsIndex1700714072055'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX IF NOT EXISTS si_tags ON smart_info USING GIN (tags);`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX IF EXISTS si_tags;`); - } -} diff --git a/server/src/migrations/1700714140297-CreateSmartInfoTextSearchIndex.ts b/server/src/migrations/1700714140297-CreateSmartInfoTextSearchIndex.ts deleted file mode 100644 index b42291f6fd..0000000000 --- a/server/src/migrations/1700714140297-CreateSmartInfoTextSearchIndex.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateSmartInfoTextSearchIndex1700714140297 implements MigrationInterface { - name = 'CreateSmartInfoTextSearchIndex1700714140297'; - - public async up(queryRunner: QueryRunner): Promise { - // https://dba.stackexchange.com/a/164081 - await queryRunner.query(` - CREATE OR REPLACE FUNCTION f_concat_ws(text, text[]) - RETURNS text - LANGUAGE sql IMMUTABLE PARALLEL SAFE AS - 'SELECT array_to_string($2, $1)'`); - - await queryRunner.query(` - ALTER TABLE smart_info ADD "smartInfoTextSearchableColumn" tsvector - GENERATED ALWAYS AS ( - TO_TSVECTOR( - 'english', - f_concat_ws( - ' '::text, - COALESCE(tags, array[]::text[]) || COALESCE(objects, array[]::text[]) - ) - ) - ) - STORED NOT NULL`); - - await queryRunner.query(` - CREATE INDEX smart_info_text_searchable_idx - ON smart_info - USING GIN ("smartInfoTextSearchableColumn")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP FUNCTION IF EXISTS immutable_concat_ws`); - await queryRunner.query(`ALTER TABLE smart_info DROP IF EXISTS "smartInfoTextSearchableColumn"`); - } -} diff --git a/server/src/migrations/1700752078178-AddAssetFaceIndicies.ts b/server/src/migrations/1700752078178-AddAssetFaceIndicies.ts deleted file mode 100644 index 38dd915139..0000000000 --- a/server/src/migrations/1700752078178-AddAssetFaceIndicies.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAssetFaceIndicies1700752078178 implements MigrationInterface { - name = 'AddAssetFaceIndicies1700752078178' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX "IDX_bf339a24070dac7e71304ec530" ON "asset_faces" ("personId", "assetId") `); - await queryRunner.query(`CREATE INDEX "IDX_b463c8edb01364bf2beba08ef1" ON "assets" ("stackParentId") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_b463c8edb01364bf2beba08ef1"`); - await queryRunner.query(`DROP INDEX "IDX_bf339a24070dac7e71304ec530"`); - } - -} diff --git a/server/src/migrations/1701665867595-AddExifCityIndex.ts b/server/src/migrations/1701665867595-AddExifCityIndex.ts deleted file mode 100644 index 0899ea1e6b..0000000000 --- a/server/src/migrations/1701665867595-AddExifCityIndex.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddExifCityIndex1701665867595 implements MigrationInterface { - name = 'AddExifCityIndex1701665867595' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX "exif_city" ON "exif" ("city") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "exif_city"`); - } - -} diff --git a/server/src/migrations/1702084989965-AddWebSocketAttachmentTable.ts b/server/src/migrations/1702084989965-AddWebSocketAttachmentTable.ts deleted file mode 100644 index c2fc0b222f..0000000000 --- a/server/src/migrations/1702084989965-AddWebSocketAttachmentTable.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddWebSocketAttachmentTable1702084989965 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - 'CREATE TABLE IF NOT EXISTS "socket_io_attachments" (id bigserial UNIQUE, created_at timestamptz DEFAULT NOW(), payload bytea);', - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "socket_io_attachments"`); - } -} diff --git a/server/src/migrations/1702257380990-DropNullIslandLatLong.ts b/server/src/migrations/1702257380990-DropNullIslandLatLong.ts deleted file mode 100644 index 173b2c5950..0000000000 --- a/server/src/migrations/1702257380990-DropNullIslandLatLong.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class DropNullIslandLatLong1702257380990 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - 'UPDATE "exif" SET latitude = NULL, longitude = NULL WHERE latitude = 0 AND longitude = 0;', - ); - } - - public async down(): Promise { - // There's no way to know which assets used to have 0/0 lat-long if we've - // already run this migration. - } -} diff --git a/server/src/migrations/1702938928766-NullifyFutureBirthDatesAndAddCheckConstraint.ts b/server/src/migrations/1702938928766-NullifyFutureBirthDatesAndAddCheckConstraint.ts deleted file mode 100644 index c646287c81..0000000000 --- a/server/src/migrations/1702938928766-NullifyFutureBirthDatesAndAddCheckConstraint.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class NullifyFutureBirthDatesAndAddCheckConstraint1702938928766 implements MigrationInterface { - name = 'NullifyFutureBirthDatesAndAddCheckConstraint1702938928766' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`UPDATE "person" SET "birthDate" = NULL WHERE "birthDate" > CURRENT_DATE;`); - await queryRunner.query(`ALTER TABLE "person" ADD CONSTRAINT "CHK_b0f82b0ed662bfc24fbb58bb45" CHECK ("birthDate" <= CURRENT_DATE)`); - } - - public async down(queryRunner: QueryRunner): Promise { - // The down method cannot revert the nullified dates - await queryRunner.query(`ALTER TABLE "person" DROP CONSTRAINT "CHK_b0f82b0ed662bfc24fbb58bb45"`); - } - -} diff --git a/server/src/migrations/1702942303661-FixRemovedAssetsSharedLink.ts b/server/src/migrations/1702942303661-FixRemovedAssetsSharedLink.ts deleted file mode 100644 index a55b12fa74..0000000000 --- a/server/src/migrations/1702942303661-FixRemovedAssetsSharedLink.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class FixRemovedAssetsSharedLink1702942303661 implements MigrationInterface { - name = 'FixRemovedAssetsSharedLink1702942303661' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_link__asset" DROP CONSTRAINT "FK_c9fab4aa97ffd1b034f3d6581ab"`); - await queryRunner.query(`ALTER TABLE "shared_link__asset" ADD CONSTRAINT "FK_c9fab4aa97ffd1b034f3d6581ab" FOREIGN KEY ("sharedLinksId") REFERENCES "shared_links"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_link__asset" DROP CONSTRAINT "FK_c9fab4aa97ffd1b034f3d6581ab"`); - await queryRunner.query(`ALTER TABLE "shared_link__asset" ADD CONSTRAINT "FK_c9fab4aa97ffd1b034f3d6581ab" FOREIGN KEY ("sharedLinksId") REFERENCES "shared_links"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - -} diff --git a/server/src/migrations/1703035138085-AddAutoStackId.ts b/server/src/migrations/1703035138085-AddAutoStackId.ts deleted file mode 100644 index d8c83ac565..0000000000 --- a/server/src/migrations/1703035138085-AddAutoStackId.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAutoStackId1703035138085 implements MigrationInterface { - name = 'AddAutoStackId1703035138085' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "autoStackId" character varying`); - await queryRunner.query(`CREATE INDEX "IDX_auto_stack_id" ON "exif" ("autoStackId") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_auto_stack_id"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "autoStackId"`); - } - -} diff --git a/server/src/migrations/1703288449127-DefaultStorageTemplateOnForExistingInstallations.ts b/server/src/migrations/1703288449127-DefaultStorageTemplateOnForExistingInstallations.ts deleted file mode 100644 index 4ea88db8eb..0000000000 --- a/server/src/migrations/1703288449127-DefaultStorageTemplateOnForExistingInstallations.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class DefaultStorageTemplateOnForExistingInstallations1703288449127 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - const adminCount = await queryRunner.query(`SELECT COUNT(*) FROM users WHERE "isAdmin" = true`) - if(adminCount[0].count > 0) { - await queryRunner.query(`INSERT INTO system_config (key, value) VALUES ('storageTemplate.enabled', 'true')`) - } - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DELETE FROM system_config WHERE key = 'storageTemplate.enabled'`) - } - -} diff --git a/server/src/migrations/1704382918223-AddQuotaColumnsToUser.ts b/server/src/migrations/1704382918223-AddQuotaColumnsToUser.ts deleted file mode 100644 index 5d6477b8fb..0000000000 --- a/server/src/migrations/1704382918223-AddQuotaColumnsToUser.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddQuotaColumnsToUser1704382918223 implements MigrationInterface { - name = 'AddQuotaColumnsToUser1704382918223' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "quotaSizeInBytes" bigint`); - await queryRunner.query(`ALTER TABLE "users" ADD "quotaUsageInBytes" bigint NOT NULL DEFAULT '0'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "quotaUsageInBytes"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "quotaSizeInBytes"`); - } - -} diff --git a/server/src/migrations/1704571051932-DefaultOnboardingForExistingInstallations.ts b/server/src/migrations/1704571051932-DefaultOnboardingForExistingInstallations.ts deleted file mode 100644 index cd737b2a62..0000000000 --- a/server/src/migrations/1704571051932-DefaultOnboardingForExistingInstallations.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class DefaultOnboardingForExistingInstallations1704571051932 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - const adminCount = await queryRunner.query(`SELECT COUNT(*) FROM users WHERE "isAdmin" = true`); - if (adminCount[0].count > 0) { - await queryRunner.query(`INSERT INTO system_metadata (key, value) VALUES ($1, $2)`, [ - 'admin-onboarding', - String.raw`"{\"isOnboarded\":true}"`, - ]); - } - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DELETE FROM system_metadata WHERE key = 'admin-onboarding'`); - } -} diff --git a/server/src/migrations/1704943345360-SetAssetFaceNullOnPersonDelete.ts b/server/src/migrations/1704943345360-SetAssetFaceNullOnPersonDelete.ts deleted file mode 100644 index 7b03ee9afb..0000000000 --- a/server/src/migrations/1704943345360-SetAssetFaceNullOnPersonDelete.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class SetAssetFaceNullOnPersonDelete1704943345360 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE "asset_faces" - DROP CONSTRAINT "FK_95ad7106dd7b484275443f580f9", - ADD CONSTRAINT "FK_95ad7106dd7b484275443f580f9" - FOREIGN KEY ("personId") REFERENCES "person"("id") - ON DELETE SET NULL ON UPDATE CASCADE - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE "asset_faces" - DROP CONSTRAINT "FK_95ad7106dd7b484275443f580f9", - ADD CONSTRAINT "FK_95ad7106dd7b484275443f580f9" - FOREIGN KEY ("personId") REFERENCES "person"("id") - ON DELETE CASCADE ON UPDATE CASCADE - `); - } - -} diff --git a/server/src/migrations/1705094221536-AddMetadataExtractedAt.ts b/server/src/migrations/1705094221536-AddMetadataExtractedAt.ts deleted file mode 100644 index e76d095c10..0000000000 --- a/server/src/migrations/1705094221536-AddMetadataExtractedAt.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddMetadataExtractedAt1705094221536 implements MigrationInterface { - name = 'AddMetadataExtractedAt1705094221536'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_job_status" ADD "metadataExtractedAt" TIMESTAMP WITH TIME ZONE`); - await queryRunner.query(` - UPDATE "asset_job_status" - SET "metadataExtractedAt" = NOW() - FROM "exif" - WHERE "exif"."assetId" = "asset_job_status"."assetId"; -`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_job_status" DROP COLUMN "metadataExtractedAt"`); - } -} diff --git a/server/src/migrations/1705306747072-AddOriginalFileNameIndex.ts b/server/src/migrations/1705306747072-AddOriginalFileNameIndex.ts deleted file mode 100644 index c62c01f50c..0000000000 --- a/server/src/migrations/1705306747072-AddOriginalFileNameIndex.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddOriginalFileNameIndex1705306747072 implements MigrationInterface { - name = 'AddOriginalFileNameIndex1705306747072'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX "IDX_4d66e76dada1ca180f67a205dc" ON "assets" ("originalFileName") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_4d66e76dada1ca180f67a205dc"`); - } -} diff --git a/server/src/migrations/1705363967169-CreateAssetStackTable.ts b/server/src/migrations/1705363967169-CreateAssetStackTable.ts deleted file mode 100644 index d1591797ff..0000000000 --- a/server/src/migrations/1705363967169-CreateAssetStackTable.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateAssetStackTable1705197515600 implements MigrationInterface { - name = 'CreateAssetStackTable1705197515600'; - - public async up(queryRunner: QueryRunner): Promise { - // create table - await queryRunner.query( - `CREATE TABLE "asset_stack" ( - "id" uuid NOT NULL DEFAULT uuid_generate_v4(), - "primaryAssetId" uuid NOT NULL, - CONSTRAINT "REL_91704e101438fd0653f582426d" UNIQUE ("primaryAssetId"), - CONSTRAINT "PK_74a27e7fcbd5852463d0af3034b" PRIMARY KEY ("id"))`, - ); - - // create stacks - await queryRunner.query( - `INSERT INTO "asset_stack" ("primaryAssetId") - SELECT DISTINCT("stackParentId" ) - FROM "assets" - WHERE "stackParentId" IS NOT NULL;`, - ); - - // add "stackId" - await queryRunner.query(`ALTER TABLE "assets" ADD COLUMN "stackId" uuid`); - - // set "stackId" for parents - await queryRunner.query( - `UPDATE "assets" - SET "stackId" = "asset_stack"."id" - FROM "asset_stack" - WHERE "assets"."id" = "asset_stack"."primaryAssetId"`, - ); - - // set "stackId" for children - await queryRunner.query( - `UPDATE "assets" - SET "stackId" = "asset_stack"."id" - FROM "asset_stack" - WHERE "assets"."stackParentId" = "asset_stack"."primaryAssetId"`, - ); - - // update constraints - await queryRunner.query(`DROP INDEX "IDX_b463c8edb01364bf2beba08ef1"`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_b463c8edb01364bf2beba08ef19"`); - await queryRunner.query( - `ALTER TABLE "assets" ADD CONSTRAINT "FK_f15d48fa3ea5e4bda05ca8ab207" FOREIGN KEY ("stackId") REFERENCES "asset_stack"("id") ON DELETE SET NULL ON UPDATE CASCADE`, - ); - await queryRunner.query( - `ALTER TABLE "asset_stack" ADD CONSTRAINT "FK_91704e101438fd0653f582426dc" FOREIGN KEY ("primaryAssetId") REFERENCES "assets"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - - // drop "stackParentId" - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "stackParentId"`); - } - - public async down(queryRunner: QueryRunner): Promise { - // add "stackParentId" - await queryRunner.query(`ALTER TABLE "assets" ADD COLUMN "stackParentId" uuid`); - - // set "stackParentId" for parents - await queryRunner.query( - `UPDATE "assets" - SET "stackParentId" = "asset_stack"."primaryAssetId" - FROM "asset_stack" - WHERE "assets"."stackId" = "asset_stack"."id" and "assets"."id" != "asset_stack"."primaryAssetId"`, - ); - - // update constraints - await queryRunner.query( - `ALTER TABLE "assets" ADD CONSTRAINT "FK_b463c8edb01364bf2beba08ef19" FOREIGN KEY ("stackParentId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE CASCADE`, - ); - await queryRunner.query(`CREATE INDEX "IDX_b463c8edb01364bf2beba08ef1" ON "assets" ("stackParentId") `); - await queryRunner.query(`ALTER TABLE "asset_stack" DROP CONSTRAINT "FK_91704e101438fd0653f582426dc"`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_f15d48fa3ea5e4bda05ca8ab207"`); - - // drop table - await queryRunner.query(`DROP TABLE "asset_stack"`); - - // drop "stackId" - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "stackId"`); - } -} diff --git a/server/src/migrations/1707000751533-AddVectorsToSearchPath.ts b/server/src/migrations/1707000751533-AddVectorsToSearchPath.ts deleted file mode 100644 index 11c84cf970..0000000000 --- a/server/src/migrations/1707000751533-AddVectorsToSearchPath.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddVectorsToSearchPath1707000751533 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - const res = await queryRunner.query(`SELECT current_database() as db`); - const databaseName = res[0]['db']; - await queryRunner.query(`ALTER DATABASE "${databaseName}" SET search_path TO "$user", public, vectors`); - } - - public async down(queryRunner: QueryRunner): Promise { - const databaseName = await queryRunner.query(`SELECT current_database()`); - await queryRunner.query(`ALTER DATABASE "${databaseName}" SET search_path TO "$user", public`); - } -} diff --git a/server/src/migrations/1708059341865-GeodataLocationSearch.ts b/server/src/migrations/1708059341865-GeodataLocationSearch.ts deleted file mode 100644 index af2d5dc5f3..0000000000 --- a/server/src/migrations/1708059341865-GeodataLocationSearch.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class GeodataLocationSearch1708059341865 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS pg_trgm`); - await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS unaccent`); - - // https://stackoverflow.com/a/11007216 - await queryRunner.query(` - CREATE OR REPLACE FUNCTION f_unaccent(text) - RETURNS text - LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT - RETURN unaccent('unaccent', $1)`); - - await queryRunner.query(`ALTER TABLE geodata_places ADD COLUMN "admin1Name" varchar`); - await queryRunner.query(`ALTER TABLE geodata_places ADD COLUMN "admin2Name" varchar`); - - await queryRunner.query(` - UPDATE geodata_places - SET "admin1Name" = admin1.name - FROM geodata_admin1 admin1 - WHERE admin1.key = "admin1Key"`); - - await queryRunner.query(` - UPDATE geodata_places - SET "admin2Name" = admin2.name - FROM geodata_admin2 admin2 - WHERE admin2.key = "admin2Key"`); - - await queryRunner.query(`DROP TABLE geodata_admin1 CASCADE`); - await queryRunner.query(`DROP TABLE geodata_admin2 CASCADE`); - - await queryRunner.query(` - ALTER TABLE geodata_places - DROP COLUMN "admin1Key", - DROP COLUMN "admin2Key"`); - - await queryRunner.query(` - CREATE INDEX idx_geodata_places_name - ON geodata_places - USING gin (f_unaccent(name) gin_trgm_ops)`); - - await queryRunner.query(` - CREATE INDEX idx_geodata_places_admin1_name - ON geodata_places - USING gin (f_unaccent("admin1Name") gin_trgm_ops)`); - - await queryRunner.query(` - CREATE INDEX idx_geodata_places_admin2_name - ON geodata_places - USING gin (f_unaccent("admin2Name") gin_trgm_ops)`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - CREATE TABLE "geodata_admin1" ( - "key" character varying NOT NULL, - "name" character varying NOT NULL, - CONSTRAINT "PK_3fe3a89c5aac789d365871cb172" PRIMARY KEY ("key") - )`); - - await queryRunner.query(` - CREATE TABLE "geodata_admin2" ( - "key" character varying NOT NULL, - "name" character varying NOT NULL, - CONSTRAINT "PK_1e3886455dbb684d6f6b4756726" PRIMARY KEY ("key") - )`); - - await queryRunner.query(` - ALTER TABLE geodata_places - ADD COLUMN "admin1Key" character varying - GENERATED ALWAYS AS ("countryCode" || '.' || "admin1Code") STORED, - ADD COLUMN "admin2Key" character varying - GENERATED ALWAYS AS ("countryCode" || '.' || "admin1Code" || '.' || "admin2Code") STORED`); - - await queryRunner.query( - ` - INSERT INTO "geodata_admin1" - SELECT DISTINCT - "admin1Key" AS "key", - "admin1Name" AS "name" - FROM geodata_places - WHERE "admin1Name" IS NOT NULL`, - ); - - await queryRunner.query( - ` - INSERT INTO "geodata_admin2" - SELECT DISTINCT - "admin2Key" AS "key", - "admin2Name" AS "name" - FROM geodata_places - WHERE "admin2Name" IS NOT NULL`, - ); - - await queryRunner.query(` - UPDATE geodata_places - SET "admin1Name" = admin1.name - FROM geodata_admin1 admin1 - WHERE admin1.key = "admin1Key"`); - - await queryRunner.query(` - UPDATE geodata_places - SET "admin2Name" = admin2.name - FROM geodata_admin2 admin2 - WHERE admin2.key = "admin2Key";`); - } -} diff --git a/server/src/migrations/1708116312820-GeonamesEnhancement.ts b/server/src/migrations/1708116312820-GeonamesEnhancement.ts deleted file mode 100644 index 0cea9a0411..0000000000 --- a/server/src/migrations/1708116312820-GeonamesEnhancement.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class GeonamesEnhancement1708116312820 implements MigrationInterface { - name = 'GeonamesEnhancement1708116312820' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE geodata_places ADD COLUMN "alternateNames" varchar`); - await queryRunner.query(` - CREATE INDEX idx_geodata_places_admin2_alternate_names - ON geodata_places - USING gin (f_unaccent("alternateNames") gin_trgm_ops)`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE geodata_places DROP COLUMN "alternateNames"`); - } - -} diff --git a/server/src/migrations/1708227417898-AddFileCreatedAtIndex.ts b/server/src/migrations/1708227417898-AddFileCreatedAtIndex.ts deleted file mode 100644 index f7ca40cd46..0000000000 --- a/server/src/migrations/1708227417898-AddFileCreatedAtIndex.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddFileCreatedAtIndex1708227417898 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX idx_asset_file_created_at ON assets ("fileCreatedAt")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX idx_asset_file_created_at`); - } -} diff --git a/server/src/migrations/1708425975121-RemoveExternalPath.ts b/server/src/migrations/1708425975121-RemoveExternalPath.ts deleted file mode 100644 index 6c43e351f9..0000000000 --- a/server/src/migrations/1708425975121-RemoveExternalPath.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveExternalPath1708425975121 implements MigrationInterface { - name = 'RemoveExternalPath1708425975121'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "externalPath"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "externalPath" character varying`); - } -} diff --git a/server/src/migrations/1709150004123-RemoveLibraryWatchPollingOption.ts b/server/src/migrations/1709150004123-RemoveLibraryWatchPollingOption.ts deleted file mode 100644 index 8b7ff3a675..0000000000 --- a/server/src/migrations/1709150004123-RemoveLibraryWatchPollingOption.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveLibraryWatchPollingOption1709150004123 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DELETE FROM "system_config" WHERE key = 'library.watch.usePolling'`); - await queryRunner.query(`DELETE FROM "system_config" WHERE key = 'library.watch.interval'`); - } - - public async down(): Promise { - // noop - } -} diff --git a/server/src/migrations/1709608140355-AddAssetOriginalPathTrigramIndex.ts b/server/src/migrations/1709608140355-AddAssetOriginalPathTrigramIndex.ts deleted file mode 100644 index 1d4f13410a..0000000000 --- a/server/src/migrations/1709608140355-AddAssetOriginalPathTrigramIndex.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddAssetOriginalPathTrigramIndex1709608140355 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - CREATE INDEX idx_originalFileName_trigram - ON assets - USING gin (f_unaccent("originalFileName") gin_trgm_ops)`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "idx_originalFileName_trigram"`); - } -} diff --git a/server/src/migrations/1709763765506-AddExtensionToOriginalFileName.ts b/server/src/migrations/1709763765506-AddExtensionToOriginalFileName.ts deleted file mode 100644 index d1f73b4e3b..0000000000 --- a/server/src/migrations/1709763765506-AddExtensionToOriginalFileName.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddExtensionToOriginalFileName1709763765506 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - WITH extension AS (WITH cte AS (SELECT a.id, STRING_TO_ARRAY(a."originalPath", '.')::TEXT[] AS arr - FROM assets a) - SELECT cte.id, cte.arr[ARRAY_UPPER(cte.arr, 1)] AS "ext" - FROM cte) - UPDATE assets - SET "originalFileName" = assets."originalFileName" || '.' || extension."ext" - FROM extension - WHERE assets.id = extension.id; - `); - } - - public async down(): Promise { - // noop - } -} diff --git a/server/src/migrations/1709825430031-CascadeSharedLinksDelete.ts b/server/src/migrations/1709825430031-CascadeSharedLinksDelete.ts deleted file mode 100644 index 9689741929..0000000000 --- a/server/src/migrations/1709825430031-CascadeSharedLinksDelete.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class CascadeSharedLinksDelete1709825430031 implements MigrationInterface { - name = 'CascadeSharedLinksDelete1709825430031' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" DROP CONSTRAINT "FK_66fe3837414c5a9f1c33ca49340"`); - await queryRunner.query(`ALTER TABLE "shared_links" ADD CONSTRAINT "FK_66fe3837414c5a9f1c33ca49340" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" DROP CONSTRAINT "FK_66fe3837414c5a9f1c33ca49340"`); - await queryRunner.query(`ALTER TABLE "shared_links" ADD CONSTRAINT "FK_66fe3837414c5a9f1c33ca49340" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - -} diff --git a/server/src/migrations/1709870213078-AddUserStatus.ts b/server/src/migrations/1709870213078-AddUserStatus.ts deleted file mode 100644 index 858f51258f..0000000000 --- a/server/src/migrations/1709870213078-AddUserStatus.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddUserStatus1709870213078 implements MigrationInterface { - name = 'AddUserStatus1709870213078' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "status" character varying NOT NULL DEFAULT 'active'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "status"`); - } - -} diff --git a/server/src/migrations/1710182081326-AscendingOrderAlbum.ts b/server/src/migrations/1710182081326-AscendingOrderAlbum.ts deleted file mode 100644 index b672ff2b20..0000000000 --- a/server/src/migrations/1710182081326-AscendingOrderAlbum.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AscendingOrderAlbum1710182081326 implements MigrationInterface { - name = 'AscendingOrderAlbum1710182081326' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" ADD "order" character varying NOT NULL DEFAULT 'desc'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" DROP COLUMN "order"`); - } - -} diff --git a/server/src/migrations/1710293990203-AddAssetRelationIndices.ts b/server/src/migrations/1710293990203-AddAssetRelationIndices.ts deleted file mode 100644 index dd0abf7fd5..0000000000 --- a/server/src/migrations/1710293990203-AddAssetRelationIndices.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAssetRelationIndices1710293990203 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX "IDX_asset_id_stackId" on assets ("id", "stackId")`); - await queryRunner.query(`CREATE INDEX "IDX_tag_asset_assetsId_tagsId" on tag_asset ("assetsId", "tagsId")`); - await queryRunner.query(`CREATE INDEX "IDX_asset_faces_assetId_personId" on asset_faces ("assetId", "personId")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_asset_id_stackId" on assets ("id", "stackId")`); - await queryRunner.query(`DROP INDEX "IDX_tag_asset_assetsId_tagsId" on tag_asset ("assetsId", "tagsId")`); - await queryRunner.query(`DROP INDEX "IDX_asset_faces_assetId_personId" on asset_faces ("assetId", "personId")`); - } -} diff --git a/server/src/migrations/1711257900274-RenameWebpJpegPaths.ts b/server/src/migrations/1711257900274-RenameWebpJpegPaths.ts deleted file mode 100644 index ab6f2a4e9f..0000000000 --- a/server/src/migrations/1711257900274-RenameWebpJpegPaths.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RenameWebpJpegPaths1711257900274 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.renameColumn('assets', 'webpPath', 'thumbnailPath'); - await queryRunner.renameColumn('assets', 'resizePath', 'previewPath'); - await queryRunner.query(` - UPDATE system_config - SET key = 'image.previewSize' - WHERE key = 'thumbnail.jpegSize'`); - await queryRunner.query( - `UPDATE system_config - SET key = 'image.thumbnailSize' - WHERE key = 'thumbnail.webpSize'`, - ); - await queryRunner.query( - `UPDATE system_config - SET key = 'image.quality' - WHERE key = 'thumbnail.quality'`, - ); - await queryRunner.query( - `UPDATE system_config - SET key = 'image.colorspace' - WHERE key = 'thumbnail.colorspace'`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.renameColumn('assets', 'thumbnailPath', 'webpPath'); - await queryRunner.renameColumn('assets', 'previewPath', 'resizePath'); - await queryRunner.query(` - UPDATE system_config - SET key = 'thumbnail.jpegSize' - WHERE key = 'image.previewSize'`); - await queryRunner.query( - `UPDATE system_config - SET key = 'thumbnail.webpSize' - WHERE key = 'image.thumbnailSize'`, - ); - await queryRunner.query( - `UPDATE system_config - SET key = 'thumbnail.quality' - WHERE key = 'image.quality'`, - ); - await queryRunner.query( - `UPDATE system_config - SET key = 'thumbnail.colorspace' - WHERE key = 'image.colorspace'`, - ); - } -} diff --git a/server/src/migrations/1711637874206-AddMemoryTable.ts b/server/src/migrations/1711637874206-AddMemoryTable.ts deleted file mode 100644 index b1c5b437d7..0000000000 --- a/server/src/migrations/1711637874206-AddMemoryTable.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddMemoryTable1711637874206 implements MigrationInterface { - name = 'AddMemoryTable1711637874206' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "memories" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "ownerId" uuid NOT NULL, "type" character varying NOT NULL, "data" jsonb NOT NULL, "isSaved" boolean NOT NULL DEFAULT false, "memoryAt" TIMESTAMP WITH TIME ZONE NOT NULL, "seenAt" TIMESTAMP WITH TIME ZONE, CONSTRAINT "PK_aaa0692d9496fe827b0568612f8" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE TABLE "memories_assets_assets" ("memoriesId" uuid NOT NULL, "assetsId" uuid NOT NULL, CONSTRAINT "PK_fcaf7112a013d1703c011c6793d" PRIMARY KEY ("memoriesId", "assetsId"))`); - await queryRunner.query(`CREATE INDEX "IDX_984e5c9ab1f04d34538cd32334" ON "memories_assets_assets" ("memoriesId") `); - await queryRunner.query(`CREATE INDEX "IDX_6942ecf52d75d4273de19d2c16" ON "memories_assets_assets" ("assetsId") `); - await queryRunner.query(`ALTER TABLE "memories" ADD CONSTRAINT "FK_575842846f0c28fa5da46c99b19" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "memories_assets_assets" ADD CONSTRAINT "FK_984e5c9ab1f04d34538cd32334e" FOREIGN KEY ("memoriesId") REFERENCES "memories"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "memories_assets_assets" ADD CONSTRAINT "FK_6942ecf52d75d4273de19d2c16f" FOREIGN KEY ("assetsId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "memories_assets_assets" DROP CONSTRAINT "FK_6942ecf52d75d4273de19d2c16f"`); - await queryRunner.query(`ALTER TABLE "memories_assets_assets" DROP CONSTRAINT "FK_984e5c9ab1f04d34538cd32334e"`); - await queryRunner.query(`ALTER TABLE "memories" DROP CONSTRAINT "FK_575842846f0c28fa5da46c99b19"`); - await queryRunner.query(`DROP INDEX "IDX_6942ecf52d75d4273de19d2c16"`); - await queryRunner.query(`DROP INDEX "IDX_984e5c9ab1f04d34538cd32334"`); - await queryRunner.query(`DROP TABLE "memories_assets_assets"`); - await queryRunner.query(`DROP TABLE "memories"`); - } - -} diff --git a/server/src/migrations/1711989989911-AddAssetDuplicateColumns.ts b/server/src/migrations/1711989989911-AddAssetDuplicateColumns.ts deleted file mode 100644 index d295ec2d7c..0000000000 --- a/server/src/migrations/1711989989911-AddAssetDuplicateColumns.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateAssetDuplicateColumns1711989989911 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE assets ADD COLUMN "duplicateId" uuid`); - await queryRunner.query(`ALTER TABLE asset_job_status ADD COLUMN "duplicatesDetectedAt" timestamptz`); - await queryRunner.query(`CREATE INDEX "IDX_assets_duplicateId" ON assets ("duplicateId")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE assets DROP COLUMN "duplicateId"`); - await queryRunner.query(`ALTER TABLE asset_job_status DROP COLUMN "duplicatesDetectedAt"`); - } -} diff --git a/server/src/migrations/1713337511945-AddAlbumUserRole.ts b/server/src/migrations/1713337511945-AddAlbumUserRole.ts deleted file mode 100644 index a8d0d3d685..0000000000 --- a/server/src/migrations/1713337511945-AddAlbumUserRole.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAlbumUserRole1713337511945 implements MigrationInterface { - name = 'AddAlbumUserRole1713337511945' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ADD "role" character varying NOT NULL DEFAULT 'editor'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP COLUMN "role"`); - } - -} diff --git a/server/src/migrations/1713490844785-RenameSessionsTable.ts b/server/src/migrations/1713490844785-RenameSessionsTable.ts deleted file mode 100644 index b1b35e8ae6..0000000000 --- a/server/src/migrations/1713490844785-RenameSessionsTable.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RenameSessionsTable1713490844785 implements MigrationInterface { - name = 'RenameSessionsTable1713490844785'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_token" RENAME TO "sessions"`); - await queryRunner.query(`ALTER TABLE "sessions" RENAME CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918" to "FK_57de40bc620f456c7311aa3a1e6"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "sessions" RENAME CONSTRAINT "FK_57de40bc620f456c7311aa3a1e6" to "FK_d37db50eecdf9b8ce4eedd2f918"`); - await queryRunner.query(`ALTER TABLE "sessions" RENAME TO "user_token"`); - } -} diff --git a/server/src/migrations/1714698592332-RemoveIsReadOnly.ts b/server/src/migrations/1714698592332-RemoveIsReadOnly.ts deleted file mode 100644 index bc56f8dac7..0000000000 --- a/server/src/migrations/1714698592332-RemoveIsReadOnly.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class RemoveIsReadOnly1714698592332 implements MigrationInterface { - name = 'RemoveIsReadOnly1714698592332' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "isReadOnly"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "isReadOnly" boolean NOT NULL DEFAULT false`); - } - -} diff --git a/server/src/migrations/1715435221124-MotionAssetExtensionMP4.ts b/server/src/migrations/1715435221124-MotionAssetExtensionMP4.ts deleted file mode 100644 index f037ba1fb0..0000000000 --- a/server/src/migrations/1715435221124-MotionAssetExtensionMP4.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class MotionAssetExtensionMP41715435221124 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `UPDATE "assets" SET "originalFileName" = regexp_replace("originalFileName", '\\.[a-zA-Z0-9]+$', '.mp4') WHERE "originalPath" LIKE '%.mp4' AND "isVisible" = false`, - ); - } - - public async down(): Promise {} -} diff --git a/server/src/migrations/1715623169039-RemoveTextSearchColumn.ts b/server/src/migrations/1715623169039-RemoveTextSearchColumn.ts deleted file mode 100644 index 50e99f0b2a..0000000000 --- a/server/src/migrations/1715623169039-RemoveTextSearchColumn.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class RemoveTextSearchColumn1715623169039 implements MigrationInterface { - name = 'RemoveTextSearchColumn1715623169039' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exifTextSearchableColumn"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "exifTextSearchableColumn" tsvector GENERATED ALWAYS AS (TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') || ' ' || - COALESCE("city", '') || ' ' || - COALESCE("state", '') || ' ' || - COALESCE("country", ''))) STORED NOT NULL`); - } - -} diff --git a/server/src/migrations/1715787369686-RemoveSystemConfigTable.ts b/server/src/migrations/1715787369686-RemoveSystemConfigTable.ts deleted file mode 100644 index c16eec7160..0000000000 --- a/server/src/migrations/1715787369686-RemoveSystemConfigTable.ts +++ /dev/null @@ -1,31 +0,0 @@ -import _ from 'lodash'; -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveSystemConfigTable1715787369686 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - const overrides = await queryRunner.query('SELECT "key", "value" FROM "system_config"'); - if (overrides.length === 0) { - return; - } - - const config = {}; - for (const { key, value } of overrides) { - _.set(config, key, JSON.parse(value)); - } - - await queryRunner.query(`INSERT INTO "system_metadata" ("key", "value") VALUES ($1, $2)`, [ - 'system-config', - // yup, we're double-stringifying it - JSON.stringify(JSON.stringify(config)), - ]); - - await queryRunner.query(`DROP TABLE "system_config"`); - } - - public async down(queryRunner: QueryRunner): Promise { - // no data restore, you just get the table back - await queryRunner.query( - `CREATE TABLE "system_config" ("key" character varying NOT NULL, "value" character varying, CONSTRAINT "PK_aab69295b445016f56731f4d535" PRIMARY KEY ("key"))`, - ); - } -} diff --git a/server/src/migrations/1715798702876-RemoveLibraryIsVisible.ts b/server/src/migrations/1715798702876-RemoveLibraryIsVisible.ts deleted file mode 100644 index 45f5248c1a..0000000000 --- a/server/src/migrations/1715798702876-RemoveLibraryIsVisible.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class RemoveLibraryIsVisible1715798702876 implements MigrationInterface { - name = 'RemoveLibraryIsVisible1715798702876' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "libraries" DROP COLUMN "isVisible"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "libraries" ADD "isVisible" boolean NOT NULL DEFAULT true`); - } - -} diff --git a/server/src/migrations/1715804005643-RemoveLibraryType.ts b/server/src/migrations/1715804005643-RemoveLibraryType.ts deleted file mode 100644 index cd4dc574f2..0000000000 --- a/server/src/migrations/1715804005643-RemoveLibraryType.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveLibraryType1715804005643 implements MigrationInterface { - name = 'RemoveLibraryType1715804005643'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_9977c3c1de01c3d848039a6b90c"`); - await queryRunner.query(`DROP INDEX "UQ_assets_owner_library_checksum"`); - await queryRunner.query(`DROP INDEX "IDX_originalPath_libraryId"`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "libraryId" DROP NOT NULL`); - await queryRunner.query(` - UPDATE "assets" - SET "libraryId" = NULL - FROM "libraries" - WHERE "assets"."libraryId" = "libraries"."id" - AND "libraries"."type" = 'UPLOAD' -`); - await queryRunner.query(`DELETE FROM "libraries" WHERE "type" = 'UPLOAD'`); - await queryRunner.query(`ALTER TABLE "libraries" DROP COLUMN "type"`); - await queryRunner.query(`CREATE INDEX "IDX_originalPath_libraryId" ON "assets" ("originalPath", "libraryId")`); - await queryRunner.query(`CREATE UNIQUE INDEX "UQ_assets_owner_checksum" ON "assets" ("ownerId", "checksum") WHERE "libraryId" IS NULL`); - await queryRunner.query(`CREATE UNIQUE INDEX "UQ_assets_owner_library_checksum" ON "assets" ("ownerId", "libraryId", "checksum") WHERE "libraryId" IS NOT NULL`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "FK_9977c3c1de01c3d848039a6b90c" FOREIGN KEY ("libraryId") REFERENCES "libraries"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(): Promise { - // not implemented - } -} diff --git a/server/src/migrations/1715890481637-FixJsonB.ts b/server/src/migrations/1715890481637-FixJsonB.ts deleted file mode 100644 index afb39565a3..0000000000 --- a/server/src/migrations/1715890481637-FixJsonB.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class FixJsonB1715890481637 implements MigrationInterface { - name = 'FixJsonB1715890481637'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "system_metadata" ALTER COLUMN "value" DROP DEFAULT`); - const records = await queryRunner.query('SELECT "key", "value" FROM "system_metadata"'); - for (const { key, value } of records) { - await queryRunner.query(`UPDATE "system_metadata" SET "value" = $1 WHERE "key" = $2`, [value, key]); - } - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "system_metadata" ALTER COLUMN "value" SET DEFAULT '{}'`); - const records = await queryRunner.query('SELECT "key", "value" FROM "system_metadata"'); - for (const { key, value } of records) { - await queryRunner.query(`UPDATE "system_metadata" SET "value" = $1 WHERE "key" = $2`, [ - JSON.stringify(JSON.stringify(value)), - key, - ]); - } - } -} diff --git a/server/src/migrations/1716312279245-UserMetadata.ts b/server/src/migrations/1716312279245-UserMetadata.ts deleted file mode 100644 index b118a8d42a..0000000000 --- a/server/src/migrations/1716312279245-UserMetadata.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UserMetadata1716312279245 implements MigrationInterface { - name = 'UserMetadata1716312279245'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TABLE "user_metadata" ("userId" uuid NOT NULL, "key" character varying NOT NULL, "value" jsonb NOT NULL, CONSTRAINT "PK_5931462150b3438cbc83277fe5a" PRIMARY KEY ("userId", "key"))`, - ); - const users = await queryRunner.query('SELECT "id", "memoriesEnabled", "avatarColor" FROM "users"'); - for (const { id, memoriesEnabled, avatarColor } of users) { - const preferences: any = {}; - if (!memoriesEnabled) { - preferences.memories = { enabled: false }; - } - - if (avatarColor) { - preferences.avatar = { color: avatarColor }; - } - - if (Object.keys(preferences).length === 0) { - continue; - } - - await queryRunner.query('INSERT INTO "user_metadata" ("userId", "key", "value") VALUES ($1, $2, $3)', [ - id, - 'preferences', - preferences, - ]); - } - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "memoriesEnabled"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "avatarColor"`); - await queryRunner.query( - `ALTER TABLE "user_metadata" ADD CONSTRAINT "FK_6afb43681a21cf7815932bc38ac" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_metadata" DROP CONSTRAINT "FK_6afb43681a21cf7815932bc38ac"`); - await queryRunner.query(`ALTER TABLE "users" ADD "avatarColor" character varying`); - await queryRunner.query(`ALTER TABLE "users" ADD "memoriesEnabled" boolean NOT NULL DEFAULT true`); - const items = await queryRunner.query( - `SELECT "userId" as "id", "value" FROM "user_metadata" WHERE "key"='preferences'`, - ); - for (const { id, value } of items) { - if (!value) { - continue; - } - - if (value.avatar?.color) { - await queryRunner.query(`UPDATE "users" SET "avatarColor" = $1 WHERE "id" = $2`, [value.avatar.color, id]); - } - - if (value.memories?.enabled === false) { - await queryRunner.query(`UPDATE "users" SET "memoriesEnabled" = false WHERE "id" = $1`, [id]); - } - } - await queryRunner.query(`DROP TABLE "user_metadata"`); - } -} diff --git a/server/src/migrations/1718486162779-AddFaceSearchRelation.ts b/server/src/migrations/1718486162779-AddFaceSearchRelation.ts deleted file mode 100644 index 2bd1acad34..0000000000 --- a/server/src/migrations/1718486162779-AddFaceSearchRelation.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { DatabaseExtension } from 'src/enum'; -import { getVectorExtension } from 'src/repositories/database.repository'; -import { vectorIndexQuery } from 'src/utils/database'; -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddFaceSearchRelation1718486162779 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - const vectorExtension = await getVectorExtension(queryRunner); - if (vectorExtension === DatabaseExtension.Vectors) { - await queryRunner.query(`SET search_path TO "$user", public, vectors`); - } - - const hasEmbeddings = async (tableName: string): Promise => { - const columns = await queryRunner.query( - `SELECT column_name as name - FROM information_schema.columns - WHERE table_name = '${tableName}'`, - ); - return columns.some((column: { name: string }) => column.name === 'embedding'); - }; - - const hasAssetEmbeddings = await hasEmbeddings('smart_search'); - if (!hasAssetEmbeddings) { - await queryRunner.query(`TRUNCATE smart_search`); - await queryRunner.query(`ALTER TABLE smart_search ADD COLUMN IF NOT EXISTS embedding vector(512) NOT NULL`); - } - - await queryRunner.query(` - CREATE TABLE face_search ( - "faceId" uuid PRIMARY KEY REFERENCES asset_faces(id) ON DELETE CASCADE, - embedding vector(512) NOT NULL )`); - - await queryRunner.query(`ALTER TABLE face_search ALTER COLUMN embedding SET STORAGE EXTERNAL`); - await queryRunner.query(`ALTER TABLE smart_search ALTER COLUMN embedding SET STORAGE EXTERNAL`); - - const hasFaceEmbeddings = await hasEmbeddings('asset_faces'); - if (hasFaceEmbeddings) { - await queryRunner.query(` - INSERT INTO face_search("faceId", embedding) - SELECT id, embedding - FROM asset_faces faces`); - } - - await queryRunner.query(`ALTER TABLE asset_faces DROP COLUMN IF EXISTS embedding`); - - await queryRunner.query(`ALTER TABLE face_search ALTER COLUMN embedding SET DATA TYPE real[]`); - await queryRunner.query(`ALTER TABLE face_search ALTER COLUMN embedding SET DATA TYPE vector(512)`); - - await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'smart_search', indexName: 'clip_index' })); - await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'face_search', indexName: 'face_index' })); - } - - public async down(queryRunner: QueryRunner): Promise { - const vectorExtension = await getVectorExtension(queryRunner); - if (vectorExtension === DatabaseExtension.Vectors) { - await queryRunner.query(`SET search_path TO "$user", public, vectors`); - } - - await queryRunner.query(`ALTER TABLE asset_faces ADD COLUMN "embedding" vector(512)`); - await queryRunner.query(`ALTER TABLE face_search ALTER COLUMN embedding SET STORAGE DEFAULT`); - await queryRunner.query(`ALTER TABLE smart_search ALTER COLUMN embedding SET STORAGE DEFAULT`); - await queryRunner.query(` - UPDATE asset_faces - SET embedding = fs.embedding - FROM face_search fs - WHERE id = fs."faceId"`); - await queryRunner.query(`DROP TABLE face_search`); - - await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'asset_faces', indexName: 'face_index' })); - } -} diff --git a/server/src/migrations/1719359859887-FixLivePhotoVideoRelation.ts b/server/src/migrations/1719359859887-FixLivePhotoVideoRelation.ts deleted file mode 100644 index 9bf2a2b8d7..0000000000 --- a/server/src/migrations/1719359859887-FixLivePhotoVideoRelation.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class FixLivePhotoVideoRelation1719359859887 implements MigrationInterface { - name = 'FixLivePhotoVideoRelation1719359859887' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_16294b83fa8c0149719a1f631ef"`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_16294b83fa8c0149719a1f631ef"`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "FK_16294b83fa8c0149719a1f631ef" FOREIGN KEY ("livePhotoVideoId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_16294b83fa8c0149719a1f631ef"`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "UQ_16294b83fa8c0149719a1f631ef" UNIQUE ("livePhotoVideoId")`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "FK_16294b83fa8c0149719a1f631ef" FOREIGN KEY ("livePhotoVideoId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE CASCADE`); - } - -} diff --git a/server/src/migrations/1720207981949-AddStackOwner.ts b/server/src/migrations/1720207981949-AddStackOwner.ts deleted file mode 100644 index 61394cc985..0000000000 --- a/server/src/migrations/1720207981949-AddStackOwner.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddStackOwner1720207981949 implements MigrationInterface { - name = 'AddStackOwner1720207981949' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_stack" ADD "ownerId" uuid`); - await queryRunner.query(` - UPDATE "asset_stack" stack - SET "ownerId" = asset."ownerId" - FROM "assets" asset - WHERE stack."primaryAssetId" = asset."id" - `) - await queryRunner.query('ALTER TABLE "asset_stack" ALTER COLUMN "ownerId" SET NOT NULL') - await queryRunner.query(`ALTER TABLE "asset_stack" ADD CONSTRAINT "FK_c05079e542fd74de3b5ecb5c1c8" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_stack" DROP CONSTRAINT "FK_c05079e542fd74de3b5ecb5c1c8"`); - await queryRunner.query(`ALTER TABLE "asset_stack" DROP COLUMN "ownerId"`); - } -} diff --git a/server/src/migrations/1720375641148-natural-earth-countries.ts b/server/src/migrations/1720375641148-natural-earth-countries.ts deleted file mode 100644 index 8c58321dca..0000000000 --- a/server/src/migrations/1720375641148-natural-earth-countries.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class NaturalEarthCountries1720375641148 implements MigrationInterface { - name = 'NaturalEarthCountries1720375641148' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "naturalearth_countries" ("id" SERIAL NOT NULL, "admin" character varying(50) NOT NULL, "admin_a3" character varying(3) NOT NULL, "type" character varying(50) NOT NULL, "coordinates" polygon NOT NULL, CONSTRAINT "PK_21a6d86d1ab5d841648212e5353" PRIMARY KEY ("id"))`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "naturalearth_countries"`); - } - -} diff --git a/server/src/migrations/1721249222549-AddSourceColumnToAssetFace.ts b/server/src/migrations/1721249222549-AddSourceColumnToAssetFace.ts deleted file mode 100644 index 7f185077ff..0000000000 --- a/server/src/migrations/1721249222549-AddSourceColumnToAssetFace.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddSourceColumnToAssetFace1721249222549 implements MigrationInterface { - name = 'AddSourceColumnToAssetFace1721249222549' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TYPE sourceType AS ENUM ('machine-learning', 'exif');`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD "sourceType" sourceType NOT NULL DEFAULT 'machine-learning'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "sourceType"`); - await queryRunner.query(`DROP TYPE sourceType`); - } - -} diff --git a/server/src/migrations/1722753178937-AddExifRating.ts b/server/src/migrations/1722753178937-AddExifRating.ts deleted file mode 100644 index 52e8fb71e8..0000000000 --- a/server/src/migrations/1722753178937-AddExifRating.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddRating1722753178937 implements MigrationInterface { - name = 'AddRating1722753178937' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "rating" integer`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "rating"`); - } - -} diff --git a/server/src/migrations/1723719333525-AddApiKeyPermissions.ts b/server/src/migrations/1723719333525-AddApiKeyPermissions.ts deleted file mode 100644 index d585d98bcb..0000000000 --- a/server/src/migrations/1723719333525-AddApiKeyPermissions.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddApiKeyPermissions1723719333525 implements MigrationInterface { - name = 'AddApiKeyPermissions1723719333525'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "api_keys" ADD "permissions" character varying array NOT NULL DEFAULT '{all}'`); - await queryRunner.query(`ALTER TABLE "api_keys" ALTER COLUMN "permissions" DROP DEFAULT`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "api_keys" DROP COLUMN "permissions"`); - } -} diff --git a/server/src/migrations/1724080823160-AddThumbnailJobStatus.ts b/server/src/migrations/1724080823160-AddThumbnailJobStatus.ts deleted file mode 100644 index a71ddfbcf3..0000000000 --- a/server/src/migrations/1724080823160-AddThumbnailJobStatus.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddThumbnailJobStatus1724080823160 implements MigrationInterface { - name = 'AddThumbnailJobStatus1724080823160'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_job_status" ADD "previewAt" TIMESTAMP WITH TIME ZONE`); - await queryRunner.query(`ALTER TABLE "asset_job_status" ADD "thumbnailAt" TIMESTAMP WITH TIME ZONE`); - await queryRunner.query(`UPDATE "asset_job_status" SET "previewAt" = NOW() FROM "assets" WHERE "assetId" = "assets"."id" AND "assets"."previewPath" IS NOT NULL`); - await queryRunner.query(`UPDATE "asset_job_status" SET "thumbnailAt" = NOW() FROM "assets" WHERE "assetId" = "assets"."id" AND "assets"."thumbnailPath" IS NOT NULL`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_job_status" DROP COLUMN "thumbnailAt"`); - await queryRunner.query(`ALTER TABLE "asset_job_status" DROP COLUMN "previewAt"`); - } -} diff --git a/server/src/migrations/1724101822106-AddAssetFilesTable.ts b/server/src/migrations/1724101822106-AddAssetFilesTable.ts deleted file mode 100644 index bb086b084e..0000000000 --- a/server/src/migrations/1724101822106-AddAssetFilesTable.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAssetFilesTable1724101822106 implements MigrationInterface { - name = 'AddAssetFilesTable1724101822106' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "asset_files" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "assetId" uuid NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "type" character varying NOT NULL, "path" character varying NOT NULL, CONSTRAINT "UQ_assetId_type" UNIQUE ("assetId", "type"), CONSTRAINT "PK_c41dc3e9ef5e1c57ca5a08a0004" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_asset_files_assetId" ON "asset_files" ("assetId") `); - await queryRunner.query(`ALTER TABLE "asset_files" ADD CONSTRAINT "FK_e3e103a5f1d8bc8402999286040" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - - // preview path migration - await queryRunner.query(`INSERT INTO "asset_files" ("assetId", "type", "path") SELECT "id", 'preview', "previewPath" FROM "assets" WHERE "previewPath" IS NOT NULL AND "previewPath" != ''`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "previewPath"`); - - // thumbnail path migration - await queryRunner.query(`INSERT INTO "asset_files" ("assetId", "type", "path") SELECT "id", 'thumbnail', "thumbnailPath" FROM "assets" WHERE "thumbnailPath" IS NOT NULL AND "thumbnailPath" != ''`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "thumbnailPath"`); - } - - public async down(queryRunner: QueryRunner): Promise { - // undo preview path migration - await queryRunner.query(`ALTER TABLE "assets" ADD "previewPath" character varying`); - await queryRunner.query(`UPDATE "assets" SET "previewPath" = "asset_files".path FROM "asset_files" WHERE "assets".id = "asset_files".assetId AND "asset_files".type = 'preview'`); - - // undo thumbnail path migration - await queryRunner.query(`ALTER TABLE "assets" ADD "thumbnailPath" character varying DEFAULT ''`); - await queryRunner.query(`UPDATE "assets" SET "thumbnailPath" = "asset_files".path FROM "asset_files" WHERE "assets".id = "asset_files".assetId AND "asset_files".type = 'thumbnail'`); - - await queryRunner.query(`ALTER TABLE "asset_files" DROP CONSTRAINT "FK_e3e103a5f1d8bc8402999286040"`); - await queryRunner.query(`DROP INDEX "IDX_asset_files_assetId"`); - await queryRunner.query(`DROP TABLE "asset_files"`); - } - -} diff --git a/server/src/migrations/1724790460210-NestedTagTable.ts b/server/src/migrations/1724790460210-NestedTagTable.ts deleted file mode 100644 index d468ff6ba4..0000000000 --- a/server/src/migrations/1724790460210-NestedTagTable.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class NestedTagTable1724790460210 implements MigrationInterface { - name = 'NestedTagTable1724790460210' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query('TRUNCATE TABLE "tags" CASCADE'); - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "FK_92e67dc508c705dd66c94615576"`); - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "UQ_tag_name_userId"`); - await queryRunner.query(`CREATE TABLE "tags_closure" ("id_ancestor" uuid NOT NULL, "id_descendant" uuid NOT NULL, CONSTRAINT "PK_eab38eb12a3ec6df8376c95477c" PRIMARY KEY ("id_ancestor", "id_descendant"))`); - await queryRunner.query(`CREATE INDEX "IDX_15fbcbc67663c6bfc07b354c22" ON "tags_closure" ("id_ancestor") `); - await queryRunner.query(`CREATE INDEX "IDX_b1a2a7ed45c29179b5ad51548a" ON "tags_closure" ("id_descendant") `); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "renameTagId"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "type"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "name"`); - await queryRunner.query(`ALTER TABLE "tags" ADD "value" character varying NOT NULL`); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "UQ_d090e09fe86ebe2ec0aec27b451" UNIQUE ("value")`); - await queryRunner.query(`ALTER TABLE "tags" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); - await queryRunner.query(`ALTER TABLE "tags" ADD "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); - await queryRunner.query(`ALTER TABLE "tags" ADD "color" character varying`); - await queryRunner.query(`ALTER TABLE "tags" ADD "parentId" uuid`); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "FK_9f9590cc11561f1f48ff034ef99" FOREIGN KEY ("parentId") REFERENCES "tags"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "FK_92e67dc508c705dd66c94615576" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "tags_closure" ADD CONSTRAINT "FK_15fbcbc67663c6bfc07b354c22c" FOREIGN KEY ("id_ancestor") REFERENCES "tags"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "tags_closure" ADD CONSTRAINT "FK_b1a2a7ed45c29179b5ad51548a1" FOREIGN KEY ("id_descendant") REFERENCES "tags"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42"`); - await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9"`); - await queryRunner.query(`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9" FOREIGN KEY ("assetsId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42" FOREIGN KEY ("tagsId") REFERENCES "tags"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42"`); - await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9"`); - await queryRunner.query(`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9" FOREIGN KEY ("assetsId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42" FOREIGN KEY ("tagsId") REFERENCES "tags"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "tags_closure" DROP CONSTRAINT "FK_b1a2a7ed45c29179b5ad51548a1"`); - await queryRunner.query(`ALTER TABLE "tags_closure" DROP CONSTRAINT "FK_15fbcbc67663c6bfc07b354c22c"`); - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "FK_92e67dc508c705dd66c94615576"`); - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "FK_9f9590cc11561f1f48ff034ef99"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "parentId"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "color"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "updatedAt"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "createdAt"`); - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "UQ_d090e09fe86ebe2ec0aec27b451"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "value"`); - await queryRunner.query(`ALTER TABLE "tags" ADD "name" character varying NOT NULL`); - await queryRunner.query(`ALTER TABLE "tags" ADD "type" character varying NOT NULL`); - await queryRunner.query(`ALTER TABLE "tags" ADD "renameTagId" uuid`); - await queryRunner.query(`DROP INDEX "IDX_b1a2a7ed45c29179b5ad51548a"`); - await queryRunner.query(`DROP INDEX "IDX_15fbcbc67663c6bfc07b354c22"`); - await queryRunner.query(`DROP TABLE "tags_closure"`); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "UQ_tag_name_userId" UNIQUE ("name", "userId")`); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "FK_92e67dc508c705dd66c94615576" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - -} diff --git a/server/src/migrations/1725023079109-FixTagUniqueness.ts b/server/src/migrations/1725023079109-FixTagUniqueness.ts deleted file mode 100644 index 859712621c..0000000000 --- a/server/src/migrations/1725023079109-FixTagUniqueness.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class FixTagUniqueness1725023079109 implements MigrationInterface { - name = 'FixTagUniqueness1725023079109' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "UQ_d090e09fe86ebe2ec0aec27b451"`); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "UQ_79d6f16e52bb2c7130375246793" UNIQUE ("userId", "value")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "UQ_79d6f16e52bb2c7130375246793"`); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "UQ_d090e09fe86ebe2ec0aec27b451" UNIQUE ("value")`); - } - -} diff --git a/server/src/migrations/1725258039306-UpsertMissingAssetJobStatus.ts b/server/src/migrations/1725258039306-UpsertMissingAssetJobStatus.ts deleted file mode 100644 index 8eb47db438..0000000000 --- a/server/src/migrations/1725258039306-UpsertMissingAssetJobStatus.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UpsertMissingAssetJobStatus1725258039306 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `INSERT INTO "asset_job_status" ("assetId", "facesRecognizedAt", "metadataExtractedAt", "duplicatesDetectedAt", "previewAt", "thumbnailAt") SELECT "assetId", NULL, NULL, NULL, NULL, NULL FROM "asset_files" f WHERE "f"."path" IS NOT NULL ON CONFLICT DO NOTHING`, - ); - - await queryRunner.query( - `UPDATE "asset_job_status" SET "previewAt" = NOW() FROM "asset_files" f WHERE "previewAt" IS NULL AND "asset_job_status"."assetId" = "f"."assetId" AND "f"."type" = 'preview' AND "f"."path" IS NOT NULL`, - ); - - await queryRunner.query( - `UPDATE "asset_job_status" SET "thumbnailAt" = NOW() FROM "asset_files" f WHERE "thumbnailAt" IS NULL AND "asset_job_status"."assetId" = "f"."assetId" AND "f"."type" = 'thumbnail' AND "f"."path" IS NOT NULL`, - ); - } - - public async down(): Promise { - // do nothing - } -} diff --git a/server/src/migrations/1725327902980-RemoveThumbailAtForMissingThumbnails.ts b/server/src/migrations/1725327902980-RemoveThumbailAtForMissingThumbnails.ts deleted file mode 100644 index 98a3fe403a..0000000000 --- a/server/src/migrations/1725327902980-RemoveThumbailAtForMissingThumbnails.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveThumbailAtForMissingThumbnails1725327902980 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `UPDATE "asset_job_status" j SET "thumbnailAt" = NULL WHERE j."thumbnailAt" IS NOT NULL AND NOT EXISTS ( SELECT 1 FROM asset_files f WHERE j."assetId" = f."assetId" AND f."type" = 'thumbnail' AND f."path" IS NOT NULL )`, - ); - } - - public async down(): Promise { - // do nothing - } -} diff --git a/server/src/migrations/1725730782681-RemoveHiddenAssetsFromAlbums.ts b/server/src/migrations/1725730782681-RemoveHiddenAssetsFromAlbums.ts deleted file mode 100644 index 2dfb5b7978..0000000000 --- a/server/src/migrations/1725730782681-RemoveHiddenAssetsFromAlbums.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveHiddenAssetsFromAlbums1725730782681 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DELETE FROM "albums_assets_assets" WHERE "assetsId" IN (SELECT "id" FROM "assets" WHERE "isVisible" = false)`, - ); - } - - public async down(): Promise { - // noop - } -} diff --git a/server/src/migrations/1726491047923-AddprofileChangedAt.ts b/server/src/migrations/1726491047923-AddprofileChangedAt.ts deleted file mode 100644 index bcf568426a..0000000000 --- a/server/src/migrations/1726491047923-AddprofileChangedAt.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddprofileChangedAt1726491047923 implements MigrationInterface { - name = 'AddprofileChangedAt1726491047923' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "profileChangedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "profileChangedAt"`); - } - -} diff --git a/server/src/migrations/1726593009549-AddAssetStatus.ts b/server/src/migrations/1726593009549-AddAssetStatus.ts deleted file mode 100644 index 5b243b05b5..0000000000 --- a/server/src/migrations/1726593009549-AddAssetStatus.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAssetStatus1726593009549 implements MigrationInterface { - name = 'AddAssetStatus1726593009549' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TYPE "assets_status_enum" AS ENUM('active', 'trashed', 'deleted')`); - await queryRunner.query(`ALTER TABLE "assets" ADD "status" "assets_status_enum" NOT NULL DEFAULT 'active'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "status"`); - await queryRunner.query(`DROP TYPE "assets_status_enum"`); - } - -} diff --git a/server/src/migrations/1727471863507-SeparateQualityForThumbnailAndPreview.ts b/server/src/migrations/1727471863507-SeparateQualityForThumbnailAndPreview.ts deleted file mode 100644 index e02203997f..0000000000 --- a/server/src/migrations/1727471863507-SeparateQualityForThumbnailAndPreview.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class SeparateQualityForThumbnailAndPreview1727471863507 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - update system_metadata - set value = jsonb_set(value, '{image}', jsonb_strip_nulls( - jsonb_build_object( - 'preview', jsonb_build_object( - 'format', value->'image'->'previewFormat', - 'quality', value->'image'->'quality', - 'size', value->'image'->'previewSize'), - 'thumbnail', jsonb_build_object( - 'format', value->'image'->'thumbnailFormat', - 'quality', value->'image'->'quality', - 'size', value->'image'->'thumbnailSize'), - 'extractEmbedded', value->'extractEmbedded', - 'colorspace', value->'colorspace' - ))) - where key = 'system-config'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - update system_metadata - set value = jsonb_set(value, '{image}', jsonb_strip_nulls(jsonb_build_object( - 'previewFormat', value->'image'->'preview'->'format', - 'previewSize', value->'image'->'preview'->'size', - 'thumbnailFormat', value->'image'->'thumbnail'->'format', - 'thumbnailSize', value->'image'->'thumbnail'->'size', - 'extractEmbedded', value->'extractEmbedded', - 'colorspace', value->'colorspace', - 'quality', value->'image'->'preview'->'quality' - ))) - where key = 'system-config'`); - } -} diff --git a/server/src/migrations/1727781844613-IsOfflineSetDeletedAt.ts b/server/src/migrations/1727781844613-IsOfflineSetDeletedAt.ts deleted file mode 100644 index 050e9a93cf..0000000000 --- a/server/src/migrations/1727781844613-IsOfflineSetDeletedAt.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class IsOfflineSetDeletedAt1727781844613 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `UPDATE assets SET "deletedAt" = now() WHERE "isOffline" = true AND "deletedAt" IS NULL`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `UPDATE assets SET "deletedAt" = null WHERE "isOffline" = true`, - ); - } -} diff --git a/server/src/migrations/1727797340951-AddVersionHistory.ts b/server/src/migrations/1727797340951-AddVersionHistory.ts deleted file mode 100644 index 7eb731d1a3..0000000000 --- a/server/src/migrations/1727797340951-AddVersionHistory.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddVersionHistory1727797340951 implements MigrationInterface { - name = 'AddVersionHistory1727797340951' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "version_history" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "version" character varying NOT NULL, CONSTRAINT "PK_5db259cbb09ce82c0d13cfd1b23" PRIMARY KEY ("id"))`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "version_history"`); - } - -} diff --git a/server/src/migrations/1729793521993-AddAlbumAssetCreatedAt.ts b/server/src/migrations/1729793521993-AddAlbumAssetCreatedAt.ts deleted file mode 100644 index 280b34890d..0000000000 --- a/server/src/migrations/1729793521993-AddAlbumAssetCreatedAt.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddAlbumAssetCreatedAt1729793521993 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "albums_assets_assets" ADD COLUMN "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP COLUMN "createdAt"`); - } -} diff --git a/server/src/migrations/1730227312171-RemoveNplFromSystemConfig.ts b/server/src/migrations/1730227312171-RemoveNplFromSystemConfig.ts deleted file mode 100644 index 2c929191dd..0000000000 --- a/server/src/migrations/1730227312171-RemoveNplFromSystemConfig.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveNplFromSystemConfig1730227312171 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - update system_metadata - set value = value #- '{ffmpeg,npl}' - where key = 'system-config' and value->'ffmpeg'->'npl' is not null`); - } - - public async down(): Promise {} -} diff --git a/server/src/migrations/1730989238718-DropSmartInfoTable.ts b/server/src/migrations/1730989238718-DropSmartInfoTable.ts deleted file mode 100644 index a4de2652d6..0000000000 --- a/server/src/migrations/1730989238718-DropSmartInfoTable.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class DropSmartInfoTable1730989238718 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE smart_info`); - } - - public async down(): Promise { - // not implemented - } -} diff --git a/server/src/migrations/1732072134943-NaturalEarthCountriesIdentityColumn.ts b/server/src/migrations/1732072134943-NaturalEarthCountriesIdentityColumn.ts deleted file mode 100644 index 3ebe8108cb..0000000000 --- a/server/src/migrations/1732072134943-NaturalEarthCountriesIdentityColumn.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class NaturalEarthCountriesIdentityColumn1732072134943 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE naturalearth_countries ALTER id DROP DEFAULT`); - await queryRunner.query(`DROP SEQUENCE naturalearth_countries_id_seq`); - await queryRunner.query(`ALTER TABLE naturalearth_countries ALTER id ADD GENERATED ALWAYS AS IDENTITY`); - - // same as ll_to_earth, but with explicit schema to avoid weirdness and allow it to work in expression indices - await queryRunner.query(` - CREATE FUNCTION ll_to_earth_public(latitude double precision, longitude double precision) RETURNS public.earth PARALLEL SAFE IMMUTABLE STRICT LANGUAGE SQL AS $$ - SELECT public.cube(public.cube(public.cube(public.earth()*cos(radians(latitude))*cos(radians(longitude))),public.earth()*cos(radians(latitude))*sin(radians(longitude))),public.earth()*sin(radians(latitude)))::public.earth - $$`); - - await queryRunner.query(`ALTER TABLE geodata_places DROP COLUMN "earthCoord"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE naturalearth_countries ALTER id DROP GENERATED`); - await queryRunner.query(`CREATE SEQUENCE naturalearth_countries_id_seq`); - await queryRunner.query( - `ALTER TABLE naturalearth_countries ALTER id SET DEFAULT nextval('naturalearth_countries_id_seq'::regclass)`, - ); - await queryRunner.query(`DROP FUNCTION ll_to_earth_public`); - await queryRunner.query( - `ALTER TABLE "geodata_places" ADD "earthCoord" earth GENERATED ALWAYS AS (ll_to_earth(latitude, longitude)) STORED`, - ); - } -} diff --git a/server/src/migrations/1733339482860-RenameMachineLearningUrlToUrls.ts b/server/src/migrations/1733339482860-RenameMachineLearningUrlToUrls.ts deleted file mode 100644 index 65bb02c8e2..0000000000 --- a/server/src/migrations/1733339482860-RenameMachineLearningUrlToUrls.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RenameMachineLearningUrlToUrls1733339482860 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_metadata - SET value = jsonb_insert(value #- '{machineLearning,url}', '{machineLearning,urls}'::text[], jsonb_build_array(value->'machineLearning'->'url')) - WHERE key = 'system-config' AND value->'machineLearning'->'url' IS NOT NULL - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_metadata - SET value = jsonb_insert(value #- '{machineLearning,urls}', '{machineLearning,url}'::text[], to_jsonb(value->'machineLearning'->'urls'->>0)) - WHERE key = 'system-config' AND value->'machineLearning'->'urls' IS NOT NULL AND jsonb_array_length(value->'machineLearning'->'urls') >= 1 - `); - } -} diff --git a/server/src/migrations/1734574016301-AddTimeBucketIndices.ts b/server/src/migrations/1734574016301-AddTimeBucketIndices.ts deleted file mode 100644 index 2162a713fc..0000000000 --- a/server/src/migrations/1734574016301-AddTimeBucketIndices.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddTimeBucketIndices1734574016301 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE INDEX idx_local_date_time_month ON assets ((date_trunc('MONTH', "localDateTime" at time zone 'UTC') at time zone 'UTC'))`, - ); - await queryRunner.query( - `CREATE INDEX idx_local_date_time ON assets ((("localDateTime" at time zone 'UTC')::date))`, - ); - await queryRunner.query(`DROP INDEX "IDX_day_of_month"`); - await queryRunner.query(`DROP INDEX "IDX_month"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX idx_local_date_time_month`); - await queryRunner.query(`DROP INDEX idx_local_date_time`); - await queryRunner.query( - `CREATE INDEX "IDX_day_of_month" ON assets (EXTRACT(DAY FROM "localDateTime" AT TIME ZONE 'UTC'))`, - ); - await queryRunner.query( - `CREATE INDEX "IDX_month" ON assets (EXTRACT(MONTH FROM "localDateTime" AT TIME ZONE 'UTC'))`, - ); - } -} diff --git a/server/src/migrations/1734879118272-AddIsFavoritePerson.ts b/server/src/migrations/1734879118272-AddIsFavoritePerson.ts deleted file mode 100644 index 6f7640f96f..0000000000 --- a/server/src/migrations/1734879118272-AddIsFavoritePerson.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddIsFavoritePerson1734879118272 implements MigrationInterface { - name = 'AddIsFavoritePerson1734879118272' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" ADD "isFavorite" boolean NOT NULL DEFAULT false`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" DROP COLUMN "isFavorite"`); - } - -} diff --git a/server/src/migrations/1737672307560-AddUpdatedAtTriggers.ts b/server/src/migrations/1737672307560-AddUpdatedAtTriggers.ts deleted file mode 100644 index 74dde826fb..0000000000 --- a/server/src/migrations/1737672307560-AddUpdatedAtTriggers.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddUpdatedAtTriggers1737672307560 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - create function updated_at() - returns trigger as $$ - begin - new."updatedAt" = now(); - return new; - end; - $$ language 'plpgsql'`); - - await queryRunner.query(` - create trigger activity_updated_at - before update on activity - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger albums_updated_at - before update on albums - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger api_keys_updated_at - before update on api_keys - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger asset_files_updated_at - before update on asset_files - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger assets_updated_at - before update on assets - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger libraries_updated_at - before update on libraries - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger memories_updated_at - before update on memories - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger partners_updated_at - before update on partners - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger person_updated_at - before update on person - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger sessions_updated_at - before update on sessions - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger tags_updated_at - before update on tags - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger users_updated_at - before update on users - for each row execute procedure updated_at() - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`drop trigger activity_updated_at on activity`); - await queryRunner.query(`drop trigger albums_updated_at on albums`); - await queryRunner.query(`drop trigger api_keys_updated_at on api_keys`); - await queryRunner.query(`drop trigger asset_files_updated_at on asset_files`); - await queryRunner.query(`drop trigger assets_updated_at on assets`); - await queryRunner.query(`drop trigger libraries_updated_at on libraries`); - await queryRunner.query(`drop trigger memories_updated_at on memories`); - await queryRunner.query(`drop trigger partners_updated_at on partners`); - await queryRunner.query(`drop trigger person_updated_at on person`); - await queryRunner.query(`drop trigger sessions_updated_at on sessions`); - await queryRunner.query(`drop trigger tags_updated_at on tags`); - await queryRunner.query(`drop trigger users_updated_at on users`); - await queryRunner.query(`drop function updated_at_trigger`); - } -} diff --git a/server/src/migrations/1737845696644-NullableDates.ts b/server/src/migrations/1737845696644-NullableDates.ts deleted file mode 100644 index 8a08b985c5..0000000000 --- a/server/src/migrations/1737845696644-NullableDates.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class NullableDates1737845696644 implements MigrationInterface { - name = 'NullableDates1737845696644' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "fileCreatedAt" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "localDateTime" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "fileModifiedAt" DROP NOT NULL`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "fileModifiedAt" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "localDateTime" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "fileCreatedAt" SET NOT NULL`); - } - -} diff --git a/server/src/migrations/1738889177573-AddPersonColor.ts b/server/src/migrations/1738889177573-AddPersonColor.ts deleted file mode 100644 index ebdc86f52d..0000000000 --- a/server/src/migrations/1738889177573-AddPersonColor.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddPersonColor1738889177573 implements MigrationInterface { - name = 'AddPersonColor1738889177573' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" ADD "color" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" DROP COLUMN "color"`); - } - -} diff --git a/server/src/migrations/1739466714036-AddDeletedAtColumnToAssetFacesTable.ts b/server/src/migrations/1739466714036-AddDeletedAtColumnToAssetFacesTable.ts deleted file mode 100644 index e6f18e2618..0000000000 --- a/server/src/migrations/1739466714036-AddDeletedAtColumnToAssetFacesTable.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddDeletedAtColumnToAssetFacesTable1739466714036 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE asset_faces - ADD COLUMN "deletedAt" TIMESTAMP WITH TIME ZONE DEFAULT NULL - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE asset_faces - DROP COLUMN "deletedAt" - `); - } -} diff --git a/server/src/migrations/1739824470990-AddMemoryShowHideDates.ts b/server/src/migrations/1739824470990-AddMemoryShowHideDates.ts deleted file mode 100644 index d53c7c17f6..0000000000 --- a/server/src/migrations/1739824470990-AddMemoryShowHideDates.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddMemoryShowHideDates1739824470990 implements MigrationInterface { - name = 'AddMemoryShowHideDates1739824470990' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "memories" ADD "showAt" TIMESTAMP WITH TIME ZONE`); - await queryRunner.query(`ALTER TABLE "memories" ADD "hideAt" TIMESTAMP WITH TIME ZONE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "memories" DROP COLUMN "hideAt"`); - await queryRunner.query(`ALTER TABLE "memories" DROP COLUMN "showAt"`); - } - -} diff --git a/server/src/migrations/1740001232576-AddSessionSyncCheckpointTable.ts b/server/src/migrations/1740001232576-AddSessionSyncCheckpointTable.ts deleted file mode 100644 index ef75dd7c0d..0000000000 --- a/server/src/migrations/1740001232576-AddSessionSyncCheckpointTable.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddSessionSyncCheckpointTable1740001232576 implements MigrationInterface { - name = 'AddSessionSyncCheckpointTable1740001232576' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "session_sync_checkpoints" ("sessionId" uuid NOT NULL, "type" character varying NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "ack" character varying NOT NULL, CONSTRAINT "PK_b846ab547a702863ef7cd9412fb" PRIMARY KEY ("sessionId", "type"))`); - await queryRunner.query(`ALTER TABLE "session_sync_checkpoints" ADD CONSTRAINT "FK_d8ddd9d687816cc490432b3d4bc" FOREIGN KEY ("sessionId") REFERENCES "sessions"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(` - create trigger session_sync_checkpoints_updated_at - before update on session_sync_checkpoints - for each row execute procedure updated_at() - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`drop trigger session_sync_checkpoints_updated_at on session_sync_checkpoints`); - await queryRunner.query(`ALTER TABLE "session_sync_checkpoints" DROP CONSTRAINT "FK_d8ddd9d687816cc490432b3d4bc"`); - await queryRunner.query(`DROP TABLE "session_sync_checkpoints"`); - } - -} diff --git a/server/src/migrations/1740064899123-AddUsersAuditTable.ts b/server/src/migrations/1740064899123-AddUsersAuditTable.ts deleted file mode 100644 index b8f2ce5e3a..0000000000 --- a/server/src/migrations/1740064899123-AddUsersAuditTable.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddUsersAuditTable1740064899123 implements MigrationInterface { - name = 'AddUsersAuditTable1740064899123' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX IF NOT EXISTS "IDX_users_updated_at_asc_id_asc" ON "users" ("updatedAt" ASC, "id" ASC);`) - await queryRunner.query(`CREATE TABLE "users_audit" ("id" SERIAL NOT NULL, "userId" uuid NOT NULL, "deletedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_e9b2bdfd90e7eb5961091175180" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX IF NOT EXISTS "IDX_users_audit_deleted_at_asc_user_id_asc" ON "users_audit" ("deletedAt" ASC, "userId" ASC);`) - await queryRunner.query(`CREATE OR REPLACE FUNCTION users_delete_audit() RETURNS TRIGGER AS - $$ - BEGIN - INSERT INTO users_audit ("userId") - SELECT "id" - FROM OLD; - RETURN NULL; - END; - $$ LANGUAGE plpgsql` - ); - await queryRunner.query(`CREATE OR REPLACE TRIGGER users_delete_audit - AFTER DELETE ON users - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - EXECUTE FUNCTION users_delete_audit(); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TRIGGER users_delete_audit`); - await queryRunner.query(`DROP FUNCTION users_delete_audit`); - await queryRunner.query(`DROP TABLE "users_audit"`); - } - -} diff --git a/server/src/migrations/1740586617223-AddUpdateIdColumns.ts b/server/src/migrations/1740586617223-AddUpdateIdColumns.ts deleted file mode 100644 index 02d680ddf6..0000000000 --- a/server/src/migrations/1740586617223-AddUpdateIdColumns.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddUpdateIdColumns1740586617223 implements MigrationInterface { - name = 'AddUpdateIdColumns1740586617223' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - create or replace function immich_uuid_v7(p_timestamp timestamp with time zone default clock_timestamp()) - returns uuid - as $$ - select encode( - set_bit( - set_bit( - overlay(uuid_send(gen_random_uuid()) - placing substring(int8send(floor(extract(epoch from p_timestamp) * 1000)::bigint) from 3) - from 1 for 6 - ), - 52, 1 - ), - 53, 1 - ), - 'hex')::uuid; - $$ - language SQL - volatile; - `) - await queryRunner.query(` - CREATE OR REPLACE FUNCTION updated_at() RETURNS TRIGGER - LANGUAGE plpgsql - as $$ - BEGIN - return new; - END; - $$; - `) - await queryRunner.query(`ALTER TABLE "person" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "asset_files" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "libraries" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "tags" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "assets" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "users" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "albums" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "sessions" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "session_sync_checkpoints" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "partners" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "memories" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "activity" ADD "updateId" uuid`); - - await queryRunner.query(`UPDATE "person" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "asset_files" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "libraries" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "tags" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "assets" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "users" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "albums" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "sessions" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "session_sync_checkpoints" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "partners" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "memories" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "api_keys" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "activity" SET "updateId" = immich_uuid_v7("updatedAt")`); - - await queryRunner.query(`ALTER TABLE "person" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "asset_files" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "libraries" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "tags" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "albums" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "sessions" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "session_sync_checkpoints" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "partners" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "memories" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "api_keys" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "activity" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - - await queryRunner.query(`CREATE INDEX "IDX_person_update_id" ON "person" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_asset_files_update_id" ON "asset_files" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_libraries_update_id" ON "libraries" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_tags_update_id" ON "tags" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_assets_update_id" ON "assets" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_users_update_id" ON "users" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_albums_update_id" ON "albums" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_sessions_update_id" ON "sessions" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_session_sync_checkpoints_update_id" ON "session_sync_checkpoints" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_partners_update_id" ON "partners" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_memories_update_id" ON "memories" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_api_keys_update_id" ON "api_keys" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_activity_update_id" ON "activity" ("updateId")`); - - await queryRunner.query(` - CREATE OR REPLACE FUNCTION updated_at() RETURNS TRIGGER - LANGUAGE plpgsql - as $$ - DECLARE - clock_timestamp TIMESTAMP := clock_timestamp(); - BEGIN - new."updatedAt" = clock_timestamp; - new."updateId" = immich_uuid_v7(clock_timestamp); - return new; - END; - $$; - `) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "activity" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "api_keys" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "memories" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "partners" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "session_sync_checkpoints" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "sessions" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "albums" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "libraries" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "asset_files" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "person" DROP COLUMN "updateId"`); - await queryRunner.query(`DROP FUNCTION immich_uuid_v7`); - await queryRunner.query(` - CREATE OR REPLACE FUNCTION updated_at() RETURNS TRIGGER - LANGUAGE plpgsql - as $$ - BEGIN - new."updatedAt" = now(); - return new; - END; - $$; - `) - } - -} diff --git a/server/src/migrations/1740595460866-UsersAuditUuidv7PrimaryKey.ts b/server/src/migrations/1740595460866-UsersAuditUuidv7PrimaryKey.ts deleted file mode 100644 index 59fc4dbd5b..0000000000 --- a/server/src/migrations/1740595460866-UsersAuditUuidv7PrimaryKey.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class UsersAuditUuidv7PrimaryKey1740595460866 implements MigrationInterface { - name = 'UsersAuditUuidv7PrimaryKey1740595460866' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_users_audit_deleted_at_asc_user_id_asc"`); - await queryRunner.query(`ALTER TABLE "users_audit" DROP CONSTRAINT "PK_e9b2bdfd90e7eb5961091175180"`); - await queryRunner.query(`ALTER TABLE "users_audit" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "users_audit" ADD "id" uuid NOT NULL DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "users_audit" ADD CONSTRAINT "PK_e9b2bdfd90e7eb5961091175180" PRIMARY KEY ("id")`); - await queryRunner.query(`ALTER TABLE "users_audit" ALTER COLUMN "deletedAt" SET DEFAULT clock_timestamp()`) - await queryRunner.query(`CREATE INDEX "IDX_users_audit_deleted_at" ON "users_audit" ("deletedAt")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_users_audit_deleted_at"`); - await queryRunner.query(`ALTER TABLE "users_audit" DROP CONSTRAINT "PK_e9b2bdfd90e7eb5961091175180"`); - await queryRunner.query(`ALTER TABLE "users_audit" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "users_audit" ADD "id" SERIAL NOT NULL`); - await queryRunner.query(`ALTER TABLE "users_audit" ADD CONSTRAINT "PK_e9b2bdfd90e7eb5961091175180" PRIMARY KEY ("id")`); - await queryRunner.query(`ALTER TABLE "users_audit" ALTER COLUMN "deletedAt" SET DEFAULT now()`); - await queryRunner.query(`CREATE INDEX "IDX_users_audit_deleted_at_asc_user_id_asc" ON "users_audit" ("userId", "deletedAt") `); - } - -} diff --git a/server/src/migrations/1740619600996-AddManualSourceType.ts b/server/src/migrations/1740619600996-AddManualSourceType.ts deleted file mode 100644 index dd53312ad7..0000000000 --- a/server/src/migrations/1740619600996-AddManualSourceType.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddManualSourceType1740619600996 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TYPE sourceType ADD VALUE 'manual'`); - } - - public async down(queryRunner: QueryRunner): Promise { - // Prior to this migration, manually tagged pictures had the 'machine-learning' type - await queryRunner.query( - `UPDATE "asset_faces" SET "sourceType" = 'machine-learning' WHERE "sourceType" = 'manual';`, - ); - - // Postgres doesn't allow removing values from enums, we have to recreate the type - await queryRunner.query(`ALTER TYPE sourceType RENAME TO oldSourceType`); - await queryRunner.query(`CREATE TYPE sourceType AS ENUM ('machine-learning', 'exif');`); - - await queryRunner.query(`ALTER TABLE "asset_faces" ALTER COLUMN "sourceType" DROP DEFAULT;`); - await queryRunner.query( - `ALTER TABLE "asset_faces" ALTER COLUMN "sourceType" TYPE sourceType USING "sourceType"::text::sourceType;`, - ); - await queryRunner.query( - `ALTER TABLE "asset_faces" ALTER COLUMN "sourceType" SET DEFAULT 'machine-learning'::sourceType;`, - ); - await queryRunner.query(`DROP TYPE oldSourceType;`); - } -} diff --git a/server/src/migrations/1740654480319-UnsetStackedAssetsFromDuplicateStatus.ts b/server/src/migrations/1740654480319-UnsetStackedAssetsFromDuplicateStatus.ts deleted file mode 100644 index 5c735a60bb..0000000000 --- a/server/src/migrations/1740654480319-UnsetStackedAssetsFromDuplicateStatus.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UnsetStackedAssetsFromDuplicateStatus1740654480319 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - update assets - set "duplicateId" = null - where "stackId" is not null`); - } - - public async down(): Promise { - // No need to revert this migration - } -} diff --git a/server/src/migrations/1740739778549-CreatePartnersAuditTable.ts b/server/src/migrations/1740739778549-CreatePartnersAuditTable.ts deleted file mode 100644 index d9c9dc1949..0000000000 --- a/server/src/migrations/1740739778549-CreatePartnersAuditTable.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class CreatePartnersAuditTable1740739778549 implements MigrationInterface { - name = 'CreatePartnersAuditTable1740739778549' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "partners_audit" ("id" uuid NOT NULL DEFAULT immich_uuid_v7(), "sharedById" uuid NOT NULL, "sharedWithId" uuid NOT NULL, "deletedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT clock_timestamp(), CONSTRAINT "PK_952b50217ff78198a7e380f0359" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_partners_audit_shared_by_id" ON "partners_audit" ("sharedById") `); - await queryRunner.query(`CREATE INDEX "IDX_partners_audit_shared_with_id" ON "partners_audit" ("sharedWithId") `); - await queryRunner.query(`CREATE INDEX "IDX_partners_audit_deleted_at" ON "partners_audit" ("deletedAt") `); - await queryRunner.query(`CREATE OR REPLACE FUNCTION partners_delete_audit() RETURNS TRIGGER AS - $$ - BEGIN - INSERT INTO partners_audit ("sharedById", "sharedWithId") - SELECT "sharedById", "sharedWithId" - FROM OLD; - RETURN NULL; - END; - $$ LANGUAGE plpgsql` - ); - await queryRunner.query(`CREATE OR REPLACE TRIGGER partners_delete_audit - AFTER DELETE ON partners - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - EXECUTE FUNCTION partners_delete_audit(); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "public"."IDX_partners_audit_deleted_at"`); - await queryRunner.query(`DROP INDEX "public"."IDX_partners_audit_shared_with_id"`); - await queryRunner.query(`DROP INDEX "public"."IDX_partners_audit_shared_by_id"`); - await queryRunner.query(`DROP TRIGGER partners_delete_audit`); - await queryRunner.query(`DROP FUNCTION partners_delete_audit`); - await queryRunner.query(`DROP TABLE "partners_audit"`); - } - -} diff --git a/server/src/migrations/1741027685381-ResetMemories.ts b/server/src/migrations/1741027685381-ResetMemories.ts deleted file mode 100644 index 6a80372219..0000000000 --- a/server/src/migrations/1741027685381-ResetMemories.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class ResetMemories1741027685381 implements MigrationInterface { - name = 'ResetMemories1741027685381'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DELETE FROM "memories"`); - await queryRunner.query(`DELETE FROM "system_metadata" WHERE "key" = 'memories-state'`); - } - - public async down(): Promise { - // nothing to do - } -} diff --git a/server/src/migrations/1741179334403-MoveHistoryUuidEntityId.ts b/server/src/migrations/1741179334403-MoveHistoryUuidEntityId.ts deleted file mode 100644 index 449272341c..0000000000 --- a/server/src/migrations/1741179334403-MoveHistoryUuidEntityId.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class MoveHistoryUuidEntityId1741179334403 implements MigrationInterface { - name = 'MoveHistoryUuidEntityId1741179334403'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "move_history" ALTER COLUMN "entityId" TYPE uuid USING "entityId"::uuid;`); - await queryRunner.query(`delete from "move_history" - where - "move_history"."entityId" not in ( - select - "id" - from - "assets" - where - "assets"."id" = "move_history"."entityId" - ) - and "move_history"."pathType" = 'original' - `) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "move_history" ALTER COLUMN "entityId" TYPE character varying`); - } -} - diff --git a/server/src/migrations/1741191762113-AssetAuditTable.ts b/server/src/migrations/1741191762113-AssetAuditTable.ts deleted file mode 100644 index c02408c384..0000000000 --- a/server/src/migrations/1741191762113-AssetAuditTable.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AssetAuditTable1741191762113 implements MigrationInterface { - name = 'AssetAuditTable1741191762113' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "assets_audit" ("id" uuid NOT NULL DEFAULT immich_uuid_v7(), "assetId" uuid NOT NULL, "ownerId" uuid NOT NULL, "deletedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT clock_timestamp(), CONSTRAINT "PK_99bd5c015f81a641927a32b4212" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_assets_audit_asset_id" ON "assets_audit" ("assetId") `); - await queryRunner.query(`CREATE INDEX "IDX_assets_audit_owner_id" ON "assets_audit" ("ownerId") `); - await queryRunner.query(`CREATE INDEX "IDX_assets_audit_deleted_at" ON "assets_audit" ("deletedAt") `); - await queryRunner.query(`CREATE OR REPLACE FUNCTION assets_delete_audit() RETURNS TRIGGER AS - $$ - BEGIN - INSERT INTO assets_audit ("assetId", "ownerId") - SELECT "id", "ownerId" - FROM OLD; - RETURN NULL; - END; - $$ LANGUAGE plpgsql` - ); - await queryRunner.query(`CREATE OR REPLACE TRIGGER assets_delete_audit - AFTER DELETE ON assets - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - EXECUTE FUNCTION assets_delete_audit(); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TRIGGER assets_delete_audit`); - await queryRunner.query(`DROP FUNCTION assets_delete_audit`); - await queryRunner.query(`DROP INDEX "IDX_assets_audit_deleted_at"`); - await queryRunner.query(`DROP INDEX "IDX_assets_audit_owner_id"`); - await queryRunner.query(`DROP INDEX "IDX_assets_audit_asset_id"`); - await queryRunner.query(`DROP TABLE "assets_audit"`); - } -} diff --git a/server/src/migrations/1741280328985-FixAssetAndUserCascadeConditions.ts b/server/src/migrations/1741280328985-FixAssetAndUserCascadeConditions.ts deleted file mode 100644 index 20215c1b59..0000000000 --- a/server/src/migrations/1741280328985-FixAssetAndUserCascadeConditions.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class FixAssetAndUserCascadeConditions1741280328985 implements MigrationInterface { - name = 'FixAssetAndUserCascadeConditions1741280328985'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - CREATE OR REPLACE TRIGGER assets_delete_audit - AFTER DELETE ON assets - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - WHEN (pg_trigger_depth() = 0) - EXECUTE FUNCTION assets_delete_audit();`); - await queryRunner.query(` - CREATE OR REPLACE TRIGGER users_delete_audit - AFTER DELETE ON users - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - WHEN (pg_trigger_depth() = 0) - EXECUTE FUNCTION users_delete_audit();`); - await queryRunner.query(` - CREATE OR REPLACE TRIGGER partners_delete_audit - AFTER DELETE ON partners - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - WHEN (pg_trigger_depth() = 0) - EXECUTE FUNCTION partners_delete_audit();`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - CREATE OR REPLACE TRIGGER assets_delete_audit - AFTER DELETE ON assets - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - EXECUTE FUNCTION assets_delete_audit();`); - await queryRunner.query(` - CREATE OR REPLACE TRIGGER users_delete_audit - AFTER DELETE ON users - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - EXECUTE FUNCTION users_delete_audit();`); - await queryRunner.query(` - CREATE OR REPLACE TRIGGER partners_delete_audit - AFTER DELETE ON partners - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - EXECUTE FUNCTION partners_delete_audit();`); - } -} diff --git a/server/src/migrations/1741281344519-AddExifUpdateId.ts b/server/src/migrations/1741281344519-AddExifUpdateId.ts deleted file mode 100644 index eb32836a1d..0000000000 --- a/server/src/migrations/1741281344519-AddExifUpdateId.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddExifUpdateId1741281344519 implements MigrationInterface { - name = 'AddExifUpdateId1741281344519'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "exif" ADD "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT clock_timestamp()`, - ); - await queryRunner.query(`ALTER TABLE "exif" ADD "updateId" uuid NOT NULL DEFAULT immich_uuid_v7()`); - await queryRunner.query(`CREATE INDEX "IDX_asset_exif_update_id" ON "exif" ("updateId") `); - await queryRunner.query(` - create trigger asset_exif_updated_at - before update on exif - for each row execute procedure updated_at() - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "public"."IDX_asset_exif_update_id"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "updatedAt"`); - await queryRunner.query(`DROP TRIGGER asset_exif_updated_at on exif`); - } -} diff --git a/server/src/migrations/1743595393000-TableCleanup.ts b/server/src/migrations/1743595393000-TableCleanup.ts deleted file mode 100644 index adf9c65afa..0000000000 --- a/server/src/migrations/1743595393000-TableCleanup.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class TableCleanup1743595393000 implements MigrationInterface { - name = 'TableCleanup1743595393000'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE IF EXISTS "system_config"`); - await queryRunner.query(`DROP TABLE IF EXISTS "socket_io_attachments"`); - } - - public async down(): Promise {} -} diff --git a/server/src/migrations/1743611339000-GeodataCleanup.ts b/server/src/migrations/1743611339000-GeodataCleanup.ts deleted file mode 100644 index 0e25a1268e..0000000000 --- a/server/src/migrations/1743611339000-GeodataCleanup.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class GeodataCleanup1743611339000 implements MigrationInterface { - name = 'GeodataCleanup1743611339000'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER INDEX IF EXISTS "idx_geodata_places_admin2_alternate_names" RENAME TO "idx_geodata_places_alternate_names"`, - ); - await queryRunner.query(`DROP TABLE IF EXISTS "geodata_places_tmp"`); - await queryRunner.query(`DROP TABLE IF EXISTS "naturalearth_countries_tmp"`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER INDEX IF EXISTS "idx_geodata_places_alternate_names" RENAME TO "idx_geodata_places_admin2_alternate_names"`, - ); - } -} diff --git a/server/src/migrations/1744662638410-MakeFileMetadataNonNullable.ts b/server/src/migrations/1744662638410-MakeFileMetadataNonNullable.ts deleted file mode 100644 index 1ba4df01cd..0000000000 --- a/server/src/migrations/1744662638410-MakeFileMetadataNonNullable.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class MakeFileMetadataNonNullable1744662638410 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DELETE FROM assets WHERE "fileCreatedAt" IS NULL OR "fileModifiedAt" IS NULL OR "localDateTime" IS NULL`, - ); - await queryRunner.query(` - ALTER TABLE assets - ALTER COLUMN "fileCreatedAt" SET NOT NULL, - ALTER COLUMN "fileModifiedAt" SET NOT NULL, - ALTER COLUMN "localDateTime" SET NOT NULL`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE assets - ALTER COLUMN "fileCreatedAt" DROP NOT NULL, - ALTER COLUMN "fileModifiedAt" DROP NOT NULL, - ALTER COLUMN "localDateTime" DROP NOT NULL`); - } -} diff --git a/server/src/migrations/1744900200559-AddForeignKeyIndexes.ts b/server/src/migrations/1744900200559-AddForeignKeyIndexes.ts deleted file mode 100644 index db351d5bab..0000000000 --- a/server/src/migrations/1744900200559-AddForeignKeyIndexes.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddForeignKeyIndexes1744900200559 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX "IDX_0f6fc2fb195f24d19b0fb0d57c" ON "libraries" ("ownerId")`); - await queryRunner.query(`CREATE INDEX "IDX_91704e101438fd0653f582426d" ON "asset_stack" ("primaryAssetId")`); - await queryRunner.query(`CREATE INDEX "IDX_c05079e542fd74de3b5ecb5c1c" ON "asset_stack" ("ownerId")`); - await queryRunner.query(`CREATE INDEX "IDX_2c5ac0d6fb58b238fd2068de67" ON "assets" ("ownerId")`); - await queryRunner.query(`CREATE INDEX "IDX_16294b83fa8c0149719a1f631e" ON "assets" ("livePhotoVideoId")`); - await queryRunner.query(`CREATE INDEX "IDX_9977c3c1de01c3d848039a6b90" ON "assets" ("libraryId")`); - await queryRunner.query(`CREATE INDEX "IDX_f15d48fa3ea5e4bda05ca8ab20" ON "assets" ("stackId")`); - await queryRunner.query(`CREATE INDEX "IDX_b22c53f35ef20c28c21637c85f" ON "albums" ("ownerId")`); - await queryRunner.query(`CREATE INDEX "IDX_05895aa505a670300d4816debc" ON "albums" ("albumThumbnailAssetId")`); - await queryRunner.query(`CREATE INDEX "IDX_1af8519996fbfb3684b58df280" ON "activity" ("albumId")`); - await queryRunner.query(`CREATE INDEX "IDX_3571467bcbe021f66e2bdce96e" ON "activity" ("userId")`); - await queryRunner.query(`CREATE INDEX "IDX_8091ea76b12338cb4428d33d78" ON "activity" ("assetId")`); - await queryRunner.query(`CREATE INDEX "IDX_6c2e267ae764a9413b863a2934" ON "api_keys" ("userId")`); - await queryRunner.query(`CREATE INDEX "IDX_5527cc99f530a547093f9e577b" ON "person" ("ownerId")`); - await queryRunner.query(`CREATE INDEX "IDX_2bbabe31656b6778c6b87b6102" ON "person" ("faceAssetId")`); - await queryRunner.query(`CREATE INDEX "IDX_575842846f0c28fa5da46c99b1" ON "memories" ("ownerId")`); - await queryRunner.query(`CREATE INDEX "IDX_d7e875c6c60e661723dbf372fd" ON "partners" ("sharedWithId")`); - await queryRunner.query(`CREATE INDEX "IDX_57de40bc620f456c7311aa3a1e" ON "sessions" ("userId")`); - await queryRunner.query(`CREATE INDEX "IDX_66fe3837414c5a9f1c33ca4934" ON "shared_links" ("userId")`); - await queryRunner.query(`CREATE INDEX "IDX_d8ddd9d687816cc490432b3d4b" ON "session_sync_checkpoints" ("sessionId")`); - await queryRunner.query(`CREATE INDEX "IDX_9f9590cc11561f1f48ff034ef9" ON "tags" ("parentId")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_66fe3837414c5a9f1c33ca4934";`); - await queryRunner.query(`DROP INDEX "IDX_91704e101438fd0653f582426d";`); - await queryRunner.query(`DROP INDEX "IDX_c05079e542fd74de3b5ecb5c1c";`); - await queryRunner.query(`DROP INDEX "IDX_5527cc99f530a547093f9e577b";`); - await queryRunner.query(`DROP INDEX "IDX_2bbabe31656b6778c6b87b6102";`); - await queryRunner.query(`DROP INDEX "IDX_0f6fc2fb195f24d19b0fb0d57c";`); - await queryRunner.query(`DROP INDEX "IDX_9f9590cc11561f1f48ff034ef9";`); - await queryRunner.query(`DROP INDEX "IDX_2c5ac0d6fb58b238fd2068de67";`); - await queryRunner.query(`DROP INDEX "IDX_16294b83fa8c0149719a1f631e";`); - await queryRunner.query(`DROP INDEX "IDX_9977c3c1de01c3d848039a6b90";`); - await queryRunner.query(`DROP INDEX "IDX_f15d48fa3ea5e4bda05ca8ab20";`); - await queryRunner.query(`DROP INDEX "IDX_b22c53f35ef20c28c21637c85f";`); - await queryRunner.query(`DROP INDEX "IDX_05895aa505a670300d4816debc";`); - await queryRunner.query(`DROP INDEX "IDX_57de40bc620f456c7311aa3a1e";`); - await queryRunner.query(`DROP INDEX "IDX_d8ddd9d687816cc490432b3d4b";`); - await queryRunner.query(`DROP INDEX "IDX_d7e875c6c60e661723dbf372fd";`); - await queryRunner.query(`DROP INDEX "IDX_575842846f0c28fa5da46c99b1";`); - await queryRunner.query(`DROP INDEX "IDX_6c2e267ae764a9413b863a2934";`); - await queryRunner.query(`DROP INDEX "IDX_1af8519996fbfb3684b58df280";`); - await queryRunner.query(`DROP INDEX "IDX_3571467bcbe021f66e2bdce96e";`); - await queryRunner.query(`DROP INDEX "IDX_8091ea76b12338cb4428d33d78";`); - } -} diff --git a/server/src/migrations/1744910873956-AddMissingIndex.ts b/server/src/migrations/1744910873956-AddMissingIndex.ts deleted file mode 100644 index 38dd6f4958..0000000000 --- a/server/src/migrations/1744910873956-AddMissingIndex.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddMissingIndex1744910873956 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE INDEX IF NOT EXISTS "IDX_geodata_gist_earthcoord" ON "geodata_places" (ll_to_earth_public(latitude, longitude))`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_geodata_gist_earthcoord";`); - } -} diff --git a/server/src/repositories/database.repository.ts b/server/src/repositories/database.repository.ts index f334896ce1..e5d88339c8 100644 --- a/server/src/repositories/database.repository.ts +++ b/server/src/repositories/database.repository.ts @@ -3,7 +3,7 @@ import AsyncLock from 'async-lock'; import { FileMigrationProvider, Kysely, Migrator, sql, Transaction } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; import { readdir } from 'node:fs/promises'; -import { join, resolve } from 'node:path'; +import { join } from 'node:path'; import semver from 'semver'; import { EXTENSION_NAMES, @@ -23,10 +23,9 @@ import { DB } from 'src/schema'; import { ExtensionVersion, VectorExtension, VectorUpdateResult } from 'src/types'; import { vectorIndexQuery } from 'src/utils/database'; import { isValidInteger } from 'src/validation'; -import { DataSource, QueryRunner } from 'typeorm'; export let cachedVectorExtension: VectorExtension | undefined; -export async function getVectorExtension(runner: Kysely | QueryRunner): Promise { +export async function getVectorExtension(runner: Kysely): Promise { if (cachedVectorExtension) { return cachedVectorExtension; } @@ -36,14 +35,8 @@ export async function getVectorExtension(runner: Kysely | QueryRunner): Prom return cachedVectorExtension; } - let availableExtensions: { name: VectorExtension }[]; const query = `SELECT name FROM pg_available_extensions WHERE name IN (${VECTOR_EXTENSIONS.map((ext) => `'${ext}'`).join(', ')})`; - if (runner instanceof Kysely) { - const { rows } = await sql.raw<{ name: VectorExtension }>(query).execute(runner); - availableExtensions = rows; - } else { - availableExtensions = (await runner.query(query)) as { name: VectorExtension }[]; - } + const { rows: availableExtensions } = await sql.raw<{ name: VectorExtension }>(query).execute(runner); const extensionNames = new Set(availableExtensions.map((row) => row.name)); cachedVectorExtension = VECTOR_EXTENSIONS.find((ext) => extensionNames.has(ext)); if (!cachedVectorExtension) { @@ -364,45 +357,9 @@ export class DatabaseRepository { return count; } - async runMigrations(options?: { transaction?: 'all' | 'none' | 'each' }): Promise { - const { database } = this.configRepository.getEnv(); + async runMigrations(): Promise { + this.logger.debug('Running migrations'); - this.logger.log('Running migrations, this may take a while'); - - const tableExists = sql<{ result: string | null }>`select to_regclass('migrations') as "result"`; - const { rows } = await tableExists.execute(this.db); - const hasTypeOrmMigrations = !!rows[0]?.result; - if (hasTypeOrmMigrations) { - // eslint-disable-next-line unicorn/prefer-module - const dist = resolve(`${__dirname}/..`); - - this.logger.debug('Running typeorm migrations'); - const dataSource = new DataSource({ - type: 'postgres', - entities: [], - subscribers: [], - migrations: [`${dist}/migrations` + '/*.{js,ts}'], - migrationsRun: false, - synchronize: false, - connectTimeoutMS: 10_000, // 10 seconds - parseInt8: true, - ...(database.config.connectionType === 'url' - ? { url: database.config.url } - : { - host: database.config.host, - port: database.config.port, - username: database.config.username, - password: database.config.password, - database: database.config.database, - }), - }); - await dataSource.initialize(); - await dataSource.runMigrations(options); - await dataSource.destroy(); - this.logger.debug('Finished running typeorm migrations'); - } - - this.logger.debug('Running kysely migrations'); const migrator = new Migrator({ db: this.db, migrationLockTableName: 'kysely_migrations_lock', @@ -429,11 +386,11 @@ export class DatabaseRepository { } if (error) { - this.logger.error(`Kysely migrations failed: ${error}`); + this.logger.error(`Migrations failed: ${error}`); throw error; } - this.logger.debug('Finished running kysely migrations'); + this.logger.debug('Finished running migrations'); } async migrateFilePaths(sourceFolder: string, targetFolder: string): Promise { diff --git a/server/src/services/download.service.spec.ts b/server/src/services/download.service.spec.ts index a85fd74c72..86d0bda7f8 100644 --- a/server/src/services/download.service.spec.ts +++ b/server/src/services/download.service.spec.ts @@ -1,10 +1,10 @@ import { BadRequestException } from '@nestjs/common'; +import { Readable } from 'node:stream'; import { DownloadResponseDto } from 'src/dtos/download.dto'; import { DownloadService } from 'src/services/download.service'; import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { makeStream, newTestService, ServiceMocks } from 'test/utils'; -import { Readable } from 'typeorm/platform/PlatformTools.js'; import { vitest } from 'vitest'; const downloadResponse: DownloadResponseDto = { diff --git a/server/test/utils.ts b/server/test/utils.ts index af6f2826f9..9f212578c0 100644 --- a/server/test/utils.ts +++ b/server/test/utils.ts @@ -4,7 +4,7 @@ import { Test } from '@nestjs/testing'; import { ClassConstructor } from 'class-transformer'; import { Kysely } from 'kysely'; import { ChildProcessWithoutNullStreams } from 'node:child_process'; -import { Writable } from 'node:stream'; +import { Readable, Writable } from 'node:stream'; import { PNG } from 'pngjs'; import postgres from 'postgres'; import { AssetUploadInterceptor } from 'src/middleware/asset-upload.interceptor'; @@ -71,7 +71,6 @@ import { newMetadataRepositoryMock } from 'test/repositories/metadata.repository import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock'; import { newSystemMetadataRepositoryMock } from 'test/repositories/system-metadata.repository.mock'; import { ITelemetryRepositoryMock, newTelemetryRepositoryMock } from 'test/repositories/telemetry.repository.mock'; -import { Readable } from 'typeorm/platform/PlatformTools'; import { assert, Mock, Mocked, vitest } from 'vitest'; export type ControllerContext = { From 2f5d543ad91303e7e442d52b4b90282706f63771 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Tue, 29 Jul 2025 17:33:24 -0400 Subject: [PATCH 108/748] fix: tweak error docs (#20417) --- docs/src/pages/errors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/pages/errors.md b/docs/src/pages/errors.md index c5af7e2575..5f73162a61 100644 --- a/docs/src/pages/errors.md +++ b/docs/src/pages/errors.md @@ -2,7 +2,7 @@ ## TypeORM Upgrade -In order to update to Immich to `v1.137.0` (or above), the application must be started at least once on a version in the range between `1.132.0` and `1.136.0`. Doing so will complete database schema upgrades that are required for `v1.137.0` (and above). After Immich has successfully started on this version, shut the system down and try the update again. We recommend users upgrade to `1.132.0` since it does not have any other breaking changes. +In order to update to Immich to `v1.137.0` (or above), the application must be started at least once on a version in the range between `1.132.0` and `1.136.0`. Doing so will complete database schema upgrades that are required for `v1.137.0` (and above). After Immich has successfully updated to a version in this range, you can now attempt to update to v1.137.0 (or above). We recommend users upgrade to `1.132.0` since it does not have any other breaking changes. ## Inconsistent Media Location From 07ed060c3293077ea6b83dea1534d2e97ea4b658 Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Tue, 29 Jul 2025 21:55:21 -0500 Subject: [PATCH 109/748] feat: 3-2-1 backup onboarding card (#20374) * feat: 3-2-1 backup onboarding card * chore: format i18n * fix: lint * Update onboarding-backup.svelte * fix: e2e onboarding test --- e2e/src/web/specs/auth.e2e-spec.ts | 1 + i18n/en.json | 7 +++ .../onboarding-page/onboarding-backup.svelte | 62 +++++++++++++++++++ web/src/routes/auth/onboarding/+page.svelte | 10 ++- 4 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 web/src/lib/components/onboarding-page/onboarding-backup.svelte diff --git a/e2e/src/web/specs/auth.e2e-spec.ts b/e2e/src/web/specs/auth.e2e-spec.ts index 0fde9a6ec6..173131ec5e 100644 --- a/e2e/src/web/specs/auth.e2e-spec.ts +++ b/e2e/src/web/specs/auth.e2e-spec.ts @@ -37,6 +37,7 @@ test.describe('Registration', () => { await page.getByRole('button', { name: 'Server Privacy' }).click(); await page.getByRole('button', { name: 'User Privacy' }).click(); await page.getByRole('button', { name: 'Storage Template' }).click(); + await page.getByRole('button', { name: 'Backups' }).click(); await page.getByRole('button', { name: 'Done' }).click(); // success diff --git a/i18n/en.json b/i18n/en.json index 524466f4e3..49d24a2f84 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -44,6 +44,13 @@ "backup_database": "Create Database Dump", "backup_database_enable_description": "Enable database dumps", "backup_keep_last_amount": "Amount of previous dumps to keep", + "backup_onboarding_1_description": "offsite copy in the cloud or at another physical location.", + "backup_onboarding_2_description": "local copies on different devices. This includes the main files and a backup of those files locally.", + "backup_onboarding_3_description": "total copies of your data, including the original files. This includes 1 offsite copy and 2 local copies.", + "backup_onboarding_description": "A 3-2-1 backup strategy is recommended to protect your data. You should keep copies of your uploaded photos/videos as well as the Immich database for a comprehensive backup solution.", + "backup_onboarding_footer": "For more information about backing up Immich, please refer to the documentation.", + "backup_onboarding_parts_title": "A 3-2-1 backup includes:", + "backup_onboarding_title": "Backups", "backup_settings": "Database Dump Settings", "backup_settings_description": "Manage database dump settings.", "cleared_jobs": "Cleared jobs for: {job}", diff --git a/web/src/lib/components/onboarding-page/onboarding-backup.svelte b/web/src/lib/components/onboarding-page/onboarding-backup.svelte new file mode 100644 index 0000000000..1ae176e1ad --- /dev/null +++ b/web/src/lib/components/onboarding-page/onboarding-backup.svelte @@ -0,0 +1,62 @@ + + +
+ + + +

+ + {#snippet children({ message })} + + {message} + + {/snippet} + +

+
+ +

+ +

+ + + + 3 + + + + 2 + + + + 1 + + + +

+ + {#snippet children({ message })} + + {message} + + {/snippet} + +

+
+
diff --git a/web/src/routes/auth/onboarding/+page.svelte b/web/src/routes/auth/onboarding/+page.svelte index 9c39c284b2..679920c971 100644 --- a/web/src/routes/auth/onboarding/+page.svelte +++ b/web/src/routes/auth/onboarding/+page.svelte @@ -8,12 +8,13 @@ import OnboardingStorageTemplate from '$lib/components/onboarding-page/onboarding-storage-template.svelte'; import OnboardingTheme from '$lib/components/onboarding-page/onboarding-theme.svelte'; import OnboardingUserPrivacy from '$lib/components/onboarding-page/onboarding-user-privacy.svelte'; + import OnboardingBackup from '$lib/components/onboarding-page/onboarding-backup.svelte'; import { AppRoute, QueryParameter } from '$lib/constants'; import { OnboardingRole } from '$lib/models/onboarding-role'; import { retrieveServerConfig, retrieveSystemConfig, serverConfig } from '$lib/stores/server-config.store'; import { user } from '$lib/stores/user.store'; import { setUserOnboarding, updateAdminOnboarding } from '@immich/sdk'; - import { mdiHarddisk, mdiIncognito, mdiThemeLightDark, mdiTranslate } from '@mdi/js'; + import { mdiCloudUpload, mdiHarddisk, mdiIncognito, mdiThemeLightDark, mdiTranslate } from '@mdi/js'; import { onMount } from 'svelte'; import { t } from 'svelte-i18n'; @@ -68,6 +69,13 @@ title: $t('admin.storage_template_settings'), icon: mdiHarddisk, }, + { + name: 'backup', + component: OnboardingBackup, + role: OnboardingRole.SERVER, + title: $t('admin.backup_onboarding_title'), + icon: mdiCloudUpload, + }, ]); let index = $state(0); From 268b411a6fa1edf08457d96d6f87c14d71fb1177 Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Wed, 30 Jul 2025 08:27:04 +0530 Subject: [PATCH 110/748] fix: sync is_favorite from native (#20412) * feat: sync is_favorite from native * handle favorite during upload * Update mobile/ios/Runner/Sync/MessagesImpl.swift Co-authored-by: Alex --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Co-authored-by: Alex --- .../src/main/kotlin/app/alextran/immich/sync/Messages.g.kt | 7 +++++-- .../kotlin/app/alextran/immich/sync/MessagesImplBase.kt | 4 ++++ mobile/ios/Runner/Sync/Messages.g.swift | 6 +++++- mobile/ios/Runner/Sync/MessagesImpl.swift | 3 ++- mobile/lib/domain/services/local_sync.service.dart | 1 + .../repositories/local_album.repository.dart | 1 + mobile/lib/platform/native_sync_api.g.dart | 6 +++++- mobile/lib/services/upload.service.dart | 5 ++++- mobile/pigeon/native_sync_api.dart | 5 +++-- 9 files changed, 30 insertions(+), 8 deletions(-) diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt index b5ef90310e..201d0a43e1 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt @@ -88,7 +88,8 @@ data class PlatformAsset ( val width: Long? = null, val height: Long? = null, val durationInSeconds: Long, - val orientation: Long + val orientation: Long, + val isFavorite: Boolean ) { companion object { @@ -102,7 +103,8 @@ data class PlatformAsset ( val height = pigeonVar_list[6] as Long? val durationInSeconds = pigeonVar_list[7] as Long val orientation = pigeonVar_list[8] as Long - return PlatformAsset(id, name, type, createdAt, updatedAt, width, height, durationInSeconds, orientation) + val isFavorite = pigeonVar_list[9] as Boolean + return PlatformAsset(id, name, type, createdAt, updatedAt, width, height, durationInSeconds, orientation, isFavorite) } } fun toList(): List { @@ -116,6 +118,7 @@ data class PlatformAsset ( height, durationInSeconds, orientation, + isFavorite, ) } override fun equals(other: Any?): Boolean { diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt index 02cd54b8c3..d7073e7cfc 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt @@ -42,6 +42,7 @@ open class NativeSyncApiImplBase(context: Context) { MediaStore.MediaColumns.HEIGHT, MediaStore.MediaColumns.DURATION, MediaStore.MediaColumns.ORIENTATION, + MediaStore.MediaColumns.IS_FAVORITE, ) const val HASH_BUFFER_SIZE = 2 * 1024 * 1024 @@ -77,6 +78,7 @@ open class NativeSyncApiImplBase(context: Context) { val durationColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.DURATION) val orientationColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.ORIENTATION) + val favoriteColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.IS_FAVORITE) while (c.moveToNext()) { val id = c.getLong(idColumn).toString() @@ -105,6 +107,7 @@ open class NativeSyncApiImplBase(context: Context) { else c.getLong(durationColumn) / 1000 val bucketId = c.getString(bucketIdColumn) val orientation = c.getInt(orientationColumn) + val isFavorite = c.getInt(favoriteColumn) != 0; val asset = PlatformAsset( id, @@ -116,6 +119,7 @@ open class NativeSyncApiImplBase(context: Context) { height, duration, orientation.toLong(), + isFavorite, ) yield(AssetResult.ValidAsset(asset, bucketId)) } diff --git a/mobile/ios/Runner/Sync/Messages.g.swift b/mobile/ios/Runner/Sync/Messages.g.swift index e629604d6a..b7f4293836 100644 --- a/mobile/ios/Runner/Sync/Messages.g.swift +++ b/mobile/ios/Runner/Sync/Messages.g.swift @@ -139,6 +139,7 @@ struct PlatformAsset: Hashable { var height: Int64? = nil var durationInSeconds: Int64 var orientation: Int64 + var isFavorite: Bool // swift-format-ignore: AlwaysUseLowerCamelCase @@ -152,6 +153,7 @@ struct PlatformAsset: Hashable { let height: Int64? = nilOrValue(pigeonVar_list[6]) let durationInSeconds = pigeonVar_list[7] as! Int64 let orientation = pigeonVar_list[8] as! Int64 + let isFavorite = pigeonVar_list[9] as! Bool return PlatformAsset( id: id, @@ -162,7 +164,8 @@ struct PlatformAsset: Hashable { width: width, height: height, durationInSeconds: durationInSeconds, - orientation: orientation + orientation: orientation, + isFavorite: isFavorite ) } func toList() -> [Any?] { @@ -176,6 +179,7 @@ struct PlatformAsset: Hashable { height, durationInSeconds, orientation, + isFavorite, ] } static func == (lhs: PlatformAsset, rhs: PlatformAsset) -> Bool { diff --git a/mobile/ios/Runner/Sync/MessagesImpl.swift b/mobile/ios/Runner/Sync/MessagesImpl.swift index 459e29fa5a..b8d97b0a82 100644 --- a/mobile/ios/Runner/Sync/MessagesImpl.swift +++ b/mobile/ios/Runner/Sync/MessagesImpl.swift @@ -28,7 +28,8 @@ extension PHAsset { width: Int64(pixelWidth), height: Int64(pixelHeight), durationInSeconds: Int64(duration), - orientation: 0 + orientation: 0, + isFavorite: isFavorite ) } } diff --git a/mobile/lib/domain/services/local_sync.service.dart b/mobile/lib/domain/services/local_sync.service.dart index cb1bb40619..13ebecfd46 100644 --- a/mobile/lib/domain/services/local_sync.service.dart +++ b/mobile/lib/domain/services/local_sync.service.dart @@ -312,6 +312,7 @@ extension on Iterable { height: e.height, durationInSeconds: e.durationInSeconds, orientation: e.orientation, + isFavorite: e.isFavorite, ), ).toList(); } diff --git a/mobile/lib/infrastructure/repositories/local_album.repository.dart b/mobile/lib/infrastructure/repositories/local_album.repository.dart index feb25925f8..869d8f0dc8 100644 --- a/mobile/lib/infrastructure/repositories/local_album.repository.dart +++ b/mobile/lib/infrastructure/repositories/local_album.repository.dart @@ -236,6 +236,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { id: asset.id, orientation: Value(asset.orientation), checksum: const Value(null), + isFavorite: Value(asset.isFavorite), ); batch.insert<$LocalAssetEntityTable, LocalAssetEntityData>( _db.localAssetEntity, diff --git a/mobile/lib/platform/native_sync_api.g.dart b/mobile/lib/platform/native_sync_api.g.dart index 67a320a96d..3cbd08cd68 100644 --- a/mobile/lib/platform/native_sync_api.g.dart +++ b/mobile/lib/platform/native_sync_api.g.dart @@ -40,6 +40,7 @@ class PlatformAsset { this.height, required this.durationInSeconds, required this.orientation, + required this.isFavorite, }); String id; @@ -60,8 +61,10 @@ class PlatformAsset { int orientation; + bool isFavorite; + List _toList() { - return [id, name, type, createdAt, updatedAt, width, height, durationInSeconds, orientation]; + return [id, name, type, createdAt, updatedAt, width, height, durationInSeconds, orientation, isFavorite]; } Object encode() { @@ -80,6 +83,7 @@ class PlatformAsset { height: result[6] as int?, durationInSeconds: result[7]! as int, orientation: result[8]! as int, + isFavorite: result[9]! as bool, ); } diff --git a/mobile/lib/services/upload.service.dart b/mobile/lib/services/upload.service.dart index dca5c02feb..c41d2b2e5f 100644 --- a/mobile/lib/services/upload.service.dart +++ b/mobile/lib/services/upload.service.dart @@ -247,6 +247,7 @@ class UploadService { metadata: metadata, group: group, priority: priority, + isFavorite: asset.isFavorite, ); } @@ -270,6 +271,7 @@ class UploadService { fields: fields, group: kBackupLivePhotoGroup, priority: 0, // Highest priority to get upload immediately + isFavorite: asset.isFavorite, ); } @@ -281,6 +283,7 @@ class UploadService { String? deviceAssetId, String? metadata, int? priority, + bool? isFavorite, }) async { final serverEndpoint = Store.get(StoreKey.serverEndpoint); final url = Uri.parse('$serverEndpoint/assets').toString(); @@ -297,7 +300,7 @@ class UploadService { 'deviceId': deviceId, 'fileCreatedAt': fileCreatedAt.toUtc().toIso8601String(), 'fileModifiedAt': fileModifiedAt.toUtc().toIso8601String(), - 'isFavorite': 'false', + 'isFavorite': isFavorite?.toString() ?? 'false', 'duration': '0', if (fields != null) ...fields, }; diff --git a/mobile/pigeon/native_sync_api.dart b/mobile/pigeon/native_sync_api.dart index 4f14b7a0b9..e84c814c3d 100644 --- a/mobile/pigeon/native_sync_api.dart +++ b/mobile/pigeon/native_sync_api.dart @@ -5,8 +5,7 @@ import 'package:pigeon/pigeon.dart'; dartOut: 'lib/platform/native_sync_api.g.dart', swiftOut: 'ios/Runner/Sync/Messages.g.swift', swiftOptions: SwiftOptions(), - kotlinOut: - 'android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt', + kotlinOut: 'android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt', kotlinOptions: KotlinOptions(package: 'app.alextran.immich.sync'), dartOptions: DartOptions(), dartPackageName: 'immich_mobile', @@ -24,6 +23,7 @@ class PlatformAsset { final int? height; final int durationInSeconds; final int orientation; + final bool isFavorite; const PlatformAsset({ required this.id, @@ -35,6 +35,7 @@ class PlatformAsset { this.height, this.durationInSeconds = 0, this.orientation = 0, + this.isFavorite = false, }); } From 29f16c6a4718ab975960f2de732e85e54656300e Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 29 Jul 2025 22:07:53 -0500 Subject: [PATCH 111/748] feat: people page/sheet/detail (#20309) --- i18n/en.json | 2 + mobile/lib/domain/models/person.model.dart | 10 +- .../lib/domain/services/people.service.dart | 30 + .../lib/domain/services/timeline.service.dart | 3 + .../repositories/asset_face.repository.dart | 30 - .../repositories/people.repository.dart | 67 +++ .../repositories/person.repository.dart | 34 -- .../repositories/timeline.repository.dart | 83 +++ .../library/partner/drift_partner.page.dart | 2 +- .../pages/drift_library.page.dart | 8 +- .../pages/drift_people_collection.page.dart | 130 ++++ .../presentation/pages/drift_person.page.dart | 97 +++ .../asset_viewer/asset_viewer.page.dart | 6 +- .../asset_viewer/bottom_sheet.widget.dart | 4 +- ...art => sheet_location_details.widget.dart} | 0 .../sheet_people_details.widget.dart | 175 ++++++ .../partner_user_avatar.widget.dart | 0 .../person_edit_birthday_modal.widget.dart | 116 ++++ .../people/person_edit_name_modal.widget.dart | 82 +++ .../people/person_option_sheet.widget.dart | 36 ++ .../widgets/timeline/fixed/segment.model.dart | 13 +- .../infrastructure/asset_face.provider.dart | 7 - .../infrastructure/people.provider.dart | 24 + .../infrastructure/person.provider.dart | 5 - mobile/lib/providers/routes.provider.dart | 2 + .../repositories/person_api.repository.dart | 4 +- .../lib/routing/app_navigation_observer.dart | 1 + mobile/lib/routing/router.dart | 6 + mobile/lib/routing/router.gr.dart | 53 ++ mobile/lib/utils/people.utils.dart | 54 ++ .../widgets/common/person_sliver_app_bar.dart | 562 ++++++++++++++++++ .../photo_view/photo_view_gallery.dart | 4 +- mobile/pubspec.lock | 8 + mobile/pubspec.yaml | 1 + 34 files changed, 1562 insertions(+), 97 deletions(-) create mode 100644 mobile/lib/domain/services/people.service.dart delete mode 100644 mobile/lib/infrastructure/repositories/asset_face.repository.dart create mode 100644 mobile/lib/infrastructure/repositories/people.repository.dart delete mode 100644 mobile/lib/infrastructure/repositories/person.repository.dart create mode 100644 mobile/lib/presentation/pages/drift_people_collection.page.dart create mode 100644 mobile/lib/presentation/pages/drift_person.page.dart rename mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/{location_details.widget.dart => sheet_location_details.widget.dart} (100%) create mode 100644 mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart rename mobile/lib/presentation/widgets/{ => people}/partner_user_avatar.widget.dart (100%) create mode 100644 mobile/lib/presentation/widgets/people/person_edit_birthday_modal.widget.dart create mode 100644 mobile/lib/presentation/widgets/people/person_edit_name_modal.widget.dart create mode 100644 mobile/lib/presentation/widgets/people/person_option_sheet.widget.dart delete mode 100644 mobile/lib/providers/infrastructure/asset_face.provider.dart create mode 100644 mobile/lib/providers/infrastructure/people.provider.dart delete mode 100644 mobile/lib/providers/infrastructure/person.provider.dart create mode 100644 mobile/lib/utils/people.utils.dart create mode 100644 mobile/lib/widgets/common/person_sliver_app_bar.dart diff --git a/i18n/en.json b/i18n/en.json index 49d24a2f84..c445110996 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -14,6 +14,7 @@ "add_a_location": "Add a location", "add_a_name": "Add a name", "add_a_title": "Add a title", + "add_birthday": "Add a birthday", "add_endpoint": "Add endpoint", "add_exclusion_pattern": "Add exclusion pattern", "add_import_path": "Add import path", @@ -828,6 +829,7 @@ "edit": "Edit", "edit_album": "Edit album", "edit_avatar": "Edit avatar", + "edit_birthday": "Edit Birthday", "edit_date": "Edit date", "edit_date_and_time": "Edit date and time", "edit_description": "Edit description", diff --git a/mobile/lib/domain/models/person.model.dart b/mobile/lib/domain/models/person.model.dart index 784bb564fe..7559720c45 100644 --- a/mobile/lib/domain/models/person.model.dart +++ b/mobile/lib/domain/models/person.model.dart @@ -91,7 +91,7 @@ class PersonDto { } // Model for a person stored in the server -class Person { +class DriftPerson { final String id; final DateTime createdAt; final DateTime updatedAt; @@ -103,7 +103,7 @@ class Person { final String? color; final DateTime? birthDate; - const Person({ + const DriftPerson({ required this.id, required this.createdAt, required this.updatedAt, @@ -116,7 +116,7 @@ class Person { this.birthDate, }); - Person copyWith({ + DriftPerson copyWith({ String? id, DateTime? createdAt, DateTime? updatedAt, @@ -128,7 +128,7 @@ class Person { String? color, DateTime? birthDate, }) { - return Person( + return DriftPerson( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, updatedAt: updatedAt ?? this.updatedAt, @@ -159,7 +159,7 @@ class Person { } @override - bool operator ==(covariant Person other) { + bool operator ==(covariant DriftPerson other) { if (identical(this, other)) return true; return other.id == id && diff --git a/mobile/lib/domain/services/people.service.dart b/mobile/lib/domain/services/people.service.dart new file mode 100644 index 0000000000..d45f710d7b --- /dev/null +++ b/mobile/lib/domain/services/people.service.dart @@ -0,0 +1,30 @@ +import 'dart:async'; + +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/infrastructure/repositories/people.repository.dart'; +import 'package:immich_mobile/repositories/person_api.repository.dart'; + +class DriftPeopleService { + final DriftPeopleRepository _repository; + final PersonApiRepository _personApiRepository; + + const DriftPeopleService(this._repository, this._personApiRepository); + + Future> getAssetPeople(String assetId) { + return _repository.getAssetPeople(assetId); + } + + Future> getAllPeople() { + return _repository.getAllPeople(); + } + + Future updateName(String personId, String name) async { + await _personApiRepository.update(personId, name: name); + return _repository.updateName(personId, name); + } + + Future updateBrithday(String personId, DateTime birthday) async { + await _personApiRepository.update(personId, birthday: birthday); + return _repository.updateBirthday(personId, birthday); + } +} diff --git a/mobile/lib/domain/services/timeline.service.dart b/mobile/lib/domain/services/timeline.service.dart index 7c22fb786d..9fa4106d17 100644 --- a/mobile/lib/domain/services/timeline.service.dart +++ b/mobile/lib/domain/services/timeline.service.dart @@ -53,6 +53,9 @@ class TimelineFactory { TimelineService place(String place) => TimelineService(_timelineRepository.place(place, groupBy)); + TimelineService person(String userId, String personId) => + TimelineService(_timelineRepository.person(userId, personId, groupBy)); + TimelineService fromAssets(List assets) => TimelineService(_timelineRepository.fromAssets(assets)); } diff --git a/mobile/lib/infrastructure/repositories/asset_face.repository.dart b/mobile/lib/infrastructure/repositories/asset_face.repository.dart deleted file mode 100644 index 7b3b97058b..0000000000 --- a/mobile/lib/infrastructure/repositories/asset_face.repository.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:immich_mobile/domain/models/asset_face.model.dart'; -import 'package:immich_mobile/infrastructure/entities/asset_face.entity.drift.dart'; -import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; - -class DriftAssetFaceRepository extends DriftDatabaseRepository { - final Drift _db; - const DriftAssetFaceRepository(this._db) : super(_db); - - Future> getAll() { - return _db.assetFaceEntity.select().map((assetFace) => assetFace.toDto()).get(); - } -} - -extension on AssetFaceEntityData { - AssetFace toDto() { - return AssetFace( - id: id, - assetId: assetId, - personId: personId, - imageWidth: imageWidth, - imageHeight: imageHeight, - boundingBoxX1: boundingBoxX1, - boundingBoxY1: boundingBoxY1, - boundingBoxX2: boundingBoxX2, - boundingBoxY2: boundingBoxY2, - sourceType: sourceType, - ); - } -} diff --git a/mobile/lib/infrastructure/repositories/people.repository.dart b/mobile/lib/infrastructure/repositories/people.repository.dart new file mode 100644 index 0000000000..9c6ed74636 --- /dev/null +++ b/mobile/lib/infrastructure/repositories/people.repository.dart @@ -0,0 +1,67 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; + +class DriftPeopleRepository extends DriftDatabaseRepository { + final Drift _db; + const DriftPeopleRepository(this._db) : super(_db); + + Future> getAssetPeople(String assetId) async { + final query = _db.select(_db.assetFaceEntity).join([ + innerJoin(_db.personEntity, _db.personEntity.id.equalsExp(_db.assetFaceEntity.personId)), + ])..where(_db.assetFaceEntity.assetId.equals(assetId) & _db.personEntity.isHidden.equals(false)); + + return query.map((row) { + final person = row.readTable(_db.personEntity); + return person.toDto(); + }).get(); + } + + Future> getAllPeople() async { + final query = + _db.select(_db.personEntity).join([ + leftOuterJoin(_db.assetFaceEntity, _db.assetFaceEntity.personId.equalsExp(_db.personEntity.id)), + ]) + ..where(_db.personEntity.isHidden.equals(false)) + ..groupBy([_db.personEntity.id]) + ..orderBy([ + OrderingTerm(expression: _db.personEntity.name.equals('').not(), mode: OrderingMode.desc), + OrderingTerm(expression: _db.assetFaceEntity.id.count(), mode: OrderingMode.desc), + ]); + + return query.map((row) { + final person = row.readTable(_db.personEntity); + return person.toDto(); + }).get(); + } + + Future updateName(String personId, String name) { + final query = _db.update(_db.personEntity)..where((row) => row.id.equals(personId)); + + return query.write(PersonEntityCompanion(name: Value(name), updatedAt: Value(DateTime.now()))); + } + + Future updateBirthday(String personId, DateTime birthday) { + final query = _db.update(_db.personEntity)..where((row) => row.id.equals(personId)); + + return query.write(PersonEntityCompanion(birthDate: Value(birthday), updatedAt: Value(DateTime.now()))); + } +} + +extension on PersonEntityData { + DriftPerson toDto() { + return DriftPerson( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + name: name, + faceAssetId: faceAssetId, + isFavorite: isFavorite, + isHidden: isHidden, + color: color, + birthDate: birthDate, + ); + } +} diff --git a/mobile/lib/infrastructure/repositories/person.repository.dart b/mobile/lib/infrastructure/repositories/person.repository.dart deleted file mode 100644 index 045fab4942..0000000000 --- a/mobile/lib/infrastructure/repositories/person.repository.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:immich_mobile/domain/models/person.model.dart'; -import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart'; -import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; - -class DriftPersonRepository extends DriftDatabaseRepository { - final Drift _db; - const DriftPersonRepository(this._db) : super(_db); - - Future> getAll(String userId) { - final query = _db.personEntity.select()..where((e) => e.ownerId.equals(userId)); - - return query.map((person) { - return person.toDto(); - }).get(); - } -} - -extension on PersonEntityData { - Person toDto() { - return Person( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - name: name, - faceAssetId: faceAssetId, - isFavorite: isFavorite, - isHidden: isHidden, - color: color, - birthDate: birthDate, - ); - } -} diff --git a/mobile/lib/infrastructure/repositories/timeline.repository.dart b/mobile/lib/infrastructure/repositories/timeline.repository.dart index dcd10faa64..663db9b82f 100644 --- a/mobile/lib/infrastructure/repositories/timeline.repository.dart +++ b/mobile/lib/infrastructure/repositories/timeline.repository.dart @@ -292,6 +292,11 @@ class DriftTimelineRepository extends DriftDatabaseRepository { assetSource: (offset, count) => _getPlaceBucketAssets(place, offset: offset, count: count), ); + TimelineQuery person(String userId, String personId, GroupAssetsBy groupBy) => ( + bucketSource: () => _watchPersonBucket(userId, personId, groupBy: groupBy), + assetSource: (offset, count) => _getPersonBucketAssets(userId, personId, offset: offset, count: count), + ); + Stream> _watchPlaceBucket(String place, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { if (groupBy == GroupAssetsBy.none) { // TODO: implement GroupAssetBy for place @@ -344,6 +349,84 @@ class DriftTimelineRepository extends DriftDatabaseRepository { return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get(); } + Stream> _watchPersonBucket(String userId, String personId, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { + if (groupBy == GroupAssetsBy.none) { + final query = _db.remoteAssetEntity.selectOnly() + ..addColumns([_db.remoteAssetEntity.id.count()]) + ..join([ + innerJoin( + _db.assetFaceEntity, + _db.assetFaceEntity.assetId.equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + ]) + ..where( + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.ownerId.equals(userId) & + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) & + _db.assetFaceEntity.personId.equals(personId), + ); + + return query.map((row) { + final count = row.read(_db.remoteAssetEntity.id.count())!; + return _generateBuckets(count); + }).watchSingle(); + } + + final assetCountExp = _db.remoteAssetEntity.id.count(); + final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy); + + final query = _db.remoteAssetEntity.selectOnly() + ..addColumns([assetCountExp, dateExp]) + ..join([ + innerJoin( + _db.assetFaceEntity, + _db.assetFaceEntity.assetId.equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + ]) + ..where( + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.ownerId.equals(userId) & + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) & + _db.assetFaceEntity.personId.equals(personId), + ) + ..groupBy([dateExp]) + ..orderBy([OrderingTerm.desc(dateExp)]); + + return query.map((row) { + final timeline = row.read(dateExp)!.dateFmt(groupBy); + final assetCount = row.read(assetCountExp)!; + return TimeBucket(date: timeline, assetCount: assetCount); + }).watch(); + } + + Future> _getPersonBucketAssets( + String userId, + String personId, { + required int offset, + required int count, + }) { + final query = + _db.remoteAssetEntity.select().join([ + innerJoin( + _db.assetFaceEntity, + _db.assetFaceEntity.assetId.equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + ]) + ..where( + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.ownerId.equals(userId) & + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) & + _db.assetFaceEntity.personId.equals(personId), + ) + ..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)]) + ..limit(count, offset: offset); + + return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get(); + } + TimelineQuery _remoteQueryBuilder({ required Expression Function($RemoteAssetEntityTable row) filter, GroupAssetsBy groupBy = GroupAssetsBy.day, diff --git a/mobile/lib/pages/library/partner/drift_partner.page.dart b/mobile/lib/pages/library/partner/drift_partner.page.dart index 171fe0ea0d..834e55ffd4 100644 --- a/mobile/lib/pages/library/partner/drift_partner.page.dart +++ b/mobile/lib/pages/library/partner/drift_partner.page.dart @@ -5,7 +5,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; -import 'package:immich_mobile/presentation/widgets/partner_user_avatar.widget.dart'; +import 'package:immich_mobile/presentation/widgets/people/partner_user_avatar.widget.dart'; import 'package:immich_mobile/providers/infrastructure/partner.provider.dart'; import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; import 'package:immich_mobile/widgets/common/confirm_dialog.dart'; diff --git a/mobile/lib/presentation/pages/drift_library.page.dart b/mobile/lib/presentation/pages/drift_library.page.dart index 28fa6c6a16..af7014bbdc 100644 --- a/mobile/lib/presentation/pages/drift_library.page.dart +++ b/mobile/lib/presentation/pages/drift_library.page.dart @@ -6,10 +6,10 @@ import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/images/local_album_thumbnail.widget.dart'; -import 'package:immich_mobile/presentation/widgets/partner_user_avatar.widget.dart'; +import 'package:immich_mobile/presentation/widgets/people/partner_user_avatar.widget.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/partner.provider.dart'; -import 'package:immich_mobile/providers/search/people.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/people.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/api.service.dart'; @@ -144,7 +144,7 @@ class _PeopleCollectionCard extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final people = ref.watch(getAllPeopleProvider); + final people = ref.watch(driftGetAllPeopleProvider); return LayoutBuilder( builder: (context, constraints) { @@ -153,7 +153,7 @@ class _PeopleCollectionCard extends ConsumerWidget { final size = context.width * widthFactor - 20.0; return GestureDetector( - onTap: () => context.pushRoute(const PeopleCollectionRoute()), + onTap: () => context.pushRoute(const DriftPeopleCollectionRoute()), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/mobile/lib/presentation/pages/drift_people_collection.page.dart b/mobile/lib/presentation/pages/drift_people_collection.page.dart new file mode 100644 index 0000000000..ca4e20aad0 --- /dev/null +++ b/mobile/lib/presentation/pages/drift_people_collection.page.dart @@ -0,0 +1,130 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/providers/infrastructure/people.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/utils/image_url_builder.dart'; +import 'package:immich_mobile/utils/people.utils.dart'; +import 'package:immich_mobile/widgets/common/search_field.dart'; + +@RoutePage() +class DriftPeopleCollectionPage extends ConsumerStatefulWidget { + const DriftPeopleCollectionPage({super.key}); + + @override + ConsumerState createState() => _DriftPeopleCollectionPageState(); +} + +class _DriftPeopleCollectionPageState extends ConsumerState { + final FocusNode _formFocus = FocusNode(); + String? _search; + + @override + void dispose() { + _formFocus.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final people = ref.watch(driftGetAllPeopleProvider); + final headers = ApiService.getRequestHeaders(); + + return LayoutBuilder( + builder: (context, constraints) { + final isTablet = constraints.maxWidth > 600; + final isPortrait = context.orientation == Orientation.portrait; + + return Scaffold( + appBar: AppBar( + automaticallyImplyLeading: _search == null, + title: _search != null + ? SearchField( + focusNode: _formFocus, + onTapOutside: (_) => _formFocus.unfocus(), + onChanged: (value) => setState(() => _search = value), + filled: true, + hintText: 'filter_people'.tr(), + autofocus: true, + ) + : Text('people'.tr()), + actions: [ + IconButton( + icon: Icon(_search != null ? Icons.close : Icons.search), + onPressed: () { + setState(() => _search = _search == null ? '' : null); + }, + ), + ], + ), + body: SafeArea( + child: people.when( + data: (people) { + if (_search != null) { + people = people.where((person) { + return person.name.toLowerCase().contains(_search!.toLowerCase()); + }).toList(); + } + return GridView.builder( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: isTablet ? 6 : 3, + childAspectRatio: 0.85, + mainAxisSpacing: isPortrait && isTablet ? 36 : 0, + ), + padding: const EdgeInsets.symmetric(vertical: 32), + itemCount: people.length, + itemBuilder: (context, index) { + final person = people[index]; + + return Column( + children: [ + GestureDetector( + onTap: () { + context.pushRoute(DriftPersonRoute(person: person)); + }, + child: Material( + shape: const CircleBorder(side: BorderSide.none), + elevation: 3, + child: CircleAvatar( + maxRadius: isTablet ? 100 / 2 : 96 / 2, + backgroundImage: NetworkImage(getFaceThumbnailUrl(person.id), headers: headers), + ), + ), + ), + const SizedBox(height: 12), + GestureDetector( + onTap: () => showNameEditModal(context, person), + child: person.name.isEmpty + ? Text( + 'add_a_name'.tr(), + style: context.textTheme.titleSmall?.copyWith( + fontWeight: FontWeight.w500, + color: context.colorScheme.primary, + ), + ) + : Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Text( + person.name, + overflow: TextOverflow.ellipsis, + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), + ), + ), + ), + ], + ); + }, + ); + }, + error: (error, stack) => const Text("error"), + loading: () => const Center(child: CircularProgressIndicator()), + ), + ), + ); + }, + ); + } +} diff --git a/mobile/lib/presentation/pages/drift_person.page.dart b/mobile/lib/presentation/pages/drift_person.page.dart new file mode 100644 index 0000000000..f3146505ae --- /dev/null +++ b/mobile/lib/presentation/pages/drift_person.page.dart @@ -0,0 +1,97 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/people/person_option_sheet.widget.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/utils/people.utils.dart'; +import 'package:immich_mobile/widgets/common/person_sliver_app_bar.dart'; + +@RoutePage() +class DriftPersonPage extends ConsumerStatefulWidget { + final DriftPerson person; + + const DriftPersonPage({super.key, required this.person}); + + @override + ConsumerState createState() => _DriftPersonPageState(); +} + +class _DriftPersonPageState extends ConsumerState { + late DriftPerson _person; + + @override + initState() { + super.initState(); + _person = widget.person; + } + + Future handleEditName(BuildContext context) async { + final newName = await showNameEditModal(context, _person); + + if (newName != null && newName.isNotEmpty) { + setState(() { + _person = _person.copyWith(name: newName); + }); + } + } + + Future handleEditBirthday(BuildContext context) async { + final birthday = await showBirthdayEditModal(context, _person); + + if (birthday != null) { + setState(() { + _person = _person.copyWith(birthDate: birthday); + }); + } + } + + void showOptionSheet(BuildContext context) { + showModalBottomSheet( + context: context, + backgroundColor: context.colorScheme.surface, + isScrollControlled: false, + builder: (context) { + return PersonOptionSheet( + onEditName: () async { + await handleEditName(context); + context.pop(); + }, + onEditBirthday: () async { + await handleEditBirthday(context); + context.pop(); + }, + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + return ProviderScope( + overrides: [ + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to view person timeline'); + } + + final timelineService = ref.watch(timelineFactoryProvider).person(user.id, _person.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), + ], + child: Timeline( + appBar: PersonSliverAppBar( + person: _person, + onNameTap: () => handleEditName(context), + onBirthdayTap: () => handleEditBirthday(context), + onShowOptions: () => showOptionSheet(context), + ), + ), + ); + } +} diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart index c6b2360c9d..2fa54ad65d 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart @@ -218,7 +218,7 @@ class _AssetViewerState extends ConsumerState { void _onPageBuild(PhotoViewControllerBase controller) { viewController ??= controller; - if (showingBottomSheet) { + if (showingBottomSheet && bottomSheetController.isAttached) { final verticalOffset = (context.height * bottomSheetController.size) - (context.height * _kBottomSheetMinimumExtent); controller.position = Offset(0, -verticalOffset); @@ -463,7 +463,9 @@ class _AssetViewerState extends ConsumerState { } void _snapBottomSheet() { - if (bottomSheetController.size > _kBottomSheetSnapExtent || bottomSheetController.size < 0.4) { + if (!bottomSheetController.isAttached || + bottomSheetController.size > _kBottomSheetSnapExtent || + bottomSheetController.size < 0.4) { return; } isSnapping = true; diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart index f27261a357..17b4cdb214 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart @@ -16,7 +16,8 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_b import 'package:immich_mobile/presentation/widgets/action_buttons/share_link_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; -import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet/sheet_location_details.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart'; import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; @@ -150,6 +151,7 @@ class _AssetDetailBottomSheet extends ConsumerWidget { titleStyle: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600), ), if (exifInfo != null) _SheetAssetDescription(exif: exifInfo), + const SheetPeopleDetails(), const SheetLocationDetails(), // Details header _SheetTile( diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_location_details.widget.dart similarity index 100% rename from mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart rename to mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_location_details.widget.dart diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart new file mode 100644 index 0000000000..4cfd95b25c --- /dev/null +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart @@ -0,0 +1,175 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/people/person_edit_name_modal.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/people.provider.dart'; +import 'package:immich_mobile/providers/routes.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/utils/people.utils.dart'; +import 'package:immich_mobile/utils/image_url_builder.dart'; + +class SheetPeopleDetails extends ConsumerStatefulWidget { + const SheetPeopleDetails({super.key}); + + @override + ConsumerState createState() => _SheetPeopleDetailsState(); +} + +class _SheetPeopleDetailsState extends ConsumerState { + @override + Widget build(BuildContext context) { + final asset = ref.watch(currentAssetNotifier); + if (asset is! RemoteAsset) { + return const SizedBox.shrink(); + } + + final peopleFuture = ref.watch(driftPeopleAssetProvider(asset.id)); + + Future showNameEditModal(DriftPerson person) async { + await showDialog( + context: context, + useRootNavigator: false, + builder: (BuildContext context) { + return DriftPersonNameEditForm(person: person); + }, + ); + + ref.invalidate(driftPeopleAssetProvider(asset.id)); + } + + return peopleFuture.when( + data: (people) { + return AnimatedCrossFade( + firstChild: const SizedBox.shrink(), + secondChild: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(left: 16, top: 16, bottom: 16), + child: Text( + "people".t(context: context).toUpperCase(), + style: context.textTheme.labelMedium?.copyWith( + color: context.textTheme.labelMedium?.color?.withAlpha(200), + fontWeight: FontWeight.w600, + ), + ), + ), + SizedBox( + height: 150, + child: ListView( + padding: const EdgeInsets.only(left: 16.0), + scrollDirection: Axis.horizontal, + children: [ + for (final person in people) + _PeopleAvatar( + person: person, + assetFileCreatedAt: asset.createdAt, + onTap: () { + final previousRouteData = ref.read(previousRouteDataProvider); + final previousRouteArgs = previousRouteData?.arguments; + + // Prevent circular navigation + if (previousRouteArgs is DriftPersonRouteArgs && previousRouteArgs.person.id == person.id) { + context.back(); + return; + } + context.back(); + context.pushRoute(DriftPersonRoute(person: person)); + }, + onNameTap: () => showNameEditModal(person), + ), + ], + ), + ), + ], + ), + crossFadeState: people.isEmpty ? CrossFadeState.showFirst : CrossFadeState.showSecond, + duration: Durations.short4, + ); + }, + error: (error, stack) => Text("error_loading_people".t(context: context), style: context.textTheme.bodyMedium), + loading: () => const SizedBox.shrink(), + ); + } +} + +class _PeopleAvatar extends StatelessWidget { + final DriftPerson person; + final DateTime assetFileCreatedAt; + final VoidCallback? onTap; + final VoidCallback? onNameTap; + final double imageSize = 96; + + const _PeopleAvatar({required this.person, required this.assetFileCreatedAt, this.onTap, this.onNameTap}); + + @override + Widget build(BuildContext context) { + final headers = ApiService.getRequestHeaders(); + + return ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 96), + child: Padding( + padding: const EdgeInsets.only(right: 16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + GestureDetector( + onTap: onTap, + child: SizedBox( + height: imageSize, + child: Material( + shape: CircleBorder(side: BorderSide(color: context.primaryColor.withAlpha(50), width: 1.0)), + shadowColor: context.colorScheme.shadow, + elevation: 3, + child: CircleAvatar( + maxRadius: imageSize / 2, + backgroundImage: NetworkImage(getFaceThumbnailUrl(person.id), headers: headers), + ), + ), + ), + ), + const SizedBox(height: 4), + if (person.name.isEmpty) + GestureDetector( + onTap: () => onNameTap?.call(), + child: Text( + "add_a_name".t(context: context), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), + maxLines: 2, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, + ), + ) + else + Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + person.name, + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + style: context.textTheme.labelLarge, + maxLines: 1, + ), + if (person.birthDate != null) + Text( + formatAge(person.birthDate!, assetFileCreatedAt), + textAlign: TextAlign.center, + style: context.textTheme.bodyMedium?.copyWith( + color: context.textTheme.bodyMedium?.color?.withAlpha(175), + ), + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/mobile/lib/presentation/widgets/partner_user_avatar.widget.dart b/mobile/lib/presentation/widgets/people/partner_user_avatar.widget.dart similarity index 100% rename from mobile/lib/presentation/widgets/partner_user_avatar.widget.dart rename to mobile/lib/presentation/widgets/people/partner_user_avatar.widget.dart diff --git a/mobile/lib/presentation/widgets/people/person_edit_birthday_modal.widget.dart b/mobile/lib/presentation/widgets/people/person_edit_birthday_modal.widget.dart new file mode 100644 index 0000000000..8d05244617 --- /dev/null +++ b/mobile/lib/presentation/widgets/people/person_edit_birthday_modal.widget.dart @@ -0,0 +1,116 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/infrastructure/people.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; +import 'package:scroll_date_picker/scroll_date_picker.dart'; + +class DriftPersonBirthdayEditForm extends ConsumerStatefulWidget { + final DriftPerson person; + + const DriftPersonBirthdayEditForm({super.key, required this.person}); + + @override + ConsumerState createState() => _DriftPersonNameEditFormState(); +} + +class _DriftPersonNameEditFormState extends ConsumerState { + late DateTime _selectedDate; + + @override + void initState() { + super.initState(); + _selectedDate = widget.person.birthDate ?? DateTime.now(); + } + + void saveBirthday() async { + try { + final result = await ref.read(driftPeopleServiceProvider).updateBrithday(widget.person.id, _selectedDate); + + if (result != 0) { + ref.invalidate(driftGetAllPeopleProvider); + context.pop(_selectedDate); + } + } catch (error) { + debugPrint('Error updating birthday: $error'); + + if (!context.mounted) { + return; + } + + ImmichToast.show( + context: context, + msg: 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: ToastType.error, + ); + } + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text( + "edit_birthday".t(context: context), + style: const TextStyle(fontWeight: FontWeight.bold), + ), + content: SizedBox( + width: double.maxFinite, + height: 300, + child: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(16.0)), + + child: ScrollDatePicker( + options: DatePickerOptions( + backgroundColor: context.colorScheme.surfaceContainerHigh, + itemExtent: 50, + diameterRatio: 5, + ), + scrollViewOptions: DatePickerScrollViewOptions( + day: ScrollViewDetailOptions( + margin: const EdgeInsets.all(12), + selectedTextStyle: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold, fontSize: 16), + ), + month: ScrollViewDetailOptions( + margin: const EdgeInsets.all(12), + selectedTextStyle: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold, fontSize: 16), + ), + year: ScrollViewDetailOptions( + margin: const EdgeInsets.all(12), + selectedTextStyle: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold, fontSize: 16), + ), + ), + selectedDate: _selectedDate, + locale: context.locale, + minimumDate: DateTime(1800, 1, 1), + onDateTimeChanged: (DateTime value) { + setState(() { + _selectedDate = value; + }); + }, + ), + ), + ), + actions: [ + TextButton( + onPressed: () => context.pop(null), + child: Text( + "cancel", + style: TextStyle(color: Colors.red[300], fontWeight: FontWeight.bold), + ).tr(), + ), + TextButton( + onPressed: () => saveBirthday(), + child: Text( + "save", + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold), + ).tr(), + ), + ], + ); + } +} diff --git a/mobile/lib/presentation/widgets/people/person_edit_name_modal.widget.dart b/mobile/lib/presentation/widgets/people/person_edit_name_modal.widget.dart new file mode 100644 index 0000000000..46fd683b81 --- /dev/null +++ b/mobile/lib/presentation/widgets/people/person_edit_name_modal.widget.dart @@ -0,0 +1,82 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/infrastructure/people.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; + +class DriftPersonNameEditForm extends ConsumerStatefulWidget { + final DriftPerson person; + + const DriftPersonNameEditForm({super.key, required this.person}); + + @override + ConsumerState createState() => _DriftPersonNameEditFormState(); +} + +class _DriftPersonNameEditFormState extends ConsumerState { + late TextEditingController _formController; + + @override + void initState() { + super.initState(); + _formController = TextEditingController(text: widget.person.name); + } + + void onEdit(String personId, String newName) async { + try { + final result = await ref.read(driftPeopleServiceProvider).updateName(personId, newName); + if (result != 0) { + ref.invalidate(driftGetAllPeopleProvider); + context.pop(newName); + } + } catch (error) { + debugPrint('Error updating name: $error'); + + if (!context.mounted) { + return; + } + + ImmichToast.show( + context: context, + msg: 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: ToastType.error, + ); + } + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text("edit_name", style: TextStyle(fontWeight: FontWeight.bold)).tr(), + content: SingleChildScrollView( + child: TextFormField( + controller: _formController, + textCapitalization: TextCapitalization.words, + autofocus: true, + decoration: InputDecoration(hintText: 'name'.tr(), border: const OutlineInputBorder()), + ), + ), + actions: [ + TextButton( + onPressed: () => context.pop(null), + child: Text( + "cancel", + style: TextStyle(color: Colors.red[300], fontWeight: FontWeight.bold), + ).tr(), + ), + TextButton( + onPressed: () => onEdit(widget.person.id, _formController.text), + child: Text( + "save", + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold), + ).tr(), + ), + ], + ); + } +} diff --git a/mobile/lib/presentation/widgets/people/person_option_sheet.widget.dart b/mobile/lib/presentation/widgets/people/person_option_sheet.widget.dart new file mode 100644 index 0000000000..6d46b37761 --- /dev/null +++ b/mobile/lib/presentation/widgets/people/person_option_sheet.widget.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; + +class PersonOptionSheet extends ConsumerWidget { + const PersonOptionSheet({super.key, this.onEditName, this.onEditBirthday}); + + final VoidCallback? onEditName; + final VoidCallback? onEditBirthday; + + @override + Widget build(BuildContext context, WidgetRef ref) { + TextStyle textStyle = Theme.of(context).textTheme.bodyLarge!.copyWith(fontWeight: FontWeight.w600); + + return SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 24.0), + child: ListView( + shrinkWrap: true, + children: [ + ListTile( + leading: const Icon(Icons.edit), + title: Text('edit_name'.t(context: context), style: textStyle), + onTap: onEditName, + ), + ListTile( + leading: const Icon(Icons.cake), + title: Text('edit_birthday'.t(context: context), style: textStyle), + onTap: onEditBirthday, + ), + ], + ), + ), + ); + } +} diff --git a/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart b/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart index ea5f55b35e..a5f0c19eb8 100644 --- a/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart +++ b/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart @@ -4,6 +4,7 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/widgets.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/services/timeline.service.dart'; import 'package:immich_mobile/presentation/widgets/images/thumbnail_tile.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/fixed/row.dart'; import 'package:immich_mobile/presentation/widgets/timeline/header.widget.dart'; @@ -99,7 +100,7 @@ class _FixedSegmentRow extends ConsumerWidget { } if (timelineService.hasRange(assetIndex, assetCount)) { - return _buildAssetRow(context, timelineService.getAssets(assetIndex, assetCount)); + return _buildAssetRow(context, timelineService.getAssets(assetIndex, assetCount), timelineService); } return FutureBuilder>( @@ -108,7 +109,7 @@ class _FixedSegmentRow extends ConsumerWidget { if (snapshot.connectionState != ConnectionState.done) { return _buildPlaceholder(context); } - return _buildAssetRow(context, snapshot.requireData); + return _buildAssetRow(context, snapshot.requireData, timelineService); }, ); } @@ -117,14 +118,18 @@ class _FixedSegmentRow extends ConsumerWidget { return SegmentBuilder.buildPlaceholder(context, assetCount, size: Size.square(tileHeight), spacing: spacing); } - Widget _buildAssetRow(BuildContext context, List assets) { + Widget _buildAssetRow(BuildContext context, List assets, TimelineService timelineService) { return FixedTimelineRow( dimension: tileHeight, spacing: spacing, textDirection: Directionality.of(context), children: [ for (int i = 0; i < assets.length; i++) - _AssetTileWidget(key: ValueKey(assets[i].heroTag), asset: assets[i], assetIndex: assetIndex + i), + _AssetTileWidget( + key: ValueKey(Object.hash(assets[i].heroTag, assetIndex + i, timelineService.hashCode)), + asset: assets[i], + assetIndex: assetIndex + i, + ), ], ); } diff --git a/mobile/lib/providers/infrastructure/asset_face.provider.dart b/mobile/lib/providers/infrastructure/asset_face.provider.dart deleted file mode 100644 index 386609ba94..0000000000 --- a/mobile/lib/providers/infrastructure/asset_face.provider.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/infrastructure/repositories/asset_face.repository.dart'; -import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; - -final driftAssetFaceProvider = Provider( - (ref) => DriftAssetFaceRepository(ref.watch(driftProvider)), -); diff --git a/mobile/lib/providers/infrastructure/people.provider.dart b/mobile/lib/providers/infrastructure/people.provider.dart new file mode 100644 index 0000000000..94a1b2447f --- /dev/null +++ b/mobile/lib/providers/infrastructure/people.provider.dart @@ -0,0 +1,24 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/domain/services/people.service.dart'; +import 'package:immich_mobile/infrastructure/repositories/people.repository.dart'; +import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/repositories/person_api.repository.dart'; + +final driftPeopleRepositoryProvider = Provider( + (ref) => DriftPeopleRepository(ref.watch(driftProvider)), +); + +final driftPeopleServiceProvider = Provider( + (ref) => DriftPeopleService(ref.watch(driftPeopleRepositoryProvider), ref.watch(personApiRepositoryProvider)), +); + +final driftPeopleAssetProvider = FutureProvider.family, String>((ref, assetId) async { + final service = ref.watch(driftPeopleServiceProvider); + return service.getAssetPeople(assetId); +}); + +final driftGetAllPeopleProvider = FutureProvider>((ref) async { + final service = ref.watch(driftPeopleServiceProvider); + return service.getAllPeople(); +}); diff --git a/mobile/lib/providers/infrastructure/person.provider.dart b/mobile/lib/providers/infrastructure/person.provider.dart deleted file mode 100644 index ac8a457e3a..0000000000 --- a/mobile/lib/providers/infrastructure/person.provider.dart +++ /dev/null @@ -1,5 +0,0 @@ -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/infrastructure/repositories/person.repository.dart'; -import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; - -final driftPersonProvider = Provider((ref) => DriftPersonRepository(ref.watch(driftProvider))); diff --git a/mobile/lib/providers/routes.provider.dart b/mobile/lib/providers/routes.provider.dart index 52adabe233..c51f67bc0e 100644 --- a/mobile/lib/providers/routes.provider.dart +++ b/mobile/lib/providers/routes.provider.dart @@ -1,5 +1,7 @@ +import 'package:flutter/widgets.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; final inLockedViewProvider = StateProvider((ref) => false); final currentRouteNameProvider = StateProvider((ref) => null); final previousRouteNameProvider = StateProvider((ref) => null); +final previousRouteDataProvider = StateProvider((ref) => null); diff --git a/mobile/lib/repositories/person_api.repository.dart b/mobile/lib/repositories/person_api.repository.dart index 545ad06741..04e4bd2a2c 100644 --- a/mobile/lib/repositories/person_api.repository.dart +++ b/mobile/lib/repositories/person_api.repository.dart @@ -16,8 +16,8 @@ class PersonApiRepository extends ApiRepository { return dto.people.map(_toPerson).toList(); } - Future update(String id, {String? name}) async { - final dto = await checkNull(_api.updatePerson(id, PersonUpdateDto(name: name))); + Future update(String id, {String? name, DateTime? birthday}) async { + final dto = await checkNull(_api.updatePerson(id, PersonUpdateDto(name: name, birthDate: birthday))); return _toPerson(dto); } diff --git a/mobile/lib/routing/app_navigation_observer.dart b/mobile/lib/routing/app_navigation_observer.dart index 20973fabd1..26ec017b9a 100644 --- a/mobile/lib/routing/app_navigation_observer.dart +++ b/mobile/lib/routing/app_navigation_observer.dart @@ -22,6 +22,7 @@ class AppNavigationObserver extends AutoRouterObserver { Future(() { ref.read(currentRouteNameProvider.notifier).state = route.settings.name; ref.read(previousRouteNameProvider.notifier).state = previousRoute?.settings.name; + ref.read(previousRouteDataProvider.notifier).state = previousRoute?.settings; }); } diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index f547db65d6..54d4d080d4 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -6,6 +6,7 @@ import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/log.model.dart'; import 'package:immich_mobile/domain/models/memory.model.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/domain/services/timeline.service.dart'; import 'package:immich_mobile/entities/album.entity.dart'; @@ -88,6 +89,8 @@ import 'package:immich_mobile/presentation/pages/drift_local_album.page.dart'; import 'package:immich_mobile/presentation/pages/drift_locked_folder.page.dart'; import 'package:immich_mobile/presentation/pages/drift_memory.page.dart'; import 'package:immich_mobile/presentation/pages/drift_partner_detail.page.dart'; +import 'package:immich_mobile/presentation/pages/drift_people_collection.page.dart'; +import 'package:immich_mobile/presentation/pages/drift_person.page.dart'; import 'package:immich_mobile/presentation/pages/drift_place.page.dart'; import 'package:immich_mobile/presentation/pages/drift_place_detail.page.dart'; import 'package:immich_mobile/presentation/pages/drift_recently_taken.page.dart'; @@ -323,6 +326,9 @@ class AppRouter extends RootStackRouter { AutoRoute(page: DriftPartnerRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: DriftUploadDetailRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: BetaSyncSettingsRoute.page, guards: [_authGuard, _duplicateGuard]), + + AutoRoute(page: DriftPeopleCollectionRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftPersonRoute.page, guards: [_authGuard]), // required to handle all deeplinks in deep_link.service.dart // auto_route_library#1722 RedirectRoute(path: '*', redirectTo: '/'), diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index f67d1e2623..63e8c6ecfe 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -966,6 +966,59 @@ class DriftPartnerRoute extends PageRouteInfo { ); } +/// generated route for +/// [DriftPeopleCollectionPage] +class DriftPeopleCollectionRoute extends PageRouteInfo { + const DriftPeopleCollectionRoute({List? children}) + : super(DriftPeopleCollectionRoute.name, initialChildren: children); + + static const String name = 'DriftPeopleCollectionRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftPeopleCollectionPage(); + }, + ); +} + +/// generated route for +/// [DriftPersonPage] +class DriftPersonRoute extends PageRouteInfo { + DriftPersonRoute({ + Key? key, + required DriftPerson person, + List? children, + }) : super( + DriftPersonRoute.name, + args: DriftPersonRouteArgs(key: key, person: person), + initialChildren: children, + ); + + static const String name = 'DriftPersonRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + final args = data.argsAs(); + return DriftPersonPage(key: args.key, person: args.person); + }, + ); +} + +class DriftPersonRouteArgs { + const DriftPersonRouteArgs({this.key, required this.person}); + + final Key? key; + + final DriftPerson person; + + @override + String toString() { + return 'DriftPersonRouteArgs{key: $key, person: $person}'; + } +} + /// generated route for /// [DriftPlaceDetailPage] class DriftPlaceDetailRoute extends PageRouteInfo { diff --git a/mobile/lib/utils/people.utils.dart b/mobile/lib/utils/people.utils.dart new file mode 100644 index 0000000000..1b0a81a8cc --- /dev/null +++ b/mobile/lib/utils/people.utils.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/people/person_edit_birthday_modal.widget.dart'; +import 'package:immich_mobile/presentation/widgets/people/person_edit_name_modal.widget.dart'; + +String formatAge(DateTime birthDate, DateTime referenceDate) { + int ageInYears = _calculateAge(birthDate, referenceDate); + int ageInMonths = _calculateAgeInMonths(birthDate, referenceDate); + + if (ageInMonths <= 11) { + return "exif_bottom_sheet_person_age_months".t(args: {'months': ageInMonths.toString()}); + } else if (ageInMonths > 12 && ageInMonths <= 23) { + return "exif_bottom_sheet_person_age_year_months".t(args: {'months': (ageInMonths - 12).toString()}); + } else { + return "exif_bottom_sheet_person_age_years".t(args: {'years': ageInYears.toString()}); + } +} + +int _calculateAge(DateTime birthDate, DateTime referenceDate) { + int age = referenceDate.year - birthDate.year; + if (referenceDate.month < birthDate.month || + (referenceDate.month == birthDate.month && referenceDate.day < birthDate.day)) { + age--; + } + return age; +} + +int _calculateAgeInMonths(DateTime birthDate, DateTime referenceDate) { + return (referenceDate.year - birthDate.year) * 12 + + referenceDate.month - + birthDate.month - + (referenceDate.day < birthDate.day ? 1 : 0); +} + +Future showNameEditModal(BuildContext context, DriftPerson person) { + return showDialog( + context: context, + useRootNavigator: false, + builder: (BuildContext context) { + return DriftPersonNameEditForm(person: person); + }, + ); +} + +Future showBirthdayEditModal(BuildContext context, DriftPerson person) { + return showDialog( + context: context, + useRootNavigator: false, + builder: (BuildContext context) { + return DriftPersonBirthdayEditForm(person: person); + }, + ); +} diff --git a/mobile/lib/widgets/common/person_sliver_app_bar.dart b/mobile/lib/widgets/common/person_sliver_app_bar.dart new file mode 100644 index 0000000000..1cc117139d --- /dev/null +++ b/mobile/lib/widgets/common/person_sliver_app_bar.dart @@ -0,0 +1,562 @@ +import 'dart:async'; +import 'dart:io'; +import 'dart:ui'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/domain/models/timeline.model.dart'; +import 'package:immich_mobile/domain/services/timeline.service.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/images/image_provider.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/utils/people.utils.dart'; +import 'package:immich_mobile/utils/image_url_builder.dart'; + +class PersonSliverAppBar extends ConsumerStatefulWidget { + const PersonSliverAppBar({ + super.key, + required this.person, + required this.onNameTap, + required this.onShowOptions, + required this.onBirthdayTap, + }); + + final DriftPerson person; + final VoidCallback onNameTap; + final VoidCallback onBirthdayTap; + final VoidCallback onShowOptions; + + @override + ConsumerState createState() => _MesmerizingSliverAppBarState(); +} + +class _MesmerizingSliverAppBarState extends ConsumerState { + double _scrollProgress = 0.0; + + double _calculateScrollProgress(FlexibleSpaceBarSettings? settings) { + if (settings?.maxExtent == null || settings?.minExtent == null) { + return 1.0; + } + + final deltaExtent = settings!.maxExtent - settings.minExtent; + if (deltaExtent <= 0.0) { + return 1.0; + } + + return (1.0 - (settings.currentExtent - settings.minExtent) / deltaExtent).clamp(0.0, 1.0); + } + + @override + Widget build(BuildContext context) { + final isMultiSelectEnabled = ref.watch(multiSelectProvider.select((s) => s.isEnabled)); + Color? actionIconColor = Color.lerp(Colors.white, context.primaryColor, _scrollProgress); + List actionIconShadows = [ + if (_scrollProgress < 0.95) + Shadow(offset: const Offset(0, 2), blurRadius: 5, color: Colors.black.withValues(alpha: 0.5)) + else + const Shadow(offset: Offset(0, 2), blurRadius: 0, color: Colors.transparent), + ]; + + return isMultiSelectEnabled + ? SliverToBoxAdapter( + child: switch (_scrollProgress) { + < 0.8 => const SizedBox(height: 120), + _ => const SizedBox(height: 352), + }, + ) + : SliverAppBar( + expandedHeight: 300.0, + floating: false, + pinned: true, + snap: false, + elevation: 0, + leading: IconButton( + icon: Icon( + Platform.isIOS ? Icons.arrow_back_ios_new_rounded : Icons.arrow_back, + color: Color.lerp(Colors.white, context.primaryColor, _scrollProgress), + shadows: [ + _scrollProgress < 0.95 + ? Shadow(offset: const Offset(0, 2), blurRadius: 5, color: Colors.black.withValues(alpha: 0.5)) + : const Shadow(offset: Offset(0, 2), blurRadius: 0, color: Colors.transparent), + ], + ), + onPressed: () { + context.pop(); + }, + ), + actions: [ + IconButton( + icon: Icon(Icons.more_vert, color: actionIconColor, shadows: actionIconShadows), + onPressed: widget.onShowOptions, + ), + ], + flexibleSpace: Builder( + builder: (context) { + final settings = context.dependOnInheritedWidgetOfExactType(); + final scrollProgress = _calculateScrollProgress(settings); + + // Update scroll progress for the leading button + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted && _scrollProgress != scrollProgress) { + setState(() { + _scrollProgress = scrollProgress; + }); + } + }); + + return FlexibleSpaceBar( + centerTitle: true, + title: AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + child: scrollProgress > 0.95 + ? Text( + widget.person.name, + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.w600, fontSize: 18), + ) + : null, + ), + background: _ExpandedBackground( + scrollProgress: scrollProgress, + person: widget.person, + onNameTap: widget.onNameTap, + onBirthdayTap: widget.onBirthdayTap, + ), + ); + }, + ), + ); + } +} + +class _ExpandedBackground extends ConsumerStatefulWidget { + final double scrollProgress; + final DriftPerson person; + final VoidCallback onNameTap; + final VoidCallback onBirthdayTap; + + const _ExpandedBackground({ + required this.scrollProgress, + required this.person, + required this.onNameTap, + required this.onBirthdayTap, + }); + + @override + ConsumerState<_ExpandedBackground> createState() => _ExpandedBackgroundState(); +} + +class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with SingleTickerProviderStateMixin { + late AnimationController _slideController; + late Animation _slideAnimation; + + @override + void initState() { + super.initState(); + + _slideController = AnimationController(duration: const Duration(milliseconds: 800), vsync: this); + + _slideAnimation = Tween( + begin: const Offset(0, 1.5), + end: Offset.zero, + ).animate(CurvedAnimation(parent: _slideController, curve: Curves.easeOutCubic)); + + Future.delayed(const Duration(milliseconds: 100), () { + if (mounted) { + _slideController.forward(); + } + }); + } + + @override + void dispose() { + _slideController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final timelineService = ref.watch(timelineServiceProvider); + + return Stack( + fit: StackFit.expand, + children: [ + Transform.translate( + offset: Offset(0, widget.scrollProgress * 50), + child: Transform.scale( + scale: 1.4 - (widget.scrollProgress * 0.2), + child: _RandomAssetBackground(timelineService: timelineService), + ), + ), + ClipRect( + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: widget.scrollProgress * 2.0, sigmaY: widget.scrollProgress * 2.0), + child: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.black.withValues(alpha: 0.05), + Colors.transparent, + Colors.black.withValues(alpha: 0.3), + Colors.black.withValues(alpha: 0.6 + (widget.scrollProgress * 0.25)), + ], + stops: const [0.0, 0.15, 0.55, 1.0], + ), + ), + ), + ), + ), + Positioned( + bottom: 16, + left: 16, + right: 16, + child: SlideTransition( + position: _slideAnimation, + child: Row( + children: [ + SizedBox( + height: 84, + width: 84, + child: Material( + shape: const CircleBorder(side: BorderSide(color: Colors.grey, width: 1.0)), + elevation: 3, + child: CircleAvatar( + maxRadius: 84 / 2, + backgroundImage: NetworkImage( + getFaceThumbnailUrl(widget.person.id), + headers: ApiService.getRequestHeaders(), + ), + ), + ), + ), + const SizedBox(width: 8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + GestureDetector( + onTap: () => widget.onNameTap.call(), + child: SizedBox( + width: double.infinity, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: widget.person.name.isNotEmpty + ? Text( + widget.person.name, + maxLines: 1, + style: const TextStyle( + color: Colors.white, + fontSize: 36, + fontWeight: FontWeight.bold, + letterSpacing: 0.5, + shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black45)], + ), + ) + : Text( + 'add_a_name'.tr(), + style: context.textTheme.titleLarge?.copyWith( + color: Colors.grey[400], + fontSize: 36, + decoration: TextDecoration.underline, + decorationColor: Colors.white, + ), + ), + ), + ), + ), + AnimatedContainer(duration: const Duration(milliseconds: 300), child: const _ItemCountText()), + const SizedBox(height: 8), + GestureDetector( + onTap: widget.onBirthdayTap, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Icon(Icons.cake_rounded, color: Colors.white, size: 14), + const SizedBox(width: 4), + + if (widget.person.birthDate != null) + Text( + "${DateFormat.yMMMd(context.locale.toString()).format(widget.person.birthDate!)} (${formatAge(widget.person.birthDate!, DateTime.now())})", + style: context.textTheme.labelLarge?.copyWith( + color: Colors.white, + height: 1.2, + fontSize: 14, + ), + ) + else + Text( + 'add_birthday'.tr(), + style: context.textTheme.labelLarge?.copyWith( + color: Colors.grey[400], + height: 1.2, + fontSize: 14, + decoration: TextDecoration.underline, + decorationColor: Colors.white, + ), + ), + ], + ), + ), + ], + ), + ), + ], + ), + ), + ), + ], + ); + } +} + +class _ItemCountText extends ConsumerStatefulWidget { + const _ItemCountText(); + + @override + ConsumerState<_ItemCountText> createState() => _ItemCountTextState(); +} + +class _ItemCountTextState extends ConsumerState<_ItemCountText> { + StreamSubscription? _reloadSubscription; + + @override + void initState() { + super.initState(); + _reloadSubscription = EventStream.shared.listen((_) => setState(() {})); + } + + @override + void dispose() { + _reloadSubscription?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final assetCount = ref.watch(timelineServiceProvider.select((s) => s.totalAssets)); + + return Text( + 'items_count'.t(context: context, args: {"count": assetCount}), + style: context.textTheme.labelLarge?.copyWith( + fontWeight: FontWeight.bold, + color: Colors.white, + shadows: [const Shadow(offset: Offset(0, 1), blurRadius: 6, color: Colors.black45)], + ), + ); + } +} + +class _RandomAssetBackground extends StatefulWidget { + final TimelineService timelineService; + + const _RandomAssetBackground({required this.timelineService}); + + @override + State<_RandomAssetBackground> createState() => _RandomAssetBackgroundState(); +} + +class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with TickerProviderStateMixin { + late AnimationController _zoomController; + late AnimationController _crossFadeController; + late Animation _zoomAnimation; + late Animation _panAnimation; + late Animation _crossFadeAnimation; + BaseAsset? _currentAsset; + BaseAsset? _nextAsset; + bool _isZoomingIn = true; + + @override + void initState() { + super.initState(); + + _zoomController = AnimationController(duration: const Duration(seconds: 12), vsync: this); + + _crossFadeController = AnimationController(duration: const Duration(milliseconds: 1200), vsync: this); + + _zoomAnimation = Tween( + begin: 1.0, + end: 1.2, + ).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut)); + + _panAnimation = Tween( + begin: Offset.zero, + end: const Offset(0.5, -0.5), + ).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut)); + + _crossFadeAnimation = Tween( + begin: 0.0, + end: 1.0, + ).animate(CurvedAnimation(parent: _crossFadeController, curve: Curves.easeInOutCubic)); + + Future.delayed(Durations.medium1, () => _loadFirstAsset()); + } + + @override + void dispose() { + _zoomController.dispose(); + _crossFadeController.dispose(); + super.dispose(); + } + + void _startAnimationCycle() { + if (_isZoomingIn) { + _zoomController.forward().then((_) { + _loadNextAsset(); + }); + } else { + _zoomController.reverse().then((_) { + _loadNextAsset(); + }); + } + } + + Future _loadFirstAsset() async { + if (!mounted) { + return; + } + + if (widget.timelineService.totalAssets == 0) { + setState(() { + _currentAsset = null; + }); + + return; + } + + setState(() { + _currentAsset = widget.timelineService.getRandomAsset(); + }); + + await _crossFadeController.forward(); + + if (_zoomController.status == AnimationStatus.dismissed) { + if (_isZoomingIn) { + _zoomController.reset(); + } else { + _zoomController.value = 1.0; + } + _startAnimationCycle(); + } + } + + Future _loadNextAsset() async { + if (!mounted) { + return; + } + + try { + if (widget.timelineService.totalAssets > 1) { + // Load next asset while keeping current one visible + final nextAsset = widget.timelineService.getRandomAsset(); + + setState(() { + _nextAsset = nextAsset; + }); + + await _crossFadeController.reverse(); + setState(() { + _currentAsset = _nextAsset; + _nextAsset = null; + }); + + _crossFadeController.value = 1.0; + + _isZoomingIn = !_isZoomingIn; + + _startAnimationCycle(); + } + } catch (e) { + _zoomController.reset(); + _startAnimationCycle(); + } + } + + @override + Widget build(BuildContext context) { + if (widget.timelineService.totalAssets == 0) { + return const SizedBox.shrink(); + } + + return AnimatedBuilder( + animation: Listenable.merge([_zoomAnimation, _panAnimation, _crossFadeAnimation]), + builder: (context, child) { + return Transform.scale( + scale: _zoomAnimation.value, + filterQuality: Platform.isAndroid ? FilterQuality.low : null, + child: Transform.translate( + offset: _panAnimation.value, + filterQuality: Platform.isAndroid ? FilterQuality.low : null, + child: Stack( + fit: StackFit.expand, + children: [ + // Current image + if (_currentAsset != null) + Opacity( + opacity: _crossFadeAnimation.value, + child: SizedBox( + width: double.infinity, + height: double.infinity, + child: Image( + alignment: Alignment.topRight, + image: getFullImageProvider(_currentAsset!), + fit: BoxFit.cover, + frameBuilder: (context, child, frame, wasSynchronouslyLoaded) { + if (wasSynchronouslyLoaded || frame != null) { + return child; + } + return Container(); + }, + errorBuilder: (context, error, stackTrace) { + return SizedBox( + width: double.infinity, + height: double.infinity, + child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]), + ); + }, + ), + ), + ), + + if (_nextAsset != null) + Opacity( + opacity: 1.0 - _crossFadeAnimation.value, + child: SizedBox( + width: double.infinity, + height: double.infinity, + child: Image( + alignment: Alignment.topRight, + image: getFullImageProvider(_nextAsset!), + fit: BoxFit.cover, + frameBuilder: (context, child, frame, wasSynchronouslyLoaded) { + if (wasSynchronouslyLoaded || frame != null) { + return child; + } + return const SizedBox.shrink(); + }, + errorBuilder: (context, error, stackTrace) { + return SizedBox( + width: double.infinity, + height: double.infinity, + child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]), + ); + }, + ), + ), + ), + ], + ), + ), + ); + }, + ); + } +} diff --git a/mobile/lib/widgets/photo_view/photo_view_gallery.dart b/mobile/lib/widgets/photo_view/photo_view_gallery.dart index 3d56f3f657..af5b9a7ce7 100644 --- a/mobile/lib/widgets/photo_view/photo_view_gallery.dart +++ b/mobile/lib/widgets/photo_view/photo_view_gallery.dart @@ -267,7 +267,7 @@ class _PhotoViewGalleryState extends State { key: pageOption.key ?? ObjectKey(index), childSize: pageOption.childSize, backgroundDecoration: widget.backgroundDecoration, - wantKeepAlive: widget.wantKeepAlive, + wantKeepAlive: false, controller: pageOption.controller, scaleStateController: pageOption.scaleStateController, customSize: widget.customSize, @@ -303,7 +303,7 @@ class _PhotoViewGalleryState extends State { loadingBuilder: widget.loadingBuilder, backgroundDecoration: widget.backgroundDecoration, semanticLabel: pageOption.semanticLabel, - wantKeepAlive: widget.wantKeepAlive, + wantKeepAlive: false, controller: pageOption.controller, onPageBuild: widget.onPageBuild, controllerCallbackBuilder: _getControllerCallbackBuilder, diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 42f37fbc85..aa78658006 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -1556,6 +1556,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.28.0" + scroll_date_picker: + dependency: "direct main" + description: + name: scroll_date_picker + sha256: "1b00a3e24d92c77aa84d5856cfe6a57fd5df5f645ce1a6af0feb3ec84bdffb34" + url: "https://pub.dev" + source: hosted + version: "3.8.0" scrollable_positioned_list: dependency: "direct main" description: diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 03519a0cf2..8510537a1f 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -72,6 +72,7 @@ dependencies: uuid: ^4.5.1 wakelock_plus: ^1.2.10 worker_manager: ^7.2.3 + scroll_date_picker: ^3.8.0 native_video_player: git: From 444133a72be21fcd837e79da0d4de2e7226a4a1b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 30 Jul 2025 10:36:50 +0000 Subject: [PATCH 112/748] chore(deps): update ghcr.io/immich-app/postgres:14-vectorchord0.3.0 docker digest to 0e763a2 (#20380) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- e2e/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/docker-compose.yml b/e2e/docker-compose.yml index ccb21e4587..b923eb0579 100644 --- a/e2e/docker-compose.yml +++ b/e2e/docker-compose.yml @@ -38,7 +38,7 @@ services: image: redis:6.2-alpine@sha256:7fe72c486b910f6b1a9769c937dad5d63648ddee82e056f47417542dd40825bb database: - image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:3aef84a0a4fabbda17ef115c3019ba0c914ec73e9f6e59203674322d858b8eea + image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:0e763a2383d56f90364fcd72767ac41400cd30d2627f407f7e7960c9f1923c21 command: -c fsync=off -c shared_preload_libraries=vchord.so -c config_file=/var/lib/postgresql/data/postgresql.conf environment: POSTGRES_PASSWORD: postgres From d8a65528119ca839b2aef6f4883b0325fcb1a30a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 30 Jul 2025 08:29:37 -0400 Subject: [PATCH 113/748] chore(deps): update ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0 docker digest to 32324a2 (#20381) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- docker/docker-compose.dev.yml | 2 +- docker/docker-compose.prod.yml | 2 +- docker/docker-compose.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index 0d918a3dbe..d9a321240a 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -123,7 +123,7 @@ services: database: container_name: immich_postgres - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:5f6a838e4e44c8e0e019d0ebfe3ee8952b69afc2809b2c25f7b0119641978e91 + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:32324a2f41df5de9efe1af166b7008c3f55646f8d0e00d9550c16c9822366b4a env_file: - .env environment: diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml index 838aea4458..6ed0aeee8e 100644 --- a/docker/docker-compose.prod.yml +++ b/docker/docker-compose.prod.yml @@ -63,7 +63,7 @@ services: database: container_name: immich_postgres - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:5f6a838e4e44c8e0e019d0ebfe3ee8952b69afc2809b2c25f7b0119641978e91 + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:32324a2f41df5de9efe1af166b7008c3f55646f8d0e00d9550c16c9822366b4a env_file: - .env environment: diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 2c0895a57a..ed4e921ff6 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -56,7 +56,7 @@ services: database: container_name: immich_postgres - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:5f6a838e4e44c8e0e019d0ebfe3ee8952b69afc2809b2c25f7b0119641978e91 + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:32324a2f41df5de9efe1af166b7008c3f55646f8d0e00d9550c16c9822366b4a environment: POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_USER: ${DB_USERNAME} From 4ea4ee40afe0a1a72773c2014099db7be9c5a93f Mon Sep 17 00:00:00 2001 From: xCJPECKOVERx Date: Wed, 30 Jul 2025 08:31:16 -0400 Subject: [PATCH 114/748] fix(web): Search chip key value heights don't match (#20312) - add flex items-stretch to stretch chip key height to match value height --- .../search/[[photos=photos]]/[[assetId=id]]/+page.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte index acb7176a7c..48ead6e6b4 100644 --- a/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -330,9 +330,9 @@ > {#each getObjectKeys(terms) as searchKey (searchKey)} {@const value = terms[searchKey]} -
+
{getHumanReadableSearchKey(searchKey as keyof SearchTerms)} From baadf9db20ec7be633283c6a60b144d0ba8b7cb7 Mon Sep 17 00:00:00 2001 From: Ben <45583362+ben-basten@users.noreply.github.com> Date: Wed, 30 Jul 2025 08:39:19 -0400 Subject: [PATCH 115/748] fix(web): timeline date group width (#19964) Fix the calculation for the date group width, so there's never a scenario where photos will be hidden. On mobile devices, photos in the second row can sometimes have a top of <100px, which throws off the calculation of the date group width. --- web/src/lib/utils/layout-utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/lib/utils/layout-utils.ts b/web/src/lib/utils/layout-utils.ts index 429055b92a..e850371b16 100644 --- a/web/src/lib/utils/layout-utils.ts +++ b/web/src/lib/utils/layout-utils.ts @@ -57,7 +57,7 @@ class Adapter { this.result = result; this.width = 0; for (const box of this.result.boxes) { - if (box.top < 100) { + if (box.top === 0) { this.width = box.left + box.width; } else { break; From 9f20522df58bb68c3059d2bd9d13aef02b309b36 Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Wed, 30 Jul 2025 19:14:19 +0530 Subject: [PATCH 116/748] chore: add isFavorite to PlatformAsset in duplicate check (#20427) Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- mobile/ios/Runner/Sync/MessagesImpl.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mobile/ios/Runner/Sync/MessagesImpl.swift b/mobile/ios/Runner/Sync/MessagesImpl.swift index b8d97b0a82..2810dee7c1 100644 --- a/mobile/ios/Runner/Sync/MessagesImpl.swift +++ b/mobile/ios/Runner/Sync/MessagesImpl.swift @@ -172,7 +172,8 @@ class NativeSyncApiImpl: NativeSyncApi { name: "", type: 0, durationInSeconds: 0, - orientation: 0 + orientation: 0, + isFavorite: false ) if (updatedAssets.contains(AssetWrapper(with: predicate))) { continue From da5deffd03797b28a15380251217ea720dbe528b Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Wed, 30 Jul 2025 20:46:23 +0530 Subject: [PATCH 117/748] fix: exclude assets from excluded albumbs on main timeline (#20425) Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- .../entities/merged_asset.drift | 28 ++++++++++++------- .../entities/merged_asset.drift.dart | 6 ++-- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/mobile/lib/infrastructure/entities/merged_asset.drift b/mobile/lib/infrastructure/entities/merged_asset.drift index 579323d641..d1377f6685 100644 --- a/mobile/lib/infrastructure/entities/merged_asset.drift +++ b/mobile/lib/infrastructure/entities/merged_asset.drift @@ -64,6 +64,11 @@ AND EXISTS ( INNER JOIN local_album_entity la on laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0 -- selected ) +AND NOT EXISTS ( + SELECT 1 FROM local_album_asset_entity laa + INNER JOIN local_album_entity la on laa.album_id = la.id + WHERE laa.asset_id = lae.id AND la.backup_selection = 2 -- excluded +) ORDER BY created_at DESC LIMIT $limit; @@ -95,16 +100,19 @@ FROM lae.created_at FROM local_asset_entity lae - LEFT JOIN - remote_asset_entity rae ON rae.checksum = lae.checksum - LEFT JOIN - local_album_asset_entity laa ON laa.asset_id = lae.id - LEFT JOIN - local_album_entity la ON la.id = laa.album_id - WHERE - rae.id IS NULL - AND rae.owner_id IN :user_ids - AND la.backup_selection = 0 -- selected + WHERE NOT EXISTS ( + SELECT 1 FROM remote_asset_entity rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN :user_ids + ) + AND EXISTS ( + SELECT 1 FROM local_album_asset_entity laa + INNER JOIN local_album_entity la on laa.album_id = la.id + WHERE laa.asset_id = lae.id AND la.backup_selection = 0 -- selected + ) + AND NOT EXISTS ( + SELECT 1 FROM local_album_asset_entity laa + INNER JOIN local_album_entity la on laa.album_id = la.id + WHERE laa.asset_id = lae.id AND la.backup_selection = 2 -- excluded + ) ) GROUP BY bucket_date ORDER BY bucket_date DESC; diff --git a/mobile/lib/infrastructure/entities/merged_asset.drift.dart b/mobile/lib/infrastructure/entities/merged_asset.drift.dart index 9916ec13bb..5a091c349c 100644 --- a/mobile/lib/infrastructure/entities/merged_asset.drift.dart +++ b/mobile/lib/infrastructure/entities/merged_asset.drift.dart @@ -29,7 +29,7 @@ class MergedAssetDrift extends i1.ModularAccessor { ); $arrayStartIndex += generatedlimit.amountOfVariables; return customSelect( - 'SELECT rae.id AS remote_id, (SELECT lae.id FROM local_asset_entity AS lae WHERE lae.checksum = rae.checksum LIMIT 1) AS local_id, rae.name, rae.type, rae.created_at AS created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandeduserIds) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at AS created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN ($expandeduserIds)) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) ORDER BY created_at DESC ${generatedlimit.sql}', + 'SELECT rae.id AS remote_id, (SELECT lae.id FROM local_asset_entity AS lae WHERE lae.checksum = rae.checksum LIMIT 1) AS local_id, rae.name, rae.type, rae.created_at AS created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandeduserIds) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at AS created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN ($expandeduserIds)) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) AND NOT EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 2) ORDER BY created_at DESC ${generatedlimit.sql}', variables: [ for (var $ in userIds) i0.Variable($), ...generatedlimit.introducedVariables, @@ -68,13 +68,13 @@ class MergedAssetDrift extends i1.ModularAccessor { i0.Selectable mergedBucket({ required int groupBy, - required List userIds, + required List userIds, }) { var $arrayStartIndex = 2; final expandeduserIds = $expandVar($arrayStartIndex, userIds.length); $arrayStartIndex += userIds.length; return customSelect( - 'SELECT COUNT(*) AS asset_count, CASE WHEN ?1 = 0 THEN STRFTIME(\'%Y-%m-%d\', created_at, \'localtime\') WHEN ?1 = 1 THEN STRFTIME(\'%Y-%m\', created_at, \'localtime\') END AS bucket_date FROM (SELECT rae.created_at FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandeduserIds) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT lae.created_at FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum LEFT JOIN local_album_asset_entity AS laa ON laa.asset_id = lae.id LEFT JOIN local_album_entity AS la ON la.id = laa.album_id WHERE rae.id IS NULL AND rae.owner_id IN ($expandeduserIds) AND la.backup_selection = 0) GROUP BY bucket_date ORDER BY bucket_date DESC', + 'SELECT COUNT(*) AS asset_count, CASE WHEN ?1 = 0 THEN STRFTIME(\'%Y-%m-%d\', created_at, \'localtime\') WHEN ?1 = 1 THEN STRFTIME(\'%Y-%m\', created_at, \'localtime\') END AS bucket_date FROM (SELECT rae.created_at FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandeduserIds) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT lae.created_at FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN ($expandeduserIds)) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) AND NOT EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 2)) GROUP BY bucket_date ORDER BY bucket_date DESC', variables: [ i0.Variable(groupBy), for (var $ in userIds) i0.Variable($), From 097e132fba660b9fdab50a14fb22002aafe5d551 Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Wed, 30 Jul 2025 11:09:28 -0500 Subject: [PATCH 118/748] fix: user profile images not working in beta timeline (#20203) * fix user icons in album view * revert updateUsersV1 change * fix: UserDto merge issues * fix: update user entity * revert what I thought were merge issues turns out drift cant figure out when it needs to gen a file... * fix removed line * handle defaults for older servers * feat: checkpoint migrations * fix: use parenthesis instead of brackets * Update 1753800911775-ProfileImageCheckpointRemoval.ts * fix: sync stream updateUsersV1 --- .../drift_schemas/main/drift_schema_v5.json | 1 + mobile/lib/domain/models/user.model.dart | 32 +- mobile/lib/domain/services/user.service.dart | 2 +- .../infrastructure/entities/user.entity.dart | 15 +- .../entities/user.entity.drift.dart | 333 +- .../repositories/db.repository.dart | 11 +- .../repositories/db.repository.steps.dart | 373 + .../repositories/remote_album.repository.dart | 5 +- .../repositories/sync_stream.repository.dart | 7 +- .../infrastructure/utils/user.converter.dart | 13 +- .../pages/drift_user_selection.page.dart | 5 +- mobile/lib/providers/auth.provider.dart | 1 - mobile/lib/utils/openapi_patching.dart | 5 + .../widgets/common/user_circle_avatar.dart | 11 +- mobile/openapi/lib/model/sync_user_v1.dart | 22 +- .../domain/services/user_service_test.dart | 4 +- mobile/test/drift/main/generated/schema.dart | 5 +- .../test/drift/main/generated/schema_v5.dart | 6402 +++++++++++++++++ mobile/test/fixtures/sync_stream.stub.dart | 20 +- mobile/test/fixtures/user.stub.dart | 6 +- .../modules/shared/sync_service_test.dart | 9 +- open-api/immich-openapi-specs.json | 11 +- server/src/database.ts | 2 +- server/src/dtos/sync.dto.ts | 4 +- server/src/queries/sync.repository.sql | 10 +- server/src/repositories/sync.repository.ts | 11 +- ...800911775-ProfileImageCheckpointRemoval.ts | 25 + server/src/services/sync.service.ts | 4 +- .../test/medium/specs/sync/sync-user.spec.ts | 2 + 29 files changed, 7069 insertions(+), 282 deletions(-) create mode 100644 mobile/drift_schemas/main/drift_schema_v5.json create mode 100644 mobile/test/drift/main/generated/schema_v5.dart create mode 100644 server/src/schema/migrations/1753800911775-ProfileImageCheckpointRemoval.ts diff --git a/mobile/drift_schemas/main/drift_schema_v5.json b/mobile/drift_schemas/main/drift_schema_v5.json new file mode 100644 index 0000000000..ce29eaabdc --- /dev/null +++ b/mobile/drift_schemas/main/drift_schema_v5.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"has_profile_image","getter_name":"hasProfileImage","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"has_profile_image\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"has_profile_image\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"profile_changed_at","getter_name":"profileChangedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":5,"references":[3,4],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":6,"references":[3],"type":"index","data":{"on":3,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_asset_owner_checksum","sql":null,"unique":true,"columns":["checksum","owner_id"]}},{"id":8,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":9,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":10,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":11,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":12,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[1,12],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":14,"references":[12,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":15,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":16,"references":[1,15],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":18,"references":[1,17],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}}]} \ No newline at end of file diff --git a/mobile/lib/domain/models/user.model.dart b/mobile/lib/domain/models/user.model.dart index 1e83fa498d..b0a66f7d70 100644 --- a/mobile/lib/domain/models/user.model.dart +++ b/mobile/lib/domain/models/user.model.dart @@ -11,7 +11,6 @@ class UserDto { final bool isAdmin; final DateTime updatedAt; - final String? profileImagePath; final AvatarColor avatarColor; final bool memoryEnabled; @@ -25,18 +24,22 @@ class UserDto { bool get hasQuota => quotaSizeInBytes > 0; + final bool hasProfileImage; + final DateTime profileChangedAt; + const UserDto({ required this.id, required this.email, required this.name, required this.isAdmin, required this.updatedAt, - this.profileImagePath, + required this.profileChangedAt, this.avatarColor = AvatarColor.primary, this.memoryEnabled = true, this.inTimeline = false, this.isPartnerSharedBy = false, this.isPartnerSharedWith = false, + this.hasProfileImage = false, this.quotaUsageInBytes = 0, this.quotaSizeInBytes = 0, }); @@ -49,14 +52,13 @@ email: $email, name: $name, isAdmin: $isAdmin, updatedAt: $updatedAt, -profileImagePath: ${profileImagePath ?? ''}, avatarColor: $avatarColor, memoryEnabled: $memoryEnabled, inTimeline: $inTimeline, isPartnerSharedBy: $isPartnerSharedBy, isPartnerSharedWith: $isPartnerSharedWith, -quotaUsageInBytes: $quotaUsageInBytes, -quotaSizeInBytes: $quotaSizeInBytes, +hasProfileImage: $hasProfileImage +profileChangedAt: $profileChangedAt }'''; } @@ -66,28 +68,26 @@ quotaSizeInBytes: $quotaSizeInBytes, String? name, bool? isAdmin, DateTime? updatedAt, - String? profileImagePath, AvatarColor? avatarColor, bool? memoryEnabled, bool? inTimeline, bool? isPartnerSharedBy, bool? isPartnerSharedWith, - int? quotaUsageInBytes, - int? quotaSizeInBytes, + bool? hasProfileImage, + DateTime? profileChangedAt, }) => UserDto( id: id ?? this.id, email: email ?? this.email, name: name ?? this.name, isAdmin: isAdmin ?? this.isAdmin, updatedAt: updatedAt ?? this.updatedAt, - profileImagePath: profileImagePath ?? this.profileImagePath, avatarColor: avatarColor ?? this.avatarColor, memoryEnabled: memoryEnabled ?? this.memoryEnabled, inTimeline: inTimeline ?? this.inTimeline, isPartnerSharedBy: isPartnerSharedBy ?? this.isPartnerSharedBy, isPartnerSharedWith: isPartnerSharedWith ?? this.isPartnerSharedWith, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, - quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, ); @override @@ -101,12 +101,11 @@ quotaSizeInBytes: $quotaSizeInBytes, other.name == name && other.isPartnerSharedBy == isPartnerSharedBy && other.isPartnerSharedWith == isPartnerSharedWith && - other.profileImagePath == profileImagePath && other.isAdmin == isAdmin && other.memoryEnabled == memoryEnabled && other.inTimeline == inTimeline && - other.quotaUsageInBytes == quotaUsageInBytes && - other.quotaSizeInBytes == quotaSizeInBytes; + other.hasProfileImage == hasProfileImage && + other.profileChangedAt.isAtSameMomentAs(profileChangedAt); } @override @@ -116,14 +115,13 @@ quotaSizeInBytes: $quotaSizeInBytes, email.hashCode ^ updatedAt.hashCode ^ isAdmin.hashCode ^ - profileImagePath.hashCode ^ avatarColor.hashCode ^ memoryEnabled.hashCode ^ inTimeline.hashCode ^ isPartnerSharedBy.hashCode ^ isPartnerSharedWith.hashCode ^ - quotaUsageInBytes.hashCode ^ - quotaSizeInBytes.hashCode; + hasProfileImage.hashCode ^ + profileChangedAt.hashCode; } class PartnerUserDto { diff --git a/mobile/lib/domain/services/user.service.dart b/mobile/lib/domain/services/user.service.dart index 3e948fe0f5..d347d8aa4f 100644 --- a/mobile/lib/domain/services/user.service.dart +++ b/mobile/lib/domain/services/user.service.dart @@ -45,7 +45,7 @@ class UserService { Future createProfileImage(String name, Uint8List image) async { try { final path = await _userApiRepository.createProfileImage(name: name, data: image); - final updatedUser = getMyUser().copyWith(profileImagePath: path); + final updatedUser = getMyUser(); await _storeService.put(StoreKey.currentUser, updatedUser); await _isarUserRepository.update(updatedUser); return path; diff --git a/mobile/lib/infrastructure/entities/user.entity.dart b/mobile/lib/infrastructure/entities/user.entity.dart index ab5b9a5621..78fc76b45d 100644 --- a/mobile/lib/infrastructure/entities/user.entity.dart +++ b/mobile/lib/infrastructure/entities/user.entity.dart @@ -50,12 +50,10 @@ class User { isAdmin: dto.isAdmin, isPartnerSharedBy: dto.isPartnerSharedBy, isPartnerSharedWith: dto.isPartnerSharedWith, - profileImagePath: dto.profileImagePath ?? "", + profileImagePath: dto.hasProfileImage ? "HAS_PROFILE_IMAGE" : "", avatarColor: dto.avatarColor, memoryEnabled: dto.memoryEnabled, inTimeline: dto.inTimeline, - quotaUsageInBytes: dto.quotaUsageInBytes, - quotaSizeInBytes: dto.quotaSizeInBytes, ); UserDto toDto() => UserDto( @@ -64,12 +62,13 @@ class User { name: name, isAdmin: isAdmin, updatedAt: updatedAt, - profileImagePath: profileImagePath.isEmpty ? null : profileImagePath, avatarColor: avatarColor, memoryEnabled: memoryEnabled, inTimeline: inTimeline, isPartnerSharedBy: isPartnerSharedBy, isPartnerSharedWith: isPartnerSharedWith, + hasProfileImage: profileImagePath.isNotEmpty, + profileChangedAt: updatedAt, quotaUsageInBytes: quotaUsageInBytes, quotaSizeInBytes: quotaSizeInBytes, ); @@ -82,11 +81,11 @@ class UserEntity extends Table with DriftDefaultsMixin { TextColumn get name => text()(); BoolColumn get isAdmin => boolean().withDefault(const Constant(false))(); TextColumn get email => text()(); - TextColumn get profileImagePath => text().nullable()(); + + BoolColumn get hasProfileImage => boolean().withDefault(const Constant(false))(); + DateTimeColumn get profileChangedAt => dateTime().withDefault(currentDateAndTime)(); + DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); - // Quota - IntColumn get quotaSizeInBytes => integer().nullable()(); - IntColumn get quotaUsageInBytes => integer().withDefault(const Constant(0))(); @override Set get primaryKey => {id}; diff --git a/mobile/lib/infrastructure/entities/user.entity.drift.dart b/mobile/lib/infrastructure/entities/user.entity.drift.dart index 2c3c8a1f9c..dbfddab4a0 100644 --- a/mobile/lib/infrastructure/entities/user.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/user.entity.drift.dart @@ -12,10 +12,9 @@ typedef $$UserEntityTableCreateCompanionBuilder = required String name, i0.Value isAdmin, required String email, - i0.Value profileImagePath, + i0.Value hasProfileImage, + i0.Value profileChangedAt, i0.Value updatedAt, - i0.Value quotaSizeInBytes, - i0.Value quotaUsageInBytes, }); typedef $$UserEntityTableUpdateCompanionBuilder = i1.UserEntityCompanion Function({ @@ -23,10 +22,9 @@ typedef $$UserEntityTableUpdateCompanionBuilder = i0.Value name, i0.Value isAdmin, i0.Value email, - i0.Value profileImagePath, + i0.Value hasProfileImage, + i0.Value profileChangedAt, i0.Value updatedAt, - i0.Value quotaSizeInBytes, - i0.Value quotaUsageInBytes, }); class $$UserEntityTableFilterComposer @@ -58,8 +56,13 @@ class $$UserEntityTableFilterComposer builder: (column) => i0.ColumnFilters(column), ); - i0.ColumnFilters get profileImagePath => $composableBuilder( - column: $table.profileImagePath, + i0.ColumnFilters get hasProfileImage => $composableBuilder( + column: $table.hasProfileImage, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get profileChangedAt => $composableBuilder( + column: $table.profileChangedAt, builder: (column) => i0.ColumnFilters(column), ); @@ -67,16 +70,6 @@ class $$UserEntityTableFilterComposer column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column), ); - - i0.ColumnFilters get quotaSizeInBytes => $composableBuilder( - column: $table.quotaSizeInBytes, - builder: (column) => i0.ColumnFilters(column), - ); - - i0.ColumnFilters get quotaUsageInBytes => $composableBuilder( - column: $table.quotaUsageInBytes, - builder: (column) => i0.ColumnFilters(column), - ); } class $$UserEntityTableOrderingComposer @@ -108,8 +101,13 @@ class $$UserEntityTableOrderingComposer builder: (column) => i0.ColumnOrderings(column), ); - i0.ColumnOrderings get profileImagePath => $composableBuilder( - column: $table.profileImagePath, + i0.ColumnOrderings get hasProfileImage => $composableBuilder( + column: $table.hasProfileImage, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get profileChangedAt => $composableBuilder( + column: $table.profileChangedAt, builder: (column) => i0.ColumnOrderings(column), ); @@ -117,16 +115,6 @@ class $$UserEntityTableOrderingComposer column: $table.updatedAt, builder: (column) => i0.ColumnOrderings(column), ); - - i0.ColumnOrderings get quotaSizeInBytes => $composableBuilder( - column: $table.quotaSizeInBytes, - builder: (column) => i0.ColumnOrderings(column), - ); - - i0.ColumnOrderings get quotaUsageInBytes => $composableBuilder( - column: $table.quotaUsageInBytes, - builder: (column) => i0.ColumnOrderings(column), - ); } class $$UserEntityTableAnnotationComposer @@ -150,23 +138,18 @@ class $$UserEntityTableAnnotationComposer i0.GeneratedColumn get email => $composableBuilder(column: $table.email, builder: (column) => column); - i0.GeneratedColumn get profileImagePath => $composableBuilder( - column: $table.profileImagePath, + i0.GeneratedColumn get hasProfileImage => $composableBuilder( + column: $table.hasProfileImage, + builder: (column) => column, + ); + + i0.GeneratedColumn get profileChangedAt => $composableBuilder( + column: $table.profileChangedAt, builder: (column) => column, ); i0.GeneratedColumn get updatedAt => $composableBuilder(column: $table.updatedAt, builder: (column) => column); - - i0.GeneratedColumn get quotaSizeInBytes => $composableBuilder( - column: $table.quotaSizeInBytes, - builder: (column) => column, - ); - - i0.GeneratedColumn get quotaUsageInBytes => $composableBuilder( - column: $table.quotaUsageInBytes, - builder: (column) => column, - ); } class $$UserEntityTableTableManager @@ -210,19 +193,17 @@ class $$UserEntityTableTableManager i0.Value name = const i0.Value.absent(), i0.Value isAdmin = const i0.Value.absent(), i0.Value email = const i0.Value.absent(), - i0.Value profileImagePath = const i0.Value.absent(), + i0.Value hasProfileImage = const i0.Value.absent(), + i0.Value profileChangedAt = const i0.Value.absent(), i0.Value updatedAt = const i0.Value.absent(), - i0.Value quotaSizeInBytes = const i0.Value.absent(), - i0.Value quotaUsageInBytes = const i0.Value.absent(), }) => i1.UserEntityCompanion( id: id, name: name, isAdmin: isAdmin, email: email, - profileImagePath: profileImagePath, + hasProfileImage: hasProfileImage, + profileChangedAt: profileChangedAt, updatedAt: updatedAt, - quotaSizeInBytes: quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes, ), createCompanionCallback: ({ @@ -230,19 +211,17 @@ class $$UserEntityTableTableManager required String name, i0.Value isAdmin = const i0.Value.absent(), required String email, - i0.Value profileImagePath = const i0.Value.absent(), + i0.Value hasProfileImage = const i0.Value.absent(), + i0.Value profileChangedAt = const i0.Value.absent(), i0.Value updatedAt = const i0.Value.absent(), - i0.Value quotaSizeInBytes = const i0.Value.absent(), - i0.Value quotaUsageInBytes = const i0.Value.absent(), }) => i1.UserEntityCompanion.insert( id: id, name: name, isAdmin: isAdmin, email: email, - profileImagePath: profileImagePath, + hasProfileImage: hasProfileImage, + profileChangedAt: profileChangedAt, updatedAt: updatedAt, - quotaSizeInBytes: quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes, ), withReferenceMapper: (p0) => p0 .map((e) => (e.readTable(table), i0.BaseReferences(db, table, e))) @@ -326,16 +305,32 @@ class $UserEntityTable extends i2.UserEntity type: i0.DriftSqlType.string, requiredDuringInsert: true, ); - static const i0.VerificationMeta _profileImagePathMeta = - const i0.VerificationMeta('profileImagePath'); + static const i0.VerificationMeta _hasProfileImageMeta = + const i0.VerificationMeta('hasProfileImage'); @override - late final i0.GeneratedColumn profileImagePath = - i0.GeneratedColumn( - 'profile_image_path', + late final i0.GeneratedColumn hasProfileImage = + i0.GeneratedColumn( + 'has_profile_image', aliasedName, - true, - type: i0.DriftSqlType.string, + false, + type: i0.DriftSqlType.bool, requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const i3.Constant(false), + ); + static const i0.VerificationMeta _profileChangedAtMeta = + const i0.VerificationMeta('profileChangedAt'); + @override + late final i0.GeneratedColumn profileChangedAt = + i0.GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i3.currentDateAndTime, ); static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( 'updatedAt', @@ -350,38 +345,15 @@ class $UserEntityTable extends i2.UserEntity requiredDuringInsert: false, defaultValue: i3.currentDateAndTime, ); - static const i0.VerificationMeta _quotaSizeInBytesMeta = - const i0.VerificationMeta('quotaSizeInBytes'); - @override - late final i0.GeneratedColumn quotaSizeInBytes = i0.GeneratedColumn( - 'quota_size_in_bytes', - aliasedName, - true, - type: i0.DriftSqlType.int, - requiredDuringInsert: false, - ); - static const i0.VerificationMeta _quotaUsageInBytesMeta = - const i0.VerificationMeta('quotaUsageInBytes'); - @override - late final i0.GeneratedColumn quotaUsageInBytes = - i0.GeneratedColumn( - 'quota_usage_in_bytes', - aliasedName, - false, - type: i0.DriftSqlType.int, - requiredDuringInsert: false, - defaultValue: const i3.Constant(0), - ); @override List get $columns => [ id, name, isAdmin, email, - profileImagePath, + hasProfileImage, + profileChangedAt, updatedAt, - quotaSizeInBytes, - quotaUsageInBytes, ]; @override String get aliasedName => _alias ?? actualTableName; @@ -422,12 +394,21 @@ class $UserEntityTable extends i2.UserEntity } else if (isInserting) { context.missing(_emailMeta); } - if (data.containsKey('profile_image_path')) { + if (data.containsKey('has_profile_image')) { context.handle( - _profileImagePathMeta, - profileImagePath.isAcceptableOrUnknown( - data['profile_image_path']!, - _profileImagePathMeta, + _hasProfileImageMeta, + hasProfileImage.isAcceptableOrUnknown( + data['has_profile_image']!, + _hasProfileImageMeta, + ), + ); + } + if (data.containsKey('profile_changed_at')) { + context.handle( + _profileChangedAtMeta, + profileChangedAt.isAcceptableOrUnknown( + data['profile_changed_at']!, + _profileChangedAtMeta, ), ); } @@ -437,24 +418,6 @@ class $UserEntityTable extends i2.UserEntity updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), ); } - if (data.containsKey('quota_size_in_bytes')) { - context.handle( - _quotaSizeInBytesMeta, - quotaSizeInBytes.isAcceptableOrUnknown( - data['quota_size_in_bytes']!, - _quotaSizeInBytesMeta, - ), - ); - } - if (data.containsKey('quota_usage_in_bytes')) { - context.handle( - _quotaUsageInBytesMeta, - quotaUsageInBytes.isAcceptableOrUnknown( - data['quota_usage_in_bytes']!, - _quotaUsageInBytesMeta, - ), - ); - } return context; } @@ -480,22 +443,18 @@ class $UserEntityTable extends i2.UserEntity i0.DriftSqlType.string, data['${effectivePrefix}email'], )!, - profileImagePath: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, - data['${effectivePrefix}profile_image_path'], - ), + hasProfileImage: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}has_profile_image'], + )!, + profileChangedAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}profile_changed_at'], + )!, updatedAt: attachedDatabase.typeMapping.read( i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'], )!, - quotaSizeInBytes: attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, - data['${effectivePrefix}quota_size_in_bytes'], - ), - quotaUsageInBytes: attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, - data['${effectivePrefix}quota_usage_in_bytes'], - )!, ); } @@ -516,19 +475,17 @@ class UserEntityData extends i0.DataClass final String name; final bool isAdmin; final String email; - final String? profileImagePath; + final bool hasProfileImage; + final DateTime profileChangedAt; final DateTime updatedAt; - final int? quotaSizeInBytes; - final int quotaUsageInBytes; const UserEntityData({ required this.id, required this.name, required this.isAdmin, required this.email, - this.profileImagePath, + required this.hasProfileImage, + required this.profileChangedAt, required this.updatedAt, - this.quotaSizeInBytes, - required this.quotaUsageInBytes, }); @override Map toColumns(bool nullToAbsent) { @@ -537,14 +494,9 @@ class UserEntityData extends i0.DataClass map['name'] = i0.Variable(name); map['is_admin'] = i0.Variable(isAdmin); map['email'] = i0.Variable(email); - if (!nullToAbsent || profileImagePath != null) { - map['profile_image_path'] = i0.Variable(profileImagePath); - } + map['has_profile_image'] = i0.Variable(hasProfileImage); + map['profile_changed_at'] = i0.Variable(profileChangedAt); map['updated_at'] = i0.Variable(updatedAt); - if (!nullToAbsent || quotaSizeInBytes != null) { - map['quota_size_in_bytes'] = i0.Variable(quotaSizeInBytes); - } - map['quota_usage_in_bytes'] = i0.Variable(quotaUsageInBytes); return map; } @@ -558,10 +510,9 @@ class UserEntityData extends i0.DataClass name: serializer.fromJson(json['name']), isAdmin: serializer.fromJson(json['isAdmin']), email: serializer.fromJson(json['email']), - profileImagePath: serializer.fromJson(json['profileImagePath']), + hasProfileImage: serializer.fromJson(json['hasProfileImage']), + profileChangedAt: serializer.fromJson(json['profileChangedAt']), updatedAt: serializer.fromJson(json['updatedAt']), - quotaSizeInBytes: serializer.fromJson(json['quotaSizeInBytes']), - quotaUsageInBytes: serializer.fromJson(json['quotaUsageInBytes']), ); } @override @@ -572,10 +523,9 @@ class UserEntityData extends i0.DataClass 'name': serializer.toJson(name), 'isAdmin': serializer.toJson(isAdmin), 'email': serializer.toJson(email), - 'profileImagePath': serializer.toJson(profileImagePath), + 'hasProfileImage': serializer.toJson(hasProfileImage), + 'profileChangedAt': serializer.toJson(profileChangedAt), 'updatedAt': serializer.toJson(updatedAt), - 'quotaSizeInBytes': serializer.toJson(quotaSizeInBytes), - 'quotaUsageInBytes': serializer.toJson(quotaUsageInBytes), }; } @@ -584,23 +534,17 @@ class UserEntityData extends i0.DataClass String? name, bool? isAdmin, String? email, - i0.Value profileImagePath = const i0.Value.absent(), + bool? hasProfileImage, + DateTime? profileChangedAt, DateTime? updatedAt, - i0.Value quotaSizeInBytes = const i0.Value.absent(), - int? quotaUsageInBytes, }) => i1.UserEntityData( id: id ?? this.id, name: name ?? this.name, isAdmin: isAdmin ?? this.isAdmin, email: email ?? this.email, - profileImagePath: profileImagePath.present - ? profileImagePath.value - : this.profileImagePath, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, updatedAt: updatedAt ?? this.updatedAt, - quotaSizeInBytes: quotaSizeInBytes.present - ? quotaSizeInBytes.value - : this.quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, ); UserEntityData copyWithCompanion(i1.UserEntityCompanion data) { return UserEntityData( @@ -608,16 +552,13 @@ class UserEntityData extends i0.DataClass name: data.name.present ? data.name.value : this.name, isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, email: data.email.present ? data.email.value : this.email, - profileImagePath: data.profileImagePath.present - ? data.profileImagePath.value - : this.profileImagePath, + hasProfileImage: data.hasProfileImage.present + ? data.hasProfileImage.value + : this.hasProfileImage, + profileChangedAt: data.profileChangedAt.present + ? data.profileChangedAt.value + : this.profileChangedAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - quotaSizeInBytes: data.quotaSizeInBytes.present - ? data.quotaSizeInBytes.value - : this.quotaSizeInBytes, - quotaUsageInBytes: data.quotaUsageInBytes.present - ? data.quotaUsageInBytes.value - : this.quotaUsageInBytes, ); } @@ -628,10 +569,9 @@ class UserEntityData extends i0.DataClass ..write('name: $name, ') ..write('isAdmin: $isAdmin, ') ..write('email: $email, ') - ..write('profileImagePath: $profileImagePath, ') - ..write('updatedAt: $updatedAt, ') - ..write('quotaSizeInBytes: $quotaSizeInBytes, ') - ..write('quotaUsageInBytes: $quotaUsageInBytes') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') ..write(')')) .toString(); } @@ -642,10 +582,9 @@ class UserEntityData extends i0.DataClass name, isAdmin, email, - profileImagePath, + hasProfileImage, + profileChangedAt, updatedAt, - quotaSizeInBytes, - quotaUsageInBytes, ); @override bool operator ==(Object other) => @@ -655,10 +594,9 @@ class UserEntityData extends i0.DataClass other.name == this.name && other.isAdmin == this.isAdmin && other.email == this.email && - other.profileImagePath == this.profileImagePath && - other.updatedAt == this.updatedAt && - other.quotaSizeInBytes == this.quotaSizeInBytes && - other.quotaUsageInBytes == this.quotaUsageInBytes); + other.hasProfileImage == this.hasProfileImage && + other.profileChangedAt == this.profileChangedAt && + other.updatedAt == this.updatedAt); } class UserEntityCompanion extends i0.UpdateCompanion { @@ -666,29 +604,26 @@ class UserEntityCompanion extends i0.UpdateCompanion { final i0.Value name; final i0.Value isAdmin; final i0.Value email; - final i0.Value profileImagePath; + final i0.Value hasProfileImage; + final i0.Value profileChangedAt; final i0.Value updatedAt; - final i0.Value quotaSizeInBytes; - final i0.Value quotaUsageInBytes; const UserEntityCompanion({ this.id = const i0.Value.absent(), this.name = const i0.Value.absent(), this.isAdmin = const i0.Value.absent(), this.email = const i0.Value.absent(), - this.profileImagePath = const i0.Value.absent(), + this.hasProfileImage = const i0.Value.absent(), + this.profileChangedAt = const i0.Value.absent(), this.updatedAt = const i0.Value.absent(), - this.quotaSizeInBytes = const i0.Value.absent(), - this.quotaUsageInBytes = const i0.Value.absent(), }); UserEntityCompanion.insert({ required String id, required String name, this.isAdmin = const i0.Value.absent(), required String email, - this.profileImagePath = const i0.Value.absent(), + this.hasProfileImage = const i0.Value.absent(), + this.profileChangedAt = const i0.Value.absent(), this.updatedAt = const i0.Value.absent(), - this.quotaSizeInBytes = const i0.Value.absent(), - this.quotaUsageInBytes = const i0.Value.absent(), }) : id = i0.Value(id), name = i0.Value(name), email = i0.Value(email); @@ -697,20 +632,18 @@ class UserEntityCompanion extends i0.UpdateCompanion { i0.Expression? name, i0.Expression? isAdmin, i0.Expression? email, - i0.Expression? profileImagePath, + i0.Expression? hasProfileImage, + i0.Expression? profileChangedAt, i0.Expression? updatedAt, - i0.Expression? quotaSizeInBytes, - i0.Expression? quotaUsageInBytes, }) { return i0.RawValuesInsertable({ if (id != null) 'id': id, if (name != null) 'name': name, if (isAdmin != null) 'is_admin': isAdmin, if (email != null) 'email': email, - if (profileImagePath != null) 'profile_image_path': profileImagePath, + if (hasProfileImage != null) 'has_profile_image': hasProfileImage, + if (profileChangedAt != null) 'profile_changed_at': profileChangedAt, if (updatedAt != null) 'updated_at': updatedAt, - if (quotaSizeInBytes != null) 'quota_size_in_bytes': quotaSizeInBytes, - if (quotaUsageInBytes != null) 'quota_usage_in_bytes': quotaUsageInBytes, }); } @@ -719,20 +652,18 @@ class UserEntityCompanion extends i0.UpdateCompanion { i0.Value? name, i0.Value? isAdmin, i0.Value? email, - i0.Value? profileImagePath, + i0.Value? hasProfileImage, + i0.Value? profileChangedAt, i0.Value? updatedAt, - i0.Value? quotaSizeInBytes, - i0.Value? quotaUsageInBytes, }) { return i1.UserEntityCompanion( id: id ?? this.id, name: name ?? this.name, isAdmin: isAdmin ?? this.isAdmin, email: email ?? this.email, - profileImagePath: profileImagePath ?? this.profileImagePath, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, updatedAt: updatedAt ?? this.updatedAt, - quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, ); } @@ -751,18 +682,15 @@ class UserEntityCompanion extends i0.UpdateCompanion { if (email.present) { map['email'] = i0.Variable(email.value); } - if (profileImagePath.present) { - map['profile_image_path'] = i0.Variable(profileImagePath.value); + if (hasProfileImage.present) { + map['has_profile_image'] = i0.Variable(hasProfileImage.value); + } + if (profileChangedAt.present) { + map['profile_changed_at'] = i0.Variable(profileChangedAt.value); } if (updatedAt.present) { map['updated_at'] = i0.Variable(updatedAt.value); } - if (quotaSizeInBytes.present) { - map['quota_size_in_bytes'] = i0.Variable(quotaSizeInBytes.value); - } - if (quotaUsageInBytes.present) { - map['quota_usage_in_bytes'] = i0.Variable(quotaUsageInBytes.value); - } return map; } @@ -773,10 +701,9 @@ class UserEntityCompanion extends i0.UpdateCompanion { ..write('name: $name, ') ..write('isAdmin: $isAdmin, ') ..write('email: $email, ') - ..write('profileImagePath: $profileImagePath, ') - ..write('updatedAt: $updatedAt, ') - ..write('quotaSizeInBytes: $quotaSizeInBytes, ') - ..write('quotaUsageInBytes: $quotaUsageInBytes') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') ..write(')')) .toString(); } diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index 6e574afa8c..353cabf31e 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -66,7 +66,7 @@ class Drift extends $Drift implements IDatabaseRepository { : super(executor ?? driftDatabase(name: 'immich', native: const DriftNativeOptions(shareAcrossIsolates: true))); @override - int get schemaVersion => 4; + int get schemaVersion => 5; @override MigrationStrategy get migration => MigrationStrategy( @@ -94,6 +94,15 @@ class Drift extends $Drift implements IDatabaseRepository { // asset_face_entity is added await m.create(v4.assetFaceEntity); }, + from4To5: (m, v5) async { + await m.alterTable( + TableMigration( + v5.userEntity, + newColumns: [v5.userEntity.hasProfileImage, v5.userEntity.profileChangedAt], + columnTransformer: {v5.userEntity.profileChangedAt: currentDateAndTime}, + ), + ); + }, ), ); diff --git a/mobile/lib/infrastructure/repositories/db.repository.steps.dart b/mobile/lib/infrastructure/repositories/db.repository.steps.dart index 5bf20780f4..8129bba00c 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.steps.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.steps.dart @@ -1954,10 +1954,376 @@ i1.GeneratedColumn _column_83(String aliasedName) => false, type: i1.DriftSqlType.string, ); + +final class Schema5 extends i0.VersionedSchema { + Schema5({required super.database}) : super(version: 5); + @override + late final List entities = [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + ]; + late final Shape16 userEntity = Shape16( + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_84, + _column_85, + _column_5, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape1 remoteAssetEntity = Shape1( + source: i0.VersionedTable( + entityName: 'remote_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_13, + _column_14, + _column_15, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape3 stackEntity = Shape3( + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_0, _column_9, _column_5, _column_15, _column_75], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape2 localAssetEntity = Shape2( + source: i0.VersionedTable( + entityName: 'local_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_22, + _column_14, + _column_23, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape6 localAlbumEntity = Shape6( + source: i0.VersionedTable( + entityName: 'local_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_5, + _column_31, + _column_32, + _column_33, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape7 localAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_34, _column_35], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLocalAssetChecksum = i1.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + final i1.Index uQRemoteAssetOwnerChecksum = i1.Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + final i1.Index idxRemoteAssetChecksum = i1.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final Shape4 userMetadataEntity = Shape4( + source: i0.VersionedTable( + entityName: 'user_metadata_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(user_id, "key")'], + columns: [_column_25, _column_26, _column_27], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape5 partnerEntity = Shape5( + source: i0.VersionedTable( + entityName: 'partner_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(shared_by_id, shared_with_id)'], + columns: [_column_28, _column_29, _column_30], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape8 remoteExifEntity = Shape8( + source: i0.VersionedTable( + entityName: 'remote_exif_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id)'], + columns: [ + _column_36, + _column_37, + _column_38, + _column_39, + _column_40, + _column_41, + _column_11, + _column_10, + _column_42, + _column_43, + _column_44, + _column_45, + _column_46, + _column_47, + _column_48, + _column_49, + _column_50, + _column_51, + _column_52, + _column_53, + _column_54, + _column_55, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape9 remoteAlbumEntity = Shape9( + source: i0.VersionedTable( + entityName: 'remote_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_56, + _column_9, + _column_5, + _column_15, + _column_57, + _column_58, + _column_59, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape7 remoteAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'remote_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_36, _column_60], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape10 remoteAlbumUserEntity = Shape10( + source: i0.VersionedTable( + entityName: 'remote_album_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(album_id, user_id)'], + columns: [_column_60, _column_25, _column_61], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape11 memoryEntity = Shape11( + source: i0.VersionedTable( + entityName: 'memory_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_18, + _column_15, + _column_8, + _column_62, + _column_63, + _column_64, + _column_65, + _column_66, + _column_67, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape12 memoryAssetEntity = Shape12( + source: i0.VersionedTable( + entityName: 'memory_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, memory_id)'], + columns: [_column_36, _column_68], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape14 personEntity = Shape14( + source: i0.VersionedTable( + entityName: 'person_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_1, + _column_69, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape15 assetFaceEntity = Shape15( + source: i0.VersionedTable( + entityName: 'asset_face_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_36, + _column_76, + _column_77, + _column_78, + _column_79, + _column_80, + _column_81, + _column_82, + _column_83, + ], + attachedDatabase: database, + ), + alias: null, + ); +} + +class Shape16 extends i0.VersionedTable { + Shape16({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get isAdmin => + columnsByName['is_admin']! as i1.GeneratedColumn; + i1.GeneratedColumn get email => + columnsByName['email']! as i1.GeneratedColumn; + i1.GeneratedColumn get hasProfileImage => + columnsByName['has_profile_image']! as i1.GeneratedColumn; + i1.GeneratedColumn get profileChangedAt => + columnsByName['profile_changed_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; +} + +i1.GeneratedColumn _column_84(String aliasedName) => + i1.GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); +i1.GeneratedColumn _column_85(String aliasedName) => + i1.GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: i1.DriftSqlType.dateTime, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); i0.MigrationStepWithVersion migrationSteps({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, required Future Function(i1.Migrator m, Schema3 schema) from2To3, required Future Function(i1.Migrator m, Schema4 schema) from3To4, + required Future Function(i1.Migrator m, Schema5 schema) from4To5, }) { return (currentVersion, database) async { switch (currentVersion) { @@ -1976,6 +2342,11 @@ i0.MigrationStepWithVersion migrationSteps({ final migrator = i1.Migrator(database, schema); await from3To4(migrator, schema); return 4; + case 4: + final schema = Schema5(database: database); + final migrator = i1.Migrator(database, schema); + await from4To5(migrator, schema); + return 5; default: throw ArgumentError.value('Unknown migration from $currentVersion'); } @@ -1986,10 +2357,12 @@ i1.OnUpgrade stepByStep({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, required Future Function(i1.Migrator m, Schema3 schema) from2To3, required Future Function(i1.Migrator m, Schema4 schema) from3To4, + required Future Function(i1.Migrator m, Schema5 schema) from4To5, }) => i0.VersionedSchema.stepByStepHelper( step: migrationSteps( from1To2: from1To2, from2To3: from2To3, from3To4: from3To4, + from4To5: from4To5, ), ); diff --git a/mobile/lib/infrastructure/repositories/remote_album.repository.dart b/mobile/lib/infrastructure/repositories/remote_album.repository.dart index 79f3d78fd7..6bc6a7066d 100644 --- a/mobile/lib/infrastructure/repositories/remote_album.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_album.repository.dart @@ -173,15 +173,14 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { id: user.id, email: user.email, name: user.name, - profileImagePath: user.profileImagePath?.isEmpty == true ? null : user.profileImagePath, isAdmin: user.isAdmin, updatedAt: user.updatedAt, - quotaSizeInBytes: user.quotaSizeInBytes ?? 0, - quotaUsageInBytes: user.quotaUsageInBytes, memoryEnabled: true, inTimeline: false, isPartnerSharedBy: false, isPartnerSharedWith: false, + profileChangedAt: user.profileChangedAt, + hasProfileImage: user.hasProfileImage, ), ) .get(); diff --git a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart index 64b0661e16..2eefa298f8 100644 --- a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart @@ -42,7 +42,12 @@ class SyncStreamRepository extends DriftDatabaseRepository { try { await _db.batch((batch) { for (final user in data) { - final companion = UserEntityCompanion(name: Value(user.name), email: Value(user.email)); + final companion = UserEntityCompanion( + name: Value(user.name), + email: Value(user.email), + hasProfileImage: Value(user.hasProfileImage), + profileChangedAt: Value(user.profileChangedAt), + ); batch.insert(_db.userEntity, companion.copyWith(id: Value(user.id)), onConflict: DoUpdate((_) => companion)); } diff --git a/mobile/lib/infrastructure/utils/user.converter.dart b/mobile/lib/infrastructure/utils/user.converter.dart index 19958beabc..dc107e6fb2 100644 --- a/mobile/lib/infrastructure/utils/user.converter.dart +++ b/mobile/lib/infrastructure/utils/user.converter.dart @@ -11,7 +11,8 @@ abstract final class UserConverter { name: dto.name, isAdmin: false, updatedAt: DateTime.now(), - profileImagePath: dto.profileImagePath, + hasProfileImage: dto.profileImagePath.isNotEmpty, + profileChangedAt: dto.profileChangedAt, avatarColor: dto.avatarColor.toAvatarColor(), ); @@ -21,14 +22,13 @@ abstract final class UserConverter { name: adminDto.name, isAdmin: adminDto.isAdmin, updatedAt: adminDto.updatedAt, - profileImagePath: adminDto.profileImagePath, avatarColor: adminDto.avatarColor.toAvatarColor(), memoryEnabled: preferenceDto?.memories.enabled ?? true, inTimeline: false, isPartnerSharedBy: false, isPartnerSharedWith: false, - quotaUsageInBytes: adminDto.quotaUsageInBytes ?? 0, - quotaSizeInBytes: adminDto.quotaSizeInBytes ?? 0, + profileChangedAt: adminDto.profileChangedAt, + hasProfileImage: adminDto.profileImagePath.isNotEmpty, ); static UserDto fromPartnerDto(PartnerResponseDto dto) => UserDto( @@ -37,14 +37,13 @@ abstract final class UserConverter { name: dto.name, isAdmin: false, updatedAt: DateTime.now(), - profileImagePath: dto.profileImagePath, avatarColor: dto.avatarColor.toAvatarColor(), memoryEnabled: false, inTimeline: dto.inTimeline ?? false, isPartnerSharedBy: false, isPartnerSharedWith: false, - quotaUsageInBytes: 0, - quotaSizeInBytes: 0, + profileChangedAt: dto.profileChangedAt, + hasProfileImage: dto.profileImagePath.isNotEmpty, ); } diff --git a/mobile/lib/presentation/pages/drift_user_selection.page.dart b/mobile/lib/presentation/pages/drift_user_selection.page.dart index e8835e7146..5bd32aaf81 100644 --- a/mobile/lib/presentation/pages/drift_user_selection.page.dart +++ b/mobile/lib/presentation/pages/drift_user_selection.page.dart @@ -27,15 +27,14 @@ final driftUsersProvider = FutureProvider.autoDispose>((ref) async name: entity.name, email: entity.email, isAdmin: entity.isAdmin, - profileImagePath: entity.profileImagePath, updatedAt: entity.updatedAt, - quotaSizeInBytes: entity.quotaSizeInBytes ?? 0, - quotaUsageInBytes: entity.quotaUsageInBytes, isPartnerSharedBy: false, isPartnerSharedWith: false, avatarColor: AvatarColor.primary, memoryEnabled: true, inTimeline: true, + profileChangedAt: entity.profileChangedAt, + hasProfileImage: entity.hasProfileImage, ), ) .toList(); diff --git a/mobile/lib/providers/auth.provider.dart b/mobile/lib/providers/auth.provider.dart index fc3e08472b..02f7920d6f 100644 --- a/mobile/lib/providers/auth.provider.dart +++ b/mobile/lib/providers/auth.provider.dart @@ -167,7 +167,6 @@ class AuthNotifier extends StateNotifier { isAuthenticated: true, name: user.name, isAdmin: user.isAdmin, - profileImagePath: user.profileImagePath, ); return true; diff --git a/mobile/lib/utils/openapi_patching.dart b/mobile/lib/utils/openapi_patching.dart index efbcf1c139..3e45390198 100644 --- a/mobile/lib/utils/openapi_patching.dart +++ b/mobile/lib/utils/openapi_patching.dart @@ -40,6 +40,11 @@ dynamic upgradeDto(dynamic value, String targetType) { addDefault(value, 'isOnboarded', false); } break; + case 'SyncUserV1': + if (value is Map) { + addDefault(value, 'profileChangedAt', DateTime.now().toIso8601String()); + addDefault(value, 'hasProfileImage', false); + } } } diff --git a/mobile/lib/widgets/common/user_circle_avatar.dart b/mobile/lib/widgets/common/user_circle_avatar.dart index 8be71e9b2e..1fbfce6c53 100644 --- a/mobile/lib/widgets/common/user_circle_avatar.dart +++ b/mobile/lib/widgets/common/user_circle_avatar.dart @@ -32,6 +32,7 @@ class UserCircleAvatar extends ConsumerWidget { ), child: Text(user.name[0].toUpperCase()), ); + return Tooltip( message: user.name, child: Container( @@ -42,13 +43,12 @@ class UserCircleAvatar extends ConsumerWidget { child: CircleAvatar( backgroundColor: userAvatarColor, radius: radius, - child: user.profileImagePath == null - ? textIcon - : ClipRRect( + child: user.hasProfileImage + ? ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(50)), child: CachedNetworkImage( fit: BoxFit.cover, - cacheKey: user.profileImagePath, + cacheKey: user.profileChangedAt.toIso8601String(), width: size, height: size, placeholder: (_, __) => Image.memory(kTransparentImage), @@ -57,7 +57,8 @@ class UserCircleAvatar extends ConsumerWidget { fadeInDuration: const Duration(milliseconds: 300), errorWidget: (context, error, stackTrace) => textIcon, ), - ), + ) + : textIcon, ), ), ); diff --git a/mobile/openapi/lib/model/sync_user_v1.dart b/mobile/openapi/lib/model/sync_user_v1.dart index c01ddcc9fc..b9fad5ae8c 100644 --- a/mobile/openapi/lib/model/sync_user_v1.dart +++ b/mobile/openapi/lib/model/sync_user_v1.dart @@ -16,8 +16,10 @@ class SyncUserV1 { required this.avatarColor, required this.deletedAt, required this.email, + required this.hasProfileImage, required this.id, required this.name, + required this.profileChangedAt, }); UserAvatarColor? avatarColor; @@ -26,17 +28,23 @@ class SyncUserV1 { String email; + bool hasProfileImage; + String id; String name; + DateTime profileChangedAt; + @override bool operator ==(Object other) => identical(this, other) || other is SyncUserV1 && other.avatarColor == avatarColor && other.deletedAt == deletedAt && other.email == email && + other.hasProfileImage == hasProfileImage && other.id == id && - other.name == name; + other.name == name && + other.profileChangedAt == profileChangedAt; @override int get hashCode => @@ -44,11 +52,13 @@ class SyncUserV1 { (avatarColor == null ? 0 : avatarColor!.hashCode) + (deletedAt == null ? 0 : deletedAt!.hashCode) + (email.hashCode) + + (hasProfileImage.hashCode) + (id.hashCode) + - (name.hashCode); + (name.hashCode) + + (profileChangedAt.hashCode); @override - String toString() => 'SyncUserV1[avatarColor=$avatarColor, deletedAt=$deletedAt, email=$email, id=$id, name=$name]'; + String toString() => 'SyncUserV1[avatarColor=$avatarColor, deletedAt=$deletedAt, email=$email, hasProfileImage=$hasProfileImage, id=$id, name=$name, profileChangedAt=$profileChangedAt]'; Map toJson() { final json = {}; @@ -63,8 +73,10 @@ class SyncUserV1 { // json[r'deletedAt'] = null; } json[r'email'] = this.email; + json[r'hasProfileImage'] = this.hasProfileImage; json[r'id'] = this.id; json[r'name'] = this.name; + json[r'profileChangedAt'] = this.profileChangedAt.toUtc().toIso8601String(); return json; } @@ -80,8 +92,10 @@ class SyncUserV1 { avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']), deletedAt: mapDateTime(json, r'deletedAt', r''), email: mapValueOfType(json, r'email')!, + hasProfileImage: mapValueOfType(json, r'hasProfileImage')!, id: mapValueOfType(json, r'id')!, name: mapValueOfType(json, r'name')!, + profileChangedAt: mapDateTime(json, r'profileChangedAt', r'')!, ); } return null; @@ -132,8 +146,10 @@ class SyncUserV1 { 'avatarColor', 'deletedAt', 'email', + 'hasProfileImage', 'id', 'name', + 'profileChangedAt', }; } diff --git a/mobile/test/domain/services/user_service_test.dart b/mobile/test/domain/services/user_service_test.dart index b3d967154c..395f38a207 100644 --- a/mobile/test/domain/services/user_service_test.dart +++ b/mobile/test/domain/services/user_service_test.dart @@ -98,7 +98,7 @@ void main() { group('createProfileImage', () { test('should return profile image path', () async { const profileImagePath = 'profile.jpg'; - final updatedUser = UserStub.admin.copyWith(profileImagePath: profileImagePath); + final updatedUser = UserStub.admin; when( () => mockUserApiRepo.createProfileImage(name: profileImagePath, data: Uint8List(0)), @@ -115,7 +115,7 @@ void main() { test('should return null if profile image creation fails', () async { const profileImagePath = 'profile.jpg'; - final updatedUser = UserStub.admin.copyWith(profileImagePath: profileImagePath); + final updatedUser = UserStub.admin; when( () => mockUserApiRepo.createProfileImage(name: profileImagePath, data: Uint8List(0)), diff --git a/mobile/test/drift/main/generated/schema.dart b/mobile/test/drift/main/generated/schema.dart index 22131b11bb..c42542afb3 100644 --- a/mobile/test/drift/main/generated/schema.dart +++ b/mobile/test/drift/main/generated/schema.dart @@ -7,6 +7,7 @@ import 'schema_v1.dart' as v1; import 'schema_v2.dart' as v2; import 'schema_v3.dart' as v3; import 'schema_v4.dart' as v4; +import 'schema_v5.dart' as v5; class GeneratedHelper implements SchemaInstantiationHelper { @override @@ -20,10 +21,12 @@ class GeneratedHelper implements SchemaInstantiationHelper { return v3.DatabaseAtV3(db); case 4: return v4.DatabaseAtV4(db); + case 5: + return v5.DatabaseAtV5(db); default: throw MissingSchemaException(version, versions); } } - static const versions = const [1, 2, 3, 4]; + static const versions = const [1, 2, 3, 4, 5]; } diff --git a/mobile/test/drift/main/generated/schema_v5.dart b/mobile/test/drift/main/generated/schema_v5.dart new file mode 100644 index 0000000000..5c94ff26cb --- /dev/null +++ b/mobile/test/drift/main/generated/schema_v5.dart @@ -0,0 +1,6402 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class UserEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn hasProfileImage = GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn profileChangedAt = + GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + @override + List get $columns => [ + id, + name, + isAdmin, + email, + hasProfileImage, + profileChangedAt, + updatedAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_entity'; + @override + Set get $primaryKey => {id}; + @override + UserEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + hasProfileImage: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}has_profile_image'], + )!, + profileChangedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}profile_changed_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ); + } + + @override + UserEntity createAlias(String alias) { + return UserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserEntityData extends DataClass implements Insertable { + final String id; + final String name; + final bool isAdmin; + final String email; + final bool hasProfileImage; + final DateTime profileChangedAt; + final DateTime updatedAt; + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + required this.hasProfileImage, + required this.profileChangedAt, + required this.updatedAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['is_admin'] = Variable(isAdmin); + map['email'] = Variable(email); + map['has_profile_image'] = Variable(hasProfileImage); + map['profile_changed_at'] = Variable(profileChangedAt); + map['updated_at'] = Variable(updatedAt); + return map; + } + + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + isAdmin: serializer.fromJson(json['isAdmin']), + email: serializer.fromJson(json['email']), + hasProfileImage: serializer.fromJson(json['hasProfileImage']), + profileChangedAt: serializer.fromJson(json['profileChangedAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'isAdmin': serializer.toJson(isAdmin), + 'email': serializer.toJson(email), + 'hasProfileImage': serializer.toJson(hasProfileImage), + 'profileChangedAt': serializer.toJson(profileChangedAt), + 'updatedAt': serializer.toJson(updatedAt), + }; + } + + UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + bool? hasProfileImage, + DateTime? profileChangedAt, + DateTime? updatedAt, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + updatedAt: updatedAt ?? this.updatedAt, + ); + UserEntityData copyWithCompanion(UserEntityCompanion data) { + return UserEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, + email: data.email.present ? data.email.value : this.email, + hasProfileImage: data.hasProfileImage.present + ? data.hasProfileImage.value + : this.hasProfileImage, + profileChangedAt: data.profileChangedAt.present + ? data.profileChangedAt.value + : this.profileChangedAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ); + } + + @override + String toString() { + return (StringBuffer('UserEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + hasProfileImage, + profileChangedAt, + updatedAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserEntityData && + other.id == this.id && + other.name == this.name && + other.isAdmin == this.isAdmin && + other.email == this.email && + other.hasProfileImage == this.hasProfileImage && + other.profileChangedAt == this.profileChangedAt && + other.updatedAt == this.updatedAt); +} + +class UserEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value isAdmin; + final Value email; + final Value hasProfileImage; + final Value profileChangedAt; + final Value updatedAt; + const UserEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.isAdmin = const Value.absent(), + this.email = const Value.absent(), + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.updatedAt = const Value.absent(), + }); + UserEntityCompanion.insert({ + required String id, + required String name, + this.isAdmin = const Value.absent(), + required String email, + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.updatedAt = const Value.absent(), + }) : id = Value(id), + name = Value(name), + email = Value(email); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? isAdmin, + Expression? email, + Expression? hasProfileImage, + Expression? profileChangedAt, + Expression? updatedAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (isAdmin != null) 'is_admin': isAdmin, + if (email != null) 'email': email, + if (hasProfileImage != null) 'has_profile_image': hasProfileImage, + if (profileChangedAt != null) 'profile_changed_at': profileChangedAt, + if (updatedAt != null) 'updated_at': updatedAt, + }); + } + + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? hasProfileImage, + Value? profileChangedAt, + Value? updatedAt, + }) { + return UserEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + updatedAt: updatedAt ?? this.updatedAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (isAdmin.present) { + map['is_admin'] = Variable(isAdmin.value); + } + if (email.present) { + map['email'] = Variable(email.value); + } + if (hasProfileImage.present) { + map['has_profile_image'] = Variable(hasProfileImage.value); + } + if (profileChangedAt.present) { + map['profile_changed_at'] = Variable(profileChangedAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } +} + +class RemoteAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn localDateTime = + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn livePhotoVideoId = GeneratedColumn( + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), + ); + } + + @override + RemoteAssetEntity createAlias(String alias) { + return RemoteAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String checksum; + final bool isFavorite; + final String ownerId; + final DateTime? localDateTime; + final String? thumbHash; + final DateTime? deletedAt; + final String? livePhotoVideoId; + final int visibility; + final String? stackId; + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + map['checksum'] = Variable(checksum); + map['is_favorite'] = Variable(isFavorite); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || localDateTime != null) { + map['local_date_time'] = Variable(localDateTime); + } + if (!nullToAbsent || thumbHash != null) { + map['thumb_hash'] = Variable(thumbHash); + } + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + if (!nullToAbsent || livePhotoVideoId != null) { + map['live_photo_video_id'] = Variable(livePhotoVideoId); + } + map['visibility'] = Variable(visibility); + if (!nullToAbsent || stackId != null) { + map['stack_id'] = Variable(stackId); + } + return map; + } + + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + ownerId: serializer.fromJson(json['ownerId']), + localDateTime: serializer.fromJson(json['localDateTime']), + thumbHash: serializer.fromJson(json['thumbHash']), + deletedAt: serializer.fromJson(json['deletedAt']), + livePhotoVideoId: serializer.fromJson(json['livePhotoVideoId']), + visibility: serializer.fromJson(json['visibility']), + stackId: serializer.fromJson(json['stackId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'ownerId': serializer.toJson(ownerId), + 'localDateTime': serializer.toJson(localDateTime), + 'thumbHash': serializer.toJson(thumbHash), + 'deletedAt': serializer.toJson(deletedAt), + 'livePhotoVideoId': serializer.toJson(livePhotoVideoId), + 'visibility': serializer.toJson(visibility), + 'stackId': serializer.toJson(stackId), + }; + } + + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + ); + RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { + return RemoteAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + localDateTime: data.localDateTime.present + ? data.localDateTime.value + : this.localDateTime, + thumbHash: data.thumbHash.present ? data.thumbHash.value : this.thumbHash, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + livePhotoVideoId: data.livePhotoVideoId.present + ? data.livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: data.visibility.present + ? data.visibility.value + : this.visibility, + stackId: data.stackId.present ? data.stackId.value : this.stackId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.ownerId == this.ownerId && + other.localDateTime == this.localDateTime && + other.thumbHash == this.thumbHash && + other.deletedAt == this.deletedAt && + other.livePhotoVideoId == this.livePhotoVideoId && + other.visibility == this.visibility && + other.stackId == this.stackId); +} + +class RemoteAssetEntityCompanion + extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value ownerId; + final Value localDateTime; + final Value thumbHash; + final Value deletedAt; + final Value livePhotoVideoId; + final Value visibility; + final Value stackId; + const RemoteAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.ownerId = const Value.absent(), + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + this.visibility = const Value.absent(), + this.stackId = const Value.absent(), + }); + RemoteAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + required String checksum, + this.isFavorite = const Value.absent(), + required String ownerId, + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + required int visibility, + this.stackId = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? ownerId, + Expression? localDateTime, + Expression? thumbHash, + Expression? deletedAt, + Expression? livePhotoVideoId, + Expression? visibility, + Expression? stackId, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (ownerId != null) 'owner_id': ownerId, + if (localDateTime != null) 'local_date_time': localDateTime, + if (thumbHash != null) 'thumb_hash': thumbHash, + if (deletedAt != null) 'deleted_at': deletedAt, + if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId, + if (visibility != null) 'visibility': visibility, + if (stackId != null) 'stack_id': stackId, + }); + } + + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + }) { + return RemoteAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime ?? this.localDateTime, + thumbHash: thumbHash ?? this.thumbHash, + deletedAt: deletedAt ?? this.deletedAt, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId ?? this.stackId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (localDateTime.present) { + map['local_date_time'] = Variable(localDateTime.value); + } + if (thumbHash.present) { + map['thumb_hash'] = Variable(thumbHash.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (livePhotoVideoId.present) { + map['live_photo_video_id'] = Variable(livePhotoVideoId.value); + } + if (visibility.present) { + map['visibility'] = Variable(visibility.value); + } + if (stackId.present) { + map['stack_id'] = Variable(stackId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId') + ..write(')')) + .toString(); + } +} + +class StackEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StackEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'stack_entity'; + @override + Set get $primaryKey => {id}; + @override + StackEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StackEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, + ); + } + + @override + StackEntity createAlias(String alias) { + return StackEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StackEntityData extends DataClass implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String primaryAssetId; + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['primary_asset_id'] = Variable(primaryAssetId); + return map; + } + + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StackEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + primaryAssetId: serializer.fromJson(json['primaryAssetId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'primaryAssetId': serializer.toJson(primaryAssetId), + }; + } + + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + StackEntityData copyWithCompanion(StackEntityCompanion data) { + return StackEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, + ); + } + + @override + String toString() { + return (StringBuffer('StackEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StackEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.primaryAssetId == this.primaryAssetId); +} + +class StackEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value primaryAssetId; + const StackEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.primaryAssetId = const Value.absent(), + }); + StackEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String primaryAssetId, + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? primaryAssetId, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, + }); + } + + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { + return StackEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (primaryAssetId.present) { + map['primary_asset_id'] = Variable(primaryAssetId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StackEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } +} + +class LocalAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, + ); + } + + @override + LocalAssetEntity createAlias(String alias) { + return LocalAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String? checksum; + final bool isFavorite; + final int orientation; + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + if (!nullToAbsent || checksum != null) { + map['checksum'] = Variable(checksum); + } + map['is_favorite'] = Variable(isFavorite); + map['orientation'] = Variable(orientation); + return map; + } + + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + orientation: serializer.fromJson(json['orientation']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'orientation': serializer.toJson(orientation), + }; + } + + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { + return LocalAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.orientation == this.orientation); +} + +class LocalAssetEntityCompanion extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value orientation; + const LocalAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }); + LocalAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? orientation, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (orientation != null) 'orientation': orientation, + }); + } + + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { + return LocalAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } +} + +class LocalAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); + @override + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), + ); + } + + @override + LocalAlbumEntity createAlias(String alias) { + return LocalAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final DateTime updatedAt; + final int backupSelection; + final bool isIosSharedAlbum; + final bool? marker_; + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['updated_at'] = Variable(updatedAt); + map['backup_selection'] = Variable(backupSelection); + map['is_ios_shared_album'] = Variable(isIosSharedAlbum); + if (!nullToAbsent || marker_ != null) { + map['marker'] = Variable(marker_); + } + return map; + } + + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + updatedAt: serializer.fromJson(json['updatedAt']), + backupSelection: serializer.fromJson(json['backupSelection']), + isIosSharedAlbum: serializer.fromJson(json['isIosSharedAlbum']), + marker_: serializer.fromJson(json['marker_']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'updatedAt': serializer.toJson(updatedAt), + 'backupSelection': serializer.toJson(backupSelection), + 'isIosSharedAlbum': serializer.toJson(isIosSharedAlbum), + 'marker_': serializer.toJson(marker_), + }; + } + + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); + LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { + return LocalAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, + marker_: data.marker_.present ? data.marker_.value : this.marker_, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.updatedAt == this.updatedAt && + other.backupSelection == this.backupSelection && + other.isIosSharedAlbum == this.isIosSharedAlbum && + other.marker_ == this.marker_); +} + +class LocalAlbumEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value updatedAt; + final Value backupSelection; + final Value isIosSharedAlbum; + final Value marker_; + const LocalAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.updatedAt = const Value.absent(), + this.backupSelection = const Value.absent(), + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }); + LocalAlbumEntityCompanion.insert({ + required String id, + required String name, + this.updatedAt = const Value.absent(), + required int backupSelection, + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? updatedAt, + Expression? backupSelection, + Expression? isIosSharedAlbum, + Expression? marker_, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (updatedAt != null) 'updated_at': updatedAt, + if (backupSelection != null) 'backup_selection': backupSelection, + if (isIosSharedAlbum != null) 'is_ios_shared_album': isIosSharedAlbum, + if (marker_ != null) 'marker': marker_, + }); + } + + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_, + }) { + return LocalAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_ ?? this.marker_, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (backupSelection.present) { + map['backup_selection'] = Variable(backupSelection.value); + } + if (isIosSharedAlbum.present) { + map['is_ios_shared_album'] = Variable(isIosSharedAlbum.value); + } + if (marker_.present) { + map['marker'] = Variable(marker_.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } +} + +class LocalAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + LocalAlbumAssetEntity createAlias(String alias) { + return LocalAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + LocalAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data, + ) { + return LocalAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const LocalAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + LocalAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return LocalAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class UserMetadataEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserMetadataEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); + @override + List get $columns => [userId, key, value]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_metadata_entity'; + @override + Set get $primaryKey => {userId, key}; + @override + UserMetadataEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserMetadataEntityData( + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, + ); + } + + @override + UserMetadataEntity createAlias(String alias) { + return UserMetadataEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserMetadataEntityData extends DataClass + implements Insertable { + final String userId; + final int key; + final Uint8List value; + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['user_id'] = Variable(userId); + map['key'] = Variable(key); + map['value'] = Variable(value); + return map; + } + + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserMetadataEntityData( + userId: serializer.fromJson(json['userId']), + key: serializer.fromJson(json['key']), + value: serializer.fromJson(json['value']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'userId': serializer.toJson(userId), + 'key': serializer.toJson(key), + 'value': serializer.toJson(value), + }; + } + + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { + return UserMetadataEntityData( + userId: data.userId.present ? data.userId.value : this.userId, + key: data.key.present ? data.key.value : this.key, + value: data.value.present ? data.value.value : this.value, + ); + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityData(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(userId, key, $driftBlobEquality.hash(value)); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserMetadataEntityData && + other.userId == this.userId && + other.key == this.key && + $driftBlobEquality.equals(other.value, this.value)); +} + +class UserMetadataEntityCompanion + extends UpdateCompanion { + final Value userId; + final Value key; + final Value value; + const UserMetadataEntityCompanion({ + this.userId = const Value.absent(), + this.key = const Value.absent(), + this.value = const Value.absent(), + }); + UserMetadataEntityCompanion.insert({ + required String userId, + required int key, + required Uint8List value, + }) : userId = Value(userId), + key = Value(key), + value = Value(value); + static Insertable custom({ + Expression? userId, + Expression? key, + Expression? value, + }) { + return RawValuesInsertable({ + if (userId != null) 'user_id': userId, + if (key != null) 'key': key, + if (value != null) 'value': value, + }); + } + + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { + return UserMetadataEntityCompanion( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (key.present) { + map['key'] = Variable(key.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityCompanion(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } +} + +class PartnerEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PartnerEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [sharedById, sharedWithId, inTimeline]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'partner_entity'; + @override + Set get $primaryKey => {sharedById, sharedWithId}; + @override + PartnerEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PartnerEntityData( + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, + ); + } + + @override + PartnerEntity createAlias(String alias) { + return PartnerEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PartnerEntityData extends DataClass + implements Insertable { + final String sharedById; + final String sharedWithId; + final bool inTimeline; + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['shared_by_id'] = Variable(sharedById); + map['shared_with_id'] = Variable(sharedWithId); + map['in_timeline'] = Variable(inTimeline); + return map; + } + + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PartnerEntityData( + sharedById: serializer.fromJson(json['sharedById']), + sharedWithId: serializer.fromJson(json['sharedWithId']), + inTimeline: serializer.fromJson(json['inTimeline']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'sharedById': serializer.toJson(sharedById), + 'sharedWithId': serializer.toJson(sharedWithId), + 'inTimeline': serializer.toJson(inTimeline), + }; + } + + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { + return PartnerEntityData( + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, + ); + } + + @override + String toString() { + return (StringBuffer('PartnerEntityData(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PartnerEntityData && + other.sharedById == this.sharedById && + other.sharedWithId == this.sharedWithId && + other.inTimeline == this.inTimeline); +} + +class PartnerEntityCompanion extends UpdateCompanion { + final Value sharedById; + final Value sharedWithId; + final Value inTimeline; + const PartnerEntityCompanion({ + this.sharedById = const Value.absent(), + this.sharedWithId = const Value.absent(), + this.inTimeline = const Value.absent(), + }); + PartnerEntityCompanion.insert({ + required String sharedById, + required String sharedWithId, + this.inTimeline = const Value.absent(), + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); + static Insertable custom({ + Expression? sharedById, + Expression? sharedWithId, + Expression? inTimeline, + }) { + return RawValuesInsertable({ + if (sharedById != null) 'shared_by_id': sharedById, + if (sharedWithId != null) 'shared_with_id': sharedWithId, + if (inTimeline != null) 'in_timeline': inTimeline, + }); + } + + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { + return PartnerEntityCompanion( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sharedById.present) { + map['shared_by_id'] = Variable(sharedById.value); + } + if (sharedWithId.present) { + map['shared_with_id'] = Variable(sharedWithId.value); + } + if (inTimeline.present) { + map['in_timeline'] = Variable(inTimeline.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PartnerEntityCompanion(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } +} + +class RemoteExifEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteExifEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn city = GeneratedColumn( + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn state = GeneratedColumn( + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn country = GeneratedColumn( + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn make = GeneratedColumn( + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn model = GeneratedColumn( + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_exif_entity'; + @override + Set get $primaryKey => {assetId}; + @override + RemoteExifEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteExifEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), + ); + } + + @override + RemoteExifEntity createAlias(String alias) { + return RemoteExifEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteExifEntityData extends DataClass + implements Insertable { + final String assetId; + final String? city; + final String? state; + final String? country; + final DateTime? dateTimeOriginal; + final String? description; + final int? height; + final int? width; + final String? exposureTime; + final double? fNumber; + final int? fileSize; + final double? focalLength; + final double? latitude; + final double? longitude; + final int? iso; + final String? make; + final String? model; + final String? lens; + final String? orientation; + final String? timeZone; + final int? rating; + final String? projectionType; + const RemoteExifEntityData({ + required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || city != null) { + map['city'] = Variable(city); + } + if (!nullToAbsent || state != null) { + map['state'] = Variable(state); + } + if (!nullToAbsent || country != null) { + map['country'] = Variable(country); + } + if (!nullToAbsent || dateTimeOriginal != null) { + map['date_time_original'] = Variable(dateTimeOriginal); + } + if (!nullToAbsent || description != null) { + map['description'] = Variable(description); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || exposureTime != null) { + map['exposure_time'] = Variable(exposureTime); + } + if (!nullToAbsent || fNumber != null) { + map['f_number'] = Variable(fNumber); + } + if (!nullToAbsent || fileSize != null) { + map['file_size'] = Variable(fileSize); + } + if (!nullToAbsent || focalLength != null) { + map['focal_length'] = Variable(focalLength); + } + if (!nullToAbsent || latitude != null) { + map['latitude'] = Variable(latitude); + } + if (!nullToAbsent || longitude != null) { + map['longitude'] = Variable(longitude); + } + if (!nullToAbsent || iso != null) { + map['iso'] = Variable(iso); + } + if (!nullToAbsent || make != null) { + map['make'] = Variable(make); + } + if (!nullToAbsent || model != null) { + map['model'] = Variable(model); + } + if (!nullToAbsent || lens != null) { + map['lens'] = Variable(lens); + } + if (!nullToAbsent || orientation != null) { + map['orientation'] = Variable(orientation); + } + if (!nullToAbsent || timeZone != null) { + map['time_zone'] = Variable(timeZone); + } + if (!nullToAbsent || rating != null) { + map['rating'] = Variable(rating); + } + if (!nullToAbsent || projectionType != null) { + map['projection_type'] = Variable(projectionType); + } + return map; + } + + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteExifEntityData( + assetId: serializer.fromJson(json['assetId']), + city: serializer.fromJson(json['city']), + state: serializer.fromJson(json['state']), + country: serializer.fromJson(json['country']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), + description: serializer.fromJson(json['description']), + height: serializer.fromJson(json['height']), + width: serializer.fromJson(json['width']), + exposureTime: serializer.fromJson(json['exposureTime']), + fNumber: serializer.fromJson(json['fNumber']), + fileSize: serializer.fromJson(json['fileSize']), + focalLength: serializer.fromJson(json['focalLength']), + latitude: serializer.fromJson(json['latitude']), + longitude: serializer.fromJson(json['longitude']), + iso: serializer.fromJson(json['iso']), + make: serializer.fromJson(json['make']), + model: serializer.fromJson(json['model']), + lens: serializer.fromJson(json['lens']), + orientation: serializer.fromJson(json['orientation']), + timeZone: serializer.fromJson(json['timeZone']), + rating: serializer.fromJson(json['rating']), + projectionType: serializer.fromJson(json['projectionType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'city': serializer.toJson(city), + 'state': serializer.toJson(state), + 'country': serializer.toJson(country), + 'dateTimeOriginal': serializer.toJson(dateTimeOriginal), + 'description': serializer.toJson(description), + 'height': serializer.toJson(height), + 'width': serializer.toJson(width), + 'exposureTime': serializer.toJson(exposureTime), + 'fNumber': serializer.toJson(fNumber), + 'fileSize': serializer.toJson(fileSize), + 'focalLength': serializer.toJson(focalLength), + 'latitude': serializer.toJson(latitude), + 'longitude': serializer.toJson(longitude), + 'iso': serializer.toJson(iso), + 'make': serializer.toJson(make), + 'model': serializer.toJson(model), + 'lens': serializer.toJson(lens), + 'orientation': serializer.toJson(orientation), + 'timeZone': serializer.toJson(timeZone), + 'rating': serializer.toJson(rating), + 'projectionType': serializer.toJson(projectionType), + }; + } + + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); + RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { + return RemoteExifEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + city: data.city.present ? data.city.value : this.city, + state: data.state.present ? data.state.value : this.state, + country: data.country.present ? data.country.value : this.country, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: data.description.present + ? data.description.value + : this.description, + height: data.height.present ? data.height.value : this.height, + width: data.width.present ? data.width.value : this.width, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, + fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, + fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, + latitude: data.latitude.present ? data.latitude.value : this.latitude, + longitude: data.longitude.present ? data.longitude.value : this.longitude, + iso: data.iso.present ? data.iso.value : this.iso, + make: data.make.present ? data.make.value : this.make, + model: data.model.present ? data.model.value : this.model, + lens: data.lens.present ? data.lens.value : this.lens, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, + rating: data.rating.present ? data.rating.value : this.rating, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityData(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteExifEntityData && + other.assetId == this.assetId && + other.city == this.city && + other.state == this.state && + other.country == this.country && + other.dateTimeOriginal == this.dateTimeOriginal && + other.description == this.description && + other.height == this.height && + other.width == this.width && + other.exposureTime == this.exposureTime && + other.fNumber == this.fNumber && + other.fileSize == this.fileSize && + other.focalLength == this.focalLength && + other.latitude == this.latitude && + other.longitude == this.longitude && + other.iso == this.iso && + other.make == this.make && + other.model == this.model && + other.lens == this.lens && + other.orientation == this.orientation && + other.timeZone == this.timeZone && + other.rating == this.rating && + other.projectionType == this.projectionType); +} + +class RemoteExifEntityCompanion extends UpdateCompanion { + final Value assetId; + final Value city; + final Value state; + final Value country; + final Value dateTimeOriginal; + final Value description; + final Value height; + final Value width; + final Value exposureTime; + final Value fNumber; + final Value fileSize; + final Value focalLength; + final Value latitude; + final Value longitude; + final Value iso; + final Value make; + final Value model; + final Value lens; + final Value orientation; + final Value timeZone; + final Value rating; + final Value projectionType; + const RemoteExifEntityCompanion({ + this.assetId = const Value.absent(), + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }); + RemoteExifEntityCompanion.insert({ + required String assetId, + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }) : assetId = Value(assetId); + static Insertable custom({ + Expression? assetId, + Expression? city, + Expression? state, + Expression? country, + Expression? dateTimeOriginal, + Expression? description, + Expression? height, + Expression? width, + Expression? exposureTime, + Expression? fNumber, + Expression? fileSize, + Expression? focalLength, + Expression? latitude, + Expression? longitude, + Expression? iso, + Expression? make, + Expression? model, + Expression? lens, + Expression? orientation, + Expression? timeZone, + Expression? rating, + Expression? projectionType, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (city != null) 'city': city, + if (state != null) 'state': state, + if (country != null) 'country': country, + if (dateTimeOriginal != null) 'date_time_original': dateTimeOriginal, + if (description != null) 'description': description, + if (height != null) 'height': height, + if (width != null) 'width': width, + if (exposureTime != null) 'exposure_time': exposureTime, + if (fNumber != null) 'f_number': fNumber, + if (fileSize != null) 'file_size': fileSize, + if (focalLength != null) 'focal_length': focalLength, + if (latitude != null) 'latitude': latitude, + if (longitude != null) 'longitude': longitude, + if (iso != null) 'iso': iso, + if (make != null) 'make': make, + if (model != null) 'model': model, + if (lens != null) 'lens': lens, + if (orientation != null) 'orientation': orientation, + if (timeZone != null) 'time_zone': timeZone, + if (rating != null) 'rating': rating, + if (projectionType != null) 'projection_type': projectionType, + }); + } + + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { + return RemoteExifEntityCompanion( + assetId: assetId ?? this.assetId, + city: city ?? this.city, + state: state ?? this.state, + country: country ?? this.country, + dateTimeOriginal: dateTimeOriginal ?? this.dateTimeOriginal, + description: description ?? this.description, + height: height ?? this.height, + width: width ?? this.width, + exposureTime: exposureTime ?? this.exposureTime, + fNumber: fNumber ?? this.fNumber, + fileSize: fileSize ?? this.fileSize, + focalLength: focalLength ?? this.focalLength, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + iso: iso ?? this.iso, + make: make ?? this.make, + model: model ?? this.model, + lens: lens ?? this.lens, + orientation: orientation ?? this.orientation, + timeZone: timeZone ?? this.timeZone, + rating: rating ?? this.rating, + projectionType: projectionType ?? this.projectionType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (city.present) { + map['city'] = Variable(city.value); + } + if (state.present) { + map['state'] = Variable(state.value); + } + if (country.present) { + map['country'] = Variable(country.value); + } + if (dateTimeOriginal.present) { + map['date_time_original'] = Variable(dateTimeOriginal.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (exposureTime.present) { + map['exposure_time'] = Variable(exposureTime.value); + } + if (fNumber.present) { + map['f_number'] = Variable(fNumber.value); + } + if (fileSize.present) { + map['file_size'] = Variable(fileSize.value); + } + if (focalLength.present) { + map['focal_length'] = Variable(focalLength.value); + } + if (latitude.present) { + map['latitude'] = Variable(latitude.value); + } + if (longitude.present) { + map['longitude'] = Variable(longitude.value); + } + if (iso.present) { + map['iso'] = Variable(iso.value); + } + if (make.present) { + map['make'] = Variable(make.value); + } + if (model.present) { + map['model'] = Variable(model.value); + } + if (lens.present) { + map['lens'] = Variable(lens.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + if (timeZone.present) { + map['time_zone'] = Variable(timeZone.value); + } + if (rating.present) { + map['rating'] = Variable(rating.value); + } + if (projectionType.present) { + map['projection_type'] = Variable(projectionType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn order = GeneratedColumn( + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, + ); + } + + @override + RemoteAlbumEntity createAlias(String alias) { + return RemoteAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String description; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String? thumbnailAssetId; + final bool isActivityEnabled; + final int order; + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['description'] = Variable(description); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || thumbnailAssetId != null) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId); + } + map['is_activity_enabled'] = Variable(isActivityEnabled); + map['order'] = Variable(order); + return map; + } + + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + description: serializer.fromJson(json['description']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + thumbnailAssetId: serializer.fromJson(json['thumbnailAssetId']), + isActivityEnabled: serializer.fromJson(json['isActivityEnabled']), + order: serializer.fromJson(json['order']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'description': serializer.toJson(description), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'thumbnailAssetId': serializer.toJson(thumbnailAssetId), + 'isActivityEnabled': serializer.toJson(isActivityEnabled), + 'order': serializer.toJson(order), + }; + } + + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { + return RemoteAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + description: data.description.present + ? data.description.value + : this.description, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, + order: data.order.present ? data.order.value : this.order, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.description == this.description && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.thumbnailAssetId == this.thumbnailAssetId && + other.isActivityEnabled == this.isActivityEnabled && + other.order == this.order); +} + +class RemoteAlbumEntityCompanion + extends UpdateCompanion { + final Value id; + final Value name; + final Value description; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value thumbnailAssetId; + final Value isActivityEnabled; + final Value order; + const RemoteAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + this.order = const Value.absent(), + }); + RemoteAlbumEntityCompanion.insert({ + required String id, + required String name, + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + required int order, + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? description, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? thumbnailAssetId, + Expression? isActivityEnabled, + Expression? order, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (description != null) 'description': description, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (thumbnailAssetId != null) 'thumbnail_asset_id': thumbnailAssetId, + if (isActivityEnabled != null) 'is_activity_enabled': isActivityEnabled, + if (order != null) 'order': order, + }); + } + + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { + return RemoteAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (thumbnailAssetId.present) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId.value); + } + if (isActivityEnabled.present) { + map['is_activity_enabled'] = Variable(isActivityEnabled.value); + } + if (order.present) { + map['order'] = Variable(order.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + RemoteAlbumAssetEntity createAlias(String alias) { + return RemoteAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data, + ) { + return RemoteAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const RemoteAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + RemoteAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return RemoteAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumUserEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn role = GeneratedColumn( + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [albumId, userId, role]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_user_entity'; + @override + Set get $primaryKey => {albumId, userId}; + @override + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumUserEntityData( + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, + ); + } + + @override + RemoteAlbumUserEntity createAlias(String alias) { + return RemoteAlbumUserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { + final String albumId; + final String userId; + final int role; + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['album_id'] = Variable(albumId); + map['user_id'] = Variable(userId); + map['role'] = Variable(role); + return map; + } + + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumUserEntityData( + albumId: serializer.fromJson(json['albumId']), + userId: serializer.fromJson(json['userId']), + role: serializer.fromJson(json['role']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'albumId': serializer.toJson(albumId), + 'userId': serializer.toJson(userId), + 'role': serializer.toJson(role), + }; + } + + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data, + ) { + return RemoteAlbumUserEntityData( + albumId: data.albumId.present ? data.albumId.value : this.albumId, + userId: data.userId.present ? data.userId.value : this.userId, + role: data.role.present ? data.role.value : this.role, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityData(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(albumId, userId, role); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumUserEntityData && + other.albumId == this.albumId && + other.userId == this.userId && + other.role == this.role); +} + +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { + final Value albumId; + final Value userId; + final Value role; + const RemoteAlbumUserEntityCompanion({ + this.albumId = const Value.absent(), + this.userId = const Value.absent(), + this.role = const Value.absent(), + }); + RemoteAlbumUserEntityCompanion.insert({ + required String albumId, + required String userId, + required int role, + }) : albumId = Value(albumId), + userId = Value(userId), + role = Value(role); + static Insertable custom({ + Expression? albumId, + Expression? userId, + Expression? role, + }) { + return RawValuesInsertable({ + if (albumId != null) 'album_id': albumId, + if (userId != null) 'user_id': userId, + if (role != null) 'role': role, + }); + } + + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { + return RemoteAlbumUserEntityCompanion( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (role.present) { + map['role'] = Variable(role.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityCompanion(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } +} + +class MemoryEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn data = GeneratedColumn( + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_entity'; + @override + Set get $primaryKey => {id}; + @override + MemoryEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), + ); + } + + @override + MemoryEntity createAlias(String alias) { + return MemoryEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final DateTime? deletedAt; + final String ownerId; + final int type; + final String data; + final bool isSaved; + final DateTime memoryAt; + final DateTime? seenAt; + final DateTime? showAt; + final DateTime? hideAt; + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + map['owner_id'] = Variable(ownerId); + map['type'] = Variable(type); + map['data'] = Variable(data); + map['is_saved'] = Variable(isSaved); + map['memory_at'] = Variable(memoryAt); + if (!nullToAbsent || seenAt != null) { + map['seen_at'] = Variable(seenAt); + } + if (!nullToAbsent || showAt != null) { + map['show_at'] = Variable(showAt); + } + if (!nullToAbsent || hideAt != null) { + map['hide_at'] = Variable(hideAt); + } + return map; + } + + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + deletedAt: serializer.fromJson(json['deletedAt']), + ownerId: serializer.fromJson(json['ownerId']), + type: serializer.fromJson(json['type']), + data: serializer.fromJson(json['data']), + isSaved: serializer.fromJson(json['isSaved']), + memoryAt: serializer.fromJson(json['memoryAt']), + seenAt: serializer.fromJson(json['seenAt']), + showAt: serializer.fromJson(json['showAt']), + hideAt: serializer.fromJson(json['hideAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'deletedAt': serializer.toJson(deletedAt), + 'ownerId': serializer.toJson(ownerId), + 'type': serializer.toJson(type), + 'data': serializer.toJson(data), + 'isSaved': serializer.toJson(isSaved), + 'memoryAt': serializer.toJson(memoryAt), + 'seenAt': serializer.toJson(seenAt), + 'showAt': serializer.toJson(showAt), + 'hideAt': serializer.toJson(hideAt), + }; + } + + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); + MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { + return MemoryEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + type: data.type.present ? data.type.value : this.type, + data: data.data.present ? data.data.value : this.data, + isSaved: data.isSaved.present ? data.isSaved.value : this.isSaved, + memoryAt: data.memoryAt.present ? data.memoryAt.value : this.memoryAt, + seenAt: data.seenAt.present ? data.seenAt.value : this.seenAt, + showAt: data.showAt.present ? data.showAt.value : this.showAt, + hideAt: data.hideAt.present ? data.hideAt.value : this.hideAt, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.deletedAt == this.deletedAt && + other.ownerId == this.ownerId && + other.type == this.type && + other.data == this.data && + other.isSaved == this.isSaved && + other.memoryAt == this.memoryAt && + other.seenAt == this.seenAt && + other.showAt == this.showAt && + other.hideAt == this.hideAt); +} + +class MemoryEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value deletedAt; + final Value ownerId; + final Value type; + final Value data; + final Value isSaved; + final Value memoryAt; + final Value seenAt; + final Value showAt; + final Value hideAt; + const MemoryEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.type = const Value.absent(), + this.data = const Value.absent(), + this.isSaved = const Value.absent(), + this.memoryAt = const Value.absent(), + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }); + MemoryEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + required String ownerId, + required int type, + required String data, + this.isSaved = const Value.absent(), + required DateTime memoryAt, + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? deletedAt, + Expression? ownerId, + Expression? type, + Expression? data, + Expression? isSaved, + Expression? memoryAt, + Expression? seenAt, + Expression? showAt, + Expression? hideAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (deletedAt != null) 'deleted_at': deletedAt, + if (ownerId != null) 'owner_id': ownerId, + if (type != null) 'type': type, + if (data != null) 'data': data, + if (isSaved != null) 'is_saved': isSaved, + if (memoryAt != null) 'memory_at': memoryAt, + if (seenAt != null) 'seen_at': seenAt, + if (showAt != null) 'show_at': showAt, + if (hideAt != null) 'hide_at': hideAt, + }); + } + + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { + return MemoryEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt ?? this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt ?? this.seenAt, + showAt: showAt ?? this.showAt, + hideAt: hideAt ?? this.hideAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (data.present) { + map['data'] = Variable(data.value); + } + if (isSaved.present) { + map['is_saved'] = Variable(isSaved.value); + } + if (memoryAt.present) { + map['memory_at'] = Variable(memoryAt.value); + } + if (seenAt.present) { + map['seen_at'] = Variable(seenAt.value); + } + if (showAt.present) { + map['show_at'] = Variable(showAt.value); + } + if (hideAt.present) { + map['hide_at'] = Variable(hideAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } +} + +class MemoryAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, memoryId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_asset_entity'; + @override + Set get $primaryKey => {assetId, memoryId}; + @override + MemoryAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, + ); + } + + @override + MemoryAssetEntity createAlias(String alias) { + return MemoryAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String memoryId; + const MemoryAssetEntityData({required this.assetId, required this.memoryId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['memory_id'] = Variable(memoryId); + return map; + } + + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + memoryId: serializer.fromJson(json['memoryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'memoryId': serializer.toJson(memoryId), + }; + } + + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + MemoryAssetEntityData copyWithCompanion(MemoryAssetEntityCompanion data) { + return MemoryAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + memoryId: data.memoryId.present ? data.memoryId.value : this.memoryId, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, memoryId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); +} + +class MemoryAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value memoryId; + const MemoryAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.memoryId = const Value.absent(), + }); + MemoryAssetEntityCompanion.insert({ + required String assetId, + required String memoryId, + }) : assetId = Value(assetId), + memoryId = Value(memoryId); + static Insertable custom({ + Expression? assetId, + Expression? memoryId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (memoryId != null) 'memory_id': memoryId, + }); + } + + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { + return MemoryAssetEntityCompanion( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (memoryId.present) { + map['memory_id'] = Variable(memoryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } +} + +class PersonEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PersonEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'person_entity'; + @override + Set get $primaryKey => {id}; + @override + PersonEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PersonEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), + ); + } + + @override + PersonEntity createAlias(String alias) { + return PersonEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PersonEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['name'] = Variable(name); + if (!nullToAbsent || faceAssetId != null) { + map['face_asset_id'] = Variable(faceAssetId); + } + map['is_favorite'] = Variable(isFavorite); + map['is_hidden'] = Variable(isHidden); + if (!nullToAbsent || color != null) { + map['color'] = Variable(color); + } + if (!nullToAbsent || birthDate != null) { + map['birth_date'] = Variable(birthDate); + } + return map; + } + + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PersonEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + name: serializer.fromJson(json['name']), + faceAssetId: serializer.fromJson(json['faceAssetId']), + isFavorite: serializer.fromJson(json['isFavorite']), + isHidden: serializer.fromJson(json['isHidden']), + color: serializer.fromJson(json['color']), + birthDate: serializer.fromJson(json['birthDate']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'name': serializer.toJson(name), + 'faceAssetId': serializer.toJson(faceAssetId), + 'isFavorite': serializer.toJson(isFavorite), + 'isHidden': serializer.toJson(isHidden), + 'color': serializer.toJson(color), + 'birthDate': serializer.toJson(birthDate), + }; + } + + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); + PersonEntityData copyWithCompanion(PersonEntityCompanion data) { + return PersonEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + name: data.name.present ? data.name.value : this.name, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + color: data.color.present ? data.color.value : this.color, + birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, + ); + } + + @override + String toString() { + return (StringBuffer('PersonEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PersonEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.name == this.name && + other.faceAssetId == this.faceAssetId && + other.isFavorite == this.isFavorite && + other.isHidden == this.isHidden && + other.color == this.color && + other.birthDate == this.birthDate); +} + +class PersonEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value name; + final Value faceAssetId; + final Value isFavorite; + final Value isHidden; + final Value color; + final Value birthDate; + const PersonEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.name = const Value.absent(), + this.faceAssetId = const Value.absent(), + this.isFavorite = const Value.absent(), + this.isHidden = const Value.absent(), + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }); + PersonEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String name, + this.faceAssetId = const Value.absent(), + required bool isFavorite, + required bool isHidden, + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? name, + Expression? faceAssetId, + Expression? isFavorite, + Expression? isHidden, + Expression? color, + Expression? birthDate, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (name != null) 'name': name, + if (faceAssetId != null) 'face_asset_id': faceAssetId, + if (isFavorite != null) 'is_favorite': isFavorite, + if (isHidden != null) 'is_hidden': isHidden, + if (color != null) 'color': color, + if (birthDate != null) 'birth_date': birthDate, + }); + } + + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { + return PersonEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (faceAssetId.present) { + map['face_asset_id'] = Variable(faceAssetId.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (isHidden.present) { + map['is_hidden'] = Variable(isHidden.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (birthDate.present) { + map['birth_date'] = Variable(birthDate.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PersonEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } +} + +class AssetFaceEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + AssetFaceEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn personId = GeneratedColumn( + 'person_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn imageWidth = GeneratedColumn( + 'image_width', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn imageHeight = GeneratedColumn( + 'image_height', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX1 = GeneratedColumn( + 'bounding_box_x1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY1 = GeneratedColumn( + 'bounding_box_y1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX2 = GeneratedColumn( + 'bounding_box_x2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY2 = GeneratedColumn( + 'bounding_box_y2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn sourceType = GeneratedColumn( + 'source_type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'asset_face_entity'; + @override + Set get $primaryKey => {id}; + @override + AssetFaceEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return AssetFaceEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + personId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}person_id'], + ), + imageWidth: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_width'], + )!, + imageHeight: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_height'], + )!, + boundingBoxX1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x1'], + )!, + boundingBoxY1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y1'], + )!, + boundingBoxX2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x2'], + )!, + boundingBoxY2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y2'], + )!, + sourceType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}source_type'], + )!, + ); + } + + @override + AssetFaceEntity createAlias(String alias) { + return AssetFaceEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AssetFaceEntityData extends DataClass + implements Insertable { + final String id; + final String assetId; + final String? personId; + final int imageWidth; + final int imageHeight; + final int boundingBoxX1; + final int boundingBoxY1; + final int boundingBoxX2; + final int boundingBoxY2; + final String sourceType; + const AssetFaceEntityData({ + required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || personId != null) { + map['person_id'] = Variable(personId); + } + map['image_width'] = Variable(imageWidth); + map['image_height'] = Variable(imageHeight); + map['bounding_box_x1'] = Variable(boundingBoxX1); + map['bounding_box_y1'] = Variable(boundingBoxY1); + map['bounding_box_x2'] = Variable(boundingBoxX2); + map['bounding_box_y2'] = Variable(boundingBoxY2); + map['source_type'] = Variable(sourceType); + return map; + } + + factory AssetFaceEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return AssetFaceEntityData( + id: serializer.fromJson(json['id']), + assetId: serializer.fromJson(json['assetId']), + personId: serializer.fromJson(json['personId']), + imageWidth: serializer.fromJson(json['imageWidth']), + imageHeight: serializer.fromJson(json['imageHeight']), + boundingBoxX1: serializer.fromJson(json['boundingBoxX1']), + boundingBoxY1: serializer.fromJson(json['boundingBoxY1']), + boundingBoxX2: serializer.fromJson(json['boundingBoxX2']), + boundingBoxY2: serializer.fromJson(json['boundingBoxY2']), + sourceType: serializer.fromJson(json['sourceType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'assetId': serializer.toJson(assetId), + 'personId': serializer.toJson(personId), + 'imageWidth': serializer.toJson(imageWidth), + 'imageHeight': serializer.toJson(imageHeight), + 'boundingBoxX1': serializer.toJson(boundingBoxX1), + 'boundingBoxY1': serializer.toJson(boundingBoxY1), + 'boundingBoxX2': serializer.toJson(boundingBoxX2), + 'boundingBoxY2': serializer.toJson(boundingBoxY2), + 'sourceType': serializer.toJson(sourceType), + }; + } + + AssetFaceEntityData copyWith({ + String? id, + String? assetId, + Value personId = const Value.absent(), + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType, + }) => AssetFaceEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId.present ? personId.value : this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + AssetFaceEntityData copyWithCompanion(AssetFaceEntityCompanion data) { + return AssetFaceEntityData( + id: data.id.present ? data.id.value : this.id, + assetId: data.assetId.present ? data.assetId.value : this.assetId, + personId: data.personId.present ? data.personId.value : this.personId, + imageWidth: data.imageWidth.present + ? data.imageWidth.value + : this.imageWidth, + imageHeight: data.imageHeight.present + ? data.imageHeight.value + : this.imageHeight, + boundingBoxX1: data.boundingBoxX1.present + ? data.boundingBoxX1.value + : this.boundingBoxX1, + boundingBoxY1: data.boundingBoxY1.present + ? data.boundingBoxY1.value + : this.boundingBoxY1, + boundingBoxX2: data.boundingBoxX2.present + ? data.boundingBoxX2.value + : this.boundingBoxX2, + boundingBoxY2: data.boundingBoxY2.present + ? data.boundingBoxY2.value + : this.boundingBoxY2, + sourceType: data.sourceType.present + ? data.sourceType.value + : this.sourceType, + ); + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityData(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is AssetFaceEntityData && + other.id == this.id && + other.assetId == this.assetId && + other.personId == this.personId && + other.imageWidth == this.imageWidth && + other.imageHeight == this.imageHeight && + other.boundingBoxX1 == this.boundingBoxX1 && + other.boundingBoxY1 == this.boundingBoxY1 && + other.boundingBoxX2 == this.boundingBoxX2 && + other.boundingBoxY2 == this.boundingBoxY2 && + other.sourceType == this.sourceType); +} + +class AssetFaceEntityCompanion extends UpdateCompanion { + final Value id; + final Value assetId; + final Value personId; + final Value imageWidth; + final Value imageHeight; + final Value boundingBoxX1; + final Value boundingBoxY1; + final Value boundingBoxX2; + final Value boundingBoxY2; + final Value sourceType; + const AssetFaceEntityCompanion({ + this.id = const Value.absent(), + this.assetId = const Value.absent(), + this.personId = const Value.absent(), + this.imageWidth = const Value.absent(), + this.imageHeight = const Value.absent(), + this.boundingBoxX1 = const Value.absent(), + this.boundingBoxY1 = const Value.absent(), + this.boundingBoxX2 = const Value.absent(), + this.boundingBoxY2 = const Value.absent(), + this.sourceType = const Value.absent(), + }); + AssetFaceEntityCompanion.insert({ + required String id, + required String assetId, + this.personId = const Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) : id = Value(id), + assetId = Value(assetId), + imageWidth = Value(imageWidth), + imageHeight = Value(imageHeight), + boundingBoxX1 = Value(boundingBoxX1), + boundingBoxY1 = Value(boundingBoxY1), + boundingBoxX2 = Value(boundingBoxX2), + boundingBoxY2 = Value(boundingBoxY2), + sourceType = Value(sourceType); + static Insertable custom({ + Expression? id, + Expression? assetId, + Expression? personId, + Expression? imageWidth, + Expression? imageHeight, + Expression? boundingBoxX1, + Expression? boundingBoxY1, + Expression? boundingBoxX2, + Expression? boundingBoxY2, + Expression? sourceType, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (assetId != null) 'asset_id': assetId, + if (personId != null) 'person_id': personId, + if (imageWidth != null) 'image_width': imageWidth, + if (imageHeight != null) 'image_height': imageHeight, + if (boundingBoxX1 != null) 'bounding_box_x1': boundingBoxX1, + if (boundingBoxY1 != null) 'bounding_box_y1': boundingBoxY1, + if (boundingBoxX2 != null) 'bounding_box_x2': boundingBoxX2, + if (boundingBoxY2 != null) 'bounding_box_y2': boundingBoxY2, + if (sourceType != null) 'source_type': sourceType, + }); + } + + AssetFaceEntityCompanion copyWith({ + Value? id, + Value? assetId, + Value? personId, + Value? imageWidth, + Value? imageHeight, + Value? boundingBoxX1, + Value? boundingBoxY1, + Value? boundingBoxX2, + Value? boundingBoxY2, + Value? sourceType, + }) { + return AssetFaceEntityCompanion( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId ?? this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (personId.present) { + map['person_id'] = Variable(personId.value); + } + if (imageWidth.present) { + map['image_width'] = Variable(imageWidth.value); + } + if (imageHeight.present) { + map['image_height'] = Variable(imageHeight.value); + } + if (boundingBoxX1.present) { + map['bounding_box_x1'] = Variable(boundingBoxX1.value); + } + if (boundingBoxY1.present) { + map['bounding_box_y1'] = Variable(boundingBoxY1.value); + } + if (boundingBoxX2.present) { + map['bounding_box_x2'] = Variable(boundingBoxX2.value); + } + if (boundingBoxY2.present) { + map['bounding_box_y2'] = Variable(boundingBoxY2.value); + } + if (sourceType.present) { + map['source_type'] = Variable(sourceType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityCompanion(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV5 extends GeneratedDatabase { + DatabaseAtV5(QueryExecutor e) : super(e); + late final UserEntity userEntity = UserEntity(this); + late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); + late final StackEntity stackEntity = StackEntity(this); + late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); + late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + late final Index uQRemoteAssetOwnerChecksum = Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); + late final PartnerEntity partnerEntity = PartnerEntity(this); + late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); + late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); + late final MemoryEntity memoryEntity = MemoryEntity(this); + late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); + late final PersonEntity personEntity = PersonEntity(this); + late final AssetFaceEntity assetFaceEntity = AssetFaceEntity(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + ]; + @override + int get schemaVersion => 5; + @override + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); +} diff --git a/mobile/test/fixtures/sync_stream.stub.dart b/mobile/test/fixtures/sync_stream.stub.dart index a89be5ad24..23c750d6d9 100644 --- a/mobile/test/fixtures/sync_stream.stub.dart +++ b/mobile/test/fixtures/sync_stream.stub.dart @@ -4,12 +4,28 @@ import 'package:openapi/api.dart'; abstract final class SyncStreamStub { static final userV1Admin = SyncEvent( type: SyncEntityType.userV1, - data: SyncUserV1(deletedAt: DateTime(2020), email: "admin@admin", id: "1", name: "Admin", avatarColor: null), + data: SyncUserV1( + deletedAt: DateTime(2020), + email: "admin@admin", + id: "1", + name: "Admin", + avatarColor: null, + hasProfileImage: false, + profileChangedAt: DateTime(2025), + ), ack: "1", ); static final userV1User = SyncEvent( type: SyncEntityType.userV1, - data: SyncUserV1(deletedAt: DateTime(2021), email: "user@user", id: "5", name: "User", avatarColor: null), + data: SyncUserV1( + deletedAt: DateTime(2021), + email: "user@user", + id: "5", + name: "User", + avatarColor: null, + hasProfileImage: false, + profileChangedAt: DateTime(2025), + ), ack: "5", ); static final userDeleteV1 = SyncEvent( diff --git a/mobile/test/fixtures/user.stub.dart b/mobile/test/fixtures/user.stub.dart index 764342520f..369e62440d 100644 --- a/mobile/test/fixtures/user.stub.dart +++ b/mobile/test/fixtures/user.stub.dart @@ -10,7 +10,7 @@ abstract final class UserStub { name: "admin", isAdmin: true, updatedAt: DateTime(2021), - profileImagePath: null, + profileChangedAt: DateTime(2021), avatarColor: AvatarColor.green, ); @@ -20,7 +20,7 @@ abstract final class UserStub { name: "user1", isAdmin: false, updatedAt: DateTime(2022), - profileImagePath: null, + profileChangedAt: DateTime(2022), avatarColor: AvatarColor.red, ); @@ -30,7 +30,7 @@ abstract final class UserStub { name: "user2", isAdmin: false, updatedAt: DateTime(2023), - profileImagePath: null, + profileChangedAt: DateTime(2023), avatarColor: AvatarColor.primary, ); } diff --git a/mobile/test/modules/shared/sync_service_test.dart b/mobile/test/modules/shared/sync_service_test.dart index b51a4d67fd..22fd3cacfc 100644 --- a/mobile/test/modules/shared/sync_service_test.dart +++ b/mobile/test/modules/shared/sync_service_test.dart @@ -66,7 +66,14 @@ void main() { final MockPartnerRepository partnerRepository = MockPartnerRepository(); final MockUserService userService = MockUserService(); - final owner = UserDto(id: "1", updatedAt: DateTime.now(), email: "a@b.c", name: "first last", isAdmin: false); + final owner = UserDto( + id: "1", + updatedAt: DateTime.now(), + email: "a@b.c", + name: "first last", + isAdmin: false, + profileChangedAt: DateTime(2021), + ); late SyncService s; setUpAll(() async { WidgetsFlutterBinding.ensureInitialized(); diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 1e9fddf79d..8c491ca471 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -15124,19 +15124,28 @@ "email": { "type": "string" }, + "hasProfileImage": { + "type": "boolean" + }, "id": { "type": "string" }, "name": { "type": "string" + }, + "profileChangedAt": { + "format": "date-time", + "type": "string" } }, "required": [ "avatarColor", "deletedAt", "email", + "hasProfileImage", "id", - "name" + "name", + "profileChangedAt" ], "type": "object" }, diff --git a/server/src/database.ts b/server/src/database.ts index 44bdefa080..052955636b 100644 --- a/server/src/database.ts +++ b/server/src/database.ts @@ -357,7 +357,7 @@ export const columns = { ], syncAlbumUser: ['album_user.albumsId as albumId', 'album_user.usersId as userId', 'album_user.role'], syncStack: ['stack.id', 'stack.createdAt', 'stack.updatedAt', 'stack.primaryAssetId', 'stack.ownerId'], - syncUser: ['id', 'name', 'email', 'avatarColor', 'deletedAt', 'updateId'], + syncUser: ['id', 'name', 'email', 'avatarColor', 'deletedAt', 'updateId', 'profileImagePath', 'profileChangedAt'], stack: ['stack.id', 'stack.primaryAssetId', 'ownerId'], syncAssetExif: [ 'asset_exif.assetId', diff --git a/server/src/dtos/sync.dto.ts b/server/src/dtos/sync.dto.ts index 92aea8f5e9..66061e7bbe 100644 --- a/server/src/dtos/sync.dto.ts +++ b/server/src/dtos/sync.dto.ts @@ -62,6 +62,8 @@ export class SyncUserV1 { @ValidateEnum({ enum: UserAvatarColor, name: 'UserAvatarColor', nullable: true }) avatarColor!: UserAvatarColor | null; deletedAt!: Date | null; + hasProfileImage!: boolean; + profileChangedAt!: Date; } @ExtraModel() @@ -74,8 +76,6 @@ export class SyncAuthUserV1 extends SyncUserV1 { quotaSizeInBytes!: number | null; @ApiProperty({ type: 'integer' }) quotaUsageInBytes!: number; - hasProfileImage!: boolean; - profileChangedAt!: Date; } @ExtraModel() diff --git a/server/src/queries/sync.repository.sql b/server/src/queries/sync.repository.sql index 7c7774a020..5c80460158 100644 --- a/server/src/queries/sync.repository.sql +++ b/server/src/queries/sync.repository.sql @@ -452,14 +452,14 @@ select "avatarColor", "deletedAt", "updateId", + "profileImagePath", + "profileChangedAt", "isAdmin", "pinCode", "oauthId", "storageLabel", "quotaSizeInBytes", - "quotaUsageInBytes", - "profileImagePath", - "profileChangedAt" + "quotaUsageInBytes" from "user" where @@ -896,7 +896,9 @@ select "email", "avatarColor", "deletedAt", - "updateId" + "updateId", + "profileImagePath", + "profileChangedAt" from "user" where diff --git a/server/src/repositories/sync.repository.ts b/server/src/repositories/sync.repository.ts index 486984118d..d72ddcfc4d 100644 --- a/server/src/repositories/sync.repository.ts +++ b/server/src/repositories/sync.repository.ts @@ -375,16 +375,7 @@ class AuthUserSync extends BaseSync { return this.db .selectFrom('user') .select(columns.syncUser) - .select([ - 'isAdmin', - 'pinCode', - 'oauthId', - 'storageLabel', - 'quotaSizeInBytes', - 'quotaUsageInBytes', - 'profileImagePath', - 'profileChangedAt', - ]) + .select(['isAdmin', 'pinCode', 'oauthId', 'storageLabel', 'quotaSizeInBytes', 'quotaUsageInBytes']) .$call(this.upsertTableFilters(ack)) .stream(); } diff --git a/server/src/schema/migrations/1753800911775-ProfileImageCheckpointRemoval.ts b/server/src/schema/migrations/1753800911775-ProfileImageCheckpointRemoval.ts new file mode 100644 index 0000000000..4f741f2113 --- /dev/null +++ b/server/src/schema/migrations/1753800911775-ProfileImageCheckpointRemoval.ts @@ -0,0 +1,25 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await sql`DELETE FROM session_sync_checkpoint + WHERE type IN ( + 'UserV1', + 'AssetV1', + 'PartnerAssetV1', + 'PartnerAssetBackfillV1', + 'AlbumAssetV1', + 'AlbumAssetBackfillV1' + )`.execute(db); +} + +export async function down(db: Kysely): Promise { + await sql`DELETE FROM session_sync_checkpoint + WHERE type IN ( + 'UserV1', + 'AssetV1', + 'PartnerAssetV1', + 'PartnerAssetBackfillV1', + 'AlbumAssetV1', + 'AlbumAssetBackfillV1' + )`.execute(db); +} diff --git a/server/src/services/sync.service.ts b/server/src/services/sync.service.ts index 57b953f12e..fee77f35ba 100644 --- a/server/src/services/sync.service.ts +++ b/server/src/services/sync.service.ts @@ -188,8 +188,8 @@ export class SyncService extends BaseService { const upsertType = SyncEntityType.UserV1; const upserts = this.syncRepository.user.getUpserts(checkpointMap[upsertType]); - for await (const { updateId, ...data } of upserts) { - send(response, { type: upsertType, ids: [updateId], data }); + for await (const { updateId, profileImagePath, ...data } of upserts) { + send(response, { type: upsertType, ids: [updateId], data: { ...data, hasProfileImage: !!profileImagePath } }); } } diff --git a/server/test/medium/specs/sync/sync-user.spec.ts b/server/test/medium/specs/sync/sync-user.spec.ts index 72661e119c..c5d572d7d6 100644 --- a/server/test/medium/specs/sync/sync-user.spec.ts +++ b/server/test/medium/specs/sync/sync-user.spec.ts @@ -35,9 +35,11 @@ describe(SyncEntityType.UserV1, () => { data: { deletedAt: user.deletedAt, email: user.email, + hasProfileImage: user.profileImagePath !== '', id: user.id, name: user.name, avatarColor: user.avatarColor, + profileChangedAt: user.profileChangedAt.toISOString(), }, type: 'UserV1', }, From d5a01c03105c0f59398134385210167d5ed4cf92 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 30 Jul 2025 12:21:02 -0400 Subject: [PATCH 119/748] fix(web): timeline time bucket issue (#20438) --- .../components/photos-page/asset-grid.svelte | 9 ++---- .../group-insertion-cache.svelte.ts | 6 ++-- .../internal/load-support.svelte.ts | 1 - .../internal/operations-support.svelte.ts | 4 +-- .../internal/search-support.svelte.ts | 6 ++-- .../timeline-manager/month-group.svelte.ts | 10 +++---- .../timeline-manager.svelte.ts | 8 ++--- .../lib/managers/timeline-manager/types.ts | 8 ++--- web/src/lib/utils/timeline-util.ts | 29 +++++++++---------- 9 files changed, 36 insertions(+), 45 deletions(-) diff --git a/web/src/lib/components/photos-page/asset-grid.svelte b/web/src/lib/components/photos-page/asset-grid.svelte index bd7900f2b6..808a4cf54c 100644 --- a/web/src/lib/components/photos-page/asset-grid.svelte +++ b/web/src/lib/components/photos-page/asset-grid.svelte @@ -29,12 +29,7 @@ import { deleteAssets, updateStackedAssetInTimeline, updateUnstackedAssetInTimeline } from '$lib/utils/actions'; import { archiveAssets, cancelMultiselect, selectAllAssets, stackAssets } from '$lib/utils/asset-utils'; import { navigate } from '$lib/utils/navigation'; - import { - getTimes, - toTimelineAsset, - type ScrubberListener, - type TimelinePlainYearMonth, - } from '$lib/utils/timeline-util'; + import { getTimes, toTimelineAsset, type ScrubberListener, type TimelineYearMonth } from '$lib/utils/timeline-util'; import { AssetVisibility, getAssetInfo, type AlbumResponseDto, type PersonResponseDto } from '@immich/sdk'; import { modalManager } from '@immich/ui'; import { DateTime } from 'luxon'; @@ -343,7 +338,7 @@ const monthsLength = timelineManager.months.length; for (let i = -1; i < monthsLength + 1; i++) { - let monthGroup: TimelinePlainYearMonth | undefined; + let monthGroup: TimelineYearMonth | undefined; let monthGroupHeight = 0; if (i === -1) { // lead-in diff --git a/web/src/lib/managers/timeline-manager/group-insertion-cache.svelte.ts b/web/src/lib/managers/timeline-manager/group-insertion-cache.svelte.ts index e511df9bf0..aa4bae8919 100644 --- a/web/src/lib/managers/timeline-manager/group-insertion-cache.svelte.ts +++ b/web/src/lib/managers/timeline-manager/group-insertion-cache.svelte.ts @@ -1,4 +1,4 @@ -import { setDifference, type TimelinePlainDate } from '$lib/utils/timeline-util'; +import { setDifference, type TimelineDate } from '$lib/utils/timeline-util'; import { AssetOrder } from '@immich/sdk'; import { SvelteSet } from 'svelte/reactivity'; import type { DayGroup } from './day-group.svelte'; @@ -13,11 +13,11 @@ export class GroupInsertionCache { changedDayGroups = new SvelteSet(); newDayGroups = new SvelteSet(); - getDayGroup({ year, month, day }: TimelinePlainDate): DayGroup | undefined { + getDayGroup({ year, month, day }: TimelineDate): DayGroup | undefined { return this.#lookupCache[year]?.[month]?.[day]; } - setDayGroup(dayGroup: DayGroup, { year, month, day }: TimelinePlainDate) { + setDayGroup(dayGroup: DayGroup, { year, month, day }: TimelineDate) { if (!this.#lookupCache[year]) { this.#lookupCache[year] = {}; } diff --git a/web/src/lib/managers/timeline-manager/internal/load-support.svelte.ts b/web/src/lib/managers/timeline-manager/internal/load-support.svelte.ts index 6146fdb600..82a9e8083d 100644 --- a/web/src/lib/managers/timeline-manager/internal/load-support.svelte.ts +++ b/web/src/lib/managers/timeline-manager/internal/load-support.svelte.ts @@ -1,7 +1,6 @@ import { authManager } from '$lib/managers/auth-manager.svelte'; import { toISOYearMonthUTC } from '$lib/utils/timeline-util'; import { getTimeBucket } from '@immich/sdk'; - import type { MonthGroup } from '../month-group.svelte'; import type { TimelineManager } from '../timeline-manager.svelte'; import type { TimelineManagerOptions } from '../types'; diff --git a/web/src/lib/managers/timeline-manager/internal/operations-support.svelte.ts b/web/src/lib/managers/timeline-manager/internal/operations-support.svelte.ts index 4419de2103..4bc99c0315 100644 --- a/web/src/lib/managers/timeline-manager/internal/operations-support.svelte.ts +++ b/web/src/lib/managers/timeline-manager/internal/operations-support.svelte.ts @@ -1,4 +1,4 @@ -import { setDifference, type TimelinePlainDate } from '$lib/utils/timeline-util'; +import { setDifference, type TimelineDate } from '$lib/utils/timeline-util'; import { AssetOrder } from '@immich/sdk'; import { SvelteSet } from 'svelte/reactivity'; @@ -70,7 +70,7 @@ export function runAssetOperation( const changedMonthGroups = new SvelteSet(); let idsToProcess = new SvelteSet(ids); const idsProcessed = new SvelteSet(); - const combinedMoveAssets: { asset: TimelineAsset; date: TimelinePlainDate }[][] = []; + const combinedMoveAssets: { asset: TimelineAsset; date: TimelineDate }[][] = []; for (const month of timelineManager.months) { if (idsToProcess.size > 0) { const { moveAssets, processedIds, changedGeometry } = month.runAssetOperation(idsToProcess, operation); diff --git a/web/src/lib/managers/timeline-manager/internal/search-support.svelte.ts b/web/src/lib/managers/timeline-manager/internal/search-support.svelte.ts index f85246933f..7e6ae734dc 100644 --- a/web/src/lib/managers/timeline-manager/internal/search-support.svelte.ts +++ b/web/src/lib/managers/timeline-manager/internal/search-support.svelte.ts @@ -1,4 +1,4 @@ -import { plainDateTimeCompare, type TimelinePlainYearMonth } from '$lib/utils/timeline-util'; +import { plainDateTimeCompare, type TimelineYearMonth } from '$lib/utils/timeline-util'; import { AssetOrder } from '@immich/sdk'; import type { MonthGroup } from '../month-group.svelte'; import type { TimelineManager } from '../timeline-manager.svelte'; @@ -42,7 +42,7 @@ export function findMonthGroupForAsset(timelineManager: TimelineManager, id: str export function getMonthGroupByDate( timelineManager: TimelineManager, - targetYearMonth: TimelinePlainYearMonth, + targetYearMonth: TimelineYearMonth, ): MonthGroup | undefined { return timelineManager.months.find( (month) => month.yearMonth.year === targetYearMonth.year && month.yearMonth.month === targetYearMonth.month, @@ -135,7 +135,7 @@ export async function retrieveRange(timelineManager: TimelineManager, start: Ass return range; } -export function findMonthGroupForDate(timelineManager: TimelineManager, targetYearMonth: TimelinePlainYearMonth) { +export function findMonthGroupForDate(timelineManager: TimelineManager, targetYearMonth: TimelineYearMonth) { for (const month of timelineManager.months) { const { year, month: monthNum } = month.yearMonth; if (monthNum === targetYearMonth.month && year === targetYearMonth.year) { diff --git a/web/src/lib/managers/timeline-manager/month-group.svelte.ts b/web/src/lib/managers/timeline-manager/month-group.svelte.ts index 9f7112963a..03d138f680 100644 --- a/web/src/lib/managers/timeline-manager/month-group.svelte.ts +++ b/web/src/lib/managers/timeline-manager/month-group.svelte.ts @@ -10,8 +10,8 @@ import { fromTimelinePlainYearMonth, getTimes, setDifference, - type TimelinePlainDateTime, - type TimelinePlainYearMonth, + type TimelineDateTime, + type TimelineYearMonth, } from '$lib/utils/timeline-util'; import { t } from 'svelte-i18n'; @@ -47,11 +47,11 @@ export class MonthGroup { isHeightActual: boolean = $state(false); readonly monthGroupTitle: string; - readonly yearMonth: TimelinePlainYearMonth; + readonly yearMonth: TimelineYearMonth; constructor( store: TimelineManager, - yearMonth: TimelinePlainYearMonth, + yearMonth: TimelineYearMonth, initialCount: number, order: AssetOrder = AssetOrder.Desc, ) { @@ -351,7 +351,7 @@ export class MonthGroup { } } - findClosest(target: TimelinePlainDateTime) { + findClosest(target: TimelineDateTime) { const targetDate = fromTimelinePlainDateTime(target); let closest = undefined; let smallestDiff = Infinity; diff --git a/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts b/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts index ff8d1b1347..2e31fa9bc1 100644 --- a/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts +++ b/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts @@ -3,7 +3,7 @@ import { AssetOrder, getAssetInfo, getTimeBuckets } from '@immich/sdk'; import { authManager } from '$lib/managers/auth-manager.svelte'; import { CancellableTask } from '$lib/utils/cancellable-task'; -import { toTimelineAsset, type TimelinePlainDateTime, type TimelinePlainYearMonth } from '$lib/utils/timeline-util'; +import { toTimelineAsset, type TimelineDateTime, type TimelineYearMonth } from '$lib/utils/timeline-util'; import { clamp, debounce, isEqual } from 'lodash-es'; import { SvelteDate, SvelteMap, SvelteSet } from 'svelte/reactivity'; @@ -387,7 +387,7 @@ export class TimelineManager { }; } - async loadMonthGroup(yearMonth: TimelinePlainYearMonth, options?: { cancelable: boolean }): Promise { + async loadMonthGroup(yearMonth: TimelineYearMonth, options?: { cancelable: boolean }): Promise { let cancelable = true; if (options) { cancelable = options.cancelable; @@ -433,7 +433,7 @@ export class TimelineManager { } } - async #loadMonthGroupAtTime(yearMonth: TimelinePlainYearMonth, options?: { cancelable: boolean }) { + async #loadMonthGroupAtTime(yearMonth: TimelineYearMonth, options?: { cancelable: boolean }) { await this.loadMonthGroup(yearMonth, options); return getMonthGroupByDate(this, yearMonth); } @@ -514,7 +514,7 @@ export class TimelineManager { return await getAssetWithOffset(this, assetDescriptor, interval, 'earlier'); } - async getClosestAssetToDate(dateTime: TimelinePlainDateTime) { + async getClosestAssetToDate(dateTime: TimelineDateTime) { const monthGroup = findMonthGroupForDate(this, dateTime); if (!monthGroup) { return; diff --git a/web/src/lib/managers/timeline-manager/types.ts b/web/src/lib/managers/timeline-manager/types.ts index 8e5523758b..18ee0426f3 100644 --- a/web/src/lib/managers/timeline-manager/types.ts +++ b/web/src/lib/managers/timeline-manager/types.ts @@ -1,4 +1,4 @@ -import type { TimelinePlainDate, TimelinePlainDateTime } from '$lib/utils/timeline-util'; +import type { TimelineDate, TimelineDateTime } from '$lib/utils/timeline-util'; import type { AssetStackResponseDto, AssetVisibility } from '@immich/sdk'; export type AssetApiGetTimeBucketsRequest = Parameters[0]; @@ -17,8 +17,8 @@ export type TimelineAsset = { ownerId: string; ratio: number; thumbhash: string | null; - localDateTime: TimelinePlainDateTime; - fileCreatedAt: TimelinePlainDateTime; + localDateTime: TimelineDateTime; + fileCreatedAt: TimelineDateTime; visibility: AssetVisibility; isFavorite: boolean; isTrashed: boolean; @@ -35,7 +35,7 @@ export type TimelineAsset = { export type AssetOperation = (asset: TimelineAsset) => { remove: boolean }; -export type MoveAsset = { asset: TimelineAsset; date: TimelinePlainDate }; +export type MoveAsset = { asset: TimelineAsset; date: TimelineDate }; export interface Viewport { width: number; diff --git a/web/src/lib/utils/timeline-util.ts b/web/src/lib/utils/timeline-util.ts index dc237c2223..c160c65922 100644 --- a/web/src/lib/utils/timeline-util.ts +++ b/web/src/lib/utils/timeline-util.ts @@ -7,16 +7,16 @@ import { SvelteSet } from 'svelte/reactivity'; import { get } from 'svelte/store'; // Move type definitions to the top -export type TimelinePlainYearMonth = { +export type TimelineYearMonth = { year: number; month: number; }; -export type TimelinePlainDate = TimelinePlainYearMonth & { +export type TimelineDate = TimelineYearMonth & { day: number; }; -export type TimelinePlainDateTime = TimelinePlainDate & { +export type TimelineDateTime = TimelineDate & { hour: number; minute: number; second: number; @@ -33,29 +33,26 @@ export type ScrubberListener = ( export const fromISODateTime = (isoDateTime: string, timeZone: string): DateTime => DateTime.fromISO(isoDateTime, { zone: timeZone, locale: get(locale) }) as DateTime; -export const fromISODateTimeToObject = (isoDateTime: string, timeZone: string): TimelinePlainDateTime => +export const fromISODateTimeToObject = (isoDateTime: string, timeZone: string): TimelineDateTime => (fromISODateTime(isoDateTime, timeZone) as DateTime).toObject(); // used for AssetResponseDto.localDateTime, amongst others export const fromISODateTimeUTC = (isoDateTimeUtc: string) => fromISODateTime(isoDateTimeUtc, 'UTC'); -export const fromISODateTimeUTCToObject = (isoDateTimeUtc: string): TimelinePlainDateTime => +export const fromISODateTimeUTCToObject = (isoDateTimeUtc: string): TimelineDateTime => (fromISODateTimeUTC(isoDateTimeUtc) as DateTime).toObject(); // used to create equivalent of AssetResponseDto.localDateTime in UTC, but without timezone information export const fromISODateTimeTruncateTZToObject = ( isoDateTimeUtc: string, timeZone: string | undefined, -): TimelinePlainDateTime => +): TimelineDateTime => ( fromISODateTime(isoDateTimeUtc, timeZone ?? 'UTC').setZone('UTC', { keepLocalTime: true }) as DateTime ).toObject(); // Used to derive a local date time from an ISO string and a UTC offset in hours -export const fromISODateTimeWithOffsetToObject = ( - isoDateTimeUtc: string, - utcOffsetHours: number, -): TimelinePlainDateTime => { +export const fromISODateTimeWithOffsetToObject = (isoDateTimeUtc: string, utcOffsetHours: number): TimelineDateTime => { const utcDateTime = fromISODateTimeUTC(isoDateTimeUtc); // Apply the offset to get the local time @@ -82,23 +79,23 @@ export const getTimes = (isoDateTimeUtc: string, localUtcOffsetHours: number) => }; }; -export const fromTimelinePlainDateTime = (timelineDateTime: TimelinePlainDateTime): DateTime => +export const fromTimelinePlainDateTime = (timelineDateTime: TimelineDateTime): DateTime => DateTime.fromObject(timelineDateTime, { zone: 'local', locale: get(locale) }) as DateTime; -export const fromTimelinePlainDate = (timelineYearMonth: TimelinePlainDate): DateTime => +export const fromTimelinePlainDate = (timelineYearMonth: TimelineDate): DateTime => DateTime.fromObject( { year: timelineYearMonth.year, month: timelineYearMonth.month, day: timelineYearMonth.day }, { zone: 'local', locale: get(locale) }, ) as DateTime; -export const fromTimelinePlainYearMonth = (timelineYearMonth: TimelinePlainYearMonth): DateTime => +export const fromTimelinePlainYearMonth = (timelineYearMonth: TimelineYearMonth): DateTime => DateTime.fromObject( { year: timelineYearMonth.year, month: timelineYearMonth.month }, { zone: 'local', locale: get(locale) }, ) as DateTime; -export const toISOYearMonthUTC = (timelineYearMonth: TimelinePlainYearMonth): string => - (fromTimelinePlainYearMonth(timelineYearMonth).setZone('UTC', { keepLocalTime: true }) as DateTime).toISO(); +export const toISOYearMonthUTC = ({ year, month }: TimelineYearMonth): string => + `${year}-${month.toString().padStart(2, '0')}-01T00:00:00.000Z`; export function formatMonthGroupTitle(_date: DateTime): string { if (!_date.isValid) { @@ -193,7 +190,7 @@ export const toTimelineAsset = (unknownAsset: AssetResponseDto | TimelineAsset): export const isTimelineAsset = (unknownAsset: AssetResponseDto | TimelineAsset): unknownAsset is TimelineAsset => (unknownAsset as TimelineAsset).ratio !== undefined; -export const plainDateTimeCompare = (ascending: boolean, a: TimelinePlainDateTime, b: TimelinePlainDateTime) => { +export const plainDateTimeCompare = (ascending: boolean, a: TimelineDateTime, b: TimelineDateTime) => { const [aDateTime, bDateTime] = ascending ? [a, b] : [b, a]; if (aDateTime.year !== bDateTime.year) { From 749f999f2aade7c3b40ae7fe305d7ec9a2d271bf Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 30 Jul 2025 12:29:36 -0400 Subject: [PATCH 120/748] feat: better endpoint descriptions (#20439) --- mobile/openapi/lib/api/activities_api.dart | 28 +- mobile/openapi/lib/api/albums_api.dart | 75 ++- mobile/openapi/lib/api/api_keys_api.dart | 33 +- mobile/openapi/lib/api/assets_api.dart | 71 ++- .../openapi/lib/api/authentication_api.dart | 28 +- mobile/openapi/lib/api/deprecated_api.dart | 4 +- mobile/openapi/lib/api/download_api.dart | 14 +- mobile/openapi/lib/api/duplicates_api.dart | 19 +- mobile/openapi/lib/api/faces_api.dart | 28 +- mobile/openapi/lib/api/jobs_api.dart | 19 +- mobile/openapi/lib/api/libraries_api.dart | 47 +- mobile/openapi/lib/api/map_api.dart | 34 +- mobile/openapi/lib/api/memories_api.dart | 56 +- mobile/openapi/lib/api/notifications_api.dart | 42 +- mobile/openapi/lib/api/partners_api.dart | 28 +- mobile/openapi/lib/api/people_api.dart | 77 ++- mobile/openapi/lib/api/search_api.dart | 66 +- mobile/openapi/lib/api/server_api.dart | 37 +- mobile/openapi/lib/api/sessions_api.dart | 38 +- mobile/openapi/lib/api/shared_links_api.dart | 69 +- mobile/openapi/lib/api/stacks_api.dart | 49 +- mobile/openapi/lib/api/sync_api.dart | 26 +- mobile/openapi/lib/api/system_config_api.dart | 22 +- .../openapi/lib/api/system_metadata_api.dart | 22 +- mobile/openapi/lib/api/tags_api.dart | 61 +- mobile/openapi/lib/api/timeline_api.dart | 14 +- mobile/openapi/lib/api/trash_api.dart | 17 +- mobile/openapi/lib/api/users_admin_api.dart | 63 +- mobile/openapi/lib/api/users_api.dart | 89 ++- open-api/immich-openapi-specs.json | 597 ++++++++++++------ open-api/typescript-sdk/src/fetch-client.ts | 522 ++++++++++++++- server/src/enum.ts | 5 + server/src/middleware/auth.guard.ts | 12 +- server/src/utils/misc.ts | 34 +- 34 files changed, 1918 insertions(+), 428 deletions(-) diff --git a/mobile/openapi/lib/api/activities_api.dart b/mobile/openapi/lib/api/activities_api.dart index 5c83ba7db9..67015499fa 100644 --- a/mobile/openapi/lib/api/activities_api.dart +++ b/mobile/openapi/lib/api/activities_api.dart @@ -16,7 +16,10 @@ class ActivitiesApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /activities' operation and returns the [Response]. + /// This endpoint requires the `activity.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [ActivityCreateDto] activityCreateDto (required): @@ -45,6 +48,8 @@ class ActivitiesApi { ); } + /// This endpoint requires the `activity.create` permission. + /// /// Parameters: /// /// * [ActivityCreateDto] activityCreateDto (required): @@ -63,7 +68,10 @@ class ActivitiesApi { return null; } - /// Performs an HTTP 'DELETE /activities/{id}' operation and returns the [Response]. + /// This endpoint requires the `activity.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -93,6 +101,8 @@ class ActivitiesApi { ); } + /// This endpoint requires the `activity.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -103,7 +113,10 @@ class ActivitiesApi { } } - /// Performs an HTTP 'GET /activities' operation and returns the [Response]. + /// This endpoint requires the `activity.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] albumId (required): @@ -154,6 +167,8 @@ class ActivitiesApi { ); } + /// This endpoint requires the `activity.read` permission. + /// /// Parameters: /// /// * [String] albumId (required): @@ -183,7 +198,10 @@ class ActivitiesApi { return null; } - /// Performs an HTTP 'GET /activities/statistics' operation and returns the [Response]. + /// This endpoint requires the `activity.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] albumId (required): @@ -219,6 +237,8 @@ class ActivitiesApi { ); } + /// This endpoint requires the `activity.statistics` permission. + /// /// Parameters: /// /// * [String] albumId (required): diff --git a/mobile/openapi/lib/api/albums_api.dart b/mobile/openapi/lib/api/albums_api.dart index fa7a562adb..10674b894f 100644 --- a/mobile/openapi/lib/api/albums_api.dart +++ b/mobile/openapi/lib/api/albums_api.dart @@ -16,7 +16,10 @@ class AlbumsApi { final ApiClient apiClient; - /// Performs an HTTP 'PUT /albums/{id}/assets' operation and returns the [Response]. + /// This endpoint requires the `albumAsset.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -59,6 +62,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `albumAsset.create` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -86,7 +91,10 @@ class AlbumsApi { return null; } - /// Performs an HTTP 'PUT /albums/{id}/users' operation and returns the [Response]. + /// This endpoint requires the `albumUser.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -118,6 +126,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `albumUser.create` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -138,7 +148,10 @@ class AlbumsApi { return null; } - /// Performs an HTTP 'POST /albums' operation and returns the [Response]. + /// This endpoint requires the `album.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [CreateAlbumDto] createAlbumDto (required): @@ -167,6 +180,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `album.create` permission. + /// /// Parameters: /// /// * [CreateAlbumDto] createAlbumDto (required): @@ -185,7 +200,10 @@ class AlbumsApi { return null; } - /// Performs an HTTP 'DELETE /albums/{id}' operation and returns the [Response]. + /// This endpoint requires the `album.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -215,6 +233,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `album.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -225,7 +245,10 @@ class AlbumsApi { } } - /// Performs an HTTP 'GET /albums/{id}' operation and returns the [Response]. + /// This endpoint requires the `album.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -271,6 +294,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `album.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -295,7 +320,9 @@ class AlbumsApi { return null; } - /// Performs an HTTP 'GET /albums/statistics' operation and returns the [Response]. + /// This endpoint requires the `album.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAlbumStatisticsWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/albums/statistics'; @@ -321,6 +348,7 @@ class AlbumsApi { ); } + /// This endpoint requires the `album.statistics` permission. Future getAlbumStatistics() async { final response = await getAlbumStatisticsWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -336,7 +364,10 @@ class AlbumsApi { return null; } - /// Performs an HTTP 'GET /albums' operation and returns the [Response]. + /// This endpoint requires the `album.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] assetId: @@ -375,6 +406,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `album.read` permission. + /// /// Parameters: /// /// * [String] assetId: @@ -399,7 +432,10 @@ class AlbumsApi { return null; } - /// Performs an HTTP 'DELETE /albums/{id}/assets' operation and returns the [Response]. + /// This endpoint requires the `albumAsset.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -431,6 +467,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `albumAsset.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -454,7 +492,10 @@ class AlbumsApi { return null; } - /// Performs an HTTP 'DELETE /albums/{id}/user/{userId}' operation and returns the [Response]. + /// This endpoint requires the `albumUser.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -487,6 +528,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `albumUser.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -499,7 +542,10 @@ class AlbumsApi { } } - /// Performs an HTTP 'PATCH /albums/{id}' operation and returns the [Response]. + /// This endpoint requires the `album.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -531,6 +577,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `album.update` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -551,7 +599,10 @@ class AlbumsApi { return null; } - /// Performs an HTTP 'PUT /albums/{id}/user/{userId}' operation and returns the [Response]. + /// This endpoint requires the `albumUser.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -586,6 +637,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `albumUser.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/api_keys_api.dart b/mobile/openapi/lib/api/api_keys_api.dart index cf54ac5c04..e86c63bc6e 100644 --- a/mobile/openapi/lib/api/api_keys_api.dart +++ b/mobile/openapi/lib/api/api_keys_api.dart @@ -16,7 +16,10 @@ class APIKeysApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /api-keys' operation and returns the [Response]. + /// This endpoint requires the `apiKey.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [APIKeyCreateDto] aPIKeyCreateDto (required): @@ -45,6 +48,8 @@ class APIKeysApi { ); } + /// This endpoint requires the `apiKey.create` permission. + /// /// Parameters: /// /// * [APIKeyCreateDto] aPIKeyCreateDto (required): @@ -63,7 +68,10 @@ class APIKeysApi { return null; } - /// Performs an HTTP 'DELETE /api-keys/{id}' operation and returns the [Response]. + /// This endpoint requires the `apiKey.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -93,6 +101,8 @@ class APIKeysApi { ); } + /// This endpoint requires the `apiKey.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -103,7 +113,10 @@ class APIKeysApi { } } - /// Performs an HTTP 'GET /api-keys/{id}' operation and returns the [Response]. + /// This endpoint requires the `apiKey.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -133,6 +146,8 @@ class APIKeysApi { ); } + /// This endpoint requires the `apiKey.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -151,7 +166,9 @@ class APIKeysApi { return null; } - /// Performs an HTTP 'GET /api-keys' operation and returns the [Response]. + /// This endpoint requires the `apiKey.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getApiKeysWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/api-keys'; @@ -177,6 +194,7 @@ class APIKeysApi { ); } + /// This endpoint requires the `apiKey.read` permission. Future?> getApiKeys() async { final response = await getApiKeysWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -195,7 +213,10 @@ class APIKeysApi { return null; } - /// Performs an HTTP 'PUT /api-keys/{id}' operation and returns the [Response]. + /// This endpoint requires the `apiKey.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -227,6 +248,8 @@ class APIKeysApi { ); } + /// This endpoint requires the `apiKey.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/assets_api.dart b/mobile/openapi/lib/api/assets_api.dart index 3cb62785be..c0de1a0801 100644 --- a/mobile/openapi/lib/api/assets_api.dart +++ b/mobile/openapi/lib/api/assets_api.dart @@ -128,7 +128,10 @@ class AssetsApi { return null; } - /// Performs an HTTP 'DELETE /assets' operation and returns the [Response]. + /// This endpoint requires the `asset.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [AssetBulkDeleteDto] assetBulkDeleteDto (required): @@ -157,6 +160,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.delete` permission. + /// /// Parameters: /// /// * [AssetBulkDeleteDto] assetBulkDeleteDto (required): @@ -167,7 +172,10 @@ class AssetsApi { } } - /// Performs an HTTP 'GET /assets/{id}/original' operation and returns the [Response]. + /// This endpoint requires the `asset.download` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -208,6 +216,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.download` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -290,7 +300,10 @@ class AssetsApi { return null; } - /// Performs an HTTP 'GET /assets/{id}' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -331,6 +344,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -353,7 +368,10 @@ class AssetsApi { return null; } - /// Performs an HTTP 'GET /assets/statistics' operation and returns the [Response]. + /// This endpoint requires the `asset.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [bool] isFavorite: @@ -396,6 +414,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.statistics` permission. + /// /// Parameters: /// /// * [bool] isFavorite: @@ -418,7 +438,7 @@ class AssetsApi { return null; } - /// This property was deprecated in v1.116.0 + /// This property was deprecated in v1.116.0. This endpoint requires the `asset.read` permission. /// /// Note: This method returns the HTTP [Response]. /// @@ -454,7 +474,7 @@ class AssetsApi { ); } - /// This property was deprecated in v1.116.0 + /// This property was deprecated in v1.116.0. This endpoint requires the `asset.read` permission. /// /// Parameters: /// @@ -477,7 +497,10 @@ class AssetsApi { return null; } - /// Performs an HTTP 'GET /assets/{id}/video/playback' operation and returns the [Response]. + /// This endpoint requires the `asset.view` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -518,6 +541,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.view` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -542,7 +567,7 @@ class AssetsApi { /// replaceAsset /// - /// Replace the asset with new file, without changing its id + /// Replace the asset with new file, without changing its id. This endpoint requires the `asset.replace` permission. /// /// Note: This method returns the HTTP [Response]. /// @@ -636,7 +661,7 @@ class AssetsApi { /// replaceAsset /// - /// Replace the asset with new file, without changing its id + /// Replace the asset with new file, without changing its id. This endpoint requires the `asset.replace` permission. /// /// Parameters: /// @@ -713,7 +738,10 @@ class AssetsApi { } } - /// Performs an HTTP 'PUT /assets/{id}' operation and returns the [Response]. + /// This endpoint requires the `asset.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -745,6 +773,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.update` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -765,7 +795,10 @@ class AssetsApi { return null; } - /// Performs an HTTP 'PUT /assets' operation and returns the [Response]. + /// This endpoint requires the `asset.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [AssetBulkUpdateDto] assetBulkUpdateDto (required): @@ -794,6 +827,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.update` permission. + /// /// Parameters: /// /// * [AssetBulkUpdateDto] assetBulkUpdateDto (required): @@ -804,7 +839,10 @@ class AssetsApi { } } - /// Performs an HTTP 'POST /assets' operation and returns the [Response]. + /// This endpoint requires the `asset.upload` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [MultipartFile] assetData (required): @@ -922,6 +960,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.upload` permission. + /// /// Parameters: /// /// * [MultipartFile] assetData (required): @@ -967,7 +1007,10 @@ class AssetsApi { return null; } - /// Performs an HTTP 'GET /assets/{id}/thumbnail' operation and returns the [Response]. + /// This endpoint requires the `asset.view` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -1013,6 +1056,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.view` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/authentication_api.dart b/mobile/openapi/lib/api/authentication_api.dart index 5482a9fc51..a74af33a43 100644 --- a/mobile/openapi/lib/api/authentication_api.dart +++ b/mobile/openapi/lib/api/authentication_api.dart @@ -16,7 +16,10 @@ class AuthenticationApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /auth/change-password' operation and returns the [Response]. + /// This endpoint requires the `auth.changePassword` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [ChangePasswordDto] changePasswordDto (required): @@ -45,6 +48,8 @@ class AuthenticationApi { ); } + /// This endpoint requires the `auth.changePassword` permission. + /// /// Parameters: /// /// * [ChangePasswordDto] changePasswordDto (required): @@ -63,7 +68,10 @@ class AuthenticationApi { return null; } - /// Performs an HTTP 'PUT /auth/pin-code' operation and returns the [Response]. + /// This endpoint requires the `pinCode.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [PinCodeChangeDto] pinCodeChangeDto (required): @@ -92,6 +100,8 @@ class AuthenticationApi { ); } + /// This endpoint requires the `pinCode.update` permission. + /// /// Parameters: /// /// * [PinCodeChangeDto] pinCodeChangeDto (required): @@ -264,7 +274,10 @@ class AuthenticationApi { return null; } - /// Performs an HTTP 'DELETE /auth/pin-code' operation and returns the [Response]. + /// This endpoint requires the `pinCode.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [PinCodeResetDto] pinCodeResetDto (required): @@ -293,6 +306,8 @@ class AuthenticationApi { ); } + /// This endpoint requires the `pinCode.delete` permission. + /// /// Parameters: /// /// * [PinCodeResetDto] pinCodeResetDto (required): @@ -303,7 +318,10 @@ class AuthenticationApi { } } - /// Performs an HTTP 'POST /auth/pin-code' operation and returns the [Response]. + /// This endpoint requires the `pinCode.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [PinCodeSetupDto] pinCodeSetupDto (required): @@ -332,6 +350,8 @@ class AuthenticationApi { ); } + /// This endpoint requires the `pinCode.create` permission. + /// /// Parameters: /// /// * [PinCodeSetupDto] pinCodeSetupDto (required): diff --git a/mobile/openapi/lib/api/deprecated_api.dart b/mobile/openapi/lib/api/deprecated_api.dart index 7aa9662c23..f9a496b990 100644 --- a/mobile/openapi/lib/api/deprecated_api.dart +++ b/mobile/openapi/lib/api/deprecated_api.dart @@ -16,7 +16,7 @@ class DeprecatedApi { final ApiClient apiClient; - /// This property was deprecated in v1.116.0 + /// This property was deprecated in v1.116.0. This endpoint requires the `asset.read` permission. /// /// Note: This method returns the HTTP [Response]. /// @@ -52,7 +52,7 @@ class DeprecatedApi { ); } - /// This property was deprecated in v1.116.0 + /// This property was deprecated in v1.116.0. This endpoint requires the `asset.read` permission. /// /// Parameters: /// diff --git a/mobile/openapi/lib/api/download_api.dart b/mobile/openapi/lib/api/download_api.dart index 675996f932..62c97bfc9c 100644 --- a/mobile/openapi/lib/api/download_api.dart +++ b/mobile/openapi/lib/api/download_api.dart @@ -16,7 +16,10 @@ class DownloadApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /download/archive' operation and returns the [Response]. + /// This endpoint requires the `asset.download` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [AssetIdsDto] assetIdsDto (required): @@ -56,6 +59,8 @@ class DownloadApi { ); } + /// This endpoint requires the `asset.download` permission. + /// /// Parameters: /// /// * [AssetIdsDto] assetIdsDto (required): @@ -78,7 +83,10 @@ class DownloadApi { return null; } - /// Performs an HTTP 'POST /download/info' operation and returns the [Response]. + /// This endpoint requires the `asset.download` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [DownloadInfoDto] downloadInfoDto (required): @@ -118,6 +126,8 @@ class DownloadApi { ); } + /// This endpoint requires the `asset.download` permission. + /// /// Parameters: /// /// * [DownloadInfoDto] downloadInfoDto (required): diff --git a/mobile/openapi/lib/api/duplicates_api.dart b/mobile/openapi/lib/api/duplicates_api.dart index d8b45d21a2..9df6e46586 100644 --- a/mobile/openapi/lib/api/duplicates_api.dart +++ b/mobile/openapi/lib/api/duplicates_api.dart @@ -16,7 +16,10 @@ class DuplicatesApi { final ApiClient apiClient; - /// Performs an HTTP 'DELETE /duplicates/{id}' operation and returns the [Response]. + /// This endpoint requires the `duplicate.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -46,6 +49,8 @@ class DuplicatesApi { ); } + /// This endpoint requires the `duplicate.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -56,7 +61,10 @@ class DuplicatesApi { } } - /// Performs an HTTP 'DELETE /duplicates' operation and returns the [Response]. + /// This endpoint requires the `duplicate.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -85,6 +93,8 @@ class DuplicatesApi { ); } + /// This endpoint requires the `duplicate.delete` permission. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -95,7 +105,9 @@ class DuplicatesApi { } } - /// Performs an HTTP 'GET /duplicates' operation and returns the [Response]. + /// This endpoint requires the `duplicate.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAssetDuplicatesWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/duplicates'; @@ -121,6 +133,7 @@ class DuplicatesApi { ); } + /// This endpoint requires the `duplicate.read` permission. Future?> getAssetDuplicates() async { final response = await getAssetDuplicatesWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { diff --git a/mobile/openapi/lib/api/faces_api.dart b/mobile/openapi/lib/api/faces_api.dart index 44e3d53f8e..2f8e6be60d 100644 --- a/mobile/openapi/lib/api/faces_api.dart +++ b/mobile/openapi/lib/api/faces_api.dart @@ -16,7 +16,10 @@ class FacesApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /faces' operation and returns the [Response]. + /// This endpoint requires the `face.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [AssetFaceCreateDto] assetFaceCreateDto (required): @@ -45,6 +48,8 @@ class FacesApi { ); } + /// This endpoint requires the `face.create` permission. + /// /// Parameters: /// /// * [AssetFaceCreateDto] assetFaceCreateDto (required): @@ -55,7 +60,10 @@ class FacesApi { } } - /// Performs an HTTP 'DELETE /faces/{id}' operation and returns the [Response]. + /// This endpoint requires the `face.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -87,6 +95,8 @@ class FacesApi { ); } + /// This endpoint requires the `face.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -99,7 +109,10 @@ class FacesApi { } } - /// Performs an HTTP 'GET /faces' operation and returns the [Response]. + /// This endpoint requires the `face.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -130,6 +143,8 @@ class FacesApi { ); } + /// This endpoint requires the `face.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -151,7 +166,10 @@ class FacesApi { return null; } - /// Performs an HTTP 'PUT /faces/{id}' operation and returns the [Response]. + /// This endpoint requires the `face.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -183,6 +201,8 @@ class FacesApi { ); } + /// This endpoint requires the `face.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/jobs_api.dart b/mobile/openapi/lib/api/jobs_api.dart index 182bb14e4f..4c935828a0 100644 --- a/mobile/openapi/lib/api/jobs_api.dart +++ b/mobile/openapi/lib/api/jobs_api.dart @@ -16,7 +16,10 @@ class JobsApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /jobs' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `job.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [JobCreateDto] jobCreateDto (required): @@ -45,6 +48,8 @@ class JobsApi { ); } + /// This endpoint is an admin-only route, and requires the `job.create` permission. + /// /// Parameters: /// /// * [JobCreateDto] jobCreateDto (required): @@ -55,7 +60,9 @@ class JobsApi { } } - /// Performs an HTTP 'GET /jobs' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `job.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAllJobsStatusWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/jobs'; @@ -81,6 +88,7 @@ class JobsApi { ); } + /// This endpoint is an admin-only route, and requires the `job.read` permission. Future getAllJobsStatus() async { final response = await getAllJobsStatusWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -96,7 +104,10 @@ class JobsApi { return null; } - /// Performs an HTTP 'PUT /jobs/{id}' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `job.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [JobName] id (required): @@ -128,6 +139,8 @@ class JobsApi { ); } + /// This endpoint is an admin-only route, and requires the `job.create` permission. + /// /// Parameters: /// /// * [JobName] id (required): diff --git a/mobile/openapi/lib/api/libraries_api.dart b/mobile/openapi/lib/api/libraries_api.dart index 86acce76b4..9258f8e3eb 100644 --- a/mobile/openapi/lib/api/libraries_api.dart +++ b/mobile/openapi/lib/api/libraries_api.dart @@ -16,7 +16,10 @@ class LibrariesApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /libraries' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `library.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [CreateLibraryDto] createLibraryDto (required): @@ -45,6 +48,8 @@ class LibrariesApi { ); } + /// This endpoint is an admin-only route, and requires the `library.create` permission. + /// /// Parameters: /// /// * [CreateLibraryDto] createLibraryDto (required): @@ -63,7 +68,10 @@ class LibrariesApi { return null; } - /// Performs an HTTP 'DELETE /libraries/{id}' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `library.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -93,6 +101,8 @@ class LibrariesApi { ); } + /// This endpoint is an admin-only route, and requires the `library.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -103,7 +113,9 @@ class LibrariesApi { } } - /// Performs an HTTP 'GET /libraries' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `library.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAllLibrariesWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/libraries'; @@ -129,6 +141,7 @@ class LibrariesApi { ); } + /// This endpoint is an admin-only route, and requires the `library.read` permission. Future?> getAllLibraries() async { final response = await getAllLibrariesWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -147,7 +160,10 @@ class LibrariesApi { return null; } - /// Performs an HTTP 'GET /libraries/{id}' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `library.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -177,6 +193,8 @@ class LibrariesApi { ); } + /// This endpoint is an admin-only route, and requires the `library.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -195,7 +213,10 @@ class LibrariesApi { return null; } - /// Performs an HTTP 'GET /libraries/{id}/statistics' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `library.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -225,6 +246,8 @@ class LibrariesApi { ); } + /// This endpoint is an admin-only route, and requires the `library.statistics` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -243,7 +266,10 @@ class LibrariesApi { return null; } - /// Performs an HTTP 'POST /libraries/{id}/scan' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `library.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -273,6 +299,8 @@ class LibrariesApi { ); } + /// This endpoint is an admin-only route, and requires the `library.update` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -283,7 +311,10 @@ class LibrariesApi { } } - /// Performs an HTTP 'PUT /libraries/{id}' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `library.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -315,6 +346,8 @@ class LibrariesApi { ); } + /// This endpoint is an admin-only route, and requires the `library.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/map_api.dart b/mobile/openapi/lib/api/map_api.dart index ffe72df453..da4f3dffcc 100644 --- a/mobile/openapi/lib/api/map_api.dart +++ b/mobile/openapi/lib/api/map_api.dart @@ -19,18 +19,18 @@ class MapApi { /// Performs an HTTP 'GET /map/markers' operation and returns the [Response]. /// Parameters: /// - /// * [DateTime] fileCreatedAfter: - /// - /// * [DateTime] fileCreatedBefore: - /// /// * [bool] isArchived: /// /// * [bool] isFavorite: /// + /// * [DateTime] fileCreatedAfter: + /// + /// * [DateTime] fileCreatedBefore: + /// /// * [bool] withPartners: /// /// * [bool] withSharedAlbums: - Future getMapMarkersWithHttpInfo({ DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, bool? isArchived, bool? isFavorite, bool? withPartners, bool? withSharedAlbums, }) async { + Future getMapMarkersWithHttpInfo({ bool? isArchived, bool? isFavorite, DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, bool? withPartners, bool? withSharedAlbums, }) async { // ignore: prefer_const_declarations final apiPath = r'/map/markers'; @@ -41,18 +41,18 @@ class MapApi { final headerParams = {}; final formParams = {}; - if (fileCreatedAfter != null) { - queryParams.addAll(_queryParams('', 'fileCreatedAfter', fileCreatedAfter)); - } - if (fileCreatedBefore != null) { - queryParams.addAll(_queryParams('', 'fileCreatedBefore', fileCreatedBefore)); - } if (isArchived != null) { queryParams.addAll(_queryParams('', 'isArchived', isArchived)); } if (isFavorite != null) { queryParams.addAll(_queryParams('', 'isFavorite', isFavorite)); } + if (fileCreatedAfter != null) { + queryParams.addAll(_queryParams('', 'fileCreatedAfter', fileCreatedAfter)); + } + if (fileCreatedBefore != null) { + queryParams.addAll(_queryParams('', 'fileCreatedBefore', fileCreatedBefore)); + } if (withPartners != null) { queryParams.addAll(_queryParams('', 'withPartners', withPartners)); } @@ -76,19 +76,19 @@ class MapApi { /// Parameters: /// - /// * [DateTime] fileCreatedAfter: - /// - /// * [DateTime] fileCreatedBefore: - /// /// * [bool] isArchived: /// /// * [bool] isFavorite: /// + /// * [DateTime] fileCreatedAfter: + /// + /// * [DateTime] fileCreatedBefore: + /// /// * [bool] withPartners: /// /// * [bool] withSharedAlbums: - Future?> getMapMarkers({ DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, bool? isArchived, bool? isFavorite, bool? withPartners, bool? withSharedAlbums, }) async { - final response = await getMapMarkersWithHttpInfo( fileCreatedAfter: fileCreatedAfter, fileCreatedBefore: fileCreatedBefore, isArchived: isArchived, isFavorite: isFavorite, withPartners: withPartners, withSharedAlbums: withSharedAlbums, ); + Future?> getMapMarkers({ bool? isArchived, bool? isFavorite, DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, bool? withPartners, bool? withSharedAlbums, }) async { + final response = await getMapMarkersWithHttpInfo( isArchived: isArchived, isFavorite: isFavorite, fileCreatedAfter: fileCreatedAfter, fileCreatedBefore: fileCreatedBefore, withPartners: withPartners, withSharedAlbums: withSharedAlbums, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/memories_api.dart b/mobile/openapi/lib/api/memories_api.dart index 9b62cce9c0..f9280101e6 100644 --- a/mobile/openapi/lib/api/memories_api.dart +++ b/mobile/openapi/lib/api/memories_api.dart @@ -16,7 +16,10 @@ class MemoriesApi { final ApiClient apiClient; - /// Performs an HTTP 'PUT /memories/{id}/assets' operation and returns the [Response]. + /// This endpoint requires the `memoryAsset.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -48,6 +51,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memoryAsset.create` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -71,7 +76,10 @@ class MemoriesApi { return null; } - /// Performs an HTTP 'POST /memories' operation and returns the [Response]. + /// This endpoint requires the `memory.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [MemoryCreateDto] memoryCreateDto (required): @@ -100,6 +108,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memory.create` permission. + /// /// Parameters: /// /// * [MemoryCreateDto] memoryCreateDto (required): @@ -118,7 +128,10 @@ class MemoriesApi { return null; } - /// Performs an HTTP 'DELETE /memories/{id}' operation and returns the [Response]. + /// This endpoint requires the `memory.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -148,6 +161,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memory.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -158,7 +173,10 @@ class MemoriesApi { } } - /// Performs an HTTP 'GET /memories/{id}' operation and returns the [Response]. + /// This endpoint requires the `memory.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -188,6 +206,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memory.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -206,7 +226,10 @@ class MemoriesApi { return null; } - /// Performs an HTTP 'GET /memories/statistics' operation and returns the [Response]. + /// This endpoint requires the `memory.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [DateTime] for_: @@ -254,6 +277,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memory.statistics` permission. + /// /// Parameters: /// /// * [DateTime] for_: @@ -278,7 +303,10 @@ class MemoriesApi { return null; } - /// Performs an HTTP 'DELETE /memories/{id}/assets' operation and returns the [Response]. + /// This endpoint requires the `memoryAsset.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -310,6 +338,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memoryAsset.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -333,7 +363,10 @@ class MemoriesApi { return null; } - /// Performs an HTTP 'GET /memories' operation and returns the [Response]. + /// This endpoint requires the `memory.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [DateTime] for_: @@ -381,6 +414,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memory.read` permission. + /// /// Parameters: /// /// * [DateTime] for_: @@ -408,7 +443,10 @@ class MemoriesApi { return null; } - /// Performs an HTTP 'PUT /memories/{id}' operation and returns the [Response]. + /// This endpoint requires the `memory.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -440,6 +478,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memory.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/notifications_api.dart b/mobile/openapi/lib/api/notifications_api.dart index 501cc70a29..1d276efaaf 100644 --- a/mobile/openapi/lib/api/notifications_api.dart +++ b/mobile/openapi/lib/api/notifications_api.dart @@ -16,7 +16,10 @@ class NotificationsApi { final ApiClient apiClient; - /// Performs an HTTP 'DELETE /notifications/{id}' operation and returns the [Response]. + /// This endpoint requires the `notification.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -46,6 +49,8 @@ class NotificationsApi { ); } + /// This endpoint requires the `notification.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -56,7 +61,10 @@ class NotificationsApi { } } - /// Performs an HTTP 'DELETE /notifications' operation and returns the [Response]. + /// This endpoint requires the `notification.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [NotificationDeleteAllDto] notificationDeleteAllDto (required): @@ -85,6 +93,8 @@ class NotificationsApi { ); } + /// This endpoint requires the `notification.delete` permission. + /// /// Parameters: /// /// * [NotificationDeleteAllDto] notificationDeleteAllDto (required): @@ -95,7 +105,10 @@ class NotificationsApi { } } - /// Performs an HTTP 'GET /notifications/{id}' operation and returns the [Response]. + /// This endpoint requires the `notification.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -125,6 +138,8 @@ class NotificationsApi { ); } + /// This endpoint requires the `notification.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -143,7 +158,10 @@ class NotificationsApi { return null; } - /// Performs an HTTP 'GET /notifications' operation and returns the [Response]. + /// This endpoint requires the `notification.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id: @@ -191,6 +209,8 @@ class NotificationsApi { ); } + /// This endpoint requires the `notification.read` permission. + /// /// Parameters: /// /// * [String] id: @@ -218,7 +238,10 @@ class NotificationsApi { return null; } - /// Performs an HTTP 'PUT /notifications/{id}' operation and returns the [Response]. + /// This endpoint requires the `notification.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -250,6 +273,8 @@ class NotificationsApi { ); } + /// This endpoint requires the `notification.update` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -270,7 +295,10 @@ class NotificationsApi { return null; } - /// Performs an HTTP 'PUT /notifications' operation and returns the [Response]. + /// This endpoint requires the `notification.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [NotificationUpdateAllDto] notificationUpdateAllDto (required): @@ -299,6 +327,8 @@ class NotificationsApi { ); } + /// This endpoint requires the `notification.update` permission. + /// /// Parameters: /// /// * [NotificationUpdateAllDto] notificationUpdateAllDto (required): diff --git a/mobile/openapi/lib/api/partners_api.dart b/mobile/openapi/lib/api/partners_api.dart index 9f10ea4d1e..eb5d5f5806 100644 --- a/mobile/openapi/lib/api/partners_api.dart +++ b/mobile/openapi/lib/api/partners_api.dart @@ -16,7 +16,10 @@ class PartnersApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /partners/{id}' operation and returns the [Response]. + /// This endpoint requires the `partner.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -46,6 +49,8 @@ class PartnersApi { ); } + /// This endpoint requires the `partner.create` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -64,7 +69,10 @@ class PartnersApi { return null; } - /// Performs an HTTP 'GET /partners' operation and returns the [Response]. + /// This endpoint requires the `partner.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [PartnerDirection] direction (required): @@ -95,6 +103,8 @@ class PartnersApi { ); } + /// This endpoint requires the `partner.read` permission. + /// /// Parameters: /// /// * [PartnerDirection] direction (required): @@ -116,7 +126,10 @@ class PartnersApi { return null; } - /// Performs an HTTP 'DELETE /partners/{id}' operation and returns the [Response]. + /// This endpoint requires the `partner.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -146,6 +159,8 @@ class PartnersApi { ); } + /// This endpoint requires the `partner.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -156,7 +171,10 @@ class PartnersApi { } } - /// Performs an HTTP 'PUT /partners/{id}' operation and returns the [Response]. + /// This endpoint requires the `partner.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -188,6 +206,8 @@ class PartnersApi { ); } + /// This endpoint requires the `partner.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/people_api.dart b/mobile/openapi/lib/api/people_api.dart index 35dbac4e97..68c16785cc 100644 --- a/mobile/openapi/lib/api/people_api.dart +++ b/mobile/openapi/lib/api/people_api.dart @@ -16,7 +16,10 @@ class PeopleApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /people' operation and returns the [Response]. + /// This endpoint requires the `person.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [PersonCreateDto] personCreateDto (required): @@ -45,6 +48,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.create` permission. + /// /// Parameters: /// /// * [PersonCreateDto] personCreateDto (required): @@ -63,7 +68,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'DELETE /people' operation and returns the [Response]. + /// This endpoint requires the `person.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -92,6 +100,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.delete` permission. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -102,7 +112,10 @@ class PeopleApi { } } - /// Performs an HTTP 'DELETE /people/{id}' operation and returns the [Response]. + /// This endpoint requires the `person.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -132,6 +145,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -142,7 +157,10 @@ class PeopleApi { } } - /// Performs an HTTP 'GET /people' operation and returns the [Response]. + /// This endpoint requires the `person.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] closestAssetId: @@ -197,6 +215,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.read` permission. + /// /// Parameters: /// /// * [String] closestAssetId: @@ -225,7 +245,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'GET /people/{id}' operation and returns the [Response]. + /// This endpoint requires the `person.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -255,6 +278,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -273,7 +298,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'GET /people/{id}/statistics' operation and returns the [Response]. + /// This endpoint requires the `person.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -303,6 +331,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.statistics` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -321,7 +351,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'GET /people/{id}/thumbnail' operation and returns the [Response]. + /// This endpoint requires the `person.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -351,6 +384,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -369,7 +404,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'POST /people/{id}/merge' operation and returns the [Response]. + /// This endpoint requires the `person.merge` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -401,6 +439,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.merge` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -424,7 +464,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'PUT /people/{id}/reassign' operation and returns the [Response]. + /// This endpoint requires the `person.reassign` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -456,6 +499,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.reassign` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -479,7 +524,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'PUT /people' operation and returns the [Response]. + /// This endpoint requires the `person.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [PeopleUpdateDto] peopleUpdateDto (required): @@ -508,6 +556,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.update` permission. + /// /// Parameters: /// /// * [PeopleUpdateDto] peopleUpdateDto (required): @@ -529,7 +579,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'PUT /people/{id}' operation and returns the [Response]. + /// This endpoint requires the `person.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -561,6 +614,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/search_api.dart b/mobile/openapi/lib/api/search_api.dart index 1b58702c40..4d9e1172b8 100644 --- a/mobile/openapi/lib/api/search_api.dart +++ b/mobile/openapi/lib/api/search_api.dart @@ -16,7 +16,9 @@ class SearchApi { final ApiClient apiClient; - /// Performs an HTTP 'GET /search/cities' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAssetsByCityWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/search/cities'; @@ -42,6 +44,7 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. Future?> getAssetsByCity() async { final response = await getAssetsByCityWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -60,7 +63,9 @@ class SearchApi { return null; } - /// Performs an HTTP 'GET /search/explore' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getExploreDataWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/search/explore'; @@ -86,6 +91,7 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. Future?> getExploreData() async { final response = await getExploreDataWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -104,7 +110,10 @@ class SearchApi { return null; } - /// Performs an HTTP 'GET /search/suggestions' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SearchSuggestionType] type (required): @@ -161,6 +170,8 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [SearchSuggestionType] type (required): @@ -193,7 +204,10 @@ class SearchApi { return null; } - /// Performs an HTTP 'POST /search/statistics' operation and returns the [Response]. + /// This endpoint requires the `asset.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [StatisticsSearchDto] statisticsSearchDto (required): @@ -222,6 +236,8 @@ class SearchApi { ); } + /// This endpoint requires the `asset.statistics` permission. + /// /// Parameters: /// /// * [StatisticsSearchDto] statisticsSearchDto (required): @@ -240,7 +256,10 @@ class SearchApi { return null; } - /// Performs an HTTP 'POST /search/metadata' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [MetadataSearchDto] metadataSearchDto (required): @@ -269,6 +288,8 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [MetadataSearchDto] metadataSearchDto (required): @@ -287,7 +308,10 @@ class SearchApi { return null; } - /// Performs an HTTP 'POST /search/large-assets' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [List] albumIds: @@ -470,6 +494,8 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [List] albumIds: @@ -551,7 +577,10 @@ class SearchApi { return null; } - /// Performs an HTTP 'GET /search/person' operation and returns the [Response]. + /// This endpoint requires the `person.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] name (required): @@ -587,6 +616,8 @@ class SearchApi { ); } + /// This endpoint requires the `person.read` permission. + /// /// Parameters: /// /// * [String] name (required): @@ -610,7 +641,10 @@ class SearchApi { return null; } - /// Performs an HTTP 'GET /search/places' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] name (required): @@ -641,6 +675,8 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [String] name (required): @@ -662,7 +698,10 @@ class SearchApi { return null; } - /// Performs an HTTP 'POST /search/random' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [RandomSearchDto] randomSearchDto (required): @@ -691,6 +730,8 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [RandomSearchDto] randomSearchDto (required): @@ -712,7 +753,10 @@ class SearchApi { return null; } - /// Performs an HTTP 'POST /search/smart' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SmartSearchDto] smartSearchDto (required): @@ -741,6 +785,8 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [SmartSearchDto] smartSearchDto (required): diff --git a/mobile/openapi/lib/api/server_api.dart b/mobile/openapi/lib/api/server_api.dart index 7abdabcd3e..9e250b83b5 100644 --- a/mobile/openapi/lib/api/server_api.dart +++ b/mobile/openapi/lib/api/server_api.dart @@ -16,7 +16,9 @@ class ServerApi { final ApiClient apiClient; - /// Performs an HTTP 'DELETE /server/license' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `serverLicense.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. Future deleteServerLicenseWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/server/license'; @@ -42,6 +44,7 @@ class ServerApi { ); } + /// This endpoint is an admin-only route, and requires the `serverLicense.delete` permission. Future deleteServerLicense() async { final response = await deleteServerLicenseWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -49,7 +52,9 @@ class ServerApi { } } - /// Performs an HTTP 'GET /server/about' operation and returns the [Response]. + /// This endpoint requires the `server.about` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAboutInfoWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/server/about'; @@ -75,6 +80,7 @@ class ServerApi { ); } + /// This endpoint requires the `server.about` permission. Future getAboutInfo() async { final response = await getAboutInfoWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -90,7 +96,9 @@ class ServerApi { return null; } - /// Performs an HTTP 'GET /server/apk-links' operation and returns the [Response]. + /// This endpoint requires the `server.apkLinks` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getApkLinksWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/server/apk-links'; @@ -116,6 +124,7 @@ class ServerApi { ); } + /// This endpoint requires the `server.apkLinks` permission. Future getApkLinks() async { final response = await getApkLinksWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -213,7 +222,9 @@ class ServerApi { return null; } - /// Performs an HTTP 'GET /server/license' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `serverLicense.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getServerLicenseWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/server/license'; @@ -239,6 +250,7 @@ class ServerApi { ); } + /// This endpoint is an admin-only route, and requires the `serverLicense.read` permission. Future getServerLicense() async { final response = await getServerLicenseWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -254,7 +266,9 @@ class ServerApi { return null; } - /// Performs an HTTP 'GET /server/statistics' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `server.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getServerStatisticsWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/server/statistics'; @@ -280,6 +294,7 @@ class ServerApi { ); } + /// This endpoint is an admin-only route, and requires the `server.statistics` permission. Future getServerStatistics() async { final response = await getServerStatisticsWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -336,7 +351,9 @@ class ServerApi { return null; } - /// Performs an HTTP 'GET /server/storage' operation and returns the [Response]. + /// This endpoint requires the `server.storage` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getStorageWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/server/storage'; @@ -362,6 +379,7 @@ class ServerApi { ); } + /// This endpoint requires the `server.storage` permission. Future getStorage() async { final response = await getStorageWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -585,7 +603,10 @@ class ServerApi { return null; } - /// Performs an HTTP 'PUT /server/license' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `serverLicense.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [LicenseKeyDto] licenseKeyDto (required): @@ -614,6 +635,8 @@ class ServerApi { ); } + /// This endpoint is an admin-only route, and requires the `serverLicense.update` permission. + /// /// Parameters: /// /// * [LicenseKeyDto] licenseKeyDto (required): diff --git a/mobile/openapi/lib/api/sessions_api.dart b/mobile/openapi/lib/api/sessions_api.dart index d54f520641..63528d17a7 100644 --- a/mobile/openapi/lib/api/sessions_api.dart +++ b/mobile/openapi/lib/api/sessions_api.dart @@ -16,7 +16,10 @@ class SessionsApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /sessions' operation and returns the [Response]. + /// This endpoint requires the `session.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SessionCreateDto] sessionCreateDto (required): @@ -45,6 +48,8 @@ class SessionsApi { ); } + /// This endpoint requires the `session.create` permission. + /// /// Parameters: /// /// * [SessionCreateDto] sessionCreateDto (required): @@ -63,7 +68,9 @@ class SessionsApi { return null; } - /// Performs an HTTP 'DELETE /sessions' operation and returns the [Response]. + /// This endpoint requires the `session.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. Future deleteAllSessionsWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/sessions'; @@ -89,6 +96,7 @@ class SessionsApi { ); } + /// This endpoint requires the `session.delete` permission. Future deleteAllSessions() async { final response = await deleteAllSessionsWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -96,7 +104,10 @@ class SessionsApi { } } - /// Performs an HTTP 'DELETE /sessions/{id}' operation and returns the [Response]. + /// This endpoint requires the `session.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -126,6 +137,8 @@ class SessionsApi { ); } + /// This endpoint requires the `session.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -136,7 +149,9 @@ class SessionsApi { } } - /// Performs an HTTP 'GET /sessions' operation and returns the [Response]. + /// This endpoint requires the `session.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getSessionsWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/sessions'; @@ -162,6 +177,7 @@ class SessionsApi { ); } + /// This endpoint requires the `session.read` permission. Future?> getSessions() async { final response = await getSessionsWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -180,7 +196,10 @@ class SessionsApi { return null; } - /// Performs an HTTP 'POST /sessions/{id}/lock' operation and returns the [Response]. + /// This endpoint requires the `session.lock` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -210,6 +229,8 @@ class SessionsApi { ); } + /// This endpoint requires the `session.lock` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -220,7 +241,10 @@ class SessionsApi { } } - /// Performs an HTTP 'PUT /sessions/{id}' operation and returns the [Response]. + /// This endpoint requires the `session.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -252,6 +276,8 @@ class SessionsApi { ); } + /// This endpoint requires the `session.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/shared_links_api.dart b/mobile/openapi/lib/api/shared_links_api.dart index dd372b962b..e32c566754 100644 --- a/mobile/openapi/lib/api/shared_links_api.dart +++ b/mobile/openapi/lib/api/shared_links_api.dart @@ -86,7 +86,10 @@ class SharedLinksApi { return null; } - /// Performs an HTTP 'POST /shared-links' operation and returns the [Response]. + /// This endpoint requires the `sharedLink.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SharedLinkCreateDto] sharedLinkCreateDto (required): @@ -115,6 +118,8 @@ class SharedLinksApi { ); } + /// This endpoint requires the `sharedLink.create` permission. + /// /// Parameters: /// /// * [SharedLinkCreateDto] sharedLinkCreateDto (required): @@ -133,7 +138,10 @@ class SharedLinksApi { return null; } - /// Performs an HTTP 'GET /shared-links' operation and returns the [Response]. + /// This endpoint requires the `sharedLink.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] albumId: @@ -166,6 +174,8 @@ class SharedLinksApi { ); } + /// This endpoint requires the `sharedLink.read` permission. + /// /// Parameters: /// /// * [String] albumId: @@ -190,14 +200,14 @@ class SharedLinksApi { /// Performs an HTTP 'GET /shared-links/me' operation and returns the [Response]. /// Parameters: /// - /// * [String] key: - /// /// * [String] password: /// - /// * [String] slug: - /// /// * [String] token: - Future getMySharedLinkWithHttpInfo({ String? key, String? password, String? slug, String? token, }) async { + /// + /// * [String] key: + /// + /// * [String] slug: + Future getMySharedLinkWithHttpInfo({ String? password, String? token, String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/shared-links/me'; @@ -208,18 +218,18 @@ class SharedLinksApi { final headerParams = {}; final formParams = {}; - if (key != null) { - queryParams.addAll(_queryParams('', 'key', key)); - } if (password != null) { queryParams.addAll(_queryParams('', 'password', password)); } - if (slug != null) { - queryParams.addAll(_queryParams('', 'slug', slug)); - } if (token != null) { queryParams.addAll(_queryParams('', 'token', token)); } + if (key != null) { + queryParams.addAll(_queryParams('', 'key', key)); + } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = []; @@ -237,15 +247,15 @@ class SharedLinksApi { /// Parameters: /// - /// * [String] key: - /// /// * [String] password: /// - /// * [String] slug: - /// /// * [String] token: - Future getMySharedLink({ String? key, String? password, String? slug, String? token, }) async { - final response = await getMySharedLinkWithHttpInfo( key: key, password: password, slug: slug, token: token, ); + /// + /// * [String] key: + /// + /// * [String] slug: + Future getMySharedLink({ String? password, String? token, String? key, String? slug, }) async { + final response = await getMySharedLinkWithHttpInfo( password: password, token: token, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -259,7 +269,10 @@ class SharedLinksApi { return null; } - /// Performs an HTTP 'GET /shared-links/{id}' operation and returns the [Response]. + /// This endpoint requires the `sharedLink.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -289,6 +302,8 @@ class SharedLinksApi { ); } + /// This endpoint requires the `sharedLink.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -307,7 +322,10 @@ class SharedLinksApi { return null; } - /// Performs an HTTP 'DELETE /shared-links/{id}' operation and returns the [Response]. + /// This endpoint requires the `sharedLink.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -337,6 +355,8 @@ class SharedLinksApi { ); } + /// This endpoint requires the `sharedLink.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -417,7 +437,10 @@ class SharedLinksApi { return null; } - /// Performs an HTTP 'PATCH /shared-links/{id}' operation and returns the [Response]. + /// This endpoint requires the `sharedLink.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -449,6 +472,8 @@ class SharedLinksApi { ); } + /// This endpoint requires the `sharedLink.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/stacks_api.dart b/mobile/openapi/lib/api/stacks_api.dart index 6d6c4506be..0f76f3396b 100644 --- a/mobile/openapi/lib/api/stacks_api.dart +++ b/mobile/openapi/lib/api/stacks_api.dart @@ -16,7 +16,10 @@ class StacksApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /stacks' operation and returns the [Response]. + /// This endpoint requires the `stack.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [StackCreateDto] stackCreateDto (required): @@ -45,6 +48,8 @@ class StacksApi { ); } + /// This endpoint requires the `stack.create` permission. + /// /// Parameters: /// /// * [StackCreateDto] stackCreateDto (required): @@ -63,7 +68,10 @@ class StacksApi { return null; } - /// Performs an HTTP 'DELETE /stacks/{id}' operation and returns the [Response]. + /// This endpoint requires the `stack.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -93,6 +101,8 @@ class StacksApi { ); } + /// This endpoint requires the `stack.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -103,7 +113,10 @@ class StacksApi { } } - /// Performs an HTTP 'DELETE /stacks' operation and returns the [Response]. + /// This endpoint requires the `stack.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -132,6 +145,8 @@ class StacksApi { ); } + /// This endpoint requires the `stack.delete` permission. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -142,7 +157,10 @@ class StacksApi { } } - /// Performs an HTTP 'GET /stacks/{id}' operation and returns the [Response]. + /// This endpoint requires the `stack.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -172,6 +190,8 @@ class StacksApi { ); } + /// This endpoint requires the `stack.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -190,7 +210,10 @@ class StacksApi { return null; } - /// Performs an HTTP 'DELETE /stacks/{id}/assets/{assetId}' operation and returns the [Response]. + /// This endpoint requires the `stack.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] assetId (required): @@ -223,6 +246,8 @@ class StacksApi { ); } + /// This endpoint requires the `stack.update` permission. + /// /// Parameters: /// /// * [String] assetId (required): @@ -235,7 +260,10 @@ class StacksApi { } } - /// Performs an HTTP 'GET /stacks' operation and returns the [Response]. + /// This endpoint requires the `stack.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] primaryAssetId: @@ -268,6 +296,8 @@ class StacksApi { ); } + /// This endpoint requires the `stack.read` permission. + /// /// Parameters: /// /// * [String] primaryAssetId: @@ -289,7 +319,10 @@ class StacksApi { return null; } - /// Performs an HTTP 'PUT /stacks/{id}' operation and returns the [Response]. + /// This endpoint requires the `stack.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -321,6 +354,8 @@ class StacksApi { ); } + /// This endpoint requires the `stack.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/sync_api.dart b/mobile/openapi/lib/api/sync_api.dart index fe2876ddd8..9e594d6ace 100644 --- a/mobile/openapi/lib/api/sync_api.dart +++ b/mobile/openapi/lib/api/sync_api.dart @@ -16,7 +16,10 @@ class SyncApi { final ApiClient apiClient; - /// Performs an HTTP 'DELETE /sync/ack' operation and returns the [Response]. + /// This endpoint requires the `syncCheckpoint.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SyncAckDeleteDto] syncAckDeleteDto (required): @@ -45,6 +48,8 @@ class SyncApi { ); } + /// This endpoint requires the `syncCheckpoint.delete` permission. + /// /// Parameters: /// /// * [SyncAckDeleteDto] syncAckDeleteDto (required): @@ -152,7 +157,9 @@ class SyncApi { return null; } - /// Performs an HTTP 'GET /sync/ack' operation and returns the [Response]. + /// This endpoint requires the `syncCheckpoint.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getSyncAckWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/sync/ack'; @@ -178,6 +185,7 @@ class SyncApi { ); } + /// This endpoint requires the `syncCheckpoint.read` permission. Future?> getSyncAck() async { final response = await getSyncAckWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -196,7 +204,10 @@ class SyncApi { return null; } - /// Performs an HTTP 'POST /sync/stream' operation and returns the [Response]. + /// This endpoint requires the `sync.stream` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SyncStreamDto] syncStreamDto (required): @@ -225,6 +236,8 @@ class SyncApi { ); } + /// This endpoint requires the `sync.stream` permission. + /// /// Parameters: /// /// * [SyncStreamDto] syncStreamDto (required): @@ -235,7 +248,10 @@ class SyncApi { } } - /// Performs an HTTP 'POST /sync/ack' operation and returns the [Response]. + /// This endpoint requires the `syncCheckpoint.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SyncAckSetDto] syncAckSetDto (required): @@ -264,6 +280,8 @@ class SyncApi { ); } + /// This endpoint requires the `syncCheckpoint.update` permission. + /// /// Parameters: /// /// * [SyncAckSetDto] syncAckSetDto (required): diff --git a/mobile/openapi/lib/api/system_config_api.dart b/mobile/openapi/lib/api/system_config_api.dart index a03b9d3e72..2ab3879b8a 100644 --- a/mobile/openapi/lib/api/system_config_api.dart +++ b/mobile/openapi/lib/api/system_config_api.dart @@ -16,7 +16,9 @@ class SystemConfigApi { final ApiClient apiClient; - /// Performs an HTTP 'GET /system-config' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemConfig.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getConfigWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/system-config'; @@ -42,6 +44,7 @@ class SystemConfigApi { ); } + /// This endpoint is an admin-only route, and requires the `systemConfig.read` permission. Future getConfig() async { final response = await getConfigWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -57,7 +60,9 @@ class SystemConfigApi { return null; } - /// Performs an HTTP 'GET /system-config/defaults' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemConfig.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getConfigDefaultsWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/system-config/defaults'; @@ -83,6 +88,7 @@ class SystemConfigApi { ); } + /// This endpoint is an admin-only route, and requires the `systemConfig.read` permission. Future getConfigDefaults() async { final response = await getConfigDefaultsWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -98,7 +104,9 @@ class SystemConfigApi { return null; } - /// Performs an HTTP 'GET /system-config/storage-template-options' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemConfig.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getStorageTemplateOptionsWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/system-config/storage-template-options'; @@ -124,6 +132,7 @@ class SystemConfigApi { ); } + /// This endpoint is an admin-only route, and requires the `systemConfig.read` permission. Future getStorageTemplateOptions() async { final response = await getStorageTemplateOptionsWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -139,7 +148,10 @@ class SystemConfigApi { return null; } - /// Performs an HTTP 'PUT /system-config' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemConfig.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SystemConfigDto] systemConfigDto (required): @@ -168,6 +180,8 @@ class SystemConfigApi { ); } + /// This endpoint is an admin-only route, and requires the `systemConfig.update` permission. + /// /// Parameters: /// /// * [SystemConfigDto] systemConfigDto (required): diff --git a/mobile/openapi/lib/api/system_metadata_api.dart b/mobile/openapi/lib/api/system_metadata_api.dart index 3fcceb8e42..f6b9bad1d6 100644 --- a/mobile/openapi/lib/api/system_metadata_api.dart +++ b/mobile/openapi/lib/api/system_metadata_api.dart @@ -16,7 +16,9 @@ class SystemMetadataApi { final ApiClient apiClient; - /// Performs an HTTP 'GET /system-metadata/admin-onboarding' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAdminOnboardingWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/system-metadata/admin-onboarding'; @@ -42,6 +44,7 @@ class SystemMetadataApi { ); } + /// This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. Future getAdminOnboarding() async { final response = await getAdminOnboardingWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -57,7 +60,9 @@ class SystemMetadataApi { return null; } - /// Performs an HTTP 'GET /system-metadata/reverse-geocoding-state' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getReverseGeocodingStateWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/system-metadata/reverse-geocoding-state'; @@ -83,6 +88,7 @@ class SystemMetadataApi { ); } + /// This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. Future getReverseGeocodingState() async { final response = await getReverseGeocodingStateWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -98,7 +104,9 @@ class SystemMetadataApi { return null; } - /// Performs an HTTP 'GET /system-metadata/version-check-state' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getVersionCheckStateWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/system-metadata/version-check-state'; @@ -124,6 +132,7 @@ class SystemMetadataApi { ); } + /// This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. Future getVersionCheckState() async { final response = await getVersionCheckStateWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -139,7 +148,10 @@ class SystemMetadataApi { return null; } - /// Performs an HTTP 'POST /system-metadata/admin-onboarding' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemMetadata.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [AdminOnboardingUpdateDto] adminOnboardingUpdateDto (required): @@ -168,6 +180,8 @@ class SystemMetadataApi { ); } + /// This endpoint is an admin-only route, and requires the `systemMetadata.update` permission. + /// /// Parameters: /// /// * [AdminOnboardingUpdateDto] adminOnboardingUpdateDto (required): diff --git a/mobile/openapi/lib/api/tags_api.dart b/mobile/openapi/lib/api/tags_api.dart index f6cfc8720b..a0cdb91acf 100644 --- a/mobile/openapi/lib/api/tags_api.dart +++ b/mobile/openapi/lib/api/tags_api.dart @@ -16,7 +16,10 @@ class TagsApi { final ApiClient apiClient; - /// Performs an HTTP 'PUT /tags/assets' operation and returns the [Response]. + /// This endpoint requires the `tag.asset` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [TagBulkAssetsDto] tagBulkAssetsDto (required): @@ -45,6 +48,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.asset` permission. + /// /// Parameters: /// /// * [TagBulkAssetsDto] tagBulkAssetsDto (required): @@ -63,7 +68,10 @@ class TagsApi { return null; } - /// Performs an HTTP 'POST /tags' operation and returns the [Response]. + /// This endpoint requires the `tag.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [TagCreateDto] tagCreateDto (required): @@ -92,6 +100,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.create` permission. + /// /// Parameters: /// /// * [TagCreateDto] tagCreateDto (required): @@ -110,7 +120,10 @@ class TagsApi { return null; } - /// Performs an HTTP 'DELETE /tags/{id}' operation and returns the [Response]. + /// This endpoint requires the `tag.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -140,6 +153,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -150,7 +165,9 @@ class TagsApi { } } - /// Performs an HTTP 'GET /tags' operation and returns the [Response]. + /// This endpoint requires the `tag.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAllTagsWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/tags'; @@ -176,6 +193,7 @@ class TagsApi { ); } + /// This endpoint requires the `tag.read` permission. Future?> getAllTags() async { final response = await getAllTagsWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -194,7 +212,10 @@ class TagsApi { return null; } - /// Performs an HTTP 'GET /tags/{id}' operation and returns the [Response]. + /// This endpoint requires the `tag.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -224,6 +245,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -242,7 +265,10 @@ class TagsApi { return null; } - /// Performs an HTTP 'PUT /tags/{id}/assets' operation and returns the [Response]. + /// This endpoint requires the `tag.asset` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -274,6 +300,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.asset` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -297,7 +325,10 @@ class TagsApi { return null; } - /// Performs an HTTP 'DELETE /tags/{id}/assets' operation and returns the [Response]. + /// This endpoint requires the `tag.asset` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -329,6 +360,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.asset` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -352,7 +385,10 @@ class TagsApi { return null; } - /// Performs an HTTP 'PUT /tags/{id}' operation and returns the [Response]. + /// This endpoint requires the `tag.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -384,6 +420,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.update` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -404,7 +442,10 @@ class TagsApi { return null; } - /// Performs an HTTP 'PUT /tags' operation and returns the [Response]. + /// This endpoint requires the `tag.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [TagUpsertDto] tagUpsertDto (required): @@ -433,6 +474,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.create` permission. + /// /// Parameters: /// /// * [TagUpsertDto] tagUpsertDto (required): diff --git a/mobile/openapi/lib/api/timeline_api.dart b/mobile/openapi/lib/api/timeline_api.dart index 2d3ced610b..2d142e3d67 100644 --- a/mobile/openapi/lib/api/timeline_api.dart +++ b/mobile/openapi/lib/api/timeline_api.dart @@ -16,7 +16,10 @@ class TimelineApi { final ApiClient apiClient; - /// Performs an HTTP 'GET /timeline/bucket' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] timeBucket (required): @@ -118,6 +121,8 @@ class TimelineApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [String] timeBucket (required): @@ -171,7 +176,10 @@ class TimelineApi { return null; } - /// Performs an HTTP 'GET /timeline/buckets' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] albumId: @@ -269,6 +277,8 @@ class TimelineApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [String] albumId: diff --git a/mobile/openapi/lib/api/trash_api.dart b/mobile/openapi/lib/api/trash_api.dart index 982dbcbeda..480d19960a 100644 --- a/mobile/openapi/lib/api/trash_api.dart +++ b/mobile/openapi/lib/api/trash_api.dart @@ -16,7 +16,9 @@ class TrashApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /trash/empty' operation and returns the [Response]. + /// This endpoint requires the `asset.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. Future emptyTrashWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/trash/empty'; @@ -42,6 +44,7 @@ class TrashApi { ); } + /// This endpoint requires the `asset.delete` permission. Future emptyTrash() async { final response = await emptyTrashWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -57,7 +60,10 @@ class TrashApi { return null; } - /// Performs an HTTP 'POST /trash/restore/assets' operation and returns the [Response]. + /// This endpoint requires the `asset.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -86,6 +92,8 @@ class TrashApi { ); } + /// This endpoint requires the `asset.delete` permission. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -104,7 +112,9 @@ class TrashApi { return null; } - /// Performs an HTTP 'POST /trash/restore' operation and returns the [Response]. + /// This endpoint requires the `asset.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. Future restoreTrashWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/trash/restore'; @@ -130,6 +140,7 @@ class TrashApi { ); } + /// This endpoint requires the `asset.delete` permission. Future restoreTrash() async { final response = await restoreTrashWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { diff --git a/mobile/openapi/lib/api/users_admin_api.dart b/mobile/openapi/lib/api/users_admin_api.dart index 58263504ce..e4fc1673ef 100644 --- a/mobile/openapi/lib/api/users_admin_api.dart +++ b/mobile/openapi/lib/api/users_admin_api.dart @@ -16,7 +16,10 @@ class UsersAdminApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /admin/users' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [UserAdminCreateDto] userAdminCreateDto (required): @@ -45,6 +48,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.create` permission. + /// /// Parameters: /// /// * [UserAdminCreateDto] userAdminCreateDto (required): @@ -63,7 +68,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'DELETE /admin/users/{id}' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -95,6 +103,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -115,7 +125,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'GET /admin/users/{id}' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -145,6 +158,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -163,7 +178,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'GET /admin/users/{id}/preferences' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -193,6 +211,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -211,7 +231,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'GET /admin/users/{id}/statistics' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -257,6 +280,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -281,7 +306,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'POST /admin/users/{id}/restore' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -311,6 +339,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -329,7 +359,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'GET /admin/users' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id: @@ -367,6 +400,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// /// Parameters: /// /// * [String] id: @@ -390,7 +425,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'PUT /admin/users/{id}' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -422,6 +460,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.update` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -442,7 +482,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'PUT /admin/users/{id}/preferences' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -474,6 +517,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/users_api.dart b/mobile/openapi/lib/api/users_api.dart index cd31617e74..c8891ba0c2 100644 --- a/mobile/openapi/lib/api/users_api.dart +++ b/mobile/openapi/lib/api/users_api.dart @@ -16,7 +16,10 @@ class UsersApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /users/profile-image' operation and returns the [Response]. + /// This endpoint requires the `userProfileImage.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [MultipartFile] file (required): @@ -55,6 +58,8 @@ class UsersApi { ); } + /// This endpoint requires the `userProfileImage.update` permission. + /// /// Parameters: /// /// * [MultipartFile] file (required): @@ -73,7 +78,9 @@ class UsersApi { return null; } - /// Performs an HTTP 'DELETE /users/profile-image' operation and returns the [Response]. + /// This endpoint requires the `userProfileImage.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. Future deleteProfileImageWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users/profile-image'; @@ -99,6 +106,7 @@ class UsersApi { ); } + /// This endpoint requires the `userProfileImage.delete` permission. Future deleteProfileImage() async { final response = await deleteProfileImageWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -106,7 +114,9 @@ class UsersApi { } } - /// Performs an HTTP 'DELETE /users/me/license' operation and returns the [Response]. + /// This endpoint requires the `userLicense.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. Future deleteUserLicenseWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users/me/license'; @@ -132,6 +142,7 @@ class UsersApi { ); } + /// This endpoint requires the `userLicense.delete` permission. Future deleteUserLicense() async { final response = await deleteUserLicenseWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -139,7 +150,9 @@ class UsersApi { } } - /// Performs an HTTP 'DELETE /users/me/onboarding' operation and returns the [Response]. + /// This endpoint requires the `userOnboarding.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. Future deleteUserOnboardingWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users/me/onboarding'; @@ -165,6 +178,7 @@ class UsersApi { ); } + /// This endpoint requires the `userOnboarding.delete` permission. Future deleteUserOnboarding() async { final response = await deleteUserOnboardingWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -172,7 +186,9 @@ class UsersApi { } } - /// Performs an HTTP 'GET /users/me/preferences' operation and returns the [Response]. + /// This endpoint requires the `userPreference.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getMyPreferencesWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users/me/preferences'; @@ -198,6 +214,7 @@ class UsersApi { ); } + /// This endpoint requires the `userPreference.read` permission. Future getMyPreferences() async { final response = await getMyPreferencesWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -213,7 +230,9 @@ class UsersApi { return null; } - /// Performs an HTTP 'GET /users/me' operation and returns the [Response]. + /// This endpoint requires the `user.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getMyUserWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users/me'; @@ -239,6 +258,7 @@ class UsersApi { ); } + /// This endpoint requires the `user.read` permission. Future getMyUser() async { final response = await getMyUserWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -254,7 +274,10 @@ class UsersApi { return null; } - /// Performs an HTTP 'GET /users/{id}/profile-image' operation and returns the [Response]. + /// This endpoint requires the `userProfileImage.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -284,6 +307,8 @@ class UsersApi { ); } + /// This endpoint requires the `userProfileImage.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -302,7 +327,10 @@ class UsersApi { return null; } - /// Performs an HTTP 'GET /users/{id}' operation and returns the [Response]. + /// This endpoint requires the `user.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -332,6 +360,8 @@ class UsersApi { ); } + /// This endpoint requires the `user.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -350,7 +380,9 @@ class UsersApi { return null; } - /// Performs an HTTP 'GET /users/me/license' operation and returns the [Response]. + /// This endpoint requires the `userLicense.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getUserLicenseWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users/me/license'; @@ -376,6 +408,7 @@ class UsersApi { ); } + /// This endpoint requires the `userLicense.read` permission. Future getUserLicense() async { final response = await getUserLicenseWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -391,7 +424,9 @@ class UsersApi { return null; } - /// Performs an HTTP 'GET /users/me/onboarding' operation and returns the [Response]. + /// This endpoint requires the `userOnboarding.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getUserOnboardingWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users/me/onboarding'; @@ -417,6 +452,7 @@ class UsersApi { ); } + /// This endpoint requires the `userOnboarding.read` permission. Future getUserOnboarding() async { final response = await getUserOnboardingWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -432,7 +468,9 @@ class UsersApi { return null; } - /// Performs an HTTP 'GET /users' operation and returns the [Response]. + /// This endpoint requires the `user.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future searchUsersWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users'; @@ -458,6 +496,7 @@ class UsersApi { ); } + /// This endpoint requires the `user.read` permission. Future?> searchUsers() async { final response = await searchUsersWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -476,7 +515,10 @@ class UsersApi { return null; } - /// Performs an HTTP 'PUT /users/me/license' operation and returns the [Response]. + /// This endpoint requires the `userLicense.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [LicenseKeyDto] licenseKeyDto (required): @@ -505,6 +547,8 @@ class UsersApi { ); } + /// This endpoint requires the `userLicense.update` permission. + /// /// Parameters: /// /// * [LicenseKeyDto] licenseKeyDto (required): @@ -523,7 +567,10 @@ class UsersApi { return null; } - /// Performs an HTTP 'PUT /users/me/onboarding' operation and returns the [Response]. + /// This endpoint requires the `userOnboarding.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [OnboardingDto] onboardingDto (required): @@ -552,6 +599,8 @@ class UsersApi { ); } + /// This endpoint requires the `userOnboarding.update` permission. + /// /// Parameters: /// /// * [OnboardingDto] onboardingDto (required): @@ -570,7 +619,10 @@ class UsersApi { return null; } - /// Performs an HTTP 'PUT /users/me/preferences' operation and returns the [Response]. + /// This endpoint requires the `userPreference.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [UserPreferencesUpdateDto] userPreferencesUpdateDto (required): @@ -599,6 +651,8 @@ class UsersApi { ); } + /// This endpoint requires the `userPreference.update` permission. + /// /// Parameters: /// /// * [UserPreferencesUpdateDto] userPreferencesUpdateDto (required): @@ -617,7 +671,10 @@ class UsersApi { return null; } - /// Performs an HTTP 'PUT /users/me' operation and returns the [Response]. + /// This endpoint requires the `user.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [UserUpdateMeDto] userUpdateMeDto (required): @@ -646,6 +703,8 @@ class UsersApi { ); } + /// This endpoint requires the `user.update` permission. + /// /// Parameters: /// /// * [UserUpdateMeDto] userUpdateMeDto (required): diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 8c491ca471..9a1e6a6937 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -78,7 +78,8 @@ "tags": [ "Activities" ], - "x-immich-permission": "activity.read" + "x-immich-permission": "activity.read", + "description": "This endpoint requires the `activity.read` permission." }, "post": { "operationId": "createActivity", @@ -119,7 +120,8 @@ "tags": [ "Activities" ], - "x-immich-permission": "activity.create" + "x-immich-permission": "activity.create", + "description": "This endpoint requires the `activity.create` permission." } }, "/activities/statistics": { @@ -171,7 +173,8 @@ "tags": [ "Activities" ], - "x-immich-permission": "activity.statistics" + "x-immich-permission": "activity.statistics", + "description": "This endpoint requires the `activity.statistics` permission." } }, "/activities/{id}": { @@ -207,7 +210,8 @@ "tags": [ "Activities" ], - "x-immich-permission": "activity.delete" + "x-immich-permission": "activity.delete", + "description": "This endpoint requires the `activity.delete` permission." } }, "/admin/notifications": { @@ -249,7 +253,8 @@ ], "tags": [ "Notifications (Admin)" - ] + ], + "x-immich-admin-only": true } }, "/admin/notifications/templates/{name}": { @@ -300,7 +305,8 @@ ], "tags": [ "Notifications (Admin)" - ] + ], + "x-immich-admin-only": true } }, "/admin/notifications/test-email": { @@ -342,7 +348,8 @@ ], "tags": [ "Notifications (Admin)" - ] + ], + "x-immich-admin-only": true } }, "/admin/users": { @@ -396,7 +403,9 @@ "tags": [ "Users (admin)" ], - "x-immich-permission": "adminUser.read" + "x-immich-admin-only": true, + "x-immich-permission": "adminUser.read", + "description": "This endpoint is an admin-only route, and requires the `adminUser.read` permission." }, "post": { "operationId": "createUserAdmin", @@ -437,7 +446,9 @@ "tags": [ "Users (admin)" ], - "x-immich-permission": "adminUser.create" + "x-immich-admin-only": true, + "x-immich-permission": "adminUser.create", + "description": "This endpoint is an admin-only route, and requires the `adminUser.create` permission." } }, "/admin/users/{id}": { @@ -490,7 +501,9 @@ "tags": [ "Users (admin)" ], - "x-immich-permission": "adminUser.delete" + "x-immich-admin-only": true, + "x-immich-permission": "adminUser.delete", + "description": "This endpoint is an admin-only route, and requires the `adminUser.delete` permission." }, "get": { "operationId": "getUserAdmin", @@ -531,7 +544,9 @@ "tags": [ "Users (admin)" ], - "x-immich-permission": "adminUser.read" + "x-immich-admin-only": true, + "x-immich-permission": "adminUser.read", + "description": "This endpoint is an admin-only route, and requires the `adminUser.read` permission." }, "put": { "operationId": "updateUserAdmin", @@ -582,7 +597,9 @@ "tags": [ "Users (admin)" ], - "x-immich-permission": "adminUser.update" + "x-immich-admin-only": true, + "x-immich-permission": "adminUser.update", + "description": "This endpoint is an admin-only route, and requires the `adminUser.update` permission." } }, "/admin/users/{id}/preferences": { @@ -625,7 +642,9 @@ "tags": [ "Users (admin)" ], - "x-immich-permission": "adminUser.read" + "x-immich-admin-only": true, + "x-immich-permission": "adminUser.read", + "description": "This endpoint is an admin-only route, and requires the `adminUser.read` permission." }, "put": { "operationId": "updateUserPreferencesAdmin", @@ -676,7 +695,9 @@ "tags": [ "Users (admin)" ], - "x-immich-permission": "adminUser.update" + "x-immich-admin-only": true, + "x-immich-permission": "adminUser.update", + "description": "This endpoint is an admin-only route, and requires the `adminUser.update` permission." } }, "/admin/users/{id}/restore": { @@ -719,7 +740,9 @@ "tags": [ "Users (admin)" ], - "x-immich-permission": "adminUser.delete" + "x-immich-admin-only": true, + "x-immich-permission": "adminUser.delete", + "description": "This endpoint is an admin-only route, and requires the `adminUser.delete` permission." } }, "/admin/users/{id}/statistics": { @@ -786,7 +809,9 @@ "tags": [ "Users (admin)" ], - "x-immich-permission": "adminUser.read" + "x-immich-admin-only": true, + "x-immich-permission": "adminUser.read", + "description": "This endpoint is an admin-only route, and requires the `adminUser.read` permission." } }, "/albums": { @@ -841,7 +866,8 @@ "tags": [ "Albums" ], - "x-immich-permission": "album.read" + "x-immich-permission": "album.read", + "description": "This endpoint requires the `album.read` permission." }, "post": { "operationId": "createAlbum", @@ -882,7 +908,8 @@ "tags": [ "Albums" ], - "x-immich-permission": "album.create" + "x-immich-permission": "album.create", + "description": "This endpoint requires the `album.create` permission." } }, "/albums/statistics": { @@ -915,7 +942,8 @@ "tags": [ "Albums" ], - "x-immich-permission": "album.statistics" + "x-immich-permission": "album.statistics", + "description": "This endpoint requires the `album.statistics` permission." } }, "/albums/{id}": { @@ -951,7 +979,8 @@ "tags": [ "Albums" ], - "x-immich-permission": "album.delete" + "x-immich-permission": "album.delete", + "description": "This endpoint requires the `album.delete` permission." }, "get": { "operationId": "getAlbumInfo", @@ -1016,7 +1045,8 @@ "tags": [ "Albums" ], - "x-immich-permission": "album.read" + "x-immich-permission": "album.read", + "description": "This endpoint requires the `album.read` permission." }, "patch": { "operationId": "updateAlbumInfo", @@ -1067,7 +1097,8 @@ "tags": [ "Albums" ], - "x-immich-permission": "album.update" + "x-immich-permission": "album.update", + "description": "This endpoint requires the `album.update` permission." } }, "/albums/{id}/assets": { @@ -1123,7 +1154,8 @@ "tags": [ "Albums" ], - "x-immich-permission": "albumAsset.delete" + "x-immich-permission": "albumAsset.delete", + "description": "This endpoint requires the `albumAsset.delete` permission." }, "put": { "operationId": "addAssetsToAlbum", @@ -1193,7 +1225,8 @@ "tags": [ "Albums" ], - "x-immich-permission": "albumAsset.create" + "x-immich-permission": "albumAsset.create", + "description": "This endpoint requires the `albumAsset.create` permission." } }, "/albums/{id}/user/{userId}": { @@ -1237,7 +1270,8 @@ "tags": [ "Albums" ], - "x-immich-permission": "albumUser.delete" + "x-immich-permission": "albumUser.delete", + "description": "This endpoint requires the `albumUser.delete` permission." }, "put": { "operationId": "updateAlbumUser", @@ -1289,7 +1323,8 @@ "tags": [ "Albums" ], - "x-immich-permission": "albumUser.update" + "x-immich-permission": "albumUser.update", + "description": "This endpoint requires the `albumUser.update` permission." } }, "/albums/{id}/users": { @@ -1342,7 +1377,8 @@ "tags": [ "Albums" ], - "x-immich-permission": "albumUser.create" + "x-immich-permission": "albumUser.create", + "description": "This endpoint requires the `albumUser.create` permission." } }, "/api-keys": { @@ -1378,7 +1414,8 @@ "tags": [ "API Keys" ], - "x-immich-permission": "apiKey.read" + "x-immich-permission": "apiKey.read", + "description": "This endpoint requires the `apiKey.read` permission." }, "post": { "operationId": "createApiKey", @@ -1419,7 +1456,8 @@ "tags": [ "API Keys" ], - "x-immich-permission": "apiKey.create" + "x-immich-permission": "apiKey.create", + "description": "This endpoint requires the `apiKey.create` permission." } }, "/api-keys/{id}": { @@ -1455,7 +1493,8 @@ "tags": [ "API Keys" ], - "x-immich-permission": "apiKey.delete" + "x-immich-permission": "apiKey.delete", + "description": "This endpoint requires the `apiKey.delete` permission." }, "get": { "operationId": "getApiKey", @@ -1496,7 +1535,8 @@ "tags": [ "API Keys" ], - "x-immich-permission": "apiKey.read" + "x-immich-permission": "apiKey.read", + "description": "This endpoint requires the `apiKey.read` permission." }, "put": { "operationId": "updateApiKey", @@ -1547,7 +1587,8 @@ "tags": [ "API Keys" ], - "x-immich-permission": "apiKey.update" + "x-immich-permission": "apiKey.update", + "description": "This endpoint requires the `apiKey.update` permission." } }, "/assets": { @@ -1583,7 +1624,8 @@ "tags": [ "Assets" ], - "x-immich-permission": "asset.delete" + "x-immich-permission": "asset.delete", + "description": "This endpoint requires the `asset.delete` permission." }, "post": { "operationId": "uploadAsset", @@ -1651,7 +1693,8 @@ "tags": [ "Assets" ], - "x-immich-permission": "asset.upload" + "x-immich-permission": "asset.upload", + "description": "This endpoint requires the `asset.upload` permission." }, "put": { "operationId": "updateAssets", @@ -1685,7 +1728,8 @@ "tags": [ "Assets" ], - "x-immich-permission": "asset.update" + "x-immich-permission": "asset.update", + "description": "This endpoint requires the `asset.update` permission." } }, "/assets/bulk-upload-check": { @@ -1860,7 +1904,7 @@ "/assets/random": { "get": { "deprecated": true, - "description": "This property was deprecated in v1.116.0", + "description": "This property was deprecated in v1.116.0. This endpoint requires the `asset.read` permission.", "operationId": "getRandom", "parameters": [ { @@ -1964,7 +2008,8 @@ "tags": [ "Assets" ], - "x-immich-permission": "asset.statistics" + "x-immich-permission": "asset.statistics", + "description": "This endpoint requires the `asset.statistics` permission." } }, "/assets/{id}": { @@ -2023,7 +2068,8 @@ "tags": [ "Assets" ], - "x-immich-permission": "asset.read" + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." }, "put": { "operationId": "updateAsset", @@ -2074,7 +2120,8 @@ "tags": [ "Assets" ], - "x-immich-permission": "asset.update" + "x-immich-permission": "asset.update", + "description": "This endpoint requires the `asset.update` permission." } }, "/assets/{id}/original": { @@ -2134,10 +2181,11 @@ "tags": [ "Assets" ], - "x-immich-permission": "asset.download" + "x-immich-permission": "asset.download", + "description": "This endpoint requires the `asset.download` permission." }, "put": { - "description": "Replace the asset with new file, without changing its id", + "description": "Replace the asset with new file, without changing its id. This endpoint requires the `asset.replace` permission.", "operationId": "replaceAsset", "parameters": [ { @@ -2274,7 +2322,8 @@ "tags": [ "Assets" ], - "x-immich-permission": "asset.view" + "x-immich-permission": "asset.view", + "description": "This endpoint requires the `asset.view` permission." } }, "/assets/{id}/video/playback": { @@ -2334,7 +2383,8 @@ "tags": [ "Assets" ], - "x-immich-permission": "asset.view" + "x-immich-permission": "asset.view", + "description": "This endpoint requires the `asset.view` permission." } }, "/auth/admin-sign-up": { @@ -2408,7 +2458,8 @@ "tags": [ "Authentication" ], - "x-immich-permission": "auth.changePassword" + "x-immich-permission": "auth.changePassword", + "description": "This endpoint requires the `auth.changePassword` permission." } }, "/auth/login": { @@ -2507,7 +2558,8 @@ "tags": [ "Authentication" ], - "x-immich-permission": "pinCode.delete" + "x-immich-permission": "pinCode.delete", + "description": "This endpoint requires the `pinCode.delete` permission." }, "post": { "operationId": "setupPinCode", @@ -2541,7 +2593,8 @@ "tags": [ "Authentication" ], - "x-immich-permission": "pinCode.create" + "x-immich-permission": "pinCode.create", + "description": "This endpoint requires the `pinCode.create` permission." }, "put": { "operationId": "changePinCode", @@ -2575,7 +2628,8 @@ "tags": [ "Authentication" ], - "x-immich-permission": "pinCode.update" + "x-immich-permission": "pinCode.update", + "description": "This endpoint requires the `pinCode.update` permission." } }, "/auth/session/lock": { @@ -2760,7 +2814,8 @@ "tags": [ "Download" ], - "x-immich-permission": "asset.download" + "x-immich-permission": "asset.download", + "description": "This endpoint requires the `asset.download` permission." } }, "/download/info": { @@ -2820,7 +2875,8 @@ "tags": [ "Download" ], - "x-immich-permission": "asset.download" + "x-immich-permission": "asset.download", + "description": "This endpoint requires the `asset.download` permission." } }, "/duplicates": { @@ -2856,7 +2912,8 @@ "tags": [ "Duplicates" ], - "x-immich-permission": "duplicate.delete" + "x-immich-permission": "duplicate.delete", + "description": "This endpoint requires the `duplicate.delete` permission." }, "get": { "operationId": "getAssetDuplicates", @@ -2890,7 +2947,8 @@ "tags": [ "Duplicates" ], - "x-immich-permission": "duplicate.read" + "x-immich-permission": "duplicate.read", + "description": "This endpoint requires the `duplicate.read` permission." } }, "/duplicates/{id}": { @@ -2926,7 +2984,8 @@ "tags": [ "Duplicates" ], - "x-immich-permission": "duplicate.delete" + "x-immich-permission": "duplicate.delete", + "description": "This endpoint requires the `duplicate.delete` permission." } }, "/faces": { @@ -2972,7 +3031,8 @@ "tags": [ "Faces" ], - "x-immich-permission": "face.read" + "x-immich-permission": "face.read", + "description": "This endpoint requires the `face.read` permission." }, "post": { "operationId": "createFace", @@ -3006,7 +3066,8 @@ "tags": [ "Faces" ], - "x-immich-permission": "face.create" + "x-immich-permission": "face.create", + "description": "This endpoint requires the `face.create` permission." } }, "/faces/{id}": { @@ -3052,7 +3113,8 @@ "tags": [ "Faces" ], - "x-immich-permission": "face.delete" + "x-immich-permission": "face.delete", + "description": "This endpoint requires the `face.delete` permission." }, "put": { "operationId": "reassignFacesById", @@ -3103,7 +3165,8 @@ "tags": [ "Faces" ], - "x-immich-permission": "face.update" + "x-immich-permission": "face.update", + "description": "This endpoint requires the `face.update` permission." } }, "/jobs": { @@ -3136,7 +3199,9 @@ "tags": [ "Jobs" ], - "x-immich-permission": "job.read" + "x-immich-admin-only": true, + "x-immich-permission": "job.read", + "description": "This endpoint is an admin-only route, and requires the `job.read` permission." }, "post": { "operationId": "createJob", @@ -3170,7 +3235,9 @@ "tags": [ "Jobs" ], - "x-immich-permission": "job.create" + "x-immich-admin-only": true, + "x-immich-permission": "job.create", + "description": "This endpoint is an admin-only route, and requires the `job.create` permission." } }, "/jobs/{id}": { @@ -3222,7 +3289,9 @@ "tags": [ "Jobs" ], - "x-immich-permission": "job.create" + "x-immich-admin-only": true, + "x-immich-permission": "job.create", + "description": "This endpoint is an admin-only route, and requires the `job.create` permission." } }, "/libraries": { @@ -3258,7 +3327,9 @@ "tags": [ "Libraries" ], - "x-immich-permission": "library.read" + "x-immich-admin-only": true, + "x-immich-permission": "library.read", + "description": "This endpoint is an admin-only route, and requires the `library.read` permission." }, "post": { "operationId": "createLibrary", @@ -3299,7 +3370,9 @@ "tags": [ "Libraries" ], - "x-immich-permission": "library.create" + "x-immich-admin-only": true, + "x-immich-permission": "library.create", + "description": "This endpoint is an admin-only route, and requires the `library.create` permission." } }, "/libraries/{id}": { @@ -3335,7 +3408,9 @@ "tags": [ "Libraries" ], - "x-immich-permission": "library.delete" + "x-immich-admin-only": true, + "x-immich-permission": "library.delete", + "description": "This endpoint is an admin-only route, and requires the `library.delete` permission." }, "get": { "operationId": "getLibrary", @@ -3376,7 +3451,9 @@ "tags": [ "Libraries" ], - "x-immich-permission": "library.read" + "x-immich-admin-only": true, + "x-immich-permission": "library.read", + "description": "This endpoint is an admin-only route, and requires the `library.read` permission." }, "put": { "operationId": "updateLibrary", @@ -3427,7 +3504,9 @@ "tags": [ "Libraries" ], - "x-immich-permission": "library.update" + "x-immich-admin-only": true, + "x-immich-permission": "library.update", + "description": "This endpoint is an admin-only route, and requires the `library.update` permission." } }, "/libraries/{id}/scan": { @@ -3463,7 +3542,9 @@ "tags": [ "Libraries" ], - "x-immich-permission": "library.update" + "x-immich-admin-only": true, + "x-immich-permission": "library.update", + "description": "This endpoint is an admin-only route, and requires the `library.update` permission." } }, "/libraries/{id}/statistics": { @@ -3506,7 +3587,9 @@ "tags": [ "Libraries" ], - "x-immich-permission": "library.statistics" + "x-immich-admin-only": true, + "x-immich-permission": "library.statistics", + "description": "This endpoint is an admin-only route, and requires the `library.statistics` permission." } }, "/libraries/{id}/validate": { @@ -3558,13 +3641,30 @@ ], "tags": [ "Libraries" - ] + ], + "x-immich-admin-only": true } }, "/map/markers": { "get": { "operationId": "getMapMarkers", "parameters": [ + { + "name": "isArchived", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "isFavorite", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, { "name": "fileCreatedAfter", "required": false, @@ -3583,22 +3683,6 @@ "type": "string" } }, - { - "name": "isArchived", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "isFavorite", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, { "name": "withPartners", "required": false, @@ -3768,7 +3852,8 @@ "tags": [ "Memories" ], - "x-immich-permission": "memory.read" + "x-immich-permission": "memory.read", + "description": "This endpoint requires the `memory.read` permission." }, "post": { "operationId": "createMemory", @@ -3809,7 +3894,8 @@ "tags": [ "Memories" ], - "x-immich-permission": "memory.create" + "x-immich-permission": "memory.create", + "description": "This endpoint requires the `memory.create` permission." } }, "/memories/statistics": { @@ -3876,7 +3962,8 @@ "tags": [ "Memories" ], - "x-immich-permission": "memory.statistics" + "x-immich-permission": "memory.statistics", + "description": "This endpoint requires the `memory.statistics` permission." } }, "/memories/{id}": { @@ -3912,7 +3999,8 @@ "tags": [ "Memories" ], - "x-immich-permission": "memory.delete" + "x-immich-permission": "memory.delete", + "description": "This endpoint requires the `memory.delete` permission." }, "get": { "operationId": "getMemory", @@ -3953,7 +4041,8 @@ "tags": [ "Memories" ], - "x-immich-permission": "memory.read" + "x-immich-permission": "memory.read", + "description": "This endpoint requires the `memory.read` permission." }, "put": { "operationId": "updateMemory", @@ -4004,7 +4093,8 @@ "tags": [ "Memories" ], - "x-immich-permission": "memory.update" + "x-immich-permission": "memory.update", + "description": "This endpoint requires the `memory.update` permission." } }, "/memories/{id}/assets": { @@ -4060,7 +4150,8 @@ "tags": [ "Memories" ], - "x-immich-permission": "memoryAsset.delete" + "x-immich-permission": "memoryAsset.delete", + "description": "This endpoint requires the `memoryAsset.delete` permission." }, "put": { "operationId": "addMemoryAssets", @@ -4114,7 +4205,8 @@ "tags": [ "Memories" ], - "x-immich-permission": "memoryAsset.create" + "x-immich-permission": "memoryAsset.create", + "description": "This endpoint requires the `memoryAsset.create` permission." } }, "/notifications": { @@ -4150,7 +4242,8 @@ "tags": [ "Notifications" ], - "x-immich-permission": "notification.delete" + "x-immich-permission": "notification.delete", + "description": "This endpoint requires the `notification.delete` permission." }, "get": { "operationId": "getNotifications", @@ -4218,7 +4311,8 @@ "tags": [ "Notifications" ], - "x-immich-permission": "notification.read" + "x-immich-permission": "notification.read", + "description": "This endpoint requires the `notification.read` permission." }, "put": { "operationId": "updateNotifications", @@ -4252,7 +4346,8 @@ "tags": [ "Notifications" ], - "x-immich-permission": "notification.update" + "x-immich-permission": "notification.update", + "description": "This endpoint requires the `notification.update` permission." } }, "/notifications/{id}": { @@ -4288,7 +4383,8 @@ "tags": [ "Notifications" ], - "x-immich-permission": "notification.delete" + "x-immich-permission": "notification.delete", + "description": "This endpoint requires the `notification.delete` permission." }, "get": { "operationId": "getNotification", @@ -4329,7 +4425,8 @@ "tags": [ "Notifications" ], - "x-immich-permission": "notification.read" + "x-immich-permission": "notification.read", + "description": "This endpoint requires the `notification.read` permission." }, "put": { "operationId": "updateNotification", @@ -4380,7 +4477,8 @@ "tags": [ "Notifications" ], - "x-immich-permission": "notification.update" + "x-immich-permission": "notification.update", + "description": "This endpoint requires the `notification.update` permission." } }, "/oauth/authorize": { @@ -4575,7 +4673,8 @@ "tags": [ "Partners" ], - "x-immich-permission": "partner.read" + "x-immich-permission": "partner.read", + "description": "This endpoint requires the `partner.read` permission." } }, "/partners/{id}": { @@ -4611,7 +4710,8 @@ "tags": [ "Partners" ], - "x-immich-permission": "partner.delete" + "x-immich-permission": "partner.delete", + "description": "This endpoint requires the `partner.delete` permission." }, "post": { "operationId": "createPartner", @@ -4652,7 +4752,8 @@ "tags": [ "Partners" ], - "x-immich-permission": "partner.create" + "x-immich-permission": "partner.create", + "description": "This endpoint requires the `partner.create` permission." }, "put": { "operationId": "updatePartner", @@ -4703,7 +4804,8 @@ "tags": [ "Partners" ], - "x-immich-permission": "partner.update" + "x-immich-permission": "partner.update", + "description": "This endpoint requires the `partner.update` permission." } }, "/people": { @@ -4739,7 +4841,8 @@ "tags": [ "People" ], - "x-immich-permission": "person.delete" + "x-immich-permission": "person.delete", + "description": "This endpoint requires the `person.delete` permission." }, "get": { "operationId": "getAllPeople", @@ -4820,7 +4923,8 @@ "tags": [ "People" ], - "x-immich-permission": "person.read" + "x-immich-permission": "person.read", + "description": "This endpoint requires the `person.read` permission." }, "post": { "operationId": "createPerson", @@ -4861,7 +4965,8 @@ "tags": [ "People" ], - "x-immich-permission": "person.create" + "x-immich-permission": "person.create", + "description": "This endpoint requires the `person.create` permission." }, "put": { "operationId": "updatePeople", @@ -4905,7 +5010,8 @@ "tags": [ "People" ], - "x-immich-permission": "person.update" + "x-immich-permission": "person.update", + "description": "This endpoint requires the `person.update` permission." } }, "/people/{id}": { @@ -4941,7 +5047,8 @@ "tags": [ "People" ], - "x-immich-permission": "person.delete" + "x-immich-permission": "person.delete", + "description": "This endpoint requires the `person.delete` permission." }, "get": { "operationId": "getPerson", @@ -4982,7 +5089,8 @@ "tags": [ "People" ], - "x-immich-permission": "person.read" + "x-immich-permission": "person.read", + "description": "This endpoint requires the `person.read` permission." }, "put": { "operationId": "updatePerson", @@ -5033,7 +5141,8 @@ "tags": [ "People" ], - "x-immich-permission": "person.update" + "x-immich-permission": "person.update", + "description": "This endpoint requires the `person.update` permission." } }, "/people/{id}/merge": { @@ -5089,7 +5198,8 @@ "tags": [ "People" ], - "x-immich-permission": "person.merge" + "x-immich-permission": "person.merge", + "description": "This endpoint requires the `person.merge` permission." } }, "/people/{id}/reassign": { @@ -5145,7 +5255,8 @@ "tags": [ "People" ], - "x-immich-permission": "person.reassign" + "x-immich-permission": "person.reassign", + "description": "This endpoint requires the `person.reassign` permission." } }, "/people/{id}/statistics": { @@ -5188,7 +5299,8 @@ "tags": [ "People" ], - "x-immich-permission": "person.statistics" + "x-immich-permission": "person.statistics", + "description": "This endpoint requires the `person.statistics` permission." } }, "/people/{id}/thumbnail": { @@ -5232,7 +5344,8 @@ "tags": [ "People" ], - "x-immich-permission": "person.read" + "x-immich-permission": "person.read", + "description": "This endpoint requires the `person.read` permission." } }, "/search/cities": { @@ -5268,7 +5381,8 @@ "tags": [ "Search" ], - "x-immich-permission": "asset.read" + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/search/explore": { @@ -5304,7 +5418,8 @@ "tags": [ "Search" ], - "x-immich-permission": "asset.read" + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/search/large-assets": { @@ -5622,7 +5737,8 @@ "tags": [ "Search" ], - "x-immich-permission": "asset.read" + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/search/metadata": { @@ -5665,7 +5781,8 @@ "tags": [ "Search" ], - "x-immich-permission": "asset.read" + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/search/person": { @@ -5718,7 +5835,8 @@ "tags": [ "Search" ], - "x-immich-permission": "person.read" + "x-immich-permission": "person.read", + "description": "This endpoint requires the `person.read` permission." } }, "/search/places": { @@ -5763,7 +5881,8 @@ "tags": [ "Search" ], - "x-immich-permission": "asset.read" + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/search/random": { @@ -5809,7 +5928,8 @@ "tags": [ "Search" ], - "x-immich-permission": "asset.read" + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/search/smart": { @@ -5852,7 +5972,8 @@ "tags": [ "Search" ], - "x-immich-permission": "asset.read" + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/search/statistics": { @@ -5895,7 +6016,8 @@ "tags": [ "Search" ], - "x-immich-permission": "asset.statistics" + "x-immich-permission": "asset.statistics", + "description": "This endpoint requires the `asset.statistics` permission." } }, "/search/suggestions": { @@ -5981,7 +6103,8 @@ "tags": [ "Search" ], - "x-immich-permission": "asset.read" + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/server/about": { @@ -6014,7 +6137,8 @@ "tags": [ "Server" ], - "x-immich-permission": "server.about" + "x-immich-permission": "server.about", + "description": "This endpoint requires the `server.about` permission." } }, "/server/apk-links": { @@ -6047,7 +6171,8 @@ "tags": [ "Server" ], - "x-immich-permission": "server.apkLinks" + "x-immich-permission": "server.apkLinks", + "description": "This endpoint requires the `server.apkLinks` permission." } }, "/server/config": { @@ -6115,7 +6240,9 @@ "tags": [ "Server" ], - "x-immich-permission": "serverLicense.delete" + "x-immich-admin-only": true, + "x-immich-permission": "serverLicense.delete", + "description": "This endpoint is an admin-only route, and requires the `serverLicense.delete` permission." }, "get": { "operationId": "getServerLicense", @@ -6149,7 +6276,9 @@ "tags": [ "Server" ], - "x-immich-permission": "serverLicense.read" + "x-immich-admin-only": true, + "x-immich-permission": "serverLicense.read", + "description": "This endpoint is an admin-only route, and requires the `serverLicense.read` permission." }, "put": { "operationId": "setServerLicense", @@ -6190,7 +6319,9 @@ "tags": [ "Server" ], - "x-immich-permission": "serverLicense.update" + "x-immich-admin-only": true, + "x-immich-permission": "serverLicense.update", + "description": "This endpoint is an admin-only route, and requires the `serverLicense.update` permission." } }, "/server/media-types": { @@ -6265,7 +6396,9 @@ "tags": [ "Server" ], - "x-immich-permission": "server.statistics" + "x-immich-admin-only": true, + "x-immich-permission": "server.statistics", + "description": "This endpoint is an admin-only route, and requires the `server.statistics` permission." } }, "/server/storage": { @@ -6298,7 +6431,8 @@ "tags": [ "Server" ], - "x-immich-permission": "server.storage" + "x-immich-permission": "server.storage", + "description": "This endpoint requires the `server.storage` permission." } }, "/server/theme": { @@ -6422,7 +6556,8 @@ "tags": [ "Sessions" ], - "x-immich-permission": "session.delete" + "x-immich-permission": "session.delete", + "description": "This endpoint requires the `session.delete` permission." }, "get": { "operationId": "getSessions", @@ -6456,7 +6591,8 @@ "tags": [ "Sessions" ], - "x-immich-permission": "session.read" + "x-immich-permission": "session.read", + "description": "This endpoint requires the `session.read` permission." }, "post": { "operationId": "createSession", @@ -6497,7 +6633,8 @@ "tags": [ "Sessions" ], - "x-immich-permission": "session.create" + "x-immich-permission": "session.create", + "description": "This endpoint requires the `session.create` permission." } }, "/sessions/{id}": { @@ -6533,7 +6670,8 @@ "tags": [ "Sessions" ], - "x-immich-permission": "session.delete" + "x-immich-permission": "session.delete", + "description": "This endpoint requires the `session.delete` permission." }, "put": { "operationId": "updateSession", @@ -6584,7 +6722,8 @@ "tags": [ "Sessions" ], - "x-immich-permission": "session.update" + "x-immich-permission": "session.update", + "description": "This endpoint requires the `session.update` permission." } }, "/sessions/{id}/lock": { @@ -6620,7 +6759,8 @@ "tags": [ "Sessions" ], - "x-immich-permission": "session.lock" + "x-immich-permission": "session.lock", + "description": "This endpoint requires the `session.lock` permission." } }, "/shared-links": { @@ -6666,7 +6806,8 @@ "tags": [ "Shared Links" ], - "x-immich-permission": "sharedLink.read" + "x-immich-permission": "sharedLink.read", + "description": "This endpoint requires the `sharedLink.read` permission." }, "post": { "operationId": "createSharedLink", @@ -6707,21 +6848,14 @@ "tags": [ "Shared Links" ], - "x-immich-permission": "sharedLink.create" + "x-immich-permission": "sharedLink.create", + "description": "This endpoint requires the `sharedLink.create` permission." } }, "/shared-links/me": { "get": { "operationId": "getMySharedLink", "parameters": [ - { - "name": "key", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, { "name": "password", "required": false, @@ -6732,7 +6866,7 @@ } }, { - "name": "slug", + "name": "token", "required": false, "in": "query", "schema": { @@ -6740,7 +6874,15 @@ } }, { - "name": "token", + "name": "key", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "slug", "required": false, "in": "query", "schema": { @@ -6809,7 +6951,8 @@ "tags": [ "Shared Links" ], - "x-immich-permission": "sharedLink.delete" + "x-immich-permission": "sharedLink.delete", + "description": "This endpoint requires the `sharedLink.delete` permission." }, "get": { "operationId": "getSharedLinkById", @@ -6850,7 +6993,8 @@ "tags": [ "Shared Links" ], - "x-immich-permission": "sharedLink.read" + "x-immich-permission": "sharedLink.read", + "description": "This endpoint requires the `sharedLink.read` permission." }, "patch": { "operationId": "updateSharedLink", @@ -6901,7 +7045,8 @@ "tags": [ "Shared Links" ], - "x-immich-permission": "sharedLink.update" + "x-immich-permission": "sharedLink.update", + "description": "This endpoint requires the `sharedLink.update` permission." } }, "/shared-links/{id}/assets": { @@ -7077,7 +7222,8 @@ "tags": [ "Stacks" ], - "x-immich-permission": "stack.delete" + "x-immich-permission": "stack.delete", + "description": "This endpoint requires the `stack.delete` permission." }, "get": { "operationId": "searchStacks", @@ -7121,7 +7267,8 @@ "tags": [ "Stacks" ], - "x-immich-permission": "stack.read" + "x-immich-permission": "stack.read", + "description": "This endpoint requires the `stack.read` permission." }, "post": { "operationId": "createStack", @@ -7162,7 +7309,8 @@ "tags": [ "Stacks" ], - "x-immich-permission": "stack.create" + "x-immich-permission": "stack.create", + "description": "This endpoint requires the `stack.create` permission." } }, "/stacks/{id}": { @@ -7198,7 +7346,8 @@ "tags": [ "Stacks" ], - "x-immich-permission": "stack.delete" + "x-immich-permission": "stack.delete", + "description": "This endpoint requires the `stack.delete` permission." }, "get": { "operationId": "getStack", @@ -7239,7 +7388,8 @@ "tags": [ "Stacks" ], - "x-immich-permission": "stack.read" + "x-immich-permission": "stack.read", + "description": "This endpoint requires the `stack.read` permission." }, "put": { "operationId": "updateStack", @@ -7290,7 +7440,8 @@ "tags": [ "Stacks" ], - "x-immich-permission": "stack.update" + "x-immich-permission": "stack.update", + "description": "This endpoint requires the `stack.update` permission." } }, "/stacks/{id}/assets/{assetId}": { @@ -7335,7 +7486,8 @@ "tags": [ "Stacks" ], - "x-immich-permission": "stack.update" + "x-immich-permission": "stack.update", + "description": "This endpoint requires the `stack.update` permission." } }, "/sync/ack": { @@ -7371,7 +7523,8 @@ "tags": [ "Sync" ], - "x-immich-permission": "syncCheckpoint.delete" + "x-immich-permission": "syncCheckpoint.delete", + "description": "This endpoint requires the `syncCheckpoint.delete` permission." }, "get": { "operationId": "getSyncAck", @@ -7405,7 +7558,8 @@ "tags": [ "Sync" ], - "x-immich-permission": "syncCheckpoint.read" + "x-immich-permission": "syncCheckpoint.read", + "description": "This endpoint requires the `syncCheckpoint.read` permission." }, "post": { "operationId": "sendSyncAck", @@ -7439,7 +7593,8 @@ "tags": [ "Sync" ], - "x-immich-permission": "syncCheckpoint.update" + "x-immich-permission": "syncCheckpoint.update", + "description": "This endpoint requires the `syncCheckpoint.update` permission." } }, "/sync/delta-sync": { @@ -7562,7 +7717,8 @@ "tags": [ "Sync" ], - "x-immich-permission": "sync.stream" + "x-immich-permission": "sync.stream", + "description": "This endpoint requires the `sync.stream` permission." } }, "/system-config": { @@ -7595,7 +7751,9 @@ "tags": [ "System Config" ], - "x-immich-permission": "systemConfig.read" + "x-immich-admin-only": true, + "x-immich-permission": "systemConfig.read", + "description": "This endpoint is an admin-only route, and requires the `systemConfig.read` permission." }, "put": { "operationId": "updateConfig", @@ -7636,7 +7794,9 @@ "tags": [ "System Config" ], - "x-immich-permission": "systemConfig.update" + "x-immich-admin-only": true, + "x-immich-permission": "systemConfig.update", + "description": "This endpoint is an admin-only route, and requires the `systemConfig.update` permission." } }, "/system-config/defaults": { @@ -7669,7 +7829,9 @@ "tags": [ "System Config" ], - "x-immich-permission": "systemConfig.read" + "x-immich-admin-only": true, + "x-immich-permission": "systemConfig.read", + "description": "This endpoint is an admin-only route, and requires the `systemConfig.read` permission." } }, "/system-config/storage-template-options": { @@ -7702,7 +7864,9 @@ "tags": [ "System Config" ], - "x-immich-permission": "systemConfig.read" + "x-immich-admin-only": true, + "x-immich-permission": "systemConfig.read", + "description": "This endpoint is an admin-only route, and requires the `systemConfig.read` permission." } }, "/system-metadata/admin-onboarding": { @@ -7735,7 +7899,9 @@ "tags": [ "System Metadata" ], - "x-immich-permission": "systemMetadata.read" + "x-immich-admin-only": true, + "x-immich-permission": "systemMetadata.read", + "description": "This endpoint is an admin-only route, and requires the `systemMetadata.read` permission." }, "post": { "operationId": "updateAdminOnboarding", @@ -7769,7 +7935,9 @@ "tags": [ "System Metadata" ], - "x-immich-permission": "systemMetadata.update" + "x-immich-admin-only": true, + "x-immich-permission": "systemMetadata.update", + "description": "This endpoint is an admin-only route, and requires the `systemMetadata.update` permission." } }, "/system-metadata/reverse-geocoding-state": { @@ -7802,7 +7970,9 @@ "tags": [ "System Metadata" ], - "x-immich-permission": "systemMetadata.read" + "x-immich-admin-only": true, + "x-immich-permission": "systemMetadata.read", + "description": "This endpoint is an admin-only route, and requires the `systemMetadata.read` permission." } }, "/system-metadata/version-check-state": { @@ -7835,7 +8005,9 @@ "tags": [ "System Metadata" ], - "x-immich-permission": "systemMetadata.read" + "x-immich-admin-only": true, + "x-immich-permission": "systemMetadata.read", + "description": "This endpoint is an admin-only route, and requires the `systemMetadata.read` permission." } }, "/tags": { @@ -7871,7 +8043,8 @@ "tags": [ "Tags" ], - "x-immich-permission": "tag.read" + "x-immich-permission": "tag.read", + "description": "This endpoint requires the `tag.read` permission." }, "post": { "operationId": "createTag", @@ -7912,7 +8085,8 @@ "tags": [ "Tags" ], - "x-immich-permission": "tag.create" + "x-immich-permission": "tag.create", + "description": "This endpoint requires the `tag.create` permission." }, "put": { "operationId": "upsertTags", @@ -7956,7 +8130,8 @@ "tags": [ "Tags" ], - "x-immich-permission": "tag.create" + "x-immich-permission": "tag.create", + "description": "This endpoint requires the `tag.create` permission." } }, "/tags/assets": { @@ -7999,7 +8174,8 @@ "tags": [ "Tags" ], - "x-immich-permission": "tag.asset" + "x-immich-permission": "tag.asset", + "description": "This endpoint requires the `tag.asset` permission." } }, "/tags/{id}": { @@ -8035,7 +8211,8 @@ "tags": [ "Tags" ], - "x-immich-permission": "tag.delete" + "x-immich-permission": "tag.delete", + "description": "This endpoint requires the `tag.delete` permission." }, "get": { "operationId": "getTagById", @@ -8076,7 +8253,8 @@ "tags": [ "Tags" ], - "x-immich-permission": "tag.read" + "x-immich-permission": "tag.read", + "description": "This endpoint requires the `tag.read` permission." }, "put": { "operationId": "updateTag", @@ -8127,7 +8305,8 @@ "tags": [ "Tags" ], - "x-immich-permission": "tag.update" + "x-immich-permission": "tag.update", + "description": "This endpoint requires the `tag.update` permission." } }, "/tags/{id}/assets": { @@ -8183,7 +8362,8 @@ "tags": [ "Tags" ], - "x-immich-permission": "tag.asset" + "x-immich-permission": "tag.asset", + "description": "This endpoint requires the `tag.asset` permission." }, "put": { "operationId": "tagAssets", @@ -8237,7 +8417,8 @@ "tags": [ "Tags" ], - "x-immich-permission": "tag.asset" + "x-immich-permission": "tag.asset", + "description": "This endpoint requires the `tag.asset` permission." } }, "/timeline/bucket": { @@ -8391,7 +8572,8 @@ "tags": [ "Timeline" ], - "x-immich-permission": "asset.read" + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/timeline/buckets": { @@ -8538,7 +8720,8 @@ "tags": [ "Timeline" ], - "x-immich-permission": "asset.read" + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/trash/empty": { @@ -8571,7 +8754,8 @@ "tags": [ "Trash" ], - "x-immich-permission": "asset.delete" + "x-immich-permission": "asset.delete", + "description": "This endpoint requires the `asset.delete` permission." } }, "/trash/restore": { @@ -8604,7 +8788,8 @@ "tags": [ "Trash" ], - "x-immich-permission": "asset.delete" + "x-immich-permission": "asset.delete", + "description": "This endpoint requires the `asset.delete` permission." } }, "/trash/restore/assets": { @@ -8647,7 +8832,8 @@ "tags": [ "Trash" ], - "x-immich-permission": "asset.delete" + "x-immich-permission": "asset.delete", + "description": "This endpoint requires the `asset.delete` permission." } }, "/users": { @@ -8683,7 +8869,8 @@ "tags": [ "Users" ], - "x-immich-permission": "user.read" + "x-immich-permission": "user.read", + "description": "This endpoint requires the `user.read` permission." } }, "/users/me": { @@ -8716,7 +8903,8 @@ "tags": [ "Users" ], - "x-immich-permission": "user.read" + "x-immich-permission": "user.read", + "description": "This endpoint requires the `user.read` permission." }, "put": { "operationId": "updateMyUser", @@ -8757,7 +8945,8 @@ "tags": [ "Users" ], - "x-immich-permission": "user.update" + "x-immich-permission": "user.update", + "description": "This endpoint requires the `user.update` permission." } }, "/users/me/license": { @@ -8783,7 +8972,8 @@ "tags": [ "Users" ], - "x-immich-permission": "userLicense.delete" + "x-immich-permission": "userLicense.delete", + "description": "This endpoint requires the `userLicense.delete` permission." }, "get": { "operationId": "getUserLicense", @@ -8814,7 +9004,8 @@ "tags": [ "Users" ], - "x-immich-permission": "userLicense.read" + "x-immich-permission": "userLicense.read", + "description": "This endpoint requires the `userLicense.read` permission." }, "put": { "operationId": "setUserLicense", @@ -8855,7 +9046,8 @@ "tags": [ "Users" ], - "x-immich-permission": "userLicense.update" + "x-immich-permission": "userLicense.update", + "description": "This endpoint requires the `userLicense.update` permission." } }, "/users/me/onboarding": { @@ -8881,7 +9073,8 @@ "tags": [ "Users" ], - "x-immich-permission": "userOnboarding.delete" + "x-immich-permission": "userOnboarding.delete", + "description": "This endpoint requires the `userOnboarding.delete` permission." }, "get": { "operationId": "getUserOnboarding", @@ -8912,7 +9105,8 @@ "tags": [ "Users" ], - "x-immich-permission": "userOnboarding.read" + "x-immich-permission": "userOnboarding.read", + "description": "This endpoint requires the `userOnboarding.read` permission." }, "put": { "operationId": "setUserOnboarding", @@ -8953,7 +9147,8 @@ "tags": [ "Users" ], - "x-immich-permission": "userOnboarding.update" + "x-immich-permission": "userOnboarding.update", + "description": "This endpoint requires the `userOnboarding.update` permission." } }, "/users/me/preferences": { @@ -8986,7 +9181,8 @@ "tags": [ "Users" ], - "x-immich-permission": "userPreference.read" + "x-immich-permission": "userPreference.read", + "description": "This endpoint requires the `userPreference.read` permission." }, "put": { "operationId": "updateMyPreferences", @@ -9027,7 +9223,8 @@ "tags": [ "Users" ], - "x-immich-permission": "userPreference.update" + "x-immich-permission": "userPreference.update", + "description": "This endpoint requires the `userPreference.update` permission." } }, "/users/profile-image": { @@ -9053,7 +9250,8 @@ "tags": [ "Users" ], - "x-immich-permission": "userProfileImage.delete" + "x-immich-permission": "userProfileImage.delete", + "description": "This endpoint requires the `userProfileImage.delete` permission." }, "post": { "operationId": "createProfileImage", @@ -9095,7 +9293,8 @@ "tags": [ "Users" ], - "x-immich-permission": "userProfileImage.update" + "x-immich-permission": "userProfileImage.update", + "description": "This endpoint requires the `userProfileImage.update` permission." } }, "/users/{id}": { @@ -9138,7 +9337,8 @@ "tags": [ "Users" ], - "x-immich-permission": "user.read" + "x-immich-permission": "user.read", + "description": "This endpoint requires the `user.read` permission." } }, "/users/{id}/profile-image": { @@ -9182,7 +9382,8 @@ "tags": [ "Users" ], - "x-immich-permission": "userProfileImage.read" + "x-immich-permission": "userProfileImage.read", + "description": "This endpoint requires the `userProfileImage.read` permission." } }, "/view/folder": { diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 33f0da8a08..53a2f4b144 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -1578,6 +1578,9 @@ export type CreateProfileImageResponseDto = { profileImagePath: string; userId: string; }; +/** + * This endpoint requires the `activity.read` permission. + */ export function getActivities({ albumId, assetId, level, $type, userId }: { albumId: string; assetId?: string; @@ -1598,6 +1601,9 @@ export function getActivities({ albumId, assetId, level, $type, userId }: { ...opts })); } +/** + * This endpoint requires the `activity.create` permission. + */ export function createActivity({ activityCreateDto }: { activityCreateDto: ActivityCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -1610,6 +1616,9 @@ export function createActivity({ activityCreateDto }: { body: activityCreateDto }))); } +/** + * This endpoint requires the `activity.statistics` permission. + */ export function getActivityStatistics({ albumId, assetId }: { albumId: string; assetId?: string; @@ -1624,6 +1633,9 @@ export function getActivityStatistics({ albumId, assetId }: { ...opts })); } +/** + * This endpoint requires the `activity.delete` permission. + */ export function deleteActivity({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -1669,6 +1681,9 @@ export function sendTestEmailAdmin({ systemConfigSmtpDto }: { body: systemConfigSmtpDto }))); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.read` permission. + */ export function searchUsersAdmin({ id, withDeleted }: { id?: string; withDeleted?: boolean; @@ -1683,6 +1698,9 @@ export function searchUsersAdmin({ id, withDeleted }: { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.create` permission. + */ export function createUserAdmin({ userAdminCreateDto }: { userAdminCreateDto: UserAdminCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -1695,6 +1713,9 @@ export function createUserAdmin({ userAdminCreateDto }: { body: userAdminCreateDto }))); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.delete` permission. + */ export function deleteUserAdmin({ id, userAdminDeleteDto }: { id: string; userAdminDeleteDto: UserAdminDeleteDto; @@ -1708,6 +1729,9 @@ export function deleteUserAdmin({ id, userAdminDeleteDto }: { body: userAdminDeleteDto }))); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.read` permission. + */ export function getUserAdmin({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -1718,6 +1742,9 @@ export function getUserAdmin({ id }: { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.update` permission. + */ export function updateUserAdmin({ id, userAdminUpdateDto }: { id: string; userAdminUpdateDto: UserAdminUpdateDto; @@ -1731,6 +1758,9 @@ export function updateUserAdmin({ id, userAdminUpdateDto }: { body: userAdminUpdateDto }))); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.read` permission. + */ export function getUserPreferencesAdmin({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -1741,6 +1771,9 @@ export function getUserPreferencesAdmin({ id }: { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.update` permission. + */ export function updateUserPreferencesAdmin({ id, userPreferencesUpdateDto }: { id: string; userPreferencesUpdateDto: UserPreferencesUpdateDto; @@ -1754,6 +1787,9 @@ export function updateUserPreferencesAdmin({ id, userPreferencesUpdateDto }: { body: userPreferencesUpdateDto }))); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.delete` permission. + */ export function restoreUserAdmin({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -1765,6 +1801,9 @@ export function restoreUserAdmin({ id }: { method: "POST" })); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.read` permission. + */ export function getUserStatisticsAdmin({ id, isFavorite, isTrashed, visibility }: { id: string; isFavorite?: boolean; @@ -1782,6 +1821,9 @@ export function getUserStatisticsAdmin({ id, isFavorite, isTrashed, visibility } ...opts })); } +/** + * This endpoint requires the `album.read` permission. + */ export function getAllAlbums({ assetId, shared }: { assetId?: string; shared?: boolean; @@ -1796,6 +1838,9 @@ export function getAllAlbums({ assetId, shared }: { ...opts })); } +/** + * This endpoint requires the `album.create` permission. + */ export function createAlbum({ createAlbumDto }: { createAlbumDto: CreateAlbumDto; }, opts?: Oazapfts.RequestOpts) { @@ -1808,6 +1853,9 @@ export function createAlbum({ createAlbumDto }: { body: createAlbumDto }))); } +/** + * This endpoint requires the `album.statistics` permission. + */ export function getAlbumStatistics(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -1816,6 +1864,9 @@ export function getAlbumStatistics(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `album.delete` permission. + */ export function deleteAlbum({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -1824,6 +1875,9 @@ export function deleteAlbum({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `album.read` permission. + */ export function getAlbumInfo({ id, key, slug, withoutAssets }: { id: string; key?: string; @@ -1841,6 +1895,9 @@ export function getAlbumInfo({ id, key, slug, withoutAssets }: { ...opts })); } +/** + * This endpoint requires the `album.update` permission. + */ export function updateAlbumInfo({ id, updateAlbumDto }: { id: string; updateAlbumDto: UpdateAlbumDto; @@ -1854,6 +1911,9 @@ export function updateAlbumInfo({ id, updateAlbumDto }: { body: updateAlbumDto }))); } +/** + * This endpoint requires the `albumAsset.delete` permission. + */ export function removeAssetFromAlbum({ id, bulkIdsDto }: { id: string; bulkIdsDto: BulkIdsDto; @@ -1867,6 +1927,9 @@ export function removeAssetFromAlbum({ id, bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `albumAsset.create` permission. + */ export function addAssetsToAlbum({ id, key, slug, bulkIdsDto }: { id: string; key?: string; @@ -1885,6 +1948,9 @@ export function addAssetsToAlbum({ id, key, slug, bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `albumUser.delete` permission. + */ export function removeUserFromAlbum({ id, userId }: { id: string; userId: string; @@ -1894,6 +1960,9 @@ export function removeUserFromAlbum({ id, userId }: { method: "DELETE" })); } +/** + * This endpoint requires the `albumUser.update` permission. + */ export function updateAlbumUser({ id, userId, updateAlbumUserDto }: { id: string; userId: string; @@ -1905,6 +1974,9 @@ export function updateAlbumUser({ id, userId, updateAlbumUserDto }: { body: updateAlbumUserDto }))); } +/** + * This endpoint requires the `albumUser.create` permission. + */ export function addUsersToAlbum({ id, addUsersDto }: { id: string; addUsersDto: AddUsersDto; @@ -1918,6 +1990,9 @@ export function addUsersToAlbum({ id, addUsersDto }: { body: addUsersDto }))); } +/** + * This endpoint requires the `apiKey.read` permission. + */ export function getApiKeys(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -1926,6 +2001,9 @@ export function getApiKeys(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `apiKey.create` permission. + */ export function createApiKey({ apiKeyCreateDto }: { apiKeyCreateDto: ApiKeyCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -1938,6 +2016,9 @@ export function createApiKey({ apiKeyCreateDto }: { body: apiKeyCreateDto }))); } +/** + * This endpoint requires the `apiKey.delete` permission. + */ export function deleteApiKey({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -1946,6 +2027,9 @@ export function deleteApiKey({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `apiKey.read` permission. + */ export function getApiKey({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -1956,6 +2040,9 @@ export function getApiKey({ id }: { ...opts })); } +/** + * This endpoint requires the `apiKey.update` permission. + */ export function updateApiKey({ id, apiKeyUpdateDto }: { id: string; apiKeyUpdateDto: ApiKeyUpdateDto; @@ -1969,6 +2056,9 @@ export function updateApiKey({ id, apiKeyUpdateDto }: { body: apiKeyUpdateDto }))); } +/** + * This endpoint requires the `asset.delete` permission. + */ export function deleteAssets({ assetBulkDeleteDto }: { assetBulkDeleteDto: AssetBulkDeleteDto; }, opts?: Oazapfts.RequestOpts) { @@ -1978,6 +2068,9 @@ export function deleteAssets({ assetBulkDeleteDto }: { body: assetBulkDeleteDto }))); } +/** + * This endpoint requires the `asset.upload` permission. + */ export function uploadAsset({ key, slug, xImmichChecksum, assetMediaCreateDto }: { key?: string; slug?: string; @@ -1999,6 +2092,9 @@ export function uploadAsset({ key, slug, xImmichChecksum, assetMediaCreateDto }: }) }))); } +/** + * This endpoint requires the `asset.update` permission. + */ export function updateAssets({ assetBulkUpdateDto }: { assetBulkUpdateDto: AssetBulkUpdateDto; }, opts?: Oazapfts.RequestOpts) { @@ -2061,7 +2157,7 @@ export function runAssetJobs({ assetJobsDto }: { }))); } /** - * This property was deprecated in v1.116.0 + * This property was deprecated in v1.116.0. This endpoint requires the `asset.read` permission. */ export function getRandom({ count }: { count?: number; @@ -2075,6 +2171,9 @@ export function getRandom({ count }: { ...opts })); } +/** + * This endpoint requires the `asset.statistics` permission. + */ export function getAssetStatistics({ isFavorite, isTrashed, visibility }: { isFavorite?: boolean; isTrashed?: boolean; @@ -2091,6 +2190,9 @@ export function getAssetStatistics({ isFavorite, isTrashed, visibility }: { ...opts })); } +/** + * This endpoint requires the `asset.read` permission. + */ export function getAssetInfo({ id, key, slug }: { id: string; key?: string; @@ -2106,6 +2208,9 @@ export function getAssetInfo({ id, key, slug }: { ...opts })); } +/** + * This endpoint requires the `asset.update` permission. + */ export function updateAsset({ id, updateAssetDto }: { id: string; updateAssetDto: UpdateAssetDto; @@ -2119,6 +2224,9 @@ export function updateAsset({ id, updateAssetDto }: { body: updateAssetDto }))); } +/** + * This endpoint requires the `asset.download` permission. + */ export function downloadAsset({ id, key, slug }: { id: string; key?: string; @@ -2155,6 +2263,9 @@ export function replaceAsset({ id, key, slug, assetMediaReplaceDto }: { body: assetMediaReplaceDto }))); } +/** + * This endpoint requires the `asset.view` permission. + */ export function viewAsset({ id, key, size, slug }: { id: string; key?: string; @@ -2172,6 +2283,9 @@ export function viewAsset({ id, key, size, slug }: { ...opts })); } +/** + * This endpoint requires the `asset.view` permission. + */ export function playAssetVideo({ id, key, slug }: { id: string; key?: string; @@ -2199,6 +2313,9 @@ export function signUpAdmin({ signUpDto }: { body: signUpDto }))); } +/** + * This endpoint requires the `auth.changePassword` permission. + */ export function changePassword({ changePasswordDto }: { changePasswordDto: ChangePasswordDto; }, opts?: Oazapfts.RequestOpts) { @@ -2232,6 +2349,9 @@ export function logout(opts?: Oazapfts.RequestOpts) { method: "POST" })); } +/** + * This endpoint requires the `pinCode.delete` permission. + */ export function resetPinCode({ pinCodeResetDto }: { pinCodeResetDto: PinCodeResetDto; }, opts?: Oazapfts.RequestOpts) { @@ -2241,6 +2361,9 @@ export function resetPinCode({ pinCodeResetDto }: { body: pinCodeResetDto }))); } +/** + * This endpoint requires the `pinCode.create` permission. + */ export function setupPinCode({ pinCodeSetupDto }: { pinCodeSetupDto: PinCodeSetupDto; }, opts?: Oazapfts.RequestOpts) { @@ -2250,6 +2373,9 @@ export function setupPinCode({ pinCodeSetupDto }: { body: pinCodeSetupDto }))); } +/** + * This endpoint requires the `pinCode.update` permission. + */ export function changePinCode({ pinCodeChangeDto }: { pinCodeChangeDto: PinCodeChangeDto; }, opts?: Oazapfts.RequestOpts) { @@ -2291,6 +2417,9 @@ export function validateAccessToken(opts?: Oazapfts.RequestOpts) { method: "POST" })); } +/** + * This endpoint requires the `asset.download` permission. + */ export function downloadArchive({ key, slug, assetIdsDto }: { key?: string; slug?: string; @@ -2308,6 +2437,9 @@ export function downloadArchive({ key, slug, assetIdsDto }: { body: assetIdsDto }))); } +/** + * This endpoint requires the `asset.download` permission. + */ export function getDownloadInfo({ key, slug, downloadInfoDto }: { key?: string; slug?: string; @@ -2325,6 +2457,9 @@ export function getDownloadInfo({ key, slug, downloadInfoDto }: { body: downloadInfoDto }))); } +/** + * This endpoint requires the `duplicate.delete` permission. + */ export function deleteDuplicates({ bulkIdsDto }: { bulkIdsDto: BulkIdsDto; }, opts?: Oazapfts.RequestOpts) { @@ -2334,6 +2469,9 @@ export function deleteDuplicates({ bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `duplicate.read` permission. + */ export function getAssetDuplicates(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2342,6 +2480,9 @@ export function getAssetDuplicates(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `duplicate.delete` permission. + */ export function deleteDuplicate({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2350,6 +2491,9 @@ export function deleteDuplicate({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `face.read` permission. + */ export function getFaces({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2362,6 +2506,9 @@ export function getFaces({ id }: { ...opts })); } +/** + * This endpoint requires the `face.create` permission. + */ export function createFace({ assetFaceCreateDto }: { assetFaceCreateDto: AssetFaceCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -2371,6 +2518,9 @@ export function createFace({ assetFaceCreateDto }: { body: assetFaceCreateDto }))); } +/** + * This endpoint requires the `face.delete` permission. + */ export function deleteFace({ id, assetFaceDeleteDto }: { id: string; assetFaceDeleteDto: AssetFaceDeleteDto; @@ -2381,6 +2531,9 @@ export function deleteFace({ id, assetFaceDeleteDto }: { body: assetFaceDeleteDto }))); } +/** + * This endpoint requires the `face.update` permission. + */ export function reassignFacesById({ id, faceDto }: { id: string; faceDto: FaceDto; @@ -2394,6 +2547,9 @@ export function reassignFacesById({ id, faceDto }: { body: faceDto }))); } +/** + * This endpoint is an admin-only route, and requires the `job.read` permission. + */ export function getAllJobsStatus(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2402,6 +2558,9 @@ export function getAllJobsStatus(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `job.create` permission. + */ export function createJob({ jobCreateDto }: { jobCreateDto: JobCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -2411,6 +2570,9 @@ export function createJob({ jobCreateDto }: { body: jobCreateDto }))); } +/** + * This endpoint is an admin-only route, and requires the `job.create` permission. + */ export function sendJobCommand({ id, jobCommandDto }: { id: JobName; jobCommandDto: JobCommandDto; @@ -2424,6 +2586,9 @@ export function sendJobCommand({ id, jobCommandDto }: { body: jobCommandDto }))); } +/** + * This endpoint is an admin-only route, and requires the `library.read` permission. + */ export function getAllLibraries(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2432,6 +2597,9 @@ export function getAllLibraries(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `library.create` permission. + */ export function createLibrary({ createLibraryDto }: { createLibraryDto: CreateLibraryDto; }, opts?: Oazapfts.RequestOpts) { @@ -2444,6 +2612,9 @@ export function createLibrary({ createLibraryDto }: { body: createLibraryDto }))); } +/** + * This endpoint is an admin-only route, and requires the `library.delete` permission. + */ export function deleteLibrary({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2452,6 +2623,9 @@ export function deleteLibrary({ id }: { method: "DELETE" })); } +/** + * This endpoint is an admin-only route, and requires the `library.read` permission. + */ export function getLibrary({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2462,6 +2636,9 @@ export function getLibrary({ id }: { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `library.update` permission. + */ export function updateLibrary({ id, updateLibraryDto }: { id: string; updateLibraryDto: UpdateLibraryDto; @@ -2475,6 +2652,9 @@ export function updateLibrary({ id, updateLibraryDto }: { body: updateLibraryDto }))); } +/** + * This endpoint is an admin-only route, and requires the `library.update` permission. + */ export function scanLibrary({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2483,6 +2663,9 @@ export function scanLibrary({ id }: { method: "POST" })); } +/** + * This endpoint is an admin-only route, and requires the `library.statistics` permission. + */ export function getLibraryStatistics({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2506,11 +2689,11 @@ export function validate({ id, validateLibraryDto }: { body: validateLibraryDto }))); } -export function getMapMarkers({ fileCreatedAfter, fileCreatedBefore, isArchived, isFavorite, withPartners, withSharedAlbums }: { - fileCreatedAfter?: string; - fileCreatedBefore?: string; +export function getMapMarkers({ isArchived, isFavorite, fileCreatedAfter, fileCreatedBefore, withPartners, withSharedAlbums }: { isArchived?: boolean; isFavorite?: boolean; + fileCreatedAfter?: string; + fileCreatedBefore?: string; withPartners?: boolean; withSharedAlbums?: boolean; }, opts?: Oazapfts.RequestOpts) { @@ -2518,10 +2701,10 @@ export function getMapMarkers({ fileCreatedAfter, fileCreatedBefore, isArchived, status: 200; data: MapMarkerResponseDto[]; }>(`/map/markers${QS.query(QS.explode({ - fileCreatedAfter, - fileCreatedBefore, isArchived, isFavorite, + fileCreatedAfter, + fileCreatedBefore, withPartners, withSharedAlbums }))}`, { @@ -2542,6 +2725,9 @@ export function reverseGeocode({ lat, lon }: { ...opts })); } +/** + * This endpoint requires the `memory.read` permission. + */ export function searchMemories({ $for, isSaved, isTrashed, $type }: { $for?: string; isSaved?: boolean; @@ -2560,6 +2746,9 @@ export function searchMemories({ $for, isSaved, isTrashed, $type }: { ...opts })); } +/** + * This endpoint requires the `memory.create` permission. + */ export function createMemory({ memoryCreateDto }: { memoryCreateDto: MemoryCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -2572,6 +2761,9 @@ export function createMemory({ memoryCreateDto }: { body: memoryCreateDto }))); } +/** + * This endpoint requires the `memory.statistics` permission. + */ export function memoriesStatistics({ $for, isSaved, isTrashed, $type }: { $for?: string; isSaved?: boolean; @@ -2590,6 +2782,9 @@ export function memoriesStatistics({ $for, isSaved, isTrashed, $type }: { ...opts })); } +/** + * This endpoint requires the `memory.delete` permission. + */ export function deleteMemory({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2598,6 +2793,9 @@ export function deleteMemory({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `memory.read` permission. + */ export function getMemory({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2608,6 +2806,9 @@ export function getMemory({ id }: { ...opts })); } +/** + * This endpoint requires the `memory.update` permission. + */ export function updateMemory({ id, memoryUpdateDto }: { id: string; memoryUpdateDto: MemoryUpdateDto; @@ -2621,6 +2822,9 @@ export function updateMemory({ id, memoryUpdateDto }: { body: memoryUpdateDto }))); } +/** + * This endpoint requires the `memoryAsset.delete` permission. + */ export function removeMemoryAssets({ id, bulkIdsDto }: { id: string; bulkIdsDto: BulkIdsDto; @@ -2634,6 +2838,9 @@ export function removeMemoryAssets({ id, bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `memoryAsset.create` permission. + */ export function addMemoryAssets({ id, bulkIdsDto }: { id: string; bulkIdsDto: BulkIdsDto; @@ -2647,6 +2854,9 @@ export function addMemoryAssets({ id, bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `notification.delete` permission. + */ export function deleteNotifications({ notificationDeleteAllDto }: { notificationDeleteAllDto: NotificationDeleteAllDto; }, opts?: Oazapfts.RequestOpts) { @@ -2656,6 +2866,9 @@ export function deleteNotifications({ notificationDeleteAllDto }: { body: notificationDeleteAllDto }))); } +/** + * This endpoint requires the `notification.read` permission. + */ export function getNotifications({ id, level, $type, unread }: { id?: string; level?: NotificationLevel; @@ -2674,6 +2887,9 @@ export function getNotifications({ id, level, $type, unread }: { ...opts })); } +/** + * This endpoint requires the `notification.update` permission. + */ export function updateNotifications({ notificationUpdateAllDto }: { notificationUpdateAllDto: NotificationUpdateAllDto; }, opts?: Oazapfts.RequestOpts) { @@ -2683,6 +2899,9 @@ export function updateNotifications({ notificationUpdateAllDto }: { body: notificationUpdateAllDto }))); } +/** + * This endpoint requires the `notification.delete` permission. + */ export function deleteNotification({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2691,6 +2910,9 @@ export function deleteNotification({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `notification.read` permission. + */ export function getNotification({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2701,6 +2923,9 @@ export function getNotification({ id }: { ...opts })); } +/** + * This endpoint requires the `notification.update` permission. + */ export function updateNotification({ id, notificationUpdateDto }: { id: string; notificationUpdateDto: NotificationUpdateDto; @@ -2764,6 +2989,9 @@ export function unlinkOAuthAccount(opts?: Oazapfts.RequestOpts) { method: "POST" })); } +/** + * This endpoint requires the `partner.read` permission. + */ export function getPartners({ direction }: { direction: PartnerDirection; }, opts?: Oazapfts.RequestOpts) { @@ -2776,6 +3004,9 @@ export function getPartners({ direction }: { ...opts })); } +/** + * This endpoint requires the `partner.delete` permission. + */ export function removePartner({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2784,6 +3015,9 @@ export function removePartner({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `partner.create` permission. + */ export function createPartner({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2795,6 +3029,9 @@ export function createPartner({ id }: { method: "POST" })); } +/** + * This endpoint requires the `partner.update` permission. + */ export function updatePartner({ id, updatePartnerDto }: { id: string; updatePartnerDto: UpdatePartnerDto; @@ -2808,6 +3045,9 @@ export function updatePartner({ id, updatePartnerDto }: { body: updatePartnerDto }))); } +/** + * This endpoint requires the `person.delete` permission. + */ export function deletePeople({ bulkIdsDto }: { bulkIdsDto: BulkIdsDto; }, opts?: Oazapfts.RequestOpts) { @@ -2817,6 +3057,9 @@ export function deletePeople({ bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `person.read` permission. + */ export function getAllPeople({ closestAssetId, closestPersonId, page, size, withHidden }: { closestAssetId?: string; closestPersonId?: string; @@ -2837,6 +3080,9 @@ export function getAllPeople({ closestAssetId, closestPersonId, page, size, with ...opts })); } +/** + * This endpoint requires the `person.create` permission. + */ export function createPerson({ personCreateDto }: { personCreateDto: PersonCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -2849,6 +3095,9 @@ export function createPerson({ personCreateDto }: { body: personCreateDto }))); } +/** + * This endpoint requires the `person.update` permission. + */ export function updatePeople({ peopleUpdateDto }: { peopleUpdateDto: PeopleUpdateDto; }, opts?: Oazapfts.RequestOpts) { @@ -2861,6 +3110,9 @@ export function updatePeople({ peopleUpdateDto }: { body: peopleUpdateDto }))); } +/** + * This endpoint requires the `person.delete` permission. + */ export function deletePerson({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2869,6 +3121,9 @@ export function deletePerson({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `person.read` permission. + */ export function getPerson({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2879,6 +3134,9 @@ export function getPerson({ id }: { ...opts })); } +/** + * This endpoint requires the `person.update` permission. + */ export function updatePerson({ id, personUpdateDto }: { id: string; personUpdateDto: PersonUpdateDto; @@ -2892,6 +3150,9 @@ export function updatePerson({ id, personUpdateDto }: { body: personUpdateDto }))); } +/** + * This endpoint requires the `person.merge` permission. + */ export function mergePerson({ id, mergePersonDto }: { id: string; mergePersonDto: MergePersonDto; @@ -2905,6 +3166,9 @@ export function mergePerson({ id, mergePersonDto }: { body: mergePersonDto }))); } +/** + * This endpoint requires the `person.reassign` permission. + */ export function reassignFaces({ id, assetFaceUpdateDto }: { id: string; assetFaceUpdateDto: AssetFaceUpdateDto; @@ -2918,6 +3182,9 @@ export function reassignFaces({ id, assetFaceUpdateDto }: { body: assetFaceUpdateDto }))); } +/** + * This endpoint requires the `person.statistics` permission. + */ export function getPersonStatistics({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2928,6 +3195,9 @@ export function getPersonStatistics({ id }: { ...opts })); } +/** + * This endpoint requires the `person.read` permission. + */ export function getPersonThumbnail({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2938,6 +3208,9 @@ export function getPersonThumbnail({ id }: { ...opts })); } +/** + * This endpoint requires the `asset.read` permission. + */ export function getAssetsByCity(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2946,6 +3219,9 @@ export function getAssetsByCity(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `asset.read` permission. + */ export function getExploreData(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2954,6 +3230,9 @@ export function getExploreData(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `asset.read` permission. + */ export function searchLargeAssets({ albumIds, city, country, createdAfter, createdBefore, deviceId, isEncoded, isFavorite, isMotion, isNotInAlbum, isOffline, lensModel, libraryId, make, minFileSize, model, personIds, rating, size, state, tagIds, takenAfter, takenBefore, trashedAfter, trashedBefore, $type, updatedAfter, updatedBefore, visibility, withDeleted, withExif }: { albumIds?: string[]; city?: string | null; @@ -3027,6 +3306,9 @@ export function searchLargeAssets({ albumIds, city, country, createdAfter, creat method: "POST" })); } +/** + * This endpoint requires the `asset.read` permission. + */ export function searchAssets({ metadataSearchDto }: { metadataSearchDto: MetadataSearchDto; }, opts?: Oazapfts.RequestOpts) { @@ -3039,6 +3321,9 @@ export function searchAssets({ metadataSearchDto }: { body: metadataSearchDto }))); } +/** + * This endpoint requires the `person.read` permission. + */ export function searchPerson({ name, withHidden }: { name: string; withHidden?: boolean; @@ -3053,6 +3338,9 @@ export function searchPerson({ name, withHidden }: { ...opts })); } +/** + * This endpoint requires the `asset.read` permission. + */ export function searchPlaces({ name }: { name: string; }, opts?: Oazapfts.RequestOpts) { @@ -3065,6 +3353,9 @@ export function searchPlaces({ name }: { ...opts })); } +/** + * This endpoint requires the `asset.read` permission. + */ export function searchRandom({ randomSearchDto }: { randomSearchDto: RandomSearchDto; }, opts?: Oazapfts.RequestOpts) { @@ -3077,6 +3368,9 @@ export function searchRandom({ randomSearchDto }: { body: randomSearchDto }))); } +/** + * This endpoint requires the `asset.read` permission. + */ export function searchSmart({ smartSearchDto }: { smartSearchDto: SmartSearchDto; }, opts?: Oazapfts.RequestOpts) { @@ -3089,6 +3383,9 @@ export function searchSmart({ smartSearchDto }: { body: smartSearchDto }))); } +/** + * This endpoint requires the `asset.statistics` permission. + */ export function searchAssetStatistics({ statisticsSearchDto }: { statisticsSearchDto: StatisticsSearchDto; }, opts?: Oazapfts.RequestOpts) { @@ -3101,6 +3398,9 @@ export function searchAssetStatistics({ statisticsSearchDto }: { body: statisticsSearchDto }))); } +/** + * This endpoint requires the `asset.read` permission. + */ export function getSearchSuggestions({ country, includeNull, make, model, state, $type }: { country?: string; includeNull?: boolean; @@ -3123,6 +3423,9 @@ export function getSearchSuggestions({ country, includeNull, make, model, state, ...opts })); } +/** + * This endpoint requires the `server.about` permission. + */ export function getAboutInfo(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3131,6 +3434,9 @@ export function getAboutInfo(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `server.apkLinks` permission. + */ export function getApkLinks(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3155,12 +3461,18 @@ export function getServerFeatures(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `serverLicense.delete` permission. + */ export function deleteServerLicense(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchText("/server/license", { ...opts, method: "DELETE" })); } +/** + * This endpoint is an admin-only route, and requires the `serverLicense.read` permission. + */ export function getServerLicense(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3171,6 +3483,9 @@ export function getServerLicense(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `serverLicense.update` permission. + */ export function setServerLicense({ licenseKeyDto }: { licenseKeyDto: LicenseKeyDto; }, opts?: Oazapfts.RequestOpts) { @@ -3199,6 +3514,9 @@ export function pingServer(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `server.statistics` permission. + */ export function getServerStatistics(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3207,6 +3525,9 @@ export function getServerStatistics(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `server.storage` permission. + */ export function getStorage(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3247,12 +3568,18 @@ export function getVersionHistory(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `session.delete` permission. + */ export function deleteAllSessions(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchText("/sessions", { ...opts, method: "DELETE" })); } +/** + * This endpoint requires the `session.read` permission. + */ export function getSessions(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3261,6 +3588,9 @@ export function getSessions(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `session.create` permission. + */ export function createSession({ sessionCreateDto }: { sessionCreateDto: SessionCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -3273,6 +3603,9 @@ export function createSession({ sessionCreateDto }: { body: sessionCreateDto }))); } +/** + * This endpoint requires the `session.delete` permission. + */ export function deleteSession({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3281,6 +3614,9 @@ export function deleteSession({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `session.update` permission. + */ export function updateSession({ id, sessionUpdateDto }: { id: string; sessionUpdateDto: SessionUpdateDto; @@ -3294,6 +3630,9 @@ export function updateSession({ id, sessionUpdateDto }: { body: sessionUpdateDto }))); } +/** + * This endpoint requires the `session.lock` permission. + */ export function lockSession({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3302,6 +3641,9 @@ export function lockSession({ id }: { method: "POST" })); } +/** + * This endpoint requires the `sharedLink.read` permission. + */ export function getAllSharedLinks({ albumId }: { albumId?: string; }, opts?: Oazapfts.RequestOpts) { @@ -3314,6 +3656,9 @@ export function getAllSharedLinks({ albumId }: { ...opts })); } +/** + * This endpoint requires the `sharedLink.create` permission. + */ export function createSharedLink({ sharedLinkCreateDto }: { sharedLinkCreateDto: SharedLinkCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -3326,24 +3671,27 @@ export function createSharedLink({ sharedLinkCreateDto }: { body: sharedLinkCreateDto }))); } -export function getMySharedLink({ key, password, slug, token }: { - key?: string; +export function getMySharedLink({ password, token, key, slug }: { password?: string; - slug?: string; token?: string; + key?: string; + slug?: string; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: SharedLinkResponseDto; }>(`/shared-links/me${QS.query(QS.explode({ - key, password, - slug, - token + token, + key, + slug }))}`, { ...opts })); } +/** + * This endpoint requires the `sharedLink.delete` permission. + */ export function removeSharedLink({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3352,6 +3700,9 @@ export function removeSharedLink({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `sharedLink.read` permission. + */ export function getSharedLinkById({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3362,6 +3713,9 @@ export function getSharedLinkById({ id }: { ...opts })); } +/** + * This endpoint requires the `sharedLink.update` permission. + */ export function updateSharedLink({ id, sharedLinkEditDto }: { id: string; sharedLinkEditDto: SharedLinkEditDto; @@ -3411,6 +3765,9 @@ export function addSharedLinkAssets({ id, key, slug, assetIdsDto }: { body: assetIdsDto }))); } +/** + * This endpoint requires the `stack.delete` permission. + */ export function deleteStacks({ bulkIdsDto }: { bulkIdsDto: BulkIdsDto; }, opts?: Oazapfts.RequestOpts) { @@ -3420,6 +3777,9 @@ export function deleteStacks({ bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `stack.read` permission. + */ export function searchStacks({ primaryAssetId }: { primaryAssetId?: string; }, opts?: Oazapfts.RequestOpts) { @@ -3432,6 +3792,9 @@ export function searchStacks({ primaryAssetId }: { ...opts })); } +/** + * This endpoint requires the `stack.create` permission. + */ export function createStack({ stackCreateDto }: { stackCreateDto: StackCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -3444,6 +3807,9 @@ export function createStack({ stackCreateDto }: { body: stackCreateDto }))); } +/** + * This endpoint requires the `stack.delete` permission. + */ export function deleteStack({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3452,6 +3818,9 @@ export function deleteStack({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `stack.read` permission. + */ export function getStack({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3462,6 +3831,9 @@ export function getStack({ id }: { ...opts })); } +/** + * This endpoint requires the `stack.update` permission. + */ export function updateStack({ id, stackUpdateDto }: { id: string; stackUpdateDto: StackUpdateDto; @@ -3475,6 +3847,9 @@ export function updateStack({ id, stackUpdateDto }: { body: stackUpdateDto }))); } +/** + * This endpoint requires the `stack.update` permission. + */ export function removeAssetFromStack({ assetId, id }: { assetId: string; id: string; @@ -3484,6 +3859,9 @@ export function removeAssetFromStack({ assetId, id }: { method: "DELETE" })); } +/** + * This endpoint requires the `syncCheckpoint.delete` permission. + */ export function deleteSyncAck({ syncAckDeleteDto }: { syncAckDeleteDto: SyncAckDeleteDto; }, opts?: Oazapfts.RequestOpts) { @@ -3493,6 +3871,9 @@ export function deleteSyncAck({ syncAckDeleteDto }: { body: syncAckDeleteDto }))); } +/** + * This endpoint requires the `syncCheckpoint.read` permission. + */ export function getSyncAck(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3501,6 +3882,9 @@ export function getSyncAck(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `syncCheckpoint.update` permission. + */ export function sendSyncAck({ syncAckSetDto }: { syncAckSetDto: SyncAckSetDto; }, opts?: Oazapfts.RequestOpts) { @@ -3534,6 +3918,9 @@ export function getFullSyncForUser({ assetFullSyncDto }: { body: assetFullSyncDto }))); } +/** + * This endpoint requires the `sync.stream` permission. + */ export function getSyncStream({ syncStreamDto }: { syncStreamDto: SyncStreamDto; }, opts?: Oazapfts.RequestOpts) { @@ -3543,6 +3930,9 @@ export function getSyncStream({ syncStreamDto }: { body: syncStreamDto }))); } +/** + * This endpoint is an admin-only route, and requires the `systemConfig.read` permission. + */ export function getConfig(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3551,6 +3941,9 @@ export function getConfig(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `systemConfig.update` permission. + */ export function updateConfig({ systemConfigDto }: { systemConfigDto: SystemConfigDto; }, opts?: Oazapfts.RequestOpts) { @@ -3563,6 +3956,9 @@ export function updateConfig({ systemConfigDto }: { body: systemConfigDto }))); } +/** + * This endpoint is an admin-only route, and requires the `systemConfig.read` permission. + */ export function getConfigDefaults(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3571,6 +3967,9 @@ export function getConfigDefaults(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `systemConfig.read` permission. + */ export function getStorageTemplateOptions(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3579,6 +3978,9 @@ export function getStorageTemplateOptions(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. + */ export function getAdminOnboarding(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3587,6 +3989,9 @@ export function getAdminOnboarding(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `systemMetadata.update` permission. + */ export function updateAdminOnboarding({ adminOnboardingUpdateDto }: { adminOnboardingUpdateDto: AdminOnboardingUpdateDto; }, opts?: Oazapfts.RequestOpts) { @@ -3596,6 +4001,9 @@ export function updateAdminOnboarding({ adminOnboardingUpdateDto }: { body: adminOnboardingUpdateDto }))); } +/** + * This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. + */ export function getReverseGeocodingState(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3604,6 +4012,9 @@ export function getReverseGeocodingState(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. + */ export function getVersionCheckState(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3612,6 +4023,9 @@ export function getVersionCheckState(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `tag.read` permission. + */ export function getAllTags(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3620,6 +4034,9 @@ export function getAllTags(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `tag.create` permission. + */ export function createTag({ tagCreateDto }: { tagCreateDto: TagCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -3632,6 +4049,9 @@ export function createTag({ tagCreateDto }: { body: tagCreateDto }))); } +/** + * This endpoint requires the `tag.create` permission. + */ export function upsertTags({ tagUpsertDto }: { tagUpsertDto: TagUpsertDto; }, opts?: Oazapfts.RequestOpts) { @@ -3644,6 +4064,9 @@ export function upsertTags({ tagUpsertDto }: { body: tagUpsertDto }))); } +/** + * This endpoint requires the `tag.asset` permission. + */ export function bulkTagAssets({ tagBulkAssetsDto }: { tagBulkAssetsDto: TagBulkAssetsDto; }, opts?: Oazapfts.RequestOpts) { @@ -3656,6 +4079,9 @@ export function bulkTagAssets({ tagBulkAssetsDto }: { body: tagBulkAssetsDto }))); } +/** + * This endpoint requires the `tag.delete` permission. + */ export function deleteTag({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3664,6 +4090,9 @@ export function deleteTag({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `tag.read` permission. + */ export function getTagById({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3674,6 +4103,9 @@ export function getTagById({ id }: { ...opts })); } +/** + * This endpoint requires the `tag.update` permission. + */ export function updateTag({ id, tagUpdateDto }: { id: string; tagUpdateDto: TagUpdateDto; @@ -3687,6 +4119,9 @@ export function updateTag({ id, tagUpdateDto }: { body: tagUpdateDto }))); } +/** + * This endpoint requires the `tag.asset` permission. + */ export function untagAssets({ id, bulkIdsDto }: { id: string; bulkIdsDto: BulkIdsDto; @@ -3700,6 +4135,9 @@ export function untagAssets({ id, bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `tag.asset` permission. + */ export function tagAssets({ id, bulkIdsDto }: { id: string; bulkIdsDto: BulkIdsDto; @@ -3713,6 +4151,9 @@ export function tagAssets({ id, bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `asset.read` permission. + */ export function getTimeBucket({ albumId, isFavorite, isTrashed, key, order, personId, slug, tagId, timeBucket, userId, visibility, withPartners, withStacked }: { albumId?: string; isFavorite?: boolean; @@ -3749,6 +4190,9 @@ export function getTimeBucket({ albumId, isFavorite, isTrashed, key, order, pers ...opts })); } +/** + * This endpoint requires the `asset.read` permission. + */ export function getTimeBuckets({ albumId, isFavorite, isTrashed, key, order, personId, slug, tagId, userId, visibility, withPartners, withStacked }: { albumId?: string; isFavorite?: boolean; @@ -3783,6 +4227,9 @@ export function getTimeBuckets({ albumId, isFavorite, isTrashed, key, order, per ...opts })); } +/** + * This endpoint requires the `asset.delete` permission. + */ export function emptyTrash(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3792,6 +4239,9 @@ export function emptyTrash(opts?: Oazapfts.RequestOpts) { method: "POST" })); } +/** + * This endpoint requires the `asset.delete` permission. + */ export function restoreTrash(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3801,6 +4251,9 @@ export function restoreTrash(opts?: Oazapfts.RequestOpts) { method: "POST" })); } +/** + * This endpoint requires the `asset.delete` permission. + */ export function restoreAssets({ bulkIdsDto }: { bulkIdsDto: BulkIdsDto; }, opts?: Oazapfts.RequestOpts) { @@ -3813,6 +4266,9 @@ export function restoreAssets({ bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `user.read` permission. + */ export function searchUsers(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3821,6 +4277,9 @@ export function searchUsers(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `user.read` permission. + */ export function getMyUser(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3829,6 +4288,9 @@ export function getMyUser(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `user.update` permission. + */ export function updateMyUser({ userUpdateMeDto }: { userUpdateMeDto: UserUpdateMeDto; }, opts?: Oazapfts.RequestOpts) { @@ -3841,12 +4303,18 @@ export function updateMyUser({ userUpdateMeDto }: { body: userUpdateMeDto }))); } +/** + * This endpoint requires the `userLicense.delete` permission. + */ export function deleteUserLicense(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchText("/users/me/license", { ...opts, method: "DELETE" })); } +/** + * This endpoint requires the `userLicense.read` permission. + */ export function getUserLicense(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3855,6 +4323,9 @@ export function getUserLicense(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `userLicense.update` permission. + */ export function setUserLicense({ licenseKeyDto }: { licenseKeyDto: LicenseKeyDto; }, opts?: Oazapfts.RequestOpts) { @@ -3867,12 +4338,18 @@ export function setUserLicense({ licenseKeyDto }: { body: licenseKeyDto }))); } +/** + * This endpoint requires the `userOnboarding.delete` permission. + */ export function deleteUserOnboarding(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchText("/users/me/onboarding", { ...opts, method: "DELETE" })); } +/** + * This endpoint requires the `userOnboarding.read` permission. + */ export function getUserOnboarding(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3881,6 +4358,9 @@ export function getUserOnboarding(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `userOnboarding.update` permission. + */ export function setUserOnboarding({ onboardingDto }: { onboardingDto: OnboardingDto; }, opts?: Oazapfts.RequestOpts) { @@ -3893,6 +4373,9 @@ export function setUserOnboarding({ onboardingDto }: { body: onboardingDto }))); } +/** + * This endpoint requires the `userPreference.read` permission. + */ export function getMyPreferences(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3901,6 +4384,9 @@ export function getMyPreferences(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `userPreference.update` permission. + */ export function updateMyPreferences({ userPreferencesUpdateDto }: { userPreferencesUpdateDto: UserPreferencesUpdateDto; }, opts?: Oazapfts.RequestOpts) { @@ -3913,12 +4399,18 @@ export function updateMyPreferences({ userPreferencesUpdateDto }: { body: userPreferencesUpdateDto }))); } +/** + * This endpoint requires the `userProfileImage.delete` permission. + */ export function deleteProfileImage(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchText("/users/profile-image", { ...opts, method: "DELETE" })); } +/** + * This endpoint requires the `userProfileImage.update` permission. + */ export function createProfileImage({ createProfileImageDto }: { createProfileImageDto: CreateProfileImageDto; }, opts?: Oazapfts.RequestOpts) { @@ -3931,6 +4423,9 @@ export function createProfileImage({ createProfileImageDto }: { body: createProfileImageDto }))); } +/** + * This endpoint requires the `user.read` permission. + */ export function getUser({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3941,6 +4436,9 @@ export function getUser({ id }: { ...opts })); } +/** + * This endpoint requires the `userProfileImage.read` permission. + */ export function getProfileImage({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { diff --git a/server/src/enum.ts b/server/src/enum.ts index 666b9fc505..93d271f19c 100644 --- a/server/src/enum.ts +++ b/server/src/enum.ts @@ -413,6 +413,11 @@ export enum LogLevel { Fatal = 'fatal', } +export enum ApiCustomExtension { + Permission = 'x-immich-permission', + AdminOnly = 'x-immich-admin-only', +} + export enum MetadataKey { AuthRoute = 'auth_route', AdminRoute = 'admin_route', diff --git a/server/src/middleware/auth.guard.ts b/server/src/middleware/auth.guard.ts index 38ff1c373f..80d7a37435 100644 --- a/server/src/middleware/auth.guard.ts +++ b/server/src/middleware/auth.guard.ts @@ -10,7 +10,7 @@ import { Reflector } from '@nestjs/core'; import { ApiBearerAuth, ApiCookieAuth, ApiExtension, ApiOkResponse, ApiQuery, ApiSecurity } from '@nestjs/swagger'; import { Request } from 'express'; import { AuthDto } from 'src/dtos/auth.dto'; -import { ImmichQuery, MetadataKey, Permission } from 'src/enum'; +import { ApiCustomExtension, ImmichQuery, MetadataKey, Permission } from 'src/enum'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { AuthService, LoginDetails } from 'src/services/auth.service'; import { UAParser } from 'ua-parser-js'; @@ -19,16 +19,20 @@ type AdminRoute = { admin?: true }; type SharedLinkRoute = { sharedLink?: true }; type AuthenticatedOptions = { permission?: Permission } & (AdminRoute | SharedLinkRoute); -export const Authenticated = (options?: AuthenticatedOptions): MethodDecorator => { +export const Authenticated = (options: AuthenticatedOptions = {}): MethodDecorator => { const decorators: MethodDecorator[] = [ ApiBearerAuth(), ApiCookieAuth(), ApiSecurity(MetadataKey.ApiKeySecurity), - SetMetadata(MetadataKey.AuthRoute, options || {}), + SetMetadata(MetadataKey.AuthRoute, options), ]; + if ((options as AdminRoute).admin) { + decorators.push(ApiExtension(ApiCustomExtension.AdminOnly, true)); + } + if (options?.permission) { - decorators.push(ApiExtension('x-immich-permission', options.permission)); + decorators.push(ApiExtension(ApiCustomExtension.Permission, options.permission ?? Permission.All)); } if ((options as SharedLinkRoute)?.sharedLink) { diff --git a/server/src/utils/misc.ts b/server/src/utils/misc.ts index 3acb72b663..a32632b52d 100644 --- a/server/src/utils/misc.ts +++ b/server/src/utils/misc.ts @@ -6,7 +6,11 @@ import { SwaggerDocumentOptions, SwaggerModule, } from '@nestjs/swagger'; -import { ReferenceObject, SchemaObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface'; +import { + OperationObject, + ReferenceObject, + SchemaObject, +} from '@nestjs/swagger/dist/interfaces/open-api-spec.interface'; import _ from 'lodash'; import { writeFileSync } from 'node:fs'; import path from 'node:path'; @@ -15,7 +19,7 @@ import parse from 'picomatch/lib/parse'; import { SystemConfig } from 'src/config'; import { CLIP_MODEL_INFO, serverVersion } from 'src/constants'; import { extraSyncModels } from 'src/dtos/sync.dto'; -import { ImmichCookie, ImmichHeader, MetadataKey } from 'src/enum'; +import { ApiCustomExtension, ImmichCookie, ImmichHeader, MetadataKey } from 'src/enum'; import { LoggingRepository } from 'src/repositories/logging.repository'; export class ImmichStartupError extends Error {} @@ -198,7 +202,12 @@ const patchOpenAPI = (document: OpenAPIObject) => { trace: path.trace, }; - for (const operation of Object.values(operations)) { + for (const operation of Object.values(operations) as Array< + OperationObject & { + [ApiCustomExtension.AdminOnly]?: boolean; + [ApiCustomExtension.Permission]?: string; + } + >) { if (!operation) { continue; } @@ -211,12 +220,21 @@ const patchOpenAPI = (document: OpenAPIObject) => { // console.log(`${routeToErrorMessage(operation.operationId).padEnd(40)} (${operation.operationId})`); } - if (operation.description === '') { - delete operation.description; - } + const adminOnly = operation[ApiCustomExtension.AdminOnly] ?? false; + const permission = operation[ApiCustomExtension.Permission]; + if (permission) { + let description = (operation.description || '').trim(); + if (description && !description.endsWith('.')) { + description += '. '; + } - if (operation.parameters) { - operation.parameters = _.orderBy(operation.parameters, 'name'); + operation.description = + description + + `This endpoint ${adminOnly ? 'is an admin-only route, and ' : ''}requires the \`${permission}\` permission.`; + + if (operation.parameters) { + operation.parameters = _.orderBy(operation.parameters, 'name'); + } } } } From 47a025f39f4afcdc1ea0592e2f4f2bff4faf2485 Mon Sep 17 00:00:00 2001 From: bo0tzz Date: Wed, 30 Jul 2025 18:30:21 +0200 Subject: [PATCH 121/748] chore: also run docs build on oapi changes (#20440) --- .github/workflows/docs-build.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docs-build.yml b/.github/workflows/docs-build.yml index 93b6c8ad04..36a9a96711 100644 --- a/.github/workflows/docs-build.yml +++ b/.github/workflows/docs-build.yml @@ -18,7 +18,7 @@ jobs: permissions: contents: read outputs: - should_run: ${{ steps.found_paths.outputs.docs == 'true' || steps.should_force.outputs.should_force == 'true' }} + should_run: ${{ steps.found_paths.outputs.docs == 'true' || steps.found_paths.outputs.open-api == 'true' || steps.should_force.outputs.should_force == 'true' }} steps: - name: Checkout code uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -32,6 +32,8 @@ jobs: - 'docs/**' workflow: - '.github/workflows/docs-build.yml' + open-api: + - 'open-api/immich-openapi-specs.json' - name: Check if we should force jobs to run id: should_force run: echo "should_force=${{ steps.found_paths.outputs.workflow == 'true' || github.event_name == 'release' || github.ref_name == 'main' }}" >> "$GITHUB_OUTPUT" From 10e9c278eefd78ee71eed668145c71fadee1e39f Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 30 Jul 2025 11:43:20 -0500 Subject: [PATCH 122/748] feat: network requirement option for upload (#20302) * wifi toggle * feat: network requirement option for upload * chore: put back holding queue previous config numbers * options * backup option page * pr feedback --- i18n/en.json | 5 ++ mobile/lib/domain/models/store.model.dart | 4 +- mobile/lib/main.dart | 3 +- .../lib/pages/backup/drift_backup.page.dart | 9 ++ .../backup/drift_backup_options.page.dart | 68 +++++++++++++++ mobile/lib/pages/common/settings.page.dart | 7 +- .../backup/drift_backup.provider.dart | 8 +- mobile/lib/routing/router.dart | 4 +- mobile/lib/routing/router.gr.dart | 16 ++++ mobile/lib/services/app_settings.service.dart | 4 +- mobile/lib/services/upload.service.dart | 24 +++++- .../drift_backup_settings.dart | 82 +++++++++++++++++++ 12 files changed, 220 insertions(+), 14 deletions(-) create mode 100644 mobile/lib/pages/backup/drift_backup_options.page.dart create mode 100644 mobile/lib/widgets/settings/backup_settings/drift_backup_settings.dart diff --git a/i18n/en.json b/i18n/en.json index c445110996..94f6920745 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -580,8 +580,10 @@ "backup_manual_in_progress": "Upload already in progress. Try after sometime", "backup_manual_success": "Success", "backup_manual_title": "Upload status", + "backup_options": "Backup Options", "backup_options_page_title": "Backup options", "backup_setting_subtitle": "Manage background and foreground upload settings", + "backup_settings_subtitle": "Manage upload settings", "backward": "Backward", "beta_sync": "Beta Sync Status", "beta_sync_subtitle": "Manage the new sync system", @@ -1312,6 +1314,9 @@ "my_albums": "My albums", "name": "Name", "name_or_nickname": "Name or nickname", + "network_requirement_photos_upload": "Use cellular data to backup photos", + "network_requirement_videos_upload": "Use cellular data to backup videos", + "network_requirements_updated": "Network requirements changed, resetting backup queue", "networking_settings": "Networking", "networking_subtitle": "Manage the server endpoint settings", "never": "Never", diff --git a/mobile/lib/domain/models/store.model.dart b/mobile/lib/domain/models/store.model.dart index 305b3f3387..e4e316b814 100644 --- a/mobile/lib/domain/models/store.model.dart +++ b/mobile/lib/domain/models/store.model.dart @@ -71,7 +71,9 @@ enum StoreKey { photoManagerCustomFilter._(1000), betaPromptShown._(1001), betaTimeline._(1002), - enableBackup._(1003); + enableBackup._(1003), + useWifiForUploadVideos._(1004), + useWifiForUploadPhotos._(1005); const StoreKey._(this.id); final int id; diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 63220295ff..d1f415a304 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -91,10 +91,9 @@ Future initApp() async { initializeTimeZones(); // Initialize the file downloader - await FileDownloader().configure( // maxConcurrent: 6, maxConcurrentByHost(server):6, maxConcurrentByGroup: 3 - globalConfig: (Config.holdingQueue, (1000, 1000, 1000)), + globalConfig: (Config.holdingQueue, (6, 6, 3)), ); await FileDownloader().trackTasksInGroup(kDownloadGroupLivePhoto, markDownloadedComplete: false); diff --git a/mobile/lib/pages/backup/drift_backup.page.dart b/mobile/lib/pages/backup/drift_backup.page.dart index 6d31c75946..70e8190014 100644 --- a/mobile/lib/pages/backup/drift_backup.page.dart +++ b/mobile/lib/pages/backup/drift_backup.page.dart @@ -65,6 +65,15 @@ class _DriftBackupPageState extends ConsumerState { splashRadius: 24, icon: const Icon(Icons.arrow_back_ios_rounded), ), + actions: [ + IconButton( + onPressed: () { + context.pushRoute(const DriftBackupOptionsRoute()); + }, + icon: const Icon(Icons.settings_outlined), + tooltip: "backup_options".t(context: context), + ), + ], ), body: Stack( children: [ diff --git a/mobile/lib/pages/backup/drift_backup_options.page.dart b/mobile/lib/pages/backup/drift_backup_options.page.dart new file mode 100644 index 0000000000..92f911ae1e --- /dev/null +++ b/mobile/lib/pages/backup/drift_backup_options.page.dart @@ -0,0 +1,68 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/widgets/settings/backup_settings/drift_backup_settings.dart'; + +@RoutePage() +class DriftBackupOptionsPage extends ConsumerWidget { + const DriftBackupOptionsPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + bool hasPopped = false; + final previousWifiReqForVideos = Store.tryGet(StoreKey.useWifiForUploadVideos) ?? false; + final previousWifiReqForPhotos = Store.tryGet(StoreKey.useWifiForUploadPhotos) ?? false; + return PopScope( + onPopInvokedWithResult: (didPop, result) async { + // There is an issue with Flutter where the pop event + // can be triggered multiple times, so we guard it with _hasPopped + + final currentWifiReqForVideos = Store.tryGet(StoreKey.useWifiForUploadVideos) ?? false; + final currentWifiReqForPhotos = Store.tryGet(StoreKey.useWifiForUploadPhotos) ?? false; + + if (currentWifiReqForVideos == previousWifiReqForVideos && + currentWifiReqForPhotos == previousWifiReqForPhotos) { + return; + } + + if (didPop && !hasPopped) { + hasPopped = true; + + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + await ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id); + final isBackupEnabled = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup); + if (!isBackupEnabled) { + return; + } + + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text("network_requirements_updated".t(context: context)), + duration: const Duration(seconds: 4), + ), + ); + + final backupNotifier = ref.read(driftBackupProvider.notifier); + backupNotifier.cancel().then((_) { + backupNotifier.startBackup(currentUser.id); + }); + } + }, + child: Scaffold( + appBar: AppBar(title: Text("backup_options".t(context: context))), + body: const DriftBackupSettings(), + ), + ); + } +} diff --git a/mobile/lib/pages/common/settings.page.dart b/mobile/lib/pages/common/settings.page.dart index d7ecb7e582..7bc8cd2b3a 100644 --- a/mobile/lib/pages/common/settings.page.dart +++ b/mobile/lib/pages/common/settings.page.dart @@ -2,6 +2,7 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart' hide Store; +import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/routing/router.dart'; @@ -9,6 +10,7 @@ import 'package:immich_mobile/widgets/settings/advanced_settings.dart'; import 'package:immich_mobile/widgets/settings/asset_list_settings/asset_list_settings.dart'; import 'package:immich_mobile/widgets/settings/asset_viewer_settings/asset_viewer_settings.dart'; import 'package:immich_mobile/widgets/settings/backup_settings/backup_settings.dart'; +import 'package:immich_mobile/widgets/settings/backup_settings/drift_backup_settings.dart'; import 'package:immich_mobile/widgets/settings/beta_sync_settings/beta_sync_settings.dart'; import 'package:immich_mobile/widgets/settings/beta_timeline_list_tile.dart'; import 'package:immich_mobile/widgets/settings/language_settings.dart'; @@ -21,7 +23,7 @@ enum SettingSection { beta('beta_sync', Icons.sync_outlined, "beta_sync_subtitle"), advanced('advanced', Icons.build_outlined, "advanced_settings_tile_subtitle"), assetViewer('asset_viewer_settings_title', Icons.image_outlined, "asset_viewer_settings_subtitle"), - backup('backup', Icons.cloud_upload_outlined, "backup_setting_subtitle"), + backup('backup', Icons.cloud_upload_outlined, "backup_settings_subtitle"), languages('language', Icons.language, "setting_languages_subtitle"), networking('networking_settings', Icons.wifi, "networking_subtitle"), notifications('notifications', Icons.notifications_none_rounded, "setting_notifications_subtitle"), @@ -36,7 +38,8 @@ enum SettingSection { SettingSection.beta => const _BetaLandscapeToggle(), SettingSection.advanced => const AdvancedSettings(), SettingSection.assetViewer => const AssetViewerSettings(), - SettingSection.backup => const BackupSettings(), + SettingSection.backup => + Store.tryGet(StoreKey.betaTimeline) ?? false ? const DriftBackupSettings() : const BackupSettings(), SettingSection.languages => const LanguageSettings(), SettingSection.networking => const NetworkingSettings(), SettingSection.notifications => const NotificationSetting(), diff --git a/mobile/lib/providers/backup/drift_backup.provider.dart b/mobile/lib/providers/backup/drift_backup.provider.dart index aca10e60a7..39949fd526 100644 --- a/mobile/lib/providers/backup/drift_backup.provider.dart +++ b/mobile/lib/providers/backup/drift_backup.provider.dart @@ -187,12 +187,12 @@ class DriftBackupState { } } -final driftBackupProvider = StateNotifierProvider((ref) { - return ExpBackupNotifier(ref.watch(uploadServiceProvider)); +final driftBackupProvider = StateNotifierProvider((ref) { + return DriftBackupNotifier(ref.watch(uploadServiceProvider)); }); -class ExpBackupNotifier extends StateNotifier { - ExpBackupNotifier(this._uploadService) +class DriftBackupNotifier extends StateNotifier { + DriftBackupNotifier(this._uploadService) : super( const DriftBackupState( totalCount: 0, diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index 54d4d080d4..4fe1673893 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -28,6 +28,7 @@ import 'package:immich_mobile/pages/backup/drift_backup.page.dart'; import 'package:immich_mobile/pages/backup/backup_album_selection.page.dart'; import 'package:immich_mobile/pages/backup/backup_controller.page.dart'; import 'package:immich_mobile/pages/backup/backup_options.page.dart'; +import 'package:immich_mobile/pages/backup/drift_backup_options.page.dart'; import 'package:immich_mobile/pages/backup/drift_upload_detail.page.dart'; import 'package:immich_mobile/pages/backup/failed_backup_status.page.dart'; import 'package:immich_mobile/pages/common/activities.page.dart'; @@ -322,13 +323,12 @@ class AppRouter extends RootStackRouter { AutoRoute(page: DriftPlaceDetailRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: DriftUserSelectionRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: ChangeExperienceRoute.page, guards: [_authGuard, _duplicateGuard]), - AutoRoute(page: DriftPartnerRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: DriftUploadDetailRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: BetaSyncSettingsRoute.page, guards: [_authGuard, _duplicateGuard]), - AutoRoute(page: DriftPeopleCollectionRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: DriftPersonRoute.page, guards: [_authGuard]), + AutoRoute(page: DriftBackupOptionsRoute.page, guards: [_authGuard, _duplicateGuard]), // required to handle all deeplinks in deep_link.service.dart // auto_route_library#1722 RedirectRoute(path: '*', redirectTo: '/'), diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index 63e8c6ecfe..e8f0dd8b1f 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -764,6 +764,22 @@ class DriftBackupAlbumSelectionRoute extends PageRouteInfo { ); } +/// generated route for +/// [DriftBackupOptionsPage] +class DriftBackupOptionsRoute extends PageRouteInfo { + const DriftBackupOptionsRoute({List? children}) + : super(DriftBackupOptionsRoute.name, initialChildren: children); + + static const String name = 'DriftBackupOptionsRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftBackupOptionsPage(); + }, + ); +} + /// generated route for /// [DriftBackupPage] class DriftBackupRoute extends PageRouteInfo { diff --git a/mobile/lib/services/app_settings.service.dart b/mobile/lib/services/app_settings.service.dart index e705912aa5..8a4b0c6719 100644 --- a/mobile/lib/services/app_settings.service.dart +++ b/mobile/lib/services/app_settings.service.dart @@ -47,7 +47,9 @@ enum AppSettingsEnum { autoEndpointSwitching(StoreKey.autoEndpointSwitching, null, false), photoManagerCustomFilter(StoreKey.photoManagerCustomFilter, null, true), betaTimeline(StoreKey.betaTimeline, null, false), - enableBackup(StoreKey.enableBackup, null, false); + enableBackup(StoreKey.enableBackup, null, false), + useCellularForUploadVideos(StoreKey.useWifiForUploadVideos, null, false), + useCellularForUploadPhotos(StoreKey.useWifiForUploadPhotos, null, false); const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue); diff --git a/mobile/lib/services/upload.service.dart b/mobile/lib/services/upload.service.dart index c41d2b2e5f..dba3817b2c 100644 --- a/mobile/lib/services/upload.service.dart +++ b/mobile/lib/services/upload.service.dart @@ -12,11 +12,13 @@ import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/backup.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; import 'package:immich_mobile/providers/infrastructure/storage.provider.dart'; import 'package:immich_mobile/repositories/upload.repository.dart'; import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:path/path.dart' as p; final uploadServiceProvider = Provider((ref) { @@ -25,6 +27,7 @@ final uploadServiceProvider = Provider((ref) { ref.watch(backupRepositoryProvider), ref.watch(storageRepositoryProvider), ref.watch(localAssetRepository), + ref.watch(appSettingsServiceProvider), ); ref.onDispose(service.dispose); @@ -32,7 +35,13 @@ final uploadServiceProvider = Provider((ref) { }); class UploadService { - UploadService(this._uploadRepository, this._backupRepository, this._storageRepository, this._localAssetRepository) { + UploadService( + this._uploadRepository, + this._backupRepository, + this._storageRepository, + this._localAssetRepository, + this._appSettingsService, + ) { _uploadRepository.onUploadStatus = _onUploadCallback; _uploadRepository.onTaskProgress = _onTaskProgressCallback; } @@ -41,6 +50,7 @@ class UploadService { final DriftBackupRepository _backupRepository; final StorageRepository _storageRepository; final DriftLocalAssetRepository _localAssetRepository; + final AppSettingsService _appSettingsService; final StreamController _taskStatusController = StreamController.broadcast(); final StreamController _taskProgressController = StreamController.broadcast(); @@ -240,6 +250,14 @@ class UploadService { livePhotoVideoId: '', ).toJson(); + bool requiresWiFi = true; + + if (asset.isVideo && _appSettingsService.getSetting(AppSettingsEnum.useCellularForUploadVideos)) { + requiresWiFi = false; + } else if (!asset.isVideo && _appSettingsService.getSetting(AppSettingsEnum.useCellularForUploadPhotos)) { + requiresWiFi = false; + } + return buildUploadTask( file, originalFileName: originalFileName, @@ -248,6 +266,7 @@ class UploadService { group: group, priority: priority, isFavorite: asset.isFavorite, + requiresWiFi: requiresWiFi, ); } @@ -284,12 +303,12 @@ class UploadService { String? metadata, int? priority, bool? isFavorite, + bool requiresWiFi = true, }) async { final serverEndpoint = Store.get(StoreKey.serverEndpoint); final url = Uri.parse('$serverEndpoint/assets').toString(); final headers = ApiService.getRequestHeaders(); final deviceId = Store.get(StoreKey.deviceId); - final (baseDirectory, directory, filename) = await Task.split(filePath: file.path); final stats = await file.stat(); final fileCreatedAt = stats.changed; @@ -318,6 +337,7 @@ class UploadService { fileField: 'assetData', metaData: metadata ?? '', group: group, + requiresWiFi: requiresWiFi, priority: priority ?? 5, updates: Updates.statusAndProgress, retries: 3, diff --git a/mobile/lib/widgets/settings/backup_settings/drift_backup_settings.dart b/mobile/lib/widgets/settings/backup_settings/drift_backup_settings.dart new file mode 100644 index 0000000000..553eb939c2 --- /dev/null +++ b/mobile/lib/widgets/settings/backup_settings/drift_backup_settings.dart @@ -0,0 +1,82 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart'; + +class DriftBackupSettings extends StatelessWidget { + const DriftBackupSettings({super.key}); + + @override + Widget build(BuildContext context) { + return const SettingsSubPageScaffold(settings: [_UseWifiForUploadVideosButton(), _UseWifiForUploadPhotosButton()]); + } +} + +class _UseWifiForUploadVideosButton extends ConsumerWidget { + const _UseWifiForUploadVideosButton(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final valueStream = Store.watch(StoreKey.useWifiForUploadVideos); + + return ListTile( + title: Text( + "videos".t(context: context), + style: context.textTheme.titleMedium?.copyWith(color: context.primaryColor), + ), + subtitle: Text("network_requirement_videos_upload".t(context: context), style: context.textTheme.labelLarge), + trailing: StreamBuilder( + stream: valueStream, + initialData: Store.tryGet(StoreKey.useWifiForUploadVideos) ?? false, + builder: (context, snapshot) { + final value = snapshot.data ?? false; + return Switch( + value: value, + onChanged: (bool newValue) async { + await ref + .read(appSettingsServiceProvider) + .setSetting(AppSettingsEnum.useCellularForUploadVideos, newValue); + }, + ); + }, + ), + ); + } +} + +class _UseWifiForUploadPhotosButton extends ConsumerWidget { + const _UseWifiForUploadPhotosButton(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final valueStream = Store.watch(StoreKey.useWifiForUploadPhotos); + + return ListTile( + title: Text( + "photos".t(context: context), + style: context.textTheme.titleMedium?.copyWith(color: context.primaryColor), + ), + subtitle: Text("network_requirement_photos_upload".t(context: context), style: context.textTheme.labelLarge), + trailing: StreamBuilder( + stream: valueStream, + initialData: Store.tryGet(StoreKey.useWifiForUploadPhotos) ?? false, + builder: (context, snapshot) { + final value = snapshot.data ?? false; + return Switch( + value: value, + onChanged: (bool newValue) async { + await ref + .read(appSettingsServiceProvider) + .setSetting(AppSettingsEnum.useCellularForUploadPhotos, newValue); + }, + ); + }, + ), + ); + } +} From c278b7ad17ebff19bdd8019caee380ad66077dc6 Mon Sep 17 00:00:00 2001 From: "Weblate (bot)" Date: Wed, 30 Jul 2025 19:16:47 +0200 Subject: [PATCH 123/748] chore(web): update translations (#20105) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: AbuKareem Tuffaha Co-authored-by: Adam Uchmanowicz Co-authored-by: AgentTricky Co-authored-by: Alexandre Garnier Co-authored-by: Alvin Co-authored-by: Andreas Johansen Co-authored-by: Bartłomiej <20731216+Jarsey45@users.noreply.github.com> Co-authored-by: Bartłomiej Co-authored-by: Benjamin Graf Co-authored-by: CanbiZ Co-authored-by: Cezar Olteanu Co-authored-by: Christoph Auer Co-authored-by: Dag Stuan Co-authored-by: Davide Ciaccia Co-authored-by: Davide Vegliante Co-authored-by: Dennis Premoli Co-authored-by: DevServs Co-authored-by: Dmitry Banny Co-authored-by: Felipe Silva Co-authored-by: Fjuro Co-authored-by: Florian Ostertag Co-authored-by: Hurricane-32 Co-authored-by: Indrek Haav Co-authored-by: Javier Villanueva García Co-authored-by: John Molkavitch Co-authored-by: Jordy H Co-authored-by: Jozef Gaal Co-authored-by: Kasper Honoré Co-authored-by: Lauritz Tieste Co-authored-by: Leo Bottaro Co-authored-by: MaBeniu Co-authored-by: Mateo Varela Co-authored-by: Matjaž T Co-authored-by: Mikkel Dupont Olesen Co-authored-by: Musab Ustun Co-authored-by: Mārtiņš Bruņenieks Co-authored-by: Nick Huang Co-authored-by: Nico Kaiser Co-authored-by: Nicolò Co-authored-by: Nikolina Babok Co-authored-by: Pavel Miniutka Co-authored-by: Petri Hämäläinen Co-authored-by: Phantom0174 Co-authored-by: R J Co-authored-by: Raman Venmarathoor Co-authored-by: Santiago Co-authored-by: Saschl Co-authored-by: Sergey Katsubo Co-authored-by: Shawn Co-authored-by: Sylvain Pichon Co-authored-by: Tajbir Prottoy Co-authored-by: Tanishq Sindhu Co-authored-by: Tijs-B Co-authored-by: Tony Ronaldo Matute Co-authored-by: User 123456789 Co-authored-by: Vegard Fladby Co-authored-by: Xo Co-authored-by: Zvonimir Co-authored-by: adri1m64 Co-authored-by: chamdim Co-authored-by: eav5jhl0 Co-authored-by: grgergo Co-authored-by: johnwoo_nl Co-authored-by: manosrh Co-authored-by: maxius65 Co-authored-by: neketos851 Co-authored-by: over adm <1ron3gg@gmail.com> Co-authored-by: pyccl Co-authored-by: syyhs668 <812741539@qq.com> Co-authored-by: thehijacker Co-authored-by: traptegies Co-authored-by: tsloms Co-authored-by: waclaw66 --- i18n/ar.json | 26 +++- i18n/be.json | 91 ++++++++++++- i18n/bg.json | 3 +- i18n/bn.json | 2 +- i18n/ca.json | 3 +- i18n/cs.json | 37 +++++- i18n/da.json | 18 ++- i18n/de.json | 49 +++++-- i18n/el.json | 87 +++++++++++- i18n/es.json | 62 +++++++-- i18n/et.json | 35 ++++- i18n/fa.json | 2 - i18n/fi.json | 4 +- i18n/fr.json | 40 ++++-- i18n/gl.json | 3 +- i18n/he.json | 91 +++++++++++-- i18n/hi.json | 245 ++++++++++++++++++++++++++++++++-- i18n/hr.json | 117 ++++++++++++++-- i18n/hu.json | 39 +++++- i18n/hy.json | 1 - i18n/id.json | 3 +- i18n/it.json | 58 +++++++- i18n/ja.json | 3 +- i18n/ko.json | 3 +- i18n/lt.json | 28 +++- i18n/lv.json | 26 +++- i18n/mk.json | 1 - i18n/ml.json | 63 ++++++++- i18n/nb_NO.json | 103 ++++++++------ i18n/nl.json | 83 +++++++----- i18n/pl.json | 54 +++++++- i18n/pt.json | 55 ++++++-- i18n/pt_BR.json | 77 ++++++++++- i18n/ro.json | 287 ++++++++++++++++++++++++++++++++++++++-- i18n/ru.json | 43 ++++-- i18n/sk.json | 97 ++++++++------ i18n/sl.json | 31 ++++- i18n/sr_Cyrl.json | 3 +- i18n/sr_Latn.json | 3 +- i18n/sv.json | 7 +- i18n/ta.json | 1 - i18n/te.json | 1 - i18n/th.json | 3 +- i18n/tr.json | 91 ++++++++++++- i18n/uk.json | 13 +- i18n/vi.json | 3 +- i18n/zh_Hant.json | 42 +++++- i18n/zh_SIMPLIFIED.json | 31 +++-- 48 files changed, 1850 insertions(+), 318 deletions(-) diff --git a/i18n/ar.json b/i18n/ar.json index 6042c8f82f..13e785668d 100644 --- a/i18n/ar.json +++ b/i18n/ar.json @@ -393,6 +393,7 @@ "album_cover_updated": "تم تحديث غلاف الألبوم", "album_delete_confirmation": "هل أنت متأكد أنك تريد حذف الألبوم {album}؟", "album_delete_confirmation_description": "إذا تمت مشاركة هذا الألبوم، فلن يتمكن المستخدمون الآخرون من الوصول إليه بعد الآن.", + "album_deleted": "تم حذف الالبوم", "album_info_card_backup_album_excluded": "مستبعد", "album_info_card_backup_album_included": "متضمنة", "album_info_updated": "تم تحديث معلومات الألبوم", @@ -402,6 +403,7 @@ "album_options": "إعدادات الألبوم", "album_remove_user": "هل ترغب في إزالة المستخدم؟", "album_remove_user_confirmation": "هل أنت متأكد أنك تريد إزالة {user}؟", + "album_search_not_found": "لم يتم ايجاد البوم مطابق لبحثك", "album_share_no_users": "يبدو أنك قمت بمشاركة هذا الألبوم مع جميع المستخدمين أو ليس لديك أي مستخدم للمشاركة معه.", "album_updated": "تم تحديث الألبوم", "album_updated_setting_description": "تلقي إشعارًا عبر البريد الإلكتروني عندما يحتوي الألبوم المشترك على محتويات جديدة", @@ -421,6 +423,7 @@ "albums_default_sort_order": "ترتيب الألبوم الافتراضي", "albums_default_sort_order_description": "ترتيب فرز الأصول الأولي عند إنشاء ألبومات جديدة.", "albums_feature_description": "مجموعة من الأصول التي يمكن مشاركتها مع مستخدمين آخرين.", + "albums_on_device_count": "عدد الالبومات على الجهاز ({count})", "all": "الكل", "all_albums": "جميع الألبومات", "all_people": "جميع الأشخاص", @@ -435,7 +438,7 @@ "api_key_description": "سيتم عرض هذه القيمة مرة واحدة فقط. يرجى التأكد من نسخها قبل إغلاق النافذة.", "api_key_empty": "يجب ألا يكون اسم مفتاح API فارغًا", "api_keys": "مفاتيح API", - "app_bar_signout_dialog_content": "هل أنت متأكد أنك تريد الخروج", + "app_bar_signout_dialog_content": "هل أنت متأكد أنك تريد تسجيل الخروج؟", "app_bar_signout_dialog_ok": "نعم", "app_bar_signout_dialog_title": "خروج", "app_settings": "إعدادات التطبيق", @@ -504,6 +507,7 @@ "back_close_deselect": "الرجوع أو الإغلاق أو إلغاء التحديد", "background_location_permission": "اذن الوصول للموقع في الخلفية", "background_location_permission_content": "للتمكن من تبديل الشبكه بالخلفية، Immich يحتاج*دائما* للحصول على موقع دقيق ليتمكن التطبيق من قرائة اسم شبكة الWi-Fi", + "backup": "دعم", "backup_album_selection_page_albums_device": "الالبومات على الجهاز ({count})", "backup_album_selection_page_albums_tap": "انقر للتضمين، وانقر نقرًا مزدوجًا للاستثناء", "backup_album_selection_page_assets_scatter": "يمكن أن تنتشر الأصول عبر ألبومات متعددة. وبالتالي، يمكن تضمين الألبومات أو استبعادها أثناء عملية النسخ الاحتياطي.", @@ -567,6 +571,8 @@ "backup_options_page_title": "خيارات النسخ الاحتياطي", "backup_setting_subtitle": "ادارة اعدادات التحميل في الخلفية والمقدمة", "backward": "الى الوراء", + "beta_sync": "حالة المزامنة التجريبية", + "beta_sync_subtitle": "ادارة نظام المزامنة الجديد", "biometric_auth_enabled": "المصادقة البايومترية مفعله", "biometric_locked_out": "لقد قفلت عنك المصادقة البيومترية", "biometric_no_options": "لا توجد خيارات بايومترية متوفرة", @@ -584,7 +590,7 @@ "cache_settings_clear_cache_button": "مسح ذاكرة التخزين المؤقت", "cache_settings_clear_cache_button_title": "يقوم بمسح ذاكرة التخزين المؤقت للتطبيق.سيؤثر هذا بشكل كبير على أداء التطبيق حتى إعادة بناء ذاكرة التخزين المؤقت.", "cache_settings_duplicated_assets_clear_button": "واضح", - "cache_settings_duplicated_assets_subtitle": "الصور ومقاطع الفيديو اللتي تم تجاهلها المدرجة في التطبيق", + "cache_settings_duplicated_assets_subtitle": "الصور والفيديوهات اللتي تم تجاهلها في التطبيق", "cache_settings_duplicated_assets_title": "الاصول المكررة ({count})", "cache_settings_statistics_album": "مكتبه الصور المصغره", "cache_settings_statistics_full": "صور كاملة", @@ -601,6 +607,7 @@ "cancel": "إلغاء", "cancel_search": "الغاء البحث", "canceled": "تم الالغاء", + "canceling": "جارِ الالغاء", "cannot_merge_people": "لا يمكن دمج الأشخاص", "cannot_undo_this_action": "لا يمكنك التراجع عن هذا الإجراء!", "cannot_update_the_description": "لا يمكن تحديث الوصف", @@ -616,7 +623,7 @@ "change_password": "تغيير كلمة المرور", "change_password_description": "هذه إما هي المرة الأولى التي تقوم فيها بتسجيل الدخول إلى النظام أو أنه تم تقديم طلب لتغيير كلمة المرور الخاصة بك. الرجاء إدخال كلمة المرور الجديدة أدناه.", "change_password_form_confirm_password": "تأكيد كلمة المرور", - "change_password_form_description": "مرحبًا ،هذه هي المرة الأولى التي تقوم فيها بالتسجيل في النظام أو تم تقديم طلب لتغيير كلمة المرور الخاصة بك.الرجاء إدخال كلمة المرور الجديدة أدناه", + "change_password_form_description": "مرحبًا {name}،\n\nاما ان تكون هذه هي المرة الأولى التي تقوم فيها بالتسجيل في النظام أو تم تقديم طلب لتغيير كلمة المرور الخاصة بك. الرجاء إدخال كلمة المرور الجديدة أدناه.", "change_password_form_new_password": "كلمة المرور الجديدة", "change_password_form_password_mismatch": "كلمة المرور غير مطابقة", "change_password_form_reenter_new_password": "أعد إدخال كلمة مرور جديدة", @@ -733,7 +740,8 @@ "default_locale": "اللغة الافتراضية", "default_locale_description": "تنسيق التواريخ والأرقام بناءً على لغة المتصفح الخاص بك", "delete": "حذف", - "delete_action_prompt": "{count} حذف بشكل نهائي", + "delete_action_confirmation_message": "هل انت متأكد من حذف هذا الملف؟ هذا سؤدي الى نقل الملف الى سلة مهملات الخادم وسيتم اشعارك ان كنت تريد حذفه على الجهاز", + "delete_action_prompt": "تم حذف {count}", "delete_album": "حذف الألبوم", "delete_api_key_prompt": "هل أنت متأكد أنك تريد حذف مفتاح API هذا؟", "delete_dialog_alert": "هذه العناصر سيتم حذفها بشكل دائم من Immich و من جهازك", @@ -747,9 +755,12 @@ "delete_key": "حذف المفتاح", "delete_library": "حذف المكتبة", "delete_link": "حذف الرابط", + "delete_local_action_prompt": "تم حذف {count} من الجهاز", "delete_local_dialog_ok_backed_up_only": "حذف النسخة الاحتياطية فقط", "delete_local_dialog_ok_force": "احذف على أي حال", "delete_others": "حذف الأخرى", + "delete_permanently": "حذف بشكل نهائي", + "delete_permanently_action_prompt": "تم حذف {count} بشكل نهائي", "delete_shared_link": "حذف الرابط المشترك", "delete_shared_link_dialog_title": "حذف الرابط المشترك", "delete_tag": "حذف العلامة", @@ -760,6 +771,7 @@ "description": "وصف", "description_input_hint_text": "اضف وصفا...", "description_input_submit_error": "خطأ تحديث الوصف ، تحقق من السجل لمزيد من التفاصيل", + "deselect_all": "الغاء تحديد الكل", "details": "تفاصيل", "direction": "الإتجاه", "disabled": "معطل", @@ -777,6 +789,7 @@ "documentation": "الوثائق", "done": "تم", "download": "تنزيل", + "download_action_prompt": "يتم تنزيل {count} ملف", "download_canceled": "الغي التنزيل", "download_complete": "اكتمل التنزيل", "download_enqueue": "تنزيل في قائمة الانتظار", @@ -833,6 +846,7 @@ "empty_trash": "أفرغ سلة المهملات", "empty_trash_confirmation": "هل أنت متأكد أنك تريد إفراغ سلة المهملات؟ سيؤدي هذا إلى إزالة جميع المحتويات الموجودة في سلة المهملات بشكل نهائي من Immich.\nلا يمكنك التراجع عن هذا الإجراء!", "enable": "تفعيل", + "enable_backup": "تشغيل النسخ الاحتياطي", "enable_biometric_auth_description": "أدخل رمز PIN الخاص بك لتمكين المصادقة البيومترية", "enabled": "مفعل", "end_date": "تاريخ الإنتهاء", @@ -989,6 +1003,8 @@ "explorer": "المستكشف", "export": "تصدير", "export_as_json": "تصدير كـ JSON", + "export_database": "تصدير قاعدة البيانات", + "export_database_description": "تصدير قاعدة البيانات من نوع SQLite", "extension": "الإمتداد", "external": "خارجي", "external_libraries": "المكتبات الخارجية", @@ -1147,7 +1163,6 @@ "light": "المضيئ", "like_deleted": "تم حذف الإعجاب", "link_motion_video": "رابط فيديو الحركة", - "link_options": "خيارات الرابط", "link_to_oauth": "الربط مع OAuth", "linked_oauth_account": "حساب مرتبط بـ OAuth", "list": "قائمة", @@ -1210,7 +1225,6 @@ "manage_your_devices": "إدارة الأجهزة التي تم تسجيل الدخول إليها", "manage_your_oauth_connection": "إدارة اتصال OAuth الخاص بك", "map": "الخريطة", - "map_assets_in_bound": "{count} صوره", "map_assets_in_bounds": "{count} صور", "map_cannot_get_user_location": "لا يمكن الحصول على موقع المستخدم", "map_location_dialog_yes": "نعم", diff --git a/i18n/be.json b/i18n/be.json index d3f59b32a5..669ec09849 100644 --- a/i18n/be.json +++ b/i18n/be.json @@ -6,7 +6,7 @@ "action": "Дзеянне", "action_common_update": "Абнавіць", "actions": "Дзеянні", - "active": "Актыўны", + "active": "Актыўных", "activity": "Актыўнасць", "activity_changed": "Актыўнасць {enabled, select, true {уключана} other {адключана}}", "add": "Дадаць", @@ -74,8 +74,11 @@ "image_fullsize_enabled_description": "Ствараць выяву ў поўным памеры для фарматаў, што не прыдатныя для вэб. Калі ўключана опцыя \"Аддаваць перавагу ўбудаванай праяве\", прагляды выкарыстоўваюцца непасрэдна без канвертацыі. Не ўплывае на вэб-прыдатныя фарматы, такія як JPEG.", "image_fullsize_quality_description": "Якасць выявы ў поўным памеры ад 1 да 100. Больш высокае значэнне лепшае, але прыводзіць да павелічэння памеру файла.", "image_fullsize_title": "Налады выявы ў поўным памеры", + "image_prefer_embedded_preview": "Аддаваць перавагу ўбудаванай праяве", "image_prefer_embedded_preview_setting_description": "Выкарыстоўваць убудаваныя праявы ў RAW-фотаздымках ў якасці ўваходных дадзеных для апрацоўкі малюнкаў, калі магчыма. Гэта дазваляе атрымаць больш дакладныя колеры для некаторых відарысаў, але ж якасць праяў залежыць ад камеры, і на відарысе можа быць больш артэфактаў сціску.", "image_prefer_wide_gamut": "Аддаць перавагу шырокай гаме", + "image_preview_description": "Відарыс сярэдняга памеру з выдаленымі метададзенымі, выкарыстоўваецца пры праглядзе асобнага рэсурсу і для машыннага навучання", + "image_preview_quality_description": "Якасць праявы ад 1 да 100. Чым вышэй, тым лепш, але пры гэтым ствараюцца файлы большага памеру і можа знізіцца хуткасць водгуку прыкладання. Ўстаноўка нізкага значэння можа паўплываць на якасць машыннага навучання.", "image_preview_title": "Налады папярэдняга прагляду", "image_quality": "Якасць", "image_resolution": "Раздзяляльнасць", @@ -93,33 +96,119 @@ "metadata_settings": "Налады метаданых", "oauth_button_text": "Тэкст кнопкі", "oauth_settings": "OAuth", + "refreshing_all_libraries": "Абнаўленне ўсіх бібліятэк", + "registration": "Рэгістрацыя адміністратара", + "registration_description": "Вы з'яўляецеся першым карыстальнікам сістэмы, таму вы будзеце прызначаны адміністратарам. Вы будзеце адказваць за адміністрацыйныя задачы, а таксама ствараць новых карыстальнікаў.", + "require_password_change_on_login": "Патрабаваць змяніць пароль пры першым уваходзе ў сістэму", + "reset_settings_to_default": "Скінуць налады да прадвызначаных", + "reset_settings_to_recent_saved": "Скінуць налады да нядаўна захаваных", + "scanning_library": "Сканіраванне бібліятэкі", + "server_external_domain_settings": "Знешні дамен", + "server_settings": "Налады сервера", + "server_settings_description": "Кіраванне наладамі сервера", + "server_welcome_message": "Прывітальнае паведамленне", + "server_welcome_message_description": "Паведамленне, якое адлюстроўваецца на старонцы ўваходу.", "system_settings": "Сістэмныя налады", + "tag_cleanup_job": "Ачыстка тэгаў", + "template_email_preview": "Перадпрагляд", "theme_settings": "Налады тэмы", + "transcoding_acceleration_nvenc": "NVENC (патрабуецца відэакарта NVIDIA)", "transcoding_acceleration_vaapi": "VAAPI", + "transcoding_accepted_containers": "Прынятыя кантэйнеры", + "transcoding_accepted_video_codecs": "Прынятыя відэакодэкі", + "transcoding_advanced_options_description": "Параметры, якія большасці карыстальнікаў не трэба змяняць", "transcoding_audio_codec": "Аудыякодэк", + "transcoding_encoding_options": "Параметры кадзіравання", "transcoding_video_codec": "Відэакодэк", + "trash_enabled_description": "Уключыць функцыі сметніцы", + "trash_number_of_days": "Колькасць дзён", "trash_settings": "Налады сметніцы", "trash_settings_description": "Кіраванне наладамі сметніцы", + "user_cleanup_job": "Ачыстка карыстальніка", + "user_management": "Кіраванне карыстальнікамі", + "user_password_has_been_reset": "Пароль карыстальніка быў скінуты:", + "user_password_reset_description": "Задайце карыстальніку часовы пароль і паведаміце яму, што пры наступным уваходзе ў сістэму яму трэба будзе змяніць пароль.", + "user_restore_description": "Уліковы запіс карыстальніка {user} будзе адноўлены.", + "user_settings": "Налады карыстальніка", + "user_settings_description": "Кіраванне наладамі карыстальніка", + "user_successfully_removed": "Карыстальнік {email} быў паспяхова выдалены.", + "version_check_enabled_description": "Уключыць праверку версіі", + "version_check_implications": "Функцыі праверкі версіі перыядычна звяртаецца да github.com", "version_check_settings": "Праверка версіі", "version_check_settings_description": "Уключыць/адключыць апавяшчэнні аб новай версіі" }, + "admin_email": "Электронная пошта адміністратара", + "admin_password": "Пароль адміністратара", + "administration": "Кіраванне серверам", + "advanced": "Пашыраныя", + "advanced_settings_log_level_title": "Узровень вядзення журнала: {level}", + "advanced_settings_proxy_headers_title": "Загалоўкі проксі", + "advanced_settings_tile_subtitle": "Пашыраныя налады карыстальніка", + "advanced_settings_troubleshooting_subtitle": "Уключыць дадатковыя функцыі для выпраўлення непаладак", "advanced_settings_troubleshooting_title": "Выпраўленне непаладак", + "age_months": "Узрост {months, plural, one {# месяц} few {# месяцы} many {# месяцаў} other {# месяцаў}}", + "age_year_months": "Узрост 1 год, {months, plural, one {# месяц} few {# месяцы} many {# месяцаў} other {# месяцаў}}", + "age_years": "{years, plural, other {Узрост #}}", "album_added": "Альбом дададзены", + "album_cover_updated": "Вокладка альбома абноўлена", + "album_delete_confirmation": "Вы ўпэўнены, што хочаце выдаліць альбом {album}?", + "album_delete_confirmation_description": "Калі гэты альбом абагулены, іншыя карыстальнікі больш не змогуць атрымаць да яго доступ.", + "album_deleted": "Альбом выдалены", + "album_info_card_backup_album_excluded": "ВЫКЛЮЧАНЫ", + "album_info_card_backup_album_included": "УКЛЮЧАНЫ", + "album_info_updated": "Інфармацыя пра альбом абноўлена", + "album_leave": "Пакінуць альбом?", + "album_leave_confirmation": "Вы ўпэўнены, што хочаце пакінуць {album}?", "album_name": "Назва альбома", + "album_options": "Параметры альбома", "album_remove_user": "Выдаліць карыстальніка?", + "album_remove_user_confirmation": "Вы ўпэўнены, што хочаце выдаліць {user}?", + "album_search_not_found": "Па вашым запыце не знойдзена альбомаў", + "album_share_no_users": "Здаецца, вы падзяліліся гэтым альбомам з усімі карыстальнікамі, або ў вас няма ніводнага карыстальніка, з якім можна падзяліцца.", "album_updated": "Альбом абноўлены", + "album_user_left": "Вы пакінулі {album}", + "album_user_removed": "Карыстальнік {user} выдалены", + "album_viewer_appbar_delete_confirm": "Вы ўпэўнены, што хочаце выдаліць гэты альбом са свайго ўліковага запісу?", + "album_viewer_appbar_share_err_delete": "Не ўдалося выдаліць альбом", + "album_viewer_appbar_share_err_leave": "Не ўдалося пакінуць альбом", + "album_viewer_appbar_share_err_title": "Не ўдалося змяніць назву альбома", + "album_viewer_appbar_share_leave": "Пакінуць альбом", + "album_viewer_appbar_share_to": "Абагуліць з", + "album_viewer_page_share_add_users": "Дадаць карыстальнікаў", + "album_with_link_access": "Дазволіць усім, хто мае спасылку, бачыць фота і людзей у гэтым альбоме.", "albums": "Альбомы", + "albums_count": "{count, plural, one {1 альбом} few {{count, number} альбомы} many {{count, number} альбомаў} other {{count, number} альбомаў}}", + "albums_default_sort_order": "Прадвызначаны парадак сартавання альбомаў", + "albums_on_device_count": "Альбомы на прыладзе ({count})", "all": "Усе", "all_albums": "Усе альбомы", "all_people": "Усе людзі", "all_videos": "Усе відэа", + "allow_dark_mode": "Дазволіць цёмны рэжым", + "allow_edits": "Дазволіць рэдагаванне", + "alt_text_qr_code": "Відарыс QR-кода", + "anti_clockwise": "Супраць гадзіннікавай стрэлкі", + "api_key": "Ключ API", + "api_key_empty": "Назва ключа API не павінна быць пустой", + "api_keys": "Ключы API", + "app_bar_signout_dialog_content": "Вы ўпэўнены, што хочаце выйсці?", "app_bar_signout_dialog_ok": "Так", "app_bar_signout_dialog_title": "Выйсці", "app_settings": "Налады праграмы", "archive": "Архіў", + "archive_page_title": "Архіў ({count})", "archive_size": "Памер архіва", + "are_these_the_same_person": "Ці гэта адзін і той жа чалавек?", + "are_you_sure_to_do_this": "Вы ўпэўнены, што хочаце гэта зрабіць?", + "asset_added_to_album": "Дададзена ў альбом", + "asset_adding_to_album": "Дадаванне ў альбом…", + "asset_skipped": "Прапушчана", + "asset_skipped_in_trash": "У сметніцы", + "asset_uploaded": "Запампавана", "asset_uploading": "Запампоўванне…", + "authorized_devices": "Аўтарызаваныя прылады", "back": "Назад", + "backup_album_selection_page_albums_device": "Альбомы на прыладзе ({count})", "backup_all": "Усе", "backup_controller_page_background_wifi": "Толькі праз Wi-Fi", "buy": "Купіць Immich", diff --git a/i18n/bg.json b/i18n/bg.json index 327fd7c7df..cb5cc437b6 100644 --- a/i18n/bg.json +++ b/i18n/bg.json @@ -508,6 +508,7 @@ "back_close_deselect": "Назад, затваряне или премахване на избора", "background_location_permission": "Разрешение за достъп до местоположението във фонов режим", "background_location_permission_content": "За да може да чете имената на Wi-Fi мрежите и да ги превключва при работа във фонов режим, Immich трябва *винаги* да има достъп до точното местоположение", + "backup": "Архивиране", "backup_album_selection_page_albums_device": "Албуми на устройството ({count})", "backup_album_selection_page_albums_tap": "Натисни за да включиш, двойно за да изключиш", "backup_album_selection_page_assets_scatter": "Обектите могат да бъдат разпръснати в няколко албума. По този начин албумите могат да бъдат включени или изключени по време на процеса на архивиране.", @@ -1154,7 +1155,6 @@ "light": "Светло", "like_deleted": "Като изтрит", "link_motion_video": "Линк към видео", - "link_options": "Опции на линк за споделяне", "link_to_oauth": "Линк към OAuth", "linked_oauth_account": "Свързан OAuth акаунт", "list": "Лист", @@ -1217,7 +1217,6 @@ "manage_your_devices": "Управление на влезлите в системата устройства", "manage_your_oauth_connection": "Управление на OAuth връзката", "map": "Карта", - "map_assets_in_bound": "{count} снимки", "map_assets_in_bounds": "{count} снимки", "map_cannot_get_user_location": "Не можах да получа местоположението", "map_location_dialog_yes": "Да", diff --git a/i18n/bn.json b/i18n/bn.json index d71e4e25ae..9dce989af4 100644 --- a/i18n/bn.json +++ b/i18n/bn.json @@ -63,7 +63,7 @@ "exclusion_pattern_description": "এক্সক্লুশন প্যাটার্ন ব্যবহার করে আপনি আপনার লাইব্রেরি স্ক্যান করার সময় ফাইল এবং ফোল্ডারগুলিকে উপেক্ষা করতে পারবেন। যদি আপনার এমন ফোল্ডার থাকে যেখানে এমন ফাইল থাকে যা আপনি আমদানি করতে চান না, যেমন RAW ফাইল।", "external_library_management": "বহিরাগত গ্রন্থাগার ব্যবস্থাপনা", "face_detection": "মুখ সনাক্তকরণ", - "face_detection_description": "মেশিন লার্নিং ব্যবহার করে অ্যাসেটে থাকা মুখগুলি সনাক্ত করুন। ভিডিওগুলির জন্য, শুধুমাত্র থাম্বনেইল বিবেচনা করা হয়। \"রিফ্রেশ\" (পুনরায়) সমস্ত অ্যাসেট প্রক্রিয়া করে। \"রিসেট\" অতিরিক্তভাবে সমস্ত বর্তমান মুখের ডেটা সাফ করে। \"অনুপস্থিত\" অ্যাসেটগুলিকে সারিবদ্ধ করে যা এখনও প্রক্রিয়া করা হয়নি। সনাক্ত করা মুখগুলিকে ফেসিয়াল রিকগনিশনের জন্য সারিবদ্ধ করা হবে, ফেসিয়াল ডিটেকশন সম্পূর্ণ হওয়ার পরে, বিদ্যমান বা নতুন ব্যক্তিদের মধ্যে গোষ্ঠীবদ্ধ করে।", + "face_detection_description": "মেশিন লার্নিং ব্যবহার করে অ্যাসেটে থাকা মুখ/চেহারা গুলি সনাক্ত করুন। ভিডিও গুলির জন্য, শুধুমাত্র থাম্বনেইল বিবেচনা করা হয়। \"রিফ্রেশ\" (পুনরায়) সমস্ত অ্যাসেট প্রক্রিয়া করে। \"রিসেট\" করার মাধ্যমে অতিরিক্তভাবে সমস্ত বর্তমান মুখের ডেটা সাফ করে। \"অনুপস্থিত\" অ্যাসেটগুলিকে সারিবদ্ধ করে যা এখনও প্রক্রিয়া করা হয়নি। সনাক্ত করা মুখগুলিকে ফেসিয়াল রিকগনিশনের জন্য সারিবদ্ধ করা হবে, ফেসিয়াল ডিটেকশন সম্পূর্ণ হওয়ার পরে, বিদ্যমান বা নতুন ব্যক্তিদের মধ্যে গোষ্ঠীবদ্ধ করে।", "facial_recognition_job_description": "শনাক্ত করা মুখগুলিকে মানুষের মধ্যে গোষ্ঠীভুক্ত করুন। মুখ সনাক্তকরণ সম্পূর্ণ হওয়ার পরে এই ধাপটি চলে। \"রিসেট\" (পুনরায়) সমস্ত মুখকে ক্লাস্টার করে। \"অনুপস্থিত\" মুখগুলিকে সারিতে রাখে যেখানে কোনও ব্যক্তিকে বরাদ্দ করা হয়নি।", "failed_job_command": "কমান্ড {command} কাজের জন্য ব্যর্থ হয়েছে: {job}", "force_delete_user_warning": "সতর্কতা: এটি ব্যবহারকারী এবং সমস্ত সম্পদ অবিলম্বে সরিয়ে ফেলবে। এটি পূর্বাবস্থায় ফেরানো যাবে না এবং ফাইলগুলি পুনরুদ্ধার করা যাবে না।", diff --git a/i18n/ca.json b/i18n/ca.json index 39c249c3a3..d24481f077 100644 --- a/i18n/ca.json +++ b/i18n/ca.json @@ -496,6 +496,7 @@ "back_close_deselect": "Tornar, tancar o anul·lar la selecció", "background_location_permission": "Permís d'ubicació en segon pla", "background_location_permission_content": "Per canviar de xarxa quan s'executa en segon pla, Immich ha de *sempre* tenir accés a la ubicació precisa perquè l'aplicació pugui llegir el nom de la xarxa Wi-Fi", + "backup": "Còpia", "backup_album_selection_page_albums_device": "Àlbums al dispositiu ({count})", "backup_album_selection_page_albums_tap": "Un toc per incloure, doble toc per excloure", "backup_album_selection_page_assets_scatter": "Els elements poden dispersar-se en diversos àlbums. Per tant, els àlbums es poden incloure o excloure durant el procés de còpia de seguretat.", @@ -1139,7 +1140,6 @@ "light": "Llum", "like_deleted": "M'agrada suprimit", "link_motion_video": "Enllaçar vídeo en moviment", - "link_options": "Opcions d'enllaç", "link_to_oauth": "Enllaç a OAuth", "linked_oauth_account": "Compte OAuth enllaçat", "list": "Llista", @@ -1202,7 +1202,6 @@ "manage_your_devices": "Gestioneu els vostres dispositius connectats", "manage_your_oauth_connection": "Gestioneu la vostra connexió OAuth", "map": "Mapa", - "map_assets_in_bound": "{count} foto", "map_assets_in_bounds": "{count} fotos", "map_cannot_get_user_location": "No es pot obtenir la ubicació de l'usuari", "map_location_dialog_yes": "Sí", diff --git a/i18n/cs.json b/i18n/cs.json index 80ea9674c0..c5e5ac9553 100644 --- a/i18n/cs.json +++ b/i18n/cs.json @@ -14,6 +14,7 @@ "add_a_location": "Přidat polohu", "add_a_name": "Přidat jméno", "add_a_title": "Přidat název", + "add_birthday": "Přidat datum narození", "add_endpoint": "Přidat koncový bod", "add_exclusion_pattern": "Přidat vzor vyloučení", "add_import_path": "Přidat cestu importu", @@ -44,6 +45,13 @@ "backup_database": "Vytvořit výpis databáze", "backup_database_enable_description": "Povolit výpisy z databáze", "backup_keep_last_amount": "Počet předchozích výpisů, které se mají ponechat", + "backup_onboarding_1_description": "kopie v cloudu nebo na jiném fyzickém místě.", + "backup_onboarding_2_description": "místní kopie na různých zařízeních. To zahrnuje hlavní soubory a jejich místní zálohu.", + "backup_onboarding_3_description": "kompletní kopie vašich dat, včetně původních souborů. To zahrnuje 1 kopii na jiném místě a 2 místní kopie.", + "backup_onboarding_description": "K ochraně vašich dat doporučujeme strategii zálohování 3-2-1. Pro komplexní zálohování byste měli uchovávat kopie nahraných fotografií/videí i databáze Immich.", + "backup_onboarding_footer": "Další informace o zálohování Immiche naleznete v dokumentaci.", + "backup_onboarding_parts_title": "Záloha 3-2-1 zahrnuje:", + "backup_onboarding_title": "Zálohy", "backup_settings": "Zálohování databáze", "backup_settings_description": "Správa nastavení výpisu databáze.", "cleared_jobs": "Hotové úlohy pro: {job}", @@ -374,7 +382,7 @@ "administration": "Administrace", "advanced": "Pokročilé", "advanced_settings_beta_timeline_subtitle": "Vyzkoušejte nové prostředí aplikace", - "advanced_settings_beta_timeline_title": "Časová osa beta verze", + "advanced_settings_beta_timeline_title": "Beta verze časové osy", "advanced_settings_enable_alternate_media_filter_subtitle": "Tuto možnost použijte k filtrování médií během synchronizace na základě alternativních kritérií. Tuto možnost vyzkoušejte pouze v případě, že máte problémy s detekcí všech alb v aplikaci.", "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTÁLNÍ] Použít alternativní filtr pro synchronizaci alb zařízení", "advanced_settings_log_level_title": "Úroveň protokolování: {level}", @@ -397,6 +405,7 @@ "album_cover_updated": "Obal alba aktualizován", "album_delete_confirmation": "Opravdu chcete album {album} odstranit?", "album_delete_confirmation_description": "Pokud je toto album sdíleno, ostatní uživatelé k němu již nebudou mít přístup.", + "album_deleted": "Album smazáno", "album_info_card_backup_album_excluded": "VYLOUČENO", "album_info_card_backup_album_included": "ZAHRNUTO", "album_info_updated": "Informace o albu aktualizovány", @@ -510,6 +519,7 @@ "back_close_deselect": "Zpět, zavřít nebo zrušit výběr", "background_location_permission": "Povolení polohy na pozadí", "background_location_permission_content": "Aby bylo možné přepínat sítě při běhu na pozadí, musí mít Immich *vždy* přístup k přesné poloze, aby mohl zjistit název Wi-Fi sítě", + "backup": "Záloha", "backup_album_selection_page_albums_device": "Alba v zařízení ({count})", "backup_album_selection_page_albums_tap": "Klepnutím na položku ji zahrnete, opětovným klepnutím ji vyloučíte", "backup_album_selection_page_assets_scatter": "Položky mohou být roztroušeny ve více albech. To umožňuje zahrnout nebo vyloučit alba během procesu zálohování.", @@ -573,7 +583,7 @@ "backup_options_page_title": "Nastavení záloh", "backup_setting_subtitle": "Správa nastavení zálohování na pozadí a na popředí", "backward": "Pozpátku", - "beta_sync": "Stav synchronizace beta verze", + "beta_sync": "Stav synchronizace (beta)", "beta_sync_subtitle": "Správa nového systému synchronizace", "biometric_auth_enabled": "Biometrické ověřování je povoleno", "biometric_locked_out": "Jste vyloučeni z biometrického ověřování", @@ -723,6 +733,7 @@ "current_server_address": "Aktuální adresa serveru", "custom_locale": "Vlastní lokalizace", "custom_locale_description": "Formátovat datumy a čísla podle jazyka a oblasti", + "custom_url": "Vlastní URL", "daily_title_text_date": "EEEE, d. MMMM", "daily_title_text_date_year": "EEEE, d. MMMM y", "dark": "Tmavý", @@ -742,7 +753,8 @@ "default_locale": "Výchozí jazyk", "default_locale_description": "Formátovat datumy a čísla podle místního prostředí prohlížeče", "delete": "Smazat", - "delete_action_prompt": "{count} trvale smazaných", + "delete_action_confirmation_message": "Opravdu chcete odstranit tuto položku? Tato akce přesune položku do serverového koše a zeptá se vás, zda ji chcete odstranit lokálně", + "delete_action_prompt": "{count} smazáno", "delete_album": "Smazat album", "delete_api_key_prompt": "Opravdu chcete tento API klíč odstranit?", "delete_dialog_alert": "Tyto položky budou trvale smazány z aplikace Immich i z vašeho zařízení", @@ -759,7 +771,9 @@ "delete_local_action_prompt": "{count} smazáno lokálně", "delete_local_dialog_ok_backed_up_only": "Smazat pouze zálohované", "delete_local_dialog_ok_force": "Přesto smazat", - "delete_others": "Odstranit ostatní", + "delete_others": "Smazat ostatní", + "delete_permanently": "Trvale smazat", + "delete_permanently_action_prompt": "{count} trvale smazáno", "delete_shared_link": "Smazat sdílený odkaz", "delete_shared_link_dialog_title": "Odstranit sdílený odkaz", "delete_tag": "Smazat značku", @@ -815,6 +829,7 @@ "edit": "Upravit", "edit_album": "Upravit album", "edit_avatar": "Upravit avatar", + "edit_birthday": "Upravit datum narození", "edit_date": "Upravit datum", "edit_date_and_time": "Upravit datum a čas", "edit_description": "Upravit popis", @@ -982,6 +997,7 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Přidat popis...", + "exif_bottom_sheet_description_error": "Chyba při aktualizaci popisu", "exif_bottom_sheet_details": "PODROBNOSTI", "exif_bottom_sheet_location": "POLOHA", "exif_bottom_sheet_people": "LIDÉ", @@ -1002,6 +1018,8 @@ "explorer": "Průzkumník", "export": "Export", "export_as_json": "Exportovat jako JSON", + "export_database": "Exportovat databázi", + "export_database_description": "Exportovat databázi SQLite", "extension": "Přípona", "external": "Externí", "external_libraries": "Externí knihovny", @@ -1146,6 +1164,7 @@ "language_no_results_title": "Nebyly nalezeny žádné jazyky", "language_search_hint": "Vyhledat jazyk...", "language_setting_description": "Vyberte upřednostňovaný jazyk", + "large_files": "Velké soubory", "last_seen": "Naposledy viděno", "latest_version": "Nejnovější verze", "latitude": "Zeměpisná šířka", @@ -1165,7 +1184,6 @@ "light": "Světlý", "like_deleted": "Lajk smazán", "link_motion_video": "Připojit pohyblivé video", - "link_options": "Možnosti odkazu", "link_to_oauth": "Propojit s OAuth", "linked_oauth_account": "Propojený OAuth účet", "list": "Seznam", @@ -1230,8 +1248,7 @@ "manage_your_devices": "Správa přihlášených zařízení", "manage_your_oauth_connection": "Správa OAuth propojení", "map": "Mapa", - "map_assets_in_bound": "{count} fotka", - "map_assets_in_bounds": "{count} fotek", + "map_assets_in_bounds": "{count, plural, one {# fotka} few{# fotky} other {# fotek}}", "map_cannot_get_user_location": "Nelze zjistit polohu uživatele", "map_location_dialog_yes": "Ano", "map_location_picker_page_use_location": "Použít tuto polohu", @@ -1582,6 +1599,7 @@ "resume": "Pokračovat", "retry_upload": "Opakování nahrávání", "review_duplicates": "Kontrola duplicit", + "review_large_files": "Kontrola velkých souborů", "role": "Role", "role_editor": "Editor", "role_viewer": "Divák", @@ -1739,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "Zkopírováno do schránky", "shared_link_clipboard_text": "Odkaz: {link}\nHeslo: {password}", "shared_link_create_error": "Chyba při vytváření sdíleného odkazu", + "shared_link_custom_url_description": "Přístup k tomuto sdílenému odkazu pomocí vlastního URL", "shared_link_edit_description_hint": "Zadejte popis sdílení", "shared_link_edit_expire_after_option_day": "1 den", "shared_link_edit_expire_after_option_days": "{count} dní", @@ -1764,6 +1783,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Spravovat sdílené odkazy", "shared_link_options": "Možnosti sdíleného odkazu", + "shared_link_password_description": "Vyžadovat heslo pro přístup k tomuto sdílenému odkazu", "shared_links": "Sdílené odkazy", "shared_links_description": "Sdílet fotky a videa pomocí odkazu", "shared_photos_and_videos_count": "{assetCount, plural, one {# sdílená fotografie a video.} few {# sdílené fotografie a videa.} other {# sdílených fotografií a videí.}}", @@ -1941,11 +1961,13 @@ "updated_at": "Aktualizováno", "updated_password": "Heslo aktualizováno", "upload": "Nahrát", + "upload_action_prompt": "{count} ve frontě pro nahrání", "upload_concurrency": "Souběžnost nahrávání", "upload_details": "Detaily nahrávání", "upload_dialog_info": "Chcete zálohovat vybrané položky na server?", "upload_dialog_title": "Nahrát položku", "upload_errors": "Nahrávání bylo dokončeno s {count, plural, one {# chybou} other {# chybami}}, obnovte stránku pro zobrazení nových položek.", + "upload_finished": "Nahrávání dokončeno", "upload_progress": "Zbývá {remaining, number} - Zpracováno {processed, number}/{total, number}", "upload_skipped_duplicates": "{count, plural, one {Přeskočena # duplicitní položka} few {Přeskočeny # duplicitní položky} other {Přeskočeno # duplicitních položek}}", "upload_status_duplicates": "Duplicity", @@ -1954,6 +1976,7 @@ "upload_success": "Nahrání proběhlo úspěšně, obnovením stránky se zobrazí nově nahrané položky.", "upload_to_immich": "Nahrát do Immich ({count})", "uploading": "Nahrávání", + "uploading_media": "Nahrávání médií", "url": "URL", "usage": "Využití", "use_biometric": "Použít biometrické údaje", diff --git a/i18n/da.json b/i18n/da.json index e9f9534927..4e9846791b 100644 --- a/i18n/da.json +++ b/i18n/da.json @@ -167,6 +167,19 @@ "migration_job": "Migrering", "migration_job_description": "Migrér miniaturebilleder for aktiver og ansigter til den seneste mappestruktur", "nightly_tasks_cluster_faces_setting_description": "Kør ansigtsgenkendelse på nye ansigter", + "nightly_tasks_cluster_new_faces_setting": "Gruppér nye ansigter", + "nightly_tasks_database_cleanup_setting": "Databaseoprydning opgaver", + "nightly_tasks_database_cleanup_setting_description": "Ryd op i gamle, udløbne data fra databasen", + "nightly_tasks_generate_memories_setting": "Generer minder", + "nightly_tasks_generate_memories_setting_description": "Skab nye minder ud fra dine medier", + "nightly_tasks_missing_thumbnails_setting": "Generer manglende miniaturebilleder", + "nightly_tasks_missing_thumbnails_setting_description": "Sæt medier uden thumbnails i kø til generering af thumbnails", + "nightly_tasks_settings": "Indstillinger for opgaver om natten", + "nightly_tasks_settings_description": "Administrér opgaver om natten", + "nightly_tasks_start_time_setting": "Starttidspunkt", + "nightly_tasks_start_time_setting_description": "Tidspunktet hvor serveren begynder at køre opgaver om natten", + "nightly_tasks_sync_quota_usage_setting": "Synkronisér kvoteforbrug", + "nightly_tasks_sync_quota_usage_setting_description": "Opdater brugerens lagerkvote baseret på aktuelt forbrug", "no_paths_added": "Ingen stier tilføjet", "no_pattern_added": "Intet mønster tilføjet", "note_apply_storage_label_previous_assets": "Bemærk: For at anvende Lagringsmærkatet på tidligere uploadede mediefiler, kør", @@ -205,7 +218,7 @@ "oauth_storage_quota_claim": "Lagringskvotefordring", "oauth_storage_quota_claim_description": "Sæt automatisk brugeres lagringskvote til denne nye fordrings værdi.", "oauth_storage_quota_default": "Standard lagringskvote (GiB)", - "oauth_storage_quota_default_description": "Kvote i GiB som bruges, når der ikke bliver oplyst en fordring (Indtast 0 for uendelig kvote).", + "oauth_storage_quota_default_description": "Kvote i GiB som bruges, når der ikke bliver oplyst en fordring.", "oauth_timeout": "Forespørgslen udløb", "oauth_timeout_description": "Udløbstid for forespørgsel i milisekunder", "password_enable_description": "Log ind med email og adgangskode", @@ -489,6 +502,7 @@ "back_close_deselect": "Tilbage, luk eller fravælg", "background_location_permission": "Tilladelse til baggrundsplacering", "background_location_permission_content": "For at skifte netværk, når appen kører i baggrunden, skal Immich *altid* have præcis placeringsadgang, så appen kan læse WiFi-netværkets navn", + "backup": "Sikkerhedskopier", "backup_album_selection_page_albums_device": "Albummer på enheden ({count})", "backup_album_selection_page_albums_tap": "Tryk en gang for at inkludere, tryk to gange for at ekskludere", "backup_album_selection_page_assets_scatter": "Elementer kan være spredt på tværs af flere albummer. Albummer kan således inkluderes eller udelukkes under sikkerhedskopieringsprocessen.", @@ -1128,7 +1142,6 @@ "light": "Lys", "like_deleted": "Ligesom slettet", "link_motion_video": "Link bevægelsesvideo", - "link_options": "Link-indstillinger", "link_to_oauth": "Link til OAuth", "linked_oauth_account": "Tilsluttet OAuth-konto", "list": "Liste", @@ -1190,7 +1203,6 @@ "manage_your_devices": "Administrér dine enheder der er logget ind", "manage_your_oauth_connection": "Administrér din OAuth-tilslutning", "map": "Kort", - "map_assets_in_bound": "{count} billede", "map_assets_in_bounds": "{count} billeder", "map_cannot_get_user_location": "Kan ikke finde brugerens placering", "map_location_dialog_yes": "Ja", diff --git a/i18n/de.json b/i18n/de.json index 45245b182b..651f68792e 100644 --- a/i18n/de.json +++ b/i18n/de.json @@ -14,6 +14,7 @@ "add_a_location": "Standort hinzufügen", "add_a_name": "Name hinzufügen", "add_a_title": "Titel hinzufügen", + "add_birthday": "Geburtsdatum hinzufügen", "add_endpoint": "Endpunkt hinzufügen", "add_exclusion_pattern": "Ausschlussmuster hinzufügen", "add_import_path": "Importpfad hinzufügen", @@ -41,9 +42,16 @@ "authentication_settings_disable_all": "Bist du sicher, dass du alle Anmeldemethoden deaktivieren willst? Die Anmeldung wird vollständig deaktiviert.", "authentication_settings_reenable": "Nutze einen Server-Befehl zur Reaktivierung.", "background_task_job": "Hintergrundaufgaben", - "backup_database": "Datenbanksicherung regelmäßig erstellen", + "backup_database": "Datenbanksicherung erstellen", "backup_database_enable_description": "Datenbank regelmäßig sichern", "backup_keep_last_amount": "Anzahl der aufzubewahrenden früheren Backups", + "backup_onboarding_1_description": "Offsite-Kopie in der Cloud oder an einem anderen physischen Ort.", + "backup_onboarding_2_description": "Lokale Kopien auf verschiedenen Geräten. Dazu gehören die Hauptdateien und eine lokale Sicherung dieser Dateien.", + "backup_onboarding_3_description": "3 komplette Kopien deiner Daten, inkl. der Originaldateien. Dies umfasst 1 Kopie an einem anderen Ort und 2 lokale Kopie.", + "backup_onboarding_description": "Eine 3-2-1 Backup-Strategie wird empfohlen, um deine Daten zu schützen. Du solltest sowohl Kopien deiner hochgeladenen Fotos/Videos als auch der Immich-Datenbank aufbewahren, um eine umfassende Backup-Lösung zu haben.", + "backup_onboarding_footer": "Weitere Informationen zum Sichern von Immich findest du in der Dokumentation.", + "backup_onboarding_parts_title": "Eine 3-2-1-Sicherung umfasst:", + "backup_onboarding_title": "Backups", "backup_settings": "Einstellungen für Datenbanksicherung", "backup_settings_description": "Einstellungen zur regelmäßigen Sicherung der Datenbank. Hinweis: Diese Jobs werden nicht überwacht und du wirst nicht über Fehler informiert.", "cleared_jobs": "Folgende Aufgaben zurückgesetzt: {job}", @@ -120,7 +128,7 @@ "machine_learning_duplicate_detection_setting_description": "Verwendung von CLIP-Embeddings zum Erkennen möglicher Duplikate", "machine_learning_enabled": "Maschinelles Lernen aktivieren", "machine_learning_enabled_description": "Wenn diese Option deaktiviert ist, werden alle ML-Funktionen unabhängig von den unten aufgeführten Einstellungen deaktiviert.", - "machine_learning_facial_recognition": "Gesichtsidentifikation", + "machine_learning_facial_recognition": "Gesichtsidentifizierung", "machine_learning_facial_recognition_description": "Erkenne, identifiziere und gruppiere Gesichter in Bildern", "machine_learning_facial_recognition_model": "Gesichtserkennungs-Modell", "machine_learning_facial_recognition_model_description": "Die Modelle sind in absteigender Reihenfolge ihrer Größe aufgeführt. Größere Modelle sind langsamer und verbrauchen mehr Speicher, liefern aber bessere Ergebnisse. Bitte beachte dabei, dass du die Gesichtserkennungsaufgabe für alle Bilder neu starten musst, wenn du ein Modell änderst.", @@ -385,7 +393,7 @@ "advanced_settings_self_signed_ssl_subtitle": "Verifizierung von SSL-Zertifikaten vom Server überspringen. Notwendig bei selbstsignierten Zertifikaten.", "advanced_settings_self_signed_ssl_title": "Selbstsignierte SSL-Zertifikate erlauben", "advanced_settings_sync_remote_deletions_subtitle": "Automatisches Löschen oder Wiederherstellen einer Datei auf diesem Gerät, wenn diese Aktion im Web durchgeführt wird", - "advanced_settings_sync_remote_deletions_title": "Synchrone Remote-Löschungen [Experimentell]", + "advanced_settings_sync_remote_deletions_title": "Mit Server-Löschungen synchronisieren [Experimentell]", "advanced_settings_tile_subtitle": "Erweiterte Benutzereinstellungen", "advanced_settings_troubleshooting_subtitle": "Erweiterte Funktionen zur Fehlersuche aktivieren", "advanced_settings_troubleshooting_title": "Fehlersuche", @@ -397,6 +405,7 @@ "album_cover_updated": "Album-Cover aktualisiert", "album_delete_confirmation": "Bist du sicher, dass du das Album {album} löschen willst?", "album_delete_confirmation_description": "Falls dieses Album geteilt wurde, können andere Benutzer nicht mehr darauf zugreifen.", + "album_deleted": "Album gelöscht", "album_info_card_backup_album_excluded": "AUSGESCHLOSSEN", "album_info_card_backup_album_included": "EINGESCHLOSSEN", "album_info_updated": "Album-Infos aktualisiert", @@ -510,6 +519,7 @@ "back_close_deselect": "Zurück, Schließen oder Abwählen", "background_location_permission": "Hintergrund Standortfreigabe", "background_location_permission_content": "Um im Hintergrund zwischen den Netzwerken wechseln zu können, muss Immich *immer* Zugriff auf den genauen Standort haben, damit die App den Namen des WLAN-Netzwerks ermitteln kann", + "backup": "Sicherung", "backup_album_selection_page_albums_device": "Alben auf dem Gerät ({count})", "backup_album_selection_page_albums_tap": "Einmalig das Album antippen um es zu sichern, doppelt antippen um es nicht mehr zu sichern", "backup_album_selection_page_assets_scatter": "Elemente (Fotos / Videos) können sich über mehrere Alben verteilen. Daher können diese vor der Sicherung eingeschlossen oder ausgeschlossen werden.", @@ -573,7 +583,7 @@ "backup_options_page_title": "Sicherungsoptionen", "backup_setting_subtitle": "Verwaltung der Upload-Einstellungen im Hintergrund und im Vordergrund", "backward": "Rückwärts", - "beta_sync": "Status des Beta Sync", + "beta_sync": "Status der Beta-Synchronisierung", "beta_sync_subtitle": "Verwalte das neue Synchronisierungssystem", "biometric_auth_enabled": "Biometrische Authentifizierung aktiviert", "biometric_locked_out": "Du bist von der biometrischen Authentifizierung ausgeschlossen", @@ -723,6 +733,7 @@ "current_server_address": "Aktuelle Serveradresse", "custom_locale": "Benutzerdefinierte Sprache", "custom_locale_description": "Datumsangaben und Zahlen je nach Sprache und Land formatieren", + "custom_url": "Benutzerdefinierte URL", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Dunkel", @@ -742,7 +753,8 @@ "default_locale": "Standard-Sprache", "default_locale_description": "Datumsangaben und Zahlen basierend auf dem Gebietsschema des Browsers formatieren", "delete": "Löschen", - "delete_action_prompt": "{count} endgültig gelöscht", + "delete_action_confirmation_message": "Bist du sicher, dass du dieses Objekt löschen willst? Diese Aktion wird das Objekt in den Papierkorb des Servers verschieben und fragen, ob du es lokal löschen willst", + "delete_action_prompt": "{count} gelöscht", "delete_album": "Album löschen", "delete_api_key_prompt": "Bist du sicher, dass du diesen API-Schlüssel löschen willst?", "delete_dialog_alert": "Diese Elemente werden unwiderruflich von Immich und dem Gerät entfernt", @@ -760,6 +772,8 @@ "delete_local_dialog_ok_backed_up_only": "Nur gesicherte Inhalte löschen", "delete_local_dialog_ok_force": "Trotzdem löschen", "delete_others": "Andere löschen", + "delete_permanently": "Endgültig löschen", + "delete_permanently_action_prompt": "{count} endgültig gelöscht", "delete_shared_link": "geteilten Link löschen", "delete_shared_link_dialog_title": "Geteilten Link löschen", "delete_tag": "Tag löschen", @@ -815,6 +829,7 @@ "edit": "Bearbeiten", "edit_album": "Album bearbeiten", "edit_avatar": "Avatar bearbeiten", + "edit_birthday": "Geburtsdatum bearbeiten", "edit_date": "Datum bearbeiten", "edit_date_and_time": "Datum und Uhrzeit bearbeiten", "edit_description": "Beschreibung bearbeiten", @@ -982,6 +997,7 @@ }, "exif": "EXIF", "exif_bottom_sheet_description": "Beschreibung hinzufügen...", + "exif_bottom_sheet_description_error": "Fehler bei der Aktualisierung der Beschreibung", "exif_bottom_sheet_details": "DETAILS", "exif_bottom_sheet_location": "STANDORT", "exif_bottom_sheet_people": "PERSONEN", @@ -1002,6 +1018,8 @@ "explorer": "Datei-Explorer", "export": "Exportieren", "export_as_json": "Als JSON exportieren", + "export_database": "Datenbank exportieren", + "export_database_description": "Exportiert die SQLite Datenbank", "extension": "Erweiterung", "external": "Extern", "external_libraries": "Externe Bibliotheken", @@ -1077,7 +1095,7 @@ "home_page_archive_err_partner": "Inhalte von Partnern können nicht archiviert werden", "home_page_building_timeline": "Zeitachse wird erstellt", "home_page_delete_err_partner": "Inhalte von Partnern können nicht gelöscht werden, überspringe", - "home_page_delete_remote_err_local": "Lokale Inhalte in der Auswahl, überspringen", + "home_page_delete_remote_err_local": "Lokale Elemente in der Auswahl zum Entfernen von Remote-Elementen, Überspringe", "home_page_favorite_err_local": "Kann lokale Elemente noch nicht favorisieren, überspringen", "home_page_favorite_err_partner": "Inhalte von Partnern können nicht favorisiert werden, überspringe", "home_page_first_time_notice": "Wenn dies das erste Mal ist dass Du Immich nutzt, stelle bitte sicher, dass mindestens ein Album zur Sicherung ausgewählt ist, sodass die Zeitachse mit Fotos und Videos gefüllt werden kann", @@ -1146,6 +1164,7 @@ "language_no_results_title": "Keine Sprachen gefunden", "language_search_hint": "Sprachen durchsuchen...", "language_setting_description": "Wähle deine bevorzugte Sprache", + "large_files": "Große Dateien", "last_seen": "Zuletzt gesehen", "latest_version": "Aktuellste Version", "latitude": "Breitengrad", @@ -1165,7 +1184,6 @@ "light": "Hell", "like_deleted": "Like gelöscht", "link_motion_video": "Bewegungsvideo verknüpfen", - "link_options": "Link-Optionen", "link_to_oauth": "Mit OAuth verknüpfen", "linked_oauth_account": "Verknüpftes OAuth-Konto", "list": "Liste", @@ -1230,8 +1248,7 @@ "manage_your_devices": "Deine eingeloggten Geräte verwalten", "manage_your_oauth_connection": "Deine OAuth-Verknüpfung verwalten", "map": "Karte", - "map_assets_in_bound": "{count} Foto", - "map_assets_in_bounds": "{count} Fotos", + "map_assets_in_bounds": "{count, plural, one {# Foto} other {# Fotos}}", "map_cannot_get_user_location": "Standort konnte nicht ermittelt werden", "map_location_dialog_yes": "Ja", "map_location_picker_page_use_location": "Aufnahmeort verwenden", @@ -1529,8 +1546,8 @@ "refreshing_faces": "Gesichter werden aktualisiert", "refreshing_metadata": "Metadaten werden aktualisiert", "regenerating_thumbnails": "Miniaturansichten werden neu erstellt", - "remote": "Entfernt", - "remote_assets": "Entfernte Dateien", + "remote": "Server", + "remote_assets": "Server-Dateien", "remove": "Entfernen", "remove_assets_album_confirmation": "Bist du sicher, dass du {count, plural, one {# Datei} other {# Dateien}} aus dem Album entfernen willst?", "remove_assets_shared_link_confirmation": "Bist du sicher, dass du {count, plural, one {# Datei} other {# Dateien}} von diesem geteilten Link entfernen willst?", @@ -1582,6 +1599,7 @@ "resume": "Fortsetzen", "retry_upload": "Upload wiederholen", "review_duplicates": "Duplikate überprüfen", + "review_large_files": "Große Dateien überprüfen", "role": "Rolle", "role_editor": "Bearbeiter", "role_viewer": "Betrachter", @@ -1739,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "Link kopiert", "shared_link_clipboard_text": "Link: {link}\nPasswort: {password}", "shared_link_create_error": "Fehler beim Erstellen der Linkfreigabe", + "shared_link_custom_url_description": "Greife über eine benutzerdefinierte URL auf diesen Freigabelink zu", "shared_link_edit_description_hint": "Beschreibung eingeben", "shared_link_edit_expire_after_option_day": "1 Tag", "shared_link_edit_expire_after_option_days": "{count} Tagen", @@ -1764,6 +1783,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Geteilte Links verwalten", "shared_link_options": "Optionen für geteilten Link", + "shared_link_password_description": "Für den Zugriff auf diesen freigegebenen Link ist ein Passwort erforderlich", "shared_links": "Geteilte Links", "shared_links_description": "Teile Fotos und Videos mit einem Link", "shared_photos_and_videos_count": "{assetCount, plural, one {# geteiltes Foto oder Video.} other {# geteilte Fotos & Videos.}}", @@ -1850,7 +1870,7 @@ "sync_albums": "Alben synchronisieren", "sync_albums_manual_subtitle": "Synchronisiere alle hochgeladenen Videos und Fotos in die ausgewählten Backup-Alben", "sync_local": "Lokal synchronisieren", - "sync_remote": "Entfernt synchronisieren", + "sync_remote": "mit Server synchronisieren", "sync_upload_album_setting_subtitle": "Erstelle deine ausgewählten Alben in Immich und lade die Fotos und Videos dort hoch", "tag": "Tag", "tag_assets": "Dateien taggen", @@ -1866,7 +1886,7 @@ "theme": "Theme", "theme_selection": "Themenauswahl", "theme_selection_description": "Automatische Einstellung des Themes auf Hell oder Dunkel, je nach Systemeinstellung des Browsers", - "theme_setting_asset_list_storage_indicator_title": "Forschrittsbalken der Sicherung auf dem Vorschaubild", + "theme_setting_asset_list_storage_indicator_title": "Fortschrittsbalken der Sicherung auf dem Vorschaubild", "theme_setting_asset_list_tiles_per_row_title": "Anzahl der Elemente pro Reihe ({count})", "theme_setting_colorful_interface_subtitle": "Primärfarbe auf App-Hintergrund anwenden.", "theme_setting_colorful_interface_title": "Farbige UI-Oberfläche", @@ -1941,11 +1961,13 @@ "updated_at": "Aktualisiert", "updated_password": "Passwort aktualisiert", "upload": "Hochladen", + "upload_action_prompt": "{count} in der Warteschlange für Upload", "upload_concurrency": "Parallelität beim Hochladen", "upload_details": "Upload Details", "upload_dialog_info": "Willst du die ausgewählten Elemente auf dem Server sichern?", "upload_dialog_title": "Element hochladen", "upload_errors": "Hochladen mit {count, plural, one {# Fehler} other {# Fehlern}} abgeschlossen, aktualisiere die Seite, um neu hochgeladene Dateien zu sehen.", + "upload_finished": "Upload fertig", "upload_progress": "{remaining, number} verbleibend - {processed, number}/{total, number} verarbeitet", "upload_skipped_duplicates": "{count, plural, one {# doppelte Datei} other {# doppelte Dateien}} ausgelassen", "upload_status_duplicates": "Duplikate", @@ -1954,6 +1976,7 @@ "upload_success": "Hochladen erfolgreich. Aktualisiere die Seite, um neue hochgeladene Dateien zu sehen.", "upload_to_immich": "Auf Immich hochladen ({count})", "uploading": "Wird hochgeladen", + "uploading_media": "Medien werden hochgeladen", "url": "URL", "usage": "Verwendung", "use_biometric": "Biometrie verwenden", diff --git a/i18n/el.json b/i18n/el.json index 015bf80c0a..d8d12bddd0 100644 --- a/i18n/el.json +++ b/i18n/el.json @@ -166,6 +166,20 @@ "metadata_settings_description": "Διαχείρηση ρυθμίσεων μεταδεδομένων", "migration_job": "Μεταφορά δεδομένων (Migration)", "migration_job_description": "Μεταφορά των εικονιδίων για αρχεία και πρόσωπα στην πιο πρόσφατη δομή αρχείων", + "nightly_tasks_cluster_faces_setting_description": "Εκτέλεση αναγνώρισης προσώπου σε νέα ανιχνευμένα πρόσωπα", + "nightly_tasks_cluster_new_faces_setting": "Ομαδοποίηση νέων προσώπων", + "nightly_tasks_database_cleanup_setting": "Εργασίες καθαρισμού βάσης δεδομένων", + "nightly_tasks_database_cleanup_setting_description": "Εκκαθάριση παλιών και ληγμένων δεδομένων από τη βάση δεδομένων", + "nightly_tasks_generate_memories_setting": "Δημιουργία αναμνήσεων", + "nightly_tasks_generate_memories_setting_description": "Δημιουργία νέων αναμνήσεων από αντικείμενα", + "nightly_tasks_missing_thumbnails_setting": "Δημιουργία ελλειπόντων μικρογραφιών", + "nightly_tasks_missing_thumbnails_setting_description": "Τοποθέτηση στη ουρά των αρχείων χωρίς μικρογραφίες για δημιουργία μικρογραφιών", + "nightly_tasks_settings": "Ρυθμίσεις για τις νυχτερινές εργασίες", + "nightly_tasks_settings_description": "Διαχείριση νυχτερινών εργασιών", + "nightly_tasks_start_time_setting": "Ώρα έναρξης", + "nightly_tasks_start_time_setting_description": "Η ώρα κατά την οποία ο διακομιστής ξεκινάει να εκτελεί τις νυχτερινές εργασίες", + "nightly_tasks_sync_quota_usage_setting": "Συγχρονισμός χρήσης χώρου", + "nightly_tasks_sync_quota_usage_setting_description": "Ενημέρωση του διαθέσιμου χώρου χρήστη, με βάση την τρέχουσα χρήση", "no_paths_added": "Δεν προστέθηκαν διαδρομές", "no_pattern_added": "Δεν προστέθηκε μοτίβο", "note_apply_storage_label_previous_assets": "Σημείωση: Για να εφαρμοστεί η Ετικέτα Αποθήκευσης σε στοιχεία που είχαν αναρτηθεί παλαιότερα, εκτέλεσε το", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "URI Ανακατεύθυνσης για κινητά τηλέφωνα", "oauth_mobile_redirect_uri_override": "Προσπέλαση URI ανακατεύθυνσης για κινητά τηλέφωνα", "oauth_mobile_redirect_uri_override_description": "Ενεργοποιήστε το όταν ο πάροχος OAuth δεν επιτρέπει μια URI για κινητά, όπως το ''{callback}''", + "oauth_role_claim": "Ανάθεση ρόλου", + "oauth_role_claim_description": "Αυτόματη παραχώρηση πρόσβασης διαχειριστή με βάση την ύπαρξη αυτής της ανάθεσης. Η ανάθεση μπορεί να είναι είτε 'χρήστης' είτε 'διαχειριστής'.", "oauth_settings": "OAuth", "oauth_settings_description": "Διαχείριση ρυθμίσεων σύνδεσης OAuth", "oauth_settings_more_details": "Για περισσότερες λεπτομέρειες σχετικά με αυτήν τη δυνατότητα, ανατρέξτε στην τεκμηρίωση.", @@ -357,10 +373,12 @@ "admin_password": "Κωδικός πρόσβασης Διαχειριστή", "administration": "Διαχείριση", "advanced": "Για προχωρημένους", + "advanced_settings_beta_timeline_subtitle": "Δοκίμασε τη νέα εμπειρία της εφαρμογής", + "advanced_settings_beta_timeline_title": "Δοκιμαστικό χρονολόγιο", "advanced_settings_enable_alternate_media_filter_subtitle": "Χρησιμοποιήστε αυτήν την επιλογή για να φιλτράρετε τα μέσα ενημέρωσης κατά τον συγχρονισμό με βάση εναλλακτικά κριτήρια. Δοκιμάστε αυτή τη δυνατότητα μόνο αν έχετε προβλήματα με την εφαρμογή που εντοπίζει όλα τα άλμπουμ.", "advanced_settings_enable_alternate_media_filter_title": "[ΠΕΙΡΑΜΑΤΙΚΟ] Χρήση εναλλακτικού φίλτρου συγχρονισμού άλμπουμ συσκευής", "advanced_settings_log_level_title": "Επίπεδο σύνδεσης: {level}", - "advanced_settings_prefer_remote_subtitle": "Μερικές συσκευές αργούν πολύ να φορτώσουν μικρογραφίες από αρχεία στη συσκευή. Ενεργοποιήστε αυτήν τη ρύθμιση για να φορτώνονται αντί αυτού απομακρυσμένες εικόνες.", + "advanced_settings_prefer_remote_subtitle": "Μερικές συσκευές αργούν πολύ να φορτώσουν μικρογραφίες από τοπικά αρχεία. Ενεργοποιήστε αυτήν τη ρύθμιση για να φορτώνονται αντί αυτού απομακρυσμένες εικόνες.", "advanced_settings_prefer_remote_title": "Προτίμηση απομακρυσμένων εικόνων", "advanced_settings_proxy_headers_subtitle": "Καθορισμός κεφαλίδων διακομιστή μεσολάβησης που το Immich πρέπει να στέλνει με κάθε αίτημα δικτύου", "advanced_settings_proxy_headers_title": "Κεφαλίδες διακομιστή μεσολάβησης", @@ -379,6 +397,7 @@ "album_cover_updated": "Το εξώφυλλο του άλμπουμ, ενημερώθηκε", "album_delete_confirmation": "Είστε σίγουροι ότι θέλετε να διαγράψετε το άλμπουμ {album};", "album_delete_confirmation_description": "Εάν αυτό το άλμπουμ είναι κοινόχρηστο, οι άλλοι χρήστες δεν θα μπορούν να έχουν πρόσβαση.", + "album_deleted": "Το άλμπουμ διαγράφηκε", "album_info_card_backup_album_excluded": "ΕΞΑΙΡΟΥΜΕΝΟ", "album_info_card_backup_album_included": "ΣΥΜΠΕΡΙΛΑΜΒΑΝΟΜΕΝΟ", "album_info_updated": "Οι πληροφορίες του άλμπουμ, ενημερώθηκαν", @@ -388,6 +407,7 @@ "album_options": "Επιλογές άλμπουμ", "album_remove_user": "Διαγραφή χρήστη;", "album_remove_user_confirmation": "Είστε σίγουροι ότι θέλετε να αφαιρέσετε τον/την {user};", + "album_search_not_found": "Δε βρέθηκαν άλμπουμ που να ταιριάζουν με την αναζήτησή σας", "album_share_no_users": "Φαίνεται ότι έχετε κοινοποιήσει αυτό το άλμπουμ σε όλους τους χρήστες ή δεν έχετε χρήστες για να το κοινοποιήσετε.", "album_updated": "Το άλμπουμ, ενημερώθηκε", "album_updated_setting_description": "Λάβετε ειδοποίηση μέσω email όταν ένα κοινόχρηστο άλμπουμ έχει νέα αρχεία", @@ -407,6 +427,7 @@ "albums_default_sort_order": "Προεπιλεγμένη ταξινόμηση άλμπουμ", "albums_default_sort_order_description": "Αρχική ταξινόμηση κατά τη δημιουργία νέων άλμπουμ.", "albums_feature_description": "Συλλογές στοιχείων που μπορούν να κοινοποιηθούν σε άλλους χρήστες.", + "albums_on_device_count": "Άλμπουμ στη συσκευή ({count})", "all": "Όλα", "all_albums": "Όλα τα άλμπουμ", "all_people": "Όλα τα άτομα", @@ -427,6 +448,7 @@ "app_settings": "Ρυθμίσεις εφαρμογής", "appears_in": "Εμφανίζεται σε", "archive": "Αρχείο", + "archive_action_prompt": "Προστέθηκαν {count} στο Αρχείο", "archive_or_unarchive_photo": "Αρχειοθέτηση ή αποαρχειοθέτηση φωτογραφίας", "archive_page_no_archived_assets": "Δε βρέθηκαν αρχειοθετημένα στοιχεία", "archive_page_title": "Αρχείο ({count})", @@ -489,6 +511,7 @@ "back_close_deselect": "Πίσω, κλείσιμο ή αποεπιλογή", "background_location_permission": "Άδεια τοποθεσίας στο παρασκήνιο", "background_location_permission_content": "Το Immich για να μπορεί να αλλάζει δίκτυα όταν τρέχει στο παρασκήνιο, πρέπει *πάντα* να έχει πρόσβαση στην ακριβή τοποθεσία ώστε η εφαρμογή να μπορεί να διαβάζει το όνομα του δικτύου Wi-Fi", + "backup": "Αντίγραφα ασφαλείας", "backup_album_selection_page_albums_device": "Άλμπουμ στη συσκευή ({count})", "backup_album_selection_page_albums_tap": "Πάτημα για συμπερίληψη, διπλό πάτημα για εξαίρεση", "backup_album_selection_page_assets_scatter": "Τα στοιχεία μπορεί να διασκορπιστούν σε πολλά άλμπουμ. Έτσι, τα άλμπουμ μπορούν να περιληφθούν ή να εξαιρεθούν κατά τη διαδικασία δημιουργίας αντιγράφων ασφαλείας.", @@ -552,6 +575,8 @@ "backup_options_page_title": "Επιλογές αντιγράφων ασφαλείας", "backup_setting_subtitle": "Διαχείριση ρυθμίσεων μεταφόρτωσης στο παρασκήνιο και στο προσκήνιο", "backward": "Προς τα πίσω", + "beta_sync": "Κατάσταση Συγχρονισμού Beta (δοκιμαστική)", + "beta_sync_subtitle": "Διαχείριση του νέου συστήματος συγχρονισμού", "biometric_auth_enabled": "Βιομετρική ταυτοποίηση ενεργοποιήθηκε", "biometric_locked_out": "Είστε κλειδωμένοι εκτός της βιομετρικής ταυτοποίησης", "biometric_no_options": "Δεν υπάρχουν διαθέσιμοι τρόποι βιομετρικής ταυτοποίησης", @@ -586,6 +611,7 @@ "cancel": "Ακύρωση", "cancel_search": "Ακύρωση αναζήτησης", "canceled": "Ακυρωμένο", + "canceling": "Ακυρώνεται", "cannot_merge_people": "Αδύνατη η συγχώνευση ατόμων", "cannot_undo_this_action": "Δεν μπορείτε να αναιρέσετε αυτήν την ενέργεια!", "cannot_update_the_description": "Αδύνατη η ενημέρωση της περιγραφής", @@ -699,9 +725,11 @@ "current_server_address": "Τρέχουσα διεύθυνση διακομιστή", "custom_locale": "Προσαρμοσμένη Τοπική Ρύθμιση", "custom_locale_description": "Μορφοποιήστε τις ημερομηνίες και τους αριθμούς, σύμφωνα με τη γλώσσα και την περιοχή", + "custom_url": "Προσαρμοσμένη διεύθυνση URL", "daily_title_text_date": "Ε, MMM dd", "daily_title_text_date_year": "Ε, MMM dd, yyyy", "dark": "Σκούρο", + "dark_theme": "Εναλλαγή σκοτεινής εμφάνισης", "date_after": "Ημερομηνία μετά", "date_and_time": "Ημερομηνία και ώρα", "date_before": "Ημερομηνία πριν", @@ -717,6 +745,8 @@ "default_locale": "Προεπιλεγμένη Τοπική Ρύθμιση", "default_locale_description": "Μορφοποιήστε τις ημερομηνίες και τους αριθμούς με βάση την τοπική ρύθμιση του προγράμματος περιήγησής σας", "delete": "Διαγραφή", + "delete_action_confirmation_message": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτό το αρχείο; Αυτή η ενέργεια θα το μετακινήσει στον κάδο απορριμμάτων του διακομιστή και θα εμφανιστεί μήνυμα για το αν θέλετε να το διαγράψετε και τοπικά", + "delete_action_prompt": "{count} διαγράφηκαν", "delete_album": "Διαγραφή άλμπουμ", "delete_api_key_prompt": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτό κλειδί API;", "delete_dialog_alert": "Αυτά τα αντικείμενα θα διαγραφούν οριστικά από το Immich και από τη συσκευή σας", @@ -730,9 +760,12 @@ "delete_key": "Διαγραφή κλειδιού", "delete_library": "Διαγραφή Βιβλιοθήκης", "delete_link": "Διαγραφή συνδέσμου", + "delete_local_action_prompt": "{count} διαγράφηκαν τοπικά", "delete_local_dialog_ok_backed_up_only": "Διαγραφή μόνο των αντιγράφων ασφαλείας", "delete_local_dialog_ok_force": "Διαγραφή όπως και να έχει", "delete_others": "Διαγραφή υπολοίπων", + "delete_permanently": "Διαγραφή οριστικά", + "delete_permanently_action_prompt": "{count} διαγράφηκε οριστικά", "delete_shared_link": "Διαγραφή κοινόχρηστου συνδέσμου", "delete_shared_link_dialog_title": "Διαγραφή Κοινοποιημένου Συνδέσμου", "delete_tag": "Διαγραφή ετικέτας", @@ -743,6 +776,7 @@ "description": "Περιγραφή", "description_input_hint_text": "Προσθήκη περιγραφής...", "description_input_submit_error": "Σφάλμα κατά την ενημέρωση της περιγραφής, ελέγξτε το αρχείο καταγραφής για περισσότερες λεπτομέρειες", + "deselect_all": "Ακύρωση όλων των επιλογών", "details": "Λεπτομέρειες", "direction": "Κατεύθυνση", "disabled": "Απενεργοποιημένο", @@ -760,6 +794,7 @@ "documentation": "Τεκμηρίωση", "done": "Έγινε", "download": "Λήψη", + "download_action_prompt": "Κατέβασμα {count} στοιχείων", "download_canceled": "Η λήψη ακυρώθηκε", "download_complete": "Η λήψη ολοκληρώθηκε", "download_enqueue": "Η λήψη τέθηκε σε ουρά", @@ -797,6 +832,7 @@ "edit_key": "Επεξεργασία κλειδιού", "edit_link": "Επεξεργασία συνδέσμου", "edit_location": "Επεξεργασία τοποθεσίας", + "edit_location_action_prompt": "Επεξεργάστηκαν {count} τοποθεσίες", "edit_location_dialog_title": "Τοποθεσία", "edit_name": "Επεξεργασία ονόματος", "edit_people": "Επεξεργασία ατόμων", @@ -815,6 +851,7 @@ "empty_trash": "Άδειασμα κάδου απορριμμάτων", "empty_trash_confirmation": "Είστε σίγουροι οτι θέλετε να αδειάσετε τον κάδο απορριμμάτων; Αυτό θα αφαιρέσει μόνιμα όλα τα στοιχεία του κάδου απορριμμάτων του Immich. \nΑυτή η ενέργεια δεν μπορεί να αναιρεθεί!", "enable": "Ενεργοποίηση", + "enable_backup": "Ενεργοποίηση αντιγράφου ασφαλείας", "enable_biometric_auth_description": "Εισάγετε τον κωδικό PIN σας για να ενεργοποιήσετε την βιομετρική ταυτοποίηση", "enabled": "Ενεργοποιημένο", "end_date": "Τελική ημερομηνία", @@ -971,6 +1008,8 @@ "explorer": "Περιηγητής", "export": "Εξαγωγή", "export_as_json": "Εξαγωγή ως JSON", + "export_database": "Εξαγωγή βάσης δεδομένων", + "export_database_description": "Εξαγωγή της SQLite βάσης δεδομένων", "extension": "Επέκταση", "external": "Εξωτερικός", "external_libraries": "Εξωτερικές βιβλιοθήκες", @@ -982,6 +1021,7 @@ "failed_to_load_assets": "Αποτυχία φόρτωσης στοιχείων", "failed_to_load_folder": "Αποτυχία φόρτωσης φακέλου", "favorite": "Αγαπημένο", + "favorite_action_prompt": "Προστέθηκαν {count} στα Αγαπημένα", "favorite_or_unfavorite_photo": "Ορίστε μία φωτογραφία ως αγαπημένη ή αφαιρέστε την από τα αγαπημένα", "favorites": "Αγαπημένα", "favorites_page_no_favorites": "Δεν βρέθηκαν αγαπημένα στοιχεία", @@ -1021,6 +1061,9 @@ "haptic_feedback_switch": "Ενεργοποίηση απτικής ανάδρασης", "haptic_feedback_title": "Απτική Ανάδραση", "has_quota": "Έχει ποσόστωση", + "hash_asset": "Κατακερματισμός στοιχείου", + "hashed_assets": "Κατακερματισμένα στοιχεία", + "hashing": "Κατακερματισμός", "header_settings_add_header_tip": "Προσθήκη Κεφαλίδας", "header_settings_field_validator_msg": "Η τιμή δεν μπορεί να είναι κενή", "header_settings_header_name_input": "Όνομα κεφαλίδας", @@ -1053,6 +1096,7 @@ "host": "Φιλοξενία", "hour": "Ώρα", "id": "ID", + "idle": "Αδράνεια", "ignore_icloud_photos": "Αγνοήστε τις φωτογραφίες iCloud", "ignore_icloud_photos_description": "Οι φωτογραφίες που είναι αποθηκευμένες στο iCloud δεν θα μεταφορτωθούν στον διακομιστή Immich", "image": "Εικόνα", @@ -1110,6 +1154,7 @@ "language_no_results_title": "Δε βρέθηκαν γλώσσες", "language_search_hint": "Αναζήτηση γλωσσών...", "language_setting_description": "Επιλέξτε τη γλώσσα που προτιμάτε", + "large_files": "Μεγάλα Αρχεία", "last_seen": "Τελευταία προβολή", "latest_version": "Τελευταία Έκδοση", "latitude": "Γεωγραφικό πλάτος", @@ -1125,16 +1170,18 @@ "library_page_sort_created": "Ημερομηνία δημιουργίας", "library_page_sort_last_modified": "Τελευταία τροποποίηση", "library_page_sort_title": "Τίτλος άλμπουμ", + "licenses": "Άδειες", "light": "Φωτεινό", "like_deleted": "Το \"μου αρέσει\" διαγράφηκε", "link_motion_video": "Σύνδεσε βίντεο κίνησης", - "link_options": "Επιλογές συνδέσμου", "link_to_oauth": "Σύνδεση στον OAuth", "linked_oauth_account": "Ο OAuth λογαριασμός συνδέθηκε", "list": "Λίστα", "loading": "Φόρτωση", "loading_search_results_failed": "Η φόρτωση αποτελεσμάτων αναζήτησης απέτυχε", + "local": "Τοπικά", "local_asset_cast_failed": "Αδυναμία μετάδοσης στοιχείου που δεν έχει ανέβει στον διακομιστή", + "local_assets": "Τοπικά στοιχεία", "local_network": "Τοπικό δίκτυο", "local_network_sheet_info": "Η εφαρμογή θα συνδεθεί με τον διακομιστή μέσω αυτού του URL όταν χρησιμοποιείται το καθορισμένο δίκτυο Wi-Fi", "location_permission": "Άδεια τοποθεσίας", @@ -1191,8 +1238,7 @@ "manage_your_devices": "Διαχειριστείτε τις συνδεδεμένες συσκευές σας", "manage_your_oauth_connection": "Διαχειριστείτε τη σύνδεσή σας OAuth", "map": "Χάρτης", - "map_assets_in_bound": "{count} φωτογραφία", - "map_assets_in_bounds": "{count} φωτογραφίες", + "map_assets_in_bounds": "{count, plural, one {# φωτογραφία} other {# φωτογραφίες}}", "map_cannot_get_user_location": "Δεν είναι δυνατή η λήψη της τοποθεσίας του χρήστη", "map_location_dialog_yes": "Ναι", "map_location_picker_page_use_location": "Χρησιμοποιήστε αυτήν την τοποθεσία", @@ -1244,6 +1290,7 @@ "more": "Περισσότερα", "move": "Μετακίνηση", "move_off_locked_folder": "Μετακίνηση έξω από τον κλειδωμένο φάκελο", + "move_to_lock_folder_action_prompt": "Προστέθηκαν {count} στον κλειδωμένο φάκελο", "move_to_locked_folder": "Μετακίνηση σε κλειδωμένο φάκελο", "move_to_locked_folder_confirmation": "Αυτές οι φωτογραφίες και τα βίντεο θα αφαιρεθούν από όλα τα άλμπουμ και θα μπορούν να προβληθούν μόνο από τον κλειδωμένο φάκελο", "moved_to_archive": "Μετακινήθηκαν {count, plural, one {# στοιχείο} other {# στοιχεία}} στο αρχείο", @@ -1290,6 +1337,7 @@ "no_results": "Κανένα αποτέλεσμα", "no_results_description": "Δοκιμάστε ένα συνώνυμο ή πιο γενική λέξη-κλειδί", "no_shared_albums_message": "Δημιουργήστε ένα άλμπουμ για να μοιράζεστε φωτογραφίες και βίντεο με άτομα στο δίκτυό σας", + "no_uploads_in_progress": "Καμία μεταφόρτωση σε εξέλιξη", "not_in_any_album": "Σε κανένα άλμπουμ", "not_selected": "Δεν επιλέχθηκε", "note_apply_storage_label_to_previously_uploaded assets": "Σημείωση: Για να εφαρμόσετε την Ετικέτα Αποθήκευσης σε στοιχεία που έχουν μεταφορτωθεί προηγουμένως, εκτελέστε το", @@ -1327,6 +1375,7 @@ "original": "πρωτότυπο", "other": "Άλλες", "other_devices": "Άλλες συσκευές", + "other_entities": "Άλλες οντότητες", "other_variables": "Άλλες μεταβλητές", "owned": "Δικά μου", "owner": "Κάτοχος", @@ -1458,6 +1507,7 @@ "purchase_server_description_2": "Κατάσταση υποστηρικτή", "purchase_server_title": "Διακομιστής", "purchase_settings_server_activated": "Η διαχείριση του κλειδιού προϊόντος του διακομιστή γίνεται από τον διαχειριστή", + "queue_status": "Τοποθέτηση στη ουρά {count} από {total}", "rating": "Αξιολόγηση με αστέρια", "rating_clear": "Εκκαθάριση αξιολόγησης", "rating_count": "{count, plural, one {# αστέρι} other {# αστέρια}}", @@ -1486,6 +1536,8 @@ "refreshing_faces": "Ανανεώνονται πρόσωπα", "refreshing_metadata": "Τα μεταδεδομένα ανανεώνονται", "regenerating_thumbnails": "Οι μικρογραφίες αναγεννώνται", + "remote": "Απομακρυσμένος", + "remote_assets": "Απομακρυσμένα στοιχεία", "remove": "Αφαίρεση", "remove_assets_album_confirmation": "Είστε σίγουροι ότι θέλετε να αφαιρέσετε {count, plural, one {# στοιχείο} other {# στοιχεία}} από το άλμπουμ;", "remove_assets_shared_link_confirmation": "Είστε σίγουροι ότι θέλετε να αφαιρέσετε {count, plural, one {# στοιχείο} other {# στοιχεία}} από αυτόν τον κοινόχρηστο σύνδεσμο;", @@ -1493,7 +1545,9 @@ "remove_custom_date_range": "Αφαίρεση προσαρμοσμένης χρονικής περιόδου", "remove_deleted_assets": "Αφαίρεση Διεγραμμένων Στοιχείων", "remove_from_album": "Αφαίρεση από το άλμπουμ", + "remove_from_album_action_prompt": "{count} αφαιρέθηκαν από το άλμπουμ", "remove_from_favorites": "Αφαίρεση από τα αγαπημένα", + "remove_from_lock_folder_action_prompt": "{count} αφαιρέθηκαν από τον κλειδωμένο φάκελο", "remove_from_locked_folder": "Αφαίρεση από κλειδωμένο φάκελο", "remove_from_locked_folder_confirmation": "Είστε σίγουροι ότι θέλετε να μετακινήσετε αυτές τις φωτογραφίες και τα βίντεο από τον κλειδωμένο φάκελο; Θα είναι πλέον ορατές στη βιβλιοθήκη σας.", "remove_from_shared_link": "Αφαίρεση από τον κοινόχρηστο σύνδεσμο", @@ -1521,19 +1575,25 @@ "reset_password": "Επαναφορά κωδικού πρόσβασης", "reset_people_visibility": "Επαναφορά προβολής ατόμων", "reset_pin_code": "Επαναφορά κωδικού PIN", + "reset_sqlite": "Επαναφορά SQLite βάσης δεδομένων", + "reset_sqlite_confirmation": "Είσαι σίγουρος ότι θέλεις να επαναφέρεις τη βάση δεδομένων SQLite; Θα χρειαστεί να κάνεις αποσύνδεση και επανασύνδεση για να επανασυγχρονίσεις τα δεδομένα", + "reset_sqlite_success": "Η επαναφορά της SQLite βάσης δεδομένων ολοκληρώθηκε με επιτυχία", "reset_to_default": "Επαναφορά στις προεπιλογές", "resolve_duplicates": "Επίλυση διπλοτύπων", "resolved_all_duplicates": "Επιλύθηκαν όλα τα διπλότυπα", "restore": "Ανάκτηση", "restore_all": "Ανάκτηση όλων", + "restore_trash_action_prompt": "{count} ανακτήθηκαν από τα απορρίμματα", "restore_user": "Επαναφορά χρήστη", "restored_asset": "Ανακτήθηκε το αρχείο", "resume": "Συνέχιση", "retry_upload": "Επανάληψη ανεβάσματος", "review_duplicates": "Προβολή διπλότυπων", + "review_large_files": "Επισκόπηση μεγάλων αρχείων", "role": "Ρόλος", "role_editor": "Επεξεργαστής", "role_viewer": "Θεατής", + "running": "Σε λειτουργία", "save": "Αποθήκευση", "save_to_gallery": "Αποθήκευση στη συλλογή", "saved_api_key": "Αποθηκευμένο API key", @@ -1665,6 +1725,7 @@ "settings_saved": "Οι ρυθμίσεις αποθηκεύτηκαν", "setup_pin_code": "Ρύθμιση κωδικού PIN", "share": "Κοινοποίηση", + "share_action_prompt": "Κοινή χρήση {count} στοιχείων", "share_add_photos": "Προσθήκη φωτογραφιών", "share_assets_selected": "{count} επιλεγμένα", "share_dialog_preparing": "Προετοιμασία...", @@ -1686,6 +1747,7 @@ "shared_link_clipboard_copied_massage": "Αντιγράφηκε στο πρόχειρο", "shared_link_clipboard_text": "Σύνδεσμος: {link}\nΚωδικός πρόσβασης: {password}", "shared_link_create_error": "Σφάλμα κατά τη δημιουργία κοινόχρηστου συνδέσμου", + "shared_link_custom_url_description": "Αποκτήστε πρόσβαση σε αυτόν τον κοινόχρηστο σύνδεσμο με μια προσαρμοσμένη διεύθυνση URL", "shared_link_edit_description_hint": "Εισαγάγετε την περιγραφή της κοινής χρήσης", "shared_link_edit_expire_after_option_day": "1 ημέρα", "shared_link_edit_expire_after_option_days": "{count} ημέρες", @@ -1711,6 +1773,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Διαχείριση Κοινόχρηστων Συνδέσμων", "shared_link_options": "Επιλογές κοινόχρηστου συνδέσμου", + "shared_link_password_description": "Απαίτηση κωδικού για πρόσβαση στον κοινοποιημένο σύνδεσμο", "shared_links": "Κοινόχρηστοι σύνδεσμοι", "shared_links_description": "Μοιραστείτε φωτογραφίες και βίντεο με σύνδεσμο", "shared_photos_and_videos_count": "{assetCount, plural, other {# κοινόχρηστες φωτογραφίες & βίντεο.}}", @@ -1766,6 +1829,7 @@ "sort_title": "Τίτλος", "source": "Πηγή", "stack": "Στοίβα", + "stack_action_prompt": "{count} συσσωρεύτηκαν", "stack_duplicates": "Στοίβαξη διπλότυπων", "stack_select_one_photo": "Επιλέξτε μια κύρια φωτογραφία για τη στοίβαξη", "stack_selected_photos": "Στοίβαγμα επιλεγμένων φωτογραφιών", @@ -1785,6 +1849,7 @@ "storage_quota": "Ποσοστό αποθηκευτικού χώρου", "storage_usage": "{used} από {available} σε χρήση", "submit": "Υποβολή", + "success": "Επιτυχία", "suggestions": "Προτάσεις", "sunrise_on_the_beach": "Ηλιοβασίλεμα στην παραλία", "support": "Υποστήριξη", @@ -1794,6 +1859,8 @@ "sync": "Συγχρονισμός", "sync_albums": "Συγχρονισμός άλμπουμ", "sync_albums_manual_subtitle": "Συγχρονίστε όλα τα μεταφορτωμένα βίντεο και φωτογραφίες με τα επιλεγμένα εφεδρικά άλμπουμ", + "sync_local": "Τοπικός Συγχρονισμός", + "sync_remote": "Απομακρυσμένος Συγχρονισμός", "sync_upload_album_setting_subtitle": "Δημιουργήστε και ανεβάστε τις φωτογραφίες και τα βίντεό σας στα επιλεγμένα άλμπουμ στο Immich", "tag": "Ετικέτα", "tag_assets": "Ετικετοποίηση στοιχείων", @@ -1804,6 +1871,7 @@ "tag_updated": "Ενημερώθηκε η ετικέτα: {tag}", "tagged_assets": "Ετικετοποιημένο/α {count, plural, one {# στοιχείο} other {# στοιχεία}}", "tags": "Ετικέτες", + "tap_to_run_job": "Πατήστε για να ξεκινήσει η εργασία", "template": "Πρότυπο", "theme": "Θέμα", "theme_selection": "Επιλογή θέματος", @@ -1836,6 +1904,7 @@ "total": "Σύνολο", "total_usage": "Συνολικη χρηση", "trash": "Κάδος απορριμμάτων", + "trash_action_prompt": "{count} μετακινήθηκαν στα απορρίμματα", "trash_all": "Διαγραφή Όλων", "trash_count": "Διαγραφή {count, number}", "trash_delete_asset": "Διαγραφή/Οριστ. Διαγραφή Αντικειμένου", @@ -1853,9 +1922,11 @@ "unable_to_change_pin_code": "Αδυναμία αλλαγής κωδικού PIN", "unable_to_setup_pin_code": "Αδυναμία ρύθμισης κωδικού PIN", "unarchive": "Αναίρεση αρχειοθέτησης", + "unarchive_action_prompt": "{count} αφαιρέθηκαν από το Αρχείο", "unarchived_count": "{count, plural, other {Αρχειοθετήσεις αναιρέθηκαν #}}", "undo": "Αναίρεση", "unfavorite": "Αποεπιλογή από τα αγαπημένα", + "unfavorite_action_prompt": "{count} αφαιρέθηκαν από τα Αγαπημένα", "unhide_person": "Αναίρεση απόκρυψης ατόμου", "unknown": "Άγνωστο", "unknown_country": "Άγνωστη Χώρα", @@ -1873,15 +1944,20 @@ "unselect_all_duplicates": "Αποεπιλογή όλων των διπλότυπων", "unselect_all_in": "Αποεπιλογή όλων στο {group}", "unstack": "Αποστοίβαξη", + "unstack_action_prompt": "{count} αποσυσσωρεύτηκαν", "unstacked_assets_count": "Αποστοιβάξατε {count, plural, one {# στοιχείο} other {# στοιχεία}}", + "untagged": "Χωρίς ετικέτα", "up_next": "Ακολουθεί", "updated_at": "Ενημερωμένο", "updated_password": "Ο κωδικός πρόσβασης ενημερώθηκε", "upload": "Μεταφόρτωση", + "upload_action_prompt": "{count} τοποθετήθηκαν στην ουρά για μεταφόρτωση", "upload_concurrency": "Ταυτόχρονη μεταφόρτωση", + "upload_details": "Λεπτομέρειες μεταφόρτωσης", "upload_dialog_info": "Θέλετε να αντιγράψετε (κάνετε backup) τα επιλεγμένo(α) στοιχείο(α) στο διακομιστή;", "upload_dialog_title": "Ανέβασμα στοιχείου", "upload_errors": "Η μεταφόρτωση ολοκληρώθηκε με {count, plural, one {# σφάλμα} other {# σφάλματα}}, ανανεώστε τη σελίδα για να δείτε νέα στοιχεία μεταφόρτωσης.", + "upload_finished": "Ολοκλήρωση μεταφόρτωσης", "upload_progress": "Απομένουν {remaining, number} - Ολοκληρώθηκαν {processed, number}/{total, number}", "upload_skipped_duplicates": "Παραλείφθηκαν {count, plural, one {# διπλότυπο στοιχείο} other {# διπλότυπα στοιχεία}}", "upload_status_duplicates": "Διπλότυπα", @@ -1890,6 +1966,7 @@ "upload_success": "Η μεταφόρτωση ολοκληρώθηκε, ανανεώστε τη σελίδα για να δείτε τα νέα αντικείμενα.", "upload_to_immich": "Μεταφόρτωση στο Immich ({count})", "uploading": "Μεταφορτώνεται", + "uploading_media": "Μεταφόρτωση πολυμέσων", "url": "URL", "usage": "Χρήση", "use_biometric": "Χρήση βιομετρικών στοιχείων", @@ -1910,6 +1987,7 @@ "user_usage_stats_description": "Προβολή στατιστικών χρήσης λογαριασμού", "username": "Όνομα Χρήστη", "users": "Χρήστες", + "users_added_to_album_count": "Προστέθηκε {count, plural, one {# χρήστης} other {# χρήστες}} στο άλμπουμ", "utilities": "Βοηθητικά προγράμματα", "validate": "Επικύρωση", "validate_endpoint_error": "Παρακαλώ εισάγετε ένα έγκυρο URL", @@ -1928,6 +2006,7 @@ "view_album": "Προβολή Άλμπουμ", "view_all": "Προβολή Όλων", "view_all_users": "Προβολή όλων των χρηστών", + "view_details": "Προβολή Λεπτομερειών", "view_in_timeline": "Προβολή στο χρονοδιάγραμμα", "view_link": "Προβολή σύνδεσμου", "view_links": "Προβολή συνδέσμων", diff --git a/i18n/es.json b/i18n/es.json index 83d1c9d355..e78c6936c7 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -182,7 +182,7 @@ "nightly_tasks_sync_quota_usage_setting_description": "Actualizar la cuota de almacenamiento del usuario, según el uso actual", "no_paths_added": "No se han añadido carpetas", "no_pattern_added": "No se han añadido patrones", - "note_apply_storage_label_previous_assets": "Nota: para aplicar una Etiqueta de Almacenamiento a un elemento anteriormente cargado, lanza el", + "note_apply_storage_label_previous_assets": "Nota: para aplicar una Etiqueta de Almacenamiento a un elemento anteriormente subido, lanza el", "note_cannot_be_changed_later": "NOTA: ¡No se puede cambiar posteriormente!", "notification_email_from_address": "Desde", "notification_email_from_address_description": "Dirección de correo electrónico del remitente, por ejemplo: \"Immich Photo Server \". Asegúrate de utilizar una dirección desde la que puedas enviar correos electrónicos.", @@ -263,7 +263,7 @@ "storage_template_onboarding_description_v2": "Al habilitar esta función, los archivos se organizarán automáticamente según la plantilla definida por el usuario. Para más información, consulte la documentación.", "storage_template_path_length": "Límite aproximado de la longitud de la ruta: {length, number}/{limit, number}", "storage_template_settings": "Plantilla de almacenamiento", - "storage_template_settings_description": "Administrar la estructura de carpetas y el nombre de archivo del recurso cargado", + "storage_template_settings_description": "Administrar la estructura de carpetas y el nombre de archivo del recurso subido", "storage_template_user_label": "{label} es la etiqueta de almacenamiento del usuario", "system_settings": "Ajustes del Sistema", "tag_cleanup_job": "Limpieza de etiquetas", @@ -397,6 +397,7 @@ "album_cover_updated": "Portada del álbum actualizada", "album_delete_confirmation": "¿Estás seguro de que deseas eliminar el álbum {album}?", "album_delete_confirmation_description": "Si este álbum se comparte, otros usuarios ya no podrán acceder a él.", + "album_deleted": "Álbum eliminado", "album_info_card_backup_album_excluded": "EXCLUIDOS", "album_info_card_backup_album_included": "INCLUIDOS", "album_info_updated": "Información del álbum actualizada", @@ -406,6 +407,7 @@ "album_options": "Opciones del Album", "album_remove_user": "¿Eliminar usuario?", "album_remove_user_confirmation": "¿Estás seguro de que quieres eliminar a {user}?", + "album_search_not_found": "No se encontraron álbumes que coincidan con tu búsqueda", "album_share_no_users": "Parece que has compartido este álbum con todos los usuarios o no tienes ningún usuario con quien compartirlo.", "album_updated": "Album actualizado", "album_updated_setting_description": "Reciba una notificación por correo electrónico cuando un álbum compartido tenga nuevos archivos", @@ -425,6 +427,7 @@ "albums_default_sort_order": "Ordenación por defecto de los álbumes", "albums_default_sort_order_description": "Orden de clasificación inicial de los recursos al crear nuevos álbumes.", "albums_feature_description": "Colecciones de recursos que pueden ser compartidos con otros usuarios.", + "albums_on_device_count": "Álbumes en el dispositivo ({count})", "all": "Todos", "all_albums": "Todos los albums", "all_people": "Todas las personas", @@ -508,6 +511,7 @@ "back_close_deselect": "Atrás, cerrar o anular la selección", "background_location_permission": "Permiso de ubicación en segundo plano", "background_location_permission_content": "Para poder cambiar de red mientras se ejecuta en segundo plano, Immich debe tener *siempre* acceso a la ubicación precisa para que la aplicación pueda leer el nombre de la red Wi-Fi", + "backup": "Copia de Seguridad", "backup_album_selection_page_albums_device": "Álbumes en el dispositivo ({count})", "backup_album_selection_page_albums_tap": "Toque para incluir, doble toque para excluir", "backup_album_selection_page_assets_scatter": "Los elementos pueden dispersarse en varios álbumes. De este modo, los álbumes pueden ser incluidos o excluidos durante el proceso de copia de seguridad.", @@ -571,6 +575,8 @@ "backup_options_page_title": "Opciones de Copia de Seguridad", "backup_setting_subtitle": "Administra las configuraciones de respaldo en segundo y primer plano", "backward": "Retroceder", + "beta_sync": "Estado de Sincronización Beta", + "beta_sync_subtitle": "Administrar el nuevo sistema de sincronización", "biometric_auth_enabled": "Autentificación biométrica habilitada", "biometric_locked_out": "Estás bloqueado de la autentificación biométrica", "biometric_no_options": "Sin opciones biométricas disponibles", @@ -588,7 +594,7 @@ "cache_settings_clear_cache_button": "Borrar caché", "cache_settings_clear_cache_button_title": "Borra la caché de la aplicación. Esto afectará significativamente el rendimiento de la aplicación hasta que se reconstruya la caché.", "cache_settings_duplicated_assets_clear_button": "LIMPIAR", - "cache_settings_duplicated_assets_subtitle": "Fotos y vídeos en la lista negra de la app", + "cache_settings_duplicated_assets_subtitle": "Fotos y vídeos ignorados por la aplicación", "cache_settings_duplicated_assets_title": "Elementos duplicados ({count})", "cache_settings_statistics_album": "Miniaturas de la biblioteca", "cache_settings_statistics_full": "Imágenes completas", @@ -605,6 +611,7 @@ "cancel": "Cancelar", "cancel_search": "Cancelar búsqueda", "canceled": "Cancelado", + "canceling": "Cancelando", "cannot_merge_people": "No se pueden fusionar personas", "cannot_undo_this_action": "¡No puedes deshacer esta acción!", "cannot_update_the_description": "No se puede actualizar la descripción", @@ -718,6 +725,7 @@ "current_server_address": "Dirección actual del servidor", "custom_locale": "Configuración regional personalizada", "custom_locale_description": "Formatear fechas y números según el idioma y la región", + "custom_url": "URL personalizada", "daily_title_text_date": "E dd, MMM", "daily_title_text_date_year": "E dd de MMM, yyyy", "dark": "Oscuro", @@ -737,13 +745,14 @@ "default_locale": "Configuración regional predeterminada", "default_locale_description": "Formatee fechas y números según la configuración regional de su navegador", "delete": "Eliminar", - "delete_action_prompt": "{count} eliminados permanentemente", + "delete_action_confirmation_message": "¿Está seguro que desea eliminar este archivo? Esta acción lo moverá a la papelera del servidor y le preguntará si desea eliminarlo localmente", + "delete_action_prompt": "{count} eliminados", "delete_album": "Eliminar álbum", "delete_api_key_prompt": "¿Está seguro de que desea eliminar esta clave API?", "delete_dialog_alert": "Estos elementos serán eliminados permanentemente de Immich y de tu dispositivo", - "delete_dialog_alert_local": "Estas imágenes van a ser borradas de tu dispositivo, pero seguirán disponibles en el servidor Immich", - "delete_dialog_alert_local_non_backed_up": "Algunas de las imágenes no tienen copia de seguridad y serán borradas de forma permanente de tu dispositivo", - "delete_dialog_alert_remote": "Estas imágenes van a ser borradas de forma permanente del servidor Immich", + "delete_dialog_alert_local": "Estos elementos se eliminarán permanente de tu dispositivo pero seguirán disponibles en el servidor de Immich", + "delete_dialog_alert_local_non_backed_up": "Algunos de los elementos no tienen copia de seguridad en Immich y serán borrados permanentemente de tu dispositivo", + "delete_dialog_alert_remote": "Estas imágenes van a ser borradas permanentemente del servidor de Immich", "delete_dialog_ok_force": "Borrar de todos modos", "delete_dialog_title": "Eliminar Permanentemente", "delete_duplicates_confirmation": "¿Está seguro de que desea eliminar permanentemente estos duplicados?", @@ -755,6 +764,8 @@ "delete_local_dialog_ok_backed_up_only": "Borrar solo las que tengan copia de seguridad", "delete_local_dialog_ok_force": "Borrar de todos modos", "delete_others": "Eliminar otros", + "delete_permanently": "Eliminar permanentemente", + "delete_permanently_action_prompt": "{count} eliminados permanentemente", "delete_shared_link": "Eliminar enlace compartido", "delete_shared_link_dialog_title": "Eliminar enlace compartido", "delete_tag": "Eliminar etiqueta", @@ -765,6 +776,7 @@ "description": "Descripción", "description_input_hint_text": "Agregar descripción...", "description_input_submit_error": "Error al actualizar la descripción, verifica el registro para obtener más detalles", + "deselect_all": "Deseleccionar Todo", "details": "Detalles", "direction": "Dirección", "disabled": "Deshabilitado", @@ -839,6 +851,7 @@ "empty_trash": "Vaciar papelera", "empty_trash_confirmation": "¿Estás seguro de que quieres vaciar la papelera? Esto eliminará permanentemente todos los archivos de la basura de Immich.\n¡No puedes deshacer esta acción!", "enable": "Habilitar", + "enable_backup": "Habilitar Copia de Seguridad", "enable_biometric_auth_description": "Introduce tu código PIN para habilitar la autentificación biométrica", "enabled": "Habilitado", "end_date": "Fecha final", @@ -995,6 +1008,8 @@ "explorer": "Explorador", "export": "Exportar", "export_as_json": "Exportar a JSON", + "export_database": "Exportar Base de Datos", + "export_database_description": "Exportar la Base de Datos SQLite", "extension": "Extensión", "external": "Externo", "external_libraries": "Bibliotecas Externas", @@ -1046,6 +1061,9 @@ "haptic_feedback_switch": "Activar respuesta háptica", "haptic_feedback_title": "Respuesta Háptica", "has_quota": "Su cuota", + "hash_asset": "Generar hash del archivo", + "hashed_assets": "Archivos con hash generado", + "hashing": "Generando hash", "header_settings_add_header_tip": "Añadir cabecera", "header_settings_field_validator_msg": "El valor no puede estar vacío", "header_settings_header_name_input": "Nombre de la cabecera", @@ -1078,6 +1096,7 @@ "host": "Host", "hour": "Hora", "id": "ID", + "idle": "Inactivo", "ignore_icloud_photos": "Ignorar fotos de iCloud", "ignore_icloud_photos_description": "Las fotos almacenadas en iCloud no se subirán a Immich", "image": "Imagen", @@ -1135,6 +1154,7 @@ "language_no_results_title": "No se han encontrado idiomas", "language_search_hint": "Buscar idiomas...", "language_setting_description": "Selecciona tu idioma preferido", + "large_files": "Archivos Grandes", "last_seen": "Ultima vez visto", "latest_version": "Última versión", "latitude": "Latitud", @@ -1154,13 +1174,14 @@ "light": "Claro", "like_deleted": "Me gusta eliminado", "link_motion_video": "Enlazar vídeo en movimiento", - "link_options": "Opciones de enlace", "link_to_oauth": "Enlace a OAuth", "linked_oauth_account": "Cuenta OAuth vinculada", "list": "Listar", "loading": "Cargando", "loading_search_results_failed": "Error al cargar los resultados de la búsqueda", + "local": "Local", "local_asset_cast_failed": "No es posible transmitir un recurso que no está subido al servidor", + "local_assets": "Archivos Locales", "local_network": "Red local", "local_network_sheet_info": "La aplicación se conectará al servidor a través de esta URL cuando utilice la red Wi-Fi especificada", "location_permission": "Permiso de ubicación", @@ -1217,8 +1238,7 @@ "manage_your_devices": "Administre sus dispositivos conectados", "manage_your_oauth_connection": "Administra tu conexión OAuth", "map": "Mapa", - "map_assets_in_bound": "{count} foto", - "map_assets_in_bounds": "{count} fotos", + "map_assets_in_bounds": "{count, plural, one {# foto} other {# fotos}}", "map_cannot_get_user_location": "No se pudo obtener la posición del usuario", "map_location_dialog_yes": "Sí", "map_location_picker_page_use_location": "Usar esta ubicación", @@ -1317,6 +1337,7 @@ "no_results": "Sin resultados", "no_results_description": "Pruebe con un sinónimo o una palabra clave más general", "no_shared_albums_message": "Crea un álbum para compartir fotos y vídeos con personas de tu red", + "no_uploads_in_progress": "No hay cargas en progreso", "not_in_any_album": "Sin álbum", "not_selected": "No seleccionado", "note_apply_storage_label_to_previously_uploaded assets": "Nota: Para aplicar la etiqueta de almacenamiento a los archivos subidos previamente, ejecute el", @@ -1354,6 +1375,7 @@ "original": "original", "other": "Otro", "other_devices": "Otro dispositivo", + "other_entities": "Otras entidades", "other_variables": "Otras variables", "owned": "Propio", "owner": "Propietario", @@ -1485,6 +1507,7 @@ "purchase_server_description_2": "Estado del soporte", "purchase_server_title": "Servidor", "purchase_settings_server_activated": "La clave del producto del servidor la administra el administrador", + "queue_status": "Poniendo en cola {count}/{total}", "rating": "Valoración", "rating_clear": "Borrar calificación", "rating_count": "{count, plural, one {# estrella} other {# estrellas}}", @@ -1513,6 +1536,8 @@ "refreshing_faces": "Recargando caras", "refreshing_metadata": "Recargando metadatos", "regenerating_thumbnails": "Recargando miniaturas", + "remote": "Remoto", + "remote_assets": "Elementos remotos", "remove": "Eliminar", "remove_assets_album_confirmation": "¿Estás seguro que quieres eliminar {count, plural, one {# elemento} other {# elementos}} del álbum?", "remove_assets_shared_link_confirmation": "¿Estás seguro que quieres eliminar {count, plural, one {# elemento} other {# elementos}} del enlace compartido?", @@ -1550,19 +1575,25 @@ "reset_password": "Restablecer la contraseña", "reset_people_visibility": "Restablecer la visibilidad de las personas", "reset_pin_code": "Restablecer PIN", + "reset_sqlite": "Restablecer la Base de Datos SQLite", + "reset_sqlite_confirmation": "¿Estás seguro que deseas restablecer la base de datos SQLite? Deberás cerrar sesión y volver a iniciarla para resincronizar los datos", + "reset_sqlite_success": "Restablecer exitosamente la base de datos SQLite", "reset_to_default": "Restablecer los valores predeterminados", "resolve_duplicates": "Resolver duplicados", "resolved_all_duplicates": "Todos los duplicados resueltos", "restore": "Restaurar", "restore_all": "Restaurar todo", + "restore_trash_action_prompt": "{count} restaurado de la papelera", "restore_user": "Restaurar usuario", "restored_asset": "Archivo restaurado", "resume": "Continuar", "retry_upload": "Reintentar subida", "review_duplicates": "Revisar duplicados", + "review_large_files": "Revisar archivos grandes", "role": "Rol", "role_editor": "Editor", "role_viewer": "Visor", + "running": "En ejecución", "save": "Guardar", "save_to_gallery": "Guardado en la galería", "saved_api_key": "Clave API guardada", @@ -1716,6 +1747,7 @@ "shared_link_clipboard_copied_massage": "Copiado al portapapeles", "shared_link_clipboard_text": "Enlace: {link}\nContraseña: {password}", "shared_link_create_error": "Error creando el enlace compartido", + "shared_link_custom_url_description": "Accede a este enlace compartido con una URL personalizada", "shared_link_edit_description_hint": "Introduce la descripción del enlace", "shared_link_edit_expire_after_option_day": "1 día", "shared_link_edit_expire_after_option_days": "{count} días", @@ -1741,6 +1773,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Administrar enlaces compartidos", "shared_link_options": "Opciones de enlaces compartidos", + "shared_link_password_description": "Requerir una contraseña para acceder a este enlace compartido", "shared_links": "Enlaces compartidos", "shared_links_description": "Comparte fotos y vídeos con un enlace", "shared_photos_and_videos_count": "{assetCount, plural, other {# Fotos y vídeos compartidos.}}", @@ -1816,6 +1849,7 @@ "storage_quota": "Cuota de Almacenamiento", "storage_usage": "{used} de {available} en uso", "submit": "Enviar", + "success": "Éxito", "suggestions": "Sugerencias", "sunrise_on_the_beach": "Amanecer en la playa", "support": "Soporte", @@ -1825,6 +1859,8 @@ "sync": "Sincronizar", "sync_albums": "Sincronizar álbumes", "sync_albums_manual_subtitle": "Sincroniza todos los videos y fotos subidos con los álbumes seleccionados a respaldar", + "sync_local": "Sincronización Local", + "sync_remote": "Sincronización Remota", "sync_upload_album_setting_subtitle": "Crea y sube tus fotos y videos a los álbumes seleccionados en Immich", "tag": "Etiqueta", "tag_assets": "Etiquetar activos", @@ -1835,6 +1871,7 @@ "tag_updated": "Etiqueta actualizada: {tag}", "tagged_assets": "Etiquetado(s) {count, plural, one {# activo} other {# activos}}", "tags": "Etiquetas", + "tap_to_run_job": "Toca para ejecutar la tarea", "template": "Plantilla", "theme": "Tema", "theme_selection": "Selección de tema", @@ -1914,10 +1951,13 @@ "updated_at": "Actualizado", "updated_password": "Contraseña actualizada", "upload": "Subir", + "upload_action_prompt": "{count} en cola para carga", "upload_concurrency": "Subidas simultáneas", + "upload_details": "Cargar Detalles", "upload_dialog_info": "¿Quieres hacer una copia de seguridad al servidor de los elementos seleccionados?", "upload_dialog_title": "Subir elementos", "upload_errors": "Subida completada con {count, plural, one {# error} other {# errores}}, actualice la página para ver los nuevos recursos de la subida.", + "upload_finished": "Carga finalizada", "upload_progress": "Restante {remaining, number} - Procesado {processed, number}/{total, number}", "upload_skipped_duplicates": "Saltado {count, plural, one {# duplicate asset} other {# duplicate assets}}", "upload_status_duplicates": "Duplicados", @@ -1926,6 +1966,7 @@ "upload_success": "Subida realizada correctamente, actualice la página para ver los nuevos recursos de subida.", "upload_to_immich": "Subir a Immich ({count})", "uploading": "Subiendo", + "uploading_media": "Subiendo medios", "url": "URL", "usage": "Uso", "use_biometric": "Uso biométrico", @@ -1965,6 +2006,7 @@ "view_album": "Ver Álbum", "view_all": "Ver todas", "view_all_users": "Mostrar todos los usuarios", + "view_details": "Ver Detalles", "view_in_timeline": "Mostrar en la línea de tiempo", "view_link": "Ver enlace", "view_links": "Mostrar enlaces", diff --git a/i18n/et.json b/i18n/et.json index 1d293a3bdf..29bacae5c1 100644 --- a/i18n/et.json +++ b/i18n/et.json @@ -14,6 +14,7 @@ "add_a_location": "Lisa asukoht", "add_a_name": "Lisa nimi", "add_a_title": "Lisa pealkiri", + "add_birthday": "Lisa sünnipäev", "add_endpoint": "Lisa lõpp-punkt", "add_exclusion_pattern": "Lisa välistamismuster", "add_import_path": "Lisa imporditee", @@ -44,6 +45,13 @@ "backup_database": "Loo andmebaasi tõmmis", "backup_database_enable_description": "Luba andmebaasi tõmmised", "backup_keep_last_amount": "Eelmiste tõmmiste arv, mida alles hoida", + "backup_onboarding_1_description": "asukohaväline koopia pilves või teises füüsilises asukohas.", + "backup_onboarding_2_description": "lokaalset koopiat erinevatel seadmetel. See hõlmab põhifaile ja nende failide lokaalsed varundust.", + "backup_onboarding_3_description": "koopiat su andmetest, kaasa arvatud originaalfailid. See hõlmab üht asukohavälist ja kaht lokaalset koopiat.", + "backup_onboarding_description": "Andmete kaitsmiseks on soovituslik 3-2-1 varundusstrateegia. Põhjaliku varunduse jaoks peaksid talletama koopiaid nii oma üleslaaditud fotodest ja videotest kui ka Immich'i andmebaasist.", + "backup_onboarding_footer": "Rohkem informatsiooni Immich'i varundamise kohta leiad dokumentatsioonist.", + "backup_onboarding_parts_title": "3-2-1 varundus hõlmab:", + "backup_onboarding_title": "Varukoopiad", "backup_settings": "Andmebaasi tõmmiste seaded", "backup_settings_description": "Halda andmebaasi tõmmiste seadeid.", "cleared_jobs": "Tööted eemaldatud: {job}", @@ -397,6 +405,7 @@ "album_cover_updated": "Albumi kaanepilt muudetud", "album_delete_confirmation": "Kas oled kindel, et soovid albumi {album} kustutada?", "album_delete_confirmation_description": "Kui see album on jagatud, ei pääse teised kasutajad sellele enam ligi.", + "album_deleted": "Album kustutatud", "album_info_card_backup_album_excluded": "VÄLJA JÄETUD", "album_info_card_backup_album_included": "LISATUD", "album_info_updated": "Albumi info muudetud", @@ -510,6 +519,7 @@ "back_close_deselect": "Tagasi, sulge või tühista valik", "background_location_permission": "Taustal asukoha luba", "background_location_permission_content": "Et taustal töötades võrguühendust vahetada, peab Immich'il *alati* olema täpse asukoha luba, et rakendus saaks WiFi-võrgu nime lugeda", + "backup": "Varundamine", "backup_album_selection_page_albums_device": "Albumid seadmel ({count})", "backup_album_selection_page_albums_tap": "Puuduta kaasamiseks, topeltpuuduta välistamiseks", "backup_album_selection_page_assets_scatter": "Üksused võivad olla jaotatud mitme albumi vahel. Seega saab albumeid varundamise protsessi kaasata või välistada.", @@ -723,6 +733,7 @@ "current_server_address": "Praegune serveri aadress", "custom_locale": "Kohandatud lokaat", "custom_locale_description": "Vorminda kuupäevad ja arvud vastavalt keelele ja regioonile", + "custom_url": "Kohandatud URL", "daily_title_text_date": "d. MMMM", "daily_title_text_date_year": "d. MMMM yyyy", "dark": "Tume", @@ -742,7 +753,8 @@ "default_locale": "Vaikimisi lokaat", "default_locale_description": "Vorminda kuupäevad ja numbrid vastavalt brauseri lokaadile", "delete": "Kustuta", - "delete_action_prompt": "{count} jäädavalt kustutatud", + "delete_action_confirmation_message": "Kas oled kindel, et soovid selle üksuse kustutada? See toiming liigutab üksuse serveri prügikasti ja küsib, kas soovid selle lokaalselt kustutada", + "delete_action_prompt": "{count} kustutatud", "delete_album": "Kustuta album", "delete_api_key_prompt": "Kas oled kindel, et soovid selle API võtme kustutada?", "delete_dialog_alert": "Need üksused kustutatakse jäädavalt Immich'ist ja sinu seadmest", @@ -760,6 +772,8 @@ "delete_local_dialog_ok_backed_up_only": "Kustuta ainult varundatud", "delete_local_dialog_ok_force": "Kustuta sellegipoolest", "delete_others": "Kustuta teised", + "delete_permanently": "Kustuta jäädavalt", + "delete_permanently_action_prompt": "{count} jäädavalt kustutatud", "delete_shared_link": "Kustuta jagatud link", "delete_shared_link_dialog_title": "Kustuta jagatud link", "delete_tag": "Kustuta silt", @@ -815,6 +829,7 @@ "edit": "Muuda", "edit_album": "Muuda albumit", "edit_avatar": "Muuda avatari", + "edit_birthday": "Muuda sünnipäeva", "edit_date": "Muuda kuupäeva", "edit_date_and_time": "Muuda kuupäeva ja kellaaega", "edit_description": "Muuda kirjeldust", @@ -982,6 +997,7 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Lisa kirjeldus...", + "exif_bottom_sheet_description_error": "Viga kirjelduse muutmisel", "exif_bottom_sheet_details": "ÜKSIKASJAD", "exif_bottom_sheet_location": "ASUKOHT", "exif_bottom_sheet_people": "ISIKUD", @@ -1002,6 +1018,8 @@ "explorer": "Brauser", "export": "Ekspordi", "export_as_json": "Ekspordi JSON-formaati", + "export_database": "Ekspordi andmebaas", + "export_database_description": "Ekspordi SQLite andmebaas", "extension": "Laiend", "external": "Väline", "external_libraries": "Välised kogud", @@ -1146,6 +1164,7 @@ "language_no_results_title": "Ühtegi keelt ei leitud", "language_search_hint": "Otsi keeli...", "language_setting_description": "Vali oma eelistatud keel", + "large_files": "Suured failid", "last_seen": "Viimati nähtud", "latest_version": "Uusim versioon", "latitude": "Laiuskraad", @@ -1165,13 +1184,12 @@ "light": "Hele", "like_deleted": "Meeldimine kustutatud", "link_motion_video": "Lingi liikuv video", - "link_options": "Lingi valikud", "link_to_oauth": "Ühenda OAuth", "linked_oauth_account": "OAuth konto ühendatud", "list": "Loend", "loading": "Laadimine", "loading_search_results_failed": "Otsitulemuste laadimine ebaõnnestus", - "local": "Lokaalne üksus", + "local": "Lokaalsed", "local_asset_cast_failed": "Ei saa edastada üksust, mis pole serverisse üles laaditud", "local_assets": "Lokaalsed üksused", "local_network": "Kohalik võrk", @@ -1230,8 +1248,7 @@ "manage_your_devices": "Halda oma autenditud seadmeid", "manage_your_oauth_connection": "Halda oma OAuth ühendust", "map": "Kaart", - "map_assets_in_bound": "{count} foto", - "map_assets_in_bounds": "{count} fotot", + "map_assets_in_bounds": "{count, plural, one {# foto} other {# fotot}}", "map_cannot_get_user_location": "Ei saa kasutaja asukohta tuvastada", "map_location_dialog_yes": "Jah", "map_location_picker_page_use_location": "Kasuta seda asukohta", @@ -1529,7 +1546,7 @@ "refreshing_faces": "Nägude värskendamine", "refreshing_metadata": "Metaandmete värskendamine", "regenerating_thumbnails": "Pisipiltide uuesti genereerimine", - "remote": "Kaugüksus", + "remote": "Serveris", "remote_assets": "Kaugüksused", "remove": "Eemalda", "remove_assets_album_confirmation": "Kas oled kindel, et soovid {count, plural, one {# üksuse} other {# üksust}} albumist eemaldada?", @@ -1582,6 +1599,7 @@ "resume": "Jätka", "retry_upload": "Proovi üleslaadimist uuesti", "review_duplicates": "Vaata duplikaadid läbi", + "review_large_files": "Vaata suured failid läbi", "role": "Roll", "role_editor": "Muutja", "role_viewer": "Vaataja", @@ -1739,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "Kopeeritud lõikelauale", "shared_link_clipboard_text": "Link: {link}\nParool: {password}", "shared_link_create_error": "Viga jagatud lingi loomisel", + "shared_link_custom_url_description": "Ligipääs jagatud lingile kohandatud URL-i kaudu", "shared_link_edit_description_hint": "Sisesta jagatud lingi kirjeldus", "shared_link_edit_expire_after_option_day": "1 päev", "shared_link_edit_expire_after_option_days": "{count} päeva", @@ -1764,6 +1783,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Halda jagatud linke", "shared_link_options": "Jagatud lingi valikud", + "shared_link_password_description": "Nõua jagatud lingile ligi pääsemiseks parooli", "shared_links": "Jagatud lingid", "shared_links_description": "Jaga fotosid ja videosid lingiga", "shared_photos_and_videos_count": "{assetCount, plural, other {# jagatud fotot ja videot.}}", @@ -1941,11 +1961,13 @@ "updated_at": "Uuendatud", "updated_password": "Parool muudetud", "upload": "Laadi üles", + "upload_action_prompt": "{count} üleslaadimise ootel", "upload_concurrency": "Üleslaadimise samaaegsus", "upload_details": "Üleslaadimise üksikasjad", "upload_dialog_info": "Kas soovid valitud üksuse(d) serverisse varundada?", "upload_dialog_title": "Üksuse üleslaadimine", "upload_errors": "Üleslaadimine lõpetatud {count, plural, one {# veaga} other {# veaga}}, uute üksuste nägemiseks värskenda lehte.", + "upload_finished": "Üleslaadimine lõpetatud", "upload_progress": "Ootel {remaining, number} - Töödeldud {processed, number}/{total, number}", "upload_skipped_duplicates": "{count, plural, one {# dubleeritud üksus} other {# dubleeritud üksust}} vahele jäetud", "upload_status_duplicates": "Duplikaadid", @@ -1954,6 +1976,7 @@ "upload_success": "Üleslaadimine õnnestus, uute üksuste nägemiseks värskenda lehte.", "upload_to_immich": "Laadi Immich'isse ({count})", "uploading": "Üleslaadimine", + "uploading_media": "Meediumi üleslaadimine", "url": "URL", "usage": "Kasutus", "use_biometric": "Kasuta biomeetriat", diff --git a/i18n/fa.json b/i18n/fa.json index 84857479b3..5b463655b2 100644 --- a/i18n/fa.json +++ b/i18n/fa.json @@ -471,7 +471,6 @@ "library": "کتابخانه", "library_options": "گزینه‌های کتابخانه", "light": "روشن", - "link_options": "گزینه‌های لینک", "link_to_oauth": "اتصال به OAuth", "linked_oauth_account": "حساب OAuth متصل شده", "list": "لیست", @@ -493,7 +492,6 @@ "manage_your_devices": "مدیریت دستگاه‌های متصل", "manage_your_oauth_connection": "مدیریت اتصال OAuth", "map": "نقشه", - "map_assets_in_bound": "{count} عکس", "map_assets_in_bounds": "{count} عکس ها", "map_cannot_get_user_location": "موقعیت مکانی در دسترس نیست", "map_location_dialog_yes": "بله", diff --git a/i18n/fi.json b/i18n/fi.json index 4e12253cf2..73ce337de0 100644 --- a/i18n/fi.json +++ b/i18n/fi.json @@ -373,6 +373,7 @@ "admin_password": "Ylläpitäjän salasana", "administration": "Ylläpito", "advanced": "Edistyneet", + "advanced_settings_beta_timeline_subtitle": "Kokeile uutta sovelluskokemusta", "advanced_settings_enable_alternate_media_filter_subtitle": "Käytä tätä vaihtoehtoa suodattaaksesi mediaa synkronoinnin aikana vaihtoehtoisten kriteerien perusteella. Kokeile tätä vain, jos sovelluksessa on ongelmia kaikkien albumien tunnistamisessa.", "advanced_settings_enable_alternate_media_filter_title": "[KOKEELLINEN] Käytä vaihtoehtoisen laitteen albumin synkronointisuodatinta", "advanced_settings_log_level_title": "Kirjaustaso: {level}", @@ -506,6 +507,7 @@ "back_close_deselect": "Palaa, sulje tai poista valinnat", "background_location_permission": "Taustasijainnin käyttöoikeus", "background_location_permission_content": "Jotta sovellus voi vaihtaa verkkoa taustalla toimiessaan, Immichillä on *aina* oltava pääsy tarkkaan sijaintiin, jotta se voi lukea Wi-Fi-verkon nimen", + "backup": "Varmuuskopiointi", "backup_album_selection_page_albums_device": "Laitteen albumit ({count})", "backup_album_selection_page_albums_tap": "Napauta sisällyttääksesi, kaksoisnapauta jättääksesi pois", "backup_album_selection_page_assets_scatter": "Kohteet voivat olla hajaantuneina useisiin albumeihin. Albumeita voidaan sisällyttää varmuuskopiointiin tai jättää siitä pois.", @@ -1149,7 +1151,6 @@ "light": "Vaalea", "like_deleted": "Tykkäys poistettu", "link_motion_video": "Linkitä liikevideo", - "link_options": "Linkin asetukset", "link_to_oauth": "Linkki OAuth", "linked_oauth_account": "Linkitetty OAuth-tili", "list": "Lista", @@ -1212,7 +1213,6 @@ "manage_your_devices": "Hallitse sisäänkirjautuneita laitteitasi", "manage_your_oauth_connection": "Hallitse OAuth-yhteyttäsi", "map": "Kartta", - "map_assets_in_bound": "{count} kuva", "map_assets_in_bounds": "{count} kuvaa", "map_cannot_get_user_location": "Käyttäjän sijaintia ei voitu määrittää", "map_location_dialog_yes": "Kyllä", diff --git a/i18n/fr.json b/i18n/fr.json index ff9f783771..872c2e4984 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -397,6 +397,7 @@ "album_cover_updated": "Couverture de l'album mise à jour", "album_delete_confirmation": "Êtes-vous sûr de vouloir supprimer l'album {album} ?", "album_delete_confirmation_description": "Si cet album est partagé, les autres utilisateurs ne pourront plus y accéder.", + "album_deleted": "Album supprimé", "album_info_card_backup_album_excluded": "EXCLUS", "album_info_card_backup_album_included": "INCLUS", "album_info_updated": "Détails de l'album mis à jour", @@ -510,6 +511,7 @@ "back_close_deselect": "Retournez en arrière, fermez ou désélectionnez", "background_location_permission": "Permission de localisation en arrière plan", "background_location_permission_content": "Afin de pouvoir changer d'adresse en arrière plan, Immich doit avoir *en permanence* accès à la localisation précise, afin d'accéder au le nom du réseau Wi-Fi utilisé", + "backup": "Sauvegarde", "backup_album_selection_page_albums_device": "Albums sur l'appareil ({count})", "backup_album_selection_page_albums_tap": "Tapez pour inclure, tapez deux fois pour exclure", "backup_album_selection_page_assets_scatter": "Les éléments peuvent être répartis sur plusieurs albums. De ce fait, les albums peuvent être inclus ou exclus pendant le processus de sauvegarde.", @@ -562,7 +564,7 @@ "backup_controller_page_to_backup": "Albums à sauvegarder", "backup_controller_page_total_sub": "Toutes les photos et vidéos uniques des albums sélectionnés", "backup_controller_page_turn_off": "Désactiver la sauvegarde", - "backup_controller_page_turn_on": "Activer la sauvegarde", + "backup_controller_page_turn_on": "Activer la sauvegarde au premier plan", "backup_controller_page_uploading_file_info": "Envoi des informations du fichier", "backup_err_only_album": "Impossible de retirer le seul album", "backup_info_card_assets": "éléments", @@ -592,7 +594,7 @@ "cache_settings_clear_cache_button": "Effacer le cache", "cache_settings_clear_cache_button_title": "Efface le cache de l'application. Cela aura un impact significatif sur les performances de l'application jusqu'à ce que le cache soit reconstruit.", "cache_settings_duplicated_assets_clear_button": "EFFACER", - "cache_settings_duplicated_assets_subtitle": "Photos et vidéos qui sont exclues par l'application", + "cache_settings_duplicated_assets_subtitle": "Photos et vidéos qui sont ignorées par l'application", "cache_settings_duplicated_assets_title": "Médias dupliqués ({count})", "cache_settings_statistics_album": "Miniatures de la bibliothèque", "cache_settings_statistics_full": "Images complètes", @@ -723,6 +725,7 @@ "current_server_address": "Adresse actuelle du serveur", "custom_locale": "Paramètres régionaux personnalisés", "custom_locale_description": "Afficher les dates et nombres en fonction des paramètres régionaux", + "custom_url": "URL personnalisée", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Sombre", @@ -742,7 +745,8 @@ "default_locale": "Région par défaut", "default_locale_description": "Afficher les dates et nombres en fonction des paramètres de votre navigateur", "delete": "Supprimer", - "delete_action_prompt": "{count} supprimé(s) définitivement", + "delete_action_confirmation_message": "Êtes-vous sûr de vouloir supprimer ce média ? Cela déplacera le média dans la poubelle du serveur et vous demandera si vous voulez le supprimer localement", + "delete_action_prompt": "{count} supprimé(s)", "delete_album": "Supprimer l'album", "delete_api_key_prompt": "Voulez-vous vraiment supprimer cette clé API ?", "delete_dialog_alert": "Ces médias seront définitivement supprimés de Immich et de votre appareil", @@ -760,6 +764,8 @@ "delete_local_dialog_ok_backed_up_only": "Suppression des données sauvegardées uniquement", "delete_local_dialog_ok_force": "Supprimer tout de même", "delete_others": "Supprimer les autres", + "delete_permanently": "Supprimer définitivement", + "delete_permanently_action_prompt": "{count} supprimé(s) définitivement", "delete_shared_link": "Supprimer le lien partagé", "delete_shared_link_dialog_title": "Supprimer le lien partagé", "delete_tag": "Supprimer l'étiquette", @@ -845,11 +851,11 @@ "empty_trash": "Vider la corbeille", "empty_trash_confirmation": "Êtes-vous sûr de vouloir vider la corbeille ? Cela supprimera définitivement de Immich tous les médias qu'elle contient.\nVous ne pouvez pas annuler cette action !", "enable": "Active", - "enable_backup": "Activer Backup", + "enable_backup": "Activer la sauvegarde", "enable_biometric_auth_description": "Entrez votre code PIN pour activer l'authentification biométrique", "enabled": "Activé", "end_date": "Date de fin", - "enqueued": "Mis en file", + "enqueued": "Mis en file d'attente", "enter_wifi_name": "Entrez le nom du réseau wifi", "enter_your_pin_code": "Entrez votre code PIN", "enter_your_pin_code_subtitle": "Entrez votre code PIN pour accéder au dossier verrouillé", @@ -1002,6 +1008,8 @@ "explorer": "Explorateur", "export": "Exporter", "export_as_json": "Exporter en JSON", + "export_database": "Exporter la base de données", + "export_database_description": "Exporter la base de données SQLite", "extension": "Extension", "external": "Externe", "external_libraries": "Bibliothèques externes", @@ -1088,6 +1096,7 @@ "host": "Hôte", "hour": "Heure", "id": "ID", + "idle": "Inactif", "ignore_icloud_photos": "Ignorer les photos iCloud", "ignore_icloud_photos_description": "Les photos stockées sur iCloud ne seront pas envoyées sur le serveur Immich", "image": "Image", @@ -1145,6 +1154,7 @@ "language_no_results_title": "Aucune langue trouvée", "language_search_hint": "Recherche de langues...", "language_setting_description": "Sélectionnez votre langue préférée", + "large_files": "Fichiers volumineux", "last_seen": "Dernièrement utilisé", "latest_version": "Dernière version", "latitude": "Latitude", @@ -1164,7 +1174,6 @@ "light": "Clair", "like_deleted": "Réaction « j'aime » supprimée", "link_motion_video": "Lier la photo animée", - "link_options": "Options de lien", "link_to_oauth": "Lien au service OAuth", "linked_oauth_account": "Compte OAuth rattaché", "list": "Liste", @@ -1229,8 +1238,7 @@ "manage_your_devices": "Gérer vos appareils", "manage_your_oauth_connection": "Gérer votre connexion OAuth", "map": "Carte", - "map_assets_in_bound": "{count} photo", - "map_assets_in_bounds": "{count} photos", + "map_assets_in_bounds": "{count, plural, one {# photo} other {# photos}}", "map_cannot_get_user_location": "Impossible d'obtenir la localisation de l'utilisateur", "map_location_dialog_yes": "Oui", "map_location_picker_page_use_location": "Utiliser ma position", @@ -1499,7 +1507,7 @@ "purchase_server_description_2": "Statut de contributeur", "purchase_server_title": "Serveur", "purchase_settings_server_activated": "La clé du produit pour le Serveur est gérée par l'administrateur", - "queue_status": "File d'attente {count}/{total}", + "queue_status": "{count}/{total} en file d'attente", "rating": "Étoile d'évaluation", "rating_clear": "Effacer l'évaluation", "rating_count": "{count, plural, one {# étoile} other {# étoiles}}", @@ -1528,7 +1536,7 @@ "refreshing_faces": "Actualisation des visages", "refreshing_metadata": "Actualisation des métadonnées", "regenerating_thumbnails": "Regénération des miniatures", - "remote": "A distance", + "remote": "À distance", "remote_assets": "Média à distance", "remove": "Supprimer", "remove_assets_album_confirmation": "Êtes-vous sûr de vouloir supprimer {count, plural, one {# média} other {# médias}} de l'album ?", @@ -1568,7 +1576,7 @@ "reset_people_visibility": "Réinitialiser la visibilité des personnes", "reset_pin_code": "Réinitialiser le code PIN", "reset_sqlite": "Réinitialiser la base de données SQLite", - "reset_sqlite_confirmation": "Êtes vous sur que vous voulez réinitialiser la base de données SQLite ? Vous devrez vous déconnecter and vous reconnecter à nouveau pour re-synchroniser les données", + "reset_sqlite_confirmation": "Êtes-vous certain de vouloir réinitialiser la base de données SQLite ? Vous devrez vous déconnecter puis vous reconnecter à nouveau pour resynchroniser les données", "reset_sqlite_success": "La base de données SQLite à été réinitialisé avec succès", "reset_to_default": "Rétablir les valeurs par défaut", "resolve_duplicates": "Résoudre les doublons", @@ -1581,10 +1589,11 @@ "resume": "Reprendre", "retry_upload": "Réessayer l'envoi", "review_duplicates": "Consulter les doublons", + "review_large_files": "Consulter les fichiers volumineux", "role": "Rôle", "role_editor": "Éditeur", "role_viewer": "Visionneuse", - "running": "En marche", + "running": "En cours", "save": "Sauvegarder", "save_to_gallery": "Enregistrer", "saved_api_key": "Clé API sauvegardée", @@ -1738,6 +1747,7 @@ "shared_link_clipboard_copied_massage": "Copié dans le presse-papier", "shared_link_clipboard_text": "Lien : {link}\nMot de passe : {password}", "shared_link_create_error": "Erreur pendant la création du lien partagé", + "shared_link_custom_url_description": "Accéder à ce lien partagé avec une URL personnalisée", "shared_link_edit_description_hint": "Saisir la description du partage", "shared_link_edit_expire_after_option_day": "1 jour", "shared_link_edit_expire_after_option_days": "{count} jours", @@ -1763,6 +1773,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Gérer les liens partagés", "shared_link_options": "Options de lien partagé", + "shared_link_password_description": "Demander un mot de passe pour accéder à ce lien partagé", "shared_links": "Liens partagés", "shared_links_description": "Partager les photos et vidéos via un lien", "shared_photos_and_videos_count": "{assetCount, plural, other {# photos et vidéos partagées.}}", @@ -1940,11 +1951,13 @@ "updated_at": "Mis à jour à", "updated_password": "Mot de passe mis à jour", "upload": "Envoyer", + "upload_action_prompt": "{count} en attente d'envoi", "upload_concurrency": "Envois simultanés", - "upload_details": "Uploader les details", + "upload_details": "Détails des envois", "upload_dialog_info": "Voulez-vous sauvegarder la sélection vers le serveur ?", "upload_dialog_title": "Envoyer le média", "upload_errors": "L'envoi s'est complété avec {count, plural, one {# erreur} other {# erreurs}}. Rafraîchissez la page pour voir les nouveaux médias envoyés.", + "upload_finished": "Envoi fini", "upload_progress": "{remaining, number} restant(s) - {processed, number} traité(s)/{total, number}", "upload_skipped_duplicates": "{count, plural, one {# doublon ignoré} other {# doublons ignorés}}", "upload_status_duplicates": "Doublons", @@ -1953,6 +1966,7 @@ "upload_success": "Envoi réussi. Rafraîchissez la page pour voir les nouveaux médias envoyés.", "upload_to_immich": "Envoyer vers Immich ({count})", "uploading": "Envoi", + "uploading_media": "Envoi du média", "url": "URL", "usage": "Utilisation", "use_biometric": "Utiliser l'authentification biométrique", diff --git a/i18n/gl.json b/i18n/gl.json index 70146abb53..92b3c8fb13 100644 --- a/i18n/gl.json +++ b/i18n/gl.json @@ -476,6 +476,7 @@ "back_close_deselect": "Atrás, pechar ou deseleccionar", "background_location_permission": "Permiso de ubicación en segundo plano", "background_location_permission_content": "Para cambiar de rede cando se executa en segundo plano, Immich debe ter *sempre* acceso á ubicación precisa para que a aplicación poida ler o nome da rede wifi", + "backup": "Copia de Seguridade", "backup_album_selection_page_albums_device": "Álbums no dispositivo ({count})", "backup_album_selection_page_albums_tap": "Tocar para incluír, dobre toque para excluír", "backup_album_selection_page_assets_scatter": "Os activos poden dispersarse por varios álbums. Polo tanto, os álbums poden incluírse ou excluírse durante o proceso de copia de seguridade.", @@ -1070,7 +1071,6 @@ "light": "Claro", "like_deleted": "Gústame eliminado", "link_motion_video": "Ligar vídeo en movemento", - "link_options": "Opcións da ligazón", "link_to_oauth": "Ligar a OAuth", "linked_oauth_account": "Conta OAuth ligada", "list": "Lista", @@ -1129,7 +1129,6 @@ "manage_your_devices": "Xestionar os teus dispositivos con sesión iniciada", "manage_your_oauth_connection": "Xestionar a túa conexión OAuth", "map": "Mapa", - "map_assets_in_bound": "{count} foto", "map_assets_in_bounds": "{count} fotos", "map_cannot_get_user_location": "Non se pode obter a ubicación do usuario", "map_location_dialog_yes": "Si", diff --git a/i18n/he.json b/i18n/he.json index 49973dbc63..3139c2cde6 100644 --- a/i18n/he.json +++ b/i18n/he.json @@ -14,6 +14,7 @@ "add_a_location": "הוספת מיקום", "add_a_name": "הוספת שם", "add_a_title": "הוספת כותרת", + "add_birthday": "הוספת יום הולדת", "add_endpoint": "הוסף נקודת קצה", "add_exclusion_pattern": "הוספת דפוס החרגה", "add_import_path": "הוספת נתיב יבוא", @@ -44,6 +45,13 @@ "backup_database": "גיבוי מסד נתונים", "backup_database_enable_description": "אפשר גיבויי מסד נתונים", "backup_keep_last_amount": "כמות של גיבויים קודמים שיש לשמור", + "backup_onboarding_1_description": "העתק בענן או במיקום פיזי אחר מחוץ למקום השרת.", + "backup_onboarding_2_description": "העתקים מקומיים במכשירים שונים. זה כולל את הקבצים הראשיים וגיבוי של הקבצים האלה באופן מקומי.", + "backup_onboarding_3_description": "סך כל ההעתקים של הנתונים שלך, כולל הקבצים המקוריים. זה כולל העתק אחד מחוץ למקום השרת ושני העתקים מקומיים.", + "backup_onboarding_description": "אסטרטגיית גיבוי 3-2-1 הינה מומלצת על מנת להגן על הנתונים שלך. עליך להשאיר העתקים של תמונות/סרטונים שהועלו כמו גם את מסד הנתונים של Immich עבור פתרון גיבוי מקיף.", + "backup_onboarding_footer": "עבור מידע נוסף על גיבוי Immich, נא לפנות אל התיעוד.", + "backup_onboarding_parts_title": "גיבוי 3-2-1 כולל:", + "backup_onboarding_title": "גיבויים", "backup_settings": "הגדרות גיבוי", "backup_settings_description": "ניהול הגדרות גיבוי מסד נתונים.", "cleared_jobs": "נוקו משימות עבור: {job}", @@ -156,15 +164,15 @@ "map_settings": "מפה", "map_settings_description": "ניהול הגדרות מפה", "map_style_description": "כתובת אתר לערכת נושא של מפה style.json", - "memory_cleanup_job": "ניקוי זיכרון", - "memory_generate_job": "יצירת זיכרון", + "memory_cleanup_job": "ניקוי זיכרון (היום לפני..)", + "memory_generate_job": "יצירת זיכרון (היום לפני..)", "metadata_extraction_job": "חלץ מטא-נתונים", "metadata_extraction_job_description": "חלץ מטא-נתונים מכל תמונה, כגון GPS, פנים ורזולוציה", "metadata_faces_import_setting": "אפשר יבוא פנים", "metadata_faces_import_setting_description": "יבא פנים מנתוני EXIF של תמונה ומקבצים נלווים", "metadata_settings": "הגדרות מטא-נתונים", - "metadata_settings_description": "ניהול הגדרות מטא-נתונים", - "migration_job": "העברה", + "metadata_settings_description": "ניהול הגדרות metadata", + "migration_job": "נדידה", "migration_job_description": "העבר תמונות ממוזערות של תמונות ופנים למבנה התיקיות העדכני ביותר", "nightly_tasks_cluster_faces_setting_description": "בצע זיהוי פנים עבור פרצופים שזוהו לאחרונה", "nightly_tasks_cluster_new_faces_setting": "קבץ פנים חדשות", @@ -178,7 +186,7 @@ "nightly_tasks_settings_description": "נהל משימות ליליות", "nightly_tasks_start_time_setting": "זמן התחלה", "nightly_tasks_start_time_setting_description": "השעה שבה השרת מתחיל להריץ את המשימות הליליות", - "nightly_tasks_sync_quota_usage_setting": "סנכרן את השימוש באחסון", + "nightly_tasks_sync_quota_usage_setting": "סנכרון מכסת שימוש", "nightly_tasks_sync_quota_usage_setting_description": "עדכן את מכסת האחסון של המשתמש בהתאם לשימוש הנוכחי", "no_paths_added": "לא נוספו נתיבים", "no_pattern_added": "לא נוספה תבנית", @@ -210,6 +218,8 @@ "oauth_mobile_redirect_uri": "URI להפניה מחדש בנייד", "oauth_mobile_redirect_uri_override": "עקיפת URI להפניה מחדש בנייד", "oauth_mobile_redirect_uri_override_description": "אפשר כאשר ספק OAuth לא מאפשר כתובת URI לנייד, כמו ''{callback}''", + "oauth_role_claim": "דרישת תפקיד", + "oauth_role_claim_description": "הענק גישת מנהל באופן אוטומטי אם תביעה זו קיימת. ערך התביעה יכול להיות 'user' או 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "ניהול הגדרות התחברות עם OAuth", "oauth_settings_more_details": "למידע נוסף אודות תכונה זו, בדוק את התיעוד.", @@ -371,10 +381,12 @@ "admin_password": "סיסמת מנהל", "administration": "ניהול", "advanced": "מתקדם", + "advanced_settings_beta_timeline_subtitle": "נסה את חווית האפליקציה החדשה", + "advanced_settings_beta_timeline_title": "ציר זמן (בטא)", "advanced_settings_enable_alternate_media_filter_subtitle": "השתמש באפשרות זו כדי לסנן מדיה במהלך הסנכרון לפי קריטריונים חלופיים. מומלץ להשתמש בזה רק אם יש בעיה בזיהוי כל האלבומים באפליקציה.", "advanced_settings_enable_alternate_media_filter_title": "[ניסיוני] השתמש במסנן סנכרון אלבום חלופי שמבכשיר", "advanced_settings_log_level_title": "רמת רישום ביומן: {level}", - "advanced_settings_prefer_remote_subtitle": "חלק מהמכשירים הם איטיים מאד לטעינה של תמונות ממוזערות מתמונות שבמכשיר. הפעל הגדרה זו כדי לטעון תמונות מרוחקות במקום.", + "advanced_settings_prefer_remote_subtitle": "במכשירים מסוימים טעינת תמונות ממוזערות מקבצים מקומיים עלולה להיות איטית במיוחד. הפעל הגדרה זו כדי לטעון תמונות מרוחקות במקום זאת.", "advanced_settings_prefer_remote_title": "העדף תמונות מרוחקות", "advanced_settings_proxy_headers_subtitle": "הגדר proxy headers שהיישום צריך לשלוח עם כל בקשת רשת", "advanced_settings_proxy_headers_title": "כותרות פרוקסי", @@ -393,6 +405,7 @@ "album_cover_updated": "עטיפת האלבום עודכנה", "album_delete_confirmation": "האם באמת ברצונך למחוק את האלבום {album}?", "album_delete_confirmation_description": "אם האלבום הזה משותף, משתמשים אחרים לא יוכלו לגשת אליו יותר.", + "album_deleted": "אלבום נמחק", "album_info_card_backup_album_excluded": "הוחרגו", "album_info_card_backup_album_included": "נכללו", "album_info_updated": "מידע האלבום עודכן", @@ -402,6 +415,7 @@ "album_options": "אפשרויות האלבום", "album_remove_user": "להסיר משתמש?", "album_remove_user_confirmation": "האם באמת ברצונך להסיר את {user}?", + "album_search_not_found": "לא נמצאו אלבומים התואמים לחיפוש שלך", "album_share_no_users": "נראה ששיתפת את האלבום הזה עם כל המשתמשים או שאין לך אף משתמש לשתף איתו.", "album_updated": "אלבום עודכן", "album_updated_setting_description": "קבל הודעת דוא\"ל כאשר לאלבום משותף יש תמונות חדשות", @@ -421,6 +435,7 @@ "albums_default_sort_order": "סדר מיון אלבומים ברירת מחדל", "albums_default_sort_order_description": "סדר מיון תמונות ראשוני בעת יצירת אלבומים חדשים.", "albums_feature_description": "אוספים של תמונות אשר ניתנים לשיתוף עם משתמשים אחרים.", + "albums_on_device_count": "אלבומים במכשיר ({count})", "all": "הכל", "all_albums": "כל האלבומים", "all_people": "כל האנשים", @@ -504,6 +519,7 @@ "back_close_deselect": "חזור, סגור, או בטל בחירה", "background_location_permission": "הרשאת מיקום ברקע", "background_location_permission_content": "כדי להחליף רשתות בעת ריצה ברקע, היישום צריך *תמיד* גישה למיקום מדויק על מנת לקרוא את השם של רשת האינטרנט האלחוטי", + "backup": "גיבוי", "backup_album_selection_page_albums_device": "({count}) אלבומים במכשיר", "backup_album_selection_page_albums_tap": "הקש כדי לכלול, הקש פעמיים כדי להחריג", "backup_album_selection_page_assets_scatter": "תמונות יכולות להתפזר על פני אלבומים מרובים. לפיכך, ניתן לכלול או להחריג אלבומים במהלך תהליך הגיבוי.", @@ -548,11 +564,11 @@ "backup_controller_page_none_selected": "אין בחירה", "backup_controller_page_remainder": "בהמתנה לגיבוי", "backup_controller_page_remainder_sub": "תמונות וסרטונים הנותרים לגיבוי מתוך בחירה", - "backup_controller_page_server_storage": "אחסון שרת", + "backup_controller_page_server_storage": "אחסון בשרת", "backup_controller_page_start_backup": "התחל גיבוי", "backup_controller_page_status_off": "גיבוי חזית אוטומטי כבוי", "backup_controller_page_status_on": "גיבוי חזית אוטומטי מופעל", - "backup_controller_page_storage_format": "{total} מתוך {used} בשימוש", + "backup_controller_page_storage_format": "{used}מתוך {total} בשימוש", "backup_controller_page_to_backup": "אלבומים לגבות", "backup_controller_page_total_sub": "כל התמונות והסרטונים הייחודיים מאלבומים שנבחרו", "backup_controller_page_turn_off": "כיבוי גיבוי חזית", @@ -567,6 +583,8 @@ "backup_options_page_title": "אפשרויות גיבוי", "backup_setting_subtitle": "ניהול הגדרות העלאת רקע וחזית", "backward": "אחורה", + "beta_sync": "סטטוס סנכרון (בטא)", + "beta_sync_subtitle": "נהל את מערכת הסנכרון החדשה", "biometric_auth_enabled": "אימות ביומטרי הופעל", "biometric_locked_out": "גישה לאימות הביומטרי נחסמה", "biometric_no_options": "אין אפשרויות זמינות עבור אימות ביומטרי", @@ -584,7 +602,7 @@ "cache_settings_clear_cache_button": "ניקוי מטמון", "cache_settings_clear_cache_button_title": "מנקה את המטמון של היישום. זה ישפיע באופן משמעותי על הביצועים של היישום עד שהמטמון מתמלא מחדש.", "cache_settings_duplicated_assets_clear_button": "נקה", - "cache_settings_duplicated_assets_subtitle": "תמונות וסרטונים שנמצאים ברשימה השחורה של היישום", + "cache_settings_duplicated_assets_subtitle": "תמונות וסרטונים שנמצאים ברשימת ההתעלמות של האפליקציה", "cache_settings_duplicated_assets_title": "({count}) תמונות משוכפלות", "cache_settings_statistics_album": "תמונות ממוזערות של ספרייה", "cache_settings_statistics_full": "תמונות מלאות", @@ -601,6 +619,7 @@ "cancel": "ביטול", "cancel_search": "ביטול חיפוש", "canceled": "בוטל", + "canceling": "מבטל", "cannot_merge_people": "לא ניתן למזג אנשים", "cannot_undo_this_action": "אין באפשרותך לבטל את הפעולה הזו!", "cannot_update_the_description": "לא ניתן לעדכן את התיאור", @@ -714,6 +733,7 @@ "current_server_address": "כתובת שרת נוכחית", "custom_locale": "אזור שפה מותאם אישית", "custom_locale_description": "עצב תאריכים ומספרים על סמך השפה והאזור", + "custom_url": "קישור מותאם אישית", "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "כהה", @@ -733,7 +753,8 @@ "default_locale": "שפת ברירת מחדל", "default_locale_description": "פורמט תאריכים ומספרים מבוסס שפת הדפדפן שלך", "delete": "מחק", - "delete_action_prompt": "{count} נמחקו לצמיתות", + "delete_action_confirmation_message": "האם אתה בטוח שברצונך למחוק את התמונה הזאת? פעולה זו תעביר אותו לאשפה של השרת, ותשאל אם ברצונך למחוק אותו גם מהמכשיר המקומי", + "delete_action_prompt": "{count} נמחקו", "delete_album": "מחק אלבום", "delete_api_key_prompt": "האם אתה בטוח שברצונך למחוק מפתח ה-API הזה?", "delete_dialog_alert": "הפריטים האלה ימחקו לצמיתות מהשרת ומהמכשיר שלך", @@ -747,9 +768,12 @@ "delete_key": "מחק מפתח", "delete_library": "מחק ספרייה", "delete_link": "מחק קישור", + "delete_local_action_prompt": "{count} נמחקו באופן מקומי", "delete_local_dialog_ok_backed_up_only": "מחק את מה שמגובה בלבד", "delete_local_dialog_ok_force": "מחק בכל זאת", "delete_others": "מחק אחרים", + "delete_permanently": "מחק לצמיתות", + "delete_permanently_action_prompt": "{count} נמחקו לצמיתות", "delete_shared_link": "מחק קישור משותף", "delete_shared_link_dialog_title": "מחק קישור משותף", "delete_tag": "מחק תג", @@ -760,6 +784,7 @@ "description": "תיאור", "description_input_hint_text": "הוסף תיאור...", "description_input_submit_error": "שגיאה בעדכון תיאור, בדוק את היומן לפרטים נוספים", + "deselect_all": "בטל הכל", "details": "פרטים", "direction": "כיוון", "disabled": "מושבת", @@ -777,6 +802,7 @@ "documentation": "תיעוד", "done": "סיום", "download": "הורדה", + "download_action_prompt": "מוריד {count} תמונות", "download_canceled": "הורדה בוטלה", "download_complete": "הורדה הושלמה", "download_enqueue": "הורדה נוספה לתור", @@ -803,6 +829,7 @@ "edit": "ערוך", "edit_album": "ערוך אלבום", "edit_avatar": "ערוך תמונת פרופיל", + "edit_birthday": "עריכת יום הולדת", "edit_date": "ערוך תאריך", "edit_date_and_time": "ערוך תאריך ושעה", "edit_description": "ערוך תיאור", @@ -833,6 +860,7 @@ "empty_trash": "רוקן אשפה", "empty_trash_confirmation": "האם באמת ברצונך לרוקן את האשפה? זה יסיר לצמיתות את כל התמונות מהאשפה של השרת.\nאין באפשרותך לבטל פעולה זו!", "enable": "אפשר", + "enable_backup": "הפעל גיבוי", "enable_biometric_auth_description": "הזן את קוד ה־PIN שלך כדי להפעיל אימות ביומטרי", "enabled": "מופעל", "end_date": "תאריך סיום", @@ -969,6 +997,7 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "הוסף תיאור...", + "exif_bottom_sheet_description_error": "שגיאה בעדכון התיאור", "exif_bottom_sheet_details": "פרטים", "exif_bottom_sheet_location": "מיקום", "exif_bottom_sheet_people": "אנשים", @@ -989,6 +1018,8 @@ "explorer": "סייר", "export": "ייצוא", "export_as_json": "ייצוא כ-JSON", + "export_database": "ייצא מסד נתונים", + "export_database_description": "ייצא מסד נתונים SQL", "extension": "סיומת", "external": "חיצוני", "external_libraries": "ספריות חיצוניות", @@ -1040,6 +1071,9 @@ "haptic_feedback_switch": "אפשר משוב ברטט", "haptic_feedback_title": "משוב ברטט", "has_quota": "יש מכסה", + "hash_asset": "גיבוב תמונה", + "hashed_assets": "תמונות מגובבות", + "hashing": "מגבב", "header_settings_add_header_tip": "הוסף כותרת", "header_settings_field_validator_msg": "ערך אינו יכול להיות ריק", "header_settings_header_name_input": "שם כותרת", @@ -1072,6 +1106,7 @@ "host": "מארח", "hour": "שעה", "id": "מזהה", + "idle": "ממתין", "ignore_icloud_photos": "התעלם מתמונות iCloud", "ignore_icloud_photos_description": "תמונות שמאוחסנות ב-iCloud לא יועלו לשרת", "image": "תמונה", @@ -1129,6 +1164,7 @@ "language_no_results_title": "לא נמצאה שפה", "language_search_hint": "חפש שפות...", "language_setting_description": "בחר את השפה המועדפת עליך", + "large_files": "קבצים גדולים", "last_seen": "נראה לאחרונה", "latest_version": "גרסה עדכנית ביותר", "latitude": "קו רוחב", @@ -1144,16 +1180,18 @@ "library_page_sort_created": "תאריך יצירה", "library_page_sort_last_modified": "שונה לאחרונה", "library_page_sort_title": "כותרת אלבום", + "licenses": "רישיונות", "light": "בהיר", "like_deleted": "לייק נמחק", "link_motion_video": "קשר סרטון תנועה", - "link_options": "אפשרויות קישור", "link_to_oauth": "קישור ל-OAuth", "linked_oauth_account": "חשבון OAuth מקושר", "list": "רשימה", "loading": "טוען", "loading_search_results_failed": "טעינת תוצאות החיפוש נכשלה", + "local": "מקומי", "local_asset_cast_failed": "לא ניתן לשדר תמונה שלא הועלתה לשרת", + "local_assets": "תמונות מקומיות", "local_network": "רשת מקומית", "local_network_sheet_info": "היישום יתחבר לשרת דרך הכתובת הזאת כאשר משתמשים ברשת האינטרנט האלחוטי שמצוינת", "location_permission": "הרשאת מיקום", @@ -1210,8 +1248,7 @@ "manage_your_devices": "ניהול המכשירים המחוברים שלך", "manage_your_oauth_connection": "ניהול חיבור ה-OAuth שלך", "map": "מפה", - "map_assets_in_bound": "תמונה {count}", - "map_assets_in_bounds": "{count} תמונות", + "map_assets_in_bounds": "{count, plural, one {תמונה #} other {# תמונות}}", "map_cannot_get_user_location": "לא ניתן לקבוע את מיקום המשתמש", "map_location_dialog_yes": "כן", "map_location_picker_page_use_location": "השתמש במיקום הזה", @@ -1245,7 +1282,7 @@ "memories_setting_description": "נהל את מה שרואים בזכרונות שלך", "memories_start_over": "התחל מחדש", "memories_swipe_to_close": "החלק למעלה כדי לסגור", - "memory": "זיכרון", + "memory": "זיכרון (היום לפני..)", "memory_lane_title": "משעול הזיכרונות {title}", "menu": "תפריט", "merge": "מזג", @@ -1310,6 +1347,7 @@ "no_results": "אין תוצאות", "no_results_description": "נסה להשתמש במילה נרדפת או במילת מפתח יותר כללית", "no_shared_albums_message": "צור אלבום כדי לשתף תמונות וסרטונים עם אנשים ברשת שלך", + "no_uploads_in_progress": "אין העלאות בתהליך", "not_in_any_album": "לא בשום אלבום", "not_selected": "לא נבחרו", "note_apply_storage_label_to_previously_uploaded assets": "הערה: כדי להחיל את תווית האחסון על תמונות שהועלו בעבר, הפעל את", @@ -1347,6 +1385,7 @@ "original": "מקורי", "other": "אחר", "other_devices": "מכשירים אחרים", + "other_entities": "ישויות אחרות", "other_variables": "משתנים אחרים", "owned": "בבעלות", "owner": "בעלים", @@ -1478,6 +1517,7 @@ "purchase_server_description_2": "מעמד תומך", "purchase_server_title": "שרת", "purchase_settings_server_activated": "מפתח המוצר של השרת מנוהל על ידי מנהל המערכת", + "queue_status": "בתור {count}/{total}", "rating": "דירוג כוכב", "rating_clear": "נקה דירוג", "rating_count": "{count, plural, one {כוכב #} other {# כוכבים}}", @@ -1506,6 +1546,8 @@ "refreshing_faces": "מרענן פרצופים", "refreshing_metadata": "מרענן מטא-נתונים", "regenerating_thumbnails": "מחדש תמונות ממוזערות", + "remote": "מרוחק", + "remote_assets": "תמונות מרוחקות", "remove": "הסר", "remove_assets_album_confirmation": "האם באמת ברצונך להסיר {count, plural, one {תמונה #} other {# תמונות}} מהאלבום?", "remove_assets_shared_link_confirmation": "האם אתה בטוח שברצונך להסיר {count, plural, one {תמונה #} other {# תמונות}} מהקישור המשותף הזה?", @@ -1543,19 +1585,25 @@ "reset_password": "איפוס סיסמה", "reset_people_visibility": "אפס את נראות האנשים", "reset_pin_code": "אפס קוד PIN", + "reset_sqlite": "אפס את מסד הנתונים SQLite", + "reset_sqlite_confirmation": "האם אתה בטוח שברצונך לאפס את מסד הנתונים SQLite? יהיה עליך להתנתק ולהתחבר מחדש כדי לסנכרן את הנתונים מחדש", + "reset_sqlite_success": "איפוס מסד הנתונים SQLite בוצע בהצלחה", "reset_to_default": "אפס לברירת מחדל", "resolve_duplicates": "פתור כפילויות", "resolved_all_duplicates": "כל הכפילויות נפתרו", "restore": "שחזר", "restore_all": "שחזר הכל", + "restore_trash_action_prompt": "{count} שוחזרו מהשאפה", "restore_user": "שחזר משתמש", "restored_asset": "התמונה שוחזרה", "resume": "המשך", "retry_upload": "נסה שוב להעלות", "review_duplicates": "בדוק כפילויות", + "review_large_files": "צפייה בקבצים גדולים", "role": "תפקיד", "role_editor": "עורך", "role_viewer": "צופה", + "running": "פועל", "save": "שמור", "save_to_gallery": "שמור לגלריה", "saved_api_key": "מפתח API שמור", @@ -1687,6 +1735,7 @@ "settings_saved": "ההגדרות נשמרו", "setup_pin_code": "הגדר קוד PIN", "share": "שתף", + "share_action_prompt": "שותפו {count} תמונות", "share_add_photos": "הוסף תמונות", "share_assets_selected": "{count} נבחרו", "share_dialog_preparing": "מכין...", @@ -1708,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "הועתק ללוח", "shared_link_clipboard_text": "קישור: {password}\nסיסמה: {link}", "shared_link_create_error": "שגיאה ביצירת קישור משותף", + "shared_link_custom_url_description": "גש לקישור ששותף באמצעות כתובת URL מותאמת אישית", "shared_link_edit_description_hint": "הכנס את תיאור השיתוף", "shared_link_edit_expire_after_option_day": "1 יום", "shared_link_edit_expire_after_option_days": "{count} ימים", @@ -1733,6 +1783,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "ניהול קישורים משותפים", "shared_link_options": "אפשרויות קישור משותף", + "shared_link_password_description": "דרוש סיסמה כדי לגשת לקישור המשותף הזה", "shared_links": "קישורים משותפים", "shared_links_description": "שתף תמונות וסרטונים עם קישור", "shared_photos_and_videos_count": "{assetCount, plural, other {# תמונות וסרטונים משותפים.}}", @@ -1788,6 +1839,7 @@ "sort_title": "כותרת", "source": "קוד מקור", "stack": "ערימה", + "stack_action_prompt": "{count} קובצו", "stack_duplicates": "צור ערימת כפילויות", "stack_select_one_photo": "בחר תמונה ראשית אחת עבור הערימה", "stack_selected_photos": "צור ערימת תמונות נבחרות", @@ -1807,6 +1859,7 @@ "storage_quota": "מכסת האחסון", "storage_usage": "{used} בשימוש מתוך {available}", "submit": "שלח", + "success": "בוצע בהצלחה", "suggestions": "הצעות", "sunrise_on_the_beach": "Sunrise on the beach (מומלץ לחפש באנגלית לתוצאות טובות יותר)", "support": "תמיכה", @@ -1816,6 +1869,8 @@ "sync": "סנכרן", "sync_albums": "סנכרן אלבומים", "sync_albums_manual_subtitle": "סנכרן את כל הסרטונים והתמונות שהועלו לאלבומי הגיבוי שנבחרו", + "sync_local": "סנכרן מקומי", + "sync_remote": "סנכרן מרוחק", "sync_upload_album_setting_subtitle": "צור והעלה תמונות וסרטונים שלך לאלבומים שנבחרו ביישום", "tag": "תג", "tag_assets": "תיוג תמונות", @@ -1826,6 +1881,7 @@ "tag_updated": "תג מעודכן: {tag}", "tagged_assets": "תויגו {count, plural, one {תמונה #} other {# תמונות}}", "tags": "תגים", + "tap_to_run_job": "לחץ על מנת להפעיל משימה", "template": "תבנית", "theme": "ערכת נושא", "theme_selection": "בחירת ערכת נושא", @@ -1898,16 +1954,20 @@ "unselect_all_duplicates": "בטל בחירת כל הכפילויות", "unselect_all_in": "בטל את הבחירה של הכל ב {group}", "unstack": "בטל ערימה", + "unstack_action_prompt": "{count} הופרדו", "unstacked_assets_count": "{count, plural, one {תמונה # הוסרה} other {# תמונות הוסרו}} מהערימה", "untagged": "לא מתיוגים", "up_next": "הבא בתור", "updated_at": "עודכן", "updated_password": "סיסמה עודכנה", "upload": "העלאה", + "upload_action_prompt": "{count} נוספו לתור להעלאה", "upload_concurrency": "בו-זמניות של העלאה", + "upload_details": "פרטי העלאה", "upload_dialog_info": "האם ברצונך לגבות את התמונות שנבחרו לשרת?", "upload_dialog_title": "העלאת תמונה", "upload_errors": "העלאה הושלמה עם {count, plural, one {שגיאה #} other {# שגיאות}}, רענן את הדף כדי לראות תמונות שהועלו.", + "upload_finished": "העלאה הסתיימה", "upload_progress": "נותרו {remaining, number} - טופלו {processed, number}/{total, number}", "upload_skipped_duplicates": "דילג על {count, plural, one {תמונה כפולה #} other {# תמונות כפולות}}", "upload_status_duplicates": "כפילויות", @@ -1916,6 +1976,7 @@ "upload_success": "ההעלאה בוצעה בהצלחה. רענן את הדף כדי לצפות בתמונות שהועלו.", "upload_to_immich": "העלה לשרת ({count})", "uploading": "מעלה", + "uploading_media": "מעלה מדיה", "url": "URL", "usage": "שימוש", "use_biometric": "השתמש באימות ביומטרי", @@ -1936,6 +1997,7 @@ "user_usage_stats_description": "הצג סטטיסטיקות שימוש בחשבון", "username": "שם משתמש", "users": "משתמשים", + "users_added_to_album_count": "נוספו {count, plural, one {משתמש #} other {# משתמשים}} לאלבום", "utilities": "כלים", "validate": "לאמת", "validate_endpoint_error": "נא להזין כתובת תקנית", @@ -1954,6 +2016,7 @@ "view_album": "הצג אלבום", "view_all": "הצג הכל", "view_all_users": "הצג את כל המשתמשים", + "view_details": "הצג פרטים", "view_in_timeline": "ראה בציר הזמן", "view_link": "הצג קישור", "view_links": "הצג קישורים", diff --git a/i18n/hi.json b/i18n/hi.json index 2cb29370b5..08c1933adc 100644 --- a/i18n/hi.json +++ b/i18n/hi.json @@ -45,7 +45,7 @@ "backup_database_enable_description": "Enable database dumps", "backup_keep_last_amount": "रखने के लिए पिछले डंप की मात्रा", "backup_settings": "डेटाबेस डंप सेटिंग्स", - "backup_settings_description": "डेटाबेस डंप सेटिंग्स प्रबंधित करें। ध्यान दें: इन कार्यों की निगरानी नहीं की जाती है और विफलता की स्थिति में आपको सूचित नहीं किया जाएगा।", + "backup_settings_description": "डेटाबेस डंप सेटिंग्स प्रबंधित करें।", "cleared_jobs": "{job}: के लिए कार्य साफ़ कर दिए गए", "config_set_by_file": "Config वर्तमान में एक config फ़ाइल द्वारा सेट किया गया है", "confirm_delete_library": "क्या आप वाकई {library} लाइब्रेरी को हटाना चाहते हैं?", @@ -166,12 +166,26 @@ "metadata_settings_description": "मेटाडेटा सेटिंग प्रबंधित करें", "migration_job": "प्रवास", "migration_job_description": "संपत्तियों और चेहरों के थंबनेल को नवीनतम फ़ोल्डर संरचना में माइग्रेट करें", + "nightly_tasks_cluster_faces_setting_description": "नए पहचाने गए चेहरों पर चेहरे की पहचान चलाएँ", + "nightly_tasks_cluster_new_faces_setting": "नए चेहरों को समूह में शामिल करें", + "nightly_tasks_database_cleanup_setting": "डेटाबेस क्लीनअप कार्य", + "nightly_tasks_database_cleanup_setting_description": "डेटाबेस से पुराना, समाप्त हो चुका डेटा साफ़ करें", + "nightly_tasks_generate_memories_setting": "यादें उत्पन्न करें", + "nightly_tasks_generate_memories_setting_description": "संपत्तियों से नई यादें बनाएँ", + "nightly_tasks_missing_thumbnails_setting": "गायब थंबनेल उत्पन्न करें", + "nightly_tasks_missing_thumbnails_setting_description": "थंबनेल निर्माण के लिए थंबनेल के बिना कतारबद्ध परिसंपत्तियाँ", + "nightly_tasks_settings": "रात्रिकालीन कार्य सेटिंग्स", + "nightly_tasks_settings_description": "रात्रिकालीन कार्यों का प्रबंधन करें", + "nightly_tasks_start_time_setting": "समय शुरू", + "nightly_tasks_start_time_setting_description": "वह समय जब सर्वर रात्रिकालीन कार्य चलाना शुरू करता है", + "nightly_tasks_sync_quota_usage_setting": "सिंक कोटा उपयोग", + "nightly_tasks_sync_quota_usage_setting_description": "वर्तमान उपयोग के आधार पर उपयोगकर्ता संग्रहण कोटा अपडेट करें", "no_paths_added": "कोई पथ नहीं डाला गया", "no_pattern_added": "कोई पैटर्न नहीं डाला गया", "note_apply_storage_label_previous_assets": "नोट: पहले अपलोड की गई संपत्तियों पर स्टोरेज लेबल लागू करने के लिए, चलाएँ", "note_cannot_be_changed_later": "नोट: इसे बाद में बदला नहीं जा सकता!", "notification_email_from_address": "इस पते से", - "notification_email_from_address_description": "प्रेषक का ईमेल पता, उदाहरण के लिए: \"इमिच फोटो सर्वर \"", + "notification_email_from_address_description": "प्रेषक का ईमेल पता, उदाहरण के लिए: \"इमिच फोटो सर्वर \"। यह सुनिश्चित करें कि आप उसी पते का उपयोग करें जिससे आपको ईमेल भेजने की अनुमति है।", "notification_email_host_description": "ईमेल सर्वर का होस्ट (उदा. smtp.immitch.app)", "notification_email_ignore_certificate_errors": "प्रमाणपत्र त्रुटियों पर ध्यान न दें", "notification_email_ignore_certificate_errors_description": "टीएलएस प्रमाणपत्र सत्यापन त्रुटियों पर ध्यान न दें (अनुशंसित नहीं)", @@ -195,7 +209,9 @@ "oauth_enable_description": "OAuth से लॉगिन करें", "oauth_mobile_redirect_uri": "मोबाइल रीडायरेक्ट यूआरआई", "oauth_mobile_redirect_uri_override": "मोबाइल रीडायरेक्ट यूआरआई ओवरराइड", - "oauth_mobile_redirect_uri_override_description": "सक्षम करें जब 'app.immitch:/' एक अमान्य रीडायरेक्ट यूआरआई हो।", + "oauth_mobile_redirect_uri_override_description": "जब OAuth प्रदाता किसी मोबाइल URI, जैसे ''{callback}'' की अनुमति नहीं देता, तब सक्षम करें", + "oauth_role_claim": "भूमिका का दावा", + "oauth_role_claim_description": "इस दावे की उपस्थिति के आधार पर स्वचालित रूप से व्यवस्थापक पहुँच प्रदान करें। दावे में 'उपयोगकर्ता' या 'व्यवस्थापक' हो सकता है।", "oauth_settings": "ओऑथ", "oauth_settings_description": "OAuth लॉगिन सेटिंग प्रबंधित करें", "oauth_settings_more_details": "इस सुविधा के बारे में अधिक जानकारी के लिए, देखें डॉक्स।", @@ -204,7 +220,7 @@ "oauth_storage_quota_claim": "भंडारण कोटा का दावा", "oauth_storage_quota_claim_description": "उपयोगकर्ता के संग्रहण कोटा को इस दावे के मूल्य पर स्वचालित रूप से सेट करें।", "oauth_storage_quota_default": "डिफ़ॉल्ट संग्रहण कोटा (GiB)", - "oauth_storage_quota_default_description": "GiB में कोटा का उपयोग तब किया जाएगा जब कोई दावा प्रदान नहीं किया गया हो (असीमित कोटा के लिए 0 दर्ज करें)।", + "oauth_storage_quota_default_description": "GiB में कोटा का उपयोग तब किया जाएगा जब कोई दावा प्रदान नहीं किया गया हो ।", "oauth_timeout": "ब्रेक का अनुरोध", "oauth_timeout_description": "अनुरोधों के लिए समय-सीमा मिलीसेकंड में", "password_enable_description": "ईमेल और पासवर्ड से लॉगिन करें", @@ -244,6 +260,7 @@ "storage_template_migration_info": "स्टोरेज टेम्प्लेट सभी एक्सटेंशन को लोअरकेस में बदल देगा। टेम्प्लेट में किए गए बदलाव सिर्फ़ नई संपत्तियों पर लागू होंगे। टेम्प्लेट को पहले अपलोड की गई संपत्तियों पर पूर्वव्यापी रूप से लागू करने के लिए, {job} चलाएँ।", "storage_template_migration_job": "संग्रहण टेम्पलेट माइग्रेशन कार्य", "storage_template_more_details": "इस सुविधा के बारे में अधिक जानकारी के लिए, देखें भंडारण टेम्पलेट और इसके आशय", + "storage_template_onboarding_description_v2": "सक्षम होने पर, यह सुविधा उपयोगकर्ता-निर्धारित टेम्पलेट के आधार पर फ़ाइलों को स्वतः व्यवस्थित करेगी। अधिक जानकारी के लिए, कृपया दस्तावेज़ीकरण देखें।", "storage_template_path_length": "अनुमानित पथ लंबाई सीमा: {length, number}/{limit, number}", "storage_template_settings": "भंडारण टेम्पलेट", "storage_template_settings_description": "अपलोड संपत्ति की फ़ोल्डर संरचना और फ़ाइल नाम प्रबंधित करें", @@ -356,10 +373,12 @@ "admin_password": "व्यवस्थापक पासवर्ड", "administration": "प्रशासन", "advanced": "विकसित", + "advanced_settings_beta_timeline_subtitle": "नए ऐप अनुभव को आज़माएँ", + "advanced_settings_beta_timeline_title": "बीटा टाइमलाइन", "advanced_settings_enable_alternate_media_filter_subtitle": "सिंक के दौरान वैकल्पिक मानदंडों के आधार पर मीडिया को फ़िल्टर करने के लिए इस विकल्प का उपयोग करें। इसे केवल तभी आज़माएँ जब आपको ऐप द्वारा सभी एल्बमों का पता लगाने में समस्या हो।", "advanced_settings_enable_alternate_media_filter_title": "[प्रयोगात्मक] वैकल्पिक डिवाइस एल्बम सिंक फ़िल्टर का उपयोग करें", "advanced_settings_log_level_title": "लॉग स्तर:{level}", - "advanced_settings_prefer_remote_subtitle": "कुछ डिवाइस पर मौजूद एसेट से थंबनेल लोड करने में काफ़ी समय लगता है। इसके बजाय रिमोट इमेज लोड करने के लिए इस सेटिंग को सक्रिय करें।", + "advanced_settings_prefer_remote_subtitle": "कुछ डिवाइस स्थानीय एसेट से थंबनेल लोड करने में बहुत धीमे होते हैं। इसके बजाय, दूरस्थ इमेज लोड करने के लिए इस सेटिंग को सक्रिय करें।", "advanced_settings_prefer_remote_title": "दूरस्थ छवियों को प्राथमिकता दें", "advanced_settings_proxy_headers_subtitle": "प्रत्येक नेटवर्क अनुरोध के साथ इम्मिच द्वारा भेजे जाने वाले प्रॉक्सी हेडर को परिभाषित करें", "advanced_settings_proxy_headers_title": "प्रॉक्सी हेडर", @@ -378,6 +397,7 @@ "album_cover_updated": "एल्बम कवर अपडेट किया गया", "album_delete_confirmation": "क्या आप वाकई एल्बम {album} हटाना चाहते हैं?", "album_delete_confirmation_description": "यदि यह एल्बम साझा किया गया है, तो अन्य उपयोगकर्ता इसे एक्सेस नहीं कर पाएंगे।", + "album_deleted": "एल्बम हटा दिया गया", "album_info_card_backup_album_excluded": "छोड़ा गया", "album_info_card_backup_album_included": "शामिल", "album_info_updated": "एल्बम की जानकारी अपडेट की गई", @@ -387,6 +407,7 @@ "album_options": "एल्बम विकल्प", "album_remove_user": "उपयोगकर्ता हटाएं?", "album_remove_user_confirmation": "क्या आप वाकई {user} को हटाना चाहते हैं?", + "album_search_not_found": "आपकी खोज से मेल खाता कोई एल्बम नहीं मिला", "album_share_no_users": "ऐसा लगता है कि आपने यह एल्बम सभी उपयोगकर्ताओं के साथ साझा कर दिया है या आपके पास साझा करने के लिए कोई उपयोगकर्ता नहीं है।", "album_updated": "एल्बम अपडेट किया गया", "album_updated_setting_description": "जब किसी साझा एल्बम में नई संपत्तियाँ हों तो एक ईमेल सूचना प्राप्त करें", @@ -402,7 +423,11 @@ "album_viewer_page_share_add_users": "उपयोगकर्ता जोड़ें", "album_with_link_access": "लिंक वाले किसी भी व्यक्ति को इस एल्बम में फ़ोटो और लोगों को देखने दें।", "albums": "एलबम", - "albums_count": "{गिनती, बहुवचन, एक {{count, number} एल्बम} अन्य {{count, number} एल्बम}}", + "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Albums}}", + "albums_default_sort_order": "डिफ़ॉल्ट एल्बम सॉर्ट क्रम", + "albums_default_sort_order_description": "नये एल्बम बनाते समय आरंभिक परिसंपत्ति सॉर्ट क्रम।", + "albums_feature_description": "परिसंपत्तियों का संग्रह जिसे अन्य उपयोगकर्ताओं के साथ साझा किया जा सकता है।", + "albums_on_device_count": "डिवाइस पर एल्बम ({count})", "all": "सभी", "all_albums": "सभी एलबम", "all_people": "सभी लोग", @@ -423,13 +448,14 @@ "app_settings": "एप्लिकेशन सेटिंग", "appears_in": "प्रकट होता है", "archive": "संग्रहालय", + "archive_action_prompt": "{count} को संग्रह में जोड़ा गया", "archive_or_unarchive_photo": "फ़ोटो को संग्रहीत या असंग्रहीत करें", "archive_page_no_archived_assets": "कोई संग्रहीत संपत्ति नहीं मिली", "archive_page_title": "पुरालेख ({count})", "archive_size": "पुरालेख आकार", "archive_size_description": "डाउनलोड के लिए संग्रह आकार कॉन्फ़िगर करें (GiB में)", "archived": "संग्रहित", - "archived_count": "{गणना, बहुवचन, अन्य {संग्रहीत #}}", + "archived_count": "{count, बहुवचन, अन्य {संग्रहीत #}}", "are_these_the_same_person": "क्या ये वही व्यक्ति हैं?", "are_you_sure_to_do_this": "क्या आप वास्तव में इसे करना चाहते हैं?", "asset_action_delete_err_read_only": "केवल पढ़ने योग्य परिसंपत्ति(ओं) को हटाया नहीं जा सकता, छोड़ा जा सकता है", @@ -442,50 +468,175 @@ "asset_hashing": "हैशिंग...।", "asset_list_group_by_sub_title": "द्वारा समूह बनाएं", "asset_list_layout_settings_dynamic_layout_title": "गतिशील लेआउट", + "asset_list_layout_settings_group_automatically": "स्वचालित", + "asset_list_layout_settings_group_by": "समूह परिसंपत्तियों द्वारा", + "asset_list_layout_settings_group_by_month_day": "महीना + दिन", + "asset_list_layout_sub_title": "लेआउट", + "asset_list_settings_subtitle": "फ़ोटो ग्रिड लेआउट सेटिंग्स", + "asset_list_settings_title": "चित्र की जाली", "asset_offline": "संपत्ति ऑफ़लाइन", "asset_offline_description": "यह संपत्ति ऑफ़लाइन है।", "asset_restored_successfully": "संपत्ति(याँ) सफलतापूर्वक पुनर्स्थापित की गईं", "asset_skipped": "छोड़ा गया", + "asset_skipped_in_trash": "कचरे में", "asset_uploaded": "अपलोड किए गए", - "asset_uploading": "अपलोड हो रहा है..।", + "asset_uploading": "अपलोड हो रहा है…", + "asset_viewer_settings_subtitle": "अपनी गैलरी व्यूअर सेटिंग प्रबंधित करें", + "asset_viewer_settings_title": "एसेट व्यूअर", "assets": "संपत्तियां", + "assets_added_count": "{count, plural, one {# asset} other {# assets}} जोड़ा गया", + "assets_added_to_album_count": "एल्बम में {count, plural, one {# asset} other {# assets}} जोड़ा गया", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} को एल्बम में नहीं जोड़ा जा सकता", + "assets_count": "{count, बहुवचन, एक {# संपत्ति} अन्य {# संपत्ति}}", "assets_deleted_permanently": "{count} संपत्ति(याँ) स्थायी रूप से हटा दी गईं", "assets_deleted_permanently_from_server": "{count} संपत्ति(याँ) इमिच सर्वर से स्थायी रूप से हटा दी गईं", + "assets_downloaded_failed": "{count, plural, one {Downloaded # file - {error} file failed} other {Downloaded # files - {error} files failed}}", + "assets_downloaded_successfully": "{count, plural, one {Downloaded # file successfully} अन्य {Downloaded # files successfully}}", + "assets_moved_to_trash_count": "{count, plural, one {# asset} other {# assets}} को ट्रैश में ले जाया गया", + "assets_permanently_deleted_count": "स्थायी रूप से हटा दिया गया {count, plural, one {# asset} other {# assets}}", + "assets_removed_count": "{count, plural, one {# asset} other {# assets}} हटा दिया गया", "assets_removed_permanently_from_device": "{count} संपत्ति(याँ) आपके डिवाइस से स्थायी रूप से हटा दी गईं", - "assets_restore_confirmation": "क्या आप वाकई अपनी सभी नष्ट की गई संपत्तियों को पुनर्स्थापित करना चाहते हैं? आप इस क्रिया को पूर्ववत नहीं कर सकते!", + "assets_restore_confirmation": "क्या आप वाकई अपनी सभी नष्ट की गई संपत्तियों को पुनर्स्थापित करना चाहते हैं? आप इस क्रिया को पूर्ववत नहीं कर सकते।", + "assets_restored_count": "पुनर्स्थापित {count, plural, one {# asset} other {# assets}}", "assets_restored_successfully": "{count} संपत्ति(याँ) सफलतापूर्वक पुनर्स्थापित की गईं", "assets_trashed": "{count} संपत्ति(याँ) कचरे में डाली गईं", + "assets_trashed_count": "ट्रैश की गई {count, plural, one {# asset} other {# assets}}", "assets_trashed_from_server": "{count} संपत्ति(याँ) इमिच सर्वर से कचरे में डाली गईं", + "assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Assets were}}एल्बम का पहले से ही हिस्सा थे", "authorized_devices": "अधिकृत उपकरण", + "automatic_endpoint_switching_subtitle": "उपलब्ध होने पर निर्दिष्ट वाई-फाई से स्थानीय रूप से कनेक्ट करें और अन्यत्र वैकल्पिक कनेक्शन का उपयोग करें", + "automatic_endpoint_switching_title": "स्वचालित URL स्विचिंग", + "autoplay_slideshow": "ऑटोप्ले स्लाइड शो", "back": "वापस", "back_close_deselect": "वापस जाएँ, बंद करें, या अचयनित करें", - "backup_controller_page_background_wifi": "Only on WiFi", + "background_location_permission": "पृष्ठभूमि स्थान अनुमति", + "background_location_permission_content": "पृष्ठभूमि में चलते समय नेटवर्क बदलने के लिए, Immich के पास *हमेशा* सटीक स्थान तक पहुंच होनी चाहिए ताकि ऐप वाई-फाई नेटवर्क का नाम पढ़ सके", + "backup": "बैकअप", + "backup_album_selection_page_albums_device": "डिवाइस पर एल्बम ({count})", + "backup_album_selection_page_albums_tap": "शामिल करने के लिए टैप करें, बाहर करने के लिए डबल टैप करें", + "backup_album_selection_page_assets_scatter": "एसेट कई एल्बमों में बिखरे हो सकते हैं। इसलिए, बैकअप प्रक्रिया के दौरान एल्बमों को शामिल या बाहर किया जा सकता है।", + "backup_album_selection_page_select_albums": "एल्बम चुनें", + "backup_album_selection_page_selection_info": "चयन जानकारी", + "backup_album_selection_page_total_assets": "कुल अद्वितीय संपत्तियाँ", + "backup_all": "सभी", + "backup_background_service_backup_failed_message": "संपत्तियों का बैकअप लेने में विफल. पुनः प्रयास किया जा रहा है…", + "backup_background_service_connection_failed_message": "सर्वर से कनेक्ट करने में विफल. पुनः प्रयास किया जा रहा है…", + "backup_background_service_current_upload_notification": "{filename} अपलोड हो रहा है", + "backup_background_service_default_notification": "नई परिसंपत्तियों की जांच की जा रही है…", + "backup_background_service_error_title": "बैकअप त्रुटि", + "backup_background_service_in_progress_notification": "अपनी परिसंपत्तियों का बैकअप लेना…", + "backup_background_service_upload_failure_notification": "{filename} अपलोड करने में विफल", + "backup_controller_page_albums": "बैकअप एल्बम", + "backup_controller_page_background_app_refresh_disabled_content": "बैकग्राउंड बैकअप का उपयोग करने के लिए सेटिंग्स > सामान्य > बैकग्राउंड ऐप रिफ्रेश में बैकग्राउंड ऐप रिफ्रेश सक्षम करें।", + "backup_controller_page_background_app_refresh_disabled_title": "पृष्ठभूमि ऐप रीफ़्रेश अक्षम", + "backup_controller_page_background_app_refresh_enable_button_text": "सेटिंग्स पर जाएँ", + "backup_controller_page_background_battery_info_link": "कैसे मुझे दिखाओ", + "backup_controller_page_background_battery_info_message": "सर्वोत्तम बैकग्राउंड बैकअप अनुभव के लिए, कृपया Immich के लिए बैकग्राउंड गतिविधि को प्रतिबंधित करने वाले किसी भी बैटरी ऑप्टिमाइज़ेशन को अक्षम करें।\n\nचूँकि यह डिवाइस-विशिष्ट है, इसलिए कृपया अपने डिवाइस निर्माता से आवश्यक जानकारी देखें।", + "backup_controller_page_background_battery_info_ok": "ठीक", + "backup_controller_page_background_battery_info_title": "बैटरी अनुकूलन", + "backup_controller_page_background_charging": "केवल चार्ज करते समय", + "backup_controller_page_background_configure_error": "पृष्ठभूमि सेवा कॉन्फ़िगर करने में विफल", + "backup_controller_page_background_delay": "नई संपत्ति का बैकअप विलंबित करें: {duration}", + "backup_controller_page_background_description": "ऐप खोले बिना किसी भी नई संपत्ति का स्वचालित रूप से बैकअप लेने के लिए पृष्ठभूमि सेवा चालू करें", + "backup_controller_page_background_is_off": "स्वचालित पृष्ठभूमि बैकअप बंद है", + "backup_controller_page_background_is_on": "स्वचालित पृष्ठभूमि बैकअप चालू है", + "backup_controller_page_background_turn_off": "पृष्ठभूमि सेवा बंद करें", + "backup_controller_page_background_turn_on": "पृष्ठभूमि सेवा चालू करें", + "backup_controller_page_background_wifi": "केवल वाई-फ़ाई पर", + "backup_controller_page_backup": "बैकअप", + "backup_controller_page_backup_selected": "चयनित: ", + "backup_controller_page_backup_sub": "बैकअप किए गए फ़ोटो और वीडियो", + "backup_controller_page_created": "निर्मित तिथि: {date}", + "backup_controller_page_desc_backup": "ऐप खोलते समय सर्वर पर नई संपत्तियों को स्वचालित रूप से अपलोड करने के लिए अग्रभूमि बैकअप चालू करें।", + "backup_controller_page_excluded": "छोड़ा गया: ", + "backup_controller_page_failed": "विफल ({count})", + "backup_controller_page_filename": "फ़ाइल का नाम: {{filename} [{size}]", + "backup_controller_page_id": "आईडी: {id}", + "backup_controller_page_info": "बैकअप जानकारी", + "backup_controller_page_none_selected": "कोई भी चयनित नहीं", + "backup_controller_page_remainder": "शेष", + "backup_controller_page_remainder_sub": "चयन से बैकअप लेने के लिए शेष फ़ोटो और वीडियो", + "backup_controller_page_server_storage": "सर्वर संग्रहण", + "backup_controller_page_start_backup": "बैकअप प्रारंभ करें", + "backup_controller_page_status_off": "स्वचालित अग्रभूमि बैकअप बंद है", + "backup_controller_page_status_on": "स्वचालित अग्रभूमि बैकअप चालू है", + "backup_controller_page_storage_format": "{कुल} में से {प्रयुक्त} प्रयुक्त", + "backup_controller_page_to_backup": "बैकअप किए जाने वाले एल्बम", + "backup_controller_page_total_sub": "चयनित एल्बमों से सभी अद्वितीय फ़ोटो और वीडियो", + "backup_controller_page_turn_off": "अग्रभूमि बैकअप बंद करें", + "backup_controller_page_turn_on": "अग्रभूमि बैकअप चालू करें", + "backup_controller_page_uploading_file_info": "फ़ाइल जानकारी अपलोड करना", + "backup_err_only_album": "एकमात्र एल्बम नहीं हटाया जा सकता", + "backup_info_card_assets": "संपत्ति", + "backup_manual_cancelled": "रद्द", + "backup_manual_in_progress": "अपलोड पहले से ही प्रगति पर है। कुछ देर बाद प्रयास करें", + "backup_manual_success": "सफलता", + "backup_manual_title": "अपलोड स्थिति", + "backup_options_page_title": "बैकअप विकल्प", + "backup_setting_subtitle": "पृष्ठभूमि और अग्रभूमि अपलोड सेटिंग प्रबंधित करें", "backward": "पिछला", + "beta_sync": "बीटा सिंक स्थिति", + "beta_sync_subtitle": "नए सिंक सिस्टम का प्रबंधन करें", + "biometric_auth_enabled": "बायोमेट्रिक प्रमाणीकरण सक्षम", + "biometric_locked_out": "आप बायोमेट्रिक प्रमाणीकरण से बाहर हैं", + "biometric_no_options": "कोई बायोमेट्रिक विकल्प उपलब्ध नहीं है", + "biometric_not_available": "इस डिवाइस पर बायोमेट्रिक प्रमाणीकरण उपलब्ध नहीं है", "birthdate_saved": "जन्मतिथि सफलतापूर्वक सहेजी गई", "birthdate_set_description": "जन्मतिथि का उपयोग फोटो के समय इस व्यक्ति की आयु की गणना करने के लिए किया जाता है।", "blurred_background": "धुंधली पृष्ठभूमि", + "bugs_and_feature_requests": "बग और सुविधा अनुरोध", "build": "निर्माण", "build_image": "छवि बनाएँ", + "bulk_delete_duplicates_confirmation": "क्या आप वाकई {count, plural, one {# duplicate asset} other {# duplicate assets}} को बल्क में हटाना चाहते हैं? इससे हर ग्रुप की सबसे बड़ी संपत्ति बनी रहेगी और बाकी सभी डुप्लिकेट हमेशा के लिए हट जाएँगे। आप इस क्रिया को पूर्ववत नहीं कर सकते!", + "bulk_keep_duplicates_confirmation": "क्या आप वाकई {count, plural, one {# duplicate asset} other {# duplicate assets}} रखना चाहते हैं? इससे बिना कुछ हटाए सभी डुप्लिकेट ग्रुप हल हो जाएँगे।", + "bulk_trash_duplicates_confirmation": "क्या आप वाकई {count, plural, one {# duplicate asset} other {# duplicate assets}} को बल्क ट्रैश करना चाहते हैं? इससे हर ग्रुप की सबसे बड़ी एसेट रहेगी और बाकी सभी डुप्लिकेट ट्रैश हो जाएँगे।", "buy": "इम्मीच खरीदो", + "cache_settings_clear_cache_button": "कैश को साफ़ करें", + "cache_settings_clear_cache_button_title": "ऐप का कैश साफ़ करता है। कैश के दोबारा बनने तक, यह ऐप के प्रदर्शन पर काफ़ी असर डालेगा।", + "cache_settings_duplicated_assets_clear_button": "स्पष्ट", + "cache_settings_duplicated_assets_subtitle": "ऐप द्वारा अनदेखा की गई तस्वीरें और वीडियो", + "cache_settings_duplicated_assets_title": "डुप्लिकेट संपत्तियां ({count})", + "cache_settings_statistics_album": "लाइब्रेरी थंबनेल", + "cache_settings_statistics_full": "पूर्ण चित्र", + "cache_settings_statistics_shared": "साझा किए गए एल्बम थंबनेल", + "cache_settings_statistics_thumbnail": "थंबनेल", + "cache_settings_statistics_title": "कैश उपयोग", + "cache_settings_subtitle": "Immich मोबाइल एप्लिकेशन के कैशिंग व्यवहार को नियंत्रित करें", "cache_settings_tile_subtitle": "स्थानीय संग्रहण के व्यवहार को नियंत्रित करें", "cache_settings_tile_title": "स्थानीय संग्रहण", + "cache_settings_title": "कैशिंग सेटिंग्स", "camera": "कैमरा", "camera_brand": "कैमरा ब्रांड", "camera_model": "कैमरा मॉडल", "cancel": "रद्द करना", "cancel_search": "खोज रद्द करें", + "canceled": "रद्द करना", + "canceling": "रद्द कर रहा है", "cannot_merge_people": "लोगों का विलय नहीं हो सकता", "cannot_undo_this_action": "आप इस क्रिया को पूर्ववत नहीं कर सकते!", "cannot_update_the_description": "विवरण अद्यतन नहीं किया जा सकता", + "cast": "ढालना", + "cast_description": "उपलब्ध कास्ट गंतव्यों को कॉन्फ़िगर करें", "change_date": "बदलाव दिनांक", + "change_description": "विवरण बदलें", + "change_display_order": "प्रदर्शन क्रम बदलें", "change_expiration_time": "समाप्ति समय बदलें", "change_location": "स्थान बदलें", "change_name": "नाम परिवर्तन करें", - "change_name_successfully": "नाम सफलतापूर्वक बदलें", + "change_name_successfully": "नाम सफलतापूर्वक बदला गया", "change_password": "पासवर्ड बदलें", "change_password_description": "यह या तो पहली बार है जब आप सिस्टम में साइन इन कर रहे हैं या आपका पासवर्ड बदलने का अनुरोध किया गया है।", + "change_password_form_confirm_password": "पासवर्ड की पुष्टि कीजिये", + "change_password_form_description": "नमस्ते {name},\n\nया तो आप पहली बार सिस्टम में साइन इन कर रहे हैं या फिर आपका पासवर्ड बदलने का अनुरोध किया गया है। कृपया नीचे नया पासवर्ड डालें।", + "change_password_form_new_password": "नया पासवर्ड", + "change_password_form_password_mismatch": "सांकेतिक शब्द मेल नहीं खाते", + "change_password_form_reenter_new_password": "नया पासवर्ड पुनः दर्ज करें", + "change_pin_code": "पिन कोड बदलें", "change_your_password": "अपना पासवर्ड बदलें", "changed_visibility_successfully": "दृश्यता सफलतापूर्वक परिवर्तित", + "check_corrupt_asset_backup": "दूषित परिसंपत्ति बैकअप की जाँच करें", + "check_corrupt_asset_backup_button": "जाँच करें", + "check_corrupt_asset_backup_description": "यह जाँच केवल वाई-फ़ाई पर ही करें और सभी संपत्तियों का बैकअप लेने के बाद ही करें। इस प्रक्रिया में कुछ मिनट लग सकते हैं।", "check_logs": "लॉग जांचें", "choose_matching_people_to_merge": "मर्ज करने के लिए मिलते-जुलते लोगों को चुनें", "city": "शहर", @@ -494,21 +645,49 @@ "clear_all_recent_searches": "सभी हालिया खोजें साफ़ करें", "clear_message": "स्पष्ट संदेश", "clear_value": "स्पष्ट मूल्य", + "client_cert_dialog_msg_confirm": "ठीक", + "client_cert_enter_password": "पास वर्ड दर्ज करें", + "client_cert_import": "आयात", + "client_cert_import_success_msg": "क्लाइंट प्रमाणपत्र आयात किया गया है", + "client_cert_invalid_msg": "अमान्य प्रमाणपत्र फ़ाइल या गलत पासवर्ड", + "client_cert_remove_msg": "क्लाइंट प्रमाणपत्र हटा दिया गया है", + "client_cert_subtitle": "केवल PKCS12 (.p12, .pfx) फ़ॉर्मैट का समर्थन करता है। प्रमाणपत्र आयात/हटाएँ केवल लॉगिन से पहले उपलब्ध हैं", + "client_cert_title": "SSL क्लाइंट प्रमाणपत्र", + "clockwise": "दक्षिणावर्त", "close": "बंद", "collapse": "गिर जाना", "collapse_all": "सभी को संकुचित करें", + "color": "रंग", "color_theme": "रंग थीम", "comment_deleted": "टिप्पणी हटा दी गई", "comment_options": "टिप्पणी विकल्प", "comments_and_likes": "टिप्पणियाँ और पसंद", "comments_are_disabled": "टिप्पणियाँ अक्षम हैं", + "common_create_new_album": "नया एल्बम बनाएँ", + "common_server_error": "कृपया अपने नेटवर्क कनेक्शन की जांच करें, सुनिश्चित करें कि सर्वर पहुंच योग्य है और ऐप/सर्वर संस्करण संगत हैं।", + "completed": "पुरा होना", "confirm": "पुष्टि", "confirm_admin_password": "एडमिन पासवर्ड की पुष्टि करें", + "confirm_delete_face": "क्या आप वाकई एसेट से {name} चेहरा हटाना चाहते हैं?", "confirm_delete_shared_link": "क्या आप वाकई इस साझा लिंक को हटाना चाहते हैं?", + "confirm_keep_this_delete_others": "इस एसेट को छोड़कर, स्टैक की सभी अन्य एसेट हटा दी जाएँगी। क्या आप वाकई जारी रखना चाहते हैं?", + "confirm_new_pin_code": "नए पिन कोड की पुष्टि करें", "confirm_password": "पासवर्ड की पुष्टि कीजिये", + "confirm_tag_face": "क्या आप इस चेहरे को {name} के रूप में टैग करना चाहते हैं?", + "confirm_tag_face_unnamed": "क्या आप इस चेहरे को टैग करना चाहते हैं?", + "connected_device": "कनेक्टेड डिवाइस", + "connected_to": "से जुड़ा", "contain": "समाहित", "context": "संदर्भ", "continue": "जारी", + "control_bottom_app_bar_create_new_album": "नया एल्बम बनाएँ", + "control_bottom_app_bar_delete_from_immich": "Immich से हटाएं", + "control_bottom_app_bar_delete_from_local": "डिवाइस से हटाएं", + "control_bottom_app_bar_edit_location": "स्थान संपादित करें", + "control_bottom_app_bar_edit_time": "तारीख और समय संपादित करें", + "control_bottom_app_bar_share_link": "लिंक शेयर करें", + "control_bottom_app_bar_share_to": "साझा करें", + "control_bottom_app_bar_trash_from_immich": "ट्रैश में ले जाएं", "copied_image_to_clipboard": "छवि को क्लिपबोर्ड पर कॉपी किया गया।", "copied_to_clipboard": "क्लिपबोर्ड पर नकल!", "copy_error": "प्रतिलिपि त्रुटि", @@ -523,6 +702,7 @@ "covers": "आवरण", "create": "तैयार करें", "create_album": "एल्बम बनाओ", + "create_album_page_untitled": "शीर्षकहीन", "create_library": "लाइब्रेरी बनाएं", "create_link": "लिंक बनाएं", "create_link_to_share": "शेयर करने के लिए लिंक बनाएं", @@ -531,39 +711,75 @@ "create_new_person": "नया व्यक्ति बनाएं", "create_new_person_hint": "चयनित संपत्तियों को एक नए व्यक्ति को सौंपें", "create_new_user": "नया उपयोगकर्ता बनाएं", + "create_shared_album_page_share_add_assets": "संपत्ति जोड़ें", + "create_shared_album_page_share_select_photos": "फ़ोटो चुनें", + "create_tag": "टैग बनाएँ", + "create_tag_description": "एक नया टैग बनाएँ। नेस्टेड टैग के लिए, कृपया फ़ॉरवर्ड स्लैश सहित टैग का पूरा पथ दर्ज करें।", "create_user": "उपयोगकर्ता बनाइये", "created": "बनाया", + "created_at": "बनाया था", "crop": "छाँटें", + "curated_object_page_title": "चीज़ें", "current_device": "वर्तमान उपकरण", + "current_pin_code": "वर्तमान पिन कोड", + "current_server_address": "वर्तमान सर्वर पता", "custom_locale": "कस्टम लोकेल", "custom_locale_description": "भाषा और क्षेत्र के आधार पर दिनांक और संख्याएँ प्रारूपित करें", + "daily_title_text_date": "ई, एमएमएम डीडी", + "daily_title_text_date_year": "ई, एमएमएम दिन, वर्ष", "dark": "डार्क", + "dark_theme": "डार्क थीम टॉगल करें", "date_after": "इसके बाद की तारीख", "date_and_time": "तिथि और समय", "date_before": "पहले की तारीख", + "date_format": "ई, एलएलएल डी, वाई • एच:एमएम ए", "date_of_birth_saved": "जन्मतिथि सफलतापूर्वक सहेजी गई", "date_range": "तिथि सीमा", "day": "दिन", "deduplicate_all": "सभी को डुप्लिकेट करें", + "deduplication_criteria_1": "छवि का आकार बाइट्स में", + "deduplication_criteria_2": "EXIF डेटा की संख्या", + "deduplication_info": "डुप्लीकेशन हटाने की जानकारी", + "deduplication_info_description": "परिसंपत्तियों का स्वचालित रूप से पूर्व-चयन करने और डुप्लिकेट को थोक में हटाने के लिए, हम निम्न पर ध्यान देते हैं:", "default_locale": "डिफ़ॉल्ट स्थान", "default_locale_description": "अपने ब्राउज़र स्थान के आधार पर दिनांक और संख्याएँ प्रारूपित करें", "delete": "हटाएँ", + "delete_action_prompt": "{count} स्थायी रूप से हटा दिया गया", "delete_album": "एल्बम हटाएँ", "delete_api_key_prompt": "क्या आप वाकई इस एपीआई कुंजी को हटाना चाहते हैं?", + "delete_dialog_alert": "ये आइटम Immich और आपके डिवाइस से स्थायी रूप से हटा दिए जाएंगे", + "delete_dialog_alert_local": "ये आइटम आपके डिवाइस से स्थायी रूप से हटा दिए जाएंगे, लेकिन फिर भी Immich सर्वर पर उपलब्ध रहेंगे", + "delete_dialog_alert_local_non_backed_up": "कुछ आइटम का Immich में बैकअप नहीं लिया गया है और उन्हें आपके डिवाइस से स्थायी रूप से हटा दिया जाएगा", + "delete_dialog_alert_remote": "ये आइटम Immich सर्वर से स्थायी रूप से हटा दिए जाएंगे", + "delete_dialog_ok_force": "फिर भी हटाएं", + "delete_dialog_title": "स्थायी रूप से हटाएँ", "delete_duplicates_confirmation": "क्या आप वाकई इन डुप्लिकेट को स्थायी रूप से हटाना चाहते हैं?", + "delete_face": "चेहरा हटाएं", "delete_key": "कुंजी हटाएँ", "delete_library": "लाइब्रेरी हटाएँ", "delete_link": "लिंक हटाएँ", + "delete_local_action_prompt": "{count} स्थानीय रूप से हटा दिया गया", + "delete_local_dialog_ok_backed_up_only": "केवल बैकअप हटाएं", + "delete_local_dialog_ok_force": "फिर भी हटाएं", + "delete_others": "अन्य को हटाएँ", "delete_shared_link": "साझा किए गए लिंक को हटाएं", "delete_shared_link_dialog_title": "साझा किए गए लिंक को हटाएं", + "delete_tag": "टैग हटाएं", + "delete_tag_confirmation_prompt": "क्या आप वाकई {tagName} टैग हटाना चाहते हैं?", "delete_user": "उपभोक्ता मिटायें", "deleted_shared_link": "साझा किया गया लिंक हटा दिया गया", + "deletes_missing_assets": "डिस्क से गायब संपत्तियों को हटाता है", "description": "वर्णन", + "description_input_hint_text": "विवरण जोड़ें..।", + "description_input_submit_error": "विवरण अपडेट करते समय त्रुटि हुई, अधिक जानकारी के लिए लॉग देखें", + "deselect_all": "सबको अचयनित करो", "details": "विवरण", "direction": "दिशा", "disabled": "अक्षम", "disallow_edits": "संपादनों की अनुमति न दें", + "discord": "डिसकॉर्ड", "discover": "खोजें", + "discovered_devices": "खोजे गए उपकरण", "dismiss_all_errors": "सभी त्रुटियाँ ख़ारिज करें", "dismiss_error": "त्रुटि ख़ारिज करें", "display_options": "प्रदर्शन चुनाव", @@ -571,14 +787,18 @@ "display_original_photos": "मूल फ़ोटो प्रदर्शित करें", "display_original_photos_setting_description": "किसी संपत्ति को देखते समय थंबनेल के बजाय मूल तस्वीर प्रदर्शित करना पसंद करें जब मूल संपत्ति वेब-संगत हो।", "do_not_show_again": "इस संदेश को दुबारा मत दिखाना", + "documentation": "प्रलेखन", "done": "ठीक है", "download": "डाउनलोड करें", + "download_action_prompt": "{count} संपत्तियां डाउनलोड हो रही हैं", "download_canceled": "डाउनलोड रद्द कर दिया गया", "download_complete": "डाउनलोड पूरा", "download_enqueue": "डाउनलोड कतार में है", "download_error": "डाउनलोड त्रुटि", "download_failed": "डाउनलोड विफल", "download_finished": "डाउनलोड समाप्त", + "download_include_embedded_motion_videos": "एम्बेडेड वीडियो", + "download_include_embedded_motion_videos_description": "मोशन फ़ोटो में एम्बेड किए गए वीडियो को एक अलग फ़ाइल के रूप में शामिल करें", "download_notfound": "डाउनलोड नहीं मिला", "download_paused": "डाउनलोड स्थगित", "download_settings": "डाउनलोड करना", @@ -588,6 +808,7 @@ "download_sucess_android": "मीडिया DCIM/Immich में डाउनलोड हो गया है", "download_waiting_to_retry": "पुनः प्रयास करने का इंतजार कर रहा है", "downloading": "डाउनलोड", + "downloading_asset_filename": "संपत्ति {filename} डाउनलोड हो रही है", "downloading_media": "मीडिया डाउनलोड हो रहा है", "drop_files_to_upload": "अपलोड करने के लिए फ़ाइलें कहीं भी छोड़ें", "duplicates": "डुप्लिकेट", @@ -598,6 +819,7 @@ "edit_avatar": "अवतार को एडिट करें", "edit_date": "संपादन की तारीख", "edit_date_and_time": "दिनांक और समय संपादित करें", + "edit_description": "संपादित करें वर्णन", "edit_exclusion_pattern": "बहिष्करण पैटर्न संपादित करें", "edit_faces": "चेहरे संपादित करें", "edit_import_path": "आयात पथ संपादित करें", @@ -816,7 +1038,6 @@ "library_options": "पुस्तकालय विकल्प", "light": "रोशनी", "like_deleted": "जैसे हटा दिया गया", - "link_options": "लिंक विकल्प", "link_to_oauth": "OAuth से लिंक करें", "linked_oauth_account": "लिंक किया गया OAuth खाता", "list": "सूची", diff --git a/i18n/hr.json b/i18n/hr.json index 35e7aba7e0..fd17e12dc2 100644 --- a/i18n/hr.json +++ b/i18n/hr.json @@ -34,6 +34,7 @@ "added_to_favorites_count": "Dodano {count, number} u omiljeno", "admin": { "add_exclusion_pattern_description": "Dodajte uzorke izuzimanja. Globiranje pomoću *, ** i ? je podržano. Za ignoriranje svih datoteka u bilo kojem direktoriju pod nazivom \"Raw\", koristite \"**/Raw/**\". Da biste zanemarili sve datoteke koje završavaju na \".tif\", koristite \"**/*.tif\". Da biste zanemarili apsolutni put, koristite \"/path/to/ignore/**\".", + "admin_user": "Administrator", "asset_offline_description": "Ovo sredstvo vanjske knjižnice više nije pronađeno na disku i premješteno je u smeće. Ako je datoteka premještena unutar biblioteke, provjerite svoju vremensku traku za novo odgovarajuće sredstvo. Da biste vratili ovo sredstvo, provjerite može li Immich pristupiti donjoj stazi datoteke i skenirajte biblioteku.", "authentication_settings": "Postavke autentikacije", "authentication_settings_description": "Uredi lozinku, OAuth, i druge postavke autentikacije", @@ -165,6 +166,20 @@ "metadata_settings_description": "Upravljanje postavkama metapodataka", "migration_job": "Migracija", "migration_job_description": "Premjestite minijature za sredstva i lica u najnoviju strukturu mapa", + "nightly_tasks_cluster_faces_setting_description": "Pokreni prepoznavanje lica na novootkrivenim licima", + "nightly_tasks_cluster_new_faces_setting": "Grupiraj nova lica", + "nightly_tasks_database_cleanup_setting": "Zadaci čišćenja baze podataka", + "nightly_tasks_database_cleanup_setting_description": "Očisti stare, istekle podatke iz baze podataka", + "nightly_tasks_generate_memories_setting": "Generiraj uspomene", + "nightly_tasks_generate_memories_setting_description": "Stvori nove uspomene iz sadržaja", + "nightly_tasks_missing_thumbnails_setting": "Generiraj nedostajuće sličice", + "nightly_tasks_missing_thumbnails_setting_description": "Stavi u red čekanja sadržaje bez sličica za generiranje sličica", + "nightly_tasks_settings": "Postavke noćnih zadataka", + "nightly_tasks_settings_description": "Upravljanje noćnim zadacima", + "nightly_tasks_start_time_setting": "Vrijeme početka", + "nightly_tasks_start_time_setting_description": "Vrijeme pokretanja noćnih zadataka na poslužitelju", + "nightly_tasks_sync_quota_usage_setting": "Sinkroniziraj iskorištenost kvote", + "nightly_tasks_sync_quota_usage_setting_description": "Ažuriraj korisničku kvotu za pohranu na temelju trenutne potrošnje", "no_paths_added": "Nema dodanih putanja", "no_pattern_added": "Nije dodan uzorak", "note_apply_storage_label_previous_assets": "Napomena: da biste primijenili Oznaku Pohrane na prethodno prenesena sredstva, pokrenite", @@ -195,6 +210,8 @@ "oauth_mobile_redirect_uri": "Mobilnog Preusmjeravanja URI", "oauth_mobile_redirect_uri_override": "Nadjačavanje URI-preusmjeravanja za mobilne uređaje", "oauth_mobile_redirect_uri_override_description": "Omogući kada pružatelj OAuth ne dopušta mobilni URI, poput ''{callback}''", + "oauth_role_claim": "Dodjela uloge", + "oauth_role_claim_description": "Automatski dodijeli administratorski pristup na temelju prisutnosti ove tvrdnje. Tvrdnja može sadržavati ili 'user' ili 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Upravljanje postavkama za prijavu kroz OAuth", "oauth_settings_more_details": "Za više pojedinosti o ovoj značajci pogledajte uputstva.", @@ -203,7 +220,7 @@ "oauth_storage_quota_claim": "Zahtjev za kvotom pohrane", "oauth_storage_quota_claim_description": "Automatski postavite korisničku kvotu pohrane na vrijednost ovog zahtjeva.", "oauth_storage_quota_default": "Zadana kvota pohrane (GiB)", - "oauth_storage_quota_default_description": "Kvota u GiB koja će se koristiti kada nema zahtjeva (unesite 0 za neograničenu kvotu).", + "oauth_storage_quota_default_description": "Kvota u GiB koja će se koristiti kada nema zahtjeva", "oauth_timeout": "Istek vremena zahtjeva", "oauth_timeout_description": "Istek vremena zahtjeva je u milisekundama", "password_enable_description": "Prijava s email adresom i zaporkom", @@ -243,6 +260,7 @@ "storage_template_migration_info": "Predložak za pohranu će sve nastavke (ekstenzije) pretvoriti u mala slova. Promjene predloška primjenjivat će se samo na nova sredstva. Za retroaktivnu primjenu predloška na prethodno prenesena sredstva, pokrenite {job}.", "storage_template_migration_job": "Posao Migracije Predloška Pohrane", "storage_template_more_details": "Za više pojedinosti o ovoj značajci pogledajte Predložak pohrane i njegove implikacije", + "storage_template_onboarding_description_v2": "Kada je omogućena, ova će značajka automatski organizira datoteke prema predlošku koji je definirao korisnik. Za više informacija pogledajte dokumentaciju.", "storage_template_path_length": "Približno ograničenje duljine putanje: {length, number}/{limit, number}", "storage_template_settings": "Predložak pohrane", "storage_template_settings_description": "Upravljajte strukturom mape i nazivom datoteke učitanog sredstva", @@ -355,10 +373,12 @@ "admin_password": "Admin lozinka", "administration": "Administracija", "advanced": "Napredno", + "advanced_settings_beta_timeline_subtitle": "Isprobaj novo iskustvo aplikacije", + "advanced_settings_beta_timeline_title": "Beta vremenska crta", "advanced_settings_enable_alternate_media_filter_subtitle": "Koristite ovu opciju za filtriranje medija tijekom sinkronizacije na temelju alternativnih kriterija. Pokušajte ovo samo ako imate problema s aplikacijom koja ne prepoznaje sve albume.", "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTALNO] Koristite alternativni filter za sinkronizaciju albuma na uređaju", "advanced_settings_log_level_title": "Razina zapisivanja: {level}", - "advanced_settings_prefer_remote_subtitle": "Neki uređaji sporo učitavaju sličice s resursa na uređaju. Aktivirajte ovu postavku kako biste umjesto toga učitali slike s udaljenih izvora.", + "advanced_settings_prefer_remote_subtitle": "Neki uređaji sporo učitavaju sličice s lokalnih resursa. Aktivirajte ovu postavku kako biste umjesto toga učitali slike s udaljenih izvora.", "advanced_settings_prefer_remote_title": "Preferiraj udaljene slike", "advanced_settings_proxy_headers_subtitle": "Definirajte zaglavlja posrednika koja Immich treba slati sa svakim mrežnim zahtjevom.", "advanced_settings_proxy_headers_title": "Proxy zaglavlja", @@ -377,6 +397,7 @@ "album_cover_updated": "Naslovnica albuma ažurirana", "album_delete_confirmation": "Jeste li sigurni da želite izbrisati album {album}?", "album_delete_confirmation_description": "Ako se ovaj album dijeli, drugi korisnici mu više neće moći pristupiti.", + "album_deleted": "Album izbrisan", "album_info_card_backup_album_excluded": "IZUZETO", "album_info_card_backup_album_included": "UKLJUČENO", "album_info_updated": "Podaci o albumu ažurirani", @@ -386,6 +407,7 @@ "album_options": "Opcije albuma", "album_remove_user": "Ukloni korisnika?", "album_remove_user_confirmation": "Jeste li sigurni da želite ukloniti {user}?", + "album_search_not_found": "Nema albuma koji odgovaraju vašem pretraživanju", "album_share_no_users": "Čini se da ste podijelili ovaj album sa svim korisnicima ili nemate nijednog korisnika s kojim biste ga dijelili.", "album_updated": "Album ažuriran", "album_updated_setting_description": "Primite obavijest e-poštom kada dijeljeni album ima nova sredstva", @@ -405,6 +427,7 @@ "albums_default_sort_order": "Zadani redoslijed sortiranja albuma", "albums_default_sort_order_description": "Početni redoslijed sortiranja elemenata prilikom izrade novih albuma.", "albums_feature_description": "Zbirke resursa koje se mogu dijeliti s drugim korisnicima.", + "albums_on_device_count": "Albumi na uređaju ({count})", "all": "Sve", "all_albums": "Svi albumi", "all_people": "Svi ljudi", @@ -425,6 +448,7 @@ "app_settings": "Postavke Aplikacije", "appears_in": "Pojavljuje se u", "archive": "Arhiva", + "archive_action_prompt": "{count} dodano u arhivu", "archive_or_unarchive_photo": "Arhivirajte ili dearhivirajte fotografiju", "archive_page_no_archived_assets": "Nema arhiviranih resursa", "archive_page_title": "Arhiviraj ({count})", @@ -462,10 +486,12 @@ "assets": "Sredstva", "assets_added_count": "Dodano {count, plural, one {# asset} other {# assets}}", "assets_added_to_album_count": "Dodano {count, plural, one {# asset} other {# assets}} u album", - "assets_cannot_be_added_to_album_count": "{count, plural,\n one {Nije moguće dodati medij u album}\n few {Nije moguće dodati # medija u album}\n other {Nije moguće dodati # medija u album}\n}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Sadržaj se ne može dodati u album} other {{count} sadržaja se ne mogu dodati u album}}", "assets_count": "{count, plural, one {# asset} other {# assets}}", "assets_deleted_permanently": "{count} resurs(i) uspješno uklonjeni", "assets_deleted_permanently_from_server": "{count} resurs(i) trajno obrisan(i) sa Immich poslužitelja", + "assets_downloaded_failed": "{count, plural, one {Preuzeta # datoteka – {error} datoteka nije uspjela} other {Preuzeto je # datoteka – {error} datoteke nisu uspjele}}", + "assets_downloaded_successfully": "{count, plural, one {Uspješno preuzeta # datoteka} other {Uspješno preuzete # datoteke}}", "assets_moved_to_trash_count": "{count, plural, one {# asset} other {# asset}} premješteno u smeće", "assets_permanently_deleted_count": "Trajno izbrisano {count, plural, one {# asset} other {# assets}}", "assets_removed_count": "Uklonjeno {count, plural, one {# asset} other {# assets}}", @@ -480,10 +506,12 @@ "authorized_devices": "Ovlašteni Uređaji", "automatic_endpoint_switching_subtitle": "Povežite se lokalno preko naznačene Wi-Fi mreže kada je dostupna i koristite alternativne veze na drugim lokacijama", "automatic_endpoint_switching_title": "Automatsko prebacivanje URL-a", + "autoplay_slideshow": "Automatsko prikazivanje slajdova", "back": "Nazad", "back_close_deselect": "Natrag, zatvorite ili poništite odabir", "background_location_permission": "Dozvola za lokaciju u pozadini", "background_location_permission_content": "Kako bi prebacivao mreže dok radi u pozadini, Immich mora *uvijek* imati pristup preciznoj lokaciji kako bi aplikacija mogla pročitati naziv Wi-Fi mreže", + "backup": "Sigurnosna kopija", "backup_album_selection_page_albums_device": "Albumi na uređaju ({count})", "backup_album_selection_page_albums_tap": "Dodirnite za uključivanje, dvostruki dodir za isključivanje", "backup_album_selection_page_assets_scatter": "Resursi mogu biti raspoređeni u više albuma. Stoga, albumi mogu biti uključeni ili isključeni tijekom procesa sigurnosnog kopiranja.", @@ -547,6 +575,8 @@ "backup_options_page_title": "Opcije sigurnosnog kopiranja", "backup_setting_subtitle": "Upravljajte postavkama učitavanja u pozadini i prvom planu", "backward": "Unazad", + "beta_sync": "Beta status sinkronizacije", + "beta_sync_subtitle": "Upravljaj novim sustavom sinkronizacije", "biometric_auth_enabled": "Biometrijska autentikacija omogućena", "biometric_locked_out": "Zaključani ste iz biometrijske autentikacije", "biometric_no_options": "Nema dostupnih biometrijskih opcija", @@ -564,7 +594,7 @@ "cache_settings_clear_cache_button": "Očisti predmemoriju", "cache_settings_clear_cache_button_title": "Briše predmemoriju aplikacije. Ovo će značajno utjecati na performanse aplikacije dok se predmemorija ponovno ne izgradi.", "cache_settings_duplicated_assets_clear_button": "OČISTI", - "cache_settings_duplicated_assets_subtitle": "Fotografije i videozapisi koje je aplikacija stavila na crnu listu", + "cache_settings_duplicated_assets_subtitle": "Fotografije i videozapisi koje je aplikacija ignorira", "cache_settings_duplicated_assets_title": "Duplicirani resursi ({count})", "cache_settings_statistics_album": "Sličice biblioteke", "cache_settings_statistics_full": "Pune slike", @@ -581,6 +611,7 @@ "cancel": "Otkaži", "cancel_search": "Otkaži pretragu", "canceled": "Otkazano", + "canceling": "Otkazivanje", "cannot_merge_people": "Nije moguće spojiti osobe", "cannot_undo_this_action": "Ne možete poništiti ovu radnju!", "cannot_update_the_description": "Nije moguće ažurirati opis", @@ -644,6 +675,7 @@ "confirm_password": "Potvrdite lozinku", "confirm_tag_face": "Želite li označiti ovo lice kao {name}?", "confirm_tag_face_unnamed": "Želite li označiti ovo lice?", + "connected_device": "Uređaj povezan", "connected_to": "Povezano s", "contain": "Sadrži", "context": "Kontekst", @@ -693,9 +725,11 @@ "current_server_address": "Trenutna adresa poslužitelja", "custom_locale": "Prilagođena Lokalizacija", "custom_locale_description": "Formatiranje datuma i brojeva na temelju jezika i regije", + "custom_url": "Prilagođena URL adresa", "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Tamno", + "dark_theme": "Prebaci tamnu temu", "date_after": "Datum nakon", "date_and_time": "Datum i Vrijeme", "date_before": "Datum prije", @@ -711,6 +745,8 @@ "default_locale": "Zadana lokalizacija", "default_locale_description": "Oblikujte datume i brojeve na temelju jezika preglednika", "delete": "Izbriši", + "delete_action_confirmation_message": "Jeste li sigurni da želite izbrisati ovaj sadržaj? Ova radnja će premjestiti sadržaj u smeće na poslužitelju i upitat će vas želite li ga izbrisati i lokalno", + "delete_action_prompt": "{count} izbrisano", "delete_album": "Izbriši album", "delete_api_key_prompt": "Jeste li sigurni da želite izbrisati ovaj API ključ?", "delete_dialog_alert": "Ove stavke bit će trajno izbrisane iz Immicha i s vašeg uređaja", @@ -724,9 +760,12 @@ "delete_key": "Ključ za brisanje", "delete_library": "Izbriši knjižnicu", "delete_link": "Izbriši poveznicu", + "delete_local_action_prompt": "{count} izbrisano lokalno", "delete_local_dialog_ok_backed_up_only": "Izbriši samo sigurnosno kopirane", "delete_local_dialog_ok_force": "Izbriši svejedno", "delete_others": "Izbriši druge", + "delete_permanently": "Izbriši trajno", + "delete_permanently_action_prompt": "{count} trajno izbrisano", "delete_shared_link": "Izbriši dijeljenu poveznicu", "delete_shared_link_dialog_title": "Izbriši dijeljenu poveznicu", "delete_tag": "Izbriši oznaku", @@ -737,12 +776,14 @@ "description": "Opis", "description_input_hint_text": "Dodaj opis...", "description_input_submit_error": "Pogreška pri ažuriranju opisa, provjerite zapisnik za više detalja", + "deselect_all": "Poništi odabir svih", "details": "Detalji", "direction": "Smjer", "disabled": "Onemogućeno", "disallow_edits": "Zabrani izmjene", "discord": "Discord", "discover": "Otkrij", + "discovered_devices": "Otkriveni uređaji", "dismiss_all_errors": "Odbaci sve pogreške", "dismiss_error": "Odbaci pogrešku", "display_options": "Mogućnosti prikaza", @@ -753,6 +794,7 @@ "documentation": "Dokumentacija", "done": "Gotovo", "download": "Preuzmi", + "download_action_prompt": "Preuzimanje {count} sadržaja", "download_canceled": "Preuzimanje otkazano", "download_complete": "Preuzimanje završeno", "download_enqueue": "Preuzimanje dodano u red", @@ -790,6 +832,7 @@ "edit_key": "Ključ za uređivanje", "edit_link": "Uredi poveznicu", "edit_location": "Uredi lokaciju", + "edit_location_action_prompt": "{count} uređenih lokacija", "edit_location_dialog_title": "Lokacija", "edit_name": "Uredi ime", "edit_people": "Uredi ljude", @@ -808,6 +851,7 @@ "empty_trash": "Isprazni smeće", "empty_trash_confirmation": "Jeste li sigurni da želite isprazniti smeće? Time će se iz Immicha trajno ukloniti sva sredstva u otpadu.\nNe možete poništiti ovu radnju!", "enable": "Omogući", + "enable_backup": "Omogući sigurnosnu kopiju", "enable_biometric_auth_description": "Unesite svoj PIN kod za omogućavanje biometrijske autentikacije", "enabled": "Omogućeno", "end_date": "Datum završetka", @@ -964,6 +1008,8 @@ "explorer": "Pretraživač (Explorer)", "export": "Izvoz", "export_as_json": "Izvezi kao JSON", + "export_database": "Izvezi bazu podataka", + "export_database_description": "Izvezi SQLite bazu podataka", "extension": "Proširenje (Extension)", "external": "Vanjski", "external_libraries": "Vanjske Biblioteke", @@ -975,6 +1021,7 @@ "failed_to_load_assets": "Neuspjelo učitavanje stavki", "failed_to_load_folder": "Neuspjelo učitavanje mape", "favorite": "Omiljeno", + "favorite_action_prompt": "{count} dodano u Omiljeno", "favorite_or_unfavorite_photo": "Omiljena ili neomiljena fotografija", "favorites": "Omiljene", "favorites_page_no_favorites": "Nema pronađenih omiljenih stavki", @@ -1014,6 +1061,9 @@ "haptic_feedback_switch": "Omogući haptičku povratnu informaciju", "haptic_feedback_title": "Haptička povratna informacija", "has_quota": "Ima kvotu", + "hash_asset": "Hash sadržaja", + "hashed_assets": "Hashirani sadržaji", + "hashing": "Hashiranje", "header_settings_add_header_tip": "Dodaj zaglavlje", "header_settings_field_validator_msg": "Vrijednost ne može biti prazna", "header_settings_header_name_input": "Naziv zaglavlja", @@ -1046,6 +1096,7 @@ "host": "Domaćin", "hour": "Sat", "id": "ID", + "idle": "Neaktivan", "ignore_icloud_photos": "Ignoriraj iCloud fotografije", "ignore_icloud_photos_description": "Fotografije pohranjene na iCloudu neće biti učitane na Immich poslužitelj", "image": "Slika", @@ -1099,6 +1150,9 @@ "kept_this_deleted_others": "Zadržana je ova datoteka i izbrisano {count, plural, one {# datoteka} other {# datoteka}}", "keyboard_shortcuts": "Prečaci tipkovnice", "language": "Jezik", + "language_no_results_subtitle": "Pokušajte prilagoditi pojam za pretraživanje", + "language_no_results_title": "Nisu pronađeni jezici", + "language_search_hint": "Pretraži jezike...", "language_setting_description": "Odaberite željeni jezik", "last_seen": "Zadnji put viđen", "latest_version": "Najnovija verzija", @@ -1115,15 +1169,18 @@ "library_page_sort_created": "Datum kreiranja", "library_page_sort_last_modified": "Zadnja izmjena", "library_page_sort_title": "Naslov albuma", + "licenses": "Licence", "light": "Svjetlo", "like_deleted": "Like izbrisan", "link_motion_video": "Povežite videozapis pokreta", - "link_options": "Opcije veze", "link_to_oauth": "Veza na OAuth", "linked_oauth_account": "Povezani OAuth račun", "list": "Popis", "loading": "Učitavanje", "loading_search_results_failed": "Učitavanje rezultata pretraživanja nije uspjelo", + "local": "Lokalno", + "local_asset_cast_failed": "Nije moguće reproducirati sadržaj koji nije prenesen na poslužitelj", + "local_assets": "Lokalni sadržaji", "local_network": "Lokalna mreža", "local_network_sheet_info": "Aplikacija će se povezati s poslužiteljem putem ovog URL-a kada koristi određenu Wi-Fi mrežu", "location_permission": "Dozvola za lokaciju", @@ -1137,6 +1194,7 @@ "locked_folder": "Zaključana Mapa", "log_out": "Odjavi se", "log_out_all_devices": "Odjava sa svih uređaja", + "logged_in_as": "Prijavljeni kao {user}", "logged_out_all_devices": "Odjavljeni su svi uređaji", "logged_out_device": "Odjavljen uređaj", "login": "Prijava", @@ -1179,8 +1237,7 @@ "manage_your_devices": "Upravljajte uređajima na kojima ste prijavljeni", "manage_your_oauth_connection": "Upravljajte svojom OAuth vezom", "map": "Karta", - "map_assets_in_bound": "{count} fotografija", - "map_assets_in_bounds": "{count} fotografija", + "map_assets_in_bounds": "{count, plural, one {# fotografija} other {# fotografija}}", "map_cannot_get_user_location": "Nije moguće dohvatiti lokaciju korisnika", "map_location_dialog_yes": "Da", "map_location_picker_page_use_location": "Koristi ovu lokaciju", @@ -1232,6 +1289,7 @@ "more": "Više", "move": "Pomakni", "move_off_locked_folder": "Premjesti iz zaključane mape", + "move_to_lock_folder_action_prompt": "{count} dodano u zaključanu mapu", "move_to_locked_folder": "Premjesti u zaključanu mapu", "move_to_locked_folder_confirmation": "Ove fotografije i videozapis bit će uklonjeni iz svih albuma i bit će vidljivi samo iz zaključane mape", "moved_to_archive": "Premješteno {count, plural, one {# resurs} other {# resursa}} u arhivu", @@ -1264,6 +1322,7 @@ "no_archived_assets_message": "Arhivirajte fotografije i videozapise kako biste ih sakrili iz prikaza fotografija", "no_assets_message": "KLIKNITE DA PRENESETE SVOJU PRVU FOTOGRAFIJU", "no_assets_to_show": "Nema stavki za prikaz", + "no_cast_devices_found": "Nisu pronađeni uređaji za reprodukciju", "no_duplicates_found": "Nisu pronađeni duplikati.", "no_exif_info_available": "Nema dostupnih exif podataka", "no_explore_results_message": "Prenesite više fotografija da istražite svoju zbirku.", @@ -1277,6 +1336,7 @@ "no_results": "Nema rezultata", "no_results_description": "Pokušajte sa sinonimom ili općenitijom ključnom riječi", "no_shared_albums_message": "Stvorite album za dijeljenje fotografija i videozapisa s osobama u svojoj mreži", + "no_uploads_in_progress": "Nema aktivnih prijenosa", "not_in_any_album": "Ni u jednom albumu", "not_selected": "Nije odabrano", "note_apply_storage_label_to_previously_uploaded assets": "Napomena: Da biste primijenili Oznaku za skladištenje na prethodno prenesena sredstva, pokrenite", @@ -1296,8 +1356,11 @@ "oldest_first": "Prvo najstarije", "on_this_device": "Na ovom uređaju", "onboarding": "Uključivanje (Onboarding)", - "onboarding_privacy_description": "Sljedeće (neobavezne) značajke oslanjaju se na vanjske usluge i mogu se onemogućiti u bilo kojem trenutku u postavkama administracije.", + "onboarding_locale_description": "Odaberite željeni jezik. Kasnije ga možete promijeniti u postavkama.", + "onboarding_privacy_description": "Sljedeće (neobavezne) značajke oslanjaju se na vanjske usluge i mogu se onemogućiti u bilo kojem trenutku u postavkama.", + "onboarding_server_welcome_description": "Postavimo vašu instancu s nekim uobičajenim postavkama.", "onboarding_theme_description": "Odaberite temu boja za svoj primjer. To možete kasnije promijeniti u postavkama.", + "onboarding_user_welcome_description": "Počnimo!", "onboarding_welcome_user": "Dobro došli, {user}", "online": "Dostupan (Online)", "only_favorites": "Samo omiljeno", @@ -1311,6 +1374,7 @@ "original": "originalno", "other": "Ostalo", "other_devices": "Ostali uređaji", + "other_entities": "Ostali entiteti", "other_variables": "Ostale varijable", "owned": "Vlasništvo", "owner": "Vlasnik", @@ -1442,6 +1506,7 @@ "purchase_server_description_2": "Status podupiratelja", "purchase_server_title": "Poslužitelj (Server)", "purchase_settings_server_activated": "Ključem proizvoda poslužitelja upravlja administrator", + "queue_status": "Stavljanje u red {count}/{total}", "rating": "Broj zvjezdica", "rating_clear": "Obriši ocjenu", "rating_count": "{count, plural, one {# zvijezda} other {# zvijezde}}", @@ -1470,6 +1535,8 @@ "refreshing_faces": "Osvježavanje lica", "refreshing_metadata": "Osvježavanje metapodataka", "regenerating_thumbnails": "Obnavljanje sličica", + "remote": "Udaljeno", + "remote_assets": "Udaljeni sadržaji", "remove": "Ukloni", "remove_assets_album_confirmation": "Jeste li sigurni da želite ukloniti {count, plural, one {# datoteku} other {# datoteke}} iz albuma?", "remove_assets_shared_link_confirmation": "Jeste li sigurni da želite ukloniti {count, plural, one {# datoteku} other {# datoteke}} iz ove dijeljene veze?", @@ -1477,12 +1544,15 @@ "remove_custom_date_range": "Ukloni prilagođeni datumski raspon", "remove_deleted_assets": "Ukloni izbrisana sredstva", "remove_from_album": "Ukloni iz albuma", + "remove_from_album_action_prompt": "{count} uklonjeno iz albuma", "remove_from_favorites": "Ukloni iz favorita", + "remove_from_lock_folder_action_prompt": "{count} uklonjeno iz zaključane mape", "remove_from_locked_folder": "Ukloni iz zaključane mape", "remove_from_locked_folder_confirmation": "Jeste li sigurni da želite premjestiti ove fotografije i videozapise iz zaključane mape? Bit će vidljivi u vašoj biblioteci.", "remove_from_shared_link": "Ukloni iz dijeljene poveznice", "remove_memory": "Ukloni uspomenu", "remove_photo_from_memory": "Ukloni fotografiju iz ove uspomene", + "remove_tag": "Ukloni oznaku", "remove_url": "Ukloni URL", "remove_user": "Ukloni korisnika", "removed_api_key": "Uklonjen API ključ: {name}", @@ -1504,11 +1574,15 @@ "reset_password": "Resetiraj lozinku", "reset_people_visibility": "Poništi vidljivost ljudi", "reset_pin_code": "Resetiraj PIN kod", + "reset_sqlite": "Resetiraj SQLite bazu podataka", + "reset_sqlite_confirmation": "Jeste li sigurni da želite resetirati SQLite bazu podataka? Morat ćete se odjaviti i ponovno prijaviti kako biste ponovno sinkronizirali podatke", + "reset_sqlite_success": "SQLite baza podataka je uspješno resetirana", "reset_to_default": "Vrati na zadano", "resolve_duplicates": "Riješite duplikate", "resolved_all_duplicates": "Razriješi sve duplikate", "restore": "Oporavi", "restore_all": "Oporavi sve", + "restore_trash_action_prompt": "{count} vraćeno iz smeća", "restore_user": "Vrati korisnika", "restored_asset": "Obnovljena datoteka", "resume": "Nastavi", @@ -1517,6 +1591,7 @@ "role": "Uloga", "role_editor": "Urednik", "role_viewer": "Gledatelj", + "running": "U tijeku", "save": "Spremi", "save_to_gallery": "Spremi u galeriju", "saved_api_key": "Spremljen API ključ", @@ -1589,6 +1664,7 @@ "select_album_cover": "Odaberite omot albuma", "select_all": "Odaberi sve", "select_all_duplicates": "Odaberi sve duplikate", + "select_all_in": "Odaberi sve u {group}", "select_avatar_color": "Odaberi boju avatara", "select_face": "Odaberi lice", "select_featured_photo": "Odaberi istaknutu fotografiju", @@ -1609,6 +1685,7 @@ "server_info_box_server_url": "URL poslužitelja", "server_offline": "Server izvan mreže", "server_online": "Server na mreži", + "server_privacy": "Privatnost poslužitelja", "server_stats": "Statistike servera", "server_version": "Verzija servera", "set": "Postavi", @@ -1618,6 +1695,7 @@ "set_date_of_birth": "Postavi datum rođenja", "set_profile_picture": "Postavi profilnu sliku", "set_slideshow_to_fullscreen": "Postavi prezentaciju na cijeli zaslon", + "set_stack_primary_asset": "Postavi kao glavni sadržaj", "setting_image_viewer_help": "Preglednik detalja prvo učitava malu sličicu, zatim učitava pregled srednje veličine (ako je omogućen), te na kraju učitava original (ako je omogućen).", "setting_image_viewer_original_subtitle": "Omogućite za učitavanje originalne slike pune rezolucije (velika!). Onemogućite za smanjenje potrošnje podataka (i mrežne i na predmemoriji uređaja).", "setting_image_viewer_original_title": "Učitaj originalnu sliku", @@ -1645,6 +1723,7 @@ "settings_saved": "Postavke su spremljene", "setup_pin_code": "Postavi PIN kod", "share": "Podijeli", + "share_action_prompt": "Podijeljeno {count} sadržaja", "share_add_photos": "Dodaj fotografije", "share_assets_selected": "{count} odabrano", "share_dialog_preparing": "Priprema...", @@ -1666,6 +1745,7 @@ "shared_link_clipboard_copied_massage": "Kopirano u međuspremnik", "shared_link_clipboard_text": "Poveznica: {link}\nLozinka: {password}", "shared_link_create_error": "Pogreška pri kreiranju dijeljene poveznice", + "shared_link_custom_url_description": "Pristupite ovom dijeljenom linku pomoću prilagođene URL adrese", "shared_link_edit_description_hint": "Unesite opis dijeljenja", "shared_link_edit_expire_after_option_day": "1 dan", "shared_link_edit_expire_after_option_days": "{count} dana", @@ -1691,6 +1771,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Upravljanje dijeljenim poveznicama", "shared_link_options": "Opcije dijeljene poveznice", + "shared_link_password_description": "Zahtjevaj loziku za pristup ovom dijeljenom linku", "shared_links": "Dijeljene poveznice", "shared_links_description": "Podijelite fotografije i videozapise putem poveznice", "shared_photos_and_videos_count": "{assetCount, plural, =1 {# podijeljena fotografija ili videozapis.} few {# podijeljene fotografije i videozapisa.} other {# podijeljenih fotografija i videozapisa.}}", @@ -1746,6 +1827,7 @@ "sort_title": "Naslov", "source": "Izvor", "stack": "Složi", + "stack_action_prompt": "{count} složeno", "stack_duplicates": "Složi duplikate", "stack_select_one_photo": "Odaberi jednu glavnu fotografiju za slaganje", "stack_selected_photos": "Složi odabrane fotografije", @@ -1755,6 +1837,7 @@ "start_date": "Datum početka", "state": "Stanje", "status": "Status", + "stop_casting": "Zaustavi reprodukciju", "stop_motion_photo": "Zaustavi pokretnu fotografiju", "stop_photo_sharing": "Prestati dijeliti svoje fotografije?", "stop_photo_sharing_description": "{partner} više neće moći pristupiti vašim fotografijama.", @@ -1764,6 +1847,7 @@ "storage_quota": "Kvota Pohrane", "storage_usage": "{used} od {available} iskorišteno", "submit": "Pošalji", + "success": "Uspijeh", "suggestions": "Prijedlozi", "sunrise_on_the_beach": "Izlazak sunca na plaži", "support": "Podrška", @@ -1773,6 +1857,8 @@ "sync": "Sink.", "sync_albums": "Sinkroniziraj albume", "sync_albums_manual_subtitle": "Sinkroniziraj sve prenesene videozapise i fotografije u odabrane albume za sigurnosnu kopiju", + "sync_local": "Sinkroniziraj lokalno", + "sync_remote": "Sinkroniziraj udaljeno", "sync_upload_album_setting_subtitle": "Kreiraj i prenesi svoje fotografije i videozapise u odabrane albume na Immichu", "tag": "Oznaka", "tag_assets": "Označi stavke", @@ -1783,6 +1869,7 @@ "tag_updated": "Ažurirana oznaka: {tag}", "tagged_assets": "Označena {count, plural, =1 {# stavka} few {# stavke} other {# stavki}}", "tags": "Oznake", + "tap_to_run_job": "Dodirnite za pokretanje zadatka", "template": "Predložak", "theme": "Tema", "theme_selection": "Izbor teme", @@ -1815,6 +1902,7 @@ "total": "Ukupno", "total_usage": "Ukupna upotreba", "trash": "Smeće", + "trash_action_prompt": "{count} premješteno u smeće", "trash_all": "Stavi sve u smeće", "trash_count": "Smeće {count, number}", "trash_delete_asset": "Premjesti u smeće / Izbriši stavku", @@ -1832,8 +1920,11 @@ "unable_to_change_pin_code": "Nije moguće promijeniti PIN kod", "unable_to_setup_pin_code": "Nije moguće postaviti PIN kod", "unarchive": "Poništi arhiviranje", + "unarchive_action_prompt": "{count} uklonjeno iz arhive", "unarchived_count": "{count, plural, =1 {Poništeno arhiviranje #} few {Poništeno arhiviranje #} other {Poništeno arhiviranje #}}", + "undo": "Poništi", "unfavorite": "Ukloni iz omiljenih", + "unfavorite_action_prompt": "{count} uklonjeno iz favorita", "unhide_person": "Prikaži osobu", "unknown": "Nepoznato", "unknown_country": "Nepoznata država", @@ -1849,16 +1940,22 @@ "unsaved_change": "Nespremljena promjena", "unselect_all": "Poništi odabir svih", "unselect_all_duplicates": "Poništi odabir svih duplikata", + "unselect_all_in": "Poništi odabir svih u {group}", "unstack": "Razdvoji", + "unstack_action_prompt": "{count} razloženo", "unstacked_assets_count": "Razdvojena {count, plural, =1 {# stavka} few {# stavke} other {# stavki}}", + "untagged": "Bez oznaka", "up_next": "Sljedeće", "updated_at": "Ažurirano", "updated_password": "Lozinka ažurirana", "upload": "Prijenos", + "upload_action_prompt": "{count} u redu za prijenos", "upload_concurrency": "Istovremeni prijenosi", + "upload_details": "Detalji prijenosa", "upload_dialog_info": "Želite li sigurnosno kopirati odabranu stavku(e) na poslužitelj?", "upload_dialog_title": "Prenesi stavku", "upload_errors": "Prijenos završen s {count, plural, =1 {# greškom} few {# greške} other {# grešaka}}, osvježite stranicu da biste vidjeli nove prenesene stavke.", + "upload_finished": "Prijenos završen", "upload_progress": "Preostalo {remaining, number} - Obrađeno {processed, number}/{total, number}", "upload_skipped_duplicates": "Preskočena {count, plural, =1 {# duplicirana stavka} few {# duplicirane stavke} other {# dupliciranih stavki}}", "upload_status_duplicates": "Duplikati", @@ -1867,6 +1964,7 @@ "upload_success": "Prijenos uspješan, osvježite stranicu da biste vidjeli nove prenesene stavke.", "upload_to_immich": "Prenesi na Immich ({count})", "uploading": "Prijenos u tijeku", + "uploading_media": "Prijenos medija", "url": "URL", "usage": "Korištenje", "use_biometric": "Koristi biometriju", @@ -1878,6 +1976,7 @@ "user_liked": "{user} je označio/la sviđa mi se {type, select, photo {ovu fotografiju} video {ovaj videozapis} asset {ovu stavku} other {to}}", "user_pin_code_settings": "PIN kod", "user_pin_code_settings_description": "Upravljajte svojim PIN kodom", + "user_privacy": "Privatnost korisnika", "user_purchase_settings": "Kupnja", "user_purchase_settings_description": "Upravljajte svojom kupnjom", "user_role_set": "Postavi {user} kao {role}", @@ -1886,6 +1985,7 @@ "user_usage_stats_description": "Pregledajte statistiku korištenja računa", "username": "Korisničko ime", "users": "Korisnici", + "users_added_to_album_count": "Dodan{o/a} {count, plural, one {# korisnik} few {# korisnika} other {# korisnika}} u album.", "utilities": "Alati", "validate": "Provjeri valjanost", "validate_endpoint_error": "Molimo unesite valjanu URL adresu", @@ -1904,6 +2004,7 @@ "view_album": "Prikaži album", "view_all": "Prikaži sve", "view_all_users": "Prikaži sve korisnike", + "view_details": "Prikaži pojedinosti", "view_in_timeline": "Prikaži na vremenskoj crti", "view_link": "Prikaži poveznicu", "view_links": "Prikaži poveznice", diff --git a/i18n/hu.json b/i18n/hu.json index df995c0d6c..71060b5330 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -167,11 +167,15 @@ "migration_job": "Migrálás", "migration_job_description": "Az elemek és arcok bélyegképeinek migrálása a legújabb mappastruktúrába", "nightly_tasks_cluster_faces_setting_description": "Arcfelismerés futtatása az újonnan érzékelt arcokon", + "nightly_tasks_cluster_new_faces_setting": "Új arcok csoportosítása", "nightly_tasks_database_cleanup_setting": "Adatbázis-tisztítási feladatok", "nightly_tasks_database_cleanup_setting_description": "A régi, lejárt adatok törlése az adatbázisból", "nightly_tasks_generate_memories_setting": "Emlékek generálása", "nightly_tasks_generate_memories_setting_description": "Új emlékek létrehozása elemekből", "nightly_tasks_missing_thumbnails_setting": "Hiányzó indexképek generálása", + "nightly_tasks_settings": "Éjjeli Feladat Beállítások", + "nightly_tasks_settings_description": "Éjjeli feladatok kezelése", + "nightly_tasks_start_time_setting": "Kezdőidő", "no_paths_added": "Nincs megadva elérési útvonal", "no_pattern_added": "Nincs megadva minta (pattern)", "note_apply_storage_label_previous_assets": "Megjegyzés: Ha a korábban feltöltött elemekhez is szeretne Tárhely Címkéket társítani, akkor futtassa ezt", @@ -363,6 +367,7 @@ "admin_password": "Admin Jelszó", "administration": "Adminisztráció", "advanced": "Haladó", + "advanced_settings_beta_timeline_title": "Béta Idővonal", "advanced_settings_enable_alternate_media_filter_subtitle": "Ezzel a beállítással a szinkronizálás során alternatív kritériumok alapján szűrheted a fájlokat. Csak akkor próbáld ki, ha problémáid vannak azzal, hogy az alkalmazás nem ismeri fel az összes albumot.", "advanced_settings_enable_alternate_media_filter_title": "[KÍSÉRLETI] Alternatív eszköz album szinkronizálási szűrő használata", "advanced_settings_log_level_title": "Naplózás szintje: {level}", @@ -385,6 +390,7 @@ "album_cover_updated": "Album borító frissítve", "album_delete_confirmation": "Biztos, hogy ki szeretnéd törölni a(z) {album} albumot?", "album_delete_confirmation_description": "Amennyiben ez egy megosztott album, a többi felhasználó sem fog tudni többé hozzáférni.", + "album_deleted": "Album törölve", "album_info_card_backup_album_excluded": "KIHAGYVA", "album_info_card_backup_album_included": "BELEÉRTVE", "album_info_updated": "Album infó frissítve", @@ -413,6 +419,7 @@ "albums_default_sort_order": "Alapértelmezett album rendezés", "albums_default_sort_order_description": "Alapértelmezett sorrendezés új albumok létrehozásánál.", "albums_feature_description": "Másokkal megosztható elemek gyűjteménye.", + "albums_on_device_count": "Albumok az eszközön ({count})", "all": "Mind", "all_albums": "Minden album", "all_people": "Minden személy", @@ -474,6 +481,7 @@ "assets_count": "{count, plural, other {# elem}}", "assets_deleted_permanently": "{count} elem véglegesen törölve", "assets_deleted_permanently_from_server": "{count} elem véglegesen törölve az Immich szerverről", + "assets_downloaded_failed": "{count, plural, one {# fájl letöltve - {error} fájl sikertelen} other {# fájl letöltve - {error} fájl sikertelen}}", "assets_downloaded_successfully": "{count, plural, one {# fájl sikeresen letöltve} other {# fájl sikeresen letöltve}}", "assets_moved_to_trash_count": "{count, plural, other {# elem}} áthelyezve a lomtárba", "assets_permanently_deleted_count": "{count, plural, other {# elem}} véglegesen törölve", @@ -493,6 +501,7 @@ "back_close_deselect": "Vissza, bezárás, vagy kijelölés törlése", "background_location_permission": "Háttérben történő helymeghatározási engedély", "background_location_permission_content": "Hálózatok automatikus váltásához az Immich-nek *mindenképpen* hozzá kell férnie a pontos helyzethez, hogy az alkalmazás le tudja kérni a Wi-Fi hálózat nevét", + "backup": "Mentés", "backup_album_selection_page_albums_device": "Ezen az eszközön lévő albumok ({count})", "backup_album_selection_page_albums_tap": "Koppints a hozzáadáshoz, duplán koppints az eltávolításhoz", "backup_album_selection_page_assets_scatter": "Egy elem több albumban is lehet. Ezért a mentéshez albumokat lehet hozzáadni vagy azokat a mentésből kihagyni.", @@ -556,6 +565,8 @@ "backup_options_page_title": "Biztonági mentés beállításai", "backup_setting_subtitle": "A háttérben és előtérben mentés beállításainak kezelése", "backward": "Visszafele", + "beta_sync": "Béta Szinkronizálás Állapota", + "beta_sync_subtitle": "Az új szinkronizálási rendszer kezelése", "biometric_auth_enabled": "Biometrikus azonosítás engedélyezve", "biometric_locked_out": "Ki vagy zárva a biometrikus azonosításból", "biometric_no_options": "Nincsen elérhető biometrikus azonosítás", @@ -572,7 +583,7 @@ "cache_settings_clear_cache_button": "Gyorsítótár kiürítése", "cache_settings_clear_cache_button_title": "Kiüríti az alkalmazás gyorsítótárát. Ez jelentősen kihat az alkalmazás teljesítményére, amíg a gyorsítótár újra nem épül.", "cache_settings_duplicated_assets_clear_button": "KIÜRÍT", - "cache_settings_duplicated_assets_subtitle": "Fotók és videók, amiket az alkalmazás fekete listára tett", + "cache_settings_duplicated_assets_subtitle": "Fotók és videók, amiket az alkalmazás figyelmen kívül hagyott", "cache_settings_duplicated_assets_title": "Duplikált Elemek ({count})", "cache_settings_statistics_album": "Képtár bélyegképei", "cache_settings_statistics_full": "Teljes méretű képek", @@ -703,6 +714,7 @@ "daily_title_text_date": "MMM dd (E)", "daily_title_text_date_year": "yyyy MMM dd (E)", "dark": "Sötét", + "dark_theme": "Sötét téma kapcsolása", "date_after": "Dátumtól", "date_and_time": "Dátum és Idő", "date_before": "Dátumig", @@ -718,6 +730,7 @@ "default_locale": "Alapértelmezett Területi Beállítás", "default_locale_description": "Dátumok és számok formázása a böngésződ területi beállítása alapján", "delete": "Törlés", + "delete_action_prompt": "{count} törölve", "delete_album": "Album törlése", "delete_api_key_prompt": "Biztosan törölni szeretnéd ezt az API kulcsot?", "delete_dialog_alert": "Ezek az elemek véglegesen törölve lesznek Immich-ről és az eszközödről is", @@ -731,9 +744,12 @@ "delete_key": "Kulcs törlése", "delete_library": "Képtár Törlése", "delete_link": "Link törlése", + "delete_local_action_prompt": "{count} törölve az eszközről", "delete_local_dialog_ok_backed_up_only": "Csak a Biztonsági Mentés Törlése", "delete_local_dialog_ok_force": "Törlés Mindenképp", "delete_others": "Többi törlése", + "delete_permanently": "Törlés véglegesen", + "delete_permanently_action_prompt": "{count} törölve véglegesen", "delete_shared_link": "Megosztott link törlése", "delete_shared_link_dialog_title": "Megosztott Link Törlése", "delete_tag": "Címke törlése", @@ -761,6 +777,7 @@ "documentation": "Dokumentáció", "done": "Kész", "download": "Letöltés", + "download_action_prompt": "{count} elem letöltése", "download_canceled": "Letöltés megszakítva", "download_complete": "Letöltés kész", "download_enqueue": "Letöltés sorba állítva", @@ -798,6 +815,7 @@ "edit_key": "Kulcs módosítása", "edit_link": "Link módosítása", "edit_location": "Hely módosítása", + "edit_location_action_prompt": "{count} hely változtatva", "edit_location_dialog_title": "Hely", "edit_name": "Név módosítása", "edit_people": "Személyek módosítása", @@ -816,6 +834,7 @@ "empty_trash": "Lomtár ürítése", "empty_trash_confirmation": "Biztosan kiüríted a lomtárat? Ez az Immich lomtárában lévő összes elemet véglegesen törli.\nEz a művelet nem visszavonható!", "enable": "Engedélyezés", + "enable_backup": "Biztonsági mentés bekapcsolása", "enable_biometric_auth_description": "Add meg a jelszavad a biometrikus azonosítás engedélyezéséhez", "enabled": "Engedélyezve", "end_date": "Vég dátum", @@ -958,6 +977,7 @@ "exif_bottom_sheet_person_add_person": "Elnevez", "exif_bottom_sheet_person_age_months": "{months} hónap idős", "exif_bottom_sheet_person_age_year_months": "1 év, {months} hónap idős", + "exif_bottom_sheet_person_age_years": "Életkor: {years}", "exit_slideshow": "Kilépés a Diavetítésből", "expand_all": "Összes kinyitása", "experimental_settings_new_asset_list_subtitle": "Fejlesztés alatt", @@ -971,6 +991,8 @@ "explorer": "Böngésző", "export": "Exportálás", "export_as_json": "Exportálás JSON formátumban", + "export_database": "Adatbázis Exportálása", + "export_database_description": "Az SQLite adatbázis exportálása", "extension": "Kiterjesztés", "external": "Külső Képtár", "external_libraries": "Külső Képtárak", @@ -1123,15 +1145,17 @@ "library_page_sort_created": "Létrehozás ideje", "library_page_sort_last_modified": "Utolsó módosítás ideje", "library_page_sort_title": "Album címe", + "licenses": "Licencek", "light": "Világos", "like_deleted": "Reakció törölve", "link_motion_video": "Motion videó hozzárendelése", - "link_options": "Link beállítások", "link_to_oauth": "Csatlakoztatás OAuth-hoz", "linked_oauth_account": "Csatlakoztatott OAuth felhasználó", "list": "Lista", "loading": "Betöltés", "loading_search_results_failed": "Keresési eredmények betöltése sikertelen", + "local": "Helyi", + "local_assets": "Helyi Elemek", "local_network": "Helyi hálózat", "local_network_sheet_info": "Az alkalmazés ezen az URL címen fogja elérni a szervert, ha a megadott WiFi hálózathoz van csatlankozva", "location_permission": "Helymeghatározási engedély", @@ -1188,8 +1212,7 @@ "manage_your_devices": "Bejelentkezett eszközök kezelése", "manage_your_oauth_connection": "OAuth kapcsolódás kezelése", "map": "Térkép", - "map_assets_in_bound": "{count} fotó", - "map_assets_in_bounds": "{count} fotó", + "map_assets_in_bounds": "{count, plural, one {# fotó} other {# fotó}}", "map_cannot_get_user_location": "A helymeghatározás nem sikerült", "map_location_dialog_yes": "Igen", "map_location_picker_page_use_location": "Kiválasztott hely használata", @@ -1478,6 +1501,8 @@ "refreshing_faces": "Arcok frissítése folyamatban", "refreshing_metadata": "Metaadatok frissítése folyamatban", "regenerating_thumbnails": "Bélyegképek újragenerálása folyamatban", + "remote": "Távoli", + "remote_assets": "Távoli Elemek", "remove": "Eltávolítás", "remove_assets_album_confirmation": "Biztosan el szeretnél távolítani {count, plural, one {# elemet} other {# elemet}} az albumból?", "remove_assets_shared_link_confirmation": "Biztosan el szeretnél távolítani {count, plural, one {# elemet} other {# elemet}} ebből a megosztott linkből?", @@ -1513,6 +1538,7 @@ "reset_password": "Jelszó visszaállítása", "reset_people_visibility": "Személyek láthatóságának visszaállítása", "reset_pin_code": "PIN kód visszaállítása", + "reset_sqlite": "SQLite Adatbázis Visszaállítása", "reset_to_default": "Visszaállítás alapállapotba", "resolve_duplicates": "Duplikátumok feloldása", "resolved_all_duplicates": "Minden duplikátum feloldása", @@ -1583,7 +1609,7 @@ "search_places": "Helyek keresése", "search_rating": "Keresés értékelés szerint...", "search_result_page_new_search_hint": "Új Keresés", - "search_settings": "Keresési beállítások", + "search_settings": "Beállítások keresése", "search_state": "Megye/Állam keresése...", "search_suggestion_list_smart_search_hint_1": "Az intelligens keresés alapértelmezetten be van kapcsolva, metaadatokat így kereshetsz ", "search_suggestion_list_smart_search_hint_2": "m:keresési-kifejezés", @@ -1774,6 +1800,7 @@ "storage_quota": "Tárhely kvóta", "storage_usage": "{used}/{available} használatban", "submit": "Beküldés", + "success": "Siker", "suggestions": "Javaslatok", "sunrise_on_the_beach": "Napkelte a tengerparton", "support": "Támogatás", @@ -1783,6 +1810,8 @@ "sync": "Szinkronizálás", "sync_albums": "Albumok szinkronizálása", "sync_albums_manual_subtitle": "Összes fotó és videó létrehozása és szinkronizálása a kiválasztott Immich albumokba", + "sync_local": "Helyi Szinkronizálása", + "sync_remote": "Távoli Szinkronizálása", "sync_upload_album_setting_subtitle": "Fotók és videók létrehozása és szinkronizálása a kiválasztott Immich albumba", "tag": "Címke", "tag_assets": "Elemek címkézése", diff --git a/i18n/hy.json b/i18n/hy.json index 86cbcd5554..3bb7e1f526 100644 --- a/i18n/hy.json +++ b/i18n/hy.json @@ -28,7 +28,6 @@ "exif_bottom_sheet_person_add_person": "Ավելացնել անուն", "exif_bottom_sheet_person_age_years": "Տարիք {years}", "hi_user": "Բարեւ {name} ({email})", - "map_assets_in_bound": "{count} նկար", "map_assets_in_bounds": "{count} նկարներ", "partner_list_user_photos": "{}-ին նկարները", "photos": "Նկարներ", diff --git a/i18n/id.json b/i18n/id.json index 92cb07ff30..7386887b5f 100644 --- a/i18n/id.json +++ b/i18n/id.json @@ -487,6 +487,7 @@ "authorized_devices": "Perangkat Terautentikasi", "back": "Kembali", "back_close_deselect": "Kembali, tutup, atau batalkan pemilihan", + "backup": "Cadangkan", "backup_album_selection_page_albums_device": "Album di perangkat ({count})", "backup_album_selection_page_albums_tap": "Sentuh untuk memilih, sentuh 2x untuk mengecualikan", "backup_album_selection_page_assets_scatter": "Aset dapat tersebar dalam banyak album. Sehingga album dapat dipilih atau dikecualikan saat proses pencadangan.", @@ -1069,7 +1070,6 @@ "light": "Terang", "like_deleted": "Suka dihapus", "link_motion_video": "Tautan video gerak", - "link_options": "Opsi tautan", "link_to_oauth": "Tautkan ke OAuth", "linked_oauth_account": "Akun OAuth tertaut", "list": "Daftar", @@ -1124,7 +1124,6 @@ "manage_your_devices": "Kelola perangkat Anda yang masuk", "manage_your_oauth_connection": "Kelola koneksi OAuth Anda", "map": "Peta", - "map_assets_in_bound": "{count} foto", "map_assets_in_bounds": "{count} foto", "map_cannot_get_user_location": "Tidak dapat memeroleh lokasi pengguna", "map_location_dialog_yes": "Ya", diff --git a/i18n/it.json b/i18n/it.json index 0aec538a85..49ef0941e1 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -14,6 +14,7 @@ "add_a_location": "Aggiungi una posizione", "add_a_name": "Aggiungi un nome", "add_a_title": "Aggiungi un titolo", + "add_birthday": "Aggiungi un compleanno", "add_endpoint": "Aggiungi endpoint", "add_exclusion_pattern": "Aggiungi un pattern di esclusione", "add_import_path": "Aggiungi un percorso di importazione", @@ -44,6 +45,13 @@ "backup_database": "Crea Dump Database", "backup_database_enable_description": "Abilita i backup del database", "backup_keep_last_amount": "Numero di backup da mantenere", + "backup_onboarding_1_description": "copia offsite nel cloud o in un'altra sede fisica.", + "backup_onboarding_2_description": "copie locali su diversi dispositivi. Ciò include i file principali e un backup di tali file a livello locale.", + "backup_onboarding_3_description": "copie totali dei tuoi dati, compresi i file originali. Ciò include 1 copia offsite e 2 copie locali.", + "backup_onboarding_description": "Per proteggere i tuoi dati, è consigliato adottare una strategia di backup 3-2-1. Per una soluzione di backup completa, è consigliato conservare copie delle foto/video caricati e del database Immich.", + "backup_onboarding_footer": "Per ulteriori informazioni sul backup di Immich, consultare la documentazione.", + "backup_onboarding_parts_title": "Un backup 3-2-1 include:", + "backup_onboarding_title": "Backup", "backup_settings": "Impostazioni Dump database", "backup_settings_description": "Gestisci le impostazioni dei backup.", "cleared_jobs": "Cancellati i processi per: {job}", @@ -397,6 +405,7 @@ "album_cover_updated": "Copertina dell'album aggiornata", "album_delete_confirmation": "Sei sicuro di voler cancellare l'album {album}?", "album_delete_confirmation_description": "Se l'album è condiviso gli altri utenti perderanno l'accesso.", + "album_deleted": "Album eliminato", "album_info_card_backup_album_excluded": "ESCLUSI", "album_info_card_backup_album_included": "INCLUSI", "album_info_updated": "Informazioni dell'album aggiornate", @@ -510,6 +519,7 @@ "back_close_deselect": "Indietro, chiudi o deseleziona", "background_location_permission": "Permesso di localizzazione in background", "background_location_permission_content": "Per fare in modo che sia possibile cambiare rete quando è in esecuzione in background, Immich deve *sempre* avere accesso alla tua posizione precisa in modo da poter leggere il nome della rete Wi-Fi", + "backup": "Backup", "backup_album_selection_page_albums_device": "Album sul dispositivo ({count})", "backup_album_selection_page_albums_tap": "Tap per includere, doppio tap per escludere", "backup_album_selection_page_assets_scatter": "Visto che le risorse possono trovarsi in più album, questi possono essere inclusi o esclusi dal backup.", @@ -573,6 +583,8 @@ "backup_options_page_title": "Opzioni di Backup", "backup_setting_subtitle": "Gestisci le impostazioni di upload in primo piano e in background", "backward": "Indietro", + "beta_sync": "Status sincronizzazione beta", + "beta_sync_subtitle": "Gestisci il nuovo sistema di sincronizzazione", "biometric_auth_enabled": "Autenticazione biometrica attivata", "biometric_locked_out": "Sei stato bloccato dall'autenticazione biometrica", "biometric_no_options": "Nessuna opzione biometrica disponibile", @@ -721,6 +733,7 @@ "current_server_address": "Indirizzo del server in uso", "custom_locale": "Localizzazione personalizzata", "custom_locale_description": "Formatta data e numeri in base alla lingua e al paese", + "custom_url": "URL personalizzato", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Scuro", @@ -740,7 +753,8 @@ "default_locale": "Localizzazione preimpostata", "default_locale_description": "Formatta la data e i numeri in base alle impostazioni del tuo browser", "delete": "Elimina", - "delete_action_prompt": "{count} elementi eliminati definitivamente", + "delete_action_confirmation_message": "Vuoi davvero eliminare questo asset? Questa azione sposterà l'asset nel cestino del server e ti chiederà se desideri eliminarla localmente", + "delete_action_prompt": "{count} elementi eliminati", "delete_album": "Elimina album", "delete_api_key_prompt": "Sei sicuro di voler eliminare questa chiave API?", "delete_dialog_alert": "Questi oggetti saranno eliminati definitivamente da Immich e dal tuo device", @@ -758,6 +772,8 @@ "delete_local_dialog_ok_backed_up_only": "Elimina solo con backup", "delete_local_dialog_ok_force": "Elimina comunque", "delete_others": "Elimina gli altri", + "delete_permanently": "Elimina definitivamente", + "delete_permanently_action_prompt": "{count} eliminati definitivamente", "delete_shared_link": "Elimina link condiviso", "delete_shared_link_dialog_title": "Elimina link condiviso", "delete_tag": "Elimina tag", @@ -813,6 +829,7 @@ "edit": "Modifica", "edit_album": "Modifica album", "edit_avatar": "Modifica avatar", + "edit_birthday": "Modifica Compleanno", "edit_date": "Modifica data", "edit_date_and_time": "Modifica data e ora", "edit_description": "Modifica la descrizione", @@ -980,6 +997,7 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Aggiungi una descrizione...", + "exif_bottom_sheet_description_error": "Errore durante l'aggiornamento della descrizione", "exif_bottom_sheet_details": "DETTAGLI", "exif_bottom_sheet_location": "POSIZIONE", "exif_bottom_sheet_people": "PERSONE", @@ -1000,6 +1018,8 @@ "explorer": "Esplora", "export": "Esporta", "export_as_json": "Esporta come JSON", + "export_database": "Esporta database", + "export_database_description": "Esporta il database SQLite", "extension": "Estensione", "external": "Esterno", "external_libraries": "Librerie esterne", @@ -1051,6 +1071,9 @@ "haptic_feedback_switch": "Abilita feedback aptico", "haptic_feedback_title": "Feedback aptico", "has_quota": "Ha limite", + "hash_asset": "Risorsa hash", + "hashed_assets": "Risorse hash", + "hashing": "Hashing", "header_settings_add_header_tip": "Aggiungi Header", "header_settings_field_validator_msg": "Il valore non può essere vuoto", "header_settings_header_name_input": "Nome header", @@ -1083,6 +1106,7 @@ "host": "Host", "hour": "Ora", "id": "ID", + "idle": "Inattivo", "ignore_icloud_photos": "Ignora foto iCloud", "ignore_icloud_photos_description": "Le foto che sono memorizzate su iCloud non verranno caricate sul server Immich", "image": "Immagine", @@ -1140,6 +1164,7 @@ "language_no_results_title": "Linguaggi non trovati", "language_search_hint": "Cerca linguaggi...", "language_setting_description": "Seleziona la tua lingua predefinita", + "large_files": "File pesanti", "last_seen": "Ultimo accesso", "latest_version": "Ultima Versione", "latitude": "Latitudine", @@ -1159,13 +1184,14 @@ "light": "Chiaro", "like_deleted": "Mi piace rimosso", "link_motion_video": "Collega video in movimento", - "link_options": "Impostazioni Collegamento", "link_to_oauth": "Collegamento a OAuth", "linked_oauth_account": "Account OAuth collegato", "list": "Lista", "loading": "Caricamento", "loading_search_results_failed": "Impossibile caricare i risultati della ricerca", + "local": "Locale", "local_asset_cast_failed": "Impossibile trasmettere una risorsa che non è caricata sul server", + "local_assets": "Risorsa locale", "local_network": "Rete locale", "local_network_sheet_info": "L'app si collegherà al server tramite questo URL quando è in uso la rete Wi-Fi specificata", "location_permission": "Permesso di localizzazione", @@ -1222,8 +1248,7 @@ "manage_your_devices": "Gestisci i tuoi dispositivi collegati", "manage_your_oauth_connection": "Gestisci la tua connessione OAuth", "map": "Mappa", - "map_assets_in_bound": "{count} foto", - "map_assets_in_bounds": "{count} foto", + "map_assets_in_bounds": "{count, plural, one {# foto} other {# foto}}", "map_cannot_get_user_location": "Non è possibile ottenere la posizione dell'utente", "map_location_dialog_yes": "Si", "map_location_picker_page_use_location": "Usa questa posizione", @@ -1322,6 +1347,7 @@ "no_results": "Nessun risultato", "no_results_description": "Prova ad usare un sinonimo oppure una parola chiave più generica", "no_shared_albums_message": "Crea un album per condividere foto e video con le persone nella tua rete", + "no_uploads_in_progress": "Nessun upload in corso", "not_in_any_album": "In nessun album", "not_selected": "Non selezionato", "note_apply_storage_label_to_previously_uploaded assets": "Nota: Per aggiungere l'etichetta dell'archiviazione agli asset caricati in precedenza, esegui", @@ -1359,6 +1385,7 @@ "original": "originale", "other": "Altro", "other_devices": "Altri dispositivi", + "other_entities": "Altre entità", "other_variables": "Altre variabili", "owned": "Posseduti", "owner": "Proprietario", @@ -1519,6 +1546,8 @@ "refreshing_faces": "Aggiornando volti", "refreshing_metadata": "Ricaricando i metadati", "regenerating_thumbnails": "Rigenerando le anteprime", + "remote": "Remoto", + "remote_assets": "Risorse remote", "remove": "Rimuovi", "remove_assets_album_confirmation": "Sei sicuro di voler rimuovere {count, plural, one {# asset} other {# asset}} dall'album?", "remove_assets_shared_link_confirmation": "Sei sicuro di voler rimuovere {count, plural, one {# asset} other {# asset}} da questo link condiviso?", @@ -1556,19 +1585,25 @@ "reset_password": "Ripristina password", "reset_people_visibility": "Ripristina visibilità persone", "reset_pin_code": "Resetta il codice PIN", + "reset_sqlite": "Resetta Database SQLite", + "reset_sqlite_confirmation": "Vuoi davvero reimpostare il database SQLite? Dovrai disconnetterti e riconnetterti per risincronizzare i dati", + "reset_sqlite_success": "Database SQLite reimpostato correttamente", "reset_to_default": "Ripristina i valori predefiniti", "resolve_duplicates": "Risolvi duplicati", "resolved_all_duplicates": "Tutti i duplicati sono stati risolti", "restore": "Ripristina", "restore_all": "Ripristina tutto", + "restore_trash_action_prompt": "{count} ripristinati dal cestino", "restore_user": "Ripristina utente", "restored_asset": "Asset ripristinato", "resume": "Riprendi", "retry_upload": "Riprova caricamento", "review_duplicates": "Esamina duplicati", + "review_large_files": "Revisiona file pesanti", "role": "Ruolo", "role_editor": "Editor", "role_viewer": "Visualizzatore", + "running": "In esecuzione", "save": "Salva", "save_to_gallery": "Salva in galleria", "saved_api_key": "Chiave API salvata", @@ -1722,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "Copiato negli appunti", "shared_link_clipboard_text": "Link: {link}\nPassword: {password}", "shared_link_create_error": "Si è verificato un errore durante la creazione del link condiviso", + "shared_link_custom_url_description": "Accedi a questo link con un URL personalizzato", "shared_link_edit_description_hint": "Inserisci la descrizione della condivisione", "shared_link_edit_expire_after_option_day": "1 giorno", "shared_link_edit_expire_after_option_days": "{count} giorni", @@ -1747,6 +1783,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Gestisci link condivisi", "shared_link_options": "Opzioni link condiviso", + "shared_link_password_description": "Imposta una password per questo link", "shared_links": "Link condivisi", "shared_links_description": "Condividi foto e video con un link", "shared_photos_and_videos_count": "{assetCount, plural, other {# foto & video condivisi.}}", @@ -1822,6 +1859,7 @@ "storage_quota": "Limite Archiviazione", "storage_usage": "{used} di {available} utilizzati", "submit": "Invia", + "success": "Successo", "suggestions": "Suggerimenti", "sunrise_on_the_beach": "Tramonto sulla spiaggia", "support": "Supporto", @@ -1831,6 +1869,8 @@ "sync": "Sincronizza", "sync_albums": "Sincronizza album", "sync_albums_manual_subtitle": "Sincronizza tutti i video e le foto caricate sull'album di backup selezionato", + "sync_local": "Sincronizza gli elementi locali", + "sync_remote": "Sincronizza gli elementi remoti", "sync_upload_album_setting_subtitle": "Crea e carica le tue foto e video sull'album selezionato in Immich", "tag": "Tag", "tag_assets": "Tagga risorse", @@ -1841,6 +1881,7 @@ "tag_updated": "Tag {tag} aggiornata", "tagged_assets": "{count, plural, one {# asset etichettato} other {# asset etichettati}}", "tags": "Tag", + "tap_to_run_job": "Tocca per eseguire l'attività", "template": "Modello", "theme": "Tema", "theme_selection": "Selezione tema", @@ -1895,6 +1936,7 @@ "unarchived_count": "{count, plural, other {Non archiviati #}}", "undo": "Annulla", "unfavorite": "Rimuovi preferito", + "unfavorite_action_prompt": "{count} rimossi dai Favoriti", "unhide_person": "Mostra persona", "unknown": "Sconosciuto", "unknown_country": "Paese sconosciuto", @@ -1912,15 +1954,20 @@ "unselect_all_duplicates": "Deseleziona tutti i duplicati", "unselect_all_in": "Deseleziona tutto in {group}", "unstack": "Rimuovi dal gruppo", + "unstack_action_prompt": "{count} rimossi", "unstacked_assets_count": "{count, plural, one {Separato # asset} other {Separati # asset}}", + "untagged": "Senza tag", "up_next": "Prossimo", "updated_at": "Aggiornato il", "updated_password": "Password aggiornata", "upload": "Carica", + "upload_action_prompt": "{count} accodati per l'upload", "upload_concurrency": "Caricamenti contemporanei", + "upload_details": "Dettagli di caricamento", "upload_dialog_info": "Vuoi fare il backup sul server delle risorse selezionate?", "upload_dialog_title": "Carica file", "upload_errors": "Caricamento completato con {count, plural, one {# errore} other {# errori}}, ricarica la pagina per vedere gli asset caricati.", + "upload_finished": "Upload terminato", "upload_progress": "Rimanenti {remaining, number} - Processati {processed, number}/{total, number}", "upload_skipped_duplicates": "{count, plural, one {Ignorato # asset duplicato} other {Ignorati # asset duplicati}}", "upload_status_duplicates": "Duplicati", @@ -1929,6 +1976,7 @@ "upload_success": "Caricamento completato con successo, aggiorna la pagina per vedere i nuovi asset caricati.", "upload_to_immich": "Carica su Immich ({count})", "uploading": "Caricamento", + "uploading_media": "Caricando i media", "url": "URL", "usage": "Utilizzo", "use_biometric": "Usa biometrica", @@ -1949,6 +1997,7 @@ "user_usage_stats_description": "Consulta le statistiche d'uso dell'account", "username": "Nome utente", "users": "Utenti", + "users_added_to_album_count": "Aggiunti {count, plural, one {# utente} other {# utenti}} all'album", "utilities": "Utilità", "validate": "Validazione", "validate_endpoint_error": "Inserisci un URL valido", @@ -1967,6 +2016,7 @@ "view_album": "Visualizza Album", "view_all": "Vedi tutto", "view_all_users": "Visualizza tutti gli utenti", + "view_details": "Visualizza Dettagli", "view_in_timeline": "Visualizza in timeline", "view_link": "Visualizza link", "view_links": "Visualizza i link", diff --git a/i18n/ja.json b/i18n/ja.json index 69c0f368fa..fd4734febf 100644 --- a/i18n/ja.json +++ b/i18n/ja.json @@ -490,6 +490,7 @@ "back_close_deselect": "戻る、閉じる、選択解除", "background_location_permission": "バックグラウンド位置情報アクセス", "background_location_permission_content": "正常にWi-Fiの名前(SSID)を獲得するにはアプリが常に詳細な位置情報にアクセスできる必要があります", + "backup": "バックアップ", "backup_album_selection_page_albums_device": "デバイス上のアルバム({count})", "backup_album_selection_page_albums_tap": "タップで選択、ダブルタップで除外", "backup_album_selection_page_assets_scatter": "アルバムを選択・除外してバックアップする写真を選ぶ。 (同じ写真が複数のアルバムに登録されていることがあるため)", @@ -1133,7 +1134,6 @@ "light": "ライトモード", "like_deleted": "いいねが削除されました", "link_motion_video": "モーションビデオのリンク", - "link_options": "リンクのオプション", "link_to_oauth": "OAuthへリンクする", "linked_oauth_account": "リンクされたOAuthアカウント", "list": "リスト", @@ -1196,7 +1196,6 @@ "manage_your_devices": "ログインデバイスを管理します", "manage_your_oauth_connection": "OAuth接続を管理します", "map": "地図", - "map_assets_in_bound": "{count}枚", "map_assets_in_bounds": "{count}枚", "map_cannot_get_user_location": "位置情報がゲットできません", "map_location_dialog_yes": "はい", diff --git a/i18n/ko.json b/i18n/ko.json index 1890962e5a..ba6e3549fe 100644 --- a/i18n/ko.json +++ b/i18n/ko.json @@ -507,6 +507,7 @@ "back_close_deselect": "뒤로, 닫기, 선택 취소", "background_location_permission": "백그라운드 위치 권한", "background_location_permission_content": "백그라운드에서 네트워크를 전환하려면, Immich가 Wi-Fi 네트워크 이름을 확인할 수 있도록 '정확한 위치' 권한을 항상 허용해야 합니다.", + "backup": "백업", "backup_album_selection_page_albums_device": "기기의 앨범 ({count})", "backup_album_selection_page_albums_tap": "한 번 탭하면 포함되고, 두 번 탭하면 제외됩니다.", "backup_album_selection_page_assets_scatter": "각 항목은 여러 앨범에 포함될 수 있으며, 백업 진행 중에도 대상 앨범을 포함하거나 제외할 수 있습니다.", @@ -1130,7 +1131,6 @@ "light": "라이트", "like_deleted": "좋아요가 삭제되었습니다.", "link_motion_video": "모션 비디오 링크", - "link_options": "링크 옵션", "link_to_oauth": "OAuth에 연결", "linked_oauth_account": "OAuth 계정이 연결되었습니다.", "list": "목록", @@ -1191,7 +1191,6 @@ "manage_your_devices": "로그인된 기기 관리", "manage_your_oauth_connection": "OAuth 연결 관리", "map": "지도", - "map_assets_in_bound": "사진 {count}개", "map_assets_in_bounds": "사진 {count}개", "map_cannot_get_user_location": "사용자의 위치를 가져올 수 없습니다.", "map_location_dialog_yes": "예", diff --git a/i18n/lt.json b/i18n/lt.json index 14668921d2..5f58f3ffdc 100644 --- a/i18n/lt.json +++ b/i18n/lt.json @@ -95,6 +95,8 @@ "job_settings": "Užduočių nustatymai", "job_settings_description": "Keisti užduočių lygiagretumą", "job_status": "Užduočių būsenos", + "jobs_delayed": "{jobCount, plural, other {# delayed}}", + "jobs_failed": "{jobCount, plural, other {# failed}}", "library_created": "Sukurta biblioteka: {library}", "library_deleted": "Biblioteka ištrinta", "library_import_path_description": "Nurodykite aplanką, kurį norite importuoti. Šiame aplanke, įskaitant poaplankius, bus nuskaityti vaizdai ir vaizdo įrašai.", @@ -167,13 +169,22 @@ "nightly_tasks_cluster_faces_setting_description": "Paleisti veido atpažinimą naujai aptiktiems veidams", "nightly_tasks_cluster_new_faces_setting": "Sugrupuoti naujus veidus", "nightly_tasks_database_cleanup_setting": "Duomenų bazės valymo darbai", + "nightly_tasks_database_cleanup_setting_description": "Išvalyti senus, nebgaliojančius duomenis iš duomenų bazės", + "nightly_tasks_generate_memories_setting": "Kurti prisiminimus", + "nightly_tasks_generate_memories_setting_description": "Iš duomenų kurti naujus prisiminimus", + "nightly_tasks_missing_thumbnails_setting": "Kurti trūkstamas miniatiūras", + "nightly_tasks_missing_thumbnails_setting_description": "Sudaryti įrašų be miniatiūrų eilę miniatiūrų generavimui", + "nightly_tasks_settings": "Naktinių užduočių nustatymai", + "nightly_tasks_settings_description": "Valdyti naktines užduotis", + "nightly_tasks_start_time_setting": "Pradžios laikas", + "nightly_tasks_start_time_setting_description": "Laikas kada serveris pradės vykdyti naktines užduotis", "no_paths_added": "Keliai nepridėti", "no_pattern_added": "Šablonas nepridėtas", "note_apply_storage_label_previous_assets": "Pastaba: norėdami pritaikyti Saugyklos Žymą seniau įkeltiems ištekliams, paleiskite", "note_cannot_be_changed_later": "PASTABA: Vėliau to pakeisti negalima!", "notification_email_from_address": "Iš adreso", - "notification_email_from_address_description": "Siuntėjo elektroninis adresas, pavyzdžiui: \"Immich Photo Server \"", - "notification_email_host_description": "Elektroninio pašto serverio savininkas (pvz. smtp.immich.app)", + "notification_email_from_address_description": "Siuntėjo el. pašto adresas, pavyzdžiui: \"Immich Photo Server \". Būtinai naudokite adresą iš kurio jums galima siųsti laiškus.", + "notification_email_host_description": "Elektroninio pašto serverio adresas (pvz. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Nepaisyti sertifikatų klaidų", "notification_email_ignore_certificate_errors_description": "Nepaisyti TLS sertifikato patvirtinimo klaidų (nerekomenduojama)", "notification_email_password_description": "Slaptažodis, naudojant autentikacijai su elektroninio pašto serveriu", @@ -261,20 +272,32 @@ "template_settings": "Pranešimų šablonai", "template_settings_description": "Tvarkyti pasirinktinius pranešimų šablonus", "theme_custom_css_settings": "Individualizuotas CSS", + "theme_custom_css_settings_description": "CSS leidžiantis keisti Immich dizainą.", "theme_settings": "Temos nustatymai", + "theme_settings_description": "Valdyti Immich web sąsajos pritaikymus", "thumbnail_generation_job": "Generuoti Miniatiūras", "thumbnail_generation_job_description": "Didelių, mažų ir neryškių miniatiūrų generavimas kiekvienam bibliotekos elementui, taip pat miniatiūrų generavimas kiekvienam asmeniui", "transcoding_acceleration_api": "Spartinimo API", + "transcoding_acceleration_api_description": "API kurį naudos paspartintam perkodavimui. Tai veiks pagal \"geriausią bandymą\": nepavykus bus naudojamas programinis perkodavimas. VP9 gali veikti arba ne priklausomai nuo jūsų techninės įrangos.", "transcoding_acceleration_nvenc": "NVENC (reikalinga NVIDIA GPU)", "transcoding_acceleration_qsv": "Quick Sync (reikalingas 7-os arba vėlesnės generacijos Intel procesorius)", + "transcoding_acceleration_rkmpp": "RKMPP (tik Rockchip SOCs)", "transcoding_acceleration_vaapi": "VAAPI", + "transcoding_accepted_audio_codecs": "Priimtini garso kodekai", + "transcoding_accepted_audio_codecs_description": "Pasirinkti kurių garso kodekų nereikia perkoduoti. Naudojma tik kai kurioms perkodavimo taisyklėms.", "transcoding_accepted_containers": "Priimami konteineriai", + "transcoding_accepted_containers_description": "Pasirinkti kurių konteinerių formatų nereikia performuoti į MP4. Naudojama tik kai kurioms perkodavimo taisyklėms.", + "transcoding_accepted_video_codecs": "Priimami vaizdo kodekai", + "transcoding_accepted_video_codecs_description": "Pasirinkti vaizdo kodekus kurių nereikia perkoduoti. Naudojama tik kai kurioms perkodavimo taisyklėms.", "transcoding_advanced_options_description": "Parinktys, kurių daugelis naudotojų keisti neturėtų", "transcoding_audio_codec": "Garso kodekas", "transcoding_audio_codec_description": "Opus yra aukščiausios kokybės variantas, tačiau turi mažesnį suderinamumą su senesniais įrenginiais ar programine įranga.", "transcoding_bitrate_description": "Vaizdo įrašai viršija maksimalią leistiną bitų spartą arba nėra priimtino formato", + "transcoding_codecs_learn_more": "Sužinoti daugiau apie naudojamą terminologiją, naudokite FFmpeg dokumentaciją H.264 codec, HEVC codec and VP9 codec.", "transcoding_constant_quality_mode": "Pastovios kokybės režimas", + "transcoding_constant_quality_mode_description": "ICQ yra geriau nei CPQ, tačiau ne visi įrenginiai palaiko šį spartinimo būdą. Šis pasirinkimas būtų naudojamas kai nustatytas Kodavimas Pagal Kokybę. NVENC nepalaiko šio pasirinkimo todėl bus ignoruojamas.", "transcoding_constant_rate_factor_description": "Video kokybės lygis. Tipinės reikšmės yra 23 jei H.264, 28 jei HVEC, 31 jei VP9, ir 35 jei AV1. Kuo mažesnis tuo kokybiškesnis tačiau didesni failai.", + "transcoding_disabled_description": "Nedaryti perkodavimo, įrašų peržiūra gali neveikti ant kai kūrių sąsajų", "transcoding_hardware_acceleration": "Techninės įrangos spartinimas", "transcoding_hardware_decoding": "Aparatinis dekodavimas", "transcoding_max_bitrate": "Maksimalus bitų srautas", @@ -646,7 +669,6 @@ "level": "Lygis", "library": "Biblioteka", "library_options": "Bibliotekos pasirinktys", - "link_options": "Nuorodų parinktys", "link_to_oauth": "Susieti su OAuth", "linked_oauth_account": "Susieta OAuth paskyra", "list": "Sąrašas", diff --git a/i18n/lv.json b/i18n/lv.json index 2e6b702e22..2c51cc3854 100644 --- a/i18n/lv.json +++ b/i18n/lv.json @@ -14,6 +14,7 @@ "add_a_location": "Pievienot atrašanās vietu", "add_a_name": "Pievienot vārdu", "add_a_title": "Pievienot virsrakstu", + "add_birthday": "Pievienot dzimšanas dienu", "add_endpoint": "Pievienot galapunktu", "add_exclusion_pattern": "Pievienot izslēgšanas šablonu", "add_import_path": "Pievienot importa ceļu", @@ -191,6 +192,7 @@ "age_years": "{years, plural, zero {# gadu} one {# gads} other {# gadi}}", "album_added": "Albums pievienots", "album_cover_updated": "Albuma attēls atjaunināts", + "album_deleted": "Albums dzēsts", "album_info_card_backup_album_excluded": "NEIEKĻAUTS", "album_info_card_backup_album_included": "IEKĻAUTS", "album_info_updated": "Albuma informācija atjaunināta", @@ -209,6 +211,10 @@ "album_viewer_appbar_share_to": "Kopīgot Uz", "album_viewer_page_share_add_users": "Pievienot lietotājus", "albums": "Albumi", + "albums_default_sort_order": "Albuma noklusējuma kārtošanas secība", + "albums_default_sort_order_description": "Sākotnējā failu kārtošanas secība, veidojot jaunus albumus.", + "albums_feature_description": "Failu kolekcijas, kuras var koplietot ar citiem lietotājiem.", + "albums_on_device_count": "Albumi ierīcē ({count})", "all": "Viss", "all_albums": "Visi albumi", "all_people": "Visi cilvēki", @@ -217,6 +223,7 @@ "allow_edits": "Atļaut labošanu", "allow_public_user_to_download": "Atļaut lejupielādēt publiskiem lietotājiem", "allow_public_user_to_upload": "Atļaut augšupielādēt publiskiem lietotājiem", + "alt_text_qr_code": "QR koda attēls", "anti_clockwise": "Pretēji pulksteņrādītāja virzienam", "api_key": "API atslēga", "api_key_description": "Šī vērtība tiks parādīta tikai vienu reizi. Nokopējiet to pirms loga aizvēršanas.", @@ -250,6 +257,7 @@ "authorized_devices": "Autorizētās ierīces", "automatic_endpoint_switching_title": "Automātiska URL pārslēgšana", "back": "Atpakaļ", + "backup": "Dublēšana", "backup_album_selection_page_albums_device": "Albumi ierīcē ({count})", "backup_album_selection_page_albums_tap": "Pieskarieties, lai iekļautu, veiciet dubultskārienu, lai izslēgtu", "backup_album_selection_page_assets_scatter": "Aktīvi var būt izmētāti pa vairākiem albumiem. Tādējādi dublēšanas procesā albumus var iekļaut vai neiekļaut.", @@ -335,6 +343,7 @@ "cache_settings_title": "Kešdarbes iestatījumi", "camera": "Fotokamera", "cancel": "Atcelt", + "canceling": "Atceļ", "cannot_merge_people": "Nevar apvienot cilvēkus", "change_date": "Mainīt datumu", "change_description": "Mainīt aprakstu", @@ -476,6 +485,7 @@ "empty_trash": "Iztukšot atkritni", "enable_biometric_auth_description": "Lai iespējotu biometrisko autentifikāciju, Ievadiet savu PIN kodu", "end_date": "Beigu datums", + "enqueued": "Ierindots", "enter_wifi_name": "Ievadi Wi-Fi nosaukumu", "enter_your_pin_code": "Ievadi savu PIN kodu", "enter_your_pin_code_subtitle": "Ievadi savu PIN kodu, lai piekļūtu slēgtajai mapei", @@ -485,6 +495,8 @@ "cant_get_faces": "Nevar iegūt sejas", "cant_search_people": "Neizdevās veikt peronu meklēšanu", "failed_to_create_album": "Neizdevās izveidot albumu", + "import_path_already_exists": "Šis importa ceļš jau pastāv.", + "incorrect_email_or_password": "Nepareizs e-pasts vai parole", "profile_picture_transparent_pixels": "Profila attēlos nevar būt caurspīdīgi pikseļi. Lūdzu, palielini un/vai pārvieto attēlu.", "unable_to_change_description": "Neizdevās nomainīt aprakstu", "unable_to_create_user": "Neizdevās izveidot lietotāju", @@ -512,6 +524,8 @@ "expired": "Derīguma termiņš beidzās", "explore": "Izpētīt", "export": "Eksportēt", + "export_database": "Eksportēt datubāzi", + "export_database_description": "Eksportēt SQLite datubāzi", "extension": "Paplašinājums", "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", "failed_to_authenticate": "Neizdevās autentificēties", @@ -527,6 +541,10 @@ "folders": "Mapes", "gcast_enabled": "Google Cast", "get_help": "Saņemt palīdzību", + "getting_started": "Pirmie soļi", + "go_back": "Doties atpakaļ", + "go_to_folder": "Doties uz mapi", + "go_to_search": "Doties uz meklēšanu", "haptic_feedback_switch": "Iestatīt haptisku reakciju", "haptic_feedback_title": "Haptiska Reakcija", "has_quota": "Ir kvota", @@ -583,6 +601,7 @@ "language": "Valoda", "language_search_hint": "Meklēt valodas...", "language_setting_description": "Izvēlieties vēlamo valodu", + "large_files": "Lielie faili", "last_seen": "Pēdējo reizi redzēts", "latest_version": "Jaunākā versija", "latitude": "Ģeogrāfiskais platums", @@ -597,6 +616,7 @@ "library_page_sort_created": "Jaunākais izveidotais", "library_page_sort_last_modified": "Pēdējo reizi modificēts", "library_page_sort_title": "Albuma virsraksts", + "licenses": "Licences", "list": "Saraksts", "loading": "Ielādē", "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", @@ -640,7 +660,6 @@ "manage_your_devices": "Pieslēgto ierīču pārvaldība", "manage_your_oauth_connection": "OAuth savienojumu pārvaldība", "map": "Karte", - "map_assets_in_bound": "{count} fotoattēls", "map_assets_in_bounds": "{count} fotoattēli", "map_cannot_get_user_location": "Nevar iegūt lietotāja atrašanās vietu", "map_location_dialog_yes": "Jā", @@ -834,6 +853,7 @@ "purchase_server_description_2": "Atbalstītāja statuss", "purchase_server_title": "Serveris", "purchase_settings_server_activated": "Servera produkta atslēgu pārvalda administrators", + "queue_status": "Ierindo {count}/{total}", "rating_clear": "Noņemt vērtējumu", "rating_description": "Rādīt EXIF vērtējumu informācijas panelī", "reaction_options": "Reakcijas iespējas", @@ -875,6 +895,7 @@ "resume": "Turpināt", "retry_upload": "Atkārtot augšupielādi", "review_duplicates": "Pārskatīt dublikātus", + "review_large_files": "Pārskatīt lielos failus", "role": "Loma", "role_editor": "Redaktors", "role_viewer": "Skatītājs", @@ -1090,12 +1111,15 @@ "updated_at": "Atjaunināts", "updated_password": "Parole ir atjaunināta", "upload": "Augšupielādēt", + "upload_action_prompt": "{count} ierindoti augšupielādei", "upload_dialog_info": "Vai vēlaties veikt izvēlētā(-o) aktīva(-u) dublējumu uz servera?", "upload_dialog_title": "Augšupielādēt Aktīvu", + "upload_finished": "Augšupielāde pabeigta", "upload_status_duplicates": "Dublikāti", "upload_status_errors": "Kļūdas", "upload_status_uploaded": "Augšupielādēts", "upload_to_immich": "Augšupielādēt Immich ({count})", + "uploading_media": "Augšupielādē failus", "url": "URL", "usage": "Lietojums", "use_biometric": "Izmantot biometrisko autentifikāciju", diff --git a/i18n/mk.json b/i18n/mk.json index 31d223cbd6..938d2158da 100644 --- a/i18n/mk.json +++ b/i18n/mk.json @@ -199,7 +199,6 @@ "level": "Ниво", "library": "Библиотека", "light": "Светло", - "link_options": "Опции за линк", "list": "Листа", "loading": "Вчитување", "log_out": "Одјави се", diff --git a/i18n/ml.json b/i18n/ml.json index 8d25cb5604..8787367bb6 100644 --- a/i18n/ml.json +++ b/i18n/ml.json @@ -3,10 +3,71 @@ "account": "അക്കൗണ്ട്", "account_settings": "അക്കൗണ്ട് സെറ്റിംഗ്സ്", "acknowledge": "അംഗീകരിക്കുക", + "action": "ആക്ഷന്‍", + "action_common_update": "പുതുക്കുക", + "actions": "പ്രവർത്തികൾ", + "active": "സജീവമായവ", + "activity": "പ്രവർത്തനങ്ങൾ", "add": "ചേർക്കുക", "add_a_description": "ഒരു വിവരണം ചേർക്കുക", "add_a_location": "ഒരു സ്ഥലം ചേർക്കുക", "add_a_name": "ഒരു പേര് ചേർക്കുക", "add_a_title": "ഒരു ശീർഷകം ചേർക്കുക", - "add_endpoint": "എൻഡ്പോയിന്റ് ചേർക്കുക" + "add_endpoint": "എൻഡ്പോയിന്റ് ചേർക്കുക", + "add_exclusion_pattern": "ഒഴിവാക്കാനുള്ള മാതൃക ചേർക്കുക", + "add_import_path": "ഇറക്കുമതി ചെയ്യുക", + "add_location": "സ്ഥലനാമം ചേര്‍ക്കുക", + "add_more_users": "കൂടുതല്‍ ഉപയോക്താക്കളെ ചേര്‍ക്കുക", + "add_partner": "പങ്കാളിയെ ചേര്‍ക്കുക", + "add_path": "പാത ചേര്‍ക്കുക", + "add_photos": "ചിത്രങ്ങള്‍ ചേര്‍ക്കുക", + "add_tag": "ടാഗ് ചേര്‍ക്കുക", + "add_to": "ചേര്‍ക്കുക", + "add_to_album": "ആല്‍ബത്തിലേക്ക് ചേര്‍ക്കുക", + "add_to_album_bottom_sheet_added": "{album} - ലേക്ക് ചേര്‍ത്തു", + "add_to_album_bottom_sheet_already_exists": "{album} ആൽബത്തിൽ ഇപ്പോള്‍ തന്നെ ഉണ്ട്", + "add_to_shared_album": "പങ്കിട്ട ആൽബത്തിലേക്ക് ചേർക്കുക", + "add_url": "URL ചേര്‍ക്കുക", + "added_to_archive": "ചരിത്രരേഖയായി (ആര്‍ക്കൈവ്) ചേര്‍ത്തിരിക്കുന്നു", + "added_to_favorites": "ഇഷ്ടപ്പെട്ടവയിലേക്ക് ചേര്‍ത്തു", + "added_to_favorites_count": "{count, number} ഇഷ്ടപ്പെട്ടവയിലേക്ക് ചേര്‍ത്തു", + "admin": { + "add_exclusion_pattern_description": "ഒഴിവാക്കൽ ചിഹ്നങ്ങള്‍ ചേർക്കുക. *, **, ? എന്നിവ ഉപയോഗിച്ചുള്ള ഗ്ലോബിംഗ് പിന്തുണയ്ക്കുന്നു. \"Raw\" എന്ന് പേരുള്ള ഏതെങ്കിലും ഡയറക്ടറിയിലെ എല്ലാ ഫയലുകളും അവഗണിക്കാൻ, \"**/Raw/**\" ഉപയോഗിക്കുക. \".tif\" ൽ അവസാനിക്കുന്ന എല്ലാ ഫയലുകളും അവഗണിക്കാൻ, \"**/*.tif\" ഉപയോഗിക്കുക. ഒരു പരിപൂർണ്ണമായ പാത അവഗണിക്കാൻ, \"/path/to/ignore/**\" ഉപയോഗിക്കുക.", + "admin_user": "ഭരണാധികാരി", + "asset_offline_description": "ഈ പുറത്തുള്ള ശേഖരത്തിലെ വസ്തുക്കള്‍ ഇനി ഡിസ്കിൽ കാണുന്നില്ല, അവയെ ട്രാഷിലേക്ക് നീക്കിയിരിക്കുന്നു. ഫയൽ ലൈബ്രറിക്കുള്ളിൽ നിന്ന് നീക്കിയിട്ടുണ്ടെങ്കിൽ, പുതിയ അനുബന്ധ വസ്തുവിനായി നിങ്ങളുടെ സമയക്രമം (ടൈംലൈന്‍) പരിശോധിക്കുക. ഈ വസ്തു പുനഃസ്ഥാപിക്കാൻ, താഴെയുള്ള ഫയൽ പാത്ത് ഇമ്മിച്ചിന് എത്തിപ്പെടാന്‍ കഴിയുമെന്ന് ഉറപ്പാക്കുകയും ശേഖരം പുനഃപരിശോധിക്കുകയും (സ്കാൻ) ചെയ്യുക.", + "authentication_settings": "ആധികാരികതാ സജ്ജീകരണങ്ങൾ", + "authentication_settings_description": "പാസ്സ്‌വേര്‍ഡ്‌, OAuth തുടങ്ങിയ സജ്ജീകരണങ്ങള്‍", + "authentication_settings_disable_all": "എല്ലാ പ്രവേശന (ലോഗിൻ) രീതികളും പ്രവർത്തനരഹിതമാക്കണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ? പ്രവേശനങ്ങള്‍ പൂർണ്ണമായും പ്രവർത്തനരഹിതമാക്കപ്പെടും.", + "background_task_job": "പശ്ചാത്തല പ്രവര്‍ത്തികള്‍", + "backup_database": "ഡാറ്റാബേസ് ഡംമ്പ് ഉണ്ടാക്കുക", + "backup_database_enable_description": "ഡാറ്റാബേസ് ഡമ്പുകൾ പ്രാപ്തമാക്കുക", + "backup_keep_last_amount": "പഴയ ഡാറ്റാബേസ് ഡമ്പുകൾ എത്രയെണ്ണം സൂക്ഷിക്കണം", + "backup_settings": "ഡാറ്റാബേസ് ഡമ്പ് ക്രമീകരണങ്ങള്‍", + "backup_settings_description": "ഡാറ്റാബേസ് ഡമ്പ് ക്രമീകരണങ്ങൾ കൈകാര്യം ചെയ്യുക.", + "cleared_jobs": "{job} - ന്‍റെ ജോലികള്‍ മായ്ച്ചിരിക്കുന്നു", + "config_set_by_file": "ക്രമീകരണങ്ങള്‍ ഇപ്പോള്‍ ഒരു ക്രമീകരണ ഫയല്‍ വഴിയാണ് നിശ്ചയിക്കുന്നത്", + "confirm_delete_library": "{library} മായ്ച്ചു കളയണം എന്നുറപ്പാണോ?", + "confirm_delete_library_assets": "ഈ ശേഖരം ഇല്ലാതാക്കണം എന്ന് ഉറപ്പാണോ? ഇത് ഇമ്മിച്ചിൽ നിന്ന് {count, plural, one {# contained asset} other {all # contained assets}} ഇല്ലാതാക്കും, ഇത് പഴയപടിയാക്കാൻ കഴിയില്ല. ഫയലുകൾ ഡിസ്കിൽ തന്നെ തുടരും.", + "confirm_email_below": "തീര്‍ച്ചപ്പെടുത്താന്‍ {email} താഴെ കൊടുക്കുക", + "confirm_reprocess_all_faces": "എല്ലാ മുഖങ്ങളും വീണ്ടും കണ്ടെത്തണം എന്ന് ഉറപ്പാണോ? ഇത് ഇതിനകം പേരു ചേര്‍ത്ത മുഖങ്ങളെയും ആളുകളെയും മായ്ക്കും.", + "confirm_user_password_reset": "{user} എന്ന ഉപയോക്താവിന്‍റെ പാസ്സ്‌വേര്‍ഡ്‌ പുനഃക്രമീകരിക്കണം എന്നുറപ്പാണോ?", + "confirm_user_pin_code_reset": "{user} എന്ന ഉപയോക്താവിന്‍റെ PIN പുനഃക്രമീകരിക്കണം എന്നുറപ്പാണോ?", + "create_job": "ജോലി സൃഷ്ടിക്കുക", + "cron_expression": "ക്രോണ്‍ (cron) പ്രയോഗശൈലി", + "cron_expression_description": "ക്രോൺ ഫോർമാറ്റ് ഉപയോഗിച്ച് സ്കാനിംഗ് ഇടവേള സജ്ജമാക്കുക. കൂടുതൽ വിവരങ്ങൾക്ക് Crontab Guru സന്ദര്‍ശിക്കുക", + "cron_expression_presets": "മുന്‍കൂട്ടി തയ്യാര്‍ ചെയ്ത ക്രോണ്‍ പ്രവര്‍ത്തനശൈലികള്‍", + "disable_login": "ലോഗിന്‍ തടയുക", + "duplicate_detection_job_description": "സമാനമായ ചിത്രങ്ങൾ കണ്ടെത്താൻ വസ്തുവഹകളില്‍ യന്ത്രപഠനം പ്രവർത്തിപ്പിക്കുക. ഇത് സ്മാർട്ട് സര്‍ച്ചിനെ ആശ്രയിക്കുന്നു", + "exclusion_pattern_description": "നിങ്ങളുടെ ലൈബ്രറി സ്കാൻ ചെയ്യുമ്പോൾ ഫയലുകളും ഫോൾഡറുകളും അവഗണിക്കാൻ ഒഴിവാക്കല്‍ മാതൃകകള്‍ നിങ്ങളെ അനുവദിക്കുന്നു. RAW ഫയലുകൾ പോലുള്ള നിങ്ങൾക്ക് ഇറക്കുമതി ചെയ്യാൻ താൽപ്പര്യമില്ലാത്ത ഫയലുകൾ അടങ്ങിയ ഫോൾഡറുകൾ ഉണ്ടെങ്കിൽ ഇത് ഉപയോഗപ്രദമാണ്.", + "external_library_management": "ബാഹ്യമായശേഖരങ്ങളുടെ നിയന്ത്രണം", + "face_detection": "മുഖങ്ങള്‍ കണ്ടെത്തുക", + "face_detection_description": "യന്ത്രപഠനം ഉപയോഗിച്ച് വസ്തുക്കളിലെ മുഖങ്ങൾ കണ്ടെത്തുക. വീഡിയോകൾക്ക്, തംബ്‌നെയിൽ മാത്രമേ പരിഗണിക്കൂ. \"Refresh\" എല്ലാ വസ്തുക്കളും (വീണ്ടും) പ്രോസസ്സ് ചെയ്യുന്നു. കൂടാതെ \"Reset\" നിലവിലുള്ള എല്ലാ മുഖളും വിവരങ്ങളും മായ്‌ക്കുന്നു. \"Missing\" ഇതുവരെ ക്രമീകരിക്കാത്ത വസ്തുക്കളെ വരിയിലേക്ക് നിർത്തുന്നു. മുഖം തിരിച്ചറിയൽ പൂർത്തിയായ ശേഷം കണ്ടെത്തിയ മുഖങ്ങൾ മുഖം തിരിച്ചറിയലിനായി ക്യൂവിൽ നിർത്തും, അവയെ നിലവിലുള്ളതോ പുതിയതോ ആയ ആളുകളിലേക്ക് ഗ്രൂപ്പുചെയ്യും.", + "facial_recognition_job_description": "കണ്ടെത്തിയ മുഖങ്ങളെ ആളുകളുടെ കൂട്ടം ആക്കുക. മുഖം കണ്ടെത്തല്‍ ഘട്ടത്തിനു ശേഷമേ ഇത് ഉണ്ടാകൂ. \"Reset\" വീണ്ടും കൂട്ടങ്ങളെ ഉണ്ടാക്കും. \"Missing\" ആളെ നിയോഗിക്കാത്ത മുഖങ്ങളെ വരിയിലേക്ക് ചേര്‍ക്കുന്നു.", + "failed_job_command": "{job} എന്ന ജോലിക്ക് വേണ്ടിയുള്ള ആജ്ഞ {command} പരാജയപ്പെട്ടിരിക്കുന്നു", + "force_delete_user_warning": "മുന്നറിയിപ്പ്: ഇത് ഉപയോക്താവിനെയും എല്ലാ വസ്തുക്കളേയും ഉടനടി നീക്കം ചെയ്യും. ഇത് പഴയപടിയാക്കാനോ ഫയലുകൾ വീണ്ടെടുക്കാനോ കഴിയില്ല.", + "image_format": "ഘടന", + "image_format_description": "WebP ഉണ്ടാക്കാന്‍ സമയം എടുക്കും എങ്കിലും JPEG ഫയലുകളെക്കാള്‍ ചെറുതായിരിക്കും.", + "image_fullsize_description": "അധികവിവരങ്ങള്‍ ഒഴിവാക്കിയ ചിത്രം, വലുതാക്കി കാണിക്കുമ്പോള്‍ ഉപയോഗിക്കപ്പെടുന്നു", + "image_fullsize_enabled": "പൂര്‍ണ വലുപ്പത്തില്‍ ഉള്ള ചിത്രങ്ങള്‍ ഉണ്ടാക്കാന്‍പ്രാപ്തമാക്കുക" + } } diff --git a/i18n/nb_NO.json b/i18n/nb_NO.json index 2abc08e7f2..1d8a3455e7 100644 --- a/i18n/nb_NO.json +++ b/i18n/nb_NO.json @@ -14,6 +14,7 @@ "add_a_location": "Legg til sted", "add_a_name": "Legg til navn", "add_a_title": "Legg til tittel", + "add_birthday": "Legg til bursdag", "add_endpoint": "API endepunkt", "add_exclusion_pattern": "Legg til ekskluderingsmønster", "add_import_path": "Legg til importsti", @@ -44,16 +45,23 @@ "backup_database": "Opprett database-dump", "backup_database_enable_description": "Aktiver database-dump", "backup_keep_last_amount": "Antall database-dumps å beholde", + "backup_onboarding_1_description": "ekstern kopi i skyen eller på et annet fysisk sted.", + "backup_onboarding_2_description": "lokale kopier på forsskjellige enheter. Dette inkluderer hovedfilene og en lokal sikkerhetskopi av disse filene.", + "backup_onboarding_3_description": "totale kopier av dataene dine, inkludert originalfilene. Dette inkluderer én ekstern kopi og to lokale kopier.", + "backup_onboarding_description": "En 3-2-1 sikkerhetskopieringsstrategi anbefales for å beskytte dataene dine. Du bør beholde kopier av opplastede bilder/videoer samt Immich-databasen for en omfattende sikkerhetskopieringsløsning.", + "backup_onboarding_footer": "For mer informasjon om sikkerhetskopiering av Immich, se dokumentasjonen.", + "backup_onboarding_parts_title": "En 3-2-1 sikkerhetskopi inkluderer:", + "backup_onboarding_title": "Sikkerhetskopier", "backup_settings": "Database-dump instillinger", "backup_settings_description": "Håndter innstillinger for database-dump.", "cleared_jobs": "Ryddet opp jobber for: {job}", "config_set_by_file": "Konfigurasjonen er for øyeblikket satt av en konfigurasjonsfil", "confirm_delete_library": "Er du sikker på at du vil slette biblioteket {library}?", - "confirm_delete_library_assets": "Er du sikker på at du vil slette dette biblioteket? Dette vil slette alle {count, plural, one {# contained asset} other {all # contained assets}} tilhørende eiendeler fra Immich og kan ikke angres. Filene vil forbli på disken.", + "confirm_delete_library_assets": "Er du sikker på at du vil slette dette biblioteket? Dette vil slette alt innhold ({count, plural, one {# objekt} other {# objekter}}) og tilhørende eiendeler fra Immich og kan ikke angres. Filene vil forbli på disken.", "confirm_email_below": "For å bekrefte, skriv inn \"{email}\" nedenfor", "confirm_reprocess_all_faces": "Er du sikker på at du vil behandle alle ansikter på nytt? Dette vil også fjerne navngitte personer.", "confirm_user_password_reset": "Er du sikker på at du vil tilbakestille passordet til {user}?", - "confirm_user_pin_code_reset": "Er du sikker på at du vil resette {user}'s PIN kode?", + "confirm_user_pin_code_reset": "Er du sikker på at du vil tilbakestille PIN-koden til {user} ?", "create_job": "Lag jobb", "cron_expression": "Cron uttrykk", "cron_expression_description": "Still inn skanneintervallet med cron-formatet. For mer informasjon henvises til f.eks. Crontab Guru", @@ -397,6 +405,7 @@ "album_cover_updated": "Albumomslag oppdatert", "album_delete_confirmation": "Er du sikker på at du vil slette albumet {album}?", "album_delete_confirmation_description": "Hvis dette albumet deles, vil andre brukere miste tilgangen til dette.", + "album_deleted": "Album slettet", "album_info_card_backup_album_excluded": "EKSKLUDERT", "album_info_card_backup_album_included": "INKLUDERT", "album_info_updated": "Albuminformasjon oppdatert", @@ -483,25 +492,25 @@ "asset_viewer_settings_subtitle": "Endre dine visningsinnstillinger for galleriet", "asset_viewer_settings_title": "Objektviser", "assets": "Filer", - "assets_added_count": "Lagt til {count, plural, one {# element} other {# elementer}}", - "assets_added_to_album_count": "Lagt til {count, plural, one {# asset} other {# assets}} i album", - "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} kan ikke legges til i albumet", + "assets_added_count": "Lagt til {count, plural, one {# objekt} other {# objekter}}", + "assets_added_to_album_count": "Lagt til {count, plural, one {# objekter} other {# objekt}} i album", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Objektet} other {Objektene}} kan ikke legges til i albumet", "assets_count": "{count, plural, one {# fil} other {# filer}}", "assets_deleted_permanently": "{count} objekt(er) slettet permanent", "assets_deleted_permanently_from_server": "{count} objekt(er) slettet permanent fra Immich-serveren", "assets_downloaded_failed": "{count, plural, one {Nedlasting av # fil - {error} fil feilet} other {Nedlastede # filer - {error} filer feilet}}", "assets_downloaded_successfully": "{count, plural, one {Nedlastet # fil vellykket} other {Nedlastede # filer vellykket}}", - "assets_moved_to_trash_count": "Flyttet {count, plural, one {# asset} other {# assets}} til søppel", - "assets_permanently_deleted_count": "Permanent slettet {count, plural, one {# asset} other {# assets}}", - "assets_removed_count": "Slettet {count, plural, one {# asset} other {# assets}}", + "assets_moved_to_trash_count": "Flyttet {count, plural, one {# objekt} other {# objekter}} til søppel", + "assets_permanently_deleted_count": "Slettet {count, plural, one {# objekt} other {# objekter}} permanent", + "assets_removed_count": "Slettet {count, plural, one {# objekt} other {# objekter}}", "assets_removed_permanently_from_device": "{count} objekt(er) slettet permanent fra enheten din", "assets_restore_confirmation": "Er du sikker på at du vil gjenopprette alle slettede eiendeler? Denne handlingen kan ikke angres! Vær oppmerksom på at frakoblede ressurser ikke kan gjenopprettes på denne måten.", - "assets_restored_count": "Gjenopprettet {count, plural, one {# asset} other {# assets}}", + "assets_restored_count": "Gjenopprettet {count, plural, one {# objekt} other {# objekter}}", "assets_restored_successfully": "{count} objekt(er) gjenopprettet", "assets_trashed": "{count} objekt(er) slettet", - "assets_trashed_count": "Kastet {count, plural, one {# asset} other {# assets}}", + "assets_trashed_count": "Kastet {count, plural, one {# objekt} other {# objekter}}", "assets_trashed_from_server": "{count} objekt(er) slettet fra Immich serveren", - "assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Assets were}} er allerede lagt til i albumet", + "assets_were_part_of_album_count": "{count, plural, one {Objektet} other {Objektene}} er allerede lagt til i albumet", "authorized_devices": "Autoriserte enheter", "automatic_endpoint_switching_subtitle": "Koble til lokalt over angitt Wi-Fi når det er tilgjengelig, og bruk alternative tilkoblinger andre steder", "automatic_endpoint_switching_title": "Automatisk URL bytte", @@ -510,6 +519,7 @@ "back_close_deselect": "Tilbake, lukk eller fjern merking", "background_location_permission": "Bakgrunnstillatelse for plassering", "background_location_permission_content": "For å bytte nettverk når du kjører i bakgrunnen, må Immich *alltid* ha presis posisjonstilgang slik at appen kan lese Wi-Fi-nettverkets navn", + "backup": "Sikkerhetskopiering", "backup_album_selection_page_albums_device": "Album på enhet ({count})", "backup_album_selection_page_albums_tap": "Trykk for å inkludere, dobbelttrykk for å ekskludere", "backup_album_selection_page_assets_scatter": "Objekter kan bli spredd over flere album. Album kan derfor bli inkludert eller ekskludert under sikkerhetskopieringen.", @@ -541,7 +551,7 @@ "backup_controller_page_background_turn_off": "Skru av bakgrunnstjenesten", "backup_controller_page_background_turn_on": "Skru på bakgrunnstjenesten", "backup_controller_page_background_wifi": "Kun på Wi-Fi", - "backup_controller_page_backup": "Sikkerhetskopier", + "backup_controller_page_backup": "Sikkerhetskopiere", "backup_controller_page_backup_selected": "Valgte: ", "backup_controller_page_backup_sub": "Opplastede bilder og videoer", "backup_controller_page_created": "Opprettet: {date}", @@ -585,9 +595,9 @@ "bugs_and_feature_requests": "Feil og funksjonsforespørsler", "build": "Bygg", "build_image": "Lag Bilde", - "bulk_delete_duplicates_confirmation": "Er du sikker på at du vil slette {count, plural, one {# duplicate asset} other {# duplicate assets}} dupliserte filer? Dette vil beholde største filen fra hver gruppe og vil permanent slette alle andre duplikater. Du kan ikke angre denne handlingen!", - "bulk_keep_duplicates_confirmation": "Er du sikker på at du vil beholde {count, plural, one {# duplicate asset} other {# duplicate assets}} dupliserte filer? Dette vil løse alle dupliserte grupper uten å slette noe.", - "bulk_trash_duplicates_confirmation": "Er du sikker på ønsker å slette {count, plural, one {# duplicate asset} other {# duplicate assets}} dupliserte filer? Dette vil beholde største filen fra hver gruppe, samt slette alle andre duplikater.", + "bulk_delete_duplicates_confirmation": "Er du sikker på at du vil slette {count, plural, one {# duplisert fil} other {# dupliserte filer}}? Dette vil beholde største filen fra hver gruppe og vil permanent slette alle andre duplikater. Du kan ikke angre denne handlingen!", + "bulk_keep_duplicates_confirmation": "Er du sikker på at du vil beholde {count, plural, one {# duplikat} other {# duplikater}}? Dette vil løse alle duplikatgrupper uten å slette noe.", + "bulk_trash_duplicates_confirmation": "Er du sikker på ønsker å slette {count, plural, one {# duplisert objekt} other {# dupliserte objekter}}? Dette vil beholde største filen fra hver gruppe, samt slette alle andre duplikater.", "buy": "Kjøp Immich", "cache_settings_clear_cache_button": "Tøm buffer", "cache_settings_clear_cache_button_title": "Tømmer app-ens buffer. Dette vil ha betydelig innvirkning på appens ytelse inntil bufferen er gjenoppbygd.", @@ -723,6 +733,7 @@ "current_server_address": "Nåværende serveradresse", "custom_locale": "Tilpasset lokalisering", "custom_locale_description": "Formater datoer og tall basert på språk og region", + "custom_url": "Tilpasset URL", "daily_title_text_date": "E MMM. dd", "daily_title_text_date_year": "E MMM. dddd, yyyy", "dark": "Mørk", @@ -742,7 +753,8 @@ "default_locale": "Standard språkinnstilling", "default_locale_description": "Formater datoer og tall basert på nettleserens språkinnstilling", "delete": "Slett", - "delete_action_prompt": "{count} permanen slettet", + "delete_action_confirmation_message": "Er du sikker på at du vil slette dette objektet? Dette vil flytte objektet til søppelkassen og vil gi deg beskjed om du vil slette det lokalt", + "delete_action_prompt": "{count} slettet", "delete_album": "Slett album", "delete_api_key_prompt": "Er du sikker på at du vil slette denne API-nøkkelen?", "delete_dialog_alert": "Disse objektene vil bli slettet permanent fra Immich og fra enheten din", @@ -760,6 +772,8 @@ "delete_local_dialog_ok_backed_up_only": "Slett kun sikkerhetskopierte objekter", "delete_local_dialog_ok_force": "Slett uansett", "delete_others": "Slett andre", + "delete_permanently": "Slett permanent", + "delete_permanently_action_prompt": "{count} slettet permanent", "delete_shared_link": "Slett delt lenke", "delete_shared_link_dialog_title": "Slett delt link", "delete_tag": "Slett tag", @@ -815,6 +829,7 @@ "edit": "Rediger", "edit_album": "Rediger album", "edit_avatar": "Rediger avatar", + "edit_birthday": "Rediger Bursdag", "edit_date": "Rediger dato", "edit_date_and_time": "Rediger dato og tid", "edit_description": "Endre beskrivelse", @@ -866,7 +881,7 @@ "cant_apply_changes": "Kan ikke legge til endringene", "cant_change_activity": "Kan ikke {enabled, select, true {disable} other {enable}} aktivitet", "cant_change_asset_favorite": "Kan ikke endre favoritt til filen", - "cant_change_metadata_assets_count": "Kan ikke endre metadata for {count, plural, one {# asset} other {# assets}}", + "cant_change_metadata_assets_count": "Kan ikke endre metadata for {count, plural, one {# objekt} other {# objekter}}", "cant_get_faces": "Kan ikke finne ansikter", "cant_get_number_of_comments": "Kan ikke hente antall kommentarer", "cant_search_people": "Kan ikke søke etter mennesker", @@ -903,8 +918,8 @@ "unable_to_add_exclusion_pattern": "Kan ikke legge til eksklusjonsmønster", "unable_to_add_import_path": "Kan ikke legge til importsti", "unable_to_add_partners": "Kan ikke legge til partnere", - "unable_to_add_remove_archive": "Kan ikke {archived, select, true {remove asset from} other {add asset to}} arkivet", - "unable_to_add_remove_favorites": "Kan ikke {favorite, select, true {add asset to} other {remove asset from}} favoritter", + "unable_to_add_remove_archive": "Kan ikke {archived, select, true {fjerne objekt fra} other {flytte objekt til}} arkivet", + "unable_to_add_remove_favorites": "Kan ikke {favorite, select, true {legge til objekt til} other {fjerne objekt fra}} favoritter", "unable_to_archive_unarchive": "Kan ikke {archived, select, true {archive} other {unarchive}}", "unable_to_change_album_user_role": "Kan ikke endre brukerens rolle i albumet", "unable_to_change_date": "Kan ikke endre dato", @@ -982,6 +997,7 @@ }, "exif": "EXIF", "exif_bottom_sheet_description": "Legg til beskrivelse ...", + "exif_bottom_sheet_description_error": "Feil ved oppdatering av beskrivelsen", "exif_bottom_sheet_details": "DETALJER", "exif_bottom_sheet_location": "PLASSERING", "exif_bottom_sheet_people": "MENNESKER", @@ -1002,6 +1018,8 @@ "explorer": "Utforsker", "export": "Eksporter", "export_as_json": "Eksporter som JSON", + "export_database": "Eksporter database", + "export_database_description": "Eksporter SQLite databasen", "extension": "Utvidelse", "external": "Ekstern", "external_libraries": "Eksterne Bibliotek", @@ -1139,13 +1157,14 @@ "keep": "Behold", "keep_all": "Behold alle", "keep_this_delete_others": "Behold denne, slett de andre", - "kept_this_deleted_others": "Behold denne filen og slett {count, plural, one {# asset} other {# assets}}", + "kept_this_deleted_others": "Behold denne filen og slett {count, plural, one {# objekt} other {# objekter}}", "keyboard_shortcuts": "Tastatursnarveier", "language": "Språk", "language_no_results_subtitle": "Prøv å endre søkeord", "language_no_results_title": "Ingen språk funnet", "language_search_hint": "Søker etter språk...", - "language_setting_description": "Velg ditt foretrukket språk", + "language_setting_description": "Velg ditt foretrukne språk", + "large_files": "Store Filer", "last_seen": "Sist sett", "latest_version": "Siste versjon", "latitude": "Breddegrad", @@ -1165,7 +1184,6 @@ "light": "Lys", "like_deleted": "Som slettede", "link_motion_video": "Koble bevegelsesvideo", - "link_options": "Lenkealternativer", "link_to_oauth": "Lenke til OAuth", "linked_oauth_account": "Lenket til OAuth-konto", "list": "Liste", @@ -1230,8 +1248,7 @@ "manage_your_devices": "Administrer dine innloggede enheter", "manage_your_oauth_connection": "Administrer tilkoblingen din med OAuth", "map": "Kart", - "map_assets_in_bound": "{count} bilde", - "map_assets_in_bounds": "{count} bilder", + "map_assets_in_bounds": "{count, plural, one {# photo} other {# photos}}", "map_cannot_get_user_location": "Kan ikke hente brukerlokasjon", "map_location_dialog_yes": "Ja", "map_location_picker_page_use_location": "Bruk denne lokasjonen", @@ -1286,8 +1303,8 @@ "move_to_lock_folder_action_prompt": "{count} lagt til i låst mappe", "move_to_locked_folder": "Flytt til låst mappe", "move_to_locked_folder_confirmation": "Disse bildene og videoene vil bli fjernet fra alle albumer, og kun tilgjengelige via den låste mappen", - "moved_to_archive": "Flyttet {count, plural, one {# asset} other {# assets}} til arkivet", - "moved_to_library": "Flyttet {count, plural, one {# asset} other {# assets}} til biblioteket", + "moved_to_archive": "Flyttet {count, plural, one {# objekt} other {# objekter}} til arkivet", + "moved_to_library": "Flyttet {count, plural, one {# objekt} other {# objekter}} til biblioteket", "moved_to_trash": "Flyttet til papirkurven", "multiselect_grid_edit_date_time_err_read_only": "Kan ikke endre dato på objekt(er) med kun lese-rettigheter, hopper over", "multiselect_grid_edit_gps_err_read_only": "Kan ikke endre lokasjon på objekt(er) med kun lese-rettigheter, hopper over", @@ -1408,10 +1425,10 @@ "permanent_deletion_warning": "Advarsel om permanent sletting", "permanent_deletion_warning_setting_description": "Vis en advarsel ved permanent sletting av filer", "permanently_delete": "Slett permanent", - "permanently_delete_assets_count": "Permanent slett {count, plural, one {asset} other {assets}}", - "permanently_delete_assets_prompt": "Er du sikker på at du vil permanent slette {count, plural, one {this asset?} other {these # assets?}} Dette vil også slette {count, plural, one {it from its} other {them from their}} album.", + "permanently_delete_assets_count": "Slett {count, plural, one {objekt} other {objekter}} permanent", + "permanently_delete_assets_prompt": "Er du sikker på at du vil permanent slette {count, plural, one {dette objektet?} other {disse # objektene?}} Dette vil også slette {count, plural, one {det fra dets} other {de fra deres}} album(er).", "permanently_deleted_asset": "Filen har blitt permanent slettet", - "permanently_deleted_assets_count": "Permanent slett {count, plural, one {# asset} other {# assets}}", + "permanently_deleted_assets_count": "Permanent slett {count, plural, one {# objekt} other {# objekter}}", "permission": "Tillatelse", "permission_empty": "Dine tillatelser burde ikke være tomme", "permission_onboarding_back": "Tilbake", @@ -1508,8 +1525,8 @@ "reaction_options": "Reaksjonsalternativer", "read_changelog": "Les endringslogg", "reassign": "Tilordne på nytt", - "reassigned_assets_to_existing_person": "Tildelt på nytt {count, plural, one {# asset} other {# assets}} to {name, select, null {an existing person} other {{name}}}", - "reassigned_assets_to_new_person": "Tildelt på nytt {count, plural, one {# asset} other {# assets}} til en ny person", + "reassigned_assets_to_existing_person": "Flyttet {count, plural, one {# objekt} other {# objekter}} to {name, select, null {en eksisterende person} other {{name}}}", + "reassigned_assets_to_new_person": "Flyttet {count, plural, one {# objekt} other {# objekter}} til en ny person", "reassing_hint": "Tilordne valgte eiendeler til en eksisterende person", "recent": "Nylig", "recent-albums": "Nylige album", @@ -1532,8 +1549,8 @@ "remote": "Eksternt", "remote_assets": "Eksterne objekter", "remove": "Fjern", - "remove_assets_album_confirmation": "Er du sikker på at du fil slette {count, plural, one {# asset} other {# assets}} fra albumet?", - "remove_assets_shared_link_confirmation": "Er du sikker på at du vil slette {count, plural, one {# asset} other {# assets}} fra den delte lenken?", + "remove_assets_album_confirmation": "Er du sikker på at du fil slette {count, plural, one {# objekt} other {# objekter}} fra albumet?", + "remove_assets_shared_link_confirmation": "Er du sikker på at du vil slette {count, plural, one {# objekt} other {# objekter}} fra den delte lenken?", "remove_assets_title": "Vil du fjerne eiendeler?", "remove_custom_date_range": "Fjern egendefinert datoperiode", "remove_deleted_assets": "Fjern fra frakoblede filer", @@ -1555,7 +1572,7 @@ "removed_from_favorites_count": "{count, plural, other {Removed #}} fra favoritter", "removed_memory": "Slettet minne", "removed_photo_from_memory": "Slettet bilde fra minne", - "removed_tagged_assets": "Fjern tag fra {count, plural, one {# asset} other {# assets}}", + "removed_tagged_assets": "Fjern tag fra {count, plural, one {# objekt} other {# objekter}}", "rename": "Gi nytt navn", "repair": "Reparer", "repair_no_results_message": "Usporrede og savnede filer vil vises her", @@ -1582,6 +1599,7 @@ "resume": "Fortsett", "retry_upload": "Prøv opplasting på nytt", "review_duplicates": "Gjennomgå duplikater", + "review_large_files": "Se gjennom store filer", "role": "Rolle", "role_editor": "Redigerer", "role_viewer": "Visning", @@ -1739,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "Kopiert til utklippslisten", "shared_link_clipboard_text": "Link: {link}\nPassord: {password}", "shared_link_create_error": "Feil ved oppretting av delbar link", + "shared_link_custom_url_description": "Få tilgang til denne delte lenken med en egendefinert URL", "shared_link_edit_description_hint": "Endre delebeskrivelse", "shared_link_edit_expire_after_option_day": "1 dag", "shared_link_edit_expire_after_option_days": "{count} dager", @@ -1764,6 +1783,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Håndter delte linker", "shared_link_options": "Alternativer for delte lenke", + "shared_link_password_description": "Krev et passord for å få tilgang til denne delte lenken", "shared_links": "Delte linker", "shared_links_description": "Del bilder og videoer med lenke", "shared_photos_and_videos_count": "{assetCount, plural, other {# delte bilder og videoer.}}", @@ -1823,7 +1843,7 @@ "stack_duplicates": "Stable duplikater", "stack_select_one_photo": "Velg hovedbilde for bildestabbel", "stack_selected_photos": "Stable valgte bilder", - "stacked_assets_count": "Stable {count, plural, one {# asset} other {# assets}}", + "stacked_assets_count": "Stable {count, plural, one {# objekt} other {# objekter}}", "stacktrace": "Stakkspor", "start": "Start", "start_date": "Startdato", @@ -1859,7 +1879,7 @@ "tag_not_found_question": "Finner du ikke en merke? Opprett en nytt merke.", "tag_people": "Tag Folk", "tag_updated": "Oppdater merke: {tag}", - "tagged_assets": "Merket {count, plural, one {# asset} other {# assets}}", + "tagged_assets": "Merket {count, plural, one {# objekt} other {# objekter}}", "tags": "Merker", "tap_to_run_job": "Trykk for å kjøre jobben", "template": "Mal", @@ -1935,25 +1955,28 @@ "unselect_all_in": "Fjern velging av alle i {group}", "unstack": "avstable", "unstack_action_prompt": "{count} ustakket", - "unstacked_assets_count": "Ikke stablet {count, plural, one {# asset} other {# assets}}", + "unstacked_assets_count": "Ikke stablet {count, plural, one {# objekt} other {# objekter}}", "untagged": "Umerket", "up_next": "Neste", "updated_at": "Oppdatert", "updated_password": "Passord oppdatert", "upload": "Last opp", + "upload_action_prompt": "{count} i kø for opplasting", "upload_concurrency": "Samtidig opplastning", "upload_details": "Opplastingsdetaljer", "upload_dialog_info": "Vil du utføre backup av valgte objekt(er) til serveren?", "upload_dialog_title": "Last opp objekt", "upload_errors": "Opplasting fullført med {count, plural, one {# error} other {# errors}}, oppdater siden for å se nye opplastingsressurser.", + "upload_finished": "Opplasting fullført", "upload_progress": "Gjenstående {remaining, number} – behandlet {processed, number}/{total, number}", - "upload_skipped_duplicates": "Hoppet over {count, plural, one {# duplicate asset} other {# duplicate assets}}", + "upload_skipped_duplicates": "Hoppet over {count, plural, one {# duplisert objekt} other {# dupliserte objekter}}", "upload_status_duplicates": "Duplikater", "upload_status_errors": "Feil", "upload_status_uploaded": "Opplastet", "upload_success": "Opplasting vellykket, oppdater siden for å se nye opplastninger.", "upload_to_immich": "Last opp til Immich ({count})", "uploading": "Laster opp", + "uploading_media": "Laster opp media", "url": "URL", "usage": "Bruk", "use_biometric": "Bruk biometri", @@ -1962,7 +1985,7 @@ "user": "Bruker", "user_has_been_deleted": "Denne brukeren har blitt slettet.", "user_id": "Bruker ID", - "user_liked": "{user} likte {type, select, photo {this photo} video {this video} asset {this asset} other {it}}", + "user_liked": "{user} likte {type, select, photo {dette bildet} video {denne videoen} asset {dette objektet} other {dette}}", "user_pin_code_settings": "PINkode", "user_pin_code_settings_description": "Håndter din PINkode", "user_privacy": "Personverninnstillinger", @@ -1974,7 +1997,7 @@ "user_usage_stats_description": "Vis kontobruksstatistikk", "username": "Brukernavn", "users": "Brukere", - "users_added_to_album_count": "Lagt til {count, plural, one {#user} other {#users}} til albumet", + "users_added_to_album_count": "Lagt til {count, plural, one {# bruker} other {# brukere}} til albumet", "utilities": "Verktøy", "validate": "Valider", "validate_endpoint_error": "Skriv inn en gyldig URL", diff --git a/i18n/nl.json b/i18n/nl.json index 86cb0fba6e..b287a7810f 100644 --- a/i18n/nl.json +++ b/i18n/nl.json @@ -14,6 +14,7 @@ "add_a_location": "Een locatie toevoegen", "add_a_name": "Naam toevoegen", "add_a_title": "Titel toevoegen", + "add_birthday": "Voeg een verjaardag toe", "add_endpoint": "Server toevoegen", "add_exclusion_pattern": "Uitsluitingspatroon toevoegen", "add_import_path": "Import-pad toevoegen", @@ -44,8 +45,15 @@ "backup_database": "Maak database back-up", "backup_database_enable_description": "Database back-ups activeren", "backup_keep_last_amount": "Aantal back-ups om te bewaren", - "backup_settings": "Database back-up instellingen", - "backup_settings_description": "Beheer database back-up instellingen.", + "backup_onboarding_1_description": "externe kopie in de cloud of op een andere fysieke locatie.", + "backup_onboarding_2_description": "lokale kopieën op verschillende apparaten. Dit omvat de hoofdbestanden én een lokale back-up van deze bestanden.", + "backup_onboarding_3_description": "totaal aantal kopieën van de gegevens, inclusief originele bestanden. Dit omvat 1 externe kopie en 2 lokale kopieën.", + "backup_onboarding_description": "Een 3-2-1 back-up strategie wordt aanbevolen om de gegevens te beschermen. Bewaar kopieën van de geüploade foto's/video's en de Immich database voor een complete back-up oplossing.", + "backup_onboarding_footer": "Raadpleeg de documentatie voor meer informatie over het maken van back-ups van Immich.", + "backup_onboarding_parts_title": "Een 3-2-1 back-up omvat:", + "backup_onboarding_title": "Back-ups", + "backup_settings": "Database dump instellingen", + "backup_settings_description": "Beheer database dump instellingen.", "cleared_jobs": "Taken gewist voor: {job}", "config_set_by_file": "Instellingen worden momenteel beheerd door een configuratiebestand", "confirm_delete_library": "Weet je zeker dat je de bibliotheek {library} wilt verwijderen?", @@ -397,6 +405,7 @@ "album_cover_updated": "Album cover is bijgewerkt", "album_delete_confirmation": "Weet je zeker dat je het album {album} wilt verwijderen?", "album_delete_confirmation_description": "Als dit album gedeeld is, hebben andere gebruikers er geen toegang meer toe.", + "album_deleted": "Album verwijderd", "album_info_card_backup_album_excluded": "UITGESLOTEN", "album_info_card_backup_album_included": "INBEGREPEN", "album_info_updated": "Albumgegevens bijgewerkt", @@ -447,7 +456,7 @@ "app_settings": "App instellingen", "appears_in": "Komt voor in", "archive": "Archief", - "archive_action_prompt": "{count}toegevoegd aan Archief", + "archive_action_prompt": "{count} toegevoegd aan archief", "archive_or_unarchive_photo": "Foto archiveren of uit het archief halen", "archive_page_no_archived_assets": "Geen gearchiveerde assets gevonden", "archive_page_title": "Archief ({count})", @@ -471,7 +480,7 @@ "asset_list_layout_settings_group_by": "Groepeer assets per", "asset_list_layout_settings_group_by_month_day": "Maand + dag", "asset_list_layout_sub_title": "Layout", - "asset_list_settings_subtitle": "Fotorasterlayoutinstellingen", + "asset_list_settings_subtitle": "Fotoraster layout instellingen", "asset_list_settings_title": "Fotoraster", "asset_offline": "Asset offline", "asset_offline_description": "Deze externe asset is niet meer op de schijf te vinden. Neem contact op met de Immich beheerder voor hulp.", @@ -510,34 +519,35 @@ "back_close_deselect": "Terug, sluiten of deselecteren", "background_location_permission": "Achtergrond locatie toestemming", "background_location_permission_content": "Om van netwerk te wisselen terwijl de app op de achtergrond draait, heeft Immich *altijd* toegang tot de exacte locatie nodig om de naam van het WiFi-netwerk te kunnen lezen", + "backup": "Back-up", "backup_album_selection_page_albums_device": "Albums op apparaat ({count})", - "backup_album_selection_page_albums_tap": "Tik om in te voegen, dubbel tik om uit te sluiten", - "backup_album_selection_page_assets_scatter": "Assets kunnen over verschillende albums verdeeld zijn, dus albums kunnen inbegrepen of uitgesloten zijn van het backup proces.", - "backup_album_selection_page_select_albums": "Albums selecteren", + "backup_album_selection_page_albums_tap": "Tik om op te nemen, dubbel tik om uit te sluiten", + "backup_album_selection_page_assets_scatter": "Assets kunnen over verschillende albums verdeeld zijn, dus albums kunnen inbegrepen of uitgesloten zijn van het back-up proces.", + "backup_album_selection_page_select_albums": "Selecteer albums", "backup_album_selection_page_selection_info": "Selectie info", "backup_album_selection_page_total_assets": "Totaal unieke assets", "backup_all": "Alle", - "backup_background_service_backup_failed_message": "Fout bij back-uppen assets. Opnieuw proberen…", - "backup_background_service_connection_failed_message": "Fout bij verbinden server. Opnieuw proberen…", - "backup_background_service_current_upload_notification": "{filename} aan het uploaden...", + "backup_background_service_backup_failed_message": "Fout bij het back-uppen van de assets. Opnieuw proberen…", + "backup_background_service_connection_failed_message": "Fout bij het verbinden met de server. Opnieuw proberen…", + "backup_background_service_current_upload_notification": "{filename} wordt geüpload...", "backup_background_service_default_notification": "Controleren op nieuwe assets…", - "backup_background_service_error_title": "Backupfout", + "backup_background_service_error_title": "Back-up fout", "backup_background_service_in_progress_notification": "Back-up van assets maken…", "backup_background_service_upload_failure_notification": "Fout bij het uploaden van {filename}", "backup_controller_page_albums": "Back-up albums", - "backup_controller_page_background_app_refresh_disabled_content": "Schakel verversen op de achtergrond in via Instellingen > Algemeen > Ververs op achtergrond, om back-ups op de achtergrond te maken.", - "backup_controller_page_background_app_refresh_disabled_title": "Verversen op achtergrond uitgeschakeld", + "backup_controller_page_background_app_refresh_disabled_content": "Schakel verversen op de achtergrond in via 'Instellingen > Algemeen > Ververs op achtergrond', om back-ups op de achtergrond te maken.", + "backup_controller_page_background_app_refresh_disabled_title": "Verversen op achtergrond is uitgeschakeld", "backup_controller_page_background_app_refresh_enable_button_text": "Ga naar instellingen", "backup_controller_page_background_battery_info_link": "Laat zien hoe", - "backup_controller_page_background_battery_info_message": "Voor de beste back-upervaring, schakel je alle batterijoptimalisaties uit omdat deze op-de-achtergrondactiviteiten van Immich beperken.\n\nAangezien dit apparaatspecifiek is, zoek de vereiste informatie op voor de fabrikant van je apparaat.", + "backup_controller_page_background_battery_info_message": "Voor de beste achtergrond back-up ervaring schakelt u alle batterij optimalisaties uit die de achtergrondactiviteit voor Immich kunnen beperken.\n\nAangezien dit apparaat specifiek is, raden we aan om de vereiste informatie op te zoeken bij de fabrikant van je apparaat.", "backup_controller_page_background_battery_info_ok": "OK", - "backup_controller_page_background_battery_info_title": "Batterijoptimalisaties", + "backup_controller_page_background_battery_info_title": "Batterij optimalisaties", "backup_controller_page_background_charging": "Alleen tijdens opladen", - "backup_controller_page_background_configure_error": "Achtergrondserviceconfiguratie mislukt", - "backup_controller_page_background_delay": "Back-upvertraging voor nieuwe assets: {duration}", + "backup_controller_page_background_configure_error": "Achtergrondservice configuratie mislukt", + "backup_controller_page_background_delay": "Back-up vertraging voor nieuwe assets: {duration}", "backup_controller_page_background_description": "Schakel de achtergrondservice in om automatisch een back-up te maken van nieuwe assets zonder de app te hoeven openen", - "backup_controller_page_background_is_off": "Automatische achtergrondback-up staat uit", - "backup_controller_page_background_is_on": "Automatische achtergrondback-up staat aan", + "backup_controller_page_background_is_off": "Automatische achtergrond back-up staat uit", + "backup_controller_page_background_is_on": "Automatische achtergrond back-up staat aan", "backup_controller_page_background_turn_off": "Achtergrondservice uitzetten", "backup_controller_page_background_turn_on": "Achtergrondservice aanzetten", "backup_controller_page_background_wifi": "Alleen op WiFi", @@ -723,6 +733,7 @@ "current_server_address": "Huidig serveradres", "custom_locale": "Aangepaste landinstelling", "custom_locale_description": "Formatteer datums en getallen op basis van de taal en de regio", + "custom_url": "Aangepaste URL", "daily_title_text_date": "E dd MMM", "daily_title_text_date_year": "E dd MMM yyyy", "dark": "Donker", @@ -738,11 +749,12 @@ "deduplication_criteria_1": "Grootte van afbeelding in bytes", "deduplication_criteria_2": "Aantal EXIF data", "deduplication_info": "Deduplicatie-info", - "deduplication_info_description": "Om automatisch bezittingen te preselecteren en duplicaten te verwijderen in bulk, kijken we naar:", + "deduplication_info_description": "Om automatisch items te preselecteren en duplicaten te verwijderen in bulk, kijken we naar:", "default_locale": "Standaard landinstelling", "default_locale_description": "Formatteer datums en getallen op basis van de landinstellingen van je browser", "delete": "Verwijderen", - "delete_action_prompt": "{count} permanent verwijderd", + "delete_action_confirmation_message": "Weet je zeker dat je dit item wilt verwijderen? Deze actie zorgt ervoor dat het item naar de prullenbak van de server wordt verplaatst en je wordt gevraagd of je deze ook lokaal wilt verwijderen", + "delete_action_prompt": "{count} verwijderd", "delete_album": "Album verwijderen", "delete_api_key_prompt": "Weet je zeker dat je deze API-sleutel wilt verwijderen?", "delete_dialog_alert": "Deze items zullen permanent verwijderd worden van Immich en je apparaat", @@ -760,6 +772,8 @@ "delete_local_dialog_ok_backed_up_only": "Verwijder alleen met back-up", "delete_local_dialog_ok_force": "Toch verwijderen", "delete_others": "Andere verwijderen", + "delete_permanently": "Permanent verwijderen", + "delete_permanently_action_prompt": "{count} permanent verwijderd", "delete_shared_link": "Verwijder gedeelde link", "delete_shared_link_dialog_title": "Verwijder gedeelde link", "delete_tag": "Tag verwijderen", @@ -770,7 +784,7 @@ "description": "Beschrijving", "description_input_hint_text": "Beschrijving toevoegen...", "description_input_submit_error": "Beschrijving bijwerken mislukt, controleer het logboek voor meer details", - "deselect_all": "Alles Deselecteren", + "deselect_all": "Alles deselecteren", "details": "Details", "direction": "Richting", "disabled": "Uitgeschakeld", @@ -815,6 +829,7 @@ "edit": "Bewerken", "edit_album": "Album bewerken", "edit_avatar": "Avatar bewerken", + "edit_birthday": "Wijzig verjaardag", "edit_date": "Datum bewerken", "edit_date_and_time": "Datum en tijd bewerken", "edit_description": "Beschrijving bewerken", @@ -982,6 +997,7 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Beschrijving toevoegen...", + "exif_bottom_sheet_description_error": "Fout bij het bijwerken van de beschrijving", "exif_bottom_sheet_details": "DETAILS", "exif_bottom_sheet_location": "LOCATIE", "exif_bottom_sheet_people": "MENSEN", @@ -1002,6 +1018,8 @@ "explorer": "Verkenner", "export": "Exporteren", "export_as_json": "Exporteren als JSON", + "export_database": "Exporteer database", + "export_database_description": "Exporteer de SQLite database", "extension": "Extensie", "external": "Extern", "external_libraries": "Externe bibliotheken", @@ -1088,7 +1106,7 @@ "host": "Host", "hour": "Uur", "id": "ID", - "idle": "Idle", + "idle": "Inactief", "ignore_icloud_photos": "Negeer iCloud foto's", "ignore_icloud_photos_description": "Foto's die op iCloud zijn opgeslagen, worden niet geüpload naar de Immich server", "image": "Afbeelding", @@ -1146,6 +1164,7 @@ "language_no_results_title": "Geen talen gevonden", "language_search_hint": "Zoek talen...", "language_setting_description": "Selecteer je voorkeurstaal", + "large_files": "Grote bestanden", "last_seen": "Laatst gezien", "latest_version": "Nieuwste versie", "latitude": "Breedtegraad", @@ -1165,7 +1184,6 @@ "light": "Licht", "like_deleted": "Like verwijderd", "link_motion_video": "Koppel bewegende video", - "link_options": "Opties voor koppeling", "link_to_oauth": "Koppel OAuth", "linked_oauth_account": "Gekoppeld OAuth account", "list": "Lijst", @@ -1230,8 +1248,7 @@ "manage_your_devices": "Beheer je ingelogde apparaten", "manage_your_oauth_connection": "Beheer je OAuth-koppeling", "map": "Kaart", - "map_assets_in_bound": "{count} foto", - "map_assets_in_bounds": "{count} foto's", + "map_assets_in_bounds": "{count, plural, one {# foto} other {# foto's}}", "map_cannot_get_user_location": "Kan locatie van de gebruiker niet ophalen", "map_location_dialog_yes": "Ja", "map_location_picker_page_use_location": "Gebruik deze locatie", @@ -1529,8 +1546,8 @@ "refreshing_faces": "Gezichten aan het vernieuwen", "refreshing_metadata": "Metadata aan het vernieuwen", "regenerating_thumbnails": "Thumbnails opnieuw aan het genereren", - "remote": "Remote", - "remote_assets": "Remote Assets", + "remote": "Externe", + "remote_assets": "Externe Assets", "remove": "Verwijderen", "remove_assets_album_confirmation": "Weet je zeker dat je {count, plural, one {# asset} other {# assets}} uit het album wilt verwijderen?", "remove_assets_shared_link_confirmation": "Weet je zeker dat je {count, plural, one {# asset} other {# assets}} uit deze gedeelde link wilt verwijderen?", @@ -1568,8 +1585,8 @@ "reset_password": "Wachtwoord resetten", "reset_people_visibility": "Zichtbaarheid mensen resetten", "reset_pin_code": "Reset PIN code", - "reset_sqlite": "Reset SQLite Database", - "reset_sqlite_confirmation": "Ben je zeker dat je de SQLite database wilt resetten? Je zal moetenn uitloggen om de data opnieuw te synchroniseren.", + "reset_sqlite": "SQLite database resetten", + "reset_sqlite_confirmation": "Ben je zeker dat je de SQLite database wilt resetten? Je zal moeten uitloggen om de data opnieuw te synchroniseren", "reset_sqlite_success": "De SQLite database is succesvol gereset", "reset_to_default": "Resetten naar standaard", "resolve_duplicates": "Duplicaten oplossen", @@ -1582,6 +1599,7 @@ "resume": "Hervatten", "retry_upload": "Opnieuw uploaden", "review_duplicates": "Controleer duplicaten", + "review_large_files": "Grote bestanden beoordelen", "role": "Rol", "role_editor": "Bewerker", "role_viewer": "Bekijker", @@ -1739,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "Gekopieerd naar klembord", "shared_link_clipboard_text": "Link: {link}\nWachtwoord: {password}", "shared_link_create_error": "Fout bij het maken van een gedeelde link", + "shared_link_custom_url_description": "Krijg toegang tot deze gedeelde link met een aangepaste URL", "shared_link_edit_description_hint": "Voer beschrijving voor de gedeelde link in", "shared_link_edit_expire_after_option_day": "1 dag", "shared_link_edit_expire_after_option_days": "{count} dagen", @@ -1764,6 +1783,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Beheer gedeelde links", "shared_link_options": "Opties voor gedeelde links", + "shared_link_password_description": "Vraag een wachtwoord om toegang te krijgen tot deze gedeelde link", "shared_links": "Gedeelde links", "shared_links_description": "Deel foto's en video's via een link", "shared_photos_and_videos_count": "{assetCount, plural, other {# gedeelde foto's & video's.}}", @@ -1941,11 +1961,13 @@ "updated_at": "Geüpdatet", "updated_password": "Wachtwoord bijgewerkt", "upload": "Uploaden", + "upload_action_prompt": "{count} in de wachtrij voor uploaden", "upload_concurrency": "Aantal gelijktijdige uploads", "upload_details": "Uploaddetails", "upload_dialog_info": "Wil je een backup maken van de geselecteerde asset(s) op de server?", "upload_dialog_title": "Asset uploaden", "upload_errors": "Upload voltooid met {count, plural, one {# fout} other {# fouten}}, vernieuw de pagina om de nieuwe assets te zien.", + "upload_finished": "Uploaden is voltooid", "upload_progress": "Resterend {remaining, number} - Verwerkt {processed, number}/{total, number}", "upload_skipped_duplicates": "{count, plural, one {# duplicate asset} other {# duplicate assets}} overgeslagen", "upload_status_duplicates": "Duplicaten", @@ -1954,6 +1976,7 @@ "upload_success": "Uploaden gelukt, vernieuw de pagina om de nieuwe assets te zien.", "upload_to_immich": "Uploaden naar Immich ({count})", "uploading": "Aan het uploaden", + "uploading_media": "Media wordt geüpload", "url": "URL", "usage": "Gebruik", "use_biometric": "Gebruik biometrische authenticatie", diff --git a/i18n/pl.json b/i18n/pl.json index 5adc6df943..3c6a8b12e0 100644 --- a/i18n/pl.json +++ b/i18n/pl.json @@ -397,6 +397,7 @@ "album_cover_updated": "Okładka albumu została zaktualizowana", "album_delete_confirmation": "Czy na pewno chcesz usunąć album {album}?", "album_delete_confirmation_description": "Jeżeli album jest udostępniany, inny stracą do niego dostęp.", + "album_deleted": "Album usunięty", "album_info_card_backup_album_excluded": "WYKLUCZONE", "album_info_card_backup_album_included": "WŁĄCZONE", "album_info_updated": "Szczegóły albumu zostały zaktualizowane", @@ -406,6 +407,7 @@ "album_options": "Opcje albumu", "album_remove_user": "Usunąć użytkownika?", "album_remove_user_confirmation": "Na pewno chcesz usunąć {user}?", + "album_search_not_found": "Nie znaleziono albumów pasujących do Twojego wyszukiwania", "album_share_no_users": "Wygląda na to, że ten album albo udostępniono wszystkim użytkownikom, albo nie ma komu go udostępnić.", "album_updated": "Album zaktualizowany", "album_updated_setting_description": "Otrzymaj powiadomienie e-mail, gdy do udostępnionego Ci albumu zostaną dodane nowe zasoby", @@ -425,6 +427,7 @@ "albums_default_sort_order": "Domyślna kolejność sortowania w albumach", "albums_default_sort_order_description": "Początkowa kolejność sortowania zasobów przy tworzeniu nowych albumów.", "albums_feature_description": "Kolekcje zasobów, które można udostępniać innym użytkownikom.", + "albums_on_device_count": "Albumów na urzadzeniu ({count})", "all": "Wszystkie", "all_albums": "Wszystkie albumy", "all_people": "Wszystkie osoby", @@ -508,6 +511,7 @@ "back_close_deselect": "Wróć, zamknij lub odznacz", "background_location_permission": "Uprawnienia do lokalizacji w tle", "background_location_permission_content": "Aby móc przełączać sieć podczas pracy w tle, Immich musi *zawsze* mieć dostęp do dokładnej lokalizacji, aby aplikacja mogła odczytać nazwę sieci Wi-Fi", + "backup": "Kopia Zapasowa", "backup_album_selection_page_albums_device": "Albumy na urządzeniu ({count})", "backup_album_selection_page_albums_tap": "Stuknij, aby włączyć, stuknij dwukrotnie, aby wykluczyć", "backup_album_selection_page_assets_scatter": "Pliki mogą być rozproszone w wielu albumach. Dzięki temu albumy mogą być włączane lub wyłączane podczas procesu tworzenia kopii zapasowej.", @@ -571,6 +575,8 @@ "backup_options_page_title": "Opcje kopi zapasowej", "backup_setting_subtitle": "Zarządzaj ustawieniami przesyłania w tle i na pierwszym planie", "backward": "Do tyłu", + "beta_sync": "Status synchronizacji w wersji Beta", + "beta_sync_subtitle": "Zarządzaj nowym systemem synchronizacji", "biometric_auth_enabled": "Włączono logowanie biometryczne", "biometric_locked_out": "Uwierzytelnianie biometryczne jest dla Ciebie zablokowane", "biometric_no_options": "Brak możliwości biometrii", @@ -588,7 +594,7 @@ "cache_settings_clear_cache_button": "Wyczyść Cache", "cache_settings_clear_cache_button_title": "Czyści pamięć podręczną aplikacji. Wpłynie to znacząco na wydajność aplikacji, dopóki pamięć podręczna nie zostanie odbudowana.", "cache_settings_duplicated_assets_clear_button": "WYCZYŚĆ", - "cache_settings_duplicated_assets_subtitle": "Zdjęcia i filmy umieszczone na czarnej liście aplikacji", + "cache_settings_duplicated_assets_subtitle": "Zdjęcia i filmy umieszczone na liście ignorowanych w aplikacji", "cache_settings_duplicated_assets_title": "Zduplikowane zasoby ({count})", "cache_settings_statistics_album": "Biblioteka miniatur", "cache_settings_statistics_full": "Pełne Zdjęcia", @@ -605,6 +611,7 @@ "cancel": "Anuluj", "cancel_search": "Anuluj wyszukiwanie", "canceled": "Anulowano", + "canceling": "Anulowanie", "cannot_merge_people": "Złączenie osób nie powiodło się", "cannot_undo_this_action": "Nie da się tego cofnąć!", "cannot_update_the_description": "Nie można zaktualizować opisu", @@ -718,6 +725,7 @@ "current_server_address": "Aktualny adres serwera", "custom_locale": "Niestandardowy Region", "custom_locale_description": "Formatuj daty i liczby na podstawie języka i regionu", + "custom_url": "Niestandardowy URL", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Ciemny", @@ -737,7 +745,8 @@ "default_locale": "Domyślny Region", "default_locale_description": "Formatuj daty i liczby na podstawie ustawień Twojej przeglądarki", "delete": "Usuń", - "delete_action_prompt": "{count} trwale usuniętych", + "delete_action_confirmation_message": "Jesteś pewien, że chcesz usunąć ten zasób? Ta czynność przeniesie zasób do kosza na serwerze i wyświetli komunikat z pytaniem, czy chcesz go usunąć lokalnie", + "delete_action_prompt": "{count} usuniętych", "delete_album": "Usuń album", "delete_api_key_prompt": "Czy na pewno chcesz usunąć ten klucz API?", "delete_dialog_alert": "Te elementy zostaną trwale usunięte z Immich i z Twojego urządzenia", @@ -755,6 +764,8 @@ "delete_local_dialog_ok_backed_up_only": "Usuń tylko kopię zapasową", "delete_local_dialog_ok_force": "Usuń mimo to", "delete_others": "Usuń inne", + "delete_permanently": "Usuń trwale", + "delete_permanently_action_prompt": "{count} trwale usuniętych", "delete_shared_link": "Usuń udostępniony link", "delete_shared_link_dialog_title": "Usuń udostępniony link", "delete_tag": "Usuń etykietę", @@ -765,6 +776,7 @@ "description": "Opis", "description_input_hint_text": "Dodaj opis...", "description_input_submit_error": "Błąd aktualizacji opisu, sprawdź dziennik, aby uzyskać więcej szczegółów", + "deselect_all": "Odznacz wszystkie", "details": "Szczegóły", "direction": "Kierunek", "disabled": "Wyłączone", @@ -782,6 +794,7 @@ "documentation": "Dokumentacja", "done": "Gotowe", "download": "Pobierz", + "download_action_prompt": "Pobieranie {count} zasobów", "download_canceled": "Pobieranie anulowane", "download_complete": "Pobieranie zakończone", "download_enqueue": "Pobieranie w kolejce", @@ -838,6 +851,7 @@ "empty_trash": "Opróżnij kosz", "empty_trash_confirmation": "Czy na pewno chcesz opróżnić kosz? Spowoduje to trwałe usunięcie wszystkich zasobów znajdujących się w koszu z Immich.\nNie można cofnąć tej operacji!", "enable": "Włącz", + "enable_backup": "Włącz kopię zapasową", "enable_biometric_auth_description": "Wprowadź kod PIN aby włączyć logowanie biometryczne", "enabled": "Włączone", "end_date": "Do dnia", @@ -974,6 +988,7 @@ }, "exif": "Metadane EXIF", "exif_bottom_sheet_description": "Dodaj Opis...", + "exif_bottom_sheet_description_error": "Wystąpił błąd podczas aktualizacji opisu", "exif_bottom_sheet_details": "SZCZEGÓŁY", "exif_bottom_sheet_location": "LOKALIZACJA", "exif_bottom_sheet_people": "LUDZIE", @@ -994,6 +1009,8 @@ "explorer": "Eksplorator", "export": "Eksportuj", "export_as_json": "Eksportuj jako JSON", + "export_database": "Exportuj bazę danych", + "export_database_description": "Exportuj bazę danych SQLite", "extension": "Rozszerzenie", "external": "Zewnętrzny", "external_libraries": "Biblioteki Zewnętrzne", @@ -1045,6 +1062,9 @@ "haptic_feedback_switch": "Włącz technologię haptyczną", "haptic_feedback_title": "Technologia haptyczna", "has_quota": "Ma limit", + "hash_asset": "Hashuj zasób", + "hashed_assets": "Zahashowane zasoby", + "hashing": "Hashowanie", "header_settings_add_header_tip": "Dodaj nagłówek", "header_settings_field_validator_msg": "Wartość nie może być pusta", "header_settings_header_name_input": "Nazwa nagłówka", @@ -1077,6 +1097,7 @@ "host": "Host", "hour": "Godzina", "id": "ID", + "idle": "Bezczynny", "ignore_icloud_photos": "Ignoruj zdjęcia w iCloud", "ignore_icloud_photos_description": "Zdjęcia przechowywane w usłudze iCloud nie zostaną przesłane na serwer Immich", "image": "Zdjęcie", @@ -1134,6 +1155,7 @@ "language_no_results_title": "Nie znaleziono żadnych języków", "language_search_hint": "Szukaj języków...", "language_setting_description": "Wybierz swój preferowany język", + "large_files": "Duże pliki", "last_seen": "Ostatnio widziane", "latest_version": "Najnowsza Wersja", "latitude": "Szerokość geograficzna", @@ -1153,13 +1175,14 @@ "light": "Jasny", "like_deleted": "Polubienie usunięte", "link_motion_video": "Podłącz ruchome wideo", - "link_options": "Opcje linku", "link_to_oauth": "Połącz z OAuth", "linked_oauth_account": "Połączone konto OAuth", "list": "Lista", "loading": "Ładowanie", "loading_search_results_failed": "Ładowanie wyników wyszukiwania nie powiodło się", + "local": "Lokalny", "local_asset_cast_failed": "Nie można strumieniować zasobu, który nie został przesłany na serwer", + "local_assets": "Zasoby lokalne", "local_network": "Sieć lokalna", "local_network_sheet_info": "Aplikacja połączy się z serwerem za pośrednictwem tego adresu URL podczas korzystania z określonej sieci Wi-Fi", "location_permission": "Zezwolenie na lokalizację", @@ -1216,8 +1239,7 @@ "manage_your_devices": "Zarządzaj swoimi zalogowanymi urządzeniami", "manage_your_oauth_connection": "Zarządzaj swoim połączeniem OAuth", "map": "Mapa", - "map_assets_in_bound": "{count} zdjęcie", - "map_assets_in_bounds": "{count} zdjęć", + "map_assets_in_bounds": "{count, plural, one {# zdjęcie} few {# zdjęcia} other {# zdjęć}}", "map_cannot_get_user_location": "Nie można uzyskać lokalizacji użytkownika", "map_location_dialog_yes": "Tak", "map_location_picker_page_use_location": "Użyj tej lokalizacji", @@ -1316,6 +1338,7 @@ "no_results": "Brak wyników", "no_results_description": "Spróbuj użyć synonimu lub bardziej ogólnego słowa kluczowego", "no_shared_albums_message": "Stwórz album aby udostępnić zdjęcia i filmy osobom w Twojej sieci", + "no_uploads_in_progress": "Brak przesyłań w toku", "not_in_any_album": "Bez albumu", "not_selected": "Nie wybrano", "note_apply_storage_label_to_previously_uploaded assets": "Uwaga: Aby przypisać etykietę magazynowania do wcześniej przesłanych zasobów, uruchom", @@ -1353,6 +1376,7 @@ "original": "oryginalny", "other": "Inne", "other_devices": "Inne urządzenia", + "other_entities": "Inne byty", "other_variables": "Inne zmienne", "owned": "Posiadany", "owner": "Właściciel", @@ -1484,6 +1508,7 @@ "purchase_server_description_2": "Status wspierającego", "purchase_server_title": "Serwer", "purchase_settings_server_activated": "Klucz produktu serwera jest zarządzany przez administratora", + "queue_status": "Kolejkowanie {count}/{total}", "rating": "Ocena gwiazdkowa", "rating_clear": "Wyczyść ocenę", "rating_count": "{count, plural, one {# gwiazdka} other {# gwiazdek}}", @@ -1512,6 +1537,8 @@ "refreshing_faces": "Odświeżanie twarzy", "refreshing_metadata": "Odświeżanie metadanych", "regenerating_thumbnails": "Regenerowanie miniatur", + "remote": "Zdalny", + "remote_assets": "Zasoby zdalne", "remove": "Usuń", "remove_assets_album_confirmation": "Czy na pewno chcesz usunąć {count, plural, one {# zasób} other {# zasoby}} z albumu?", "remove_assets_shared_link_confirmation": "Czy na pewno chcesz usunąć {count, plural, one {# zasób} other {# zasoby}} z tego udostępnionego linku?", @@ -1549,19 +1576,25 @@ "reset_password": "Resetuj hasło", "reset_people_visibility": "Zresetuj widoczność osób", "reset_pin_code": "Zresetuj kod PIN", + "reset_sqlite": "Zresetuj bazę danych SQLite", + "reset_sqlite_confirmation": "Czy na pewno chcesz zresetować bazę danych SQLite? Wymagane będzie wylogowanie oraz ponowne zalogowanie, aby zsynchronizować dane", + "reset_sqlite_success": "Pomyślnie zresetowano bazę danych SQLite", "reset_to_default": "Przywróć ustawienia domyślne", "resolve_duplicates": "Rozwiąż problemy z duplikatami", "resolved_all_duplicates": "Rozwiązano wszystkie duplikaty", "restore": "Przywrócić", "restore_all": "Przywróć wszystko", + "restore_trash_action_prompt": "{count} przywrócono z kosza", "restore_user": "Przywróć użytkownika", "restored_asset": "Przywrócony zasób", "resume": "Wznów", "retry_upload": "Prześlij ponownie", "review_duplicates": "Przejrzyj duplikaty", + "review_large_files": "Przejrzyj duże pliki", "role": "Rola", "role_editor": "Edytor", "role_viewer": "Widz", + "running": "W trakcie", "save": "Zapisz", "save_to_gallery": "Zapisz w galerii", "saved_api_key": "Zapisany klucz API", @@ -1715,6 +1748,7 @@ "shared_link_clipboard_copied_massage": "Skopiowane do schowka", "shared_link_clipboard_text": "Link: {link}\nHasło: {password}", "shared_link_create_error": "Błąd podczas tworzenia linka do udostępnienia", + "shared_link_custom_url_description": "Otwórz udostępniony link z niestandardowym adresem URL", "shared_link_edit_description_hint": "Wprowadź opis udostępnienia", "shared_link_edit_expire_after_option_day": "1 dniu", "shared_link_edit_expire_after_option_days": "{count} dniach", @@ -1740,6 +1774,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Zarządzaj udostępnionymi linkami", "shared_link_options": "Opcje udostępniania linku", + "shared_link_password_description": "Wymagaj hasła dostępu dla udostępnionego linku", "shared_links": "Udostępnione linki", "shared_links_description": "Udostępnij zdjęcia oraz filmy przez link", "shared_photos_and_videos_count": "{assetCount, plural, one {# udostępnione zdjęcie lub film.} other {# udostępnione zdjęcia i filmy.}}", @@ -1815,6 +1850,7 @@ "storage_quota": "Limit pamięci", "storage_usage": "{used} z {available} użyte", "submit": "Zatwierdź", + "success": "Sukces", "suggestions": "Sugestie", "sunrise_on_the_beach": "Wschód słońca na plaży", "support": "Wsparcie", @@ -1824,6 +1860,8 @@ "sync": "Synchronizuj", "sync_albums": "Synchronizuj albumy", "sync_albums_manual_subtitle": "Zsynchronizuj wszystkie przesłane filmy i zdjęcia z wybranymi albumami kopii zapasowych", + "sync_local": "Synchronizacja lokalna", + "sync_remote": "Synchronizacja zdalna", "sync_upload_album_setting_subtitle": "Twórz i przesyłaj swoje zdjęcia i filmy do wybranych albumów w Immich", "tag": "Etykieta", "tag_assets": "Ustaw etykiety zasobów", @@ -1834,6 +1872,7 @@ "tag_updated": "Uaktualniono etykietę: {tag}", "tagged_assets": "Przypisano etykietę {count, plural, one {# zasobowi} other {# zasobom}}", "tags": "Etykiety", + "tap_to_run_job": "Uruchom zadanie", "template": "Szablon", "theme": "Motyw", "theme_selection": "Wybór motywu", @@ -1913,10 +1952,13 @@ "updated_at": "Zaktualizowany", "updated_password": "Pomyślnie zaktualizowano hasło", "upload": "Prześlij", + "upload_action_prompt": "{count} w kolejce do wysłania", "upload_concurrency": "Współbieżność wysyłania", + "upload_details": "Szczegóły przesyłania", "upload_dialog_info": "Czy chcesz wykonać kopię zapasową wybranych zasobów na serwerze?", "upload_dialog_title": "Prześlij Zasób", "upload_errors": "Przesyłanie zakończone z {count, plural, one {# błędem} other {# błędami}}. Odśwież stronę, aby zobaczyć nowo przesłane zasoby.", + "upload_finished": "Przesyłanie zakończone", "upload_progress": "Pozostałe {remaining, number} - Przetworzone {processed, number}/{total, number}", "upload_skipped_duplicates": "Pominięto {count, plural, one {# zduplikowany zasób} few {# zduplikowane zasoby} other {# zduplikowanych zasobów}}", "upload_status_duplicates": "Duplikaty", @@ -1925,6 +1967,7 @@ "upload_success": "Przesyłanie powiodło się, odśwież stronę, aby zobaczyć nowo przesłane zasoby.", "upload_to_immich": "Prześlij do Immich ({count})", "uploading": "Przesyłanie", + "uploading_media": "Przesyłanie multimediów", "url": "URL", "usage": "Użycie", "use_biometric": "Użyj biometrii", @@ -1964,6 +2007,7 @@ "view_album": "Wyświetl Album", "view_all": "Pokaż wszystkie", "view_all_users": "Pokaż wszystkich użytkowników", + "view_details": "Zobacz szczegóły", "view_in_timeline": "Pokaż na osi czasu", "view_link": "Zobacz link", "view_links": "Pokaż łącza", diff --git a/i18n/pt.json b/i18n/pt.json index 1878c9b9cc..efe4060730 100644 --- a/i18n/pt.json +++ b/i18n/pt.json @@ -397,6 +397,7 @@ "album_cover_updated": "Capa do álbum atualizada", "album_delete_confirmation": "Tem a certeza de que quer eliminar o álbum {album}?", "album_delete_confirmation_description": "Se este álbum for partilhado, os outros utilizadores deixam de o poder aceder.", + "album_deleted": "Álbum eliminado", "album_info_card_backup_album_excluded": "EXCLUÍDO", "album_info_card_backup_album_included": "INCLUÍDO", "album_info_updated": "Informações do álbum atualizadas", @@ -406,6 +407,7 @@ "album_options": "Opções de álbum", "album_remove_user": "Remover utilizador?", "album_remove_user_confirmation": "Tem a certeza de que quer remover {user}?", + "album_search_not_found": "Nenhum álbum encontrado segundo a pesquisa", "album_share_no_users": "Parece que tem este álbum partilhado com todos os utilizadores ou que não existem utilizadores com quem o partilhar.", "album_updated": "Álbum atualizado", "album_updated_setting_description": "Receber uma notificação por e-mail quando um álbum partilhado tiver novos ficheiros", @@ -425,6 +427,7 @@ "albums_default_sort_order": "Ordem padrão de organização do álbum", "albums_default_sort_order_description": "Ordem inicial dos ficheiros ao criar novos álbuns.", "albums_feature_description": "Coleções de ficheiros que podem ser partilhados com outros utilizadores.", + "albums_on_device_count": "Álbums no dispositivo ({count})", "all": "Todos", "all_albums": "Todos os álbuns", "all_people": "Todas as pessoas", @@ -508,6 +511,7 @@ "back_close_deselect": "Voltar, fechar ou desmarcar", "background_location_permission": "Permissão de localização em segundo plano", "background_location_permission_content": "Para que seja possível trocar a URL quando estiver executando em segundo plano, o Immich deve *sempre* ter a permissão de localização precisa para que o aplicativo consiga ler o nome da rede Wi-Fi", + "backup": "Cópia de segurança", "backup_album_selection_page_albums_device": "Álbuns no dispositivo ({count})", "backup_album_selection_page_albums_tap": "Toque para incluir, duplo toque para excluir", "backup_album_selection_page_assets_scatter": "Os arquivos podem estar espalhados em vários álbuns. Assim, os álbuns podem ser incluídos ou excluídos durante o processo de backup.", @@ -571,6 +575,8 @@ "backup_options_page_title": "Opções de backup", "backup_setting_subtitle": "Gerenciar as configurações de envio em primeiro e segundo plano", "backward": "Para trás", + "beta_sync": "Estado de Sincronização Beta", + "beta_sync_subtitle": "Gerir o novo sistema de sincronização", "biometric_auth_enabled": "Autenticação biométrica ativada", "biometric_locked_out": "Está impedido de utilizar a autenticação biométrica", "biometric_no_options": "Sem opções biométricas disponíveis", @@ -588,7 +594,7 @@ "cache_settings_clear_cache_button": "Limpar cache", "cache_settings_clear_cache_button_title": "Limpa o cache do aplicativo. Isso afetará significativamente o desempenho do aplicativo até que o cache seja reconstruído.", "cache_settings_duplicated_assets_clear_button": "LIMPAR", - "cache_settings_duplicated_assets_subtitle": "Fotos e vídeos que estão na lista negra da aplicação", + "cache_settings_duplicated_assets_subtitle": "Fotos e vídeos que estão na lista de bloqueio da aplicação", "cache_settings_duplicated_assets_title": "Ficheiros duplicados ({count})", "cache_settings_statistics_album": "Miniaturas da biblioteca", "cache_settings_statistics_full": "Imagens completas", @@ -605,6 +611,7 @@ "cancel": "Cancelar", "cancel_search": "Cancelar pesquisa", "canceled": "Cancelado", + "canceling": "A cancelar", "cannot_merge_people": "Não foi possível unir pessoas", "cannot_undo_this_action": "Não é possível anular esta ação!", "cannot_update_the_description": "Não foi possível atualizar a descrição", @@ -737,24 +744,27 @@ "default_locale": "Localização Padrão", "default_locale_description": "Formatar datas e números baseados na linguagem do seu navegador", "delete": "Eliminar", - "delete_action_prompt": "{count} eliminados permanentemente", - "delete_album": "Eliminar álbum", - "delete_api_key_prompt": "Tem a certeza de que deseja eliminar esta chave de API?", + "delete_action_confirmation_message": "Tem a certeza de que quer eliminar este ficheiro? Está ação irá mover o ficheiro para a reciclagem do servidor e perguntar se quer apagá-lo localmente", + "delete_action_prompt": "{count} eliminados", + "delete_album": "Apagar álbum", + "delete_api_key_prompt": "Tem a certeza de que deseja remover esta chave de API?", "delete_dialog_alert": "Esses arquivos serão permanentemente apagados do Immich e de seu dispositivo", "delete_dialog_alert_local": "Estes arquivos serão permanentemente excluídos do seu dispositivo, mas continuarão disponíveis no servidor Immich", "delete_dialog_alert_local_non_backed_up": "Não há backup de alguns dos arquivos no servidor e eles serão excluídos permanentemente do seu dispositivo", "delete_dialog_alert_remote": "Estes arquivos serão permanentemente excluídos do servidor Immich", - "delete_dialog_ok_force": "Excluir mesmo assim", + "delete_dialog_ok_force": "Confirmo que quero excluir", "delete_dialog_title": "Excluir Permanentemente", "delete_duplicates_confirmation": "Tem a certeza de que deseja eliminar permanentemente estes itens duplicados?", "delete_face": "Remover rosto", - "delete_key": "Eliminar chave", + "delete_key": "Apagar chave", "delete_library": "Eliminar Biblioteca", "delete_link": "Eliminar link", "delete_local_action_prompt": "{count} eliminados localmente", "delete_local_dialog_ok_backed_up_only": "Excluir apenas arquivos com backup", "delete_local_dialog_ok_force": "Excluir mesmo assim", "delete_others": "Excluir outros", + "delete_permanently": "Eliminar permanentemente", + "delete_permanently_action_prompt": "{count} eliminados permanentemente", "delete_shared_link": "Eliminar link de partilha", "delete_shared_link_dialog_title": "Excluir link compartilhado", "delete_tag": "Eliminar etiqueta", @@ -765,6 +775,7 @@ "description": "Descrição", "description_input_hint_text": "Adicionar descrição...", "description_input_submit_error": "Erro ao atualizar a descrição, verifique o registo para obter mais detalhes", + "deselect_all": "Remover seleção de tudo", "details": "Detalhes", "direction": "Direção", "disabled": "Desativado", @@ -839,6 +850,7 @@ "empty_trash": "Esvaziar reciclagem", "empty_trash_confirmation": "Tem a certeza de que deseja esvaziar a reciclagem? Isto removerá todos os ficheiros da reciclagem do Immich permanentemente.\nNão é possível anular esta ação!", "enable": "Ativar", + "enable_backup": "Ativar Cópia de Segurança", "enable_biometric_auth_description": "Insira o código PIN para ativar a autenticação biométrica", "enabled": "Ativado", "end_date": "Data final", @@ -995,6 +1007,8 @@ "explorer": "Explorador", "export": "Exportar", "export_as_json": "Exportar como JSON", + "export_database": "Exportar Base de Dados", + "export_database_description": "Exportar a Base de Dados SQLite", "extension": "Extensão", "external": "Externo", "external_libraries": "Bibliotecas externas", @@ -1046,6 +1060,9 @@ "haptic_feedback_switch": "Habilitar vibração", "haptic_feedback_title": "Vibração", "has_quota": "Tem quota", + "hash_asset": "Criptografar ficheiro", + "hashed_assets": "Ficheiros criptografados", + "hashing": "A criptografar", "header_settings_add_header_tip": "Adicionar cabeçalho", "header_settings_field_validator_msg": "Campo deve ser preenchido", "header_settings_header_name_input": "Nome do cabeçalho", @@ -1078,6 +1095,7 @@ "host": "Servidor", "hour": "Hora", "id": "ID", + "idle": "Em espera", "ignore_icloud_photos": "ignorar fotos no iCloud", "ignore_icloud_photos_description": "Fotos que estão armazenadas no iCloud não serão carregadas para o servidor do Immich", "image": "Imagem", @@ -1154,13 +1172,14 @@ "light": "Claro", "like_deleted": "Gosto removido", "link_motion_video": "Relacionar video animado", - "link_options": "Opções do Link", "link_to_oauth": "Link do OAuth", "linked_oauth_account": "Conta OAuth Associada", "list": "Lista", "loading": "A Carregar", "loading_search_results_failed": "Ocorreu um erro ao carregar os resultados da pesquisa", + "local": "Local", "local_asset_cast_failed": "Não é possível transmitir um ficheiro que não tenha sido enviado antes para o servidor", + "local_assets": "Ficheiros Locais", "local_network": "Rede local", "local_network_sheet_info": "O aplicativo irá se conectar ao servidor através desta URL quando estiver na rede Wi-Fi especificada", "location_permission": "Permissão de localização", @@ -1217,8 +1236,7 @@ "manage_your_devices": "Gerir os seus dispositivos com sessão iniciada", "manage_your_oauth_connection": "Gerir a sua ligação ao OAuth", "map": "Mapa", - "map_assets_in_bound": "{count} foto", - "map_assets_in_bounds": "{count} fotos", + "map_assets_in_bounds": "{count, plural, one {# foto} other {# fotos}}", "map_cannot_get_user_location": "Impossível obter a sua localização", "map_location_dialog_yes": "Sim", "map_location_picker_page_use_location": "Utilizar esta localização", @@ -1317,6 +1335,7 @@ "no_results": "Sem resultados", "no_results_description": "Tente um sinónimo ou uma palavra-chave mais comum", "no_shared_albums_message": "Crie um álbum para partilhar fotos e vídeos com pessoas na sua rede", + "no_uploads_in_progress": "Nenhum carregamento em curso", "not_in_any_album": "Não está em nenhum álbum", "not_selected": "Não selecionado", "note_apply_storage_label_to_previously_uploaded assets": "Nota: Para aplicar o Rótulo de Armazenamento a ficheiros carregados anteriormente, execute o", @@ -1354,6 +1373,7 @@ "original": "original", "other": "Outro", "other_devices": "Outros dispositivos", + "other_entities": "Outras entidades", "other_variables": "Outras variáveis", "owned": "Seu", "owner": "Dono", @@ -1485,6 +1505,7 @@ "purchase_server_description_2": "Status de apoiante", "purchase_server_title": "Servidor", "purchase_settings_server_activated": "A chave de produto do servidor é gerida pelo administrador", + "queue_status": "Em fila {count}/{total}", "rating": "Classificação por estrelas", "rating_clear": "Limpar classificação", "rating_count": "{count, plural, one {# estrela} other {# estrelas}}", @@ -1513,6 +1534,8 @@ "refreshing_faces": "A atualizar rostos", "refreshing_metadata": "A atualizar metadados", "regenerating_thumbnails": "A atualizar miniaturas", + "remote": "Remoto", + "remote_assets": "Ficheiros Remotos", "remove": "Remover", "remove_assets_album_confirmation": "Tem a certeza de que deseja remover {count, plural, one {# ficheiro} other {# ficheiros}} do álbum?", "remove_assets_shared_link_confirmation": "Tem certeza de que deseja remover {count, plural, one {# ficheiro} other {# ficheiros}} deste link partilhado?", @@ -1550,11 +1573,15 @@ "reset_password": "Redefinir palavra-passe", "reset_people_visibility": "Redefinir pessoas ocultas", "reset_pin_code": "Repor código PIN", + "reset_sqlite": "Reiniciar Base de Dados SQLite", + "reset_sqlite_confirmation": "Tem a certeza de que quer reiniciar a base de dados SQLite? Vai ter de terminar a sessão e entrar outra vez para sincronizar os dados de novo", + "reset_sqlite_success": "Base de dados SQLite reiniciada com sucesso", "reset_to_default": "Repor predefinições", "resolve_duplicates": "Resolver itens duplicados", "resolved_all_duplicates": "Todos os itens duplicados resolvidos", "restore": "Restaurar", "restore_all": "Restaurar tudo", + "restore_trash_action_prompt": "{count} restaurados da reciclagem", "restore_user": "Restaurar utilizador", "restored_asset": "Ficheiro restaurado", "resume": "Continuar", @@ -1563,6 +1590,7 @@ "role": "Função", "role_editor": "Editor", "role_viewer": "Visualizador", + "running": "A executar", "save": "Guardar", "save_to_gallery": "Salvar na galeria", "saved_api_key": "Chave de API guardada", @@ -1816,6 +1844,7 @@ "storage_quota": "Quota de armazenamento", "storage_usage": "Utilizado {used} de {available}", "submit": "Enviar", + "success": "Sucesso", "suggestions": "Sugestões", "sunrise_on_the_beach": "Nascer do sol na praia", "support": "Apoio", @@ -1825,6 +1854,8 @@ "sync": "Sincronizar", "sync_albums": "Sincronizar álbuns", "sync_albums_manual_subtitle": "Sincronizar todas as fotos e vídeos enviados para o álbum de backup selecionado", + "sync_local": "Sincronização Local", + "sync_remote": "Sincronização Remota", "sync_upload_album_setting_subtitle": "Crie e envie suas fotos e vídeos para o álbum selecionado no Immich", "tag": "Etiqueta", "tag_assets": "Etiquetar ficheiros", @@ -1835,6 +1866,7 @@ "tag_updated": "Atualizada a etiqueta: {tag}", "tagged_assets": "Etiquetado {count, plural, one {# ficheiros} other {# ficheiros}}", "tags": "Etiquetas", + "tap_to_run_job": "Tocar para executar tarefa", "template": "Modelo", "theme": "Tema", "theme_selection": "Selecionar tema", @@ -1914,10 +1946,13 @@ "updated_at": "Atualizado a", "updated_password": "Palavra-passe atualizada", "upload": "Carregar", + "upload_action_prompt": "{count} à espera de carregar", "upload_concurrency": "Carregamentos em simultâneo", + "upload_details": "Detalhes do Carregamento", "upload_dialog_info": "Deseja fazer o backup dos arquivos selecionados no servidor?", "upload_dialog_title": "Enviar arquivo", "upload_errors": "Envio completo com {count, plural, one {# erro} other {# erros}}, atualize a página para ver os novos ficheiros enviados.", + "upload_finished": "Carregamento acabado", "upload_progress": "Restante(s) {remaining, number} - Processado(s) {processed, number}/{total, number}", "upload_skipped_duplicates": "{count, plural, one {# Ignorado ficheiro duplicado} other {# Ignorados ficheiros duplicados}}", "upload_status_duplicates": "Duplicados", @@ -1926,6 +1961,7 @@ "upload_success": "Carregamento realizado com sucesso, atualize a página para ver os novos ficheiros carregados.", "upload_to_immich": "Enviar para o Immich ({count})", "uploading": "Enviando", + "uploading_media": "A carregar media", "url": "URL", "usage": "Utilização", "use_biometric": "Utilizar dados biométricos", @@ -1965,6 +2001,7 @@ "view_album": "Ver Álbum", "view_all": "Ver tudo", "view_all_users": "Ver todos os utilizadores", + "view_details": "Ver Detalhes", "view_in_timeline": "Ver na linha do tempo", "view_link": "Ver link", "view_links": "Ver links", diff --git a/i18n/pt_BR.json b/i18n/pt_BR.json index 1af3bda2ac..c8bf543731 100644 --- a/i18n/pt_BR.json +++ b/i18n/pt_BR.json @@ -166,6 +166,20 @@ "metadata_settings_description": "Gerenciar configurações de metadados", "migration_job": "Migração", "migration_job_description": "Migrar miniaturas de arquivos e rostos para a estrutura de pastas mais recente", + "nightly_tasks_cluster_faces_setting_description": "Fazer o reconhecimento facial dos novos rostos detectados", + "nightly_tasks_cluster_new_faces_setting": "Agrupar novos rostos", + "nightly_tasks_database_cleanup_setting": "Tarefas de limpeza do banco de dados", + "nightly_tasks_database_cleanup_setting_description": "Limpe dados velhos e expirados do banco de dados", + "nightly_tasks_generate_memories_setting": "Gerar memórias", + "nightly_tasks_generate_memories_setting_description": "Criar novas memórias a partir dos arquivos", + "nightly_tasks_missing_thumbnails_setting": "Gerar miniaturas em falta", + "nightly_tasks_missing_thumbnails_setting_description": "Adiciona na fila de geração de miniaturas as fotos ainda sem miniaturas", + "nightly_tasks_settings": "Configurações de Tarefas Diárias", + "nightly_tasks_settings_description": "Gerenciar tarefas diárias", + "nightly_tasks_start_time_setting": "Hora de início", + "nightly_tasks_start_time_setting_description": "A hora que o servidor começa a executar as tarefas diárias", + "nightly_tasks_sync_quota_usage_setting": "Utilização da quota de sincronização", + "nightly_tasks_sync_quota_usage_setting_description": "Atualizar quotas de armazenamento dos usuários, com base na utilização atual", "no_paths_added": "Nenhum caminho adicionado", "no_pattern_added": "Nenhum padrão adicionado", "note_apply_storage_label_previous_assets": "Observação: Para aplicar o rótulo de armazenamento a arquivos enviados anteriormente, execute o", @@ -359,6 +373,8 @@ "admin_password": "Senha do administrador", "administration": "Administração", "advanced": "Avançado", + "advanced_settings_beta_timeline_subtitle": "Teste a nova interface do aplicativo", + "advanced_settings_beta_timeline_title": "Linha do tempo Beta", "advanced_settings_enable_alternate_media_filter_subtitle": "Use esta opção para filtrar mídias durante a sincronização com base em critérios alternativos. Tente esta opção somente se o aplicativo estiver com problemas para detectar todos os álbuns.", "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTAL] Utilizar filtro alternativo de sincronização de álbum de dispositivo", "advanced_settings_log_level_title": "Nível de log: {level}", @@ -381,6 +397,7 @@ "album_cover_updated": "Capa do álbum atualizada", "album_delete_confirmation": "Tem certeza de que deseja excluir o álbum {album}?", "album_delete_confirmation_description": "Se este álbum é compartilhado, os outros usuários não conseguiram mais acessá-lo.", + "album_deleted": "Álbum deletado", "album_info_card_backup_album_excluded": "EXCLUÍDO", "album_info_card_backup_album_included": "INCLUÍDO", "album_info_updated": "Informações do álbum atualizadas", @@ -390,6 +407,7 @@ "album_options": "Opções de álbum", "album_remove_user": "Remover usuário?", "album_remove_user_confirmation": "Tem certeza de que deseja remover {user}?", + "album_search_not_found": "Não há álbum que corresponda à sua pesquisa", "album_share_no_users": "Parece que você já compartilhou este álbum com todos os usuários ou não há nenhum usuário para compartilhar.", "album_updated": "Álbum atualizado", "album_updated_setting_description": "Receba uma notificação por e-mail quando um álbum compartilhado tiver novos recursos", @@ -409,6 +427,7 @@ "albums_default_sort_order": "Ordem padrão do álbum", "albums_default_sort_order_description": "Ordem padrão dos arquivos ao criar novos álbuns.", "albums_feature_description": "Coleções de arquivos que podem ser compartilhados com outros usuários.", + "albums_on_device_count": "Álbuns no dispositivo ({count})", "all": "Todos", "all_albums": "Todos os álbuns", "all_people": "Todas as pessoas", @@ -492,6 +511,7 @@ "back_close_deselect": "Voltar, fechar ou desmarcar", "background_location_permission": "Permissão de localização em segundo plano", "background_location_permission_content": "Para que seja possível trocar o endereço quando estiver executando em segundo plano, o Immich deve *sempre* ter a permissão de localização precisa para que o aplicativo consiga ler o nome da rede Wi-Fi", + "backup": "Backup", "backup_album_selection_page_albums_device": "Álbuns no dispositivo ({count})", "backup_album_selection_page_albums_tap": "Toque para incluir, toque duas vezes para excluir", "backup_album_selection_page_assets_scatter": "Os recursos podem se espalhar por vários álbuns. Assim, os álbuns podem ser incluídos ou excluídos durante o processo de backup.", @@ -555,6 +575,8 @@ "backup_options_page_title": "Opções de backup", "backup_setting_subtitle": "Gerenciar as configurações de envio em primeiro e segundo plano", "backward": "Para trás", + "beta_sync": "Status da sincronização Beta", + "beta_sync_subtitle": "Configurar o novo sistema de sincronização", "biometric_auth_enabled": "Autenticação por biometria ativada", "biometric_locked_out": "Sua autenticação por biometria está bloqueada", "biometric_no_options": "Não há opções de biometria disponíveis", @@ -589,6 +611,7 @@ "cancel": "Cancelar", "cancel_search": "Cancelar pesquisa", "canceled": "Cancelado", + "canceling": "Cancelando", "cannot_merge_people": "Não é possível mesclar pessoas", "cannot_undo_this_action": "Você não pode desfazer esta ação!", "cannot_update_the_description": "Não é possível atualizar a descrição", @@ -642,7 +665,7 @@ "comments_are_disabled": "Comentários estão desativados", "common_create_new_album": "Criar novo álbum", "common_server_error": "Verifique a sua conexão de rede, certifique-se de que o servidor está acessível e de que as versões do aplicativo e servidor são compatíveis.", - "completed": "Sucesso", + "completed": "Completado", "confirm": "Confirmar", "confirm_admin_password": "Confirmar senha de administrador", "confirm_delete_face": "Tem certeza que deseja remover a rosto de {name} deste arquivo?", @@ -702,6 +725,7 @@ "current_server_address": "Endereço atual do servidor", "custom_locale": "Localização Customizada", "custom_locale_description": "Formatar datas e números baseados na linguagem e região", + "custom_url": "URL personalizada", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Escuro", @@ -721,7 +745,8 @@ "default_locale": "Localização Padrão", "default_locale_description": "Formatar datas e números baseados na linguagem do seu navegador", "delete": "Excluir", - "delete_action_prompt": "{count} deletados permanentemente", + "delete_action_confirmation_message": "Confirma deletar este arquivo? O arquivo será enviado para a lixeira do servidor e depois perguntará se deseja deletar do seu dispositivo local", + "delete_action_prompt": "{count} deletados", "delete_album": "Excluir álbum", "delete_api_key_prompt": "Tem certeza de que deseja excluir esta chave de API?", "delete_dialog_alert": "Esses itens serão excluídos permanentemente do Immich e do seu dispositivo", @@ -735,9 +760,12 @@ "delete_key": "Excluir chave", "delete_library": "Excluir biblioteca", "delete_link": "Excluir link", + "delete_local_action_prompt": "{count} deletados do dispositivo local", "delete_local_dialog_ok_backed_up_only": "Excluir apenas arquivos com backup feito", "delete_local_dialog_ok_force": "Excluir mesmo assim", "delete_others": "Excluir restante", + "delete_permanently": "Deletar permanentemente", + "delete_permanently_action_prompt": "{count} Deletado permanentemente", "delete_shared_link": "Excluir link de compartilhamento", "delete_shared_link_dialog_title": "Excluir link compartilhado", "delete_tag": "Excluir marcador", @@ -748,6 +776,7 @@ "description": "Descrição", "description_input_hint_text": "Adicionar descrição...", "description_input_submit_error": "Erro ao atualizar a descrição, verifique o log para mais detalhes", + "deselect_all": "Desselecionar tudo", "details": "Detalhes", "direction": "Direção", "disabled": "Desativado", @@ -765,6 +794,7 @@ "documentation": "Documentação", "done": "Feito", "download": "Baixar", + "download_action_prompt": "Baixando {count} arquivos", "download_canceled": "Cancelado", "download_complete": "Sucesso", "download_enqueue": "Na fila", @@ -821,6 +851,7 @@ "empty_trash": "Esvaziar lixeira", "empty_trash_confirmation": "Tem certeza de que deseja esvaziar a lixeira? Isso removerá permanentemente do Immich todos os arquivos que estão na lixeira.\nVocê não pode desfazer esta ação!", "enable": "Habilitar", + "enable_backup": "Ativar Backup", "enable_biometric_auth_description": "Insira seu código PIN para ativar a autenticação por biometria", "enabled": "Habilitado", "end_date": "Data final", @@ -977,6 +1008,8 @@ "explorer": "Explorar", "export": "Exportar", "export_as_json": "Exportar como JSON", + "export_database": "Exportar Banco de Dados", + "export_database_description": "Exportar o Banco de Dados SQLite", "extension": "Extensão", "external": "Externo", "external_libraries": "Bibliotecas externas", @@ -1028,6 +1061,9 @@ "haptic_feedback_switch": "Ativar vibração", "haptic_feedback_title": "Vibração", "has_quota": "Cota", + "hash_asset": "Calcular hash dos arquivos", + "hashed_assets": "Com hash", + "hashing": "Calculando", "header_settings_add_header_tip": "Adicionar Cabeçalho", "header_settings_field_validator_msg": "O valor não pode estar vazio", "header_settings_header_name_input": "Nome do cabeçalho", @@ -1060,6 +1096,7 @@ "host": "Servidor", "hour": "Hora", "id": "ID", + "idle": "Inativo", "ignore_icloud_photos": "Ignorar fotos do iCloud", "ignore_icloud_photos_description": "Fotos que estão armazenadas no iCloud não serão enviadas para o servidor do Immich", "image": "Imagem", @@ -1132,16 +1169,18 @@ "library_page_sort_created": "Data de criação", "library_page_sort_last_modified": "Última modificação", "library_page_sort_title": "Título do álbum", + "licenses": "Licenças", "light": "Claro", "like_deleted": "Curtida excluída", "link_motion_video": "Relacionar video animado", - "link_options": "Opções do Link", "link_to_oauth": "Link do OAuth", "linked_oauth_account": "Conta OAuth Vinculada", "list": "Lista", "loading": "Carregando", "loading_search_results_failed": "Falha ao carregar os resultados da pesquisa", + "local": "Local", "local_asset_cast_failed": "Não é possível transmitir um arquivo que não foi enviado ao servidor", + "local_assets": "Arquivos no dispositivo", "local_network": "Rede local", "local_network_sheet_info": "O aplicativo irá se conectar ao servidor através deste endereço quando estiver na rede Wi-Fi especificada", "location_permission": "Permissão de localização", @@ -1172,7 +1211,7 @@ "login_form_err_trailing_whitespace": "Há um espaço em branco no fim", "login_form_failed_get_oauth_server_config": "Erro de login com OAuth, verifique a URL do servidor", "login_form_failed_get_oauth_server_disable": "O recurso OAuth não está disponível neste servidor", - "login_form_failed_login": "Erro ao fazer login, verifique a url do servidor, e-mail e senha", + "login_form_failed_login": "Erro ao fazer login, verifique a URL do servidor, e-mail e senha", "login_form_handshake_exception": "Houve um erro de autorização com o servidor. Se estiver utilizando um certificado auto assinado, ative o suporte a isso nas configurações.", "login_form_password_hint": "senha", "login_form_save_login": "Permaneçer conectado", @@ -1198,8 +1237,7 @@ "manage_your_devices": "Gerenciar seus dispositivos logados", "manage_your_oauth_connection": "Gerenciar sua conexão OAuth", "map": "Mapa", - "map_assets_in_bound": "{count} foto", - "map_assets_in_bounds": "{count} fotos", + "map_assets_in_bounds": "{count, plural, one {# foto} other {# fotos}}", "map_cannot_get_user_location": "Não foi possível obter a sua localização", "map_location_dialog_yes": "Sim", "map_location_picker_page_use_location": "Use esta localização", @@ -1298,6 +1336,7 @@ "no_results": "Sem resultados", "no_results_description": "Tente um sinônimo ou uma palavra-chave mais geral", "no_shared_albums_message": "Crie um álbum para compartilhar fotos e vídeos com pessoas em sua rede", + "no_uploads_in_progress": "Nenhum envio em progresso", "not_in_any_album": "Fora de álbum", "not_selected": "Não selecionado", "note_apply_storage_label_to_previously_uploaded assets": "Nota: Para aplicar o rótulo de armazenamento a arquivos enviados anteriormente, execute o", @@ -1335,6 +1374,7 @@ "original": "original", "other": "Outro", "other_devices": "Outros dispositivos", + "other_entities": "Outras entidades", "other_variables": "Outras variáveis", "owned": "Seu", "owner": "Dono", @@ -1466,6 +1506,7 @@ "purchase_server_description_2": "Status de Contribuidor", "purchase_server_title": "Servidor", "purchase_settings_server_activated": "A chave do produto para servidor é gerenciada pelo administrador", + "queue_status": "Na fila {count} de {total}", "rating": "Estrelas", "rating_clear": "Limpar classificação", "rating_count": "{count, plural, one {# estrela} other {# estrelas}}", @@ -1494,6 +1535,8 @@ "refreshing_faces": "Atualizando rostos", "refreshing_metadata": "Atualizando metadados", "regenerating_thumbnails": "Regenerando miniaturas", + "remote": "Remoto", + "remote_assets": "Arquivos Remotos", "remove": "Remover", "remove_assets_album_confirmation": "Tem certeza de que deseja remover {count, plural, one {# arquivo} other {# arquivos}} do álbum?", "remove_assets_shared_link_confirmation": "Tem certeza de que deseja remover {count, plural, one {# arquivo} other {# arquivos}} desse link compartilhado?", @@ -1531,11 +1574,15 @@ "reset_password": "Resetar senha", "reset_people_visibility": "Resetar pessoas ocultas", "reset_pin_code": "Redefinir código PIN", + "reset_sqlite": "Redefinir o Banco de Dados SQLite", + "reset_sqlite_confirmation": "Realmente deseja redefinir o banco de dados SQLite? Será necessário sair e entrar em sua conta novamente para ressincronizar os dados", + "reset_sqlite_success": "Banco de dados SQLite redefinido com sucesso", "reset_to_default": "Redefinir para a configuração padrão", "resolve_duplicates": "Resolver duplicatas", "resolved_all_duplicates": "Todas duplicidades resolvidas", "restore": "Restaurar", "restore_all": "Restaurar tudo", + "restore_trash_action_prompt": "{count} restaurados da lixeira", "restore_user": "Restaurar usuário", "restored_asset": "Arquivo restaurado", "resume": "Continuar", @@ -1544,6 +1591,7 @@ "role": "Função", "role_editor": "Editor", "role_viewer": "Visualizador", + "running": "Executando", "save": "Salvar", "save_to_gallery": "Salvar na galeria", "saved_api_key": "Chave de API salva", @@ -1675,10 +1723,11 @@ "settings_saved": "Configurações salvas", "setup_pin_code": "Criar um código PIN", "share": "Compartilhar", + "share_action_prompt": "{count} arquivos compartilhados", "share_add_photos": "Adicionar fotos", "share_assets_selected": "{count} selecionado", "share_dialog_preparing": "Preparando...", - "share_link": "Compartilhar Link", + "share_link": "Criar Link", "shared": "Compartilhado", "shared_album_activities_input_disable": "Comentários desativados", "shared_album_activity_remove_content": "Deseja excluir esta atividade?", @@ -1696,6 +1745,7 @@ "shared_link_clipboard_copied_massage": "Copiado para a área de transferência", "shared_link_clipboard_text": "Link: {link}\nSenha: {password}", "shared_link_create_error": "Erro ao criar o link compartilhado", + "shared_link_custom_url_description": "Acessar este link com uma URL personalizada", "shared_link_edit_description_hint": "Digite a descrição do compartilhamento", "shared_link_edit_expire_after_option_day": "1 dia", "shared_link_edit_expire_after_option_days": "{count} dias", @@ -1721,6 +1771,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Gerenciar links compartilhados", "shared_link_options": "Opções de link compartilhado", + "shared_link_password_description": "Exija uma senha para acessar este link compartilhado", "shared_links": "Links", "shared_links_description": "Compartilhar fotos e videos com um link", "shared_photos_and_videos_count": "{assetCount, plural, one {# Foto & vídeo compartilhado.} other {# Fotos & vídeos compartilhados.}}", @@ -1776,6 +1827,7 @@ "sort_title": "Título", "source": "Fonte", "stack": "Agrupar", + "stack_action_prompt": "{count} agrupados", "stack_duplicates": "Agrupar duplicados", "stack_select_one_photo": "Selecione uma foto principal para o grupo", "stack_selected_photos": "Agrupar fotos selecionadas", @@ -1795,6 +1847,7 @@ "storage_quota": "Quota de armazenamento", "storage_usage": "Utilizado {used} de {available}", "submit": "Enviar", + "success": "Sucesso", "suggestions": "Sugestões", "sunrise_on_the_beach": "Nascer do sol na praia", "support": "Ajuda", @@ -1804,6 +1857,8 @@ "sync": "Sincronizar", "sync_albums": "Sincronizar álbuns", "sync_albums_manual_subtitle": "Sincronize todos as fotos e vídeos enviados para os álbuns de backup selecionados", + "sync_local": "Sincronização Local", + "sync_remote": "Sincronização Remota", "sync_upload_album_setting_subtitle": "Crie e envie suas fotos e vídeos para o álbum selecionado no Immich", "tag": "Marcador", "tag_assets": "Marcar arquivos", @@ -1814,6 +1869,7 @@ "tag_updated": "Marcador foi atualizado: {tag}", "tagged_assets": "{count, plural, one {# Arquivo marcado} other {# Arquivos marcados}}", "tags": "Marcadores", + "tap_to_run_job": "Toque para executar", "template": "Modelo", "theme": "Tema", "theme_selection": "Selecionar tema", @@ -1886,16 +1942,20 @@ "unselect_all_duplicates": "Desselecionar todas as duplicatas", "unselect_all_in": "Remover seleção de {group}", "unstack": "Retirar do grupo", + "unstack_action_prompt": "{count} desagrupados", "unstacked_assets_count": "{count, plural, one {# arquivo retirado} other {# arquivos retirados}} do grupo", "untagged": "Marcador removido", "up_next": "A seguir", "updated_at": "Atualizado em", "updated_password": "Senha atualizada", "upload": "Enviar", + "upload_action_prompt": "{count} na fila de envio", "upload_concurrency": "Envios simultâneos", + "upload_details": "Detalhes do envio", "upload_dialog_info": "Deseja fazer o backup dos arquivos selecionados no servidor?", "upload_dialog_title": "Enviar arquivo", "upload_errors": "Envio concluído com {count, plural, one {# erro} other {# erros}}, atualize a página para ver os novos arquivos.", + "upload_finished": "Envio finalizado", "upload_progress": "{remaining, number} restantes - {processed, number}/{total, number} já processados", "upload_skipped_duplicates": "{count, plural, one {# Arquivo duplicado foi ignorado} other {# Arquivos duplicados foram ignorados}}", "upload_status_duplicates": "Duplicados", @@ -1904,6 +1964,7 @@ "upload_success": "Enviado com sucesso, atualize a página para ver os novos arquivos.", "upload_to_immich": "Enviar para o Immich ({count})", "uploading": "Enviando", + "uploading_media": "Enviando mídia", "url": "URL", "usage": "Uso", "use_biometric": "Usar biometria", @@ -1924,6 +1985,7 @@ "user_usage_stats_description": "Ver estatísticas de utilização da conta", "username": "Nome do usuário", "users": "Usuários", + "users_added_to_album_count": "{count, plural, one {# usuário adicionado} other {# usuários adicionados}} ao álbum", "utilities": "Ferramentas", "validate": "Validar", "validate_endpoint_error": "Digite uma URL válida", @@ -1942,6 +2004,7 @@ "view_album": "Ver álbum", "view_all": "Ver tudo", "view_all_users": "Ver todos os usuários", + "view_details": "Ver Detalhes", "view_in_timeline": "Ver na linha do tempo", "view_link": "Ver link", "view_links": "Ver links", diff --git a/i18n/ro.json b/i18n/ro.json index 46197e943f..40c50a317d 100644 --- a/i18n/ro.json +++ b/i18n/ro.json @@ -166,6 +166,19 @@ "metadata_settings_description": "Gestionează setările pentru metadate", "migration_job": "Migrare", "migration_job_description": "Migrați miniaturile pentru elemente și fețe la cea mai recentă structură de foldere", + "nightly_tasks_cluster_faces_setting_description": "Rulează recunoașterea facială pe fețele noi recunoscute", + "nightly_tasks_cluster_new_faces_setting": "Grupează fetele noi", + "nightly_tasks_database_cleanup_setting": "Sarcini curățare baze de date", + "nightly_tasks_database_cleanup_setting_description": "Curată date vechi, expirate din baza de date", + "nightly_tasks_generate_memories_setting": "Generare memorii", + "nightly_tasks_generate_memories_setting_description": "Creează amintiri noi din resurse", + "nightly_tasks_missing_thumbnails_setting": "Generează miniaturi lipsă", + "nightly_tasks_settings": "Setări pentru sarcinile nocturne", + "nightly_tasks_settings_description": "Gestionați sarcinile nocturne", + "nightly_tasks_start_time_setting": "Ora de începere", + "nightly_tasks_start_time_setting_description": "Ora la care serverul începe să execute sarcinile nocturne", + "nightly_tasks_sync_quota_usage_setting": "Utilizarea cotei de sincronizare", + "nightly_tasks_sync_quota_usage_setting_description": "Actualizați cota de stocare a utilizatorului, în funcție de utilizarea actuală", "no_paths_added": "Nicio cale adăugată", "no_pattern_added": "Niciun tipar adăugat", "note_apply_storage_label_previous_assets": "Notă: Pentru a aplica Eticheta de Stocare la elementele încărcate anterior, executați", @@ -196,6 +209,8 @@ "oauth_mobile_redirect_uri": "URI de redirecționare mobilă", "oauth_mobile_redirect_uri_override": "Înlocuire URI de redirecționare mobilă", "oauth_mobile_redirect_uri_override_description": "Activați atunci când furnizorul OAuth nu permite un URI mobil, precum ''{callback}''", + "oauth_role_claim": "Revendicare de rol", + "oauth_role_claim_description": "Acordă automat acces de administrator în funcție de prezența acestei revendicări. Revendicarea poate avea fie 'utilizator', fie 'administrator'.", "oauth_settings": "OAuth", "oauth_settings_description": "Gestionați setările de conectare OAuth", "oauth_settings_more_details": "Pentru mai multe detalii despre aceastǎ funcționalitate, verificǎ documentația.", @@ -357,10 +372,12 @@ "admin_password": "Parolă Administrator", "administration": "Administrare", "advanced": "Avansat", + "advanced_settings_beta_timeline_subtitle": "Încearcă noua experiență în aplicație", + "advanced_settings_beta_timeline_title": "Cronologie beta", "advanced_settings_enable_alternate_media_filter_subtitle": "Utilizați această opțiune pentru a filtra conținutul media în timpul sincronizării pe baza unor criterii alternative. Încercați numai dacă întâmpinați probleme cu aplicația la detectarea tuturor albumelor.", "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTAL] Utilizați filtrul alternativ de sincronizare a albumelor de pe dispozitiv", "advanced_settings_log_level_title": "Nivel log: {level}", - "advanced_settings_prefer_remote_subtitle": "Unele dispozitive întâmpină dificultăți în încărcarea miniaturilor pentru resursele de pe dispozitiv. Activează această setare pentru a încărca imaginile de la distanță în schimb.", + "advanced_settings_prefer_remote_subtitle": "Unele dispozitive încarcă extrem de lent miniaturile din resursele locale. Activați această setare pentru a încărca imagini la distanță.", "advanced_settings_prefer_remote_title": "Preferă fotografii la distanță", "advanced_settings_proxy_headers_subtitle": "Definește antetele proxy pe care Immich ar trebui să le trimită cu fiecare solicitare de rețea", "advanced_settings_proxy_headers_title": "Antete Proxy", @@ -379,6 +396,7 @@ "album_cover_updated": "Coperta albumului a fost actualizată", "album_delete_confirmation": "Ești sigur că vrei să ștergi albumul {album}?", "album_delete_confirmation_description": "Dacă acest album este partajat, alți utilizatori nu vor mai putea accesa.", + "album_deleted": "Album șters", "album_info_card_backup_album_excluded": "EXCLUSE", "album_info_card_backup_album_included": "INCLUSE", "album_info_updated": "Informații album actualizate", @@ -388,6 +406,7 @@ "album_options": "Opțiuni album", "album_remove_user": "Eliminare utilizator?", "album_remove_user_confirmation": "Ești sigur că dorești eliminarea {user}?", + "album_search_not_found": "Nu s-au găsit albume care să corespundă căutării dumneavoastră", "album_share_no_users": "Se pare că ai partajat acest album cu toți utilizatorii sau nu ai niciun utilizator cu care să-l partajezi.", "album_updated": "Album actualizat", "album_updated_setting_description": "Primiți o notificare prin e-mail când un album partajat are elemente noi", @@ -407,6 +426,7 @@ "albums_default_sort_order": "Ordinea implicită de sortare a albumelor", "albums_default_sort_order_description": "Ordinea inițială de sortare a pozelor la crearea de albume noi.", "albums_feature_description": "Colecții de date care pot fi partajate cu alți utilizatori.", + "albums_on_device_count": "{count} albume pe dispozitiv", "all": "Toate", "all_albums": "Toate albumele", "all_people": "Toți oamenii", @@ -490,6 +510,7 @@ "back_close_deselect": "Înapoi, închidere sau deselectare", "background_location_permission": "Permisiune locație în fundal", "background_location_permission_content": "Pentru a putea schimba rețeaua activă în fundal, Immich are nevoie de acces *permanent* la locația precisă pentru a citi numele rețelei Wi-Fi", + "backup": "Backup", "backup_album_selection_page_albums_device": "Albume în dispozitiv ({count})", "backup_album_selection_page_albums_tap": "Apasă odata pentru a include, de două ori pentru a exclude", "backup_album_selection_page_assets_scatter": "Resursele pot fi împrăștiate în mai multe albume. Prin urmare, albumele pot fi incluse sau excluse în timpul procesului de backup.", @@ -553,6 +574,8 @@ "backup_options_page_title": "Opțiuni Backup", "backup_setting_subtitle": "Schimbă opțiuni pentru backup în prim-plan și în fundal", "backward": "În sens invers", + "beta_sync": "Starea sincronizării Beta", + "beta_sync_subtitle": "Gestionați noul sistem de sincronizare", "biometric_auth_enabled": "Autentificare biometrică activată", "biometric_locked_out": "Sunteți blocați de la autentificare biometrică", "biometric_no_options": "Nu sunt disponibile opțiuni biometrice", @@ -570,7 +593,7 @@ "cache_settings_clear_cache_button": "Șterge cache", "cache_settings_clear_cache_button_title": "Șterge memoria cache a aplicatiei. Performanța aplicației va fi semnificativ afectată până când va fi reconstruită.", "cache_settings_duplicated_assets_clear_button": "ȘTERGE", - "cache_settings_duplicated_assets_subtitle": "Fotografii și videoclipuri care sunt pe lista neagră a aplicației", + "cache_settings_duplicated_assets_subtitle": "Fotografii și videoclipuri ignorate în lista aplicației", "cache_settings_duplicated_assets_title": "Resurse duplicate ({count})", "cache_settings_statistics_album": "Miniaturi pentru librării", "cache_settings_statistics_full": "Fotografii complete", @@ -587,6 +610,7 @@ "cancel": "Anulați", "cancel_search": "Anulați căutarea", "canceled": "Anulat", + "canceling": "Se anulează", "cannot_merge_people": "Nu se pot îmbina persoanele", "cannot_undo_this_action": "Nu puteți anula această acțiune!", "cannot_update_the_description": "Nu se poate actualiza descrierea", @@ -700,11 +724,14 @@ "current_server_address": "Adresa actuală a serverului", "custom_locale": "Setare Regională Personalizată", "custom_locale_description": "Formatați datele și numerele în funcție de limbă și regiune", + "daily_title_text_date": "E, MMM dd", + "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Întunecat", "dark_theme": "Comută tema întunecată", "date_after": "După data", "date_and_time": "Dată și oră", "date_before": "Anterior datei", + "date_format": "E, LLL d, y • h:mm a", "date_of_birth_saved": "Data nașterii salvată cu succes", "date_range": "Interval de date", "day": "Zi", @@ -716,7 +743,8 @@ "default_locale": "Setare Regională Implicită", "default_locale_description": "Formatați datele și numerele în funcție de regiunea browserului dvs", "delete": "Ștergere", - "delete_action_prompt": "{count} șterse permanent", + "delete_action_confirmation_message": "Sigur vrei să ștergi acest element? Această acțiune va muta elementul în coșul de gunoi al serverului și te va întreba dacă vrei să-l ștergi local", + "delete_action_prompt": "{count} șterse", "delete_album": "Ștergere album", "delete_api_key_prompt": "Sunteți sigur că doriți să ștergeți această cheie API?", "delete_dialog_alert": "Aceste elemente vor fi șterse permanent de pe server-ul Immich și din dispozitivul tău", @@ -730,9 +758,12 @@ "delete_key": "Ștergere cheie", "delete_library": "Ștergere biblioteca", "delete_link": "Ștergere link", + "delete_local_action_prompt": "{count} șterse local", "delete_local_dialog_ok_backed_up_only": "Șterge doar fișierele pentru care s-a făcut backup", "delete_local_dialog_ok_force": "Șterge oricum", "delete_others": "Ștergeți celelalte", + "delete_permanently": "Șterge permanent", + "delete_permanently_action_prompt": "{count} șterse permanent", "delete_shared_link": "Ștergere link partajat", "delete_shared_link_dialog_title": "Șterge link distribuire", "delete_tag": "Ștergere etichetă", @@ -743,6 +774,7 @@ "description": "Descriere", "description_input_hint_text": "Adaugă descriere...", "description_input_submit_error": "Eroare actualizare descriere, verifică log-urile pentru mai multe detalii", + "deselect_all": "Deselectează toate", "details": "Detalii", "direction": "Direcție", "disabled": "Dezactivat", @@ -760,6 +792,7 @@ "documentation": "Documentație", "done": "Gata", "download": "Descărcați", + "download_action_prompt": "Se descarcă {count} elemente", "download_canceled": "Descărcare anulată", "download_complete": "Descărcare completă", "download_enqueue": "Descărcare în coadă", @@ -768,10 +801,17 @@ "download_finished": "Descărcare finalizată", "download_include_embedded_motion_videos": "Videoclipuri încorporate", "download_include_embedded_motion_videos_description": "Include videoclipurile încorporate în fotografiile în mișcare ca fișier separat", + "download_notfound": "Descărcare negăsită", + "download_paused": "Descărcarea a fost întreruptă", "download_settings": "Descărcați", "download_settings_description": "Gestionați setările legate de descărcarea resurselor", + "download_started": "Descărcarea a început", + "download_sucess": "Descărcare reușită", + "download_sucess_android": "Fișierul media a fost descărcat în DCIM/Immich", + "download_waiting_to_retry": "Se așteaptă o nouă încercare", "downloading": "Se descarcă", "downloading_asset_filename": "Se descarcă resursa {filename}", + "downloading_media": "Se descarcă fișierele media", "drop_files_to_upload": "Trageți fișierele aici pentru a le încărca", "duplicates": "Duplicate", "duplicates_description": "Rezolvați fiecare grup indicând care sunt duplicate, dacă există", @@ -781,6 +821,8 @@ "edit_avatar": "Editare avatar", "edit_date": "Editare dată", "edit_date_and_time": "Editare dată și oră", + "edit_description": "Editează descrierea", + "edit_description_prompt": "Vă rugăm să selectați o descriere nouă:", "edit_exclusion_pattern": "Editarea modelului de excludere", "edit_faces": "Editare fețe", "edit_import_path": "Editare cale de import", @@ -788,6 +830,7 @@ "edit_key": "Tastă de editare", "edit_link": "Editare link", "edit_location": "Editare locație", + "edit_location_action_prompt": "{count} locație(i) editată(e)", "edit_location_dialog_title": "Locație", "edit_name": "Editare nume", "edit_people": "Editare persoane", @@ -795,19 +838,31 @@ "edit_title": "Editare Titlu", "edit_user": "Editare utilizator", "edited": "Editat", + "editor": "Editor", "editor_close_without_save_prompt": "Schimbările nu vor fi salvate", "editor_close_without_save_title": "Închideți editorul?", "editor_crop_tool_h2_aspect_ratios": "Raporturi de aspect", "editor_crop_tool_h2_rotation": "Rotire", + "email": "Adresă de mail", + "email_notifications": "Notificări e-mail", + "empty_folder": "Acest dosar este gol", "empty_trash": "Goliți coșul de gunoi", "empty_trash_confirmation": "Sunteți sigur că doriți să goliți coșul de gunoi? Acest lucru va elimina definitiv din Immich toate resursele din coșul de gunoi.\nNu puteți anula această acțiune!", "enable": "Permite", + "enable_backup": "Activează backup", + "enable_biometric_auth_description": "Introduceți codul PIN pentru a activa autentificarea biometrică", "enabled": "Activat", "end_date": "Data de încheiere", - "enter_wifi_name": "Enter WiFi name", + "enqueued": "Pus în coadă", + "enter_wifi_name": "Introduceți numele rețelei Wi-Fi", + "enter_your_pin_code": "Introduceți codul PIN", + "enter_your_pin_code_subtitle": "Introduceți codul PIN pentru a accesa folderul blocat", "error": "Eroare", + "error_change_sort_album": "Nu s-a putut modifica ordinea de sortare a albumului", "error_delete_face": "Eroare la ștergerea feței din activ", "error_loading_image": "Eroare la încărcarea imaginii", + "error_saving_image": "Eroare: {error}", + "error_tag_face_bounding_box": "Eroare la etichetarea feței - nu se pot obține coordonatele casetei de delimitare", "error_title": "Eroare - ceva nu a mers", "errors": { "cannot_navigate_next_asset": "Nu se poate naviga către următoarea resursă", @@ -835,10 +890,12 @@ "failed_to_keep_this_delete_others": "Nu s-a putut păstra acest material respectiv nu s-au putut șterge celelalte materiale", "failed_to_load_asset": "Eșec la încărcarea resursei", "failed_to_load_assets": "Eșec la încărcarea resurselor", + "failed_to_load_notifications": "Nu s-au putut încărca notificările", "failed_to_load_people": "Eșec la încărcarea persoanelor", "failed_to_remove_product_key": "Eșec la eliminarea cheii de produs", "failed_to_stack_assets": "Eșec la combinarea resurselor", "failed_to_unstack_assets": "Eșec la desfășurarea resurselor", + "failed_to_update_notification_status": "Nu s-a putut actualiza starea notificării", "import_path_already_exists": "Această cale de import există deja.", "incorrect_email_or_password": "E-mail sau parolă incorect/ă", "paths_validation_failed": "{paths, plural, one {# cale} other {# căi}} nu a trecut validarea", @@ -855,6 +912,7 @@ "unable_to_archive_unarchive": "Nu se poate {archived, select, true {arhiva} other {dezarhiva}}", "unable_to_change_album_user_role": "Nu se poate schimba rolul utilizatorului de album", "unable_to_change_date": "Imposibil de schimbat data", + "unable_to_change_description": "Nu se poate schimba descrierea", "unable_to_change_favorite": "Nu se pot modifica favoritele pentru resursa", "unable_to_change_location": "Imposibil de schimbat locația", "unable_to_change_password": "Imposibil de schimbat parola", @@ -898,6 +956,7 @@ "unable_to_remove_partner": "Imposibil de eliminat partenerul", "unable_to_remove_reaction": "Nu se poate elimina reacția", "unable_to_reset_password": "Imposibil de resetat parola", + "unable_to_reset_pin_code": "Nu se poate reseta codul PIN", "unable_to_resolve_duplicate": "Nu se poate rezolva duplicatul", "unable_to_restore_assets": "Nu se pot restaura resursele", "unable_to_restore_trash": "Nu se poate restaura coșul de gunoi", @@ -930,11 +989,16 @@ "exif_bottom_sheet_details": "DETALII", "exif_bottom_sheet_location": "LOCAȚIE", "exif_bottom_sheet_people": "PERSOANE", + "exif_bottom_sheet_person_add_person": "Adăugați nume", + "exif_bottom_sheet_person_age_months": "Vârstă {months} luni", + "exif_bottom_sheet_person_age_year_months": "Vârstă 1 an, {months} luni", + "exif_bottom_sheet_person_age_years": "Vârstă {years}", "exit_slideshow": "Ieșire din Prezentare", "expand_all": "Extindeți-le pe toate", "experimental_settings_new_asset_list_subtitle": "Acțiune în desfășurare", "experimental_settings_new_asset_list_title": "Activează grila experimentală de fotografii", "experimental_settings_subtitle": "Folosește pe propria răspundere!", + "experimental_settings_title": "Experimental", "expire_after": "Expiră după", "expired": "Expirat", "expires_date": "Expiră la {date}", @@ -942,13 +1006,20 @@ "explorer": "Explorator", "export": "Exportare", "export_as_json": "Exportare ca JSON", + "export_database": "Exportați baza de date", + "export_database_description": "Exportați baza de date SQLite", "extension": "Extensie", "external": "Extern", "external_libraries": "Biblioteci Externe", - "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", + "external_network": "Rețea externă", + "external_network_sheet_info": "Când nu se află în rețeaua Wi-Fi preferată, aplicația se va conecta la server prin prima dintre adresele URL de mai jos pe care o poate accesa, începând de sus în jos", "face_unassigned": "Nealocat", + "failed": "Eșuat", + "failed_to_authenticate": "Autentificarea nu a reușit", "failed_to_load_assets": "Nu s-au încărcat activele", + "failed_to_load_folder": "Nu s-a putut încărca folderul", "favorite": "Favorit", + "favorite_action_prompt": "{count} adăugate la Favorite", "favorite_or_unfavorite_photo": "Fotografie preferată sau nepreferată", "favorites": "Favorite", "favorites_page_no_favorites": "Nu au fost găsite resurse favorite", @@ -959,25 +1030,41 @@ "file_name_or_extension": "Numele sau extensia fișierului", "filename": "Numele fișierului", "filetype": "Tipul fișierului", + "filter": "Filtre", "filter_people": "Filtrați persoanele", "filter_places": "Filtrează locurile", "find_them_fast": "Găsiți-le rapid prin căutare după nume", "fix_incorrect_match": "Remediați potrivirea incorectă", + "folder": "Dosar", + "folder_not_found": "Dosar negăsit", "folders": "Foldere", "folders_feature_description": "Răsfoire în conținutul folderului pentru fotografiile și videoclipurile din sistemul de fișiere", "forward": "Redirecționare", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Această funcție încarcă resurse externe de la Google pentru a funcționa.", + "general": "General", "get_help": "Obțineți Ajutor", + "get_wifiname_error": "Nu s-a putut obține numele rețelei Wi-Fi. Asigurați-vă că ați acordat permisiunile necesare și că sunteți conectat la o rețea Wi-Fi", "getting_started": "Noțiuni de Bază", "go_back": "Întoarcere", "go_to_folder": "Accesați folderul", "go_to_search": "Spre căutare", + "grant_permission": "Acordați permisiunea", "group_albums_by": "Grupați albume de...", "group_country": "Grupare după țară", "group_no": "Fără grupare", "group_owner": "Grupați după proprietar", "group_places_by": "Grupare locuri după...", "group_year": "Grupați după an", + "haptic_feedback_switch": "Activează feedback-ul haptic", + "haptic_feedback_title": "Feedback haptic", "has_quota": "Are spațiu de stocare", + "header_settings_add_header_tip": "Adăugați antet", + "header_settings_field_validator_msg": "Valoarea nu poate fi goală", + "header_settings_header_name_input": "Numele antetului", + "header_settings_header_value_input": "Valoarea antetului", + "headers_settings_tile_subtitle": "Definiți header-urile proxy pe care aplicația ar trebui să le trimită cu fiecare solicitare de rețea", + "headers_settings_tile_title": "Header-uri proxy personalizate", "hi_user": "Bună {name} ({email})", "hide_all_people": "Ascundeți toate persoanele", "hide_gallery": "Ascundeți galeria", @@ -997,10 +1084,16 @@ "home_page_favorite_err_local": "Resursele locale nu pot fi adăugate la favorite încă, omitere", "home_page_favorite_err_partner": "Momentan nu se pot adăuga fișierele partenerului la favorite, omitere", "home_page_first_time_notice": "Dacă este prima dată când utilizezi aplicația, te rugăm să te asiguri că alegi unul sau mai multe albume de backup, astfel încât cronologia să poată fi populată cu fotografiile și videoclipurile din aceste albume", + "home_page_locked_error_local": "Nu se pot muta resursele locale în folderul blocat, se omit", + "home_page_locked_error_partner": "Nu se pot muta materialele partenerului în folderul blocat, se omit.", "home_page_share_err_local": "Nu se pot distribui fișiere locale prin link, omitere", "home_page_upload_err_limit": "Se pot încărca maxim 30 de resurse odată, omitere", "host": "Gazdă", "hour": "Oră", + "id": "ID", + "idle": "Inactiv", + "ignore_icloud_photos": "Ignoră fotografiile din iCloud", + "ignore_icloud_photos_description": "Fotografiile stocate pe iCloud nu vor fi încărcate pe serverul Immich", "image": "Imagine", "image_alt_text_date": "{isVideo, select, true {Video} other {imagine}} preluată în {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {imagine}} preluată cu {person1} în {date}", @@ -1012,6 +1105,8 @@ "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {imagine}} preluată în {city}, {country} cu {person1} și {person2} în {date}", "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {imagine}} preluată în {city}, {country} cu {person1}, {person2}, și {person3} în {date}", "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {imagine}} preluată în {city}, {country} cu {person1}, {person2}, și {additionalCount, number} alții în {date}", + "image_saved_successfully": "Imaginea a fost salvată", + "image_viewer_page_state_provider_download_started": "Descărcare începută", "image_viewer_page_state_provider_download_success": "Descărcare cu succes", "image_viewer_page_state_provider_share_error": "Eroare distribuire", "immich_logo": "Logo Immich", @@ -1032,8 +1127,15 @@ "night_at_midnight": "În fiecare noapte la miezul nopții", "night_at_twoam": "În fiecare noapte la 2 dimineața" }, + "invalid_date": "Dată invalidă", + "invalid_date_format": "Format de dată invalid", "invite_people": "Invitați Persoane", "invite_to_album": "Invitați în album", + "ios_debug_info_fetch_ran_at": "Fetch a funcționat la {dateTime}", + "ios_debug_info_last_sync_at": "Ultima sincronizare {dateTime}", + "ios_debug_info_no_processes_queued": "Niciun proces în fundal pus în coadă", + "ios_debug_info_no_sync_yet": "Nicio sarcină de sincronizare în fundal nu a fost încă executată", + "ios_debug_info_processes_queued": "{count, plural, one {{count} proces în fundal pus în coadă} other {{count} procese în fundal puse în coadă}}", "ios_debug_info_processing_ran_at": "Procesarea a rulat {dateTime}", "items_count": "{count, plural, one {# element} other{# elemente}}", "jobs": "Sarcini", @@ -1043,6 +1145,9 @@ "kept_this_deleted_others": "S-a păstrat acest material și s-au șters {count, plural, one {# material} other {# materiale}}", "keyboard_shortcuts": "Comenzi rapide de tastatură", "language": "Limbă", + "language_no_results_subtitle": "Încercați să ajustați termenul de căutare", + "language_no_results_title": "Nu au fost găsite limbi", + "language_search_hint": "Căutați limbi...", "language_setting_description": "Selectați limba preferată", "last_seen": "Văzut ultima dată", "latest_version": "Ultima Versiune", @@ -1059,23 +1164,32 @@ "library_page_sort_created": "Data creării", "library_page_sort_last_modified": "Ultima dată modificat", "library_page_sort_title": "Titlu album", + "licenses": "Licențe", "light": "Lumină", "like_deleted": "Preferat șters", "link_motion_video": "Link video în mișcare", - "link_options": "Opțiuni de link", "link_to_oauth": "Link către OAuth", "linked_oauth_account": "Cont OAuth conectat", "list": "Listă", "loading": "Încărcare", "loading_search_results_failed": "Încărcarea rezultatelor căutării nu a reușit", - "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", + "local": "Local", + "local_asset_cast_failed": "Nu se poate converti un element care nu este încărcat pe server", + "local_assets": "Asset-uri locale", + "local_network": "Rețea locală", + "local_network_sheet_info": "Aplicația se va conecta la server prin intermediul acestei adrese URL atunci când utilizează rețeaua Wi-Fi specificată", + "location_permission": "Permisiunea de locație", + "location_permission_content": "Pentru a utiliza funcția de comutare automată, Immich are nevoie de permisiune pentru locația precisă, astfel încât să poată citi numele rețelei Wi-Fi curente", "location_picker_choose_on_map": "Alege pe hartă", "location_picker_latitude_error": "Introdu o latitudine validă", "location_picker_latitude_hint": "Introdu latitudinea aici", "location_picker_longitude_error": "Introdu o longitudine validă", "location_picker_longitude_hint": "Introdu longitudinea aici", + "lock": "Blocare", + "locked_folder": "Dosar blocat", "log_out": "Deconectare", "log_out_all_devices": "Deconectați-vă de la toate dispozitivele", + "logged_in_as": "Conectat ca {user}", "logged_out_all_devices": "S-au deconectat toate dispozitivele", "logged_out_device": "Dispozitiv deconectat", "login": "Conectare", @@ -1118,8 +1232,7 @@ "manage_your_devices": "Gestionați-vă dispozitivele conectate", "manage_your_oauth_connection": "Gestionați-vă conexiunea OAuth", "map": "Hartă", - "map_assets_in_bound": "{count} fotografie", - "map_assets_in_bounds": "{count} fotografii", + "map_assets_in_bounds": "{count, plural, one {# poză} other {# poze}}", "map_cannot_get_user_location": "Nu se poate obține locația utilizatorului", "map_location_dialog_yes": "Da", "map_location_picker_page_use_location": "Folosește această locație", @@ -1138,13 +1251,21 @@ "map_settings_date_range_option_years": "Ultimii {years} ani", "map_settings_dialog_title": "Setările hărții", "map_settings_include_show_archived": "Include resursele arhivate", + "map_settings_include_show_partners": "Includeți partenerii", "map_settings_only_show_favorites": "Arată doar favorite", "map_settings_theme_settings": "Stilul hărții", "map_zoom_to_see_photos": "Zoom out pentru a vedea fotografii", + "mark_all_as_read": "Marchează toate ca citite", + "mark_as_read": "Marchează ca citit", + "marked_all_as_read": "Marcate toate ca citite", "matches": "Corespunde", "media_type": "Tip media", "memories": "Amintiri", + "memories_all_caught_up": "Sunteți la zi", + "memories_check_back_tomorrow": "Reveniți mâine pentru mai multe amintiri", "memories_setting_description": "Administrați ce vedeți în amintiri", + "memories_start_over": "Începeți de la început", + "memories_swipe_to_close": "Glisează în sus pentru a închide", "memory": "Amintire", "memory_lane_title": "Banda Memoriei {title}", "menu": "Meniu", @@ -1155,9 +1276,19 @@ "merge_people_successfully": "Persoane îmbinate cu succes", "merged_people_count": "Imbinate {count, plural, one {# persoană} other {# persoane}}", "minimize": "Minimizare", + "minute": "Minute", "missing": "Lipsă", + "model": "Model", "month": "Lună", + "monthly_title_text_date_format": "MMMM y", "more": "Mai mult", + "move": "Mută", + "move_off_locked_folder": "Mutați din folderul blocat", + "move_to_lock_folder_action_prompt": "{count} adăugate în dosarul blocat", + "move_to_locked_folder": "Mută în dosarul blocat", + "move_to_locked_folder_confirmation": "Aceste fotografii și videoclipuri vor fi eliminate din toate albumele și vor putea fi vizualizate doar din dosarul blocat", + "moved_to_archive": "Au fost mutate {count, plural, one {# element} other {# elemente}} în arhivă", + "moved_to_library": "Au fost mutate {count, plural, one {# element} other {# elemente}} la bibliotecă", "moved_to_trash": "Mutat în coșul de gunoi", "multiselect_grid_edit_date_time_err_read_only": "Nu se poate edita data fișierului(lor) cu permisiuni doar pentru citire, omitere", "multiselect_grid_edit_gps_err_read_only": "Nu se poate edita locația fișierului(lor) cu permisiuni doar pentru citire, omitere", @@ -1165,11 +1296,15 @@ "my_albums": "Albumele mele", "name": "Nume", "name_or_nickname": "Nume sau poreclǎ", + "networking_settings": "Rețele", + "networking_subtitle": "Gestionați setările endpoint-ului serverului", "never": "Niciodată", "new_album": "Album Nou", "new_api_key": "Cheie API nouǎ", "new_password": "Parolă nouă", "new_person": "Persoanǎ nouǎ", + "new_pin_code": "Cod PIN nou", + "new_pin_code_subtitle": "Aceasta este prima dată când accesați folderul blocat. Creați un cod PIN pentru a accesa în siguranță această pagină", "new_user_created": "Utilizator nou creat", "new_version_available": "VERSIUNE NOUĂ DISPONIBILĂ", "newest_first": "Cel mai nou primul", @@ -1181,19 +1316,27 @@ "no_albums_yet": "Se pare că nu aveți încă niciun album.", "no_archived_assets_message": "Arhivați fotografii și videoclipuri pentru a le ascunde din vizualizarea fotografii", "no_assets_message": "CLICK PENTRU A ÎNCĂRCA PRIMA TA FOTOGRAFIE", + "no_assets_to_show": "Nicio resursă de afișat", + "no_cast_devices_found": "Nu s-au găsit dispozitive de difuzare", "no_duplicates_found": "Nu au fost găsite duplicate.", "no_exif_info_available": "Nu există informații exif disponibile", "no_explore_results_message": "Încarcați mai multe fotografii pentru a vă explora colecția.", "no_favorites_message": "Adăugați favorite pentru a găsi rapid cele mai bune fotografii și videoclipuri", "no_libraries_message": "Creați o bibliotecă externă pentru a vă vizualiza fotografiile și videoclipurile", + "no_locked_photos_message": "Fotografiile și videoclipurile din folderul blocat sunt ascunse și nu vor apărea atunci când răsfoiți sau căutați în bibliotecă.", "no_name": "Fără Nume", + "no_notifications": "Nicio notificare", + "no_people_found": "Nu au fost găsite persoane potrivite căutării", "no_places": "Nu există locuri", "no_results": "Fără rezultate", "no_results_description": "Încercați un sinonim sau un cuvânt cheie mai general", "no_shared_albums_message": "Creați un album pentru a partaja fotografii și videoclipuri cu persoanele din rețeaua dvs", + "no_uploads_in_progress": "Nicio încărcare în curs", "not_in_any_album": "Nu există în niciun album", + "not_selected": "Neselectat", "note_apply_storage_label_to_previously_uploaded assets": "Notă: Pentru a aplica eticheta de stocare la resursele încărcate anterior, rulați", "notes": "Note", + "nothing_here_yet": "Nimic aici încă", "notification_permission_dialog_content": "Pentru a activa notificările, mergi în Setări > Immich și selectează permite.", "notification_permission_list_tile_content": "Acordă permisiunea pentru a activa notificările.", "notification_permission_list_tile_enable_button": "Activează notificările", @@ -1201,13 +1344,20 @@ "notification_toggle_setting_description": "Activați notificările prin email", "notifications": "Notificări", "notifications_setting_description": "Gestionați notificările", + "oauth": "OAuth", "official_immich_resources": "Resurse Oficiale Immich", + "offline": "Offline", "ok": "Bine", "oldest_first": "Cel mai vechi mai întâi", + "on_this_device": "Pe acest dispozitiv", "onboarding": "Integrare", - "onboarding_privacy_description": "Următoarele caracteristici (opționale) se bazează pe servicii externe și pot fi dezactivate în orice moment din setările de administrare.", + "onboarding_locale_description": "Selectați limba preferată. Puteți schimba această opțiune ulterior în setări.", + "onboarding_privacy_description": "Următoarele caracteristici (opționale) se bazează pe servicii externe și pot fi dezactivate în orice moment din setări.", + "onboarding_server_welcome_description": "Hai să configurăm instanța cu câteva setări comune.", "onboarding_theme_description": "Alegeți o temă de culoare pentru exemplul dvs. Puteți modifica acest lucru mai târziu în setări.", + "onboarding_user_welcome_description": "Hai să începem!", "onboarding_welcome_user": "Bun venit, {user}", + "online": "Online", "only_favorites": "Doar favorite", "open": "Deschide", "open_in_map_view": "Deschideți în vizualizarea hărții", @@ -1216,8 +1366,10 @@ "options": "Opțiuni", "or": "sau", "organize_your_library": "Organizează-ți biblioteca", + "original": "original", "other": "Alte", "other_devices": "Alte dispozitive", + "other_entities": "Alte entități", "other_variables": "Alte variabile", "owned": "Deținut", "owner": "Proprietar", @@ -1225,6 +1377,8 @@ "partner_can_access": "{partner} poate accesa", "partner_can_access_assets": "Toate fotografiile și videoclipurile tale, cu excepția celor din arhivate și sterse", "partner_can_access_location": "Locația în care au fost făcute fotografiile dvs", + "partner_list_user_photos": "Fotografiile lui {user}", + "partner_list_view_all": "Vezi toate", "partner_page_empty_message": "Fotografiile tale nu sunt încă distribuite cu nici un partener.", "partner_page_no_more_users": "Nu mai sunt utilizatori de adăugat", "partner_page_partner_add_failed": "Eșuare adăugare partener", @@ -1259,6 +1413,8 @@ "permanently_delete_assets_prompt": "Sigur doriți să ștergeți definitiv {count, plural, one {această resursă?} other {aceste # resurse?}} Acest lucru va elimina și {count, plural, one {din ea} other {din ele}} album(e).", "permanently_deleted_asset": "Resursă ștearsă definitiv", "permanently_deleted_assets_count": "S-au șters definitiv {count, plural, one {# resursă} other {# resurse}}", + "permission": "Permisiune", + "permission_empty": "Permisiunea dvs. nu trebuie să fie goală", "permission_onboarding_back": "Înapoi", "permission_onboarding_continue_anyway": "Continuă oricum", "permission_onboarding_get_started": "Începe", @@ -1276,6 +1432,10 @@ "photos_count": "{count, plural, one {{count, number} imagine} other{{count, number} imagini}}", "photos_from_previous_years": "Fotografii din anii anteriori", "pick_a_location": "Alegeți o locație", + "pin_code_changed_successfully": "Codul PIN a fost modificat cu succes", + "pin_code_reset_successfully": "Codul PIN a fost resetat cu succes", + "pin_code_setup_successfully": "Configurarea cu succes a unui cod PIN", + "pin_verification": "Verificarea codului PIN", "place": "Loc", "places": "Locații", "places_count": "{count, plural, one {{count, number} Loc} other {{count, number} Locuri}}", @@ -1283,10 +1443,16 @@ "play_memories": "Redare amintiri", "play_motion_photo": "Redare Fotografie în Mișcare", "play_or_pause_video": "Redați sau întrerupeți videoclipul", + "please_auth_to_access": "Vă rugăm să vă autentificați pentru a accesa", + "port": "Port", + "preferences_settings_subtitle": "Gestionați preferințele aplicației", + "preferences_settings_title": "Preferințe", "preset": "Presetat", "preview": "Previzualizare", "previous": "Anterior", "previous_memory": "Memoria anterioară", + "previous_or_next_day": "Zi înainte/înapoi", + "previous_or_next_month": "Lună înainte/înapoi", "previous_or_next_photo": "Fotografie înainte/înapoi", "previous_or_next_year": "An înainte/înapoi", "primary": "Primar", @@ -1323,8 +1489,9 @@ "purchase_lifetime_description": "Achiziție pe viață", "purchase_option_title": "OPȚIUNI DE CUMPĂRARE", "purchase_panel_info_1": "Dezvoltarea programului Immich necesită mult timp și efort și avem ingineri cu normă întreagă care lucrează la el pentru a-l face cât se poate de bun. Misiunea noastră este ca software-ul open-source și practicile de afaceri etice să devină o sursă de venit durabilă pentru dezvoltatori și să se creeze un ecosistem care să respecte confidențialitatea utilizatorilor, cu alternative reale la serviciile cloud care exploatează utilizatorii.", - "purchase_panel_info_2": "Deoarece ne-am angajat să nu adăugăm planuri de plată, această achiziție nu vă va oferi nicio funcție suplimentară în Immich. Ne bazăm pe utilizatori ca dvs. pentru a sprijini dezvoltarea continuă a lui Immich.", + "purchase_panel_info_2": "Deoarece ne-am angajat să nu adăugăm planuri de plată, această achiziție nu vă va oferi nicio funcție suplimentară în Immich. Ne bazăm pe utilizatori ca dvs. pentru a sprijini dezvoltarea continuă a Immich.", "purchase_panel_title": "Susțineți proiectul", + "purchase_per_server": "Per server", "purchase_per_user": "Per utilizator", "purchase_remove_product_key": "Eliminați Cheia Produsului", "purchase_remove_product_key_prompt": "Sigur doriți să eliminați cheia de produs?", @@ -1334,6 +1501,7 @@ "purchase_server_description_2": "Statutul de suporter", "purchase_server_title": "Server", "purchase_settings_server_activated": "Cheia de produs a serverului este gestionată de administrator", + "queue_status": "Se pun în coadă {count}/{total}", "rating": "Evaluare cu stele", "rating_clear": "Anulați evaluarea", "rating_count": "{count, plural, one {# stea} other {# stele}}", @@ -1344,10 +1512,13 @@ "reassigned_assets_to_existing_person": "Re-alocat {count, plural, one {# resursă} other {# resurse}} to {name, select, null {unei persoane existente} other {{name}}}", "reassigned_assets_to_new_person": "Re-alocat {count, plural, one {# resursă} other {# resurse}} unei noi persoane", "reassing_hint": "Atribuiți resursele selectate unei persoane existente", + "recent": "Recent", "recent-albums": "Albume recente", "recent_searches": "Căutări recente", "recently_added": "Adăugate recent", "recently_added_page_title": "Adăugate recent", + "recently_taken": "Recent realizate", + "recently_taken_page_title": "Recent realizate", "refresh": "Reîmprospătare", "refresh_encoded_videos": "Actualizează videoclipurile encodate", "refresh_faces": "Reîmprospătați fețele", @@ -1359,6 +1530,8 @@ "refreshing_faces": "Se reîmprospătează fețele", "refreshing_metadata": "Se reîmprospătează metadatele", "regenerating_thumbnails": "Se regenerează miniaturile", + "remote": "De la distanță", + "remote_assets": "Elemente la distanță", "remove": "Eliminați", "remove_assets_album_confirmation": "Sigur doriți să eliminați {count, plural, one {# resursă} other {# resurse}} din album?", "remove_assets_shared_link_confirmation": "Sigur doriți să eliminați {count, plural, one {# resursă} other {# resurse}} din acest link comun?", @@ -1366,7 +1539,9 @@ "remove_custom_date_range": "Eliminați intervalul de date personalizat", "remove_deleted_assets": "Eliminați Resursele Șterse", "remove_from_album": "Ștergeți din album", + "remove_from_album_action_prompt": "{count} șters(e) din album", "remove_from_favorites": "Eliminați din favorite", + "remove_from_lock_folder_action_prompt": "{count} șters(e) din dosarul blocat", "remove_from_locked_folder": "Eliminați din folderul securizat", "remove_from_locked_folder_confirmation": "Sunteți sigur că doriți să mutați aceste poze și videoclipuri afară din folderul securizat? Vor deveni vizibile în biblioteca dvs.", "remove_from_shared_link": "Eliminați din linkul partajat", @@ -1393,19 +1568,27 @@ "reset": "Resetare", "reset_password": "Resetare parolă", "reset_people_visibility": "Resetați vizibilitatea persoanelor", + "reset_pin_code": "Resetare cod PIN", + "reset_sqlite": "Resetare bază de date SQLite", + "reset_sqlite_confirmation": "Sigur doriți să resetați baza de date SQLite? Va trebui să vă deconectați și să vă conectați din nou pentru a resincroniza datele", + "reset_sqlite_success": "Resetarea cu succes a bazei de date SQLite", "reset_to_default": "Resetați la valoarea implicită", "resolve_duplicates": "Rezolvați duplicatele", "resolved_all_duplicates": "Rezolvați toate duplicatele", "restore": "Restaurați", "restore_all": "Restaurați toate", + "restore_trash_action_prompt": "{count} restaurate din gunoi", "restore_user": "Restabiliți utilizatorul", "restored_asset": "Resursă restaurată", "resume": "Reluare", "retry_upload": "Reîncercați încărcarea", "review_duplicates": "Examinați duplicatele", "role": "Rol", + "role_editor": "Editor", "role_viewer": "Vizualizator", + "running": "Rulează", "save": "Salvați", + "save_to_gallery": "Salvați în galerie", "saved_api_key": "Cheie API salvată", "saved_profile": "Profil salvat", "saved_settings": "Setări salvate", @@ -1426,16 +1609,32 @@ "search_camera_model": "Se caută modelul camerei...", "search_city": "Se caută orașul...", "search_country": "Se caută țara...", + "search_filter_apply": "Aplicați filtrul", + "search_filter_camera_title": "Selectați tipul de cameră", + "search_filter_date": "Dată", + "search_filter_date_interval": "{start} la {end}", + "search_filter_date_title": "Selectați un interval de dată", + "search_filter_display_option_not_in_album": "Nu este în album", + "search_filter_display_options": "Opțiuni de afișare", + "search_filter_filename": "Căutare după numele fișierului", + "search_filter_location": "Locaţie", + "search_filter_location_title": "Selectați locația", + "search_filter_media_type": "Tip media", + "search_filter_media_type_title": "Selectați tipul media", + "search_filter_people_title": "Selectați persoane", "search_for": "Căutare după", "search_for_existing_person": "Se caută o persoană existentă", + "search_no_more_result": "Nu mai există rezultate", "search_no_people": "Fără persoane", "search_no_people_named": "Nicio persoană numită \"{name}\"", + "search_no_result": "Nu s-au găsit rezultate, încercați un alt termen sau o altă combinație de termeni de căutare", "search_options": "Opțiuni de căutare", "search_page_categories": "Categorii", "search_page_motion_photos": "Fotografii în mișcare", "search_page_no_objects": "Nu sunt informații disponibile despre obiecte", "search_page_no_places": "Nici o informație disponibilă despre locuri", "search_page_screenshots": "Capturi de ecran", + "search_page_search_photos_videos": "Caută fotografiile și videoclipurile tale", "search_page_selfies": "Selfie-uri", "search_page_things": "Obiecte", "search_page_view_all_button": "Vezi toate", @@ -1460,6 +1659,7 @@ "select_album_cover": "Selectați coperta albumului", "select_all": "Selectați tot", "select_all_duplicates": "Selectați toate duplicatele", + "select_all_in": "Selectați tot în {group}", "select_avatar_color": "Selectați culoarea avatarului", "select_face": "Selectați fața", "select_featured_photo": "Selectați fotografia recomandată", @@ -1467,6 +1667,7 @@ "select_keep_all": "Selectați tot pentru păstrare", "select_library_owner": "Selectați proprietarul bibliotecii", "select_new_face": "Selectați o nouǎ fațǎ", + "select_person_to_tag": "Selectați o persoană pentru a o eticheta", "select_photos": "Selectați fotografii", "select_trash_all": "Selectați tot pentru ștergere", "select_user_for_sharing_page_err_album": "Creare album eșuată", @@ -1474,8 +1675,12 @@ "selected_count": "{count, plural, other {# selectat}}", "send_message": "Trimiteți mesaj", "send_welcome_email": "Trimiteți email de bun venit", + "server_endpoint": "Endpoint server", "server_info_box_app_version": "Versiune Aplicatie", "server_info_box_server_url": "URL-ul server-ului", + "server_offline": "Serverul este offline", + "server_online": "Server online", + "server_privacy": "Confidențialitatea serverului", "server_stats": "Statistici Server", "server_version": "Versiune Server", "set": "Setați", @@ -1485,11 +1690,15 @@ "set_date_of_birth": "Setați data nașterii", "set_profile_picture": "Setați poza de profil", "set_slideshow_to_fullscreen": "Setați Prezentare de Diapozitive la ecran complet", + "set_stack_primary_asset": "Setați ca element principal", "setting_image_viewer_help": "Vizualizatorul detaliilor încarcă mai întâi miniatura mică, apoi încarcă previzualizarea de dimensiune medie (dacă este activată), în cele din urmă încarcă originalul (dacă este activat).", "setting_image_viewer_original_subtitle": "Activează pentru a încărca imaginea originală în rezoluție completă (mare!). Dezactivează pentru a reduce consumul de date (atat pe rețea, cât și în memoria cache a dispozitivului).", "setting_image_viewer_original_title": "Încarcă fotografia originală", "setting_image_viewer_preview_subtitle": "Activează pentru a încărca o imagine în rezoluție medie. Dezactivează pentru a încărca direct imaginea originală sau doar a utiliza miniatura.", "setting_image_viewer_preview_title": "Încarcă imaginea de previzualizare", + "setting_image_viewer_title": "Imagini", + "setting_languages_apply": "Aplică", + "setting_languages_subtitle": "Schimbați limba aplicației", "setting_notifications_notify_failures_grace_period": "Notificare eșuări backup în fundal: {duration}", "setting_notifications_notify_hours": "{count} ore", "setting_notifications_notify_immediately": "imediat", @@ -1501,12 +1710,19 @@ "setting_notifications_subtitle": "Ajustează preferințele pentru notificări", "setting_notifications_total_progress_subtitle": "Progresul general al încărcării (resurse finalizate/total)", "setting_notifications_total_progress_title": "Afișează progresul total al copiilor de siguranță în fundal", + "setting_video_viewer_looping_title": "Buclă", + "setting_video_viewer_original_video_subtitle": "Când redați în flux un videoclip de pe server, redați originalul chiar și atunci când este disponibilă o transcodare. Poate duce la încărcare temporară. Videoclipurile disponibile local sunt redate la calitatea originală indiferent de această setare.", + "setting_video_viewer_original_video_title": "Forțează videoclipul original", "settings": "Setări", "settings_require_restart": "Te rugăm să repornești Immich pentru a aplica această setare", "settings_saved": "Setările au fost salvate", + "setup_pin_code": "Configurați un cod PIN", "share": "Distribuiți", + "share_action_prompt": "{count} elemente partajate", "share_add_photos": "Adaugă fotografii", + "share_assets_selected": "{count} selectat(e)", "share_dialog_preparing": "Se pregătește...", + "share_link": "Partajați linkul", "shared": "Partajat", "shared_album_activities_input_disable": "Cometariile sunt dezactivate", "shared_album_activity_remove_content": "Dorești să ștergi această activitate?", @@ -1519,6 +1735,7 @@ "shared_by_user": "Partajat de {user}", "shared_by_you": "Partajat de tine", "shared_from_partner": "Fotografii de la {partner}", + "shared_intent_upload_button_progress_text": "{current} / {total} încărcate", "shared_link_app_bar_title": "Link-uri distribuite", "shared_link_clipboard_copied_massage": "Copiat în clipboard", "shared_link_clipboard_text": "Link: {link}\nParolă: {password}", @@ -1530,6 +1747,8 @@ "shared_link_edit_expire_after_option_hours": "{count} ore", "shared_link_edit_expire_after_option_minute": "1 minut", "shared_link_edit_expire_after_option_minutes": "{count} minute", + "shared_link_edit_expire_after_option_months": "{count} luni", + "shared_link_edit_expire_after_option_year": "{count} an", "shared_link_edit_password_hint": "Introdu parola de distribuire", "shared_link_edit_submit_button": "Actualizează link", "shared_link_error_server_url_fetch": "Nu se poate accesa URL-ul serverului", @@ -1542,11 +1761,14 @@ "shared_link_expires_never": "Expiră ∞", "shared_link_expires_second": "Expiră în {count} secunde", "shared_link_expires_seconds": "Expiră în {count} secunde", + "shared_link_individual_shared": "Partajat individual", + "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Administrează link-urile distribuite", "shared_link_options": "Opțiuni de link partajat", "shared_links": "Link-uri distribuite", "shared_links_description": "Partajare imagini și clipuri printr-un link", "shared_photos_and_videos_count": "{assetCount, plural, other {# fotografii și videoclipuri partajate.}}", + "shared_with_me": "Distribuit cu mine", "shared_with_partner": "Partajat cu {partner}", "sharing": "Distribuire", "sharing_enter_password": "Vă rugăm să introduceți parola pentru a vizualiza această pagină.", @@ -1598,6 +1820,7 @@ "sort_title": "Titlu", "source": "Sursă", "stack": "Stivă", + "stack_action_prompt": "{count} suprapuse", "stack_duplicates": "Duplicate stive", "stack_select_one_photo": "Selectați o fotografie principală pentru stivă", "stack_selected_photos": "Fotografie stivă selectată", @@ -1607,14 +1830,17 @@ "start_date": "Data de începere", "state": "Situaţie", "status": "Stare", + "stop_casting": "Opriți difuzarea", "stop_motion_photo": "Opriți Fotografia in Mișcare", "stop_photo_sharing": "Încetați distribuirea fotografiilor?", "stop_photo_sharing_description": "{partner} nu va mai putea accesa fotografiile dvs.", "stop_sharing_photos_with_user": "Nu mai partajați fotografiile cu acest utilizator", "storage": "Spațiu de stocare", "storage_label": "Eticheta de depozitare", + "storage_quota": "Cotă de stocare", "storage_usage": "{used} din {available} utilizați", "submit": "Trimiteți", + "success": "Succes", "suggestions": "Sugestii", "sunrise_on_the_beach": "Rǎsǎrit pe plajǎ", "support": "Suport tehnic", @@ -1622,6 +1848,11 @@ "support_third_party_description": "Instalarea dvs. Immich a fost pregătită de o terță parte. Problemele pe care le întâmpinați pot fi cauzate de acel pachet, așa că vă rugăm să ridicați probleme cu ei în primă instanță utilizând linkurile de mai jos.", "swap_merge_direction": "Schimbați direcția de îmbinare", "sync": "Sincronizare", + "sync_albums": "Sincronizează albumele", + "sync_albums_manual_subtitle": "Sincronizează toate videoclipurile și fotografiile încărcate cu albumele de rezervă selectate", + "sync_local": "Sincronizare locală", + "sync_remote": "Sincronizare la distanță", + "sync_upload_album_setting_subtitle": "Creează și încarcă fotografiile și videoclipurile tale în albumele selectate de pe Immich", "tag": "Etichetă", "tag_assets": "Eticheta resurselor", "tag_created": "Etichetă creată: {tag}", @@ -1631,14 +1862,20 @@ "tag_updated": "Etichetă actualizată: {tag}", "tagged_assets": "Etichetat {count, plural, one {# resursă} other {# resurse}}", "tags": "Etichete", + "tap_to_run_job": "Atingeți pentru a rula job-ul", "template": "Șablon", "theme": "Temă", "theme_selection": "Selectarea temei", "theme_selection_description": "Setați automat tema la mod luminos sau întunecată, în funcție de preferințele de sistem ale browserului dvs", "theme_setting_asset_list_storage_indicator_title": "Arată indicator stocare", "theme_setting_asset_list_tiles_per_row_title": "Număr de resurse pe rând ({count})", + "theme_setting_colorful_interface_subtitle": "Aplicați culoarea primară pe suprafețele de fundal.", + "theme_setting_colorful_interface_title": "Interfață colorată", "theme_setting_image_viewer_quality_subtitle": "Ajustează calitatea detaliilor vizualizatorului de imagine", "theme_setting_image_viewer_quality_title": "Calitate vizualizator de imagine", + "theme_setting_primary_color_subtitle": "Alege o culoare pentru acțiunile și accentele principale.", + "theme_setting_primary_color_title": "Culoare primară", + "theme_setting_system_primary_color_title": "Folosește culoarea sistemului", "theme_setting_system_theme_switch": "Automat (La fel ca setarea sistemului)", "theme_setting_theme_subtitle": "Alege tema aplicației", "theme_setting_three_stage_loading_subtitle": "Încărcarea în trei etape are putea crește performanța încărcării dar generează un volum semnificativ mai mare de trafic pe rețea", @@ -1655,11 +1892,14 @@ "to_parent": "Du-te la părinte", "to_trash": "Coș de gunoi", "toggle_settings": "Activați setările", + "total": "Total", "total_usage": "Utilizare totală", "trash": "Coș de gunoi", + "trash_action_prompt": "{count} mutat(e) la coșul de gunoi", "trash_all": "Ștergeți Tot", "trash_count": "Ștergeți {count, number}", "trash_delete_asset": "Coș de gunoi/Ștergeți resursa", + "trash_emptied": "Coș de gunoi golit", "trash_no_results_message": "Fotografiile și videoclipurile mutate în coșul de gunoi vor apărea aici.", "trash_page_delete_all": "Șterge tot", "trash_page_empty_trash_dialog_content": "Dorești să golești coșul? Aceste fișiere vor fi șterse permanent din Immich", @@ -1670,9 +1910,14 @@ "trash_page_title": "Coș ({count})", "trashed_items_will_be_permanently_deleted_after": "Elementele din coșul de gunoi vor fi șterse definitiv după {days, plural, one {# zi} other {# zile}}.", "type": "Tip", + "unable_to_change_pin_code": "Nu se poate schimba codul PIN", + "unable_to_setup_pin_code": "Nu se poate configura codul PIN", "unarchive": "Dezarhivați", + "unarchive_action_prompt": "{count} șters(e) din Arhivă", "unarchived_count": "{count, plural, other {dezarhivat #}}", + "undo": "Anulează", "unfavorite": "Ștergeți din favorite", + "unfavorite_action_prompt": "{count} șters(e) de la Favorite", "unhide_person": "Dezvăluie persoana", "unknown": "Necunoscut", "unknown_country": "Țară necunoscută", @@ -1688,26 +1933,43 @@ "unsaved_change": "Modificare nesalvată", "unselect_all": "Deselectați toate", "unselect_all_duplicates": "Deselectați toate duplicatele", + "unselect_all_in": "Deselectați toate din {group}", "unstack": "Dezasamblați", + "unstack_action_prompt": "{count} unstacked", "unstacked_assets_count": "Nestivuit {count, plural, one {# resursă} other {# resurse}}", + "untagged": "Neetichetat", "up_next": "Mai departe", + "updated_at": "Actualizat", "updated_password": "Parolă actualizată", "upload": "Încărcați", + "upload_action_prompt": "{count} în coadă pentru încărcare", "upload_concurrency": "Încărcați simultan", + "upload_details": "Detalii încărcare", "upload_dialog_info": "Vrei să backup resursele selectate pe server?", "upload_dialog_title": "Încarcă resursă", "upload_errors": "Încărcare finalizată cu {count, plural, one {# eroare} other {# erori}}, reîmprospătați pagina pentru a reîncărca noile resurse.", + "upload_finished": "Încărcarea s-a finalizat", "upload_progress": "Rămas {remaining, number} - Procesat {processed, number}/{total, number}", "upload_skipped_duplicates": "Sărit {count, plural, one {# duplicat resursă} other {# duplicate resurse}}", "upload_status_duplicates": "Duplicate", "upload_status_errors": "Erori", "upload_status_uploaded": "Încărcat", "upload_success": "Încărcare reușită, reîmprospătați pagina pentru a vedea resursele noi încărcate.", + "upload_to_immich": "Încărcați pe Immich ({count})", + "uploading": "Se încarcă", + "uploading_media": "Se încarcă fișierele media", + "url": "URL", "usage": "Utilizare", + "use_biometric": "Folosește biometrice", + "use_current_connection": "folosește conexiunea curentă", "use_custom_date_range": "Utilizați în schimb un interval de date personalizat", "user": "Utilizator", + "user_has_been_deleted": "Acest utilizator a fost șters.", "user_id": "ID utilizator", "user_liked": "{user} a apreciat {type, select, photo {această imagine} video {acest video} asset {această resursă} other {it}}", + "user_pin_code_settings": "Cod PIN", + "user_pin_code_settings_description": "Gestionați-vă codul PIN", + "user_privacy": "Confidențialitatea utilizatorilor", "user_purchase_settings": "Cumpărare", "user_purchase_settings_description": "Gestionați-vă achiziția", "user_role_set": "Setați {user} ca {role}", @@ -1716,8 +1978,10 @@ "user_usage_stats_description": "Vedeți statisticile de utilizare a contului", "username": "Nume de utilizator", "users": "Utilizatori", + "users_added_to_album_count": "{count, plural, one {# utilizator a fost adăugat} other {# utilizatori au fost adăugați}} în album", "utilities": "Utilitǎți", "validate": "Validați", + "validate_endpoint_error": "Vă rugăm să introduceți o adresă URL validă", "variables": "Variabile", "version": "Versiune", "version_announcement_closing": "Prietenul tǎu, Alex", @@ -1733,6 +1997,7 @@ "view_album": "Vizualizați Album", "view_all": "Vizualizați Tot", "view_all_users": "Vizulizați toți utilizatorii", + "view_details": "Vedeți detaliile", "view_in_timeline": "Vizualizați în cronologie", "view_link": "Vezi link", "view_links": "Vizualizați scurtǎturi", diff --git a/i18n/ru.json b/i18n/ru.json index c0a01b0be2..921666fb54 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -14,6 +14,7 @@ "add_a_location": "Добавить местоположение", "add_a_name": "Добавить имя", "add_a_title": "Добавить название", + "add_birthday": "Указать дату рождения", "add_endpoint": "Добавить адрес", "add_exclusion_pattern": "Добавить шаблон исключения", "add_import_path": "Добавить путь импорта", @@ -44,6 +45,13 @@ "backup_database": "Создать резервную копию базы данных", "backup_database_enable_description": "Включить создание дампов базы данных", "backup_keep_last_amount": "Количество хранимых резервных копий базы данных", + "backup_onboarding_1_description": "хранение дополнительной внешней копии в облаке или другом физическом месте.", + "backup_onboarding_2_description": "хранение основных файлов и их локальной копии на двух разных типах носителей.", + "backup_onboarding_3_description": "создание трёх копий данных, включая исходные файлы. 2 локальных копии и 1 внешнюю.", + "backup_onboarding_description": "Для надёжной защиты рекомендуется использовать стратегию резервирования данных 3-2-1. Делайте копии как загруженных фотографий и видео, так и базы данных Immich.", + "backup_onboarding_footer": "Дополнительная информация по резервному копированию Immich доступна в документации.", + "backup_onboarding_parts_title": "Стратегия 3-2-1 подразумевает:", + "backup_onboarding_title": "Резервное копирование", "backup_settings": "Настройки резервного копирования базы данных", "backup_settings_description": "Настройки создания резервных копий базы данных.", "cleared_jobs": "Очищены задачи для: {job}", @@ -397,6 +405,7 @@ "album_cover_updated": "Обложка альбома обновлена", "album_delete_confirmation": "Вы уверены, что хотите удалить альбом {album}?", "album_delete_confirmation_description": "Если альбом был общим, другие пользователи больше не смогут получить к нему доступ.", + "album_deleted": "Альбом удалён", "album_info_card_backup_album_excluded": "ИСКЛЮЧЕН", "album_info_card_backup_album_included": "ВКЛЮЧЕН", "album_info_updated": "Информация об альбоме обновлена", @@ -510,6 +519,7 @@ "back_close_deselect": "Назад, закрыть или отменить выбор", "background_location_permission": "Доступ к местоположению в фоне", "background_location_permission_content": "Чтобы считывать имя Wi-Fi сети в фоне, приложению *всегда* необходим доступ к точному местоположению устройства", + "backup": "Резервное копирование", "backup_album_selection_page_albums_device": "Альбомы на устройстве ({count})", "backup_album_selection_page_albums_tap": "Нажмите, чтобы включить, дважды, чтобы исключить", "backup_album_selection_page_assets_scatter": "Ваши изображения и видео могут находиться в разных альбомах. Вы можете выбрать, какие альбомы включить, а какие исключить из резервного копирования.", @@ -582,7 +592,7 @@ "birthdate_saved": "Дата рождения успешно сохранена", "birthdate_set_description": "Дата рождения используется для определения возраста человека на момент фотографии.", "blurred_background": "Размытый фон", - "bugs_and_feature_requests": "Ошибки и предложения", + "bugs_and_feature_requests": "Ошибки и запросы", "build": "Сборка", "build_image": "Версия сборки", "bulk_delete_duplicates_confirmation": "Вы уверены, что хотите удалить {count, plural, one {# дублирующийся объект} many {# дублирующихся объектов} other {# дублирующихся объекта}}? Будет сохранён самый большой файл в каждой группе, а его дубликаты навсегда удалены. Это действие нельзя отменить!", @@ -723,6 +733,7 @@ "current_server_address": "Текущий адрес сервера", "custom_locale": "Пользовательский регион", "custom_locale_description": "Форматирование дат и чисел в зависимости от языка и региона", + "custom_url": "Свой URL", "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Тёмная", @@ -742,7 +753,8 @@ "default_locale": "Дата и время по умолчанию", "default_locale_description": "Использовать формат даты и времени в соответствии с языковым стандартом вашего браузера", "delete": "Удалить", - "delete_action_prompt": "{count} удалено навсегда", + "delete_action_confirmation_message": "Вы действительно хотите удалить этот объект? Это действие переместит объект в корзину сервера и предложит удалить его локально.", + "delete_action_prompt": "{count} удалено", "delete_album": "Удалить альбом", "delete_api_key_prompt": "Вы действительно хотите удалить этот API ключ?", "delete_dialog_alert": "Эти элементы будут безвозвратно удалены с сервера, а также с вашего устройства", @@ -760,6 +772,8 @@ "delete_local_dialog_ok_backed_up_only": "Удалить только резервные копии", "delete_local_dialog_ok_force": "Все равно удалить", "delete_others": "Удалить остальные", + "delete_permanently": "Удалить навсегда", + "delete_permanently_action_prompt": "{count} удалено навсегда", "delete_shared_link": "Удалить публичную ссылку", "delete_shared_link_dialog_title": "Удалить публичную ссылку", "delete_tag": "Удалить тег", @@ -815,6 +829,7 @@ "edit": "Редактировать", "edit_album": "Редактировать альбом", "edit_avatar": "Изменить аватар", + "edit_birthday": "Изменить дату рождения", "edit_date": "редактировать дату", "edit_date_and_time": "редактировать дату и время", "edit_description": "Изменить описание", @@ -982,6 +997,7 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Добавить описание...", + "exif_bottom_sheet_description_error": "Не удалось обновить описание", "exif_bottom_sheet_details": "ПОДРОБНОСТИ", "exif_bottom_sheet_location": "МЕСТО", "exif_bottom_sheet_people": "ЛЮДИ", @@ -1002,6 +1018,8 @@ "explorer": "Проводник", "export": "Экспортировать", "export_as_json": "Экспорт в JSON", + "export_database": "Экспорт базы данных", + "export_database_description": "Экспорт базы данных SQLite", "extension": "Расширение", "external": "Внешний", "external_libraries": "Внешние библиотеки", @@ -1146,6 +1164,7 @@ "language_no_results_title": "Языков не найдено", "language_search_hint": "Поиск языков...", "language_setting_description": "Выберите предпочитаемый вами язык", + "large_files": "Файлы наибольшего размера", "last_seen": "Последний доступ", "latest_version": "Последняя версия", "latitude": "Широта", @@ -1165,7 +1184,6 @@ "light": "Светлая", "like_deleted": "Лайк удален", "link_motion_video": "Ссылка на движущееся видео", - "link_options": "Настройки ссылки", "link_to_oauth": "Присоединение к OAuth", "linked_oauth_account": "Присоединённый аккаунт OAuth", "list": "Список", @@ -1230,8 +1248,7 @@ "manage_your_devices": "Управление устройствами, на которых вы входили в свой аккаунт", "manage_your_oauth_connection": "Настройки подключённого OAuth", "map": "Карта", - "map_assets_in_bound": "{count} фото", - "map_assets_in_bounds": "{count} фото", + "map_assets_in_bounds": "{count, plural, one {# фото} other {# фото}}", "map_cannot_get_user_location": "Невозможно получить местоположение пользователя", "map_location_dialog_yes": "Да", "map_location_picker_page_use_location": "Это местоположение", @@ -1295,6 +1312,8 @@ "my_albums": "Мои альбомы", "name": "Имя", "name_or_nickname": "Имя или ник", + "network_requirement_photos_upload": "Использовать мобильный интернет для загрузки фото", + "network_requirement_videos_upload": "Использовать мобильный интернет для загрузки видео", "networking_settings": "Сеть", "networking_subtitle": "Настройка подключения к серверу", "never": "никогда", @@ -1565,7 +1584,7 @@ "require_user_to_change_password_on_first_login": "Требовать у пользователя сменить пароль при первом входе", "rescan": "Повторное сканирование", "reset": "Сброс", - "reset_password": "Сброс пароля", + "reset_password": "Сбросить пароль", "reset_people_visibility": "Восстановить видимость людей", "reset_pin_code": "Сбросить PIN-код", "reset_sqlite": "Очистить базу данных SQLite", @@ -1581,7 +1600,8 @@ "restored_asset": "Восстановленный объект", "resume": "Продолжить", "retry_upload": "Повторить загрузку", - "review_duplicates": "Разобрать дубликаты", + "review_duplicates": "Разбор дубликатов", + "review_large_files": "Обзор больших файлов", "role": "Роль", "role_editor": "Редактор", "role_viewer": "Зритель", @@ -1662,7 +1682,7 @@ "select_avatar_color": "Выберите цвет аватара", "select_face": "Выбрать лицо", "select_featured_photo": "Выбрать избранное фото", - "select_from_computer": "Выберите с компьютера", + "select_from_computer": "Выбрать с компьютера", "select_keep_all": "Выбрать все для сохранения", "select_library_owner": "Выберите владельца библиотеки", "select_new_face": "Выбрать другое лицо", @@ -1739,6 +1759,7 @@ "shared_link_clipboard_copied_massage": "Скопировано в буфер обмена", "shared_link_clipboard_text": "Ссылка: {link}\nПароль: {password}", "shared_link_create_error": "Ошибка при создании публичной ссылки", + "shared_link_custom_url_description": "Доступ к этой общей ссылке с помощью заданного пользователем URL-адреса", "shared_link_edit_description_hint": "Введите описание публичного доступа", "shared_link_edit_expire_after_option_day": "1 день", "shared_link_edit_expire_after_option_days": "{count} дней", @@ -1764,6 +1785,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Управление публичными ссылками", "shared_link_options": "Параметры публичных ссылок", + "shared_link_password_description": "Требовать пароль для доступа к этой общей ссылке", "shared_links": "Публичные ссылки", "shared_links_description": "Делитесь фотографиями и видео по ссылке", "shared_photos_and_videos_count": "{assetCount, plural, other {# фото и видео.}}", @@ -1941,11 +1963,13 @@ "updated_at": "Обновлён", "updated_password": "Пароль изменён", "upload": "Загрузить", + "upload_action_prompt": "{count} ожидают загрузки", "upload_concurrency": "Параллельность загрузки", "upload_details": "Подробности загрузки", "upload_dialog_info": "Хотите загрузить выбранные объекты на сервер?", "upload_dialog_title": "Загрузить объект", "upload_errors": "Загрузка завершена с {count, plural, one {# ошибкой} other {# ошибками}}, обновите страницу, чтобы увидеть новые загруженные объекты.", + "upload_finished": "Загрузка завершена", "upload_progress": "Осталось {remaining, number} - Обработано {processed, number}/{total, number}", "upload_skipped_duplicates": "Пропущен{count, plural, one { # дубликат} many {о # дубликатов} other {о # дубликата}}", "upload_status_duplicates": "Дубликаты", @@ -1954,6 +1978,7 @@ "upload_success": "Загрузка прошла успешно. Обновите страницу, чтобы увидеть новые объекты.", "upload_to_immich": "Загрузка в Immich ({count})", "uploading": "Загружается", + "uploading_media": "Выполняется загрузка", "url": "URL", "usage": "Использование", "use_biometric": "Использовать биометрию", @@ -1990,7 +2015,7 @@ "videos": "Видео", "videos_count": "{count, plural, one {# видео} other {# видео}}", "view": "Просмотр", - "view_album": "Просмотреть альбом", + "view_album": "Открыть альбом", "view_all": "Посмотреть всё", "view_all_users": "Показать всех пользователей", "view_details": "Посмотреть подробности", diff --git a/i18n/sk.json b/i18n/sk.json index a29262a6e8..a5c4c08af9 100644 --- a/i18n/sk.json +++ b/i18n/sk.json @@ -14,6 +14,7 @@ "add_a_location": "Pridať polohu", "add_a_name": "Pridať meno", "add_a_title": "Pridať názov", + "add_birthday": "Pridať narodeniny", "add_endpoint": "Pridať koncový bod", "add_exclusion_pattern": "Pridať vzor vylúčenia", "add_import_path": "Pridať cestu pre import", @@ -44,6 +45,13 @@ "backup_database": "Vytvoriť výpis databázy", "backup_database_enable_description": "Povoliť výpisy z databázy", "backup_keep_last_amount": "Množstvo predchádzajúcich výpisov, ktoré sa majú zachovať", + "backup_onboarding_1_description": "externú kópiu v cloude alebo na inom fyzickom mieste.", + "backup_onboarding_2_description": "lokálne kópie na rôznych zariadeniach. To zahŕňa hlavné súbory a ich lokálnu zálohu.", + "backup_onboarding_3_description": "celkový počet kópií vašich údajov vrátane pôvodných súborov. Toto zahŕňa 1 externú kópiu a 2 lokálne kópie.", + "backup_onboarding_description": "Na ochranu vašich údajov sa odporúča stratégia zálohovania 3-2-1. Pre komplexné riešenie zálohovania by ste mali uchovávať kópie nahratých fotografií/videí, ako aj databázy Immich.", + "backup_onboarding_footer": "Ďalšie informácie o vytváraní zálohy Immich nájdete v dokumentácii.", + "backup_onboarding_parts_title": "Zálohovanie 3-2-1 zahŕňa:", + "backup_onboarding_title": "Zálohy", "backup_settings": "Nastavenia výpisu databázy", "backup_settings_description": "Spravovať nastavenia výpisu databázy.", "cleared_jobs": "Hotové úlohy pre: {job}", @@ -240,8 +248,8 @@ "send_welcome_email": "Odoslať uvítací e-mail", "server_external_domain_settings": "Externá doména", "server_external_domain_settings_description": "Verejná doména pre zdieľané odkazy, vrátane http(s)://", - "server_public_users": "Verejní užívatelia", - "server_public_users_description": "Všetci užívatelia (meno a email) sú uvedení pri pridávaní užívateľa do zdieľaných albumov. Ak je táto funkcia vypnutá, zoznam užívateľov bude dostupný iba správcom.", + "server_public_users": "Verejní používatelia", + "server_public_users_description": "Všetci používatelia (meno a email) sú uvedení pri pridávaní používateľa do zdieľaných albumov. Ak je táto funkcia vypnutá, zoznam používateľov bude dostupný iba správcom.", "server_settings": "Server", "server_settings_description": "Spravovať nastavenia servera", "server_welcome_message": "Uvítacia správa", @@ -264,16 +272,16 @@ "storage_template_path_length": "Približný limit dĺžky cesty: {length, number}/{limit, number}", "storage_template_settings": "Šablóna úložiska", "storage_template_settings_description": "Spravujte štruktúru priečinkov a názov súboru odovzdaného média", - "storage_template_user_label": "{label} je Štítok úložiska používateľa", + "storage_template_user_label": "{label} je štítok úložiska používateľa", "system_settings": "Nastavenia systému", "tag_cleanup_job": "Prečistenie štítkov", "template_email_available_tags": "V šablóne môžete použiť nasledujúce premenné: {tags}", - "template_email_if_empty": "Ak nie je zadaná žiadna šablóna, bude použitá predvolená šablóna.", + "template_email_if_empty": "Ak je šablóna prázdna, použije sa predvolený e-mail.", "template_email_invite_album": "Šablóna Pozvánky do albumu", "template_email_preview": "Ukážka", "template_email_settings": "Emailové šablóny", "template_email_update_album": "Upraviť šablónu albumu", - "template_email_welcome": "Šablóna uvítajúceho emailu", + "template_email_welcome": "Šablóna uvítacieho e-mailu", "template_settings": "Šablóna upozornení", "template_settings_description": "Spravovanie vlastných šablón upozornení", "theme_custom_css_settings": "Vlastné CSS", @@ -347,19 +355,19 @@ "trash_number_of_days_description": "Počet dní, počas ktorých sa majú médiá ponechať v koši pred ich trvalým odstránením", "trash_settings": "Kôš", "trash_settings_description": "Spravovať nastavenia koša", - "user_cleanup_job": "Premazanie používateľov", + "user_cleanup_job": "Prečistenie používateľov", "user_delete_delay": "Konto {user} a jeho médiá budú podľa plánu natrvalo vymazané za {delay, plural, one {# deň} few {# dni} other {# dní}}.", "user_delete_delay_settings": "Oneskorenie vymazania", "user_delete_delay_settings_description": "Počet dní, po ktorých sa po odstránení používateľa natrvalo odstráni jeho účet a položky. Úloha odstraňovania používateľov sa spúšťa o polnoci, aby sa skontrolovali používatelia, ktorí sú pripravení na odstránenie. Zmeny tohto nastavenia sa vyhodnotia pri ďalšom spustení.", "user_delete_immediately": "Konto a médiá používateľa {user} budú zaradené do poradia na trvalé vymazanie okamžite.", - "user_delete_immediately_checkbox": "Používateľ a médiá budú zaradení do frontu na okamžité vymazanie", + "user_delete_immediately_checkbox": "Zaradiť používateľa a položky do poradia na okamžité vymazanie", "user_details": "Podrobnosti o používateľovi", "user_management": "Spravovanie používateľov", "user_password_has_been_reset": "Heslo používateľa bolo obnovené:", "user_password_reset_description": "Poskytnite používateľovi dočasné heslo a informujte ho, že si ho bude musieť zmeniť pri ďalšom prihlásení.", "user_restore_description": "{user} bude účet obnovený.", "user_restore_scheduled_removal": "Obnoviť používateľa - plánované odstránenie na {date, date, long}", - "user_settings": "Používateľ", + "user_settings": "Nastavenia používateľa", "user_settings_description": "Spravovať používateľské nastavenia", "user_successfully_removed": "Používateľ {email} bol úspešne odstránený.", "version_check_enabled_description": "Povoliť kontrolu verzie", @@ -397,6 +405,7 @@ "album_cover_updated": "Obal albumu aktualizovaný", "album_delete_confirmation": "Ste si istý, že chcete odstrániť album {album}?", "album_delete_confirmation_description": "Ak je tento album zdieľaný, ostatní používatelia k nemu už nebudú mať prístup.", + "album_deleted": "Album bol vymazaný", "album_info_card_backup_album_excluded": "VYLÚČENÉ", "album_info_card_backup_album_included": "ZAHRNUTÉ", "album_info_updated": "Informácie albumu aktualizované", @@ -510,6 +519,7 @@ "back_close_deselect": "Späť, zavrieť alebo zrušiť výber", "background_location_permission": "Povolenie na určenie polohy na pozadí", "background_location_permission_content": "Aby bolo možné prepínať siete pri spustení na pozadí, musí mať aplikácia Immich *vždy* presný prístup k polohe, aby mohla prečítať názov siete Wi-Fi", + "backup": "Zálohovanie", "backup_album_selection_page_albums_device": "Albumy v zariadení ({count})", "backup_album_selection_page_albums_tap": "Ťuknutím na položku ju zahrniete, dvojitým ťuknutím ju vylúčite", "backup_album_selection_page_assets_scatter": "Súbory môžu byť roztrúsené vo viacerých albumoch. To umožňuje zahrnúť alebo vylúčiť albumy počas procesu zálohovania.", @@ -721,8 +731,9 @@ "current_device": "Súčasné zariadenie", "current_pin_code": "Aktuálny PIN kód", "current_server_address": "Aktuálna adresa servera", - "custom_locale": "Vlastná Lokalizácia", + "custom_locale": "Vlastné nastavenie jazyka", "custom_locale_description": "Formátovanie dátumov a čísel podľa jazyka a regiónu", + "custom_url": "Vlastná URL adresa", "daily_title_text_date": "EEEE, d. MMMM", "daily_title_text_date_year": "EEEE, d. MMMM y", "dark": "Tmavá", @@ -742,7 +753,8 @@ "default_locale": "Predvolené miestne nastavenie", "default_locale_description": "Formátovanie dátumov a čísel na základe miestneho nastavenia prehliadača", "delete": "Vymazať", - "delete_action_prompt": "{count} natrvalo vymazaných", + "delete_action_confirmation_message": "Naozaj chcete túto položku odstrániť? Táto akcia presunie položku do koša na serveri a zobrazí sa otázka, či ju chcete odstrániť aj lokálne", + "delete_action_prompt": "{count} vymazaných", "delete_album": "Odstrániť album", "delete_api_key_prompt": "Naozaj chcete odstrániť tento API kľúč?", "delete_dialog_alert": "Tieto položky budú natrvalo odstránené z aplikácie Immich a z vášho zariadenia", @@ -760,13 +772,15 @@ "delete_local_dialog_ok_backed_up_only": "Vymazať len zálohované", "delete_local_dialog_ok_force": "Napriek tomu vymazať", "delete_others": "Vymazať ostatné", + "delete_permanently": "Natrvalo odstrániť", + "delete_permanently_action_prompt": "{count} natrvalo odstránených", "delete_shared_link": "Odstrániť zdieľaný odkaz", "delete_shared_link_dialog_title": "Odstrániť zdieľaný odkaz", "delete_tag": "Odstrániť štítok", "delete_tag_confirmation_prompt": "Naozaj chcete odstrániť štítok menom {tagName}?", "delete_user": "Vymazať používateľa", "deleted_shared_link": "Vymazaný zdieľaný odkaz", - "deletes_missing_assets": "Chýbajú vymazané položky z disku", + "deletes_missing_assets": "Odstráni položky chýbajúce na disku", "description": "Popis", "description_input_hint_text": "Pridať popis...", "description_input_submit_error": "Chyba pri aktualizovaní popisu, zobrazte log pre viac detailov", @@ -774,7 +788,7 @@ "details": "Podrobnosti", "direction": "Smer", "disabled": "Vypnuté", - "disallow_edits": "Zakázať editovanie", + "disallow_edits": "Zakázať úpravy", "discord": "Discord", "discover": "Objaviť", "discovered_devices": "Objavené zariadenia", @@ -814,7 +828,8 @@ "duration": "Trvanie", "edit": "Upraviť", "edit_album": "Upraviť album", - "edit_avatar": "Upraviť avatar", + "edit_avatar": "Upraviť profilový obrázok", + "edit_birthday": "Upraviť narodeniny", "edit_date": "Upraviť dátum", "edit_date_and_time": "Upraviť dátum a čas", "edit_description": "Upraviť popis", @@ -838,7 +853,7 @@ "editor_close_without_save_prompt": "Úpravy nebudú uložené", "editor_close_without_save_title": "Zavrieť editor?", "editor_crop_tool_h2_aspect_ratios": "Pomer strán", - "editor_crop_tool_h2_rotation": "Rotovanie", + "editor_crop_tool_h2_rotation": "Otočenie", "email": "E-mail", "email_notifications": "E-mailové oznámenia", "empty_folder": "Tento priečinok je prázdny", @@ -861,23 +876,23 @@ "error_tag_face_bounding_box": "Chyba pri označovaní tváre - nemožno získať súradnice ohraničujúceho poľa", "error_title": "Chyba - niečo sa pokazilo", "errors": { - "cannot_navigate_next_asset": "Nedokážem prejsť na ďaľšiu položku", - "cannot_navigate_previous_asset": "Nedokážem prejsť na predošlú položku", - "cant_apply_changes": "Nedokážem aplikovať zmeny", + "cannot_navigate_next_asset": "Nie je možné prejsť na ďalšiu položku", + "cannot_navigate_previous_asset": "Nie je možné prejsť na predošlú položku", + "cant_apply_changes": "Nie je možné použiť zmeny", "cant_change_activity": "Nie je možné {enabled, select, true {zakázať} other {povoliť}} aktivitu", - "cant_change_asset_favorite": "Nedokážem zmeniť obľúbenosť pre položku", + "cant_change_asset_favorite": "Nie je možné zmeniť stav obľúbenosti pre položku", "cant_change_metadata_assets_count": "Nie je možné zmeniť metadáta pre {count, plural, one {# položku} few {# položky} other {# položiek}}", "cant_get_faces": "Nedokážem získať tváre", "cant_get_number_of_comments": "Nedokážem získať počet komentárov", "cant_search_people": "Nedokážem hľadať osoby", "cant_search_places": "Nedokážem hľadať miesta", "error_adding_assets_to_album": "Nepodarilo sa pridať položky do albumu", - "error_adding_users_to_album": "Nepodarilo sa pridať užívateľov do albumu", - "error_deleting_shared_user": "Nepodarilo sa odstrániť zdieľaného používateľa", + "error_adding_users_to_album": "Nepodarilo sa pridať používateľov do albumu", + "error_deleting_shared_user": "Chyba pri odstraňovaní zdieľaného používateľa", "error_downloading": "Nepodarilo sa stiahnuť {filename}", "error_hiding_buy_button": "Nepodarilo sa skryť tlačidlo kúpiť", "error_removing_assets_from_album": "Nepodarilo sa odstrániť položku z albumu, podrobnejšie informácie nájdete v konzole", - "error_selecting_all_assets": "Nepodarilo sa vybrať položky", + "error_selecting_all_assets": "Chyba pri výbere všetkých položiek", "exclusion_pattern_already_exists": "Tento vzor vylúčenia už existuje.", "failed_to_create_album": "Nepodarilo sa vytvoriť album", "failed_to_create_shared_link": "Nepodarilo sa vytvoriť zdieľaný odkaz", @@ -982,6 +997,7 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Pridať popis...", + "exif_bottom_sheet_description_error": "Chyba pri aktualizácii popisu", "exif_bottom_sheet_details": "PODROBNOSTI", "exif_bottom_sheet_location": "POLOHA", "exif_bottom_sheet_people": "ĽUDIA", @@ -1002,6 +1018,8 @@ "explorer": "Prieskumník", "export": "Exportovať", "export_as_json": "Exportovať do JSON", + "export_database": "Exportovať databázu", + "export_database_description": "Exportovať databázu SQLite", "extension": "Rozšírenie", "external": "Externý", "external_libraries": "Externé knižnice", @@ -1146,6 +1164,7 @@ "language_no_results_title": "Neboli nájdené žiadne jazyky", "language_search_hint": "Vyhľadať jazyky...", "language_setting_description": "Vyberte požadovaný jazyk", + "large_files": "Veľké súbory", "last_seen": "Naposledy videné", "latest_version": "Najnovšia verzia", "latitude": "Zemepisná šírka", @@ -1165,7 +1184,6 @@ "light": "Svetlá", "like_deleted": "Like odstránený", "link_motion_video": "Pripojiť pohyblivé video", - "link_options": "Možnosti odkazu", "link_to_oauth": "Prepojiť s OAuth", "linked_oauth_account": "Pripojený OAuth účet", "list": "Zoznam", @@ -1205,7 +1223,7 @@ "login_form_failed_get_oauth_server_config": "Chyba prihlásenia pomocou OAuth, skontrolujte adresu URL servera", "login_form_failed_get_oauth_server_disable": "Funkcia OAuth nie je na tomto serveri dostupná", "login_form_failed_login": "Chyba prihlásenia, skontrolujte url adresu servera, e-mail a heslo", - "login_form_handshake_exception": "Nastala chyba handshake. Zapnite podoporu samo-podpísaných certifikátov v nastaveniach ak používate samo-podpísané certifikáty.", + "login_form_handshake_exception": "Došlo k výnimke Handshake so serverom. Ak používate certifikát s vlastným podpisom, povoľte v nastaveniach podporu certifikátov s vlastným podpisom.", "login_form_password_hint": "heslo", "login_form_save_login": "Zostať prihlásený", "login_form_server_empty": "Zadajte URL adresu servera.", @@ -1230,8 +1248,7 @@ "manage_your_devices": "Spravovať vaše prihlásené zariadenia", "manage_your_oauth_connection": "Spravovať vaše OAuth spojenia", "map": "Mapa", - "map_assets_in_bound": "{count} fotka", - "map_assets_in_bounds": "{count} fotiek", + "map_assets_in_bounds": "{count, plural, one {# fotka} few {# fotky} other {# fotiek}}", "map_cannot_get_user_location": "Nie je možné získať polohu používateľa", "map_location_dialog_yes": "Áno", "map_location_picker_page_use_location": "Použiť túto polohu", @@ -1306,7 +1323,7 @@ "new_pin_code_subtitle": "Toto je váš prvý prístup k zamknutému priečinku. Vytvorte si PIN kód na bezpečný prístup k tejto stránke", "new_user_created": "Nový používateľ vytvorený", "new_version_available": "JE DOSTUPNÁ NOVÁ VERZIA", - "newest_first": "Najnovšie prvé", + "newest_first": "Najprv najnovšie", "next": "Ďalej", "next_memory": "Ďalšia spomienka", "no": "Nie", @@ -1347,7 +1364,7 @@ "official_immich_resources": "Oficiálne Immich zdroje", "offline": "Offline", "ok": "OK", - "oldest_first": "Najstaršie prvé", + "oldest_first": "Najprv najstaršie", "on_this_device": "Na tomto zariadení", "onboarding": "Na palube", "onboarding_locale_description": "Vyberte požadovaný jazyk. Neskôr ho môžete zmeniť v nastaveniach.", @@ -1379,7 +1396,7 @@ "partner_list_user_photos": "Fotky používateľa {user}", "partner_list_view_all": "Zobraziť všetky", "partner_page_empty_message": "Vaše fotky zatiaľ nie sú zdieľané so žiadnym partnerom.", - "partner_page_no_more_users": "Žiadni ďalší užívatelia na zdieľanie", + "partner_page_no_more_users": "Žiadni ďalší používatelia na pridanie", "partner_page_partner_add_failed": "Pridávanie partnera zlyhalo", "partner_page_select_partner": "Zvoliť partnera", "partner_page_shared_to_title": "Zdieľané pre", @@ -1469,7 +1486,7 @@ "public_album": "Verejný album", "public_share": "Verejné zdieľanie", "purchase_account_info": "Podporovateľ", - "purchase_activated_subtitle": "Ďakujeme za podporu Immich a softvéru s otvorenými zdrojákmi", + "purchase_activated_subtitle": "Ďakujeme vám za podporu aplikácie Immich a softvéru s otvoreným zdrojovým kódom", "purchase_activated_time": "Aktivované {date}", "purchase_activated_title": "Váš kľúč je úspešne aktivovaný", "purchase_button_activate": "Aktivovať", @@ -1487,8 +1504,8 @@ "purchase_license_subtitle": "Kúpte si Immich a podporte neustály vývoj tejto služby", "purchase_lifetime_description": "Doživotná platnosť", "purchase_option_title": "MOŽNOSTI NÁKUPU", - "purchase_panel_info_1": "Vývoj Immich zaberá veľa času a úsilia, a máme zamestnaných fulltime inžinierov, aby ho spravili ako sa najlepšie dá. Naša misia je, aby sa open-source softvér a etické biznis praktiky stali udržateľným zdrojom príjmu pre vývojárov a vytvorili ekosystém rešpektujúci súkromie so skutočnými náhradami voči zneužívajúcim cloudovým službám.", - "purchase_panel_info_2": "Keďže sme zaviazaní nezavádzať platené verzie, nezískate týmto nákupom žiadne prídavné funkcie. Spoliehame sa na používateľov, ako ste vy, že podporia neustály vývoj aplikácie Immich.", + "purchase_panel_info_1": "Vývoj aplikácie Immich zaberá veľa času a úsilia, pričom na ňom pracujú inžinieri na plný úväzok, aby bol čo najlepší. Naším poslaním je, aby sa softvér s otvoreným zdrojovým kódom a etické obchodné postupy stali udržateľným zdrojom príjmov pre vývojárov a aby sme vytvorili ekosystém rešpektujúci súkromie so skutočnými alternatívami k zneužívajúcim cloudovým službám.", + "purchase_panel_info_2": "Keďže sme zaviazaní nezavádzať platené verzie, nezískate týmto nákupom žiadne pridané funkcie. Spoliehame sa na používateľov, ako ste vy, že podporia neustály vývoj aplikácie Immich.", "purchase_panel_title": "Podporiť projekt", "purchase_per_server": "Za server", "purchase_per_user": "Za používateľa", @@ -1575,13 +1592,14 @@ "resolve_duplicates": "Vyriešiť duplicity", "resolved_all_duplicates": "Vyriešené všetky duplicity", "restore": "Navrátiť", - "restore_all": "Navrátit všetko", + "restore_all": "Navrátiť všetko", "restore_trash_action_prompt": "{count} obnovených z koša", "restore_user": "Navrátiť používateľa", - "restored_asset": "Navrátené položky", + "restored_asset": "Navrátená položka", "resume": "Pokračovať", "retry_upload": "Zopakovať nahrávanie", "review_duplicates": "Preskúmať duplikáty", + "review_large_files": "Skontrolovať veľké súbory", "role": "Rola", "role_editor": "Editor", "role_viewer": "Divák", @@ -1726,7 +1744,7 @@ "shared_album_activities_input_disable": "Komentár je zakázaný", "shared_album_activity_remove_content": "Chcete vymazať túto aktivitu?", "shared_album_activity_remove_title": "Vymazať aktivitu", - "shared_album_section_people_action_error": "Vyskytla sa chyba pri odchádzaní / odstraňovaní používateľa z albumu", + "shared_album_section_people_action_error": "Vyskytla sa chyba pri opustení/odobratí z albumu", "shared_album_section_people_action_leave": "Odstrániť používateľa z albumu", "shared_album_section_people_action_remove_user": "Odstrániť používateľa z albumu", "shared_album_section_people_title": "ĽUDIA", @@ -1739,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "Skopírované do schránky", "shared_link_clipboard_text": "Odkaz: {link}\nHeslo: {password}", "shared_link_create_error": "Vyskytla sa chyba behom vytvárania zdieľaného odkazu", + "shared_link_custom_url_description": "Prístup k tomuto zdieľanému odkazu pomocou vlastnej URL adresy", "shared_link_edit_description_hint": "Zadajte popis zdieľania", "shared_link_edit_expire_after_option_day": "1 deň", "shared_link_edit_expire_after_option_days": "{count} dní", @@ -1764,6 +1783,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Spravovať zdieľané odkazy", "shared_link_options": "Možnosti zdieľaných odkazov", + "shared_link_password_description": "Vyžadovať heslo pre prístup k tomuto zdieľanému odkazu", "shared_links": "Zdieľané odkazy", "shared_links_description": "Zdieľanie fotografií a videí pomocou odkazu", "shared_photos_and_videos_count": "{assetCount, plural, few {# zdieľané fotky a videá.} other {# zdieľaných fotiek a videí.}}", @@ -1833,7 +1853,7 @@ "stop_motion_photo": "Stopmotion fotka", "stop_photo_sharing": "Zastaviť zdieľanie vašich fotiek?", "stop_photo_sharing_description": "{partner} už nebude mať prístup k vašim fotkám.", - "stop_sharing_photos_with_user": "Zastaviť zdieľanie týchto fotiek s týmto používateľom", + "stop_sharing_photos_with_user": "Zastaviť zdieľanie vašich fotiek s týmto používateľom", "storage": "Ukladací priestor", "storage_label": "Štítok úložiska", "storage_quota": "Úložný limit", @@ -1941,11 +1961,13 @@ "updated_at": "Aktualizované", "updated_password": "Heslo zmenené", "upload": "Nahrať", + "upload_action_prompt": "{count} v poradí na nahratie", "upload_concurrency": "Súbežnosť nahrávania", "upload_details": "Podrobnosti o nahrávaní", "upload_dialog_info": "Chcete zálohovať zvolené médiá na server?", "upload_dialog_title": "Nahrať médiá", "upload_errors": "Nahrávanie ukončené s {count, plural, one {# chybou} other {# chybami}}, obnovte stránku, aby sa zobrazili nové položky.", + "upload_finished": "Nahrávanie dokončené", "upload_progress": "Ostáva {remaining, number} - Spracovaných {processed, number}/{total, number}", "upload_skipped_duplicates": "{count, plural, one {Preskočená # duplicitná položka} few {Preskočené # duplicitné položky} other {Preskočených # duplicitných položiek}}", "upload_status_duplicates": "Duplikáty", @@ -1954,6 +1976,7 @@ "upload_success": "Nahrávanie úspešné, pridané súbory sa zobrazia po obnovení stránky.", "upload_to_immich": "Nahrať na Immich ({count})", "uploading": "Nahrávanie", + "uploading_media": "Nahrávanie médií", "url": "Odkaz URL", "usage": "Použitie", "use_biometric": "Použiť biometrické údaje", @@ -1961,7 +1984,7 @@ "use_custom_date_range": "Použiť radšej vlastný rozsah dátumov", "user": "Používateľ", "user_has_been_deleted": "Tento používateľ bol vymazaný.", - "user_id": "Používateľské ID", + "user_id": "ID používateľa", "user_liked": "Používateľovi {user} sa páči {type, select, photo {táto fotka} video {toto video} asset {táto položka} other {toto}}", "user_pin_code_settings": "PIN kód", "user_pin_code_settings_description": "Spravujte svoj PIN kód", @@ -1981,7 +2004,7 @@ "variables": "Premenné", "version": "Verzia", "version_announcement_closing": "Tvoj kamarát, Alex", - "version_announcement_message": "Ahoj! Nová verzia Immich je dostupná. Prosím prečítajte si poznámky k vydaniu, aby ste sa uistili, že inštalácia bude aktuálna bez problémov, najmä ak používate WatchTower alebo akýkoľvek spôsob automatickej aktualizácie Immich servera.", + "version_announcement_message": "Ahoj! K dispozícii je nová verzia aplikácie Immich. Prosím, venujte trochu času prečítaniu poznámok k vydaniu, aby ste sa uistili, že vaša inštalácia je aktuálna a predišli tak akýmkoľvek chybám v konfigurácii, najmä ak používate WatchTower alebo akýkoľvek mechanizmus, ktorý sa stará o automatickú aktualizáciu vašej inštancie Immich.", "version_history": "História verzií", "version_history_item": "Inštalovaná {version} dňa {date}", "video": "Video", diff --git a/i18n/sl.json b/i18n/sl.json index e08a9dd506..ad67e2f5c4 100644 --- a/i18n/sl.json +++ b/i18n/sl.json @@ -14,6 +14,7 @@ "add_a_location": "Dodaj lokacijo", "add_a_name": "Dodaj ime", "add_a_title": "Dodaj naslov", + "add_birthday": "Dodaj rojstni dan", "add_endpoint": "Dodaj končno točko", "add_exclusion_pattern": "Dodaj vzorec izključitve", "add_import_path": "Dodaj pot uvoza", @@ -44,6 +45,13 @@ "backup_database": "Ustvari izpis baze podatkov", "backup_database_enable_description": "Omogoči izpise baze podatkov", "backup_keep_last_amount": "Število prejšnjih odlagališč, ki jih je treba obdržati", + "backup_onboarding_1_description": "kopijo zunaj lokacije v oblaku ali na drugi fizični lokaciji.", + "backup_onboarding_2_description": "lokalne kopije na različnih napravah. To vključuje glavne datoteke in lokalno varnostno kopijo teh datotek.", + "backup_onboarding_3_description": "skupno število kopij vaših podatkov, vključno z izvirnimi datotekami. To vključuje 1 kopijo zunaj lokacije in 2 lokalni kopiji.", + "backup_onboarding_description": "Za zaščito podatkov priporočamo strategijo varnostnega kopiranja 3-2-1. Za celovito rešitev varnostnega kopiranja hranite kopije naloženih fotografij/videoposnetkov in podatkovne baze Immich.", + "backup_onboarding_footer": "Za več informacij o varnostnem kopiranju Immicha glejte dokumentacijo.", + "backup_onboarding_parts_title": "Varnostna kopija 3-2-1 vključuje:", + "backup_onboarding_title": "Varnostne kopije", "backup_settings": "Nastavitve izpisa baze podatkov", "backup_settings_description": "Upravljanje nastavitev izpisa podatkovne baze.", "cleared_jobs": "Razčiščeno opravilo za: {job}", @@ -397,6 +405,7 @@ "album_cover_updated": "Naslovnica albuma posodobljena", "album_delete_confirmation": "Ali ste prepričani, da želite izbrisati album {album}?", "album_delete_confirmation_description": "Če je ta album v skupni rabi, drugi uporabniki ne bodo mogli več dostopati do njega.", + "album_deleted": "Album izbrisan", "album_info_card_backup_album_excluded": "IZKLJUČENO", "album_info_card_backup_album_included": "VKLJUČENO", "album_info_updated": "Podatki o albumu posodobljeni", @@ -510,6 +519,7 @@ "back_close_deselect": "Nazaj, zaprite ali prekličite izbiro", "background_location_permission": "Dovoljenje za iskanje lokacije v ozadju", "background_location_permission_content": "Ko deluje v ozadju mora imeti Immich za zamenjavo omrežij, *vedno* dostop do natančne lokacije, da lahko aplikacija prebere ime omrežja Wi-Fi", + "backup": "Varnostna kopija", "backup_album_selection_page_albums_device": "Albumi v napravi ({count})", "backup_album_selection_page_albums_tap": "Tapnite za vključitev, dvakrat tapnite za izključitev", "backup_album_selection_page_assets_scatter": "Sredstva so lahko razpršena po več albumih. Tako je mogoče med postopkom varnostnega kopiranja albume vključiti ali izključiti.", @@ -723,6 +733,7 @@ "current_server_address": "Trenutni naslov strežnika", "custom_locale": "Jezik po meri", "custom_locale_description": "Oblikujte datume in številke glede na jezik in regijo", + "custom_url": "URL po meri", "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Temno", @@ -742,7 +753,8 @@ "default_locale": "Privzeti jezik", "default_locale_description": "Oblikujte datume in številke glede na lokalne nastavitve brskalnika", "delete": "Izbriši", - "delete_action_prompt": "trajno izbrisano {count}", + "delete_action_confirmation_message": "Ali ste prepričani, da želite izbrisati to sredstvo? S tem dejanjem boste sredstvo premaknili v koš na strežniku in vas pozvali, ali ga želite izbrisati lokalno", + "delete_action_prompt": "izbrisano {count}", "delete_album": "Izbriši album", "delete_api_key_prompt": "Ali ste prepričani, da želite izbrisati ta API ključ?", "delete_dialog_alert": "Ti elementi bodo trajno izbrisani iz Immicha in vaše naprave", @@ -760,6 +772,8 @@ "delete_local_dialog_ok_backed_up_only": "Izbriši samo kar je varnostno kopirano", "delete_local_dialog_ok_force": "Vseeno izbriši", "delete_others": "Izbriši ostale", + "delete_permanently": "Izbriši trajno", + "delete_permanently_action_prompt": "{count} trajno izbrisano", "delete_shared_link": "Izbriši povezavo skupne rabe", "delete_shared_link_dialog_title": "Izbriši povezavo skupne rabe", "delete_tag": "Izbriši oznako", @@ -815,6 +829,7 @@ "edit": "Uredi", "edit_album": "Uredi album", "edit_avatar": "Uredi avatar", + "edit_birthday": "Uredi rojstni dan", "edit_date": "Uredi datum", "edit_date_and_time": "Uredi datum in uro", "edit_description": "Uredi opis", @@ -982,6 +997,7 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Dodaj opis..", + "exif_bottom_sheet_description_error": "Napaka pri posodabljanju opisa", "exif_bottom_sheet_details": "PODROBNOSTI", "exif_bottom_sheet_location": "LOKACIJA", "exif_bottom_sheet_people": "OSEBE", @@ -1002,6 +1018,8 @@ "explorer": "Raziskovalec", "export": "Izvoz", "export_as_json": "Izvozi kot JSON", + "export_database": "Izvoz baze podatkov", + "export_database_description": "Izvozite bazo podatkov SQLite", "extension": "Razširitev", "external": "Zunanji", "external_libraries": "Zunanje knjižnice", @@ -1146,6 +1164,7 @@ "language_no_results_title": "Ni najdenih jezikov", "language_search_hint": "Iskanje jezikov...", "language_setting_description": "Izberite želeni jezik", + "large_files": "Velike datoteke", "last_seen": "Nazadnje viden", "latest_version": "Najnovejša različica", "latitude": "Zemljepisna širina", @@ -1165,7 +1184,6 @@ "light": "Svetlo", "like_deleted": "Všeček izbrisan", "link_motion_video": "Povezava videa gibanja", - "link_options": "Možnosti povezave", "link_to_oauth": "Povezava do OAuth", "linked_oauth_account": "Povezan račun OAuth", "list": "Seznam", @@ -1230,8 +1248,7 @@ "manage_your_devices": "Upravljajte svoje prijavljene naprave", "manage_your_oauth_connection": "Upravljajte svojo OAuth povezavo", "map": "Zemljevid", - "map_assets_in_bound": "{count} slika", - "map_assets_in_bounds": "{count} slik", + "map_assets_in_bounds": "{count, plural, one {# slika} other {# slik}}", "map_cannot_get_user_location": "Lokacije uporabnika ni mogoče dobiti", "map_location_dialog_yes": "Da", "map_location_picker_page_use_location": "Uporabi to lokacijo", @@ -1582,6 +1599,7 @@ "resume": "Nadaljuj", "retry_upload": "Poskusite znova naložiti", "review_duplicates": "Pregled dvojnikov", + "review_large_files": "Pregled velikih datotek", "role": "Vloga", "role_editor": "Urejevalec", "role_viewer": "Gledalec", @@ -1739,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "Kopirano v odložišče", "shared_link_clipboard_text": "Povezava: {link}\nGeslo: {password}", "shared_link_create_error": "Napaka pri ustvarjanju povezave skupne rabe", + "shared_link_custom_url_description": "Dostop do te deljene povezave z URL-jem po meri", "shared_link_edit_description_hint": "Vnesi opis skupne rabe", "shared_link_edit_expire_after_option_day": "1 dan", "shared_link_edit_expire_after_option_days": "{count} dni", @@ -1764,6 +1783,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Upravljanje povezav v skupni rabi", "shared_link_options": "Možnosti skupne povezave", + "shared_link_password_description": "Za dostop do te deljene povezave je potrebno geslo", "shared_links": "Povezave v skupni rabi", "shared_links_description": "Deli fotografije in videoposnetke s povezavo", "shared_photos_and_videos_count": "{assetCount, plural, other {# deljenih fotografij & videoposnetkov.}}", @@ -1941,11 +1961,13 @@ "updated_at": "Posodobljeno", "updated_password": "Posodobljeno geslo", "upload": "Naloži", + "upload_action_prompt": "{count} v čakalni vrsti za nalaganje", "upload_concurrency": "Sočasnost nalaganja", "upload_details": "Podrobnosti o nalaganju", "upload_dialog_info": "Ali želite varnostno kopirati izbrana sredstva na strežnik?", "upload_dialog_title": "Naloži sredstvo", "upload_errors": "Nalaganje je končano s/z {count, plural, one {# napako} two {# napakama} other {# napakami}}, osvežite stran, da vidite nova sredstva za nalaganje.", + "upload_finished": "Nalaganje končano", "upload_progress": "Preostalo {remaining, number} - Obdelano {processed, number}/{total, number}", "upload_skipped_duplicates": "Preskočeno {count, plural, one {# podvojeno sredstvo} two {# podvojeni sredstvi} few {# podvojena sredstva} other {# podvojenih sredstev}}", "upload_status_duplicates": "Dvojniki", @@ -1954,6 +1976,7 @@ "upload_success": "Nalaganje je uspelo, osvežite stran, da vidite nova sredstva za nalaganje.", "upload_to_immich": "Naloži v Immich ({count})", "uploading": "Nalagam", + "uploading_media": "Nalaganje medijev", "url": "URL", "usage": "Uporaba", "use_biometric": "Uporabite biometrične podatke", diff --git a/i18n/sr_Cyrl.json b/i18n/sr_Cyrl.json index 6216e671a8..f085adf532 100644 --- a/i18n/sr_Cyrl.json +++ b/i18n/sr_Cyrl.json @@ -479,6 +479,7 @@ "back_close_deselect": "Назад, затворите или опозовите избор", "background_location_permission": "Дозвола за локацију у позадини", "background_location_permission_content": "Да би се мењале мреже док се ради у позадини, Имих мора *увек* имати прецизан приступ локацији како би апликација могла да прочита име Wi-Fi мреже", + "backup": "Направи резервну копију", "backup_album_selection_page_albums_device": "Албума на уређају ({count})", "backup_album_selection_page_albums_tap": "Додирни да укључиш, додирни двапут да искључиш", "backup_album_selection_page_assets_scatter": "Записи се могу наћи у више различитих албума. Одатле албуми се могу укључити или искључити током процеса прављења позадинских копија.", @@ -1085,7 +1086,6 @@ "light": "Светло", "like_deleted": "Лајкуј избрисано", "link_motion_video": "Направи везу за видео запис", - "link_options": "Опције везе", "link_to_oauth": "Веза до OAuth-а", "linked_oauth_account": "Повезани OAuth налог", "list": "Излистај", @@ -1144,7 +1144,6 @@ "manage_your_devices": "Управљајте својим пријављеним уређајима", "manage_your_oauth_connection": "Управљајте својом OAuth везом", "map": "Мапа", - "map_assets_in_bound": "{count} фотографија", "map_assets_in_bounds": "{count} фотографија", "map_cannot_get_user_location": "Није могуц́е добити локацију корисника", "map_location_dialog_yes": "Да", diff --git a/i18n/sr_Latn.json b/i18n/sr_Latn.json index a757146861..6eb88d7fd2 100644 --- a/i18n/sr_Latn.json +++ b/i18n/sr_Latn.json @@ -477,6 +477,7 @@ "back_close_deselect": "Nazad, zatvorite ili opozovite izbor", "background_location_permission": "Dozvola za lokaciju u pozadini", "background_location_permission_content": "Da bi se menjale mreže dok se radi u pozadini, Imih mora *uvek* imati precizan pristup lokaciji kako bi aplikacija mogla da pročita ime Wi-Fi mreže", + "backup": "Napravi rezervnu kopiju", "backup_album_selection_page_albums_device": "Albuma na uređaju ({count})", "backup_album_selection_page_albums_tap": "Dodirni da uključiš, dodirni dvaput da isključiš", "backup_album_selection_page_assets_scatter": "Zapisi se mogu naći u više različitih albuma. Odatle albumi se mogu uključiti ili isključiti tokom procesa pravljenja pozadinskih kopija.", @@ -1070,7 +1071,6 @@ "light": "Svetlo", "like_deleted": "Lajkuj izbrisano", "link_motion_video": "Napravi vezu za video zapis", - "link_options": "Opcije veze", "link_to_oauth": "Veza do OAuth-a", "linked_oauth_account": "Povezani OAuth nalog", "list": "Izlistaj", @@ -1127,7 +1127,6 @@ "manage_your_devices": "Upravljajte svojim prijavljenim uređajima", "manage_your_oauth_connection": "Upravljajte svojom OAuth vezom", "map": "Mapa", - "map_assets_in_bound": "{count} fotografija", "map_assets_in_bounds": "{count} fotografija", "map_cannot_get_user_location": "Nije moguće dobiti lokaciju korisnika", "map_location_dialog_yes": "Da", diff --git a/i18n/sv.json b/i18n/sv.json index 00f5d95b66..e4842084f8 100644 --- a/i18n/sv.json +++ b/i18n/sv.json @@ -166,6 +166,10 @@ "metadata_settings_description": "Hantera metadata-inställningar", "migration_job": "Migrering", "migration_job_description": "Migrera miniatyrbilder för resurser och ansikten till den senaste mappstrukturen", + "nightly_tasks_cluster_faces_setting_description": "Kör ansiktsigenkänning på nyligen upptäckta ansikten", + "nightly_tasks_database_cleanup_setting": "Uppgifter för databassanering", + "nightly_tasks_database_cleanup_setting_description": "Rensa bort gammal, utgången data från databasen", + "nightly_tasks_generate_memories_setting": "Generera minnen", "no_paths_added": "Inga vägar tillagda", "no_pattern_added": "Inga mönster tillagda", "note_apply_storage_label_previous_assets": "Obs: Om du vill använda lagringsetiketten på tidigare uppladdade tillgångar kör du", @@ -488,6 +492,7 @@ "back_close_deselect": "Tillbaka, stäng eller avmarkera", "background_location_permission": "Tillåtelse för bakgrundsplats", "background_location_permission_content": "För att kunna byta nätverk när appen körs i bakgrunden måste Immich *alltid* ha åtkomst till exakt plats så att appen kan läsa av Wi-Fi-nätverkets namn", + "backup": "Säkerhetskopiera", "backup_album_selection_page_albums_device": "Album på enhet ({count})", "backup_album_selection_page_albums_tap": "Tryck en gång för att inkludera, tryck två gånger för att exkludera", "backup_album_selection_page_assets_scatter": "Objekt kan vara utspridda över flera album. Därför kan album inkluderas eller exkluderas under säkerhetskopieringsprocessen.", @@ -1123,7 +1128,6 @@ "light": "Ljus", "like_deleted": "Gilla borttagen", "link_motion_video": "Länka rörlig video", - "link_options": "Alternativ för länk", "link_to_oauth": "Länk till OAuth", "linked_oauth_account": "Länkat OAuth konto", "list": "Lista", @@ -1185,7 +1189,6 @@ "manage_your_devices": "Hantera dina inloggade enheter", "manage_your_oauth_connection": "Hantera din OAuth-anslutning", "map": "Karta", - "map_assets_in_bound": "{count} foto", "map_assets_in_bounds": "{count} foton", "map_cannot_get_user_location": "Kan inte hämta användarens plats", "map_location_dialog_yes": "Ja", diff --git a/i18n/ta.json b/i18n/ta.json index a91ea9d045..6e1ff10484 100644 --- a/i18n/ta.json +++ b/i18n/ta.json @@ -783,7 +783,6 @@ "light": "ஒளி", "like_deleted": "நீக்கப்பட்டதைப் போல", "link_motion_video": "இணைப்பு இயக்க வீடியோ", - "link_options": "இணைப்பு விருப்பங்கள்", "link_to_oauth": "OAUTH உடன் இணைப்பு", "linked_oauth_account": "இணைக்கப்பட்ட OAUTH கணக்கு", "list": "பட்டியல்", diff --git a/i18n/te.json b/i18n/te.json index 4a2e938c9a..0d5242891e 100644 --- a/i18n/te.json +++ b/i18n/te.json @@ -810,7 +810,6 @@ "light": "వెలుతురు", "like_deleted": "ఇస్టం తొలగించబడింది", "link_motion_video": "మోషన్ వీడియో లింక్ చేయండి", - "link_options": "లింక్ ఎంపికలు", "link_to_oauth": "OAuth కి లింక్ చేయండి", "linked_oauth_account": "లింక్ చేయబడిన OAuth ఖాతా", "list": "జాబితా", diff --git a/i18n/th.json b/i18n/th.json index 3cce9afd1c..8da7819ac5 100644 --- a/i18n/th.json +++ b/i18n/th.json @@ -489,6 +489,7 @@ "back_close_deselect": "ย้อนกลับ, ปิด, หรือยกเลิกการเลือก", "background_location_permission": "การอนุญาตระบุตำแหน่งพื้นหลัง", "background_location_permission_content": "เพื่อที่จะสลับการเชื่อมต่อขณะที่รันในพื้นหลัง Immich ต้องรู้ตำแหน่งที่แม่ยำตลอดเวลา เพื่อจะสามารถอ่านชื่อ Wi-Fi", + "backup": "สำรองข้อมูล", "backup_album_selection_page_albums_device": "อัลบั้มบนเครื่อง ({count})", "backup_album_selection_page_albums_tap": "กดเพื่อรวม กดสองครั้งเพื่อยกเว้น", "backup_album_selection_page_assets_scatter": "ทรัพยาการสามารถกระจายไปในหลายอัลบั้ม ดังนั้นอัลบั้มสามารถถูกรวมหรือยกเว้นในกระบวนการสำรองข้อมูล", @@ -1125,7 +1126,6 @@ "light": "สว่าง", "like_deleted": "ลบที่ถูกใจแล้ว", "link_motion_video": "ลิงก์วิดีโอเคลื่อนไหว", - "link_options": "ตัวเลือกลิงก์", "link_to_oauth": "ลิงก์ไปยัง OAuth", "linked_oauth_account": "ลิงก์บัญชีผู้ใช้ OAuth", "list": "รายการ", @@ -1188,7 +1188,6 @@ "manage_your_devices": "จัดการอุปกรณ์ของคุณ", "manage_your_oauth_connection": "จัดการการเชื่อมต่อ OAuth ของคุณ", "map": "แผนที่", - "map_assets_in_bound": "{count} รูปภาพ", "map_assets_in_bounds": "{count} รูปภาพ", "map_cannot_get_user_location": "ไม่สามารถหาตำแหน่งผู้ใช้งานได้", "map_location_dialog_yes": "ใช่", diff --git a/i18n/tr.json b/i18n/tr.json index b26f70acc2..c7a327472b 100644 --- a/i18n/tr.json +++ b/i18n/tr.json @@ -14,6 +14,7 @@ "add_a_location": "Lokasyon ekle", "add_a_name": "İsim ekle", "add_a_title": "Başlık ekle", + "add_birthday": "Doğum günü ekle", "add_endpoint": "Uç nokta ekle", "add_exclusion_pattern": "Hariç tutma deseni ekle", "add_import_path": "İçe aktarma yolu ekle", @@ -44,6 +45,13 @@ "backup_database": "Veritabanı yığını oluştur", "backup_database_enable_description": "Veritabanı yığınlarını etkinleştir", "backup_keep_last_amount": "Tutulması gereken geçmiş yığını miktarı", + "backup_onboarding_1_description": "bulutta veya başka bir fiziksel konumda bulunan yedek kopya.", + "backup_onboarding_2_description": "farklı cihazlarda yerel kopyalar. Bu, ana dosyaları ve bu dosyaların yerel yedeklerini içerir.", + "backup_onboarding_3_description": "Verilerinizin toplam kopyaları, orijinal dosyalar dahil. Bu, 1 adet dış mekan kopyası ve 2 adet yerel kopya içerir.", + "backup_onboarding_description": "Verilerinizi korumak için 3-2-1 yedekleme stratejisi önerilir. Kapsamlı bir yedekleme çözümü için, yüklediğiniz fotoğrafların/videoların yanı sıra Immich veritabanının kopyalarını da saklamalısınız.", + "backup_onboarding_footer": "Immich'i yedekleme hakkında daha fazla bilgi için lütfen belgelere bakın.", + "backup_onboarding_parts_title": "3-2-1 yedekleme şunları içerir:", + "backup_onboarding_title": "Yedeklemeler", "backup_settings": "Veritabanı yığını ayarları", "backup_settings_description": "Veritabanı döküm ayarlarını yönet.", "cleared_jobs": "{job} için işler temizlendi", @@ -166,6 +174,20 @@ "metadata_settings_description": "Metaveri ayarlarını yönet", "migration_job": "Birleştirme", "migration_job_description": "Varlıklar ve yüzler için resim çerçeve önizlemelerini en yeni klasör yapısına aktar", + "nightly_tasks_cluster_faces_setting_description": "Yeni algılanan yüzlerde yüz tanıma işlemini çalıştırın", + "nightly_tasks_cluster_new_faces_setting": "Yeni yüzleri bir araya getirin", + "nightly_tasks_database_cleanup_setting": "Veritabanı temizleme görevleri", + "nightly_tasks_database_cleanup_setting_description": "Veritabanından eski, süresi dolmuş verileri temizleyin", + "nightly_tasks_generate_memories_setting": "Anılar oluşturun", + "nightly_tasks_generate_memories_setting_description": "Varlıklardan yeni anılar yaratın", + "nightly_tasks_missing_thumbnails_setting": "Eksik küçük resimleri oluştur", + "nightly_tasks_missing_thumbnails_setting_description": "Küçük resim oluşturmak için küçük resim içermeyen varlıkları sıraya alın", + "nightly_tasks_settings": "Gece Görevleri Ayarları", + "nightly_tasks_settings_description": "Gece görevlerini yönet", + "nightly_tasks_start_time_setting": "Başlangıç saati", + "nightly_tasks_start_time_setting_description": "Sunucunun gece görevlerini çalıştırmaya başladığı saat", + "nightly_tasks_sync_quota_usage_setting": "Kota kullanımını senkronize et", + "nightly_tasks_sync_quota_usage_setting_description": "Mevcut kullanıma göre kullanıcı depolama kotasını güncelle", "no_paths_added": "Yol eklenmedi", "no_pattern_added": "Desen eklenmedi", "note_apply_storage_label_previous_assets": "Not: Daha önce yüklenen varlıklara Depolama Etiketi uygulamak için şu komutu çalıştırın", @@ -196,6 +218,8 @@ "oauth_mobile_redirect_uri": "Mobil yönlendirme URL'si", "oauth_mobile_redirect_uri_override": "Mobilde zorla kullanılacak Yönlendirme Adresi", "oauth_mobile_redirect_uri_override_description": "Mobil URI'ye izin vermeyen OAuth sağlayıcısı olduğunda etkinleştir, örneğin ''{callback}''", + "oauth_role_claim": "Rol Talebi", + "oauth_role_claim_description": "Bu iddianın varlığına göre otomatik olarak yönetici erişimi verin. İddia, 'kullanıcı' veya 'yönetici' olabilir.", "oauth_settings": "OAuth", "oauth_settings_description": "OAuth giriş ayarlarını yönet", "oauth_settings_more_details": "Bu özellik hakkında daha fazla bilgi için bu sayfayı ziyaret edin Dökümanlar.", @@ -357,10 +381,12 @@ "admin_password": "Yönetici Şifresi", "administration": "Yönetim", "advanced": "Gelişmiş", + "advanced_settings_beta_timeline_subtitle": "Yeni uygulama deneyimini deneyin", + "advanced_settings_beta_timeline_title": "Beta Zaman Çizelgesi", "advanced_settings_enable_alternate_media_filter_subtitle": "Eşleme sırasında medyayı alternatif ölçütlere göre süzgeçten geçirmek için bu seçeneği kullanın. Uygulamanın tüm albümleri algılamasında sorun yaşıyorsanız yalnızca bu durumda deneyin.", "advanced_settings_enable_alternate_media_filter_title": "[DENEYSEL] Alternatif cihaz albüm eşleme süzgeci kullanın", "advanced_settings_log_level_title": "Günlük düzeyi: {level}", - "advanced_settings_prefer_remote_subtitle": "Bazı cihazlar, cihazdaki öğelerin küçük resimlerini göstermekte çok yavaştır. Bunun yerine sunucudaki küçük resimleri göstermek için bu ayarı etkinleştirin.", + "advanced_settings_prefer_remote_subtitle": "Bazı cihazlar yerel varlıklardan küçük resimleri yüklerken çok yavaş çalışır. Bu ayarı etkinleştirerek uzak görüntüleri yükleyin.", "advanced_settings_prefer_remote_title": "Uzak görüntüleri tercih et", "advanced_settings_proxy_headers_subtitle": "Immich'in her ağ isteğiyle birlikte göndermesi gereken proxy header'ları tanımlayın", "advanced_settings_proxy_headers_title": "Proxy Header'lar", @@ -379,6 +405,7 @@ "album_cover_updated": "Albüm Kapağı güncellendi", "album_delete_confirmation": "{album} albümünü silmek istediğinize emin misiniz?", "album_delete_confirmation_description": "Albüm paylaşılıyorsa, diğer kullanıcılar artık bu albüme erişemeyecektir.", + "album_deleted": "Albüm silindi", "album_info_card_backup_album_excluded": "HARİÇ", "album_info_card_backup_album_included": "DAHİL", "album_info_updated": "Albüm bilgisi güncellendi", @@ -388,6 +415,7 @@ "album_options": "Albüm seçenekleri", "album_remove_user": "Kullanıcıyı kaldır?", "album_remove_user_confirmation": "{user} kullanıcısını kaldırmak istediğinize emin misiniz?", + "album_search_not_found": "Aramanızla eşleşen albüm bulunamadı", "album_share_no_users": "Görünüşe göre bu albümü tüm kullanıcılarla paylaştınız veya paylaşacak herhangi bir başka kullanıcınız yok.", "album_updated": "Albüm güncellendi", "album_updated_setting_description": "Paylaşılan bir albüme yeni bir varlık eklendiğinde email bildirimi alın", @@ -407,6 +435,7 @@ "albums_default_sort_order": "Varsayılan albüm sıralama düzeni", "albums_default_sort_order_description": "Yeni albüm oluştururken kullanılacak başlangıç varlık sıralama düzeni.", "albums_feature_description": "Diğer kullanıcılarla paylaşılabilen varlık koleksiyonları.", + "albums_on_device_count": "Cihazdaki albümler ({count})", "all": "Tümü", "all_albums": "Tüm Albümler", "all_people": "Tüm Kişiler", @@ -490,6 +519,7 @@ "back_close_deselect": "Geri, kapat veya seçimi kaldır", "background_location_permission": "Arka plan konum izni", "background_location_permission_content": "Arka planda çalışırken ağ değiştirmek için Immich'in *her zaman* tam konum erişimine sahip olması gerekir, böylece uygulama Wi-Fi ağının adını okuyabilir", + "backup": "Yedekle", "backup_album_selection_page_albums_device": "Cihazdaki albümler ({count})", "backup_album_selection_page_albums_tap": "Seçmek için dokunun, hariç tutmak için çift dokunun", "backup_album_selection_page_assets_scatter": "Varlıklar birden fazla albüme dağılabilir. Bu nedenle, yedekleme işlemi sırasında albümler dahil edilebilir veya hariç tutulabilir.", @@ -553,6 +583,8 @@ "backup_options_page_title": "Yedekleme seçenekleri", "backup_setting_subtitle": "Arka planda ve ön planda yükleme ayarlarını düzenle", "backward": "Geriye doğru", + "beta_sync": "Beta Senkronizasyon Durumu", + "beta_sync_subtitle": "Yeni senkronizasyon sistemini yönetin", "biometric_auth_enabled": "Biyometrik kimlik doğrulama etkin", "biometric_locked_out": "Biyometrik kimlik doğrulaması kilitli", "biometric_no_options": "Biyometrik seçenek yok", @@ -570,7 +602,7 @@ "cache_settings_clear_cache_button": "Önbelleği temizle", "cache_settings_clear_cache_button_title": "Uygulamanın önbelleğini temizleyin. Önbellek yeniden oluşturulana kadar uygulamanın performansını önemli ölçüde etkileyecektir.", "cache_settings_duplicated_assets_clear_button": "TEMİZLE", - "cache_settings_duplicated_assets_subtitle": "Uygulama tarafından kara listeye alınan öğeler", + "cache_settings_duplicated_assets_subtitle": "Uygulama tarafından yok sayılan fotoğraflar ve videolar", "cache_settings_duplicated_assets_title": "Yinelenen Öğeler ({count})", "cache_settings_statistics_album": "Kütüphane küçük resimleri", "cache_settings_statistics_full": "Tam çözünürlükte resimler", @@ -587,6 +619,7 @@ "cancel": "İptal", "cancel_search": "Aramayı iptal et", "canceled": "İptal edildi", + "canceling": "Vazgeçmek", "cannot_merge_people": "Kişiler birleştirilemiyor", "cannot_undo_this_action": "Bu işlem geri alınamaz!", "cannot_update_the_description": "Açıklama güncellenemiyor", @@ -700,6 +733,7 @@ "current_server_address": "Mevcut sunucu adresi", "custom_locale": "Özel Yerel Ayar", "custom_locale_description": "Tarihleri ve sayıları dile ve bölgeye göre biçimlendirin", + "custom_url": "Özel URL", "daily_title_text_date": "dd MMM E", "daily_title_text_date_year": "dd MMM yyyy E", "dark": "Koyu", @@ -719,7 +753,8 @@ "default_locale": "Varsayılan Yerel Ayar", "default_locale_description": "Tarihleri ve sayıları tarayıcınızın yerel ayarına göre biçimlendirin", "delete": "Sil", - "delete_action_prompt": "{count} kalıcı olarak silindi", + "delete_action_confirmation_message": "Bu varlığı silmek istediğinizden emin misiniz? Bu işlem, varlığı sunucunun çöp kutusuna taşıyacak ve yerel olarak silmek isteyip istemediğinizi soracaktır", + "delete_action_prompt": "{count} silindi", "delete_album": "Albümü sil", "delete_api_key_prompt": "Bu API anahtarını silmek istediğinizden emin misiniz?", "delete_dialog_alert": "Bu öğeler cihazınızdan ve Immich'ten kalıcı olarak silinecektir", @@ -737,6 +772,8 @@ "delete_local_dialog_ok_backed_up_only": "Sadece Yedeklenmişleri Sil", "delete_local_dialog_ok_force": "Yine de Sil", "delete_others": "Diğerlerini sil", + "delete_permanently": "Kalıcı olarak sil", + "delete_permanently_action_prompt": "{count} kalıcı olarak silindi", "delete_shared_link": "Paylaşılmış linki sil", "delete_shared_link_dialog_title": "Paylaşılan Bağlantı Sil", "delete_tag": "Etiketi sil", @@ -747,6 +784,7 @@ "description": "Açıklama", "description_input_hint_text": "Açıklama ekle...", "description_input_submit_error": "Açıklama güncellenirken hata oluştu, daha fazla ayrıntı için günlüğü kontrol edin", + "deselect_all": "Tümünü Seçimi Kaldır", "details": "Detaylar", "direction": "Yön", "disabled": "Devre dışı bırakıldı", @@ -764,6 +802,7 @@ "documentation": "Dokümantasyon", "done": "Bitti", "download": "İndir", + "download_action_prompt": "{count} varlık indiriliyor", "download_canceled": "İndirme iptal edildi", "download_complete": "İndirme tamamlandı", "download_enqueue": "İndirme sıraya alındı", @@ -790,6 +829,7 @@ "edit": "Düzenle", "edit_album": "Albümü düzenle", "edit_avatar": "Avatarı Düzenle", + "edit_birthday": "Doğum Günü Düzenle", "edit_date": "Tarihi Düzenle", "edit_date_and_time": "Tarih ve zamanı düzenleyin", "edit_description": "Açıklamayı düzenle", @@ -801,6 +841,7 @@ "edit_key": "Anahtarı düzenle", "edit_link": "Bağlantıyı düzenle", "edit_location": "Lokasyonu düzenleyin", + "edit_location_action_prompt": "{count} konum düzenlendi", "edit_location_dialog_title": "Konum", "edit_name": "İsmi düzenleyin", "edit_people": "Kişileri düzenle", @@ -819,6 +860,7 @@ "empty_trash": "Çöpü boşalt", "empty_trash_confirmation": "Çöp kutusunu boşaltmak istediğinizden emin misiniz? Bu işlem, Immich'teki çöp kutusundaki tüm varlıkları kalıcı olarak silecektir.\nBu işlemi geri alamazsınız!", "enable": "Etkinleştir", + "enable_backup": "Yedeklemeyi Etkinleştir", "enable_biometric_auth_description": "Biyometrik kimlik doğrulamasını etkinleştirmek için PIN kodu girin", "enabled": "Etkinleştirildi", "end_date": "Bitiş tarihi", @@ -955,6 +997,7 @@ }, "exif": "EXIF", "exif_bottom_sheet_description": "Açıklama Ekle...", + "exif_bottom_sheet_description_error": "Açıklama güncelleme hatası", "exif_bottom_sheet_details": "DETAYLAR", "exif_bottom_sheet_location": "KONUM", "exif_bottom_sheet_people": "KİŞİLER", @@ -975,6 +1018,8 @@ "explorer": "Geçmiş", "export": "Dışa Aktar", "export_as_json": "JSON olarak Dışa Aktar", + "export_database": "Veritabanını Dışa Aktar", + "export_database_description": "SQLite veritabanını dışa aktarın", "extension": "Uzantı", "external": "Harici", "external_libraries": "Harici kütüphaneler", @@ -1026,6 +1071,9 @@ "haptic_feedback_switch": "Dokunsal geri bildirimi aç", "haptic_feedback_title": "Dokunsal Geri Bildirim (Haptic Feedback)", "has_quota": "Kota var", + "hash_asset": "Hash varlığı", + "hashed_assets": "Hashlenmiş varlıklar", + "hashing": "Hashleme", "header_settings_add_header_tip": "Header Ekle", "header_settings_field_validator_msg": "Değer boş olamaz", "header_settings_header_name_input": "Header adı", @@ -1058,6 +1106,7 @@ "host": "Ana bilgisayar", "hour": "Saat", "id": "ID", + "idle": "Boşta", "ignore_icloud_photos": "iCloud Fotoğraflarını Yok Say", "ignore_icloud_photos_description": "iCloud'a yüklenmiş fotoğraflar Immich sunucusuna yüklenmesin", "image": "Resim", @@ -1115,6 +1164,7 @@ "language_no_results_title": "Dil bulunamadı", "language_search_hint": "Dilleri ara...", "language_setting_description": "Tercih ettiğiniz dili seçiniz", + "large_files": "Büyük Dosyalar", "last_seen": "Son görülme", "latest_version": "En son versiyon", "latitude": "Enlem", @@ -1134,13 +1184,14 @@ "light": "Açık", "like_deleted": "Beğeni silindi", "link_motion_video": "Hareket videosunu bağla", - "link_options": "Bağlantı seçenekleri", "link_to_oauth": "OAuth'a bağla", "linked_oauth_account": "Bağlı OAuth hesabı", "list": "Liste", "loading": "Yükleniyor", "loading_search_results_failed": "Arama sonuçları yüklenemedi", + "local": "Yerel", "local_asset_cast_failed": "Sunucuya yüklenmemiş bir varlık yansıtılamaz", + "local_assets": "Yerel Varlıklar", "local_network": "Yerel Wi-Fi", "local_network_sheet_info": "Uygulama belirlenmiş Wi-Fi ağını kullanırken bu URL üzerinden sunucuya bağlanacaktır", "location_permission": "Konum izni", @@ -1197,7 +1248,6 @@ "manage_your_devices": "Cihazlarınızı yönetin", "manage_your_oauth_connection": "OAuth bağlantınızı yönetin", "map": "Harita", - "map_assets_in_bound": "{count} fotoğraf", "map_assets_in_bounds": "{count} fotoğraf", "map_cannot_get_user_location": "Kullanıcının konumu alınamıyor", "map_location_dialog_yes": "Evet", @@ -1250,6 +1300,7 @@ "more": "Daha fazla", "move": "Taşı", "move_off_locked_folder": "Kilitli klasörden taşı", + "move_to_lock_folder_action_prompt": "{count} kilitli klasöre eklendi", "move_to_locked_folder": "Kilitli klasöre taşı", "move_to_locked_folder_confirmation": "Bu fotoğraflar ve videolar tüm albümlerden kaldırılacak ve yalnızca kilitli klasörden görüntülenebilecektir", "moved_to_archive": "{count, plural, one {# öğe arşive taşındı} other {# öğe arşive taşındı}}", @@ -1296,6 +1347,7 @@ "no_results": "Sonuç bulunamadı", "no_results_description": "Eş anlamlı ya da daha genel anlamlı bir kelime deneyin", "no_shared_albums_message": "Fotoğrafları ve videoları ağınızdaki kişilerle paylaşmak için bir albüm oluşturun", + "no_uploads_in_progress": "Yükleme işlemi yok", "not_in_any_album": "Hiçbir albümde değil", "not_selected": "Seçilmedi", "note_apply_storage_label_to_previously_uploaded assets": "Not: Daha önce yüklenen varlıklar için bir depolama yolu etiketi uygulamak üzere şunu başlatın", @@ -1333,6 +1385,7 @@ "original": "orijinal", "other": "Diğer", "other_devices": "Diğer cihazlar", + "other_entities": "Diğer kuruluşlar", "other_variables": "Diğer değişkenler", "owned": "Sahip olunan", "owner": "Sahip", @@ -1464,6 +1517,7 @@ "purchase_server_description_2": "Destekçi statüsü", "purchase_server_title": "Sunucu", "purchase_settings_server_activated": "Sunucu ürün anahtarı, yönetici tarafından yönetilir", + "queue_status": "Sırada {count}/{total}", "rating": "Derecelendirme", "rating_clear": "Derecelendirmeyi temizle", "rating_count": "{count, plural, one {# yıldız} other {# yıldız}}", @@ -1492,6 +1546,8 @@ "refreshing_faces": "Yüzler yenileniyor", "refreshing_metadata": "Meta veriler yenileniyor", "regenerating_thumbnails": "Küçük resimler yeniden oluşturuluyor", + "remote": "Uzaktan", + "remote_assets": "Uzak Varlıklar", "remove": "Kaldır", "remove_assets_album_confirmation": "{count, plural, one {# dosyayı} other {# dosyayı}} albümden çıkarmak istediğinizden emin misiniz?", "remove_assets_shared_link_confirmation": "{count, plural, one {# dosyayı} other {# dosyayı}} bu paylaşılan bağlantıdan çıkarmak istediğinizden emin misiniz?", @@ -1529,19 +1585,25 @@ "reset_password": "Şifreyi sıfırla", "reset_people_visibility": "Kişilerin görünürlüğünü sıfırla", "reset_pin_code": "PIN kodunu sıfırlayın", + "reset_sqlite": "SQLite Veritabanını Sıfırla", + "reset_sqlite_confirmation": "SQLite veritabanını sıfırlamak istediğinizden emin misiniz? Verileri yeniden senkronize etmek için oturumu kapatıp tekrar oturum açmanız gerekecektir", + "reset_sqlite_success": "SQLite veritabanını başarıyla sıfırladınız", "reset_to_default": "Varsayılana sıfırla", "resolve_duplicates": "Çiftleri çöz", "resolved_all_duplicates": "Tüm çiftler çözüldü", "restore": "Geri yükle", "restore_all": "Tümünü geri yükle", + "restore_trash_action_prompt": "{count} çöp kutusundan geri yüklendi", "restore_user": "Kullanıcıyı geri yükle", "restored_asset": "Dosya geri yüklendi", "resume": "Devam et", "retry_upload": "Yeniden yüklemeyi dene", "review_duplicates": "Çiftleri gözden geçir", + "review_large_files": "Büyük dosyaları inceleyin", "role": "Rol", "role_editor": "Düzenleyici", "role_viewer": "Görüntüleyici", + "running": "Çalışıyor", "save": "Kaydet", "save_to_gallery": "Fotoğraflar'a kaydet", "saved_api_key": "API anahtarı kaydedildi", @@ -1673,6 +1735,7 @@ "settings_saved": "Ayarlar kaydedildi", "setup_pin_code": "PIN kodunu ayarlayın", "share": "Paylaş", + "share_action_prompt": "Paylaşılan {count} varlık", "share_add_photos": "Fotoğraf ekle", "share_assets_selected": "{count} seçili", "share_dialog_preparing": "Hazırlanıyor...", @@ -1694,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "Panoya kopyalandı", "shared_link_clipboard_text": "Bağlantı: {link}\nParola: {password}", "shared_link_create_error": "Paylaşım bağlantısı oluşturulurken hata oluştu", + "shared_link_custom_url_description": "Özel bir URL ile bu paylaşılan bağlantıya erişin", "shared_link_edit_description_hint": "Açıklama yazın", "shared_link_edit_expire_after_option_day": "1 gün", "shared_link_edit_expire_after_option_days": "{count} gün", @@ -1719,6 +1783,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Paylaşılan Bağlantıları Yönet", "shared_link_options": "Paylaşılan bağlantı seçenekleri", + "shared_link_password_description": "Bu paylaşılan bağlantıya erişmek için şifre gerektirir", "shared_links": "Paylaşılan bağlantılar", "shared_links_description": "Fotoğraf ve videoları bir bağlantı ile paylaş", "shared_photos_and_videos_count": "{assetCount, plural, one {# paylaşılan fotoğraf veya video.} other {# paylaşılan fotoğraf & video.}}", @@ -1774,6 +1839,7 @@ "sort_title": "Başlık", "source": "Kaynak", "stack": "Yığın", + "stack_action_prompt": "{count} istiflenmiş", "stack_duplicates": "Çiftleri yığınla", "stack_select_one_photo": "Yığın için ana fotoğrafı seç", "stack_selected_photos": "Seçili fotoğrafları yığınla", @@ -1793,6 +1859,7 @@ "storage_quota": "Depolama Kotası", "storage_usage": "{used} / {available} kullanıldı", "submit": "Gönder", + "success": "Başarılı", "suggestions": "Öneriler", "sunrise_on_the_beach": "Plajda gün doğumu", "support": "Destek", @@ -1802,6 +1869,8 @@ "sync": "Senkronize et", "sync_albums": "Albümleri eşzamanla", "sync_albums_manual_subtitle": "Yüklenmiş fotoğraf ve videoları yedekleme için seçili albümler ile eşzamanlayın", + "sync_local": "Yerel Senkronizasyon", + "sync_remote": "Uzaktan Senkronizasyon", "sync_upload_album_setting_subtitle": "Seçili albümleri Immich'te oluşturun ve içindekileri Immich'e yükleyin.", "tag": "Etiket", "tag_assets": "Dosyaları etiketle", @@ -1812,6 +1881,7 @@ "tag_updated": "Etiket güncellendi: {tag}", "tagged_assets": "{count, plural, one {# dosya} other {# dosya}} etiketlendi", "tags": "Etiketler", + "tap_to_run_job": "Başlatmak için dokunun", "template": "Şablon", "theme": "Tema", "theme_selection": "Tema seçimi", @@ -1844,6 +1914,7 @@ "total": "Toplam", "total_usage": "Toplam kullanım", "trash": "Çöp", + "trash_action_prompt": "{count} çöp kutusuna taşındı", "trash_all": "Hepsini sil", "trash_count": "Çöp kutusu {count, number}", "trash_delete_asset": "Ögeyi Sil/Çöpe gönder", @@ -1861,9 +1932,11 @@ "unable_to_change_pin_code": "PIN kodu değiştirilemedi", "unable_to_setup_pin_code": "PIN kodu ayarlanamadı", "unarchive": "Arşivden çıkar", + "unarchive_action_prompt": "{count} Arşivden kaldırıldı", "unarchived_count": "{count, plural, other {# arşivden çıkarıldı}}", "undo": "Geri al", "unfavorite": "Gözdelerden kaldır", + "unfavorite_action_prompt": "{count} Sık Kullanılanlar'dan kaldırıldı", "unhide_person": "Kişiyi göster", "unknown": "Bilinmeyen", "unknown_country": "Bilinmeyen ülke", @@ -1881,15 +1954,20 @@ "unselect_all_duplicates": "Tüm çiftlerin seçimini kaldır", "unselect_all_in": "{group} içindeki tüm seçimleri kaldır", "unstack": "Yığını kaldır", + "unstack_action_prompt": "{count} istiflenmemiş", "unstacked_assets_count": "{count, plural, one {# dosya} other {# dosya}} yığını kaldırıldı", + "untagged": "Etiketlenmemiş", "up_next": "Sıradaki", "updated_at": "Güncellenme", "updated_password": "Şifreyi güncelle", "upload": "Yükle", + "upload_action_prompt": "{count} yükleme için sıraya alındı", "upload_concurrency": "Yükleme eşzamanlılığı", + "upload_details": "Yükleme Ayrıntıları", "upload_dialog_info": "Seçili öğeleri sunucuya yedeklemek istiyor musunuz?", "upload_dialog_title": "Öğe Yükle", "upload_errors": "{count, plural, one {# hata} other {# hatayla}} yükleme tamamlandı, yeni yüklenen dosyaları görmek için sayfayı güncelleyin.", + "upload_finished": "Yükleme tamamlandı", "upload_progress": "{remaining, number} kalan - {processed, number}/{total, number} işlendi", "upload_skipped_duplicates": "{count, plural, one {# çift dosya} other {# çift dosya}} atlandı", "upload_status_duplicates": "Çiftler", @@ -1898,6 +1976,7 @@ "upload_success": "Yükleme başarılı, yüklenen yeni ögeleri görebilmek için sayfayı yenileyin.", "upload_to_immich": "Immich'e Yükle ({count})", "uploading": "Yükleniyor", + "uploading_media": "Medya yükleme", "url": "URL", "usage": "Kullanım", "use_biometric": "Biyometri kullan", @@ -1918,6 +1997,7 @@ "user_usage_stats_description": "hesap kullanım istatistiklerini göster", "username": "Kullanıcı adı", "users": "Kullanıcılar", + "users_added_to_album_count": "Albüme {count, plural, one {# user} other {# users}} eklendi", "utilities": "Yardımcılar", "validate": "Doğrula", "validate_endpoint_error": "Lütfen geçerli bir URL girin", @@ -1936,6 +2016,7 @@ "view_album": "Albümü görüntüle", "view_all": "Tümünü gör", "view_all_users": "Tüm kullanıcıları görüntüle", + "view_details": "Ayrıntıları Görüntüle", "view_in_timeline": "Zaman çizelgesinde görüntüle", "view_link": "Bağlantıyı göster", "view_links": "Bağlantıları göster", diff --git a/i18n/uk.json b/i18n/uk.json index 8a375e627a..ca5e834991 100644 --- a/i18n/uk.json +++ b/i18n/uk.json @@ -10,14 +10,14 @@ "activity": "Активність", "activity_changed": "Активність {enabled, select, true {увімкнено} other {вимкнено}}", "add": "Додати", - "add_a_description": "Додади опис", + "add_a_description": "Додати опис", "add_a_location": "Додати місцезнаходження", "add_a_name": "Додати ім'я", "add_a_title": "Додати назву", "add_endpoint": "Додати кінцеву точку", - "add_exclusion_pattern": "Додайте шаблон виключення", + "add_exclusion_pattern": "Додати шаблон виключення", "add_import_path": "Додати шлях імпорту", - "add_location": "Додайте місцезнаходження", + "add_location": "Додати місцезнаходження", "add_more_users": "Додати користувачів", "add_partner": "Додати партнера", "add_path": "Додати шлях", @@ -489,6 +489,7 @@ "back_close_deselect": "Повернутися, закрити або скасувати вибір", "background_location_permission": "Дозвіл до місцезнаходження у фоні", "background_location_permission_content": "Щоб перемикати мережі у фоновому режимі, Immich має *завжди* мати доступ до точної геолокації, щоб зчитувати назву Wi-Fi мережі", + "backup": "Резервне копіювання", "backup_album_selection_page_albums_device": "Альбоми на пристрої ({count})", "backup_album_selection_page_albums_tap": "Торкніться, щоб включити, двічі, щоб виключити", "backup_album_selection_page_assets_scatter": "Елементи можуть належати до кількох альбомів водночас. Таким чином, альбоми можуть бути включені або вилучені під час резервного копіювання.", @@ -1128,7 +1129,6 @@ "light": "Світла", "like_deleted": "Лайк видалено", "link_motion_video": "Посилання на рухоме відео", - "link_options": "Налаштування посилання", "link_to_oauth": "Приєднання до OAuth", "linked_oauth_account": "Приєднаний акаунт OAuth", "list": "Перелік", @@ -1191,7 +1191,6 @@ "manage_your_devices": "Керуйте пристроями, які увійшли в систему", "manage_your_oauth_connection": "Налаштування підключеного OAuth", "map": "Мапа", - "map_assets_in_bound": "{count} фото", "map_assets_in_bounds": "{count} фото", "map_cannot_get_user_location": "Не можу отримати місцезнаходження", "map_location_dialog_yes": "Так", @@ -1566,8 +1565,8 @@ "search_filter_filename": "Пошук за назвою файлу", "search_filter_location": "Місцезнаходження", "search_filter_location_title": "Виберіть місцезнаходження", - "search_filter_media_type": "Тип носія", - "search_filter_media_type_title": "Виберіть тип носія", + "search_filter_media_type": "Тип медіа", + "search_filter_media_type_title": "Виберіть тип медіа", "search_filter_people_title": "Виберіть людей", "search_for": "Шукати для", "search_for_existing_person": "Пошук існуючої особи", diff --git a/i18n/vi.json b/i18n/vi.json index 10c883dc70..ad1738908b 100644 --- a/i18n/vi.json +++ b/i18n/vi.json @@ -485,6 +485,7 @@ "back_close_deselect": "Quay lại, đóng, hoặc bỏ chọn", "background_location_permission": "Quyền truy cập vị trí ở nền", "background_location_permission_content": "Để chuyển đổi mạng khi chạy ở chế độ nền, Immich *luôn* phải có quyền truy cập vị trí chính xác để ứng dụng có thể đọc tên mạng Wi-Fi", + "backup": "Sao lưu", "backup_album_selection_page_albums_device": "Album trên thiết bị ({count})", "backup_album_selection_page_albums_tap": "Nhấn để chọn, nhấn đúp để bỏ qua", "backup_album_selection_page_assets_scatter": "Ảnh có thể có trong nhiều album khác nhau. Trong quá trình sao lưu, bạn có thể chọn để sao lưu tất cả các album hoặc chỉ một số album nhất định.", @@ -1122,7 +1123,6 @@ "light": "Sáng", "like_deleted": "Đã xoá thích", "link_motion_video": "Liên kết video chuyển động", - "link_options": "Tùy chọn liên kết", "link_to_oauth": "Liên kết đến OAuth", "linked_oauth_account": "Tài khoản OAuth đã liên kết", "list": "Danh sách", @@ -1183,7 +1183,6 @@ "manage_your_devices": "Quản lý các thiết bị đã đăng nhập của bạn", "manage_your_oauth_connection": "Quản lý kết nối OAuth của bạn", "map": "Bản đồ", - "map_assets_in_bound": "{count} ảnh", "map_assets_in_bounds": "{count} ảnh", "map_cannot_get_user_location": "Không thể xác định vị trí của bạn", "map_location_dialog_yes": "Có", diff --git a/i18n/zh_Hant.json b/i18n/zh_Hant.json index 334da7bfb1..b2acdc02eb 100644 --- a/i18n/zh_Hant.json +++ b/i18n/zh_Hant.json @@ -166,6 +166,19 @@ "metadata_settings_description": "管理詮釋資料設定", "migration_job": "遷移", "migration_job_description": "將項目和臉孔的縮圖移到新的延伸資料夾", + "nightly_tasks_cluster_faces_setting_description": "對新偵測到的臉孔進行辨識", + "nightly_tasks_database_cleanup_setting": "資料庫清理作業", + "nightly_tasks_database_cleanup_setting_description": "清除資料庫中舊的與已過期的資料", + "nightly_tasks_generate_memories_setting": "產生回憶", + "nightly_tasks_generate_memories_setting_description": "從項目建立新回憶", + "nightly_tasks_missing_thumbnails_setting": "產生缺少的縮圖", + "nightly_tasks_missing_thumbnails_setting_description": "將沒有縮圖的項目加入縮圖產生佇列", + "nightly_tasks_settings": "夜間任務設定", + "nightly_tasks_settings_description": "管理夜間任務", + "nightly_tasks_start_time_setting": "開始時間", + "nightly_tasks_start_time_setting_description": "伺服器開始執行夜間任務的時間", + "nightly_tasks_sync_quota_usage_setting": "同步配額使用量", + "nightly_tasks_sync_quota_usage_setting_description": "根據當前使用情況更新使用者儲存配額", "no_paths_added": "未添加路徑", "no_pattern_added": "未添加pattern", "note_apply_storage_label_previous_assets": "*註:執行套用儲存標籤前先上傳項目", @@ -357,6 +370,8 @@ "admin_password": "管理者密碼", "administration": "管理", "advanced": "進階", + "advanced_settings_beta_timeline_subtitle": "立即體驗新版應用程式", + "advanced_settings_beta_timeline_title": "測試版時間軸", "advanced_settings_enable_alternate_media_filter_subtitle": "使用此選項可在同步時依照替代條件篩選媒體。僅當應用程式在偵測所有相簿時出現問題時才建議使用。", "advanced_settings_enable_alternate_media_filter_title": "[實驗]使用其他的裝置相簿同步篩選器", "advanced_settings_log_level_title": "日誌等級:{level}", @@ -379,6 +394,7 @@ "album_cover_updated": "已更新相簿封面", "album_delete_confirmation": "確定要刪除「{album}」(相簿)嗎?", "album_delete_confirmation_description": "如果已分享此相簿,其他使用者就無法再存取這本相簿了。", + "album_deleted": "相簿已刪除", "album_info_card_backup_album_excluded": "已排除", "album_info_card_backup_album_included": "已選中", "album_info_updated": "已更新相簿資訊", @@ -388,6 +404,7 @@ "album_options": "相簿選項", "album_remove_user": "移除使用者?", "album_remove_user_confirmation": "確定要移除 {user} 嗎?", + "album_search_not_found": "找不到符合搜尋條件的相簿", "album_share_no_users": "看來您與所有使用者共享了這本相簿,或沒有其他使用者可供分享。", "album_updated": "更新相簿時", "album_updated_setting_description": "當共享相簿有新項目時用電子郵件通知我", @@ -486,6 +503,7 @@ "back_close_deselect": "返回、關閉及取消選取", "background_location_permission": "背景存取位置權限", "background_location_permission_content": "開啟背景執行時自動切換網路,請充許 Immich 一律充許使用精確位置權限,以確認 Wi-Fi 網路名稱", + "backup": "備份", "backup_album_selection_page_albums_device": "裝置上的相簿({count})", "backup_album_selection_page_albums_tap": "點擊選取,連續點擊兩次取消", "backup_album_selection_page_assets_scatter": "項目會分散在不同相簿。因此,可以設定要備份的相簿。", @@ -549,6 +567,8 @@ "backup_options_page_title": "備份選項", "backup_setting_subtitle": "管理背景與前景上傳設定", "backward": "倒轉", + "beta_sync": "測試版同步狀態", + "beta_sync_subtitle": "管理新的同步系統", "biometric_auth_enabled": "生物辨識驗證已啟用", "biometric_locked_out": "您已被鎖定無法使用生物辨識驗證", "biometric_no_options": "無生物辨識選項可用", @@ -583,6 +603,7 @@ "cancel": "取消", "cancel_search": "取消搜尋", "canceled": "已取消", + "canceling": "取消中", "cannot_merge_people": "無法合併人物", "cannot_undo_this_action": "此步驟無法取消喔!", "cannot_update_the_description": "無法更新描述", @@ -698,6 +719,7 @@ "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "YYYY年M月D日 (E)", "dark": "深色", + "dark_theme": "切換深色主題", "date_after": "日期之後", "date_and_time": "日期與時間", "date_before": "日期之前", @@ -739,6 +761,7 @@ "description": "描述", "description_input_hint_text": "新增描述...", "description_input_submit_error": "更新描述時出錯,請檢查日誌以獲取更多詳細資訊", + "deselect_all": "取消全選", "details": "詳細資訊", "direction": "方向", "disabled": "停用", @@ -756,6 +779,7 @@ "documentation": "說明書", "done": "完成", "download": "下載", + "download_action_prompt": "正在下載 {count} 項目", "download_canceled": "下載已取消", "download_complete": "下載完成", "download_enqueue": "已加入下載隊列", @@ -811,6 +835,7 @@ "empty_trash": "清空垃圾桶", "empty_trash_confirmation": "確定要清空垃圾桶嗎?這會永久刪除 Immich 垃圾桶中所有的檔案。\n此步驟無法取消喔!", "enable": "啟用", + "enable_backup": "開啟備份", "enable_biometric_auth_description": "輸入您的 PIN 碼以啟用生物辨識驗證", "enabled": "己啟用", "end_date": "結束日期", @@ -967,6 +992,8 @@ "explorer": "總攬", "export": "匯出", "export_as_json": "匯出 JSON", + "export_database": "匯出資料庫", + "export_database_description": "匯出 SQLite 資料庫", "extension": "副檔名", "external": "外部", "external_libraries": "外部圖庫", @@ -978,6 +1005,7 @@ "failed_to_load_assets": "無法加載檔案", "failed_to_load_folder": "無法載入資料夾", "favorite": "收藏", + "favorite_action_prompt": "已新增 {count} 個到我的最愛", "favorite_or_unfavorite_photo": "收藏或取消收藏照片", "favorites": "收藏", "favorites_page_no_favorites": "未找到收藏項目", @@ -1016,6 +1044,8 @@ "haptic_feedback_switch": "啓用振動反饋", "haptic_feedback_title": "振動反饋", "has_quota": "配額", + "hash_asset": "雜湊項目", + "hashed_assets": "已雜湊項目", "header_settings_add_header_tip": "新增標頭", "header_settings_field_validator_msg": "設定不可為空", "header_settings_header_name_input": "標頭名稱", @@ -1114,12 +1144,13 @@ "light": "淺色", "like_deleted": "已刪除的收藏", "link_motion_video": "鏈結動態影片", - "link_options": "鏈結選項", "link_to_oauth": "連接 OAuth", "linked_oauth_account": "已連接 OAuth 帳號", "list": "列表", "loading": "載入中", "loading_search_results_failed": "載入搜尋結果失敗", + "local": "本地", + "local_assets": "本地項目", "local_network": "本地網路", "local_network_sheet_info": "當使用指定的 Wi-Fi 網路時,應用程式將透過此網址連線至伺服器", "location_permission": "位置權限", @@ -1175,7 +1206,6 @@ "manage_your_devices": "管理已登入的裝置", "manage_your_oauth_connection": "管理您的 OAuth 連接", "map": "地圖", - "map_assets_in_bound": "{count} 張照片", "map_assets_in_bounds": "{count} 張照片", "map_cannot_get_user_location": "無法獲取用戶位置", "map_location_dialog_yes": "確定", @@ -1274,6 +1304,7 @@ "no_results": "沒有結果", "no_results_description": "試試同義詞或更通用的關鍵字吧", "no_shared_albums_message": "建立相簿分享照片和影片", + "no_uploads_in_progress": "沒有正在上傳的項目", "not_in_any_album": "不在任何相簿中", "not_selected": "未選擇", "note_apply_storage_label_to_previously_uploaded assets": "*註:執行套用儲存標籤前先上傳項目", @@ -1465,6 +1496,8 @@ "refreshing_faces": "重整面部資料中", "refreshing_metadata": "正在重新整理詳細資料", "regenerating_thumbnails": "重新產生縮圖中", + "remote": "遠端", + "remote_assets": "遠端項目", "remove": "移除", "remove_assets_album_confirmation": "確定要從相簿中移除 {count, plural, other {# 個檔案}}嗎?", "remove_assets_shared_link_confirmation": "確定刪除共享連結中{count, plural, other {# 個項目}}嗎?", @@ -1500,6 +1533,8 @@ "reset_password": "重設密碼", "reset_people_visibility": "重設人物可見性", "reset_pin_code": "重置 PIN 碼", + "reset_sqlite": "重設 SQLite 資料庫", + "reset_sqlite_success": "已成功重設 SQLite 資料庫", "reset_to_default": "重設回預設", "resolve_duplicates": "解決重複項", "resolved_all_duplicates": "已解決所有重複項目", @@ -1864,13 +1899,14 @@ "upload_success": "上傳成功,要查看新上傳的檔案請重新整理頁面。", "upload_to_immich": "上傳至 Immich ({count})", "uploading": "上傳中", + "uploading_media": "媒體上傳中", "url": "網址", "usage": "用量", "use_biometric": "使用生物辨識", "use_current_connection": "使用目前的連線", "use_custom_date_range": "改用自訂日期範圍", "user": "使用者", - "user_has_been_deleted": "此用戶以被刪除", + "user_has_been_deleted": "此用戶已被刪除。", "user_id": "使用者 ID", "user_liked": "{user} 喜歡了 {type, select, photo {這張照片} video {這段影片} asset {這個檔案} other {它}}", "user_pin_code_settings": "PIN 碼", diff --git a/i18n/zh_SIMPLIFIED.json b/i18n/zh_SIMPLIFIED.json index bcbdcc5d6b..10aef204bd 100644 --- a/i18n/zh_SIMPLIFIED.json +++ b/i18n/zh_SIMPLIFIED.json @@ -53,7 +53,7 @@ "confirm_email_below": "请输入“{email}”以进行确认", "confirm_reprocess_all_faces": "确定要对全部照片重新进行面部识别吗?这将同时清除所有已命名人物。", "confirm_user_password_reset": "确定要重置用户“{user}”的密码吗?", - "confirm_user_pin_code_reset": "确定要重置{user}的PIN码吗?", + "confirm_user_pin_code_reset": "确定要重置用户“{user}”的PIN码吗?", "create_job": "创建任务", "cron_expression": "Cron 表达式", "cron_expression_description": "使用 Cron 格式设置扫描间隔。更多详细信息请参阅 Crontab Guru", @@ -389,7 +389,7 @@ "advanced_settings_tile_subtitle": "高级用户设置", "advanced_settings_troubleshooting_subtitle": "启用用于故障排除的额外功能", "advanced_settings_troubleshooting_title": "故障排除", - "age_months": "{months, plural, one {#月龄} other {#月龄}}", + "age_months": "{months, plural, one {#个月} other {#个月}}", "age_year_months": "1岁{months, plural, one {#个月} other {#个月}}", "age_years": "{years, plural, other {#岁}}", "album_added": "被添加到相册", @@ -397,6 +397,7 @@ "album_cover_updated": "相册封面已更新", "album_delete_confirmation": "确定要删除相册“{album}”吗?", "album_delete_confirmation_description": "如果该相册是共享的,其他用户将无法再访问它。", + "album_deleted": "相册已删除", "album_info_card_backup_album_excluded": "已排除", "album_info_card_backup_album_included": "已选中", "album_info_updated": "相册信息已更新", @@ -419,7 +420,7 @@ "album_viewer_appbar_share_err_title": "修改相册标题失败", "album_viewer_appbar_share_leave": "退出共享", "album_viewer_appbar_share_to": "共享给", - "album_viewer_page_share_add_users": "创建用户", + "album_viewer_page_share_add_users": "邀请他人", "album_with_link_access": "拥有此链接的任何人均可查看本相册中的照片和人物。", "albums": "相册", "albums_count": "{count, plural, one {{count, number} 个相册} other {{count, number} 个相册}}", @@ -510,6 +511,7 @@ "back_close_deselect": "返回、关闭或反选", "background_location_permission": "后台定位权限", "background_location_permission_content": "为了在后台运行时切换网络,Immich 必须*始终*拥有精确的位置访问权限,这样应用程序才能读取 Wi-Fi 网络的名称", + "backup": "备份", "backup_album_selection_page_albums_device": "设备上的相册({count})", "backup_album_selection_page_albums_tap": "单击选中,双击取消", "backup_album_selection_page_assets_scatter": "项目会分散在多个相册中。因此,可以在备份过程中包含或排除相册。", @@ -723,6 +725,7 @@ "current_server_address": "当前服务器地址", "custom_locale": "自定义地区", "custom_locale_description": "日期和数字显示格式跟随语言和地区", + "custom_url": "自定义URL", "daily_title_text_date": "MMM dd (E)", "daily_title_text_date_year": "YYYY年M月D日 (E)", "dark": "深色", @@ -742,9 +745,10 @@ "default_locale": "默认地区", "default_locale_description": "根据您的浏览器地区设置日期和数字显示格式", "delete": "删除", - "delete_action_prompt": "已永久删除 {count} 项", + "delete_action_confirmation_message": "您确定要删除此资产吗?此操作会将资产移至服务器回收站,并会提示您是否要在本地删除它", + "delete_action_prompt": "已删除 {count} 项", "delete_album": "删除相册", - "delete_api_key_prompt": "确定删除此 API 密钥吗?", + "delete_api_key_prompt": "是否确认删除此 API 密钥?", "delete_dialog_alert": "这些项目将从 Immich 和您的设备中永久删除", "delete_dialog_alert_local": "这些项目将从您的移动设备中永久删除,但仍然可以从 Immich 服务器中再次获取", "delete_dialog_alert_local_non_backed_up": "部分项目还未备份至 Immich 服务器,将从您的移动设备中永久删除", @@ -760,6 +764,8 @@ "delete_local_dialog_ok_backed_up_only": "仅删除已备份项目", "delete_local_dialog_ok_force": "确认删除", "delete_others": "删除其它", + "delete_permanently": "永久删除", + "delete_permanently_action_prompt": "已永久删除 {count} 项", "delete_shared_link": "删除共享链接", "delete_shared_link_dialog_title": "删除共享链接", "delete_tag": "删除标签", @@ -1002,6 +1008,8 @@ "explorer": "资源管理器", "export": "导出", "export_as_json": "导出为 JSON", + "export_database": "导出数据库", + "export_database_description": "导出 SQLite 数据库", "extension": "扩展", "external": "外部的", "external_libraries": "外部图库", @@ -1034,7 +1042,7 @@ "folders": "文件夹", "folders_feature_description": "在文件夹视图中浏览文件系统上的照片和视频", "forward": "向前", - "gcast_enabled": "Google Cast", + "gcast_enabled": "Google Cast 投屏", "gcast_enabled_description": "该功能需要加载来自 Google 的外部资源。", "general": "通用", "get_help": "获取帮助", @@ -1146,6 +1154,7 @@ "language_no_results_title": "未找到对应语言", "language_search_hint": "搜索语言...", "language_setting_description": "选择您的语言偏好", + "large_files": "大文件", "last_seen": "最后上线于", "latest_version": "最新版本", "latitude": "纬度", @@ -1165,7 +1174,6 @@ "light": "浅色", "like_deleted": "已删除的收藏", "link_motion_video": "链接动态视频", - "link_options": "链接选项", "link_to_oauth": "绑定 OAuth", "linked_oauth_account": "已绑定 OAuth 账户", "list": "列表", @@ -1230,8 +1238,7 @@ "manage_your_devices": "管理已登录设备", "manage_your_oauth_connection": "管理您的 OAuth 绑定", "map": "地图", - "map_assets_in_bound": "{count} 张照片", - "map_assets_in_bounds": "{count} 张照片", + "map_assets_in_bounds": "{count, plural, one {# 张照片} other {# 张照片}}", "map_cannot_get_user_location": "无法获取用户位置", "map_location_dialog_yes": "是", "map_location_picker_page_use_location": "使用此位置", @@ -1582,6 +1589,7 @@ "resume": "继续", "retry_upload": "重新上传", "review_duplicates": "检查重复项", + "review_large_files": "查看大文件", "role": "选择用户权限", "role_editor": "可编辑", "role_viewer": "仅查看", @@ -1739,6 +1747,7 @@ "shared_link_clipboard_copied_massage": "复制到剪贴板", "shared_link_clipboard_text": "链接:{link}\n密码:{password}", "shared_link_create_error": "创建共享链接出错", + "shared_link_custom_url_description": "使用自定义URL访问此共享链接", "shared_link_edit_description_hint": "编辑共享描述", "shared_link_edit_expire_after_option_day": "1天", "shared_link_edit_expire_after_option_days": "{count} 天", @@ -1764,6 +1773,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "管理共享链接", "shared_link_options": "共享链接选项", + "shared_link_password_description": "需要密码才能访问此共享链接", "shared_links": "共享链接", "shared_links_description": "通过链接分享照片和视频", "shared_photos_and_videos_count": "{assetCount, plural, other {#项已共享照片&视频。}}", @@ -1941,11 +1951,13 @@ "updated_at": "已更新", "updated_password": "更新密码", "upload": "上传", + "upload_action_prompt": "有{count}个待上传", "upload_concurrency": "上传并发", "upload_details": "上传详情", "upload_dialog_info": "是否要将所选项目备份到服务器?", "upload_dialog_title": "上传项目", "upload_errors": "上传完成,出现{count, plural, one {#个错误} other {#个错误}},刷新页面以查看新上传的项目。", + "upload_finished": "上传完成", "upload_progress": "剩余{remaining, number} - 已处理 {processed, number}/{total, number}", "upload_skipped_duplicates": "已跳过{count, plural, one {#个重复项} other {#个重复项}}", "upload_status_duplicates": "重复项", @@ -1954,6 +1966,7 @@ "upload_success": "上传成功,刷新页面查看新上传的项目。", "upload_to_immich": "上传至 Immich({count})", "uploading": "正在上传", + "uploading_media": "文件上传中", "url": "URL", "usage": "用量", "use_biometric": "使用生物识别", From f85d8add012ed40cc965f44a2b22872394dad698 Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Wed, 30 Jul 2025 23:18:18 +0530 Subject: [PATCH 124/748] fix: json encoding failure during live photo download (#20444) Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- mobile/lib/repositories/download.repository.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mobile/lib/repositories/download.repository.dart b/mobile/lib/repositories/download.repository.dart index 6f38a802e2..c4b31e9d93 100644 --- a/mobile/lib/repositories/download.repository.dart +++ b/mobile/lib/repositories/download.repository.dart @@ -21,7 +21,7 @@ class DownloadRepository { group: '', updates: Updates.statusAndProgress, ); - static final _dummyMetadata = {'part': LivePhotosPart.image, 'id': ''}; + static final _dummyMetadata = {'part': LivePhotosPart.image.index, 'id': ''}; void Function(TaskStatusUpdate)? onImageDownloadStatus; @@ -102,7 +102,7 @@ class DownloadRepository { continue; } - _dummyMetadata['part'] = LivePhotosPart.image; + _dummyMetadata['part'] = LivePhotosPart.image.index; _dummyMetadata['id'] = id; tasks[taskIndex++] = DownloadTask( taskId: id, @@ -114,7 +114,7 @@ class DownloadRepository { metaData: json.encode(_dummyMetadata), ); - _dummyMetadata['part'] = LivePhotosPart.video; + _dummyMetadata['part'] = LivePhotosPart.video.index; tasks[taskIndex++] = DownloadTask( taskId: livePhotoVideoId, url: url, From 641a3baaddada4fd90e11f8a15eea94641d32d4b Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Wed, 30 Jul 2025 23:59:00 +0530 Subject: [PATCH 125/748] fix(mobile): add partial index based on library ID to remote assets (#20214) * feat: add libraryId to SyncAssetV1 * add partial index # Conflicts: # mobile/drift_schemas/main/drift_schema_v5.json # mobile/lib/infrastructure/repositories/db.repository.dart # mobile/lib/infrastructure/repositories/db.repository.steps.dart # mobile/test/drift/main/generated/schema_v5.dart * chore: make build * rebase --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- .../drift_schemas/main/drift_schema_v6.json | 1 + .../entities/remote_asset.entity.dart | 14 +- .../entities/remote_asset.entity.drift.dart | 85 +- .../repositories/db.repository.dart | 11 +- .../repositories/db.repository.drift.dart | 4 +- .../repositories/db.repository.steps.dart | 394 + .../repositories/sync_stream.repository.dart | 1 + mobile/openapi/lib/model/sync_asset_v1.dart | 14 +- mobile/test/drift/main/generated/schema.dart | 5 +- .../test/drift/main/generated/schema_v6.dart | 6448 +++++++++++++++++ open-api/immich-openapi-specs.json | 5 + server/src/database.ts | 1 + server/src/dtos/sync.dto.ts | 1 + server/src/queries/sync.repository.sql | 5 + server/src/services/job.service.ts | 1 + .../specs/sync/sync-album-asset.spec.ts | 2 + .../test/medium/specs/sync/sync-asset.spec.ts | 2 + .../specs/sync/sync-partner-asset.spec.ts | 2 + 18 files changed, 6985 insertions(+), 11 deletions(-) create mode 100644 mobile/drift_schemas/main/drift_schema_v6.json create mode 100644 mobile/test/drift/main/generated/schema_v6.dart diff --git a/mobile/drift_schemas/main/drift_schema_v6.json b/mobile/drift_schemas/main/drift_schema_v6.json new file mode 100644 index 0000000000..adb2484006 --- /dev/null +++ b/mobile/drift_schemas/main/drift_schema_v6.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"has_profile_image","getter_name":"hasProfileImage","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"has_profile_image\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"has_profile_image\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"profile_changed_at","getter_name":"profileChangedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"library_id","getter_name":"libraryId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":5,"references":[3,4],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":6,"references":[3],"type":"index","data":{"on":3,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_owner_checksum","sql":null,"unique":false,"columns":["owner_id","checksum"]}},{"id":8,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum\nON remote_asset_entity (owner_id, checksum)\nWHERE (library_id IS NULL);\n","unique":true,"columns":[]}},{"id":9,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_library_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum\nON remote_asset_entity (owner_id, library_id, checksum)\nWHERE (library_id IS NOT NULL);\n","unique":true,"columns":[]}},{"id":10,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":11,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":12,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":13,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":14,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":15,"references":[1,14],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":16,"references":[14,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":18,"references":[1,17],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":19,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":20,"references":[1,19],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}}]} \ No newline at end of file diff --git a/mobile/lib/infrastructure/entities/remote_asset.entity.dart b/mobile/lib/infrastructure/entities/remote_asset.entity.dart index 7ee2e76ff6..ecc0aa3d76 100644 --- a/mobile/lib/infrastructure/entities/remote_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/remote_asset.entity.dart @@ -5,7 +5,17 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart'; import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; -@TableIndex(name: 'UQ_remote_asset_owner_checksum', columns: {#checksum, #ownerId}, unique: true) +@TableIndex(name: 'idx_remote_asset_owner_checksum', columns: {#ownerId, #checksum}) +@TableIndex.sql(''' +CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum +ON remote_asset_entity (owner_id, checksum) +WHERE (library_id IS NULL); +''') +@TableIndex.sql(''' +CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum +ON remote_asset_entity (owner_id, library_id, checksum) +WHERE (library_id IS NOT NULL); +''') @TableIndex(name: 'idx_remote_asset_checksum', columns: {#checksum}) class RemoteAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin { const RemoteAssetEntity(); @@ -30,6 +40,8 @@ class RemoteAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin TextColumn get stackId => text().nullable()(); + TextColumn get libraryId => text().nullable()(); + @override Set get primaryKey => {id}; } diff --git a/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart index 6bd416b259..9681d1e75d 100644 --- a/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart @@ -30,6 +30,7 @@ typedef $$RemoteAssetEntityTableCreateCompanionBuilder = i0.Value livePhotoVideoId, required i2.AssetVisibility visibility, i0.Value stackId, + i0.Value libraryId, }); typedef $$RemoteAssetEntityTableUpdateCompanionBuilder = i1.RemoteAssetEntityCompanion Function({ @@ -50,6 +51,7 @@ typedef $$RemoteAssetEntityTableUpdateCompanionBuilder = i0.Value livePhotoVideoId, i0.Value visibility, i0.Value stackId, + i0.Value libraryId, }); final class $$RemoteAssetEntityTableReferences @@ -189,6 +191,11 @@ class $$RemoteAssetEntityTableFilterComposer builder: (column) => i0.ColumnFilters(column), ); + i0.ColumnFilters get libraryId => $composableBuilder( + column: $table.libraryId, + builder: (column) => i0.ColumnFilters(column), + ); + i5.$$UserEntityTableFilterComposer get ownerId { final i5.$$UserEntityTableFilterComposer composer = $composerBuilder( composer: this, @@ -306,6 +313,11 @@ class $$RemoteAssetEntityTableOrderingComposer builder: (column) => i0.ColumnOrderings(column), ); + i0.ColumnOrderings get libraryId => $composableBuilder( + column: $table.libraryId, + builder: (column) => i0.ColumnOrderings(column), + ); + i5.$$UserEntityTableOrderingComposer get ownerId { final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder( composer: this, @@ -402,6 +414,9 @@ class $$RemoteAssetEntityTableAnnotationComposer i0.GeneratedColumn get stackId => $composableBuilder(column: $table.stackId, builder: (column) => column); + i0.GeneratedColumn get libraryId => + $composableBuilder(column: $table.libraryId, builder: (column) => column); + i5.$$UserEntityTableAnnotationComposer get ownerId { final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder( composer: this, @@ -481,6 +496,7 @@ class $$RemoteAssetEntityTableTableManager i0.Value visibility = const i0.Value.absent(), i0.Value stackId = const i0.Value.absent(), + i0.Value libraryId = const i0.Value.absent(), }) => i1.RemoteAssetEntityCompanion( name: name, type: type, @@ -499,6 +515,7 @@ class $$RemoteAssetEntityTableTableManager livePhotoVideoId: livePhotoVideoId, visibility: visibility, stackId: stackId, + libraryId: libraryId, ), createCompanionCallback: ({ @@ -519,6 +536,7 @@ class $$RemoteAssetEntityTableTableManager i0.Value livePhotoVideoId = const i0.Value.absent(), required i2.AssetVisibility visibility, i0.Value stackId = const i0.Value.absent(), + i0.Value libraryId = const i0.Value.absent(), }) => i1.RemoteAssetEntityCompanion.insert( name: name, type: type, @@ -537,6 +555,7 @@ class $$RemoteAssetEntityTableTableManager livePhotoVideoId: livePhotoVideoId, visibility: visibility, stackId: stackId, + libraryId: libraryId, ), withReferenceMapper: (p0) => p0 .map( @@ -607,9 +626,9 @@ typedef $$RemoteAssetEntityTableProcessedTableManager = i1.RemoteAssetEntityData, i0.PrefetchHooks Function({bool ownerId}) >; -i0.Index get uQRemoteAssetOwnerChecksum => i0.Index( - 'UQ_remote_asset_owner_checksum', - 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', +i0.Index get idxRemoteAssetOwnerChecksum => i0.Index( + 'idx_remote_asset_owner_checksum', + 'CREATE INDEX idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', ); class $RemoteAssetEntityTable extends i3.RemoteAssetEntity @@ -814,6 +833,17 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity type: i0.DriftSqlType.string, requiredDuringInsert: false, ); + static const i0.VerificationMeta _libraryIdMeta = const i0.VerificationMeta( + 'libraryId', + ); + @override + late final i0.GeneratedColumn libraryId = i0.GeneratedColumn( + 'library_id', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ name, @@ -833,6 +863,7 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity livePhotoVideoId, visibility, stackId, + libraryId, ]; @override String get aliasedName => _alias ?? actualTableName; @@ -950,6 +981,12 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity stackId.isAcceptableOrUnknown(data['stack_id']!, _stackIdMeta), ); } + if (data.containsKey('library_id')) { + context.handle( + _libraryIdMeta, + libraryId.isAcceptableOrUnknown(data['library_id']!, _libraryIdMeta), + ); + } return context; } @@ -1034,6 +1071,10 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity i0.DriftSqlType.string, data['${effectivePrefix}stack_id'], ), + libraryId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}library_id'], + ), ); } @@ -1073,6 +1114,7 @@ class RemoteAssetEntityData extends i0.DataClass final String? livePhotoVideoId; final i2.AssetVisibility visibility; final String? stackId; + final String? libraryId; const RemoteAssetEntityData({ required this.name, required this.type, @@ -1091,6 +1133,7 @@ class RemoteAssetEntityData extends i0.DataClass this.livePhotoVideoId, required this.visibility, this.stackId, + this.libraryId, }); @override Map toColumns(bool nullToAbsent) { @@ -1136,6 +1179,9 @@ class RemoteAssetEntityData extends i0.DataClass if (!nullToAbsent || stackId != null) { map['stack_id'] = i0.Variable(stackId); } + if (!nullToAbsent || libraryId != null) { + map['library_id'] = i0.Variable(libraryId); + } return map; } @@ -1166,6 +1212,7 @@ class RemoteAssetEntityData extends i0.DataClass serializer.fromJson(json['visibility']), ), stackId: serializer.fromJson(json['stackId']), + libraryId: serializer.fromJson(json['libraryId']), ); } @override @@ -1193,6 +1240,7 @@ class RemoteAssetEntityData extends i0.DataClass i1.$RemoteAssetEntityTable.$convertervisibility.toJson(visibility), ), 'stackId': serializer.toJson(stackId), + 'libraryId': serializer.toJson(libraryId), }; } @@ -1214,6 +1262,7 @@ class RemoteAssetEntityData extends i0.DataClass i0.Value livePhotoVideoId = const i0.Value.absent(), i2.AssetVisibility? visibility, i0.Value stackId = const i0.Value.absent(), + i0.Value libraryId = const i0.Value.absent(), }) => i1.RemoteAssetEntityData( name: name ?? this.name, type: type ?? this.type, @@ -1238,6 +1287,7 @@ class RemoteAssetEntityData extends i0.DataClass : this.livePhotoVideoId, visibility: visibility ?? this.visibility, stackId: stackId.present ? stackId.value : this.stackId, + libraryId: libraryId.present ? libraryId.value : this.libraryId, ); RemoteAssetEntityData copyWithCompanion(i1.RemoteAssetEntityCompanion data) { return RemoteAssetEntityData( @@ -1268,6 +1318,7 @@ class RemoteAssetEntityData extends i0.DataClass ? data.visibility.value : this.visibility, stackId: data.stackId.present ? data.stackId.value : this.stackId, + libraryId: data.libraryId.present ? data.libraryId.value : this.libraryId, ); } @@ -1290,7 +1341,8 @@ class RemoteAssetEntityData extends i0.DataClass ..write('deletedAt: $deletedAt, ') ..write('livePhotoVideoId: $livePhotoVideoId, ') ..write('visibility: $visibility, ') - ..write('stackId: $stackId') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') ..write(')')) .toString(); } @@ -1314,6 +1366,7 @@ class RemoteAssetEntityData extends i0.DataClass livePhotoVideoId, visibility, stackId, + libraryId, ); @override bool operator ==(Object other) => @@ -1335,7 +1388,8 @@ class RemoteAssetEntityData extends i0.DataClass other.deletedAt == this.deletedAt && other.livePhotoVideoId == this.livePhotoVideoId && other.visibility == this.visibility && - other.stackId == this.stackId); + other.stackId == this.stackId && + other.libraryId == this.libraryId); } class RemoteAssetEntityCompanion @@ -1357,6 +1411,7 @@ class RemoteAssetEntityCompanion final i0.Value livePhotoVideoId; final i0.Value visibility; final i0.Value stackId; + final i0.Value libraryId; const RemoteAssetEntityCompanion({ this.name = const i0.Value.absent(), this.type = const i0.Value.absent(), @@ -1375,6 +1430,7 @@ class RemoteAssetEntityCompanion this.livePhotoVideoId = const i0.Value.absent(), this.visibility = const i0.Value.absent(), this.stackId = const i0.Value.absent(), + this.libraryId = const i0.Value.absent(), }); RemoteAssetEntityCompanion.insert({ required String name, @@ -1394,6 +1450,7 @@ class RemoteAssetEntityCompanion this.livePhotoVideoId = const i0.Value.absent(), required i2.AssetVisibility visibility, this.stackId = const i0.Value.absent(), + this.libraryId = const i0.Value.absent(), }) : name = i0.Value(name), type = i0.Value(type), id = i0.Value(id), @@ -1418,6 +1475,7 @@ class RemoteAssetEntityCompanion i0.Expression? livePhotoVideoId, i0.Expression? visibility, i0.Expression? stackId, + i0.Expression? libraryId, }) { return i0.RawValuesInsertable({ if (name != null) 'name': name, @@ -1437,6 +1495,7 @@ class RemoteAssetEntityCompanion if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId, if (visibility != null) 'visibility': visibility, if (stackId != null) 'stack_id': stackId, + if (libraryId != null) 'library_id': libraryId, }); } @@ -1458,6 +1517,7 @@ class RemoteAssetEntityCompanion i0.Value? livePhotoVideoId, i0.Value? visibility, i0.Value? stackId, + i0.Value? libraryId, }) { return i1.RemoteAssetEntityCompanion( name: name ?? this.name, @@ -1477,6 +1537,7 @@ class RemoteAssetEntityCompanion livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, visibility: visibility ?? this.visibility, stackId: stackId ?? this.stackId, + libraryId: libraryId ?? this.libraryId, ); } @@ -1538,6 +1599,9 @@ class RemoteAssetEntityCompanion if (stackId.present) { map['stack_id'] = i0.Variable(stackId.value); } + if (libraryId.present) { + map['library_id'] = i0.Variable(libraryId.value); + } return map; } @@ -1560,12 +1624,21 @@ class RemoteAssetEntityCompanion ..write('deletedAt: $deletedAt, ') ..write('livePhotoVideoId: $livePhotoVideoId, ') ..write('visibility: $visibility, ') - ..write('stackId: $stackId') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') ..write(')')) .toString(); } } +i0.Index get uQRemoteAssetsOwnerChecksum => i0.Index( + 'UQ_remote_assets_owner_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)', +); +i0.Index get uQRemoteAssetsOwnerLibraryChecksum => i0.Index( + 'UQ_remote_assets_owner_library_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)', +); i0.Index get idxRemoteAssetChecksum => i0.Index( 'idx_remote_asset_checksum', 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index 353cabf31e..0458a5b254 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -66,7 +66,7 @@ class Drift extends $Drift implements IDatabaseRepository { : super(executor ?? driftDatabase(name: 'immich', native: const DriftNativeOptions(shareAcrossIsolates: true))); @override - int get schemaVersion => 5; + int get schemaVersion => 6; @override MigrationStrategy get migration => MigrationStrategy( @@ -103,6 +103,15 @@ class Drift extends $Drift implements IDatabaseRepository { ), ); }, + from5To6: (m, v6) async { + // Drops the (checksum, ownerId) and adds it back as (ownerId, checksum) + await customStatement('DROP INDEX IF EXISTS UQ_remote_asset_owner_checksum'); + await m.create(v6.idxRemoteAssetOwnerChecksum); + // Adds libraryId to remote_asset_entity + await m.addColumn(v6.remoteAssetEntity, v6.remoteAssetEntity.libraryId); + await m.create(v6.uQRemoteAssetsOwnerChecksum); + await m.create(v6.uQRemoteAssetsOwnerLibraryChecksum); + }, ), ); diff --git a/mobile/lib/infrastructure/repositories/db.repository.drift.dart b/mobile/lib/infrastructure/repositories/db.repository.drift.dart index 296b87900e..2b7203eb46 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.drift.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.drift.dart @@ -84,7 +84,9 @@ abstract class $Drift extends i0.GeneratedDatabase { localAlbumEntity, localAlbumAssetEntity, i4.idxLocalAssetChecksum, - i2.uQRemoteAssetOwnerChecksum, + i2.idxRemoteAssetOwnerChecksum, + i2.uQRemoteAssetsOwnerChecksum, + i2.uQRemoteAssetsOwnerLibraryChecksum, i2.idxRemoteAssetChecksum, userMetadataEntity, partnerEntity, diff --git a/mobile/lib/infrastructure/repositories/db.repository.steps.dart b/mobile/lib/infrastructure/repositories/db.repository.steps.dart index 8129bba00c..32b309881e 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.steps.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.steps.dart @@ -2319,11 +2319,398 @@ i1.GeneratedColumn _column_85(String aliasedName) => type: i1.DriftSqlType.dateTime, defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), ); + +final class Schema6 extends i0.VersionedSchema { + Schema6({required super.database}) : super(version: 6); + @override + late final List entities = [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + idxRemoteAssetOwnerChecksum, + uQRemoteAssetsOwnerChecksum, + uQRemoteAssetsOwnerLibraryChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + ]; + late final Shape16 userEntity = Shape16( + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_84, + _column_85, + _column_5, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape17 remoteAssetEntity = Shape17( + source: i0.VersionedTable( + entityName: 'remote_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_13, + _column_14, + _column_15, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + _column_86, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape3 stackEntity = Shape3( + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_0, _column_9, _column_5, _column_15, _column_75], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape2 localAssetEntity = Shape2( + source: i0.VersionedTable( + entityName: 'local_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_22, + _column_14, + _column_23, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape6 localAlbumEntity = Shape6( + source: i0.VersionedTable( + entityName: 'local_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_5, + _column_31, + _column_32, + _column_33, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape7 localAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_34, _column_35], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLocalAssetChecksum = i1.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + final i1.Index idxRemoteAssetOwnerChecksum = i1.Index( + 'idx_remote_asset_owner_checksum', + 'CREATE INDEX idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', + ); + final i1.Index uQRemoteAssetsOwnerChecksum = i1.Index( + 'UQ_remote_assets_owner_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)', + ); + final i1.Index uQRemoteAssetsOwnerLibraryChecksum = i1.Index( + 'UQ_remote_assets_owner_library_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)', + ); + final i1.Index idxRemoteAssetChecksum = i1.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final Shape4 userMetadataEntity = Shape4( + source: i0.VersionedTable( + entityName: 'user_metadata_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(user_id, "key")'], + columns: [_column_25, _column_26, _column_27], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape5 partnerEntity = Shape5( + source: i0.VersionedTable( + entityName: 'partner_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(shared_by_id, shared_with_id)'], + columns: [_column_28, _column_29, _column_30], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape8 remoteExifEntity = Shape8( + source: i0.VersionedTable( + entityName: 'remote_exif_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id)'], + columns: [ + _column_36, + _column_37, + _column_38, + _column_39, + _column_40, + _column_41, + _column_11, + _column_10, + _column_42, + _column_43, + _column_44, + _column_45, + _column_46, + _column_47, + _column_48, + _column_49, + _column_50, + _column_51, + _column_52, + _column_53, + _column_54, + _column_55, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape9 remoteAlbumEntity = Shape9( + source: i0.VersionedTable( + entityName: 'remote_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_56, + _column_9, + _column_5, + _column_15, + _column_57, + _column_58, + _column_59, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape7 remoteAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'remote_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_36, _column_60], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape10 remoteAlbumUserEntity = Shape10( + source: i0.VersionedTable( + entityName: 'remote_album_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(album_id, user_id)'], + columns: [_column_60, _column_25, _column_61], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape11 memoryEntity = Shape11( + source: i0.VersionedTable( + entityName: 'memory_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_18, + _column_15, + _column_8, + _column_62, + _column_63, + _column_64, + _column_65, + _column_66, + _column_67, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape12 memoryAssetEntity = Shape12( + source: i0.VersionedTable( + entityName: 'memory_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, memory_id)'], + columns: [_column_36, _column_68], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape14 personEntity = Shape14( + source: i0.VersionedTable( + entityName: 'person_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_1, + _column_69, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape15 assetFaceEntity = Shape15( + source: i0.VersionedTable( + entityName: 'asset_face_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_36, + _column_76, + _column_77, + _column_78, + _column_79, + _column_80, + _column_81, + _column_82, + _column_83, + ], + attachedDatabase: database, + ), + alias: null, + ); +} + +class Shape17 extends i0.VersionedTable { + Shape17({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get type => + columnsByName['type']! as i1.GeneratedColumn; + i1.GeneratedColumn get createdAt => + columnsByName['created_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get width => + columnsByName['width']! as i1.GeneratedColumn; + i1.GeneratedColumn get height => + columnsByName['height']! as i1.GeneratedColumn; + i1.GeneratedColumn get durationInSeconds => + columnsByName['duration_in_seconds']! as i1.GeneratedColumn; + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get checksum => + columnsByName['checksum']! as i1.GeneratedColumn; + i1.GeneratedColumn get isFavorite => + columnsByName['is_favorite']! as i1.GeneratedColumn; + i1.GeneratedColumn get ownerId => + columnsByName['owner_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get localDateTime => + columnsByName['local_date_time']! as i1.GeneratedColumn; + i1.GeneratedColumn get thumbHash => + columnsByName['thumb_hash']! as i1.GeneratedColumn; + i1.GeneratedColumn get deletedAt => + columnsByName['deleted_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get livePhotoVideoId => + columnsByName['live_photo_video_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get visibility => + columnsByName['visibility']! as i1.GeneratedColumn; + i1.GeneratedColumn get stackId => + columnsByName['stack_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get libraryId => + columnsByName['library_id']! as i1.GeneratedColumn; +} + +i1.GeneratedColumn _column_86(String aliasedName) => + i1.GeneratedColumn( + 'library_id', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i0.MigrationStepWithVersion migrationSteps({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, required Future Function(i1.Migrator m, Schema3 schema) from2To3, required Future Function(i1.Migrator m, Schema4 schema) from3To4, required Future Function(i1.Migrator m, Schema5 schema) from4To5, + required Future Function(i1.Migrator m, Schema6 schema) from5To6, }) { return (currentVersion, database) async { switch (currentVersion) { @@ -2347,6 +2734,11 @@ i0.MigrationStepWithVersion migrationSteps({ final migrator = i1.Migrator(database, schema); await from4To5(migrator, schema); return 5; + case 5: + final schema = Schema6(database: database); + final migrator = i1.Migrator(database, schema); + await from5To6(migrator, schema); + return 6; default: throw ArgumentError.value('Unknown migration from $currentVersion'); } @@ -2358,11 +2750,13 @@ i1.OnUpgrade stepByStep({ required Future Function(i1.Migrator m, Schema3 schema) from2To3, required Future Function(i1.Migrator m, Schema4 schema) from3To4, required Future Function(i1.Migrator m, Schema5 schema) from4To5, + required Future Function(i1.Migrator m, Schema6 schema) from5To6, }) => i0.VersionedSchema.stepByStepHelper( step: migrationSteps( from1To2: from1To2, from2To3: from2To3, from3To4: from3To4, from4To5: from4To5, + from5To6: from5To6, ), ); diff --git a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart index 2eefa298f8..52ffaabca9 100644 --- a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart @@ -121,6 +121,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { visibility: Value(asset.visibility.toAssetVisibility()), livePhotoVideoId: Value(asset.livePhotoVideoId), stackId: Value(asset.stackId), + libraryId: Value(asset.libraryId), ); batch.insert( diff --git a/mobile/openapi/lib/model/sync_asset_v1.dart b/mobile/openapi/lib/model/sync_asset_v1.dart index 4c42d08a5f..f0d5097ea4 100644 --- a/mobile/openapi/lib/model/sync_asset_v1.dart +++ b/mobile/openapi/lib/model/sync_asset_v1.dart @@ -20,6 +20,7 @@ class SyncAssetV1 { required this.fileModifiedAt, required this.id, required this.isFavorite, + required this.libraryId, required this.livePhotoVideoId, required this.localDateTime, required this.originalFileName, @@ -44,6 +45,8 @@ class SyncAssetV1 { bool isFavorite; + String? libraryId; + String? livePhotoVideoId; DateTime? localDateTime; @@ -69,6 +72,7 @@ class SyncAssetV1 { other.fileModifiedAt == fileModifiedAt && other.id == id && other.isFavorite == isFavorite && + other.libraryId == libraryId && other.livePhotoVideoId == livePhotoVideoId && other.localDateTime == localDateTime && other.originalFileName == originalFileName && @@ -88,6 +92,7 @@ class SyncAssetV1 { (fileModifiedAt == null ? 0 : fileModifiedAt!.hashCode) + (id.hashCode) + (isFavorite.hashCode) + + (libraryId == null ? 0 : libraryId!.hashCode) + (livePhotoVideoId == null ? 0 : livePhotoVideoId!.hashCode) + (localDateTime == null ? 0 : localDateTime!.hashCode) + (originalFileName.hashCode) + @@ -98,7 +103,7 @@ class SyncAssetV1 { (visibility.hashCode); @override - String toString() => 'SyncAssetV1[checksum=$checksum, deletedAt=$deletedAt, duration=$duration, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, id=$id, isFavorite=$isFavorite, livePhotoVideoId=$livePhotoVideoId, localDateTime=$localDateTime, originalFileName=$originalFileName, ownerId=$ownerId, stackId=$stackId, thumbhash=$thumbhash, type=$type, visibility=$visibility]'; + String toString() => 'SyncAssetV1[checksum=$checksum, deletedAt=$deletedAt, duration=$duration, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, id=$id, isFavorite=$isFavorite, libraryId=$libraryId, livePhotoVideoId=$livePhotoVideoId, localDateTime=$localDateTime, originalFileName=$originalFileName, ownerId=$ownerId, stackId=$stackId, thumbhash=$thumbhash, type=$type, visibility=$visibility]'; Map toJson() { final json = {}; @@ -125,6 +130,11 @@ class SyncAssetV1 { } json[r'id'] = this.id; json[r'isFavorite'] = this.isFavorite; + if (this.libraryId != null) { + json[r'libraryId'] = this.libraryId; + } else { + // json[r'libraryId'] = null; + } if (this.livePhotoVideoId != null) { json[r'livePhotoVideoId'] = this.livePhotoVideoId; } else { @@ -168,6 +178,7 @@ class SyncAssetV1 { fileModifiedAt: mapDateTime(json, r'fileModifiedAt', r''), id: mapValueOfType(json, r'id')!, isFavorite: mapValueOfType(json, r'isFavorite')!, + libraryId: mapValueOfType(json, r'libraryId'), livePhotoVideoId: mapValueOfType(json, r'livePhotoVideoId'), localDateTime: mapDateTime(json, r'localDateTime', r''), originalFileName: mapValueOfType(json, r'originalFileName')!, @@ -230,6 +241,7 @@ class SyncAssetV1 { 'fileModifiedAt', 'id', 'isFavorite', + 'libraryId', 'livePhotoVideoId', 'localDateTime', 'originalFileName', diff --git a/mobile/test/drift/main/generated/schema.dart b/mobile/test/drift/main/generated/schema.dart index c42542afb3..d59002bf56 100644 --- a/mobile/test/drift/main/generated/schema.dart +++ b/mobile/test/drift/main/generated/schema.dart @@ -8,6 +8,7 @@ import 'schema_v2.dart' as v2; import 'schema_v3.dart' as v3; import 'schema_v4.dart' as v4; import 'schema_v5.dart' as v5; +import 'schema_v6.dart' as v6; class GeneratedHelper implements SchemaInstantiationHelper { @override @@ -23,10 +24,12 @@ class GeneratedHelper implements SchemaInstantiationHelper { return v4.DatabaseAtV4(db); case 5: return v5.DatabaseAtV5(db); + case 6: + return v6.DatabaseAtV6(db); default: throw MissingSchemaException(version, versions); } } - static const versions = const [1, 2, 3, 4, 5]; + static const versions = const [1, 2, 3, 4, 5, 6]; } diff --git a/mobile/test/drift/main/generated/schema_v6.dart b/mobile/test/drift/main/generated/schema_v6.dart new file mode 100644 index 0000000000..97b91caa07 --- /dev/null +++ b/mobile/test/drift/main/generated/schema_v6.dart @@ -0,0 +1,6448 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class UserEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn hasProfileImage = GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn profileChangedAt = + GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + @override + List get $columns => [ + id, + name, + isAdmin, + email, + hasProfileImage, + profileChangedAt, + updatedAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_entity'; + @override + Set get $primaryKey => {id}; + @override + UserEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + hasProfileImage: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}has_profile_image'], + )!, + profileChangedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}profile_changed_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ); + } + + @override + UserEntity createAlias(String alias) { + return UserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserEntityData extends DataClass implements Insertable { + final String id; + final String name; + final bool isAdmin; + final String email; + final bool hasProfileImage; + final DateTime profileChangedAt; + final DateTime updatedAt; + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + required this.hasProfileImage, + required this.profileChangedAt, + required this.updatedAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['is_admin'] = Variable(isAdmin); + map['email'] = Variable(email); + map['has_profile_image'] = Variable(hasProfileImage); + map['profile_changed_at'] = Variable(profileChangedAt); + map['updated_at'] = Variable(updatedAt); + return map; + } + + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + isAdmin: serializer.fromJson(json['isAdmin']), + email: serializer.fromJson(json['email']), + hasProfileImage: serializer.fromJson(json['hasProfileImage']), + profileChangedAt: serializer.fromJson(json['profileChangedAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'isAdmin': serializer.toJson(isAdmin), + 'email': serializer.toJson(email), + 'hasProfileImage': serializer.toJson(hasProfileImage), + 'profileChangedAt': serializer.toJson(profileChangedAt), + 'updatedAt': serializer.toJson(updatedAt), + }; + } + + UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + bool? hasProfileImage, + DateTime? profileChangedAt, + DateTime? updatedAt, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + updatedAt: updatedAt ?? this.updatedAt, + ); + UserEntityData copyWithCompanion(UserEntityCompanion data) { + return UserEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, + email: data.email.present ? data.email.value : this.email, + hasProfileImage: data.hasProfileImage.present + ? data.hasProfileImage.value + : this.hasProfileImage, + profileChangedAt: data.profileChangedAt.present + ? data.profileChangedAt.value + : this.profileChangedAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ); + } + + @override + String toString() { + return (StringBuffer('UserEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + hasProfileImage, + profileChangedAt, + updatedAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserEntityData && + other.id == this.id && + other.name == this.name && + other.isAdmin == this.isAdmin && + other.email == this.email && + other.hasProfileImage == this.hasProfileImage && + other.profileChangedAt == this.profileChangedAt && + other.updatedAt == this.updatedAt); +} + +class UserEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value isAdmin; + final Value email; + final Value hasProfileImage; + final Value profileChangedAt; + final Value updatedAt; + const UserEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.isAdmin = const Value.absent(), + this.email = const Value.absent(), + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.updatedAt = const Value.absent(), + }); + UserEntityCompanion.insert({ + required String id, + required String name, + this.isAdmin = const Value.absent(), + required String email, + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.updatedAt = const Value.absent(), + }) : id = Value(id), + name = Value(name), + email = Value(email); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? isAdmin, + Expression? email, + Expression? hasProfileImage, + Expression? profileChangedAt, + Expression? updatedAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (isAdmin != null) 'is_admin': isAdmin, + if (email != null) 'email': email, + if (hasProfileImage != null) 'has_profile_image': hasProfileImage, + if (profileChangedAt != null) 'profile_changed_at': profileChangedAt, + if (updatedAt != null) 'updated_at': updatedAt, + }); + } + + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? hasProfileImage, + Value? profileChangedAt, + Value? updatedAt, + }) { + return UserEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + updatedAt: updatedAt ?? this.updatedAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (isAdmin.present) { + map['is_admin'] = Variable(isAdmin.value); + } + if (email.present) { + map['email'] = Variable(email.value); + } + if (hasProfileImage.present) { + map['has_profile_image'] = Variable(hasProfileImage.value); + } + if (profileChangedAt.present) { + map['profile_changed_at'] = Variable(profileChangedAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } +} + +class RemoteAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn localDateTime = + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn livePhotoVideoId = GeneratedColumn( + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn libraryId = GeneratedColumn( + 'library_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), + libraryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}library_id'], + ), + ); + } + + @override + RemoteAssetEntity createAlias(String alias) { + return RemoteAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String checksum; + final bool isFavorite; + final String ownerId; + final DateTime? localDateTime; + final String? thumbHash; + final DateTime? deletedAt; + final String? livePhotoVideoId; + final int visibility; + final String? stackId; + final String? libraryId; + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + this.libraryId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + map['checksum'] = Variable(checksum); + map['is_favorite'] = Variable(isFavorite); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || localDateTime != null) { + map['local_date_time'] = Variable(localDateTime); + } + if (!nullToAbsent || thumbHash != null) { + map['thumb_hash'] = Variable(thumbHash); + } + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + if (!nullToAbsent || livePhotoVideoId != null) { + map['live_photo_video_id'] = Variable(livePhotoVideoId); + } + map['visibility'] = Variable(visibility); + if (!nullToAbsent || stackId != null) { + map['stack_id'] = Variable(stackId); + } + if (!nullToAbsent || libraryId != null) { + map['library_id'] = Variable(libraryId); + } + return map; + } + + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + ownerId: serializer.fromJson(json['ownerId']), + localDateTime: serializer.fromJson(json['localDateTime']), + thumbHash: serializer.fromJson(json['thumbHash']), + deletedAt: serializer.fromJson(json['deletedAt']), + livePhotoVideoId: serializer.fromJson(json['livePhotoVideoId']), + visibility: serializer.fromJson(json['visibility']), + stackId: serializer.fromJson(json['stackId']), + libraryId: serializer.fromJson(json['libraryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'ownerId': serializer.toJson(ownerId), + 'localDateTime': serializer.toJson(localDateTime), + 'thumbHash': serializer.toJson(thumbHash), + 'deletedAt': serializer.toJson(deletedAt), + 'livePhotoVideoId': serializer.toJson(livePhotoVideoId), + 'visibility': serializer.toJson(visibility), + 'stackId': serializer.toJson(stackId), + 'libraryId': serializer.toJson(libraryId), + }; + } + + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + Value libraryId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + libraryId: libraryId.present ? libraryId.value : this.libraryId, + ); + RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { + return RemoteAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + localDateTime: data.localDateTime.present + ? data.localDateTime.value + : this.localDateTime, + thumbHash: data.thumbHash.present ? data.thumbHash.value : this.thumbHash, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + livePhotoVideoId: data.livePhotoVideoId.present + ? data.livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: data.visibility.present + ? data.visibility.value + : this.visibility, + stackId: data.stackId.present ? data.stackId.value : this.stackId, + libraryId: data.libraryId.present ? data.libraryId.value : this.libraryId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.ownerId == this.ownerId && + other.localDateTime == this.localDateTime && + other.thumbHash == this.thumbHash && + other.deletedAt == this.deletedAt && + other.livePhotoVideoId == this.livePhotoVideoId && + other.visibility == this.visibility && + other.stackId == this.stackId && + other.libraryId == this.libraryId); +} + +class RemoteAssetEntityCompanion + extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value ownerId; + final Value localDateTime; + final Value thumbHash; + final Value deletedAt; + final Value livePhotoVideoId; + final Value visibility; + final Value stackId; + final Value libraryId; + const RemoteAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.ownerId = const Value.absent(), + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + this.visibility = const Value.absent(), + this.stackId = const Value.absent(), + this.libraryId = const Value.absent(), + }); + RemoteAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + required String checksum, + this.isFavorite = const Value.absent(), + required String ownerId, + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + required int visibility, + this.stackId = const Value.absent(), + this.libraryId = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? ownerId, + Expression? localDateTime, + Expression? thumbHash, + Expression? deletedAt, + Expression? livePhotoVideoId, + Expression? visibility, + Expression? stackId, + Expression? libraryId, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (ownerId != null) 'owner_id': ownerId, + if (localDateTime != null) 'local_date_time': localDateTime, + if (thumbHash != null) 'thumb_hash': thumbHash, + if (deletedAt != null) 'deleted_at': deletedAt, + if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId, + if (visibility != null) 'visibility': visibility, + if (stackId != null) 'stack_id': stackId, + if (libraryId != null) 'library_id': libraryId, + }); + } + + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + Value? libraryId, + }) { + return RemoteAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime ?? this.localDateTime, + thumbHash: thumbHash ?? this.thumbHash, + deletedAt: deletedAt ?? this.deletedAt, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId ?? this.stackId, + libraryId: libraryId ?? this.libraryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (localDateTime.present) { + map['local_date_time'] = Variable(localDateTime.value); + } + if (thumbHash.present) { + map['thumb_hash'] = Variable(thumbHash.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (livePhotoVideoId.present) { + map['live_photo_video_id'] = Variable(livePhotoVideoId.value); + } + if (visibility.present) { + map['visibility'] = Variable(visibility.value); + } + if (stackId.present) { + map['stack_id'] = Variable(stackId.value); + } + if (libraryId.present) { + map['library_id'] = Variable(libraryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') + ..write(')')) + .toString(); + } +} + +class StackEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StackEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'stack_entity'; + @override + Set get $primaryKey => {id}; + @override + StackEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StackEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, + ); + } + + @override + StackEntity createAlias(String alias) { + return StackEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StackEntityData extends DataClass implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String primaryAssetId; + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['primary_asset_id'] = Variable(primaryAssetId); + return map; + } + + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StackEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + primaryAssetId: serializer.fromJson(json['primaryAssetId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'primaryAssetId': serializer.toJson(primaryAssetId), + }; + } + + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + StackEntityData copyWithCompanion(StackEntityCompanion data) { + return StackEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, + ); + } + + @override + String toString() { + return (StringBuffer('StackEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StackEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.primaryAssetId == this.primaryAssetId); +} + +class StackEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value primaryAssetId; + const StackEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.primaryAssetId = const Value.absent(), + }); + StackEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String primaryAssetId, + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? primaryAssetId, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, + }); + } + + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { + return StackEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (primaryAssetId.present) { + map['primary_asset_id'] = Variable(primaryAssetId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StackEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } +} + +class LocalAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, + ); + } + + @override + LocalAssetEntity createAlias(String alias) { + return LocalAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String? checksum; + final bool isFavorite; + final int orientation; + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + if (!nullToAbsent || checksum != null) { + map['checksum'] = Variable(checksum); + } + map['is_favorite'] = Variable(isFavorite); + map['orientation'] = Variable(orientation); + return map; + } + + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + orientation: serializer.fromJson(json['orientation']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'orientation': serializer.toJson(orientation), + }; + } + + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { + return LocalAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.orientation == this.orientation); +} + +class LocalAssetEntityCompanion extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value orientation; + const LocalAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }); + LocalAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? orientation, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (orientation != null) 'orientation': orientation, + }); + } + + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { + return LocalAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } +} + +class LocalAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); + @override + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), + ); + } + + @override + LocalAlbumEntity createAlias(String alias) { + return LocalAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final DateTime updatedAt; + final int backupSelection; + final bool isIosSharedAlbum; + final bool? marker_; + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['updated_at'] = Variable(updatedAt); + map['backup_selection'] = Variable(backupSelection); + map['is_ios_shared_album'] = Variable(isIosSharedAlbum); + if (!nullToAbsent || marker_ != null) { + map['marker'] = Variable(marker_); + } + return map; + } + + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + updatedAt: serializer.fromJson(json['updatedAt']), + backupSelection: serializer.fromJson(json['backupSelection']), + isIosSharedAlbum: serializer.fromJson(json['isIosSharedAlbum']), + marker_: serializer.fromJson(json['marker_']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'updatedAt': serializer.toJson(updatedAt), + 'backupSelection': serializer.toJson(backupSelection), + 'isIosSharedAlbum': serializer.toJson(isIosSharedAlbum), + 'marker_': serializer.toJson(marker_), + }; + } + + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); + LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { + return LocalAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, + marker_: data.marker_.present ? data.marker_.value : this.marker_, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.updatedAt == this.updatedAt && + other.backupSelection == this.backupSelection && + other.isIosSharedAlbum == this.isIosSharedAlbum && + other.marker_ == this.marker_); +} + +class LocalAlbumEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value updatedAt; + final Value backupSelection; + final Value isIosSharedAlbum; + final Value marker_; + const LocalAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.updatedAt = const Value.absent(), + this.backupSelection = const Value.absent(), + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }); + LocalAlbumEntityCompanion.insert({ + required String id, + required String name, + this.updatedAt = const Value.absent(), + required int backupSelection, + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? updatedAt, + Expression? backupSelection, + Expression? isIosSharedAlbum, + Expression? marker_, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (updatedAt != null) 'updated_at': updatedAt, + if (backupSelection != null) 'backup_selection': backupSelection, + if (isIosSharedAlbum != null) 'is_ios_shared_album': isIosSharedAlbum, + if (marker_ != null) 'marker': marker_, + }); + } + + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_, + }) { + return LocalAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_ ?? this.marker_, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (backupSelection.present) { + map['backup_selection'] = Variable(backupSelection.value); + } + if (isIosSharedAlbum.present) { + map['is_ios_shared_album'] = Variable(isIosSharedAlbum.value); + } + if (marker_.present) { + map['marker'] = Variable(marker_.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } +} + +class LocalAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + LocalAlbumAssetEntity createAlias(String alias) { + return LocalAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + LocalAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data, + ) { + return LocalAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const LocalAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + LocalAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return LocalAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class UserMetadataEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserMetadataEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); + @override + List get $columns => [userId, key, value]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_metadata_entity'; + @override + Set get $primaryKey => {userId, key}; + @override + UserMetadataEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserMetadataEntityData( + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, + ); + } + + @override + UserMetadataEntity createAlias(String alias) { + return UserMetadataEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserMetadataEntityData extends DataClass + implements Insertable { + final String userId; + final int key; + final Uint8List value; + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['user_id'] = Variable(userId); + map['key'] = Variable(key); + map['value'] = Variable(value); + return map; + } + + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserMetadataEntityData( + userId: serializer.fromJson(json['userId']), + key: serializer.fromJson(json['key']), + value: serializer.fromJson(json['value']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'userId': serializer.toJson(userId), + 'key': serializer.toJson(key), + 'value': serializer.toJson(value), + }; + } + + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { + return UserMetadataEntityData( + userId: data.userId.present ? data.userId.value : this.userId, + key: data.key.present ? data.key.value : this.key, + value: data.value.present ? data.value.value : this.value, + ); + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityData(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(userId, key, $driftBlobEquality.hash(value)); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserMetadataEntityData && + other.userId == this.userId && + other.key == this.key && + $driftBlobEquality.equals(other.value, this.value)); +} + +class UserMetadataEntityCompanion + extends UpdateCompanion { + final Value userId; + final Value key; + final Value value; + const UserMetadataEntityCompanion({ + this.userId = const Value.absent(), + this.key = const Value.absent(), + this.value = const Value.absent(), + }); + UserMetadataEntityCompanion.insert({ + required String userId, + required int key, + required Uint8List value, + }) : userId = Value(userId), + key = Value(key), + value = Value(value); + static Insertable custom({ + Expression? userId, + Expression? key, + Expression? value, + }) { + return RawValuesInsertable({ + if (userId != null) 'user_id': userId, + if (key != null) 'key': key, + if (value != null) 'value': value, + }); + } + + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { + return UserMetadataEntityCompanion( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (key.present) { + map['key'] = Variable(key.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityCompanion(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } +} + +class PartnerEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PartnerEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [sharedById, sharedWithId, inTimeline]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'partner_entity'; + @override + Set get $primaryKey => {sharedById, sharedWithId}; + @override + PartnerEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PartnerEntityData( + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, + ); + } + + @override + PartnerEntity createAlias(String alias) { + return PartnerEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PartnerEntityData extends DataClass + implements Insertable { + final String sharedById; + final String sharedWithId; + final bool inTimeline; + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['shared_by_id'] = Variable(sharedById); + map['shared_with_id'] = Variable(sharedWithId); + map['in_timeline'] = Variable(inTimeline); + return map; + } + + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PartnerEntityData( + sharedById: serializer.fromJson(json['sharedById']), + sharedWithId: serializer.fromJson(json['sharedWithId']), + inTimeline: serializer.fromJson(json['inTimeline']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'sharedById': serializer.toJson(sharedById), + 'sharedWithId': serializer.toJson(sharedWithId), + 'inTimeline': serializer.toJson(inTimeline), + }; + } + + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { + return PartnerEntityData( + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, + ); + } + + @override + String toString() { + return (StringBuffer('PartnerEntityData(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PartnerEntityData && + other.sharedById == this.sharedById && + other.sharedWithId == this.sharedWithId && + other.inTimeline == this.inTimeline); +} + +class PartnerEntityCompanion extends UpdateCompanion { + final Value sharedById; + final Value sharedWithId; + final Value inTimeline; + const PartnerEntityCompanion({ + this.sharedById = const Value.absent(), + this.sharedWithId = const Value.absent(), + this.inTimeline = const Value.absent(), + }); + PartnerEntityCompanion.insert({ + required String sharedById, + required String sharedWithId, + this.inTimeline = const Value.absent(), + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); + static Insertable custom({ + Expression? sharedById, + Expression? sharedWithId, + Expression? inTimeline, + }) { + return RawValuesInsertable({ + if (sharedById != null) 'shared_by_id': sharedById, + if (sharedWithId != null) 'shared_with_id': sharedWithId, + if (inTimeline != null) 'in_timeline': inTimeline, + }); + } + + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { + return PartnerEntityCompanion( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sharedById.present) { + map['shared_by_id'] = Variable(sharedById.value); + } + if (sharedWithId.present) { + map['shared_with_id'] = Variable(sharedWithId.value); + } + if (inTimeline.present) { + map['in_timeline'] = Variable(inTimeline.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PartnerEntityCompanion(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } +} + +class RemoteExifEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteExifEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn city = GeneratedColumn( + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn state = GeneratedColumn( + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn country = GeneratedColumn( + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn make = GeneratedColumn( + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn model = GeneratedColumn( + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_exif_entity'; + @override + Set get $primaryKey => {assetId}; + @override + RemoteExifEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteExifEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), + ); + } + + @override + RemoteExifEntity createAlias(String alias) { + return RemoteExifEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteExifEntityData extends DataClass + implements Insertable { + final String assetId; + final String? city; + final String? state; + final String? country; + final DateTime? dateTimeOriginal; + final String? description; + final int? height; + final int? width; + final String? exposureTime; + final double? fNumber; + final int? fileSize; + final double? focalLength; + final double? latitude; + final double? longitude; + final int? iso; + final String? make; + final String? model; + final String? lens; + final String? orientation; + final String? timeZone; + final int? rating; + final String? projectionType; + const RemoteExifEntityData({ + required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || city != null) { + map['city'] = Variable(city); + } + if (!nullToAbsent || state != null) { + map['state'] = Variable(state); + } + if (!nullToAbsent || country != null) { + map['country'] = Variable(country); + } + if (!nullToAbsent || dateTimeOriginal != null) { + map['date_time_original'] = Variable(dateTimeOriginal); + } + if (!nullToAbsent || description != null) { + map['description'] = Variable(description); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || exposureTime != null) { + map['exposure_time'] = Variable(exposureTime); + } + if (!nullToAbsent || fNumber != null) { + map['f_number'] = Variable(fNumber); + } + if (!nullToAbsent || fileSize != null) { + map['file_size'] = Variable(fileSize); + } + if (!nullToAbsent || focalLength != null) { + map['focal_length'] = Variable(focalLength); + } + if (!nullToAbsent || latitude != null) { + map['latitude'] = Variable(latitude); + } + if (!nullToAbsent || longitude != null) { + map['longitude'] = Variable(longitude); + } + if (!nullToAbsent || iso != null) { + map['iso'] = Variable(iso); + } + if (!nullToAbsent || make != null) { + map['make'] = Variable(make); + } + if (!nullToAbsent || model != null) { + map['model'] = Variable(model); + } + if (!nullToAbsent || lens != null) { + map['lens'] = Variable(lens); + } + if (!nullToAbsent || orientation != null) { + map['orientation'] = Variable(orientation); + } + if (!nullToAbsent || timeZone != null) { + map['time_zone'] = Variable(timeZone); + } + if (!nullToAbsent || rating != null) { + map['rating'] = Variable(rating); + } + if (!nullToAbsent || projectionType != null) { + map['projection_type'] = Variable(projectionType); + } + return map; + } + + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteExifEntityData( + assetId: serializer.fromJson(json['assetId']), + city: serializer.fromJson(json['city']), + state: serializer.fromJson(json['state']), + country: serializer.fromJson(json['country']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), + description: serializer.fromJson(json['description']), + height: serializer.fromJson(json['height']), + width: serializer.fromJson(json['width']), + exposureTime: serializer.fromJson(json['exposureTime']), + fNumber: serializer.fromJson(json['fNumber']), + fileSize: serializer.fromJson(json['fileSize']), + focalLength: serializer.fromJson(json['focalLength']), + latitude: serializer.fromJson(json['latitude']), + longitude: serializer.fromJson(json['longitude']), + iso: serializer.fromJson(json['iso']), + make: serializer.fromJson(json['make']), + model: serializer.fromJson(json['model']), + lens: serializer.fromJson(json['lens']), + orientation: serializer.fromJson(json['orientation']), + timeZone: serializer.fromJson(json['timeZone']), + rating: serializer.fromJson(json['rating']), + projectionType: serializer.fromJson(json['projectionType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'city': serializer.toJson(city), + 'state': serializer.toJson(state), + 'country': serializer.toJson(country), + 'dateTimeOriginal': serializer.toJson(dateTimeOriginal), + 'description': serializer.toJson(description), + 'height': serializer.toJson(height), + 'width': serializer.toJson(width), + 'exposureTime': serializer.toJson(exposureTime), + 'fNumber': serializer.toJson(fNumber), + 'fileSize': serializer.toJson(fileSize), + 'focalLength': serializer.toJson(focalLength), + 'latitude': serializer.toJson(latitude), + 'longitude': serializer.toJson(longitude), + 'iso': serializer.toJson(iso), + 'make': serializer.toJson(make), + 'model': serializer.toJson(model), + 'lens': serializer.toJson(lens), + 'orientation': serializer.toJson(orientation), + 'timeZone': serializer.toJson(timeZone), + 'rating': serializer.toJson(rating), + 'projectionType': serializer.toJson(projectionType), + }; + } + + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); + RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { + return RemoteExifEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + city: data.city.present ? data.city.value : this.city, + state: data.state.present ? data.state.value : this.state, + country: data.country.present ? data.country.value : this.country, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: data.description.present + ? data.description.value + : this.description, + height: data.height.present ? data.height.value : this.height, + width: data.width.present ? data.width.value : this.width, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, + fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, + fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, + latitude: data.latitude.present ? data.latitude.value : this.latitude, + longitude: data.longitude.present ? data.longitude.value : this.longitude, + iso: data.iso.present ? data.iso.value : this.iso, + make: data.make.present ? data.make.value : this.make, + model: data.model.present ? data.model.value : this.model, + lens: data.lens.present ? data.lens.value : this.lens, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, + rating: data.rating.present ? data.rating.value : this.rating, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityData(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteExifEntityData && + other.assetId == this.assetId && + other.city == this.city && + other.state == this.state && + other.country == this.country && + other.dateTimeOriginal == this.dateTimeOriginal && + other.description == this.description && + other.height == this.height && + other.width == this.width && + other.exposureTime == this.exposureTime && + other.fNumber == this.fNumber && + other.fileSize == this.fileSize && + other.focalLength == this.focalLength && + other.latitude == this.latitude && + other.longitude == this.longitude && + other.iso == this.iso && + other.make == this.make && + other.model == this.model && + other.lens == this.lens && + other.orientation == this.orientation && + other.timeZone == this.timeZone && + other.rating == this.rating && + other.projectionType == this.projectionType); +} + +class RemoteExifEntityCompanion extends UpdateCompanion { + final Value assetId; + final Value city; + final Value state; + final Value country; + final Value dateTimeOriginal; + final Value description; + final Value height; + final Value width; + final Value exposureTime; + final Value fNumber; + final Value fileSize; + final Value focalLength; + final Value latitude; + final Value longitude; + final Value iso; + final Value make; + final Value model; + final Value lens; + final Value orientation; + final Value timeZone; + final Value rating; + final Value projectionType; + const RemoteExifEntityCompanion({ + this.assetId = const Value.absent(), + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }); + RemoteExifEntityCompanion.insert({ + required String assetId, + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }) : assetId = Value(assetId); + static Insertable custom({ + Expression? assetId, + Expression? city, + Expression? state, + Expression? country, + Expression? dateTimeOriginal, + Expression? description, + Expression? height, + Expression? width, + Expression? exposureTime, + Expression? fNumber, + Expression? fileSize, + Expression? focalLength, + Expression? latitude, + Expression? longitude, + Expression? iso, + Expression? make, + Expression? model, + Expression? lens, + Expression? orientation, + Expression? timeZone, + Expression? rating, + Expression? projectionType, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (city != null) 'city': city, + if (state != null) 'state': state, + if (country != null) 'country': country, + if (dateTimeOriginal != null) 'date_time_original': dateTimeOriginal, + if (description != null) 'description': description, + if (height != null) 'height': height, + if (width != null) 'width': width, + if (exposureTime != null) 'exposure_time': exposureTime, + if (fNumber != null) 'f_number': fNumber, + if (fileSize != null) 'file_size': fileSize, + if (focalLength != null) 'focal_length': focalLength, + if (latitude != null) 'latitude': latitude, + if (longitude != null) 'longitude': longitude, + if (iso != null) 'iso': iso, + if (make != null) 'make': make, + if (model != null) 'model': model, + if (lens != null) 'lens': lens, + if (orientation != null) 'orientation': orientation, + if (timeZone != null) 'time_zone': timeZone, + if (rating != null) 'rating': rating, + if (projectionType != null) 'projection_type': projectionType, + }); + } + + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { + return RemoteExifEntityCompanion( + assetId: assetId ?? this.assetId, + city: city ?? this.city, + state: state ?? this.state, + country: country ?? this.country, + dateTimeOriginal: dateTimeOriginal ?? this.dateTimeOriginal, + description: description ?? this.description, + height: height ?? this.height, + width: width ?? this.width, + exposureTime: exposureTime ?? this.exposureTime, + fNumber: fNumber ?? this.fNumber, + fileSize: fileSize ?? this.fileSize, + focalLength: focalLength ?? this.focalLength, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + iso: iso ?? this.iso, + make: make ?? this.make, + model: model ?? this.model, + lens: lens ?? this.lens, + orientation: orientation ?? this.orientation, + timeZone: timeZone ?? this.timeZone, + rating: rating ?? this.rating, + projectionType: projectionType ?? this.projectionType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (city.present) { + map['city'] = Variable(city.value); + } + if (state.present) { + map['state'] = Variable(state.value); + } + if (country.present) { + map['country'] = Variable(country.value); + } + if (dateTimeOriginal.present) { + map['date_time_original'] = Variable(dateTimeOriginal.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (exposureTime.present) { + map['exposure_time'] = Variable(exposureTime.value); + } + if (fNumber.present) { + map['f_number'] = Variable(fNumber.value); + } + if (fileSize.present) { + map['file_size'] = Variable(fileSize.value); + } + if (focalLength.present) { + map['focal_length'] = Variable(focalLength.value); + } + if (latitude.present) { + map['latitude'] = Variable(latitude.value); + } + if (longitude.present) { + map['longitude'] = Variable(longitude.value); + } + if (iso.present) { + map['iso'] = Variable(iso.value); + } + if (make.present) { + map['make'] = Variable(make.value); + } + if (model.present) { + map['model'] = Variable(model.value); + } + if (lens.present) { + map['lens'] = Variable(lens.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + if (timeZone.present) { + map['time_zone'] = Variable(timeZone.value); + } + if (rating.present) { + map['rating'] = Variable(rating.value); + } + if (projectionType.present) { + map['projection_type'] = Variable(projectionType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn order = GeneratedColumn( + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, + ); + } + + @override + RemoteAlbumEntity createAlias(String alias) { + return RemoteAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String description; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String? thumbnailAssetId; + final bool isActivityEnabled; + final int order; + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['description'] = Variable(description); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || thumbnailAssetId != null) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId); + } + map['is_activity_enabled'] = Variable(isActivityEnabled); + map['order'] = Variable(order); + return map; + } + + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + description: serializer.fromJson(json['description']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + thumbnailAssetId: serializer.fromJson(json['thumbnailAssetId']), + isActivityEnabled: serializer.fromJson(json['isActivityEnabled']), + order: serializer.fromJson(json['order']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'description': serializer.toJson(description), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'thumbnailAssetId': serializer.toJson(thumbnailAssetId), + 'isActivityEnabled': serializer.toJson(isActivityEnabled), + 'order': serializer.toJson(order), + }; + } + + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { + return RemoteAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + description: data.description.present + ? data.description.value + : this.description, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, + order: data.order.present ? data.order.value : this.order, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.description == this.description && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.thumbnailAssetId == this.thumbnailAssetId && + other.isActivityEnabled == this.isActivityEnabled && + other.order == this.order); +} + +class RemoteAlbumEntityCompanion + extends UpdateCompanion { + final Value id; + final Value name; + final Value description; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value thumbnailAssetId; + final Value isActivityEnabled; + final Value order; + const RemoteAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + this.order = const Value.absent(), + }); + RemoteAlbumEntityCompanion.insert({ + required String id, + required String name, + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + required int order, + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? description, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? thumbnailAssetId, + Expression? isActivityEnabled, + Expression? order, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (description != null) 'description': description, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (thumbnailAssetId != null) 'thumbnail_asset_id': thumbnailAssetId, + if (isActivityEnabled != null) 'is_activity_enabled': isActivityEnabled, + if (order != null) 'order': order, + }); + } + + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { + return RemoteAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (thumbnailAssetId.present) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId.value); + } + if (isActivityEnabled.present) { + map['is_activity_enabled'] = Variable(isActivityEnabled.value); + } + if (order.present) { + map['order'] = Variable(order.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + RemoteAlbumAssetEntity createAlias(String alias) { + return RemoteAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data, + ) { + return RemoteAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const RemoteAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + RemoteAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return RemoteAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumUserEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn role = GeneratedColumn( + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [albumId, userId, role]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_user_entity'; + @override + Set get $primaryKey => {albumId, userId}; + @override + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumUserEntityData( + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, + ); + } + + @override + RemoteAlbumUserEntity createAlias(String alias) { + return RemoteAlbumUserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { + final String albumId; + final String userId; + final int role; + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['album_id'] = Variable(albumId); + map['user_id'] = Variable(userId); + map['role'] = Variable(role); + return map; + } + + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumUserEntityData( + albumId: serializer.fromJson(json['albumId']), + userId: serializer.fromJson(json['userId']), + role: serializer.fromJson(json['role']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'albumId': serializer.toJson(albumId), + 'userId': serializer.toJson(userId), + 'role': serializer.toJson(role), + }; + } + + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data, + ) { + return RemoteAlbumUserEntityData( + albumId: data.albumId.present ? data.albumId.value : this.albumId, + userId: data.userId.present ? data.userId.value : this.userId, + role: data.role.present ? data.role.value : this.role, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityData(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(albumId, userId, role); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumUserEntityData && + other.albumId == this.albumId && + other.userId == this.userId && + other.role == this.role); +} + +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { + final Value albumId; + final Value userId; + final Value role; + const RemoteAlbumUserEntityCompanion({ + this.albumId = const Value.absent(), + this.userId = const Value.absent(), + this.role = const Value.absent(), + }); + RemoteAlbumUserEntityCompanion.insert({ + required String albumId, + required String userId, + required int role, + }) : albumId = Value(albumId), + userId = Value(userId), + role = Value(role); + static Insertable custom({ + Expression? albumId, + Expression? userId, + Expression? role, + }) { + return RawValuesInsertable({ + if (albumId != null) 'album_id': albumId, + if (userId != null) 'user_id': userId, + if (role != null) 'role': role, + }); + } + + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { + return RemoteAlbumUserEntityCompanion( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (role.present) { + map['role'] = Variable(role.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityCompanion(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } +} + +class MemoryEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn data = GeneratedColumn( + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_entity'; + @override + Set get $primaryKey => {id}; + @override + MemoryEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), + ); + } + + @override + MemoryEntity createAlias(String alias) { + return MemoryEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final DateTime? deletedAt; + final String ownerId; + final int type; + final String data; + final bool isSaved; + final DateTime memoryAt; + final DateTime? seenAt; + final DateTime? showAt; + final DateTime? hideAt; + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + map['owner_id'] = Variable(ownerId); + map['type'] = Variable(type); + map['data'] = Variable(data); + map['is_saved'] = Variable(isSaved); + map['memory_at'] = Variable(memoryAt); + if (!nullToAbsent || seenAt != null) { + map['seen_at'] = Variable(seenAt); + } + if (!nullToAbsent || showAt != null) { + map['show_at'] = Variable(showAt); + } + if (!nullToAbsent || hideAt != null) { + map['hide_at'] = Variable(hideAt); + } + return map; + } + + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + deletedAt: serializer.fromJson(json['deletedAt']), + ownerId: serializer.fromJson(json['ownerId']), + type: serializer.fromJson(json['type']), + data: serializer.fromJson(json['data']), + isSaved: serializer.fromJson(json['isSaved']), + memoryAt: serializer.fromJson(json['memoryAt']), + seenAt: serializer.fromJson(json['seenAt']), + showAt: serializer.fromJson(json['showAt']), + hideAt: serializer.fromJson(json['hideAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'deletedAt': serializer.toJson(deletedAt), + 'ownerId': serializer.toJson(ownerId), + 'type': serializer.toJson(type), + 'data': serializer.toJson(data), + 'isSaved': serializer.toJson(isSaved), + 'memoryAt': serializer.toJson(memoryAt), + 'seenAt': serializer.toJson(seenAt), + 'showAt': serializer.toJson(showAt), + 'hideAt': serializer.toJson(hideAt), + }; + } + + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); + MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { + return MemoryEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + type: data.type.present ? data.type.value : this.type, + data: data.data.present ? data.data.value : this.data, + isSaved: data.isSaved.present ? data.isSaved.value : this.isSaved, + memoryAt: data.memoryAt.present ? data.memoryAt.value : this.memoryAt, + seenAt: data.seenAt.present ? data.seenAt.value : this.seenAt, + showAt: data.showAt.present ? data.showAt.value : this.showAt, + hideAt: data.hideAt.present ? data.hideAt.value : this.hideAt, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.deletedAt == this.deletedAt && + other.ownerId == this.ownerId && + other.type == this.type && + other.data == this.data && + other.isSaved == this.isSaved && + other.memoryAt == this.memoryAt && + other.seenAt == this.seenAt && + other.showAt == this.showAt && + other.hideAt == this.hideAt); +} + +class MemoryEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value deletedAt; + final Value ownerId; + final Value type; + final Value data; + final Value isSaved; + final Value memoryAt; + final Value seenAt; + final Value showAt; + final Value hideAt; + const MemoryEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.type = const Value.absent(), + this.data = const Value.absent(), + this.isSaved = const Value.absent(), + this.memoryAt = const Value.absent(), + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }); + MemoryEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + required String ownerId, + required int type, + required String data, + this.isSaved = const Value.absent(), + required DateTime memoryAt, + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? deletedAt, + Expression? ownerId, + Expression? type, + Expression? data, + Expression? isSaved, + Expression? memoryAt, + Expression? seenAt, + Expression? showAt, + Expression? hideAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (deletedAt != null) 'deleted_at': deletedAt, + if (ownerId != null) 'owner_id': ownerId, + if (type != null) 'type': type, + if (data != null) 'data': data, + if (isSaved != null) 'is_saved': isSaved, + if (memoryAt != null) 'memory_at': memoryAt, + if (seenAt != null) 'seen_at': seenAt, + if (showAt != null) 'show_at': showAt, + if (hideAt != null) 'hide_at': hideAt, + }); + } + + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { + return MemoryEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt ?? this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt ?? this.seenAt, + showAt: showAt ?? this.showAt, + hideAt: hideAt ?? this.hideAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (data.present) { + map['data'] = Variable(data.value); + } + if (isSaved.present) { + map['is_saved'] = Variable(isSaved.value); + } + if (memoryAt.present) { + map['memory_at'] = Variable(memoryAt.value); + } + if (seenAt.present) { + map['seen_at'] = Variable(seenAt.value); + } + if (showAt.present) { + map['show_at'] = Variable(showAt.value); + } + if (hideAt.present) { + map['hide_at'] = Variable(hideAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } +} + +class MemoryAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, memoryId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_asset_entity'; + @override + Set get $primaryKey => {assetId, memoryId}; + @override + MemoryAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, + ); + } + + @override + MemoryAssetEntity createAlias(String alias) { + return MemoryAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String memoryId; + const MemoryAssetEntityData({required this.assetId, required this.memoryId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['memory_id'] = Variable(memoryId); + return map; + } + + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + memoryId: serializer.fromJson(json['memoryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'memoryId': serializer.toJson(memoryId), + }; + } + + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + MemoryAssetEntityData copyWithCompanion(MemoryAssetEntityCompanion data) { + return MemoryAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + memoryId: data.memoryId.present ? data.memoryId.value : this.memoryId, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, memoryId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); +} + +class MemoryAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value memoryId; + const MemoryAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.memoryId = const Value.absent(), + }); + MemoryAssetEntityCompanion.insert({ + required String assetId, + required String memoryId, + }) : assetId = Value(assetId), + memoryId = Value(memoryId); + static Insertable custom({ + Expression? assetId, + Expression? memoryId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (memoryId != null) 'memory_id': memoryId, + }); + } + + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { + return MemoryAssetEntityCompanion( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (memoryId.present) { + map['memory_id'] = Variable(memoryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } +} + +class PersonEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PersonEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'person_entity'; + @override + Set get $primaryKey => {id}; + @override + PersonEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PersonEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), + ); + } + + @override + PersonEntity createAlias(String alias) { + return PersonEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PersonEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['name'] = Variable(name); + if (!nullToAbsent || faceAssetId != null) { + map['face_asset_id'] = Variable(faceAssetId); + } + map['is_favorite'] = Variable(isFavorite); + map['is_hidden'] = Variable(isHidden); + if (!nullToAbsent || color != null) { + map['color'] = Variable(color); + } + if (!nullToAbsent || birthDate != null) { + map['birth_date'] = Variable(birthDate); + } + return map; + } + + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PersonEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + name: serializer.fromJson(json['name']), + faceAssetId: serializer.fromJson(json['faceAssetId']), + isFavorite: serializer.fromJson(json['isFavorite']), + isHidden: serializer.fromJson(json['isHidden']), + color: serializer.fromJson(json['color']), + birthDate: serializer.fromJson(json['birthDate']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'name': serializer.toJson(name), + 'faceAssetId': serializer.toJson(faceAssetId), + 'isFavorite': serializer.toJson(isFavorite), + 'isHidden': serializer.toJson(isHidden), + 'color': serializer.toJson(color), + 'birthDate': serializer.toJson(birthDate), + }; + } + + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); + PersonEntityData copyWithCompanion(PersonEntityCompanion data) { + return PersonEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + name: data.name.present ? data.name.value : this.name, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + color: data.color.present ? data.color.value : this.color, + birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, + ); + } + + @override + String toString() { + return (StringBuffer('PersonEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PersonEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.name == this.name && + other.faceAssetId == this.faceAssetId && + other.isFavorite == this.isFavorite && + other.isHidden == this.isHidden && + other.color == this.color && + other.birthDate == this.birthDate); +} + +class PersonEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value name; + final Value faceAssetId; + final Value isFavorite; + final Value isHidden; + final Value color; + final Value birthDate; + const PersonEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.name = const Value.absent(), + this.faceAssetId = const Value.absent(), + this.isFavorite = const Value.absent(), + this.isHidden = const Value.absent(), + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }); + PersonEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String name, + this.faceAssetId = const Value.absent(), + required bool isFavorite, + required bool isHidden, + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? name, + Expression? faceAssetId, + Expression? isFavorite, + Expression? isHidden, + Expression? color, + Expression? birthDate, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (name != null) 'name': name, + if (faceAssetId != null) 'face_asset_id': faceAssetId, + if (isFavorite != null) 'is_favorite': isFavorite, + if (isHidden != null) 'is_hidden': isHidden, + if (color != null) 'color': color, + if (birthDate != null) 'birth_date': birthDate, + }); + } + + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { + return PersonEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (faceAssetId.present) { + map['face_asset_id'] = Variable(faceAssetId.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (isHidden.present) { + map['is_hidden'] = Variable(isHidden.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (birthDate.present) { + map['birth_date'] = Variable(birthDate.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PersonEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } +} + +class AssetFaceEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + AssetFaceEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn personId = GeneratedColumn( + 'person_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn imageWidth = GeneratedColumn( + 'image_width', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn imageHeight = GeneratedColumn( + 'image_height', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX1 = GeneratedColumn( + 'bounding_box_x1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY1 = GeneratedColumn( + 'bounding_box_y1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX2 = GeneratedColumn( + 'bounding_box_x2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY2 = GeneratedColumn( + 'bounding_box_y2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn sourceType = GeneratedColumn( + 'source_type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'asset_face_entity'; + @override + Set get $primaryKey => {id}; + @override + AssetFaceEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return AssetFaceEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + personId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}person_id'], + ), + imageWidth: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_width'], + )!, + imageHeight: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_height'], + )!, + boundingBoxX1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x1'], + )!, + boundingBoxY1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y1'], + )!, + boundingBoxX2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x2'], + )!, + boundingBoxY2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y2'], + )!, + sourceType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}source_type'], + )!, + ); + } + + @override + AssetFaceEntity createAlias(String alias) { + return AssetFaceEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AssetFaceEntityData extends DataClass + implements Insertable { + final String id; + final String assetId; + final String? personId; + final int imageWidth; + final int imageHeight; + final int boundingBoxX1; + final int boundingBoxY1; + final int boundingBoxX2; + final int boundingBoxY2; + final String sourceType; + const AssetFaceEntityData({ + required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || personId != null) { + map['person_id'] = Variable(personId); + } + map['image_width'] = Variable(imageWidth); + map['image_height'] = Variable(imageHeight); + map['bounding_box_x1'] = Variable(boundingBoxX1); + map['bounding_box_y1'] = Variable(boundingBoxY1); + map['bounding_box_x2'] = Variable(boundingBoxX2); + map['bounding_box_y2'] = Variable(boundingBoxY2); + map['source_type'] = Variable(sourceType); + return map; + } + + factory AssetFaceEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return AssetFaceEntityData( + id: serializer.fromJson(json['id']), + assetId: serializer.fromJson(json['assetId']), + personId: serializer.fromJson(json['personId']), + imageWidth: serializer.fromJson(json['imageWidth']), + imageHeight: serializer.fromJson(json['imageHeight']), + boundingBoxX1: serializer.fromJson(json['boundingBoxX1']), + boundingBoxY1: serializer.fromJson(json['boundingBoxY1']), + boundingBoxX2: serializer.fromJson(json['boundingBoxX2']), + boundingBoxY2: serializer.fromJson(json['boundingBoxY2']), + sourceType: serializer.fromJson(json['sourceType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'assetId': serializer.toJson(assetId), + 'personId': serializer.toJson(personId), + 'imageWidth': serializer.toJson(imageWidth), + 'imageHeight': serializer.toJson(imageHeight), + 'boundingBoxX1': serializer.toJson(boundingBoxX1), + 'boundingBoxY1': serializer.toJson(boundingBoxY1), + 'boundingBoxX2': serializer.toJson(boundingBoxX2), + 'boundingBoxY2': serializer.toJson(boundingBoxY2), + 'sourceType': serializer.toJson(sourceType), + }; + } + + AssetFaceEntityData copyWith({ + String? id, + String? assetId, + Value personId = const Value.absent(), + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType, + }) => AssetFaceEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId.present ? personId.value : this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + AssetFaceEntityData copyWithCompanion(AssetFaceEntityCompanion data) { + return AssetFaceEntityData( + id: data.id.present ? data.id.value : this.id, + assetId: data.assetId.present ? data.assetId.value : this.assetId, + personId: data.personId.present ? data.personId.value : this.personId, + imageWidth: data.imageWidth.present + ? data.imageWidth.value + : this.imageWidth, + imageHeight: data.imageHeight.present + ? data.imageHeight.value + : this.imageHeight, + boundingBoxX1: data.boundingBoxX1.present + ? data.boundingBoxX1.value + : this.boundingBoxX1, + boundingBoxY1: data.boundingBoxY1.present + ? data.boundingBoxY1.value + : this.boundingBoxY1, + boundingBoxX2: data.boundingBoxX2.present + ? data.boundingBoxX2.value + : this.boundingBoxX2, + boundingBoxY2: data.boundingBoxY2.present + ? data.boundingBoxY2.value + : this.boundingBoxY2, + sourceType: data.sourceType.present + ? data.sourceType.value + : this.sourceType, + ); + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityData(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is AssetFaceEntityData && + other.id == this.id && + other.assetId == this.assetId && + other.personId == this.personId && + other.imageWidth == this.imageWidth && + other.imageHeight == this.imageHeight && + other.boundingBoxX1 == this.boundingBoxX1 && + other.boundingBoxY1 == this.boundingBoxY1 && + other.boundingBoxX2 == this.boundingBoxX2 && + other.boundingBoxY2 == this.boundingBoxY2 && + other.sourceType == this.sourceType); +} + +class AssetFaceEntityCompanion extends UpdateCompanion { + final Value id; + final Value assetId; + final Value personId; + final Value imageWidth; + final Value imageHeight; + final Value boundingBoxX1; + final Value boundingBoxY1; + final Value boundingBoxX2; + final Value boundingBoxY2; + final Value sourceType; + const AssetFaceEntityCompanion({ + this.id = const Value.absent(), + this.assetId = const Value.absent(), + this.personId = const Value.absent(), + this.imageWidth = const Value.absent(), + this.imageHeight = const Value.absent(), + this.boundingBoxX1 = const Value.absent(), + this.boundingBoxY1 = const Value.absent(), + this.boundingBoxX2 = const Value.absent(), + this.boundingBoxY2 = const Value.absent(), + this.sourceType = const Value.absent(), + }); + AssetFaceEntityCompanion.insert({ + required String id, + required String assetId, + this.personId = const Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) : id = Value(id), + assetId = Value(assetId), + imageWidth = Value(imageWidth), + imageHeight = Value(imageHeight), + boundingBoxX1 = Value(boundingBoxX1), + boundingBoxY1 = Value(boundingBoxY1), + boundingBoxX2 = Value(boundingBoxX2), + boundingBoxY2 = Value(boundingBoxY2), + sourceType = Value(sourceType); + static Insertable custom({ + Expression? id, + Expression? assetId, + Expression? personId, + Expression? imageWidth, + Expression? imageHeight, + Expression? boundingBoxX1, + Expression? boundingBoxY1, + Expression? boundingBoxX2, + Expression? boundingBoxY2, + Expression? sourceType, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (assetId != null) 'asset_id': assetId, + if (personId != null) 'person_id': personId, + if (imageWidth != null) 'image_width': imageWidth, + if (imageHeight != null) 'image_height': imageHeight, + if (boundingBoxX1 != null) 'bounding_box_x1': boundingBoxX1, + if (boundingBoxY1 != null) 'bounding_box_y1': boundingBoxY1, + if (boundingBoxX2 != null) 'bounding_box_x2': boundingBoxX2, + if (boundingBoxY2 != null) 'bounding_box_y2': boundingBoxY2, + if (sourceType != null) 'source_type': sourceType, + }); + } + + AssetFaceEntityCompanion copyWith({ + Value? id, + Value? assetId, + Value? personId, + Value? imageWidth, + Value? imageHeight, + Value? boundingBoxX1, + Value? boundingBoxY1, + Value? boundingBoxX2, + Value? boundingBoxY2, + Value? sourceType, + }) { + return AssetFaceEntityCompanion( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId ?? this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (personId.present) { + map['person_id'] = Variable(personId.value); + } + if (imageWidth.present) { + map['image_width'] = Variable(imageWidth.value); + } + if (imageHeight.present) { + map['image_height'] = Variable(imageHeight.value); + } + if (boundingBoxX1.present) { + map['bounding_box_x1'] = Variable(boundingBoxX1.value); + } + if (boundingBoxY1.present) { + map['bounding_box_y1'] = Variable(boundingBoxY1.value); + } + if (boundingBoxX2.present) { + map['bounding_box_x2'] = Variable(boundingBoxX2.value); + } + if (boundingBoxY2.present) { + map['bounding_box_y2'] = Variable(boundingBoxY2.value); + } + if (sourceType.present) { + map['source_type'] = Variable(sourceType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityCompanion(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV6 extends GeneratedDatabase { + DatabaseAtV6(QueryExecutor e) : super(e); + late final UserEntity userEntity = UserEntity(this); + late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); + late final StackEntity stackEntity = StackEntity(this); + late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); + late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + late final Index idxRemoteAssetOwnerChecksum = Index( + 'idx_remote_asset_owner_checksum', + 'CREATE INDEX idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', + ); + late final Index uQRemoteAssetsOwnerChecksum = Index( + 'UQ_remote_assets_owner_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)', + ); + late final Index uQRemoteAssetsOwnerLibraryChecksum = Index( + 'UQ_remote_assets_owner_library_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); + late final PartnerEntity partnerEntity = PartnerEntity(this); + late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); + late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); + late final MemoryEntity memoryEntity = MemoryEntity(this); + late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); + late final PersonEntity personEntity = PersonEntity(this); + late final AssetFaceEntity assetFaceEntity = AssetFaceEntity(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + idxRemoteAssetOwnerChecksum, + uQRemoteAssetsOwnerChecksum, + uQRemoteAssetsOwnerLibraryChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + ]; + @override + int get schemaVersion => 6; + @override + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); +} diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 9a1e6a6937..9dc7540dd8 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -14786,6 +14786,10 @@ "isFavorite": { "type": "boolean" }, + "libraryId": { + "nullable": true, + "type": "string" + }, "livePhotoVideoId": { "nullable": true, "type": "string" @@ -14832,6 +14836,7 @@ "fileModifiedAt", "id", "isFavorite", + "libraryId", "livePhotoVideoId", "localDateTime", "originalFileName", diff --git a/server/src/database.ts b/server/src/database.ts index 052955636b..e7946cd8fb 100644 --- a/server/src/database.ts +++ b/server/src/database.ts @@ -354,6 +354,7 @@ export const columns = { 'asset.duration', 'asset.livePhotoVideoId', 'asset.stackId', + 'asset.libraryId', ], syncAlbumUser: ['album_user.albumsId as albumId', 'album_user.usersId as userId', 'album_user.role'], syncStack: ['stack.id', 'stack.createdAt', 'stack.updatedAt', 'stack.primaryAssetId', 'stack.ownerId'], diff --git a/server/src/dtos/sync.dto.ts b/server/src/dtos/sync.dto.ts index 66061e7bbe..9c304c0d3c 100644 --- a/server/src/dtos/sync.dto.ts +++ b/server/src/dtos/sync.dto.ts @@ -115,6 +115,7 @@ export class SyncAssetV1 { visibility!: AssetVisibility; livePhotoVideoId!: string | null; stackId!: string | null; + libraryId!: string | null; } @ExtraModel() diff --git a/server/src/queries/sync.repository.sql b/server/src/queries/sync.repository.sql index 5c80460158..28c6f32acc 100644 --- a/server/src/queries/sync.repository.sql +++ b/server/src/queries/sync.repository.sql @@ -66,6 +66,7 @@ select "asset"."duration", "asset"."livePhotoVideoId", "asset"."stackId", + "asset"."libraryId", "asset"."updateId" from "asset" @@ -95,6 +96,7 @@ select "asset"."duration", "asset"."livePhotoVideoId", "asset"."stackId", + "asset"."libraryId", "asset"."updateId" from "asset" @@ -357,6 +359,7 @@ select "asset"."duration", "asset"."livePhotoVideoId", "asset"."stackId", + "asset"."libraryId", "asset"."updateId" from "asset" @@ -605,6 +608,7 @@ select "asset"."duration", "asset"."livePhotoVideoId", "asset"."stackId", + "asset"."libraryId", "asset"."updateId" from "asset" @@ -652,6 +656,7 @@ select "asset"."duration", "asset"."livePhotoVideoId", "asset"."stackId", + "asset"."libraryId", "asset"."updateId" from "asset" diff --git a/server/src/services/job.service.ts b/server/src/services/job.service.ts index c67f3af39f..0116c869c6 100644 --- a/server/src/services/job.service.ts +++ b/server/src/services/job.service.ts @@ -382,6 +382,7 @@ export class JobService extends BaseService { visibility: asset.visibility, livePhotoVideoId: asset.livePhotoVideoId, stackId: asset.stackId, + libraryId: asset.libraryId, }, exif: { assetId: exif.assetId, diff --git a/server/test/medium/specs/sync/sync-album-asset.spec.ts b/server/test/medium/specs/sync/sync-album-asset.spec.ts index 9a42c0f027..3002b99071 100644 --- a/server/test/medium/specs/sync/sync-album-asset.spec.ts +++ b/server/test/medium/specs/sync/sync-album-asset.spec.ts @@ -38,6 +38,7 @@ describe(SyncRequestType.AlbumAssetsV1, () => { duration: '0:10:00.00000', livePhotoVideoId: null, stackId: null, + libraryId: null, }); const { album } = await ctx.newAlbum({ ownerId: user2.id }); await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id }); @@ -64,6 +65,7 @@ describe(SyncRequestType.AlbumAssetsV1, () => { duration: asset.duration, livePhotoVideoId: asset.livePhotoVideoId, stackId: asset.stackId, + libraryId: asset.libraryId, }, type: SyncEntityType.AlbumAssetV1, }, diff --git a/server/test/medium/specs/sync/sync-asset.spec.ts b/server/test/medium/specs/sync/sync-asset.spec.ts index 52d6bcb524..ce83eed98c 100644 --- a/server/test/medium/specs/sync/sync-asset.spec.ts +++ b/server/test/medium/specs/sync/sync-asset.spec.ts @@ -36,6 +36,7 @@ describe(SyncEntityType.AssetV1, () => { localDateTime: date, deletedAt: null, duration: '0:10:00.00000', + libraryId: null, }); const response = await ctx.syncStream(auth, [SyncRequestType.AssetsV1]); @@ -59,6 +60,7 @@ describe(SyncEntityType.AssetV1, () => { duration: asset.duration, stackId: null, livePhotoVideoId: null, + libraryId: asset.libraryId, }, type: 'AssetV1', }, diff --git a/server/test/medium/specs/sync/sync-partner-asset.spec.ts b/server/test/medium/specs/sync/sync-partner-asset.spec.ts index 2daa750bf3..e9dc7403bd 100644 --- a/server/test/medium/specs/sync/sync-partner-asset.spec.ts +++ b/server/test/medium/specs/sync/sync-partner-asset.spec.ts @@ -40,6 +40,7 @@ describe(SyncRequestType.PartnerAssetsV1, () => { localDateTime: date, deletedAt: null, duration: '0:10:00.00000', + libraryId: null, }); await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id }); @@ -65,6 +66,7 @@ describe(SyncRequestType.PartnerAssetsV1, () => { duration: asset.duration, stackId: null, livePhotoVideoId: null, + libraryId: asset.libraryId, }, type: SyncEntityType.PartnerAssetV1, }, From d73335ecbcd21ed611381a4ed83b6b670006a20a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric?= Date: Wed, 30 Jul 2025 21:13:19 +0200 Subject: [PATCH 126/748] docs: add config example for Authelia (#20223) --- docs/docs/administration/oauth.md | 89 +++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 3 deletions(-) diff --git a/docs/docs/administration/oauth.md b/docs/docs/administration/oauth.md index 833b70f77a..7450ae1b08 100644 --- a/docs/docs/administration/oauth.md +++ b/docs/docs/administration/oauth.md @@ -64,7 +64,7 @@ Once you have a new OAuth client application configured, Immich can be configure | Storage Label Claim | string | preferred_username | Claim mapping for the user's storage label**¹** | | Role Claim | string | immich_role | Claim mapping for the user's role. (should return "user" or "admin")**¹** | | Storage Quota Claim | string | immich_quota | Claim mapping for the user's storage**¹** | -| Default Storage Quota (GiB) | number | 0 | Default quota for user without storage quota claim (Enter 0 for unlimited quota) | +| Default Storage Quota (GiB) | number | 0 | Default quota for user without storage quota claim (empty for unlimited quota) | | Button Text | string | Login with OAuth | Text for the OAuth button on the web | | Auto Register | boolean | true | When true, will automatically register a user the first time they sign in | | [Auto Launch](#auto-launch) | boolean | false | When true, will skip the login page and automatically start the OAuth login process | @@ -106,6 +106,89 @@ Immich has a route (`/api/oauth/mobile-redirect`) that is already configured to ## Example Configuration +
+Authelia Example + +### Authelia Example + +Here's an example of OAuth configured for Authelia: + +This assumes there exist an attribute `immichquota` in the user schema, which is used to set the user's storage quota in Immich. +The configuration concerning the quota is optional. + +```yaml +authentication_backend: + ldap: + # The LDAP server configuration goes here. + # See: https://www.authelia.com/c/ldap + attributes: + extra: + immichquota: # The attribute name from LDAP + name: 'immich_quota' + multi_valued: false + value_type: 'integer' +identity_providers: + oidc: + ## The other portions of the mandatory OpenID Connect 1.0 configuration go here. + ## See: https://www.authelia.com/c/oidc + claims_policies: + immich_policy: + custom_claims: + immich_quota: + attribute: 'immich_quota' + scopes: + immich_scope: + claims: + - 'immich_quota' + + clients: + - client_id: 'immich' + client_name: 'Immich' + # https://www.authelia.com/integration/openid-connect/frequently-asked-questions/#how-do-i-generate-a-client-identifier-or-client-secret + client_secret: $pbkdf2-sha512$310000$c8p78n7pUMln0jzvd4aK4Q$JNRBzwAo0ek5qKn50cFzzvE9RXV88h1wJn5KGiHrD0YKtZaR/nCb2CJPOsKaPK0hjf.9yHxzQGZziziccp6Yng' + public: false + require_pkce: false + redirect_uris: + - 'https://example.immich.app/auth/login' + - 'https://example.immich.app/user-settings' + - 'app.immich:///oauth-callback' + scopes: + - 'openid' + - 'profile' + - 'email' + - 'immich_scope' + claims_policy: 'immich_policy' + response_types: + - 'code' + grant_types: + - 'authorization_code' + id_token_signed_response_alg: 'RS256' + userinfo_signed_response_alg: 'RS256' + token_endpoint_auth_method: 'client_secret_post' +``` + +Configuration of OAuth in Immich System Settings + +| Setting | Value | +| ---------------------------------- | ------------------------------------------------------------------- | +| Issuer URL | `https://example.immich.app/.well-known/openid-configuration` | +| Client ID | immich | +| Client Secret | 0v89FXkQOWO\***\*\*\*\*\***\*\*\***\*\*\*\*\***mprbvXD549HH6s1iw... | +| Token Endpoint Auth Method | client_secret_post | +| Scope | openid email profile immich_scope | +| ID Token Signed Response Algorithm | RS256 | +| Userinfo Signed Response Algorithm | RS256 | +| Storage Label Claim | uid | +| Storage Quota Claim | immich_quota | +| Default Storage Quota (GiB) | 0 (empty for unlimited quota) | +| Button Text | Sign in with Authelia (optional) | +| Auto Register | Enabled (optional) | +| Auto Launch | Enabled (optional) | +| Mobile Redirect URI Override | Disable | +| Mobile Redirect URI | | + +
+
Authentik Example @@ -128,7 +211,7 @@ Configuration of OAuth in Immich System Settings | Signing Algorithm | RS256 | | Storage Label Claim | preferred_username | | Storage Quota Claim | immich_quota | -| Default Storage Quota (GiB) | 0 (0 for unlimited quota) | +| Default Storage Quota (GiB) | 0 (empty for unlimited quota) | | Button Text | Sign in with Authentik (optional) | | Auto Register | Enabled (optional) | | Auto Launch | Enabled (optional) | @@ -159,7 +242,7 @@ Configuration of OAuth in Immich System Settings | Signing Algorithm | RS256 | | Storage Label Claim | preferred_username | | Storage Quota Claim | immich_quota | -| Default Storage Quota (GiB) | 0 (0 for unlimited quota) | +| Default Storage Quota (GiB) | 0 (empty for unlimited quota) | | Button Text | Sign in with Google (optional) | | Auto Register | Enabled (optional) | | Auto Launch | Enabled | From f416342effe89a3b69ea937e8201462e8b7e721d Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Thu, 31 Jul 2025 01:02:38 +0530 Subject: [PATCH 127/748] fix: clear local file cache before upload (#20448) * clear local file cache before upload * clear cache during hashing * fix test * add button to clear cache manually * add button to clear cache manually --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Co-authored-by: Alex --- i18n/en.json | 1 + mobile/lib/domain/services/hash.service.dart | 1 + .../repositories/storage.repository.dart | 10 ++++++++++ mobile/lib/services/upload.service.dart | 4 ++++ .../beta_sync_settings/beta_sync_settings.dart | 13 +++++++++++++ mobile/test/domain/services/hash_service_test.dart | 1 + 6 files changed, 30 insertions(+) diff --git a/i18n/en.json b/i18n/en.json index 94f6920745..c4cf101b73 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -653,6 +653,7 @@ "clear": "Clear", "clear_all": "Clear all", "clear_all_recent_searches": "Clear all recent searches", + "clear_file_cache": "Clear File Cache", "clear_message": "Clear message", "clear_value": "Clear value", "client_cert_dialog_msg_confirm": "OK", diff --git a/mobile/lib/domain/services/hash.service.dart b/mobile/lib/domain/services/hash.service.dart index 2eb9aec4db..2a07320906 100644 --- a/mobile/lib/domain/services/hash.service.dart +++ b/mobile/lib/domain/services/hash.service.dart @@ -104,6 +104,7 @@ class HashService { DLog.log("Hashed ${hashed.length}/${toHash.length} assets"); await _localAssetRepository.updateHashes(hashed); + await _storageRepository.clearCache(); } } diff --git a/mobile/lib/infrastructure/repositories/storage.repository.dart b/mobile/lib/infrastructure/repositories/storage.repository.dart index 0cf4f20ba8..18302aeb7d 100644 --- a/mobile/lib/infrastructure/repositories/storage.repository.dart +++ b/mobile/lib/infrastructure/repositories/storage.repository.dart @@ -66,4 +66,14 @@ class StorageRepository { } return entity; } + + Future clearCache() async { + final log = Logger('StorageRepository'); + + try { + await PhotoManager.clearFileCache(); + } catch (error, stackTrace) { + log.warning("Error clearing cache", error, stackTrace); + } + } } diff --git a/mobile/lib/services/upload.service.dart b/mobile/lib/services/upload.service.dart index dba3817b2c..9e5193c8cb 100644 --- a/mobile/lib/services/upload.service.dart +++ b/mobile/lib/services/upload.service.dart @@ -99,6 +99,7 @@ class UploadService { } Future manualBackup(List localAssets) async { + await _storageRepository.clearCache(); List tasks = []; for (final asset in localAssets) { final task = await _getUploadTask( @@ -120,6 +121,8 @@ class UploadService { /// Build the upload tasks /// Enqueue the tasks Future startBackup(String userId, void Function(EnqueueStatus status) onEnqueueTasks) async { + await _storageRepository.clearCache(); + shouldAbortQueuingTasks = false; final candidates = await _backupRepository.getCandidates(userId); @@ -159,6 +162,7 @@ class UploadService { Future cancelBackup() async { shouldAbortQueuingTasks = true; + await _storageRepository.clearCache(); await _uploadRepository.reset(kBackupGroup); await _uploadRepository.deleteDatabaseRecords(kBackupGroup); diff --git a/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart b/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart index 12a3c51e8e..8916fdd92b 100644 --- a/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart +++ b/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart @@ -10,6 +10,7 @@ import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/providers/infrastructure/memory.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/storage.provider.dart'; import 'package:immich_mobile/providers/sync_status.provider.dart'; import 'package:immich_mobile/widgets/settings/beta_sync_settings/entity_count_tile.dart'; import 'package:path/path.dart' as path; @@ -104,6 +105,10 @@ class BetaSyncSettings extends HookConsumerWidget { } } + Future clearFileCache() async { + await ref.read(storageRepositoryProvider).clearCache(); + } + return FutureBuilder>( future: loadCounts(), builder: (context, snapshot) { @@ -241,6 +246,14 @@ class BetaSyncSettings extends HookConsumerWidget { const Divider(height: 1, indent: 16, endIndent: 16), const SizedBox(height: 24), _SectionHeaderText(text: "actions".t(context: context)), + ListTile( + title: Text( + "clear_file_cache".t(context: context), + style: const TextStyle(fontWeight: FontWeight.w500), + ), + leading: const Icon(Icons.playlist_remove_rounded), + onTap: clearFileCache, + ), ListTile( title: Text( "export_database".t(context: context), diff --git a/mobile/test/domain/services/hash_service_test.dart b/mobile/test/domain/services/hash_service_test.dart index 1534b2e914..7969131e7f 100644 --- a/mobile/test/domain/services/hash_service_test.dart +++ b/mobile/test/domain/services/hash_service_test.dart @@ -40,6 +40,7 @@ void main() { registerFallbackValue(LocalAssetStub.image1); when(() => mockAssetRepo.updateHashes(any())).thenAnswer((_) async => {}); + when(() => mockStorageRepo.clearCache()).thenAnswer((_) async => {}); }); group('HashService hashAssets', () { From 86d31d7d291555d5ab1a38d3305ff1cff6ee4736 Mon Sep 17 00:00:00 2001 From: Peter Ombodi Date: Wed, 30 Jul 2025 22:33:55 +0300 Subject: [PATCH 128/748] fix(download): handle completed downloads and refresh albums (#20241) * fix(download): handle completed downloads and refresh albums * fix(download): remove use of outdated AlbumService --------- Co-authored-by: Peter Ombodi --- .../infrastructure/action.provider.dart | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/mobile/lib/providers/infrastructure/action.provider.dart b/mobile/lib/providers/infrastructure/action.provider.dart index 69c0532303..80e27b5970 100644 --- a/mobile/lib/providers/infrastructure/action.provider.dart +++ b/mobile/lib/providers/infrastructure/action.provider.dart @@ -1,10 +1,13 @@ +import 'package:background_downloader/background_downloader.dart'; import 'package:flutter/material.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/models/download/livephotos_medatada.model.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/services/action.service.dart'; +import 'package:immich_mobile/services/download.service.dart'; import 'package:immich_mobile/services/timeline.service.dart'; import 'package:immich_mobile/services/upload.service.dart'; import 'package:logging/logging.dart'; @@ -30,6 +33,7 @@ class ActionNotifier extends Notifier { final Logger _logger = Logger('ActionNotifier'); late ActionService _service; late UploadService _uploadService; + late DownloadService _downloadService; ActionNotifier() : super(); @@ -37,6 +41,29 @@ class ActionNotifier extends Notifier { void build() { _uploadService = ref.watch(uploadServiceProvider); _service = ref.watch(actionServiceProvider); + _downloadService = ref.watch(downloadServiceProvider); + _downloadService.onImageDownloadStatus = _downloadImageCallback; + _downloadService.onVideoDownloadStatus = _downloadVideoCallback; + _downloadService.onLivePhotoDownloadStatus = _downloadLivePhotoCallback; + } + + void _downloadImageCallback(TaskStatusUpdate update) { + if (update.status == TaskStatus.complete) { + _downloadService.saveImageWithPath(update.task); + } + } + + void _downloadVideoCallback(TaskStatusUpdate update) { + if (update.status == TaskStatus.complete) { + _downloadService.saveVideo(update.task); + } + } + + void _downloadLivePhotoCallback(TaskStatusUpdate update) async { + if (update.status == TaskStatus.complete) { + final livePhotosId = LivePhotosMetadata.fromJson(update.task.metaData).id; + _downloadService.saveLivePhotos(update.task, livePhotosId); + } } List _getRemoteIdsForSource(ActionSource source) { From e7d051db3c1a427ce675e304e69f87bbf8bb1a87 Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Wed, 30 Jul 2025 14:40:13 -0500 Subject: [PATCH 129/748] feat: drift edit time and date action (#20377) * feat: drift edit time and date action * feat: add edit button on asset viewer bottom sheet * update localDateTime column in addition to createdAt to keep consistency * fix: dont update local dateTime Server calcs this anyway and it will be synced when the change is applied. We don't use localDateTime on mobile so there is no reason to update this value * fix: padding around edit icon in ListTile Co-authored-by: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> * chore: format * fix: hide date edit control when asset does not have a remote * fix: pull timezones correctly from image --------- Co-authored-by: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> --- i18n/en.json | 1 + .../repositories/remote_asset.repository.dart | 17 ++++++++ .../edit_date_time_action_button.widget.dart | 37 +++++++++++++++++- .../asset_viewer/bottom_sheet.widget.dart | 20 +++++++++- .../archive_bottom_sheet.widget.dart | 2 +- .../favorite_bottom_sheet.widget.dart | 2 +- .../general_bottom_sheet.widget.dart | 2 +- .../remote_album_bottom_sheet.widget.dart | 2 +- .../infrastructure/action.provider.dart | 15 +++++++ .../repositories/asset_api.repository.dart | 4 ++ mobile/lib/services/action.service.dart | 39 +++++++++++++++++++ .../lib/widgets/common/date_time_picker.dart | 2 +- 12 files changed, 136 insertions(+), 7 deletions(-) diff --git a/i18n/en.json b/i18n/en.json index c4cf101b73..700ff60c53 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -835,6 +835,7 @@ "edit_birthday": "Edit Birthday", "edit_date": "Edit date", "edit_date_and_time": "Edit date and time", + "edit_date_and_time_action_prompt": "{count} date and time edited", "edit_description": "Edit description", "edit_description_prompt": "Please select a new description:", "edit_exclusion_pattern": "Edit exclusion pattern", diff --git a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart index 33735f1709..44d7cfb6bb 100644 --- a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart @@ -186,6 +186,23 @@ class RemoteAssetRepository extends DriftDatabaseRepository { }); } + Future updateDateTime(List ids, DateTime dateTime) { + return _db.batch((batch) async { + for (final id in ids) { + batch.update( + _db.remoteExifEntity, + RemoteExifEntityCompanion(dateTimeOriginal: Value(dateTime)), + where: (e) => e.assetId.equals(id), + ); + batch.update( + _db.remoteAssetEntity, + RemoteAssetEntityCompanion(createdAt: Value(dateTime)), + where: (e) => e.id.equals(id), + ); + } + }); + } + Future stack(String userId, StackResponse stack) { return _db.transaction(() async { final stackIds = await _db.managers.stackEntity diff --git a/mobile/lib/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart index 3db3dde44d..6eeec0658b 100644 --- a/mobile/lib/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart @@ -1,10 +1,44 @@ import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; class EditDateTimeActionButton extends ConsumerWidget { - const EditDateTimeActionButton({super.key}); + final ActionSource source; + + const EditDateTimeActionButton({super.key, required this.source}); + + _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = await ref.read(actionProvider.notifier).editDateTime(source, context); + if (result == null) { + return; + } + + ref.read(multiSelectProvider.notifier).reset(); + + final successMessage = 'edit_date_and_time_action_prompt'.t( + context: context, + args: {'count': result.count.toString()}, + ); + + if (context.mounted) { + ImmichToast.show( + context: context, + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: result.success ? ToastType.success : ToastType.error, + ); + } + } @override Widget build(BuildContext context, WidgetRef ref) { @@ -12,6 +46,7 @@ class EditDateTimeActionButton extends ConsumerWidget { maxWidth: 95.0, iconData: Icons.edit_calendar_outlined, label: "control_bottom_app_bar_edit_time".t(context: context), + onPressed: () => _onTap(context, ref), ); } } diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart index 17b4cdb214..1d76d3c39d 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart @@ -143,12 +143,18 @@ class _AssetDetailBottomSheet extends ConsumerWidget { final exifInfo = ref.watch(currentAssetExifProvider).valueOrNull; final cameraTitle = _getCameraInfoTitle(exifInfo); + Future editDateTime() async { + await ref.read(actionProvider.notifier).editDateTime(ActionSource.viewer, context); + } + return SliverList.list( children: [ // Asset Date and Time _SheetTile( title: _getDateTime(context, asset), titleStyle: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600), + trailing: asset.hasRemote ? const Icon(Icons.edit, size: 18) : null, + onTap: asset.hasRemote ? () async => await editDateTime() : null, ), if (exifInfo != null) _SheetAssetDescription(exif: exifInfo), const SheetPeopleDetails(), @@ -194,11 +200,21 @@ class _AssetDetailBottomSheet extends ConsumerWidget { class _SheetTile extends StatelessWidget { final String title; final Widget? leading; + final Widget? trailing; final String? subtitle; final TextStyle? titleStyle; final TextStyle? subtitleStyle; + final VoidCallback? onTap; - const _SheetTile({required this.title, this.titleStyle, this.leading, this.subtitle, this.subtitleStyle}); + const _SheetTile({ + required this.title, + this.titleStyle, + this.leading, + this.subtitle, + this.subtitleStyle, + this.trailing, + this.onTap, + }); @override Widget build(BuildContext context) { @@ -234,8 +250,10 @@ class _SheetTile extends StatelessWidget { title: titleWidget, titleAlignment: ListTileTitleAlignment.center, leading: leading, + trailing: trailing, contentPadding: leading == null ? null : const EdgeInsets.only(left: 25), subtitle: subtitleWidget, + onTap: onTap, ); } } diff --git a/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart index 6485926996..45c602935d 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart @@ -40,7 +40,7 @@ class ArchiveBottomSheet extends ConsumerWidget { isTrashEnable ? const TrashActionButton(source: ActionSource.timeline) : const DeletePermanentActionButton(source: ActionSource.timeline), - const EditDateTimeActionButton(), + const EditDateTimeActionButton(source: ActionSource.timeline), const EditLocationActionButton(source: ActionSource.timeline), const MoveToLockFolderActionButton(source: ActionSource.timeline), const StackActionButton(source: ActionSource.timeline), diff --git a/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart index ec0fded6c3..3fb499f2a1 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart @@ -40,7 +40,7 @@ class FavoriteBottomSheet extends ConsumerWidget { isTrashEnable ? const TrashActionButton(source: ActionSource.timeline) : const DeletePermanentActionButton(source: ActionSource.timeline), - const EditDateTimeActionButton(), + const EditDateTimeActionButton(source: ActionSource.timeline), const EditLocationActionButton(source: ActionSource.timeline), const MoveToLockFolderActionButton(source: ActionSource.timeline), const StackActionButton(source: ActionSource.timeline), diff --git a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart index 3912aef15c..70b2fb00b0 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart @@ -76,7 +76,7 @@ class GeneralBottomSheet extends ConsumerWidget { if (multiselect.hasLocal || multiselect.hasMerged) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), ], - const EditDateTimeActionButton(), + const EditDateTimeActionButton(source: ActionSource.timeline), const EditLocationActionButton(source: ActionSource.timeline), const MoveToLockFolderActionButton(source: ActionSource.timeline), const StackActionButton(source: ActionSource.timeline), diff --git a/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart index 9765b61684..9f41a0c681 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart @@ -43,7 +43,7 @@ class RemoteAlbumBottomSheet extends ConsumerWidget { isTrashEnable ? const TrashActionButton(source: ActionSource.timeline) : const DeletePermanentActionButton(source: ActionSource.timeline), - const EditDateTimeActionButton(), + const EditDateTimeActionButton(source: ActionSource.timeline), const EditLocationActionButton(source: ActionSource.timeline), const MoveToLockFolderActionButton(source: ActionSource.timeline), const StackActionButton(source: ActionSource.timeline), diff --git a/mobile/lib/providers/infrastructure/action.provider.dart b/mobile/lib/providers/infrastructure/action.provider.dart index 80e27b5970..21a22e7e5f 100644 --- a/mobile/lib/providers/infrastructure/action.provider.dart +++ b/mobile/lib/providers/infrastructure/action.provider.dart @@ -266,6 +266,21 @@ class ActionNotifier extends Notifier { } } + Future editDateTime(ActionSource source, BuildContext context) async { + final ids = _getOwnedRemoteIdsForSource(source); + try { + final isEdited = await _service.editDateTime(ids, context); + if (!isEdited) { + return null; + } + + return ActionResult(count: ids.length, success: true); + } catch (error, stack) { + _logger.severe('Failed to edit date and time for assets', error, stack); + return ActionResult(count: ids.length, success: false, error: error.toString()); + } + } + Future removeFromAlbum(ActionSource source, String albumId) async { final ids = _getRemoteIdsForSource(source); try { diff --git a/mobile/lib/repositories/asset_api.repository.dart b/mobile/lib/repositories/asset_api.repository.dart index bbb176ffa7..07639fbb3a 100644 --- a/mobile/lib/repositories/asset_api.repository.dart +++ b/mobile/lib/repositories/asset_api.repository.dart @@ -66,6 +66,10 @@ class AssetApiRepository extends ApiRepository { return _api.updateAssets(AssetBulkUpdateDto(ids: ids, latitude: location.latitude, longitude: location.longitude)); } + Future updateDateTime(List ids, DateTime dateTime) async { + return _api.updateAssets(AssetBulkUpdateDto(ids: ids, dateTimeOriginal: dateTime.toIso8601String())); + } + Future stack(List ids) async { final responseDto = await checkNull(_stacksApi.createStack(StackCreateDto(assetIds: ids))); diff --git a/mobile/lib/services/action.service.dart b/mobile/lib/services/action.service.dart index f45071e7f7..9a12745acd 100644 --- a/mobile/lib/services/action.service.dart +++ b/mobile/lib/services/action.service.dart @@ -13,6 +13,7 @@ import 'package:immich_mobile/repositories/asset_api.repository.dart'; import 'package:immich_mobile/repositories/asset_media.repository.dart'; import 'package:immich_mobile/repositories/drift_album_api_repository.dart'; import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/widgets/common/date_time_picker.dart'; import 'package:immich_mobile/widgets/common/location_picker.dart'; import 'package:maplibre_gl/maplibre_gl.dart' as maplibre; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -159,6 +160,44 @@ class ActionService { return true; } + Future editDateTime(List remoteIds, BuildContext context) async { + DateTime? initialDate; + String? timeZone; + Duration? offset; + + if (remoteIds.length == 1) { + final assetId = remoteIds.first; + final asset = await _remoteAssetRepository.get(assetId); + if (asset == null) { + return false; + } + + final exifData = await _remoteAssetRepository.getExif(assetId); + initialDate = asset.createdAt.toLocal(); + offset = initialDate.timeZoneOffset; + timeZone = exifData?.timeZone; + } + + final dateTime = await showDateTimePicker( + context: context, + initialDateTime: initialDate, + initialTZ: timeZone, + initialTZOffset: offset, + ); + + if (dateTime == null) { + return false; + } + + // convert dateTime to DateTime object + final parsedDateTime = DateTime.parse(dateTime); + + await _assetApiRepository.updateDateTime(remoteIds, parsedDateTime); + await _remoteAssetRepository.updateDateTime(remoteIds, parsedDateTime); + + return true; + } + Future removeFromAlbum(List remoteIds, String albumId) async { int removedCount = 0; final result = await _albumApiRepository.removeAssets(albumId, remoteIds); diff --git a/mobile/lib/widgets/common/date_time_picker.dart b/mobile/lib/widgets/common/date_time_picker.dart index 113462c6c8..9cc8de29ee 100644 --- a/mobile/lib/widgets/common/date_time_picker.dart +++ b/mobile/lib/widgets/common/date_time_picker.dart @@ -145,7 +145,7 @@ class _DateTimePicker extends HookWidget { 1, ), trailing: Icon(Icons.edit_outlined, size: 18, color: context.primaryColor), - title: Text(DateFormat("dd-MM-yyyy hh:mm a").format(date.value), style: context.textTheme.bodyMedium).tr(), + title: Text(DateFormat("dd-MM-yyyy hh:mm a").format(date.value), style: context.textTheme.bodyMedium), onTap: pickDate, ), const SizedBox(height: 24), From 27c456eb75333566881da659c22e8085920ceb25 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 30 Jul 2025 14:47:47 -0500 Subject: [PATCH 130/748] fix: people navigation (#20450) --- .../asset_viewer/bottom_sheet/sheet_people_details.widget.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart index 4cfd95b25c..5adaa7cc72 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart @@ -79,7 +79,7 @@ class _SheetPeopleDetailsState extends ConsumerState { context.back(); return; } - context.back(); + context.pop(); context.pushRoute(DriftPersonRoute(person: person)); }, onNameTap: () => showNameEditModal(person), From 6b50d958f4cde887562a8abba4b8aa296557e3d4 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 30 Jul 2025 16:50:52 -0300 Subject: [PATCH 131/748] fix: incorrect next/previous action after folder view refresh (#20447) --- .../gallery-viewer/gallery-viewer.svelte | 31 ++++++++++++------- .../[[assetId=id]]/+page.svelte | 4 +-- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte b/web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte index 6e0c547b90..30e14abc59 100644 --- a/web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte +++ b/web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte @@ -27,6 +27,7 @@ import Portal from '../portal/portal.svelte'; interface Props { + initialAssetId?: string; assets: (TimelineAsset | AssetResponseDto)[]; assetInteraction: AssetInteraction; disableAssetSelect?: boolean; @@ -44,6 +45,7 @@ } let { + initialAssetId = undefined, assets = $bindable(), assetInteraction, disableAssetSelect = false, @@ -117,7 +119,14 @@ }; }); - let currentViewAssetIndex = 0; + let currentIndex = 0; + if (initialAssetId && assets.length > 0) { + const index = assets.findIndex(({ id }) => id === initialAssetId); + if (index !== -1) { + currentIndex = index; + } + } + let shiftKeyIsDown = $state(false); let lastAssetMouseEvent: TimelineAsset | null = $state(null); let slidingWindow = $state({ top: 0, bottom: 0 }); @@ -150,8 +159,8 @@ } }); const viewAssetHandler = async (asset: TimelineAsset) => { - currentViewAssetIndex = assets.findIndex((a) => a.id == asset.id); - await setAssetId(assets[currentViewAssetIndex].id); + currentIndex = assets.findIndex((a) => a.id == asset.id); + await setAssetId(assets[currentIndex].id); await navigate({ targetRoute: 'current', assetId: $viewingAsset.id }); }; @@ -324,12 +333,12 @@ if (onNext) { asset = await onNext(); } else { - if (currentViewAssetIndex >= assets.length - 1) { + if (currentIndex >= assets.length - 1) { return false; } - currentViewAssetIndex = currentViewAssetIndex + 1; - asset = currentViewAssetIndex < assets.length ? assets[currentViewAssetIndex] : undefined; + currentIndex = currentIndex + 1; + asset = currentIndex < assets.length ? assets[currentIndex] : undefined; } if (!asset) { @@ -374,12 +383,12 @@ if (onPrevious) { asset = await onPrevious(); } else { - if (currentViewAssetIndex <= 0) { + if (currentIndex <= 0) { return false; } - currentViewAssetIndex = currentViewAssetIndex - 1; - asset = currentViewAssetIndex >= 0 ? assets[currentViewAssetIndex] : undefined; + currentIndex = currentIndex - 1; + asset = currentIndex >= 0 ? assets[currentIndex] : undefined; } if (!asset) { @@ -412,10 +421,10 @@ ); if (assets.length === 0) { await goto(AppRoute.PHOTOS); - } else if (currentViewAssetIndex === assets.length) { + } else if (currentIndex === assets.length) { await handlePrevious(); } else { - await setAssetId(assets[currentViewAssetIndex].id); + await setAssetId(assets[currentIndex].id); } break; } diff --git a/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.svelte index 84ae328ccc..51de0ac1ee 100644 --- a/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -21,8 +21,8 @@ import TreeItems from '$lib/components/shared-components/tree/tree-items.svelte'; import Sidebar from '$lib/components/sidebar/sidebar.svelte'; import { AppRoute, QueryParameter } from '$lib/constants'; - import { AssetInteraction } from '$lib/stores/asset-interaction.svelte'; import type { Viewport } from '$lib/managers/timeline-manager/types'; + import { AssetInteraction } from '$lib/stores/asset-interaction.svelte'; import { foldersStore } from '$lib/stores/folders.svelte'; import { preferences } from '$lib/stores/user.store'; import { cancelMultiselect } from '$lib/utils/asset-utils'; @@ -40,7 +40,6 @@ let { data }: Props = $props(); const viewport: Viewport = $state({ width: 0, height: 0 }); - const assetInteraction = new AssetInteraction(); const handleNavigateToFolder = (folderName: string) => navigateToView(joinPaths(data.tree.path, folderName)); @@ -104,6 +103,7 @@ {#if data.pathAssets && data.pathAssets.length > 0}
Date: Thu, 31 Jul 2025 14:28:45 +0200 Subject: [PATCH 132/748] fix: modal race conditions (#20460) --- web/package-lock.json | 8 ++++---- web/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index eb4aae708b..14c58a47ee 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", "@immich/sdk": "file:../open-api/typescript-sdk", - "@immich/ui": "^0.23.5", + "@immich/ui": "^0.23.6", "@mapbox/mapbox-gl-rtl-text": "0.2.3", "@mdi/js": "^7.4.47", "@photo-sphere-viewer/core": "^5.11.5", @@ -1357,9 +1357,9 @@ "link": true }, "node_modules/@immich/ui": { - "version": "0.23.5", - "resolved": "https://registry.npmjs.org/@immich/ui/-/ui-0.23.5.tgz", - "integrity": "sha512-1wlFMmfDmtGC+Kcc8cYTT00mQaSumR41KEOOOmVn5Rw/8z9pUhpNY8mGl1AxY4qhtnaz+G3dH6vowYzL23D+YQ==", + "version": "0.23.6", + "resolved": "https://registry.npmjs.org/@immich/ui/-/ui-0.23.6.tgz", + "integrity": "sha512-HYIguDx/nCXcvqLKhY1R/+Aks6mn8B9jIiNVQH6WODDPbvGFrvQT5uINhXHrjsdyuzKBVS6dps+lx9+9Z6z4rA==", "license": "GNU Affero General Public License version 3", "dependencies": { "@mdi/js": "^7.4.47", diff --git a/web/package.json b/web/package.json index a8d338d547..687263f1a9 100644 --- a/web/package.json +++ b/web/package.json @@ -28,7 +28,7 @@ "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", "@immich/sdk": "file:../open-api/typescript-sdk", - "@immich/ui": "^0.23.5", + "@immich/ui": "^0.23.6", "@mapbox/mapbox-gl-rtl-text": "0.2.3", "@mdi/js": "^7.4.47", "@photo-sphere-viewer/core": "^5.11.5", From 7391ea6ff9347ca008028e7e6c88d0e06493157c Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 31 Jul 2025 11:52:19 -0500 Subject: [PATCH 133/748] chore: large file size grid view styling (#20472) * chore: large file grid styles * chore: large file grid styles --- .../large-assets/large-asset-data.svelte | 55 ++++++------------- .../[[assetId=id]]/+page.svelte | 2 +- 2 files changed, 18 insertions(+), 39 deletions(-) diff --git a/web/src/lib/components/utilities-page/large-assets/large-asset-data.svelte b/web/src/lib/components/utilities-page/large-assets/large-asset-data.svelte index 71f3dbb5c4..db1fe4615b 100644 --- a/web/src/lib/components/utilities-page/large-assets/large-asset-data.svelte +++ b/web/src/lib/components/utilities-page/large-assets/large-asset-data.svelte @@ -1,12 +1,8 @@
-
- + {#if !!asset.libraryId} +
External
+ {/if}
- -
-
-
{asset.originalFileName}
- {getAssetResolution(asset)} -
-
- {getFileSize(asset, 1)} -
+
+ {asset.originalFileName} +
+
+

{getFileSize(asset, 1)}

diff --git a/web/src/routes/(user)/utilities/large-files/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/utilities/large-files/[[photos=photos]]/[[assetId=id]]/+page.svelte index 75ac4fab93..35c101492c 100644 --- a/web/src/routes/(user)/utilities/large-files/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/utilities/large-files/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -56,7 +56,7 @@ -
+
{#if assets && data.assets.length > 0} {#each assets as asset (asset.id)} setAsset(asset)} /> From c3263e50fce9233f843307d3b0963849ad810f13 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 31 Jul 2025 20:19:26 +0000 Subject: [PATCH 134/748] chore: version v1.137.0 --- cli/package-lock.json | 6 +++--- cli/package.json | 2 +- docs/static/archived-versions.json | 4 ++++ e2e/package-lock.json | 8 ++++---- e2e/package.json | 2 +- mobile/android/fastlane/Fastfile | 2 +- mobile/ios/fastlane/Fastfile | 2 +- mobile/openapi/README.md | 2 +- mobile/pubspec.yaml | 2 +- open-api/immich-openapi-specs.json | 2 +- open-api/typescript-sdk/package-lock.json | 4 ++-- open-api/typescript-sdk/package.json | 2 +- open-api/typescript-sdk/src/fetch-client.ts | 2 +- server/package-lock.json | 4 ++-- server/package.json | 2 +- web/package-lock.json | 6 +++--- web/package.json | 2 +- 17 files changed, 29 insertions(+), 25 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index 2b20af801b..90304a8b12 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@immich/cli", - "version": "2.2.73", + "version": "2.2.74", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/cli", - "version": "2.2.73", + "version": "2.2.74", "license": "GNU Affero General Public License version 3", "dependencies": { "chokidar": "^4.0.3", @@ -54,7 +54,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.136.0", + "version": "1.137.0", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { diff --git a/cli/package.json b/cli/package.json index a20ae145b4..5ff2a9a522 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@immich/cli", - "version": "2.2.73", + "version": "2.2.74", "description": "Command Line Interface (CLI) for Immich", "type": "module", "exports": "./dist/index.js", diff --git a/docs/static/archived-versions.json b/docs/static/archived-versions.json index 6018e26011..2fb214b824 100644 --- a/docs/static/archived-versions.json +++ b/docs/static/archived-versions.json @@ -1,4 +1,8 @@ [ + { + "label": "v1.137.0", + "url": "https://v1.137.0.archive.immich.app" + }, { "label": "v1.136.0", "url": "https://v1.136.0.archive.immich.app" diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 8c1e6e2ba1..ae5d9164b2 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich-e2e", - "version": "1.136.0", + "version": "1.137.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-e2e", - "version": "1.136.0", + "version": "1.137.0", "license": "GNU Affero General Public License version 3", "devDependencies": { "@eslint/eslintrc": "^3.1.0", @@ -46,7 +46,7 @@ }, "../cli": { "name": "@immich/cli", - "version": "2.2.73", + "version": "2.2.74", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { @@ -95,7 +95,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.136.0", + "version": "1.137.0", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { diff --git a/e2e/package.json b/e2e/package.json index 988f1dfd96..a607e30d96 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -1,6 +1,6 @@ { "name": "immich-e2e", - "version": "1.136.0", + "version": "1.137.0", "description": "", "main": "index.js", "type": "module", diff --git a/mobile/android/fastlane/Fastfile b/mobile/android/fastlane/Fastfile index 2287ab0821..06d0d2cc02 100644 --- a/mobile/android/fastlane/Fastfile +++ b/mobile/android/fastlane/Fastfile @@ -36,7 +36,7 @@ platform :android do build_type: 'Release', properties: { "android.injected.version.code" => 205, - "android.injected.version.name" => "1.136.0", + "android.injected.version.name" => "1.137.0", } ) upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab') diff --git a/mobile/ios/fastlane/Fastfile b/mobile/ios/fastlane/Fastfile index 70fade71ba..e037eb08c7 100644 --- a/mobile/ios/fastlane/Fastfile +++ b/mobile/ios/fastlane/Fastfile @@ -22,7 +22,7 @@ platform :ios do path: "./Runner.xcodeproj", ) increment_version_number( - version_number: "1.136.0" + version_number: "1.137.0" ) increment_build_number( build_number: latest_testflight_build_number + 1, diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 058524479c..140d1fa44c 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -3,7 +3,7 @@ Immich API This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: -- API version: 1.136.0 +- API version: 1.137.0 - Generator version: 7.8.0 - Build package: org.openapitools.codegen.languages.DartClientCodegen diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 8510537a1f..5fc1736f54 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -2,7 +2,7 @@ name: immich_mobile description: Immich - selfhosted backup media file on mobile phone publish_to: 'none' -version: 1.136.0+3000 +version: 1.137.0+3001 environment: sdk: '>=3.8.0 <4.0.0' diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 9dc7540dd8..b031f65498 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -9469,7 +9469,7 @@ "info": { "title": "Immich", "description": "Immich API", - "version": "1.136.0", + "version": "1.137.0", "contact": {} }, "tags": [], diff --git a/open-api/typescript-sdk/package-lock.json b/open-api/typescript-sdk/package-lock.json index e906674878..6aaea1d8f5 100644 --- a/open-api/typescript-sdk/package-lock.json +++ b/open-api/typescript-sdk/package-lock.json @@ -1,12 +1,12 @@ { "name": "@immich/sdk", - "version": "1.136.0", + "version": "1.137.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/sdk", - "version": "1.136.0", + "version": "1.137.0", "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index a5582ee60a..9495235d23 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@immich/sdk", - "version": "1.136.0", + "version": "1.137.0", "description": "Auto-generated TypeScript SDK for the Immich API", "type": "module", "main": "./build/index.js", diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 53a2f4b144..1151cbfc08 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -1,6 +1,6 @@ /** * Immich - * 1.136.0 + * 1.137.0 * DO NOT MODIFY - This file has been generated using oazapfts. * See https://www.npmjs.com/package/oazapfts */ diff --git a/server/package-lock.json b/server/package-lock.json index bac0540e38..a314b20872 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich", - "version": "1.136.0", + "version": "1.137.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich", - "version": "1.136.0", + "version": "1.137.0", "license": "GNU Affero General Public License version 3", "dependencies": { "@nestjs/bullmq": "^11.0.1", diff --git a/server/package.json b/server/package.json index 2a5fa5ea8f..82e96b6b51 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "immich", - "version": "1.136.0", + "version": "1.137.0", "description": "", "author": "", "private": true, diff --git a/web/package-lock.json b/web/package-lock.json index 14c58a47ee..57d264c4d2 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich-web", - "version": "1.136.0", + "version": "1.137.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-web", - "version": "1.136.0", + "version": "1.137.0", "license": "GNU Affero General Public License version 3", "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", @@ -94,7 +94,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.136.0", + "version": "1.137.0", "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" diff --git a/web/package.json b/web/package.json index 687263f1a9..52e55d4619 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "immich-web", - "version": "1.136.0", + "version": "1.137.0", "license": "GNU Affero General Public License version 3", "type": "module", "scripts": { From 3cdc6844a13d11f00bfc21684196e1c9f5ceb221 Mon Sep 17 00:00:00 2001 From: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Fri, 1 Aug 2025 00:58:35 +0200 Subject: [PATCH 135/748] fix: automatic media location migration without internal assets (#20489) --- server/src/queries/asset.repository.sql | 23 +++------------------ server/src/repositories/asset.repository.ts | 16 +------------- server/src/services/cli.service.ts | 7 +------ server/src/services/storage.service.ts | 6 +++--- 4 files changed, 8 insertions(+), 44 deletions(-) diff --git a/server/src/queries/asset.repository.sql b/server/src/queries/asset.repository.sql index 185afa562b..f7a4d1402d 100644 --- a/server/src/queries/asset.repository.sql +++ b/server/src/queries/asset.repository.sql @@ -170,27 +170,10 @@ where -- AssetRepository.getFileSamples select - "asset"."id", - "asset"."originalPath", - "asset"."sidecarPath", - "asset"."encodedVideoPath", - ( - select - coalesce(json_agg(agg), '[]') - from - ( - select - "path" - from - "asset_file" - where - "asset"."id" = "asset_file"."assetId" - ) as agg - ) as "files" + "assetId", + "path" from - "asset" -where - "asset"."libraryId" is null + "asset_file" limit 3 diff --git a/server/src/repositories/asset.repository.ts b/server/src/repositories/asset.repository.ts index 286a66e9af..8aa25c4a6a 100644 --- a/server/src/repositories/asset.repository.ts +++ b/server/src/repositories/asset.repository.ts @@ -1,6 +1,5 @@ import { Injectable } from '@nestjs/common'; import { Insertable, Kysely, NotNull, Selectable, UpdateResult, Updateable, sql } from 'kysely'; -import { jsonArrayFrom } from 'kysely/helpers/postgres'; import { isEmpty, isUndefined, omitBy } from 'lodash'; import { InjectKysely } from 'nestjs-kysely'; import { Stack } from 'src/database'; @@ -338,20 +337,7 @@ export class AssetRepository { @GenerateSql() getFileSamples() { - return this.db - .selectFrom('asset') - .select((eb) => [ - 'asset.id', - 'asset.originalPath', - 'asset.sidecarPath', - 'asset.encodedVideoPath', - jsonArrayFrom(eb.selectFrom('asset_file').select('path').whereRef('asset.id', '=', 'asset_file.assetId')).as( - 'files', - ), - ]) - .where('asset.libraryId', 'is', null) - .limit(sql.lit(3)) - .execute(); + return this.db.selectFrom('asset_file').select(['assetId', 'path']).limit(sql.lit(3)).execute(); } @GenerateSql({ params: [DummyValue.UUID] }) diff --git a/server/src/services/cli.service.ts b/server/src/services/cli.service.ts index 674b885dc4..38144e95b4 100644 --- a/server/src/services/cli.service.ts +++ b/server/src/services/cli.service.ts @@ -86,12 +86,7 @@ export class CliService extends BaseService { } for (const asset of assets) { - paths.push( - asset.originalPath, - asset.sidecarPath, - asset.encodedVideoPath, - ...asset.files.map((file) => file.path), - ); + paths.push(asset.path); } return paths.filter(Boolean) as string[]; diff --git a/server/src/services/storage.service.ts b/server/src/services/storage.service.ts index cd8910305a..bcfb34535c 100644 --- a/server/src/services/storage.service.ts +++ b/server/src/services/storage.service.ts @@ -97,18 +97,18 @@ export class StorageService extends BaseService { const current = StorageCore.getMediaLocation(); const samples = await this.assetRepository.getFileSamples(); if (samples.length > 0) { - const originalPath = samples[0].originalPath; + const path = samples[0].path; const savedValue = await this.systemMetadataRepository.get(SystemMetadataKey.MediaLocation); let previous = savedValue?.location || ''; if (!previous) { - previous = originalPath.startsWith('upload/') ? 'upload' : '/usr/src/app/upload'; + previous = path.startsWith('upload/') ? 'upload' : '/usr/src/app/upload'; } if (previous !== current) { this.logger.log(`Media location changed (from=${previous}, to=${current})`); - if (!originalPath.startsWith(previous)) { + if (!path.startsWith(previous)) { throw new Error( 'Detected an inconsistent media location. For more information, see https://immich.app/errors#inconsistent-media-location', ); From a07531be3b27416aeec9ee0630672fd8d1949556 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 31 Jul 2025 23:05:34 +0000 Subject: [PATCH 136/748] chore: version v1.137.1 --- cli/package-lock.json | 6 +++--- cli/package.json | 2 +- docs/static/archived-versions.json | 4 ++++ e2e/package-lock.json | 8 ++++---- e2e/package.json | 2 +- mobile/android/fastlane/Fastfile | 2 +- mobile/ios/fastlane/Fastfile | 2 +- mobile/openapi/README.md | 2 +- mobile/pubspec.yaml | 2 +- open-api/immich-openapi-specs.json | 2 +- open-api/typescript-sdk/package-lock.json | 4 ++-- open-api/typescript-sdk/package.json | 2 +- open-api/typescript-sdk/src/fetch-client.ts | 2 +- server/package-lock.json | 4 ++-- server/package.json | 2 +- web/package-lock.json | 6 +++--- web/package.json | 2 +- 17 files changed, 29 insertions(+), 25 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index 90304a8b12..d27a7371a4 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@immich/cli", - "version": "2.2.74", + "version": "2.2.75", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/cli", - "version": "2.2.74", + "version": "2.2.75", "license": "GNU Affero General Public License version 3", "dependencies": { "chokidar": "^4.0.3", @@ -54,7 +54,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.137.0", + "version": "1.137.1", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { diff --git a/cli/package.json b/cli/package.json index 5ff2a9a522..b8db8cbd06 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@immich/cli", - "version": "2.2.74", + "version": "2.2.75", "description": "Command Line Interface (CLI) for Immich", "type": "module", "exports": "./dist/index.js", diff --git a/docs/static/archived-versions.json b/docs/static/archived-versions.json index 2fb214b824..d41f1a7bc4 100644 --- a/docs/static/archived-versions.json +++ b/docs/static/archived-versions.json @@ -1,4 +1,8 @@ [ + { + "label": "v1.137.1", + "url": "https://v1.137.1.archive.immich.app" + }, { "label": "v1.137.0", "url": "https://v1.137.0.archive.immich.app" diff --git a/e2e/package-lock.json b/e2e/package-lock.json index ae5d9164b2..473748d065 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich-e2e", - "version": "1.137.0", + "version": "1.137.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-e2e", - "version": "1.137.0", + "version": "1.137.1", "license": "GNU Affero General Public License version 3", "devDependencies": { "@eslint/eslintrc": "^3.1.0", @@ -46,7 +46,7 @@ }, "../cli": { "name": "@immich/cli", - "version": "2.2.74", + "version": "2.2.75", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { @@ -95,7 +95,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.137.0", + "version": "1.137.1", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { diff --git a/e2e/package.json b/e2e/package.json index a607e30d96..d736a9201f 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -1,6 +1,6 @@ { "name": "immich-e2e", - "version": "1.137.0", + "version": "1.137.1", "description": "", "main": "index.js", "type": "module", diff --git a/mobile/android/fastlane/Fastfile b/mobile/android/fastlane/Fastfile index 06d0d2cc02..35af2a1e1f 100644 --- a/mobile/android/fastlane/Fastfile +++ b/mobile/android/fastlane/Fastfile @@ -36,7 +36,7 @@ platform :android do build_type: 'Release', properties: { "android.injected.version.code" => 205, - "android.injected.version.name" => "1.137.0", + "android.injected.version.name" => "1.137.1", } ) upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab') diff --git a/mobile/ios/fastlane/Fastfile b/mobile/ios/fastlane/Fastfile index e037eb08c7..c3eaf44cfc 100644 --- a/mobile/ios/fastlane/Fastfile +++ b/mobile/ios/fastlane/Fastfile @@ -22,7 +22,7 @@ platform :ios do path: "./Runner.xcodeproj", ) increment_version_number( - version_number: "1.137.0" + version_number: "1.137.1" ) increment_build_number( build_number: latest_testflight_build_number + 1, diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 140d1fa44c..8bd08e4d13 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -3,7 +3,7 @@ Immich API This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: -- API version: 1.137.0 +- API version: 1.137.1 - Generator version: 7.8.0 - Build package: org.openapitools.codegen.languages.DartClientCodegen diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 5fc1736f54..d3448d7f1e 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -2,7 +2,7 @@ name: immich_mobile description: Immich - selfhosted backup media file on mobile phone publish_to: 'none' -version: 1.137.0+3001 +version: 1.137.1+3001 environment: sdk: '>=3.8.0 <4.0.0' diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index b031f65498..1288c95df8 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -9469,7 +9469,7 @@ "info": { "title": "Immich", "description": "Immich API", - "version": "1.137.0", + "version": "1.137.1", "contact": {} }, "tags": [], diff --git a/open-api/typescript-sdk/package-lock.json b/open-api/typescript-sdk/package-lock.json index 6aaea1d8f5..bb49b1060f 100644 --- a/open-api/typescript-sdk/package-lock.json +++ b/open-api/typescript-sdk/package-lock.json @@ -1,12 +1,12 @@ { "name": "@immich/sdk", - "version": "1.137.0", + "version": "1.137.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/sdk", - "version": "1.137.0", + "version": "1.137.1", "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index 9495235d23..415695de62 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@immich/sdk", - "version": "1.137.0", + "version": "1.137.1", "description": "Auto-generated TypeScript SDK for the Immich API", "type": "module", "main": "./build/index.js", diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 1151cbfc08..6a8ee07b4f 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -1,6 +1,6 @@ /** * Immich - * 1.137.0 + * 1.137.1 * DO NOT MODIFY - This file has been generated using oazapfts. * See https://www.npmjs.com/package/oazapfts */ diff --git a/server/package-lock.json b/server/package-lock.json index a314b20872..f59c267496 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich", - "version": "1.137.0", + "version": "1.137.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich", - "version": "1.137.0", + "version": "1.137.1", "license": "GNU Affero General Public License version 3", "dependencies": { "@nestjs/bullmq": "^11.0.1", diff --git a/server/package.json b/server/package.json index 82e96b6b51..1a00b2c243 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "immich", - "version": "1.137.0", + "version": "1.137.1", "description": "", "author": "", "private": true, diff --git a/web/package-lock.json b/web/package-lock.json index 57d264c4d2..1f4769083b 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich-web", - "version": "1.137.0", + "version": "1.137.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-web", - "version": "1.137.0", + "version": "1.137.1", "license": "GNU Affero General Public License version 3", "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", @@ -94,7 +94,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.137.0", + "version": "1.137.1", "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" diff --git a/web/package.json b/web/package.json index 52e55d4619..ad33a36a0f 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "immich-web", - "version": "1.137.0", + "version": "1.137.1", "license": "GNU Affero General Public License version 3", "type": "module", "scripts": { From 4bd465e7525f750cdd98296775cf5f27f2fec67f Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 31 Jul 2025 21:02:28 -0500 Subject: [PATCH 137/748] feat: change grid size with gesture (#20455) --- .../widgets/timeline/timeline.widget.dart | 121 +++++++++++++----- 1 file changed, 86 insertions(+), 35 deletions(-) diff --git a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart index d946872781..dcf2c74ed5 100644 --- a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart @@ -3,6 +3,7 @@ import 'dart:math' as math; import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -88,10 +89,23 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { final _scrollController = ScrollController(); StreamSubscription? _eventSubscription; + int _perRow = 4; + double _scaleFactor = 3.0; + double _baseScaleFactor = 3.0; + @override void initState() { super.initState(); _eventSubscription = EventStream.shared.listen(_onEvent); + + WidgetsBinding.instance.addPostFrameCallback((_) { + final currentTilesPerRow = ref.read(settingsProvider).get(Setting.tilesPerRow); + setState(() { + _perRow = currentTilesPerRow; + _scaleFactor = 7.0 - _perRow; + _baseScaleFactor = _scaleFactor; + }); + }); } void _onEvent(Event event) { @@ -177,43 +191,72 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { return PrimaryScrollController( controller: _scrollController, - child: Stack( - children: [ - Scrubber( - layoutSegments: segments, - timelineHeight: maxHeight, - topPadding: topPadding, - bottomPadding: bottomPadding, - monthSegmentSnappingOffset: widget.topSliverWidgetHeight ?? 0 + appBarExpandedHeight, - child: CustomScrollView( - primary: true, - cacheExtent: maxHeight * 2, - slivers: [ - if (isSelectionMode) const SelectionSliverAppBar() else if (widget.appBar != null) widget.appBar!, - if (widget.topSliverWidget != null) widget.topSliverWidget!, - _SliverSegmentedList( - segments: segments, - delegate: SliverChildBuilderDelegate( - (ctx, index) { - if (index >= childCount) return null; - final segment = segments.findByIndex(index); - return segment?.builder(ctx, index) ?? const SizedBox.shrink(); - }, - childCount: childCount, - addAutomaticKeepAlives: false, - // We add repaint boundary around tiles, so skip the auto boundaries - addRepaintBoundaries: false, - ), - ), - const SliverPadding(padding: EdgeInsets.only(bottom: scrubberBottomPadding)), - ], - ), + child: RawGestureDetector( + gestures: { + CustomScaleGestureRecognizer: GestureRecognizerFactoryWithHandlers( + () => CustomScaleGestureRecognizer(), + (CustomScaleGestureRecognizer scale) { + scale.onStart = (details) { + _baseScaleFactor = _scaleFactor; + }; + + scale.onUpdate = (details) { + final newScaleFactor = math.max(math.min(5.0, _baseScaleFactor * details.scale), 1.0); + final newPerRow = 7 - newScaleFactor.toInt(); + + if (newPerRow != _perRow) { + setState(() { + _scaleFactor = newScaleFactor; + _perRow = newPerRow; + }); + + ref.read(settingsProvider.notifier).set(Setting.tilesPerRow, _perRow); + } + }; + }, ), - if (!isSelectionMode && isMultiSelectEnabled) ...[ - const Positioned(top: 60, left: 25, child: _MultiSelectStatusButton()), - if (widget.bottomSheet != null) widget.bottomSheet!, + }, + child: Stack( + children: [ + Scrubber( + layoutSegments: segments, + timelineHeight: maxHeight, + topPadding: topPadding, + bottomPadding: bottomPadding, + monthSegmentSnappingOffset: widget.topSliverWidgetHeight ?? 0 + appBarExpandedHeight, + child: CustomScrollView( + primary: true, + cacheExtent: maxHeight * 2, + slivers: [ + if (isSelectionMode) + const SelectionSliverAppBar() + else if (widget.appBar != null) + widget.appBar!, + if (widget.topSliverWidget != null) widget.topSliverWidget!, + _SliverSegmentedList( + segments: segments, + delegate: SliverChildBuilderDelegate( + (ctx, index) { + if (index >= childCount) return null; + final segment = segments.findByIndex(index); + return segment?.builder(ctx, index) ?? const SizedBox.shrink(); + }, + childCount: childCount, + addAutomaticKeepAlives: false, + // We add repaint boundary around tiles, so skip the auto boundaries + addRepaintBoundaries: false, + ), + ), + const SliverPadding(padding: EdgeInsets.only(bottom: scrubberBottomPadding)), + ], + ), + ), + if (!isSelectionMode && isMultiSelectEnabled) ...[ + const Positioned(top: 60, left: 25, child: _MultiSelectStatusButton()), + if (widget.bottomSheet != null) widget.bottomSheet!, + ], ], - ], + ), ), ); }, @@ -443,3 +486,11 @@ class _MultiSelectStatusButton extends ConsumerWidget { ); } } + +/// accepts a gesture even though it should reject it (because child won) +class CustomScaleGestureRecognizer extends ScaleGestureRecognizer { + @override + void rejectGesture(int pointer) { + acceptGesture(pointer); + } +} From 1378f223682d6cccc4cd9d94b19e16ab59e088b8 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 31 Jul 2025 21:28:33 -0500 Subject: [PATCH 138/748] fix: add to album render empty app bar (#20480) * fix: add to album render empty app bar * set current album --- .../drift_backup_album_selection.page.dart | 37 +++++++++---------- .../pages/drift_remote_album.page.dart | 29 +++++++++------ .../widgets/album/album_selector.widget.dart | 2 + 3 files changed, 36 insertions(+), 32 deletions(-) diff --git a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart index 396b711d07..865845525a 100644 --- a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart +++ b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart @@ -6,7 +6,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; -import 'package:immich_mobile/providers/album/album.provider.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/backup/backup_album.provider.dart'; import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; @@ -14,7 +13,6 @@ import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/widgets/backup/drift_album_info_list_tile.dart'; import 'package:immich_mobile/widgets/common/search_field.dart'; -import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; @RoutePage() class DriftBackupAlbumSelectionPage extends ConsumerStatefulWidget { @@ -67,14 +65,14 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState album.backupSelection == BackupSelection.selected).toList(); final excludedBackupAlbums = albums.where((album) => album.backupSelection == BackupSelection.excluded).toList(); - handleSyncAlbumToggle(bool isEnable) async { - if (isEnable) { - await ref.read(albumProvider.notifier).refreshRemoteAlbums(); - for (final album in selectedBackupAlbums) { - await ref.read(albumProvider.notifier).createSyncAlbum(album.name); - } - } - } + // handleSyncAlbumToggle(bool isEnable) async { + // if (isEnable) { + // await ref.read(albumProvider.notifier).refreshRemoteAlbums(); + // for (final album in selectedBackupAlbums) { + // await ref.read(albumProvider.notifier).createSyncAlbum(album.name); + // } + // } + // } return PopScope( onPopInvokedWithResult: (didPop, result) async { @@ -167,16 +165,15 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState { + late RemoteAlbum _album; @override void initState() { super.initState(); + _album = widget.album; } Future addAssets(BuildContext context) async { - final albumAssets = await ref.read(remoteAlbumProvider.notifier).getAssets(widget.album.id); + final albumAssets = await ref.read(remoteAlbumProvider.notifier).getAssets(_album.id); final newAssets = await context.pushRoute>( DriftAssetSelectionTimelineRoute(lockedSelectionAssets: albumAssets.toSet()), @@ -47,7 +49,7 @@ class _RemoteAlbumPageState extends ConsumerState { final added = await ref .read(remoteAlbumProvider.notifier) .addAssets( - widget.album.id, + _album.id, newAssets.map((asset) { final remoteAsset = asset as RemoteAsset; return remoteAsset.id; @@ -64,14 +66,14 @@ class _RemoteAlbumPageState extends ConsumerState { } Future addUsers(BuildContext context) async { - final newUsers = await context.pushRoute>(DriftUserSelectionRoute(album: widget.album)); + final newUsers = await context.pushRoute>(DriftUserSelectionRoute(album: _album)); if (newUsers == null || newUsers.isEmpty) { return; } try { - await ref.read(remoteAlbumProvider.notifier).addUsers(widget.album.id, newUsers); + await ref.read(remoteAlbumProvider.notifier).addUsers(_album.id, newUsers); if (newUsers.isNotEmpty) { ImmichToast.show( @@ -81,7 +83,7 @@ class _RemoteAlbumPageState extends ConsumerState { ); } - ref.invalidate(remoteAlbumSharedUsersProvider(widget.album.id)); + ref.invalidate(remoteAlbumSharedUsersProvider(_album.id)); } catch (e) { ImmichToast.show( context: context, @@ -92,7 +94,7 @@ class _RemoteAlbumPageState extends ConsumerState { } Future toggleAlbumOrder() async { - await ref.read(remoteAlbumProvider.notifier).toggleAlbumOrder(widget.album.id); + await ref.read(remoteAlbumProvider.notifier).toggleAlbumOrder(_album.id); ref.invalidate(timelineServiceProvider); } @@ -106,7 +108,7 @@ class _RemoteAlbumPageState extends ConsumerState { content: Column( mainAxisSize: MainAxisSize.min, children: [ - Text('album_delete_confirmation'.t(context: context, args: {'album': widget.album.name})), + Text('album_delete_confirmation'.t(context: context, args: {'album': _album.name})), const SizedBox(height: 8), Text('album_delete_confirmation_description'.t(context: context)), ], @@ -128,7 +130,7 @@ class _RemoteAlbumPageState extends ConsumerState { if (confirmed == true) { try { - await ref.read(remoteAlbumProvider.notifier).deleteAlbum(widget.album.id); + await ref.read(remoteAlbumProvider.notifier).deleteAlbum(_album.id); ImmichToast.show( context: context, @@ -151,17 +153,20 @@ class _RemoteAlbumPageState extends ConsumerState { final result = await showDialog<_EditAlbumData?>( context: context, barrierDismissible: true, - builder: (context) => _EditAlbumDialog(album: widget.album), + builder: (context) => _EditAlbumDialog(album: _album), ); if (result != null && context.mounted) { + setState(() { + _album = _album.copyWith(name: result.name, description: result.description ?? ''); + }); HapticFeedback.mediumImpact(); } } void showOptionSheet(BuildContext context) { final user = ref.watch(currentUserProvider); - final isOwner = user != null ? user.id == widget.album.ownerId : false; + final isOwner = user != null ? user.id == _album.ownerId : false; showModalBottomSheet( context: context, @@ -205,7 +210,7 @@ class _RemoteAlbumPageState extends ConsumerState { return ProviderScope( overrides: [ timelineServiceProvider.overrideWith((ref) { - final timelineService = ref.watch(timelineFactoryProvider).remoteAlbum(albumId: widget.album.id); + final timelineService = ref.watch(timelineFactoryProvider).remoteAlbum(albumId: _album.id); ref.onDispose(timelineService.dispose); return timelineService; }), @@ -217,7 +222,7 @@ class _RemoteAlbumPageState extends ConsumerState { onToggleAlbumOrder: () => toggleAlbumOrder(), onEditTitle: () => showEditTitleAndDescription(context), ), - bottomSheet: RemoteAlbumBottomSheet(album: widget.album), + bottomSheet: RemoteAlbumBottomSheet(album: _album), ), ); } diff --git a/mobile/lib/presentation/widgets/album/album_selector.widget.dart b/mobile/lib/presentation/widgets/album/album_selector.widget.dart index cb6a38041d..2eec620ec4 100644 --- a/mobile/lib/presentation/widgets/album/album_selector.widget.dart +++ b/mobile/lib/presentation/widgets/album/album_selector.widget.dart @@ -14,6 +14,7 @@ import 'package:immich_mobile/models/albums/album_search.model.dart'; import 'package:immich_mobile/pages/common/large_leading_tile.dart'; import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/routing/router.dart'; @@ -578,6 +579,7 @@ class AddToAlbumHeader extends ConsumerWidget { return; } + ref.read(currentRemoteAlbumProvider.notifier).setAlbum(newAlbum); context.pushRoute(RemoteAlbumRoute(album: newAlbum)); } From c5f14adff035328dd3af04b00318b07c560b5909 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 31 Jul 2025 21:29:01 -0500 Subject: [PATCH 139/748] feat: drag to select beta timeline (#20456) --- .../widgets/timeline/fixed/segment.model.dart | 13 +- .../widgets/timeline/timeline.widget.dart | 159 ++++++++++--- .../timeline/timeline_drag_region.dart | 212 ++++++++++++++++++ 3 files changed, 343 insertions(+), 41 deletions(-) create mode 100644 mobile/lib/presentation/widgets/timeline/timeline_drag_region.dart diff --git a/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart b/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart index a5f0c19eb8..05f96d49de 100644 --- a/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart +++ b/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart @@ -1,7 +1,7 @@ import 'dart:math' as math; import 'package:auto_route/auto_route.dart'; -import 'package:flutter/widgets.dart'; +import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/services/timeline.service.dart'; @@ -11,6 +11,7 @@ import 'package:immich_mobile/presentation/widgets/timeline/header.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart'; import 'package:immich_mobile/presentation/widgets/timeline/segment_builder.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline_drag_region.dart'; import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart'; import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; @@ -125,10 +126,14 @@ class _FixedSegmentRow extends ConsumerWidget { textDirection: Directionality.of(context), children: [ for (int i = 0; i < assets.length; i++) - _AssetTileWidget( - key: ValueKey(Object.hash(assets[i].heroTag, assetIndex + i, timelineService.hashCode)), - asset: assets[i], + TimelineAssetIndexWrapper( assetIndex: assetIndex + i, + segmentIndex: 0, // For simplicity, using 0 for now + child: _AssetTileWidget( + key: ValueKey(Object.hash(assets[i].heroTag, assetIndex + i, timelineService.hashCode)), + asset: assets[i], + assetIndex: assetIndex + i, + ), ), ], ); diff --git a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart index dcf2c74ed5..838edd8a47 100644 --- a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:collection'; import 'dart:math' as math; import 'package:collection/collection.dart'; @@ -7,6 +8,7 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/setting.model.dart'; import 'package:immich_mobile/domain/models/timeline.model.dart'; import 'package:immich_mobile/domain/utils/event_stream.dart'; @@ -16,6 +18,7 @@ import 'package:immich_mobile/presentation/widgets/bottom_sheet/general_bottom_s import 'package:immich_mobile/presentation/widgets/timeline/scrubber.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline_drag_region.dart'; import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; @@ -89,6 +92,12 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { final _scrollController = ScrollController(); StreamSubscription? _eventSubscription; + // Drag selection state + bool _dragging = false; + TimelineAssetIndex? _dragAnchorIndex; + final Set _draggedAssets = HashSet(); + ScrollPhysics? _scrollPhysics; + int _perRow = 4; double _scaleFactor = 3.0; double _baseScaleFactor = 3.0; @@ -164,6 +173,71 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { }); } + // Drag selection methods + void _setDragStartIndex(TimelineAssetIndex index) { + setState(() { + _scrollPhysics = const ClampingScrollPhysics(); + _dragAnchorIndex = index; + _dragging = true; + }); + } + + void _stopDrag() { + WidgetsBinding.instance.addPostFrameCallback((_) { + // Update the physics post frame to prevent sudden change in physics on iOS. + setState(() { + _scrollPhysics = null; + }); + }); + setState(() { + _dragging = false; + _draggedAssets.clear(); + }); + // Reset the scrolling state after a small delay to allow bottom sheet to expand again + Future.delayed(const Duration(milliseconds: 300), () { + if (mounted) { + ref.read(timelineStateProvider.notifier).setScrolling(false); + } + }); + } + + void _dragScroll(ScrollDirection direction) { + _scrollController.animateTo( + _scrollController.offset + (direction == ScrollDirection.forward ? 175 : -175), + duration: const Duration(milliseconds: 125), + curve: Curves.easeOut, + ); + } + + void _handleDragAssetEnter(TimelineAssetIndex index) { + if (_dragAnchorIndex == null || !_dragging) return; + + final timelineService = ref.read(timelineServiceProvider); + final dragAnchorIndex = _dragAnchorIndex!; + + // Calculate the range of assets to select + final startIndex = math.min(dragAnchorIndex.assetIndex, index.assetIndex); + final endIndex = math.max(dragAnchorIndex.assetIndex, index.assetIndex); + final count = endIndex - startIndex + 1; + + // Load the assets in the range + if (timelineService.hasRange(startIndex, count)) { + final selectedAssets = timelineService.getAssets(startIndex, count); + + // Clear previous drag selection and add new range + final multiSelectNotifier = ref.read(multiSelectProvider.notifier); + for (final asset in _draggedAssets) { + multiSelectNotifier.deselectAsset(asset); + } + _draggedAssets.clear(); + + for (final asset in selectedAssets) { + multiSelectNotifier.selectAsset(asset); + _draggedAssets.add(asset); + } + } + } + @override Widget build(BuildContext _) { final asyncSegments = ref.watch(timelineSegmentProvider); @@ -216,46 +290,57 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { }, ), }, - child: Stack( - children: [ - Scrubber( - layoutSegments: segments, - timelineHeight: maxHeight, - topPadding: topPadding, - bottomPadding: bottomPadding, - monthSegmentSnappingOffset: widget.topSliverWidgetHeight ?? 0 + appBarExpandedHeight, - child: CustomScrollView( - primary: true, - cacheExtent: maxHeight * 2, - slivers: [ - if (isSelectionMode) - const SelectionSliverAppBar() - else if (widget.appBar != null) - widget.appBar!, - if (widget.topSliverWidget != null) widget.topSliverWidget!, - _SliverSegmentedList( - segments: segments, - delegate: SliverChildBuilderDelegate( - (ctx, index) { - if (index >= childCount) return null; - final segment = segments.findByIndex(index); - return segment?.builder(ctx, index) ?? const SizedBox.shrink(); - }, - childCount: childCount, - addAutomaticKeepAlives: false, - // We add repaint boundary around tiles, so skip the auto boundaries - addRepaintBoundaries: false, + child: TimelineDragRegion( + onStart: _setDragStartIndex, + onAssetEnter: _handleDragAssetEnter, + onEnd: _stopDrag, + onScroll: _dragScroll, + onScrollStart: () { + // Minimize the bottom sheet when drag selection starts + ref.read(timelineStateProvider.notifier).setScrolling(true); + }, + child: Stack( + children: [ + Scrubber( + layoutSegments: segments, + timelineHeight: maxHeight, + topPadding: topPadding, + bottomPadding: bottomPadding, + monthSegmentSnappingOffset: widget.topSliverWidgetHeight ?? 0 + appBarExpandedHeight, + child: CustomScrollView( + primary: true, + physics: _scrollPhysics, + cacheExtent: maxHeight * 2, + slivers: [ + if (isSelectionMode) + const SelectionSliverAppBar() + else if (widget.appBar != null) + widget.appBar!, + if (widget.topSliverWidget != null) widget.topSliverWidget!, + _SliverSegmentedList( + segments: segments, + delegate: SliverChildBuilderDelegate( + (ctx, index) { + if (index >= childCount) return null; + final segment = segments.findByIndex(index); + return segment?.builder(ctx, index) ?? const SizedBox.shrink(); + }, + childCount: childCount, + addAutomaticKeepAlives: false, + // We add repaint boundary around tiles, so skip the auto boundaries + addRepaintBoundaries: false, + ), ), - ), - const SliverPadding(padding: EdgeInsets.only(bottom: scrubberBottomPadding)), - ], + const SliverPadding(padding: EdgeInsets.only(bottom: scrubberBottomPadding)), + ], + ), ), - ), - if (!isSelectionMode && isMultiSelectEnabled) ...[ - const Positioned(top: 60, left: 25, child: _MultiSelectStatusButton()), - if (widget.bottomSheet != null) widget.bottomSheet!, + if (!isSelectionMode && isMultiSelectEnabled) ...[ + const Positioned(top: 60, left: 25, child: _MultiSelectStatusButton()), + if (widget.bottomSheet != null) widget.bottomSheet!, + ], ], - ], + ), ), ), ); diff --git a/mobile/lib/presentation/widgets/timeline/timeline_drag_region.dart b/mobile/lib/presentation/widgets/timeline/timeline_drag_region.dart new file mode 100644 index 0000000000..88d46b143f --- /dev/null +++ b/mobile/lib/presentation/widgets/timeline/timeline_drag_region.dart @@ -0,0 +1,212 @@ +import 'dart:async'; + +import 'package:collection/collection.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; + +class TimelineDragRegion extends StatefulWidget { + final Widget child; + + final void Function(TimelineAssetIndex valueKey)? onStart; + final void Function(TimelineAssetIndex valueKey)? onAssetEnter; + final void Function()? onEnd; + final void Function()? onScrollStart; + final void Function(ScrollDirection direction)? onScroll; + + const TimelineDragRegion({ + super.key, + required this.child, + this.onStart, + this.onAssetEnter, + this.onEnd, + this.onScrollStart, + this.onScroll, + }); + + @override + State createState() => _TimelineDragRegionState(); +} + +class _TimelineDragRegionState extends State { + late TimelineAssetIndex? assetUnderPointer; + late TimelineAssetIndex? anchorAsset; + + // Scroll related state + static const double scrollOffset = 0.10; + double? topScrollOffset; + double? bottomScrollOffset; + Timer? scrollTimer; + late bool scrollNotified; + + @override + void initState() { + super.initState(); + assetUnderPointer = null; + anchorAsset = null; + scrollNotified = false; + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + topScrollOffset = null; + bottomScrollOffset = null; + } + + @override + void dispose() { + scrollTimer?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return RawGestureDetector( + gestures: { + _CustomLongPressGestureRecognizer: GestureRecognizerFactoryWithHandlers<_CustomLongPressGestureRecognizer>( + () => _CustomLongPressGestureRecognizer(), + _registerCallbacks, + ), + }, + child: widget.child, + ); + } + + void _registerCallbacks(_CustomLongPressGestureRecognizer recognizer) { + recognizer.onLongPressMoveUpdate = (details) => _onLongPressMove(details); + recognizer.onLongPressStart = (details) => _onLongPressStart(details); + recognizer.onLongPressUp = _onLongPressEnd; + } + + TimelineAssetIndex? _getValueKeyAtPosition(Offset position) { + final box = context.findAncestorRenderObjectOfType(); + if (box == null) return null; + + final hitTestResult = BoxHitTestResult(); + final local = box.globalToLocal(position); + if (!box.hitTest(hitTestResult, position: local)) return null; + + return (hitTestResult.path.firstWhereOrNull((hit) => hit.target is _TimelineAssetIndexProxy)?.target + as _TimelineAssetIndexProxy?) + ?.index; + } + + void _onLongPressStart(LongPressStartDetails event) { + /// Calculate widget height and scroll offset when long press starting instead of in [initState] + /// or [didChangeDependencies] as the grid might still be rendering into view to get the actual size + final height = context.size?.height; + if (height != null && (topScrollOffset == null || bottomScrollOffset == null)) { + topScrollOffset = height * scrollOffset; + bottomScrollOffset = height - topScrollOffset!; + } + + final initialHit = _getValueKeyAtPosition(event.globalPosition); + anchorAsset = initialHit; + if (initialHit == null) return; + + if (anchorAsset != null) { + widget.onStart?.call(anchorAsset!); + } + } + + void _onLongPressEnd() { + scrollNotified = false; + scrollTimer?.cancel(); + widget.onEnd?.call(); + } + + void _onLongPressMove(LongPressMoveUpdateDetails event) { + if (anchorAsset == null) return; + if (topScrollOffset == null || bottomScrollOffset == null) return; + + final currentDy = event.localPosition.dy; + + if (currentDy > bottomScrollOffset!) { + scrollTimer ??= Timer.periodic( + const Duration(milliseconds: 50), + (_) => widget.onScroll?.call(ScrollDirection.forward), + ); + } else if (currentDy < topScrollOffset!) { + scrollTimer ??= Timer.periodic( + const Duration(milliseconds: 50), + (_) => widget.onScroll?.call(ScrollDirection.reverse), + ); + } else { + scrollTimer?.cancel(); + scrollTimer = null; + } + + final currentlyTouchingAsset = _getValueKeyAtPosition(event.globalPosition); + if (currentlyTouchingAsset == null) return; + + if (assetUnderPointer != currentlyTouchingAsset) { + if (!scrollNotified) { + scrollNotified = true; + widget.onScrollStart?.call(); + } + + widget.onAssetEnter?.call(currentlyTouchingAsset); + assetUnderPointer = currentlyTouchingAsset; + } + } +} + +class _CustomLongPressGestureRecognizer extends LongPressGestureRecognizer { + @override + void rejectGesture(int pointer) { + acceptGesture(pointer); + } +} + +class TimelineAssetIndexWrapper extends SingleChildRenderObjectWidget { + final int assetIndex; + final int segmentIndex; + + const TimelineAssetIndexWrapper({ + required Widget super.child, + required this.assetIndex, + required this.segmentIndex, + super.key, + }); + + @override + // ignore: library_private_types_in_public_api + _TimelineAssetIndexProxy createRenderObject(BuildContext context) { + return _TimelineAssetIndexProxy( + index: TimelineAssetIndex(assetIndex: assetIndex, segmentIndex: segmentIndex), + ); + } + + @override + void updateRenderObject( + BuildContext context, + // ignore: library_private_types_in_public_api + _TimelineAssetIndexProxy renderObject, + ) { + renderObject.index = TimelineAssetIndex(assetIndex: assetIndex, segmentIndex: segmentIndex); + } +} + +class _TimelineAssetIndexProxy extends RenderProxyBox { + TimelineAssetIndex index; + + _TimelineAssetIndexProxy({required this.index}); +} + +class TimelineAssetIndex { + final int assetIndex; + final int segmentIndex; + + const TimelineAssetIndex({required this.assetIndex, required this.segmentIndex}); + + @override + bool operator ==(covariant TimelineAssetIndex other) { + if (identical(this, other)) return true; + + return other.assetIndex == assetIndex && other.segmentIndex == segmentIndex; + } + + @override + int get hashCode => assetIndex.hashCode ^ segmentIndex.hashCode; +} From 9242afb4b01137cdb031f73a0e361f35d70b539e Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 1 Aug 2025 02:45:16 +0000 Subject: [PATCH 140/748] chore: version v1.137.2 --- cli/package-lock.json | 6 +++--- cli/package.json | 2 +- docs/static/archived-versions.json | 4 ++++ e2e/package-lock.json | 8 ++++---- e2e/package.json | 2 +- mobile/android/fastlane/Fastfile | 2 +- mobile/ios/fastlane/Fastfile | 2 +- mobile/openapi/README.md | 2 +- mobile/pubspec.yaml | 2 +- open-api/immich-openapi-specs.json | 2 +- open-api/typescript-sdk/package-lock.json | 4 ++-- open-api/typescript-sdk/package.json | 2 +- open-api/typescript-sdk/src/fetch-client.ts | 2 +- server/package-lock.json | 4 ++-- server/package.json | 2 +- web/package-lock.json | 6 +++--- web/package.json | 2 +- 17 files changed, 29 insertions(+), 25 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index d27a7371a4..777789eb51 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@immich/cli", - "version": "2.2.75", + "version": "2.2.76", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/cli", - "version": "2.2.75", + "version": "2.2.76", "license": "GNU Affero General Public License version 3", "dependencies": { "chokidar": "^4.0.3", @@ -54,7 +54,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.137.1", + "version": "1.137.2", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { diff --git a/cli/package.json b/cli/package.json index b8db8cbd06..d7147a0f36 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@immich/cli", - "version": "2.2.75", + "version": "2.2.76", "description": "Command Line Interface (CLI) for Immich", "type": "module", "exports": "./dist/index.js", diff --git a/docs/static/archived-versions.json b/docs/static/archived-versions.json index d41f1a7bc4..e64d8c8b35 100644 --- a/docs/static/archived-versions.json +++ b/docs/static/archived-versions.json @@ -1,4 +1,8 @@ [ + { + "label": "v1.137.2", + "url": "https://v1.137.2.archive.immich.app" + }, { "label": "v1.137.1", "url": "https://v1.137.1.archive.immich.app" diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 473748d065..7c05316f86 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich-e2e", - "version": "1.137.1", + "version": "1.137.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-e2e", - "version": "1.137.1", + "version": "1.137.2", "license": "GNU Affero General Public License version 3", "devDependencies": { "@eslint/eslintrc": "^3.1.0", @@ -46,7 +46,7 @@ }, "../cli": { "name": "@immich/cli", - "version": "2.2.75", + "version": "2.2.76", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { @@ -95,7 +95,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.137.1", + "version": "1.137.2", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { diff --git a/e2e/package.json b/e2e/package.json index d736a9201f..1d6d757306 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -1,6 +1,6 @@ { "name": "immich-e2e", - "version": "1.137.1", + "version": "1.137.2", "description": "", "main": "index.js", "type": "module", diff --git a/mobile/android/fastlane/Fastfile b/mobile/android/fastlane/Fastfile index 35af2a1e1f..e693eeb491 100644 --- a/mobile/android/fastlane/Fastfile +++ b/mobile/android/fastlane/Fastfile @@ -36,7 +36,7 @@ platform :android do build_type: 'Release', properties: { "android.injected.version.code" => 205, - "android.injected.version.name" => "1.137.1", + "android.injected.version.name" => "1.137.2", } ) upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab') diff --git a/mobile/ios/fastlane/Fastfile b/mobile/ios/fastlane/Fastfile index c3eaf44cfc..704e7c8590 100644 --- a/mobile/ios/fastlane/Fastfile +++ b/mobile/ios/fastlane/Fastfile @@ -22,7 +22,7 @@ platform :ios do path: "./Runner.xcodeproj", ) increment_version_number( - version_number: "1.137.1" + version_number: "1.137.2" ) increment_build_number( build_number: latest_testflight_build_number + 1, diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 8bd08e4d13..395e81227e 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -3,7 +3,7 @@ Immich API This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: -- API version: 1.137.1 +- API version: 1.137.2 - Generator version: 7.8.0 - Build package: org.openapitools.codegen.languages.DartClientCodegen diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index d3448d7f1e..22644ffcbc 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -2,7 +2,7 @@ name: immich_mobile description: Immich - selfhosted backup media file on mobile phone publish_to: 'none' -version: 1.137.1+3001 +version: 1.137.2+3002 environment: sdk: '>=3.8.0 <4.0.0' diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 1288c95df8..7b503e2253 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -9469,7 +9469,7 @@ "info": { "title": "Immich", "description": "Immich API", - "version": "1.137.1", + "version": "1.137.2", "contact": {} }, "tags": [], diff --git a/open-api/typescript-sdk/package-lock.json b/open-api/typescript-sdk/package-lock.json index bb49b1060f..fe9f3d4485 100644 --- a/open-api/typescript-sdk/package-lock.json +++ b/open-api/typescript-sdk/package-lock.json @@ -1,12 +1,12 @@ { "name": "@immich/sdk", - "version": "1.137.1", + "version": "1.137.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/sdk", - "version": "1.137.1", + "version": "1.137.2", "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index 415695de62..351345926a 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@immich/sdk", - "version": "1.137.1", + "version": "1.137.2", "description": "Auto-generated TypeScript SDK for the Immich API", "type": "module", "main": "./build/index.js", diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 6a8ee07b4f..cb4f6b7f8f 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -1,6 +1,6 @@ /** * Immich - * 1.137.1 + * 1.137.2 * DO NOT MODIFY - This file has been generated using oazapfts. * See https://www.npmjs.com/package/oazapfts */ diff --git a/server/package-lock.json b/server/package-lock.json index f59c267496..01db436776 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich", - "version": "1.137.1", + "version": "1.137.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich", - "version": "1.137.1", + "version": "1.137.2", "license": "GNU Affero General Public License version 3", "dependencies": { "@nestjs/bullmq": "^11.0.1", diff --git a/server/package.json b/server/package.json index 1a00b2c243..19f0d8e344 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "immich", - "version": "1.137.1", + "version": "1.137.2", "description": "", "author": "", "private": true, diff --git a/web/package-lock.json b/web/package-lock.json index 1f4769083b..c687883ae7 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich-web", - "version": "1.137.1", + "version": "1.137.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-web", - "version": "1.137.1", + "version": "1.137.2", "license": "GNU Affero General Public License version 3", "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", @@ -94,7 +94,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.137.1", + "version": "1.137.2", "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" diff --git a/web/package.json b/web/package.json index ad33a36a0f..57196bc6f9 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "immich-web", - "version": "1.137.1", + "version": "1.137.2", "license": "GNU Affero General Public License version 3", "type": "module", "scripts": { From 1b8354ed36a1910898c3fcb5588be78782f02206 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 1 Aug 2025 05:38:52 -0500 Subject: [PATCH 141/748] chore: post release tasks (#20497) --- mobile/android/fastlane/Fastfile | 2 +- mobile/ios/Runner.xcodeproj/project.pbxproj | 18 +++++++++--------- mobile/ios/Runner/Info.plist | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/mobile/android/fastlane/Fastfile b/mobile/android/fastlane/Fastfile index e693eeb491..09e72dfa25 100644 --- a/mobile/android/fastlane/Fastfile +++ b/mobile/android/fastlane/Fastfile @@ -35,7 +35,7 @@ platform :android do task: 'bundle', build_type: 'Release', properties: { - "android.injected.version.code" => 205, + "android.injected.version.code" => 3002, "android.injected.version.name" => "1.137.2", } ) diff --git a/mobile/ios/Runner.xcodeproj/project.pbxproj b/mobile/ios/Runner.xcodeproj/project.pbxproj index fb0908e8b6..218a294c33 100644 --- a/mobile/ios/Runner.xcodeproj/project.pbxproj +++ b/mobile/ios/Runner.xcodeproj/project.pbxproj @@ -649,7 +649,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 213; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; @@ -793,7 +793,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 213; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; @@ -823,7 +823,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 213; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; @@ -857,7 +857,7 @@ CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 213; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -900,7 +900,7 @@ CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 213; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -940,7 +940,7 @@ CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 213; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -979,7 +979,7 @@ CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 213; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -1023,7 +1023,7 @@ CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 213; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -1064,7 +1064,7 @@ CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 213; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; diff --git a/mobile/ios/Runner/Info.plist b/mobile/ios/Runner/Info.plist index 4237813dfc..d3e42b9939 100644 --- a/mobile/ios/Runner/Info.plist +++ b/mobile/ios/Runner/Info.plist @@ -78,7 +78,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.135.1 + 1.137.2 CFBundleSignature ???? CFBundleURLTypes @@ -105,7 +105,7 @@ CFBundleVersion - 210 + 213 FLTEnableImpeller ITSAppUsesNonExemptEncryption From 8108f50c4e0e5ef02b0e9dbf353ff67fd22ed28d Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Fri, 1 Aug 2025 16:09:59 +0530 Subject: [PATCH 142/748] fix: guard IS_FAVORITE column with SDK check (#20511) Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- .../alextran/immich/sync/MessagesImplBase.kt | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt index d7073e7cfc..b2ceb8a9f2 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt @@ -29,21 +29,24 @@ open class NativeSyncApiImplBase(context: Context) { MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO.toString() ) const val BUCKET_SELECTION = "(${MediaStore.Files.FileColumns.BUCKET_ID} = ?)" - val ASSET_PROJECTION = arrayOf( - MediaStore.MediaColumns._ID, - MediaStore.MediaColumns.DATA, - MediaStore.MediaColumns.DISPLAY_NAME, - MediaStore.MediaColumns.DATE_TAKEN, - MediaStore.MediaColumns.DATE_ADDED, - MediaStore.MediaColumns.DATE_MODIFIED, - MediaStore.Files.FileColumns.MEDIA_TYPE, - MediaStore.MediaColumns.BUCKET_ID, - MediaStore.MediaColumns.WIDTH, - MediaStore.MediaColumns.HEIGHT, - MediaStore.MediaColumns.DURATION, - MediaStore.MediaColumns.ORIENTATION, - MediaStore.MediaColumns.IS_FAVORITE, - ) + val ASSET_PROJECTION = buildList { + add(MediaStore.MediaColumns._ID) + add(MediaStore.MediaColumns.DATA) + add(MediaStore.MediaColumns.DISPLAY_NAME) + add(MediaStore.MediaColumns.DATE_TAKEN) + add(MediaStore.MediaColumns.DATE_ADDED) + add(MediaStore.MediaColumns.DATE_MODIFIED) + add(MediaStore.Files.FileColumns.MEDIA_TYPE) + add(MediaStore.MediaColumns.BUCKET_ID) + add(MediaStore.MediaColumns.WIDTH) + add(MediaStore.MediaColumns.HEIGHT) + add(MediaStore.MediaColumns.DURATION) + add(MediaStore.MediaColumns.ORIENTATION) + // IS_FAVORITE is only available on Android 11 and above + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { + add(MediaStore.MediaColumns.IS_FAVORITE) + } + }.toTypedArray() const val HASH_BUFFER_SIZE = 2 * 1024 * 1024 } @@ -78,7 +81,7 @@ open class NativeSyncApiImplBase(context: Context) { val durationColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.DURATION) val orientationColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.ORIENTATION) - val favoriteColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.IS_FAVORITE) + val favoriteColumn = c.getColumnIndex(MediaStore.MediaColumns.IS_FAVORITE) while (c.moveToNext()) { val id = c.getLong(idColumn).toString() @@ -107,7 +110,7 @@ open class NativeSyncApiImplBase(context: Context) { else c.getLong(durationColumn) / 1000 val bucketId = c.getString(bucketIdColumn) val orientation = c.getInt(orientationColumn) - val isFavorite = c.getInt(favoriteColumn) != 0; + val isFavorite = if (favoriteColumn == -1) false else c.getInt(favoriteColumn) != 0 val asset = PlatformAsset( id, From 4d5cd1a6b50a0575567a4d7f4d1d65a06597d73a Mon Sep 17 00:00:00 2001 From: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Fri, 1 Aug 2025 16:49:51 +0200 Subject: [PATCH 143/748] fix: migration if media location is set (#20532) --- server/src/services/storage.service.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/src/services/storage.service.ts b/server/src/services/storage.service.ts index bcfb34535c..5b08204bf9 100644 --- a/server/src/services/storage.service.ts +++ b/server/src/services/storage.service.ts @@ -101,6 +101,10 @@ export class StorageService extends BaseService { const savedValue = await this.systemMetadataRepository.get(SystemMetadataKey.MediaLocation); let previous = savedValue?.location || ''; + if (!previous && this.configRepository.getEnv().storage.mediaLocation) { + previous = current; + } + if (!previous) { previous = path.startsWith('upload/') ? 'upload' : '/usr/src/app/upload'; } From 007ba1d9efdc571c87ca1553dd28dbbfe7eeee2f Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 1 Aug 2025 14:52:24 +0000 Subject: [PATCH 144/748] chore: version v1.137.3 --- cli/package-lock.json | 6 +++--- cli/package.json | 2 +- docs/static/archived-versions.json | 4 ++++ e2e/package-lock.json | 8 ++++---- e2e/package.json | 2 +- mobile/android/fastlane/Fastfile | 2 +- mobile/ios/fastlane/Fastfile | 2 +- mobile/openapi/README.md | 2 +- mobile/pubspec.yaml | 2 +- open-api/immich-openapi-specs.json | 2 +- open-api/typescript-sdk/package-lock.json | 4 ++-- open-api/typescript-sdk/package.json | 2 +- open-api/typescript-sdk/src/fetch-client.ts | 2 +- server/package-lock.json | 4 ++-- server/package.json | 2 +- web/package-lock.json | 6 +++--- web/package.json | 2 +- 17 files changed, 29 insertions(+), 25 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index 777789eb51..1271247865 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@immich/cli", - "version": "2.2.76", + "version": "2.2.77", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/cli", - "version": "2.2.76", + "version": "2.2.77", "license": "GNU Affero General Public License version 3", "dependencies": { "chokidar": "^4.0.3", @@ -54,7 +54,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.137.2", + "version": "1.137.3", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { diff --git a/cli/package.json b/cli/package.json index d7147a0f36..a5c1b19159 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@immich/cli", - "version": "2.2.76", + "version": "2.2.77", "description": "Command Line Interface (CLI) for Immich", "type": "module", "exports": "./dist/index.js", diff --git a/docs/static/archived-versions.json b/docs/static/archived-versions.json index e64d8c8b35..04c6604a14 100644 --- a/docs/static/archived-versions.json +++ b/docs/static/archived-versions.json @@ -1,4 +1,8 @@ [ + { + "label": "v1.137.3", + "url": "https://v1.137.3.archive.immich.app" + }, { "label": "v1.137.2", "url": "https://v1.137.2.archive.immich.app" diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 7c05316f86..319f1b34b3 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich-e2e", - "version": "1.137.2", + "version": "1.137.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-e2e", - "version": "1.137.2", + "version": "1.137.3", "license": "GNU Affero General Public License version 3", "devDependencies": { "@eslint/eslintrc": "^3.1.0", @@ -46,7 +46,7 @@ }, "../cli": { "name": "@immich/cli", - "version": "2.2.76", + "version": "2.2.77", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { @@ -95,7 +95,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.137.2", + "version": "1.137.3", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { diff --git a/e2e/package.json b/e2e/package.json index 1d6d757306..8b8540e9ba 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -1,6 +1,6 @@ { "name": "immich-e2e", - "version": "1.137.2", + "version": "1.137.3", "description": "", "main": "index.js", "type": "module", diff --git a/mobile/android/fastlane/Fastfile b/mobile/android/fastlane/Fastfile index 09e72dfa25..8fd2ba3059 100644 --- a/mobile/android/fastlane/Fastfile +++ b/mobile/android/fastlane/Fastfile @@ -36,7 +36,7 @@ platform :android do build_type: 'Release', properties: { "android.injected.version.code" => 3002, - "android.injected.version.name" => "1.137.2", + "android.injected.version.name" => "1.137.3", } ) upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab') diff --git a/mobile/ios/fastlane/Fastfile b/mobile/ios/fastlane/Fastfile index 704e7c8590..19f00c5f43 100644 --- a/mobile/ios/fastlane/Fastfile +++ b/mobile/ios/fastlane/Fastfile @@ -22,7 +22,7 @@ platform :ios do path: "./Runner.xcodeproj", ) increment_version_number( - version_number: "1.137.2" + version_number: "1.137.3" ) increment_build_number( build_number: latest_testflight_build_number + 1, diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 395e81227e..c4349ff657 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -3,7 +3,7 @@ Immich API This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: -- API version: 1.137.2 +- API version: 1.137.3 - Generator version: 7.8.0 - Build package: org.openapitools.codegen.languages.DartClientCodegen diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 22644ffcbc..020ce4676e 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -2,7 +2,7 @@ name: immich_mobile description: Immich - selfhosted backup media file on mobile phone publish_to: 'none' -version: 1.137.2+3002 +version: 1.137.3+3002 environment: sdk: '>=3.8.0 <4.0.0' diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 7b503e2253..d97585a39e 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -9469,7 +9469,7 @@ "info": { "title": "Immich", "description": "Immich API", - "version": "1.137.2", + "version": "1.137.3", "contact": {} }, "tags": [], diff --git a/open-api/typescript-sdk/package-lock.json b/open-api/typescript-sdk/package-lock.json index fe9f3d4485..a9b5c295ca 100644 --- a/open-api/typescript-sdk/package-lock.json +++ b/open-api/typescript-sdk/package-lock.json @@ -1,12 +1,12 @@ { "name": "@immich/sdk", - "version": "1.137.2", + "version": "1.137.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/sdk", - "version": "1.137.2", + "version": "1.137.3", "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index 351345926a..f01ec302ce 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@immich/sdk", - "version": "1.137.2", + "version": "1.137.3", "description": "Auto-generated TypeScript SDK for the Immich API", "type": "module", "main": "./build/index.js", diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index cb4f6b7f8f..d26d14aa4a 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -1,6 +1,6 @@ /** * Immich - * 1.137.2 + * 1.137.3 * DO NOT MODIFY - This file has been generated using oazapfts. * See https://www.npmjs.com/package/oazapfts */ diff --git a/server/package-lock.json b/server/package-lock.json index 01db436776..ed2dc2e14f 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich", - "version": "1.137.2", + "version": "1.137.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich", - "version": "1.137.2", + "version": "1.137.3", "license": "GNU Affero General Public License version 3", "dependencies": { "@nestjs/bullmq": "^11.0.1", diff --git a/server/package.json b/server/package.json index 19f0d8e344..472b746630 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "immich", - "version": "1.137.2", + "version": "1.137.3", "description": "", "author": "", "private": true, diff --git a/web/package-lock.json b/web/package-lock.json index c687883ae7..cfd65b63d8 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich-web", - "version": "1.137.2", + "version": "1.137.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-web", - "version": "1.137.2", + "version": "1.137.3", "license": "GNU Affero General Public License version 3", "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", @@ -94,7 +94,7 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.137.2", + "version": "1.137.3", "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" diff --git a/web/package.json b/web/package.json index 57196bc6f9..bb17955b08 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "immich-web", - "version": "1.137.2", + "version": "1.137.3", "license": "GNU Affero General Public License version 3", "type": "module", "scripts": { From e2c3c395971ed2235e6c6864aae453d2e843d35d Mon Sep 17 00:00:00 2001 From: Alden Bansemer Date: Sun, 3 Aug 2025 23:07:11 -0700 Subject: [PATCH 145/748] chore: tweak photo sphere fov and zoom speed constants (#20595) --- .../asset-viewer/photo-sphere-viewer-adapter.svelte | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web/src/lib/components/asset-viewer/photo-sphere-viewer-adapter.svelte b/web/src/lib/components/asset-viewer/photo-sphere-viewer-adapter.svelte index 820c7f3fd4..cd9a010f78 100644 --- a/web/src/lib/components/asset-viewer/photo-sphere-viewer-adapter.svelte +++ b/web/src/lib/components/asset-viewer/photo-sphere-viewer-adapter.svelte @@ -63,8 +63,9 @@ touchmoveTwoFingers: false, mousewheelCtrlKey: false, navbar, - minFov: 10, - maxFov: 120, + minFov: 15, + maxFov: 90, + zoomSpeed: 0.5, fisheye: false, }); const resolutionPlugin = viewer.getPlugin(ResolutionPlugin) as ResolutionPlugin; From 4efbf36d821149986d2b487be7447c2b8a008364 Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Mon, 4 Aug 2025 11:37:50 +0530 Subject: [PATCH 146/748] chore: log asset name on hash failures (#20608) --- mobile/lib/domain/services/hash.service.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/lib/domain/services/hash.service.dart b/mobile/lib/domain/services/hash.service.dart index 2a07320906..36a4b9efba 100644 --- a/mobile/lib/domain/services/hash.service.dart +++ b/mobile/lib/domain/services/hash.service.dart @@ -96,7 +96,7 @@ class HashService { if (hash?.length == 20) { hashed.add(asset.copyWith(checksum: base64.encode(hash!))); } else { - _log.warning("Failed to hash file for ${asset.id}"); + _log.warning("Failed to hash file for ${asset.id}: ${asset.name} created at ${asset.createdAt}"); } } From 3d633a81c4116df0bff2548403fab1c0b3892f87 Mon Sep 17 00:00:00 2001 From: Alexandre Garnier Date: Mon, 4 Aug 2025 08:23:11 +0200 Subject: [PATCH 147/748] fix(mobile): use right translation function for pluralized ICU message format (#20404) --- i18n/en.json | 3 +-- mobile/lib/widgets/map/map_asset_grid.dart | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/i18n/en.json b/i18n/en.json index 700ff60c53..a66c3faa9a 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1252,7 +1252,7 @@ "manage_your_devices": "Manage your logged-in devices", "manage_your_oauth_connection": "Manage your OAuth connection", "map": "Map", - "map_assets_in_bounds": "{count, plural, one {# photo} other {# photos}}", + "map_assets_in_bounds": "{count, plural, =0 {No photos in this area} one {# photo} other {# photos}}", "map_cannot_get_user_location": "Cannot get user's location", "map_location_dialog_yes": "Yes", "map_location_picker_page_use_location": "Use this location", @@ -1260,7 +1260,6 @@ "map_location_service_disabled_title": "Location Service disabled", "map_marker_for_images": "Map marker for images taken in {city}, {country}", "map_marker_with_image": "Map marker with image", - "map_no_assets_in_bounds": "No photos in this area", "map_no_location_permission_content": "Location permission is needed to display assets from your current location. Do you want to allow it now?", "map_no_location_permission_title": "Location Permission denied", "map_settings": "Map settings", diff --git a/mobile/lib/widgets/map/map_asset_grid.dart b/mobile/lib/widgets/map/map_asset_grid.dart index 893c36d43f..488b8480f2 100644 --- a/mobile/lib/widgets/map/map_asset_grid.dart +++ b/mobile/lib/widgets/map/map_asset_grid.dart @@ -8,6 +8,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/collection_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/models/map/map_event.model.dart'; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/providers/timeline.provider.dart'; @@ -229,9 +230,7 @@ class _MapSheetDragRegion extends StatelessWidget { @override Widget build(BuildContext context) { - final assetsInBoundsText = assetsInBoundCount > 0 - ? "map_assets_in_bounds".tr(namedArgs: {'count': assetsInBoundCount.toString()}) - : "map_no_assets_in_bounds".tr(); + final assetsInBoundsText = "map_assets_in_bounds".t(context: context, args: {'count': assetsInBoundCount}); return SingleChildScrollView( controller: controller, From c8f9a72d3e59b610d78a1f3efcebd6c13d59c470 Mon Sep 17 00:00:00 2001 From: bo0tzz Date: Mon, 4 Aug 2025 18:15:15 +0200 Subject: [PATCH 148/748] feat: close likely duplicates (#20556) Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- .github/workflows/close-duplicates.yml | 96 ++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 .github/workflows/close-duplicates.yml diff --git a/.github/workflows/close-duplicates.yml b/.github/workflows/close-duplicates.yml new file mode 100644 index 0000000000..5ef56a6daf --- /dev/null +++ b/.github/workflows/close-duplicates.yml @@ -0,0 +1,96 @@ +on: + issues: + types: [opened] + discussion: + types: [created] + +name: Close likely duplicates +permissions: {} + +jobs: + get_body: + runs-on: ubuntu-latest + env: + EVENT: ${{ toJSON(github.event) }} + outputs: + body: ${{ steps.get_body.outputs.body }} + steps: + - id: get_body + run: | + BODY=$(echo """$EVENT""" | jq -r '.issue // .discussion | .body' | base64 -w 0) + echo "body=$BODY" >> $GITHUB_OUTPUT + + get_checkbox_json: + runs-on: ubuntu-latest + needs: get_body + container: + image: yshavit/mdq:0.7.2 + outputs: + json: ${{ steps.get_checkbox.outputs.json }} + steps: + - id: get_checkbox + env: + BODY: ${{ needs.get_body.outputs.body }} + run: | + JSON=$(echo "$BODY" | base64 -d | /mdq --output json '# I have searched | - [?] Yes') + echo "json=$JSON" >> $GITHUB_OUTPUT + + close_and_comment: + runs-on: ubuntu-latest + needs: get_checkbox_json + if: ${{ !fromJSON(needs.get_checkbox_json.outputs.json).items[0].list[0].checked }} + permissions: + issues: write + discussions: write + steps: + - name: Close issue + if: ${{ github.event_name == 'issues' }} + env: + GH_TOKEN: ${{ github.token }} + NODE_ID: ${{ github.event.issue.node_id }} + run: | + gh api graphql \ + -f issueId="$NODE_ID" \ + -f body="This issue has automatically been closed as it is likely a duplicate. We get a lot of duplicate threads each day, which is why we ask you in the template to confirm that you searched for duplicates before opening one." \ + -f query=' + mutation CommentAndCloseIssue($issueId: ID!, $body: String!) { + addComment(input: { + subjectId: $issueId, + body: $body + }) { + __typename + } + + closeIssue(input: { + issueId: $issueId, + stateReason: DUPLICATE + }) { + __typename + } + }' + + - name: Close discussion + if: ${{ github.event_name == 'discussion' && github.event.discussion.category.name == 'Feature Request' }} + env: + GH_TOKEN: ${{ github.token }} + NODE_ID: ${{ github.event.discussion.node_id }} + run: | + gh api graphql \ + -f discussionId="$NODE_ID" \ + -f body="This discussion has automatically been closed as it is likely a duplicate. We get a lot of duplicate threads each day, which is why we ask you in the template to confirm that you searched for duplicates before opening one." \ + -f query=' + mutation CommentAndCloseDiscussion($discussionId: ID!, $body: String!) { + addDiscussionComment(input: { + discussionId: $discussionId, + body: $body + }) { + __typename + } + + closeDiscussion(input: { + discussionId: $discussionId, + reason: DUPLICATE + }) { + __typename + } + }' From be85832b209f800c75335673524baff52eefecaa Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 4 Aug 2025 12:25:11 -0500 Subject: [PATCH 149/748] fix: add assets to album (#20626) * fix: add assets to album * always navigate back to the albums view from album page --- .../lib/presentation/widgets/album/album_selector.widget.dart | 1 + mobile/lib/widgets/common/remote_album_sliver_app_bar.dart | 4 +++- mobile/lib/widgets/common/selection_sliver_app_bar.dart | 3 +-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/mobile/lib/presentation/widgets/album/album_selector.widget.dart b/mobile/lib/presentation/widgets/album/album_selector.widget.dart index 2eec620ec4..47ce167455 100644 --- a/mobile/lib/presentation/widgets/album/album_selector.widget.dart +++ b/mobile/lib/presentation/widgets/album/album_selector.widget.dart @@ -580,6 +580,7 @@ class AddToAlbumHeader extends ConsumerWidget { } ref.read(currentRemoteAlbumProvider.notifier).setAlbum(newAlbum); + ref.read(multiSelectProvider.notifier).reset(); context.pushRoute(RemoteAlbumRoute(album: newAlbum)); } diff --git a/mobile/lib/widgets/common/remote_album_sliver_app_bar.dart b/mobile/lib/widgets/common/remote_album_sliver_app_bar.dart index 2efe8a3ce1..e1332165bd 100644 --- a/mobile/lib/widgets/common/remote_album_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/remote_album_sliver_app_bar.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:io'; import 'dart:ui'; +import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -18,6 +19,7 @@ import 'package:immich_mobile/providers/infrastructure/current_album.provider.da import 'package:immich_mobile/providers/infrastructure/remote_album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/widgets/album/remote_album_shared_user_icons.dart'; class RemoteAlbumSliverAppBar extends ConsumerStatefulWidget { @@ -93,7 +95,7 @@ class _MesmerizingSliverAppBarState extends ConsumerState { onDone(Set selected) { ref.read(multiSelectProvider.notifier).reset(); - context.maybePop>(selected); + context.pop>(selected); } return SliverAppBar( From 5901c2e9639dda03d1ebc97aa773ef39c817d75c Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Mon, 4 Aug 2025 23:09:40 +0530 Subject: [PATCH 150/748] fix: hide navigation bar in search page during multi-selection (#20616) fix: hide navigation bar in search page during multiselect Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- mobile/lib/pages/common/tab_shell.page.dart | 43 +++++++++++++++---- .../pages/search/drift_search.page.dart | 8 +++- .../base_bottom_sheet.widget.dart | 4 +- .../general_bottom_sheet.widget.dart | 6 ++- .../widgets/timeline/timeline.widget.dart | 6 +++ .../timeline/multiselect.provider.dart | 7 ++- 6 files changed, 60 insertions(+), 14 deletions(-) diff --git a/mobile/lib/pages/common/tab_shell.page.dart b/mobile/lib/pages/common/tab_shell.page.dart index e06f7ca441..2c2c64fb25 100644 --- a/mobile/lib/pages/common/tab_shell.page.dart +++ b/mobile/lib/pages/common/tab_shell.page.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; @@ -137,25 +139,50 @@ void _onNavigationSelected(TabsRouter router, int index, WidgetRef ref) { ref.read(tabProvider.notifier).state = TabEnum.values[index]; } -class _BottomNavigationBar extends ConsumerWidget { +class _BottomNavigationBar extends ConsumerStatefulWidget { const _BottomNavigationBar({required this.tabsRouter, required this.destinations}); final List destinations; final TabsRouter tabsRouter; @override - Widget build(BuildContext context, WidgetRef ref) { - final isScreenLandscape = context.orientation == Orientation.landscape; - final isMultiselectEnabled = ref.watch(multiSelectProvider.select((s) => s.isEnabled)); + ConsumerState createState() => _BottomNavigationBarState(); +} - if (isScreenLandscape || isMultiselectEnabled) { +class _BottomNavigationBarState extends ConsumerState<_BottomNavigationBar> { + bool hideNavigationBar = false; + StreamSubscription? _eventSubscription; + + @override + void initState() { + super.initState(); + _eventSubscription = EventStream.shared.listen(_onEvent); + } + + void _onEvent(MultiSelectToggleEvent event) { + setState(() { + hideNavigationBar = event.isEnabled; + }); + } + + @override + void dispose() { + _eventSubscription?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final isScreenLandscape = context.orientation == Orientation.landscape; + + if (isScreenLandscape || hideNavigationBar) { return const SizedBox.shrink(); } return NavigationBar( - selectedIndex: tabsRouter.activeIndex, - onDestinationSelected: (index) => _onNavigationSelected(tabsRouter, index, ref), - destinations: destinations, + selectedIndex: widget.tabsRouter.activeIndex, + onDestinationSelected: (index) => _onNavigationSelected(widget.tabsRouter, index, ref), + destinations: widget.destinations, ); } } diff --git a/mobile/lib/presentation/pages/search/drift_search.page.dart b/mobile/lib/presentation/pages/search/drift_search.page.dart index f61fad5484..d44b215b03 100644 --- a/mobile/lib/presentation/pages/search/drift_search.page.dart +++ b/mobile/lib/presentation/pages/search/drift_search.page.dart @@ -13,6 +13,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/models/search/search_filter.model.dart'; import 'package:immich_mobile/presentation/pages/search/paginated_search.provider.dart'; +import 'package:immich_mobile/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/search/search_input_focus.provider.dart'; @@ -627,7 +628,12 @@ class _SearchResultGrid extends ConsumerWidget { return timelineService; }), ], - child: Timeline(key: ValueKey(searchResult.totalAssets), appBar: null, groupBy: GroupAssetsBy.none), + child: Timeline( + key: ValueKey(searchResult.totalAssets), + groupBy: GroupAssetsBy.none, + appBar: null, + bottomSheet: const GeneralBottomSheet(minChildSize: 0.20), + ), ), ), ); diff --git a/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart index a2c88d9fd7..82f2e1c3b2 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart @@ -22,13 +22,13 @@ class BaseBottomSheet extends ConsumerStatefulWidget { this.slivers, this.controller, this.initialChildSize = 0.35, - this.minChildSize = 0.15, + double? minChildSize, this.maxChildSize = 0.65, this.expand = true, this.shouldCloseOnMinExtent = true, this.resizeOnScroll = true, this.backgroundColor, - }); + }) : minChildSize = minChildSize ?? 0.15; @override ConsumerState createState() => _BaseDraggableScrollableSheetState(); diff --git a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart index 70b2fb00b0..8edfcb749e 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart @@ -6,8 +6,8 @@ import 'package:immich_mobile/domain/models/album/album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/edit_location_action_button.widget.dart'; @@ -26,7 +26,8 @@ import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; class GeneralBottomSheet extends ConsumerWidget { - const GeneralBottomSheet({super.key}); + final double? minChildSize; + const GeneralBottomSheet({super.key, this.minChildSize}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -60,6 +61,7 @@ class GeneralBottomSheet extends ConsumerWidget { return BaseBottomSheet( initialChildSize: 0.45, + minChildSize: minChildSize, maxChildSize: 0.85, shouldCloseOnMinExtent: false, actions: [ diff --git a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart index 838edd8a47..b0886f020e 100644 --- a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart @@ -115,6 +115,8 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { _baseScaleFactor = _scaleFactor; }); }); + + ref.listenManual(multiSelectProvider.select((s) => s.isEnabled), _onMultiSelectionToggled); } void _onEvent(Event event) { @@ -130,6 +132,10 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { } } + void _onMultiSelectionToggled(_, bool isEnabled) { + EventStream.shared.emit(MultiSelectToggleEvent(isEnabled)); + } + @override void dispose() { _scrollController.dispose(); diff --git a/mobile/lib/providers/timeline/multiselect.provider.dart b/mobile/lib/providers/timeline/multiselect.provider.dart index 742cbd7dea..e225e0c98d 100644 --- a/mobile/lib/providers/timeline/multiselect.provider.dart +++ b/mobile/lib/providers/timeline/multiselect.provider.dart @@ -1,8 +1,8 @@ import 'package:collection/collection.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; - import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/services/timeline.service.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; final multiSelectProvider = NotifierProvider( @@ -10,6 +10,11 @@ final multiSelectProvider = NotifierProvider selectedAssets; final Set lockedSelectionAssets; From b56a272f647b5ac90255443c6154cb09271aeca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Wojtaszko?= Date: Mon, 4 Aug 2025 19:46:46 +0200 Subject: [PATCH 151/748] fix: adjust search bar padding and visibility based on input state (#20598) --- .../components/shared-components/search-bar/search-bar.svelte | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/web/src/lib/components/shared-components/search-bar/search-bar.svelte b/web/src/lib/components/shared-components/search-bar/search-bar.svelte index 4c807e7652..05816d2d76 100644 --- a/web/src/lib/components/shared-components/search-bar/search-bar.svelte +++ b/web/src/lib/components/shared-components/search-bar/search-bar.svelte @@ -230,7 +230,8 @@ type="text" name="q" id="main-search-bar" - class="w-full transition-all border-2 px-14 py-4 max-md:py-2 text-immich-fg/75 dark:text-immich-dark-fg + class="w-full transition-all border-2 ps-14 py-4 max-md:py-2 text-immich-fg/75 dark:text-immich-dark-fg + {showClearIcon ? 'pe-[90px]' : 'pe-14'} {grayTheme ? 'dark:bg-immich-dark-gray' : 'dark:bg-immich-dark-bg'} {showSuggestions && isSearchSuggestions ? 'rounded-t-3xl' : 'rounded-3xl bg-gray-200'} {searchStore.isSearchEnabled ? 'border-gray-200 dark:border-gray-700 bg-white' : 'border-transparent'}" @@ -285,6 +286,7 @@ {#if isFocus}
0} > From 67736c8fcef3b65ae4e250d7e58425e156026627 Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Mon, 4 Aug 2025 14:28:43 -0500 Subject: [PATCH 152/748] fix(mobile): fetch serverConfig before building shared link (#20638) fix(mobile): fetch serverConfig before trying to pull externalDomain for new shared link --- .../lib/pages/library/shared_link/shared_link_edit.page.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart b/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart index dcd503335b..c78a9d5138 100644 --- a/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart +++ b/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart @@ -264,11 +264,15 @@ class SharedLinkEditPage extends HookConsumerWidget { expiresAt: expiryAfter.value == 0 ? null : calculateExpiry(), ); ref.invalidate(sharedLinksStateProvider); + + await ref.read(serverInfoProvider.notifier).getServerConfig(); final externalDomain = ref.read(serverInfoProvider.select((s) => s.serverConfig.externalDomain)); + var serverUrl = externalDomain.isNotEmpty ? externalDomain : getServerUrl(); if (serverUrl != null && !serverUrl.endsWith('/')) { serverUrl += '/'; } + if (newLink != null && serverUrl != null) { newShareLink.value = "${serverUrl}share/${newLink.key}"; copyLinkToClipboard(); From 10141504a2cb8c77995ebd7d98fed193f2c7fa69 Mon Sep 17 00:00:00 2001 From: cford256 Date: Mon, 4 Aug 2025 14:29:51 -0500 Subject: [PATCH 153/748] fix: exif rating rounding (#20457) * fix_Exlif_Metadata_Rating_Rounding_to_Interger Rounding Exlif Rating Interger Images support having numbers other than integers for the rating metadata in EXLIF. The database expects it to be an integer though. Trying to upload an image that has a rating other than an integer results in it failing to parse the image and defaulting to showing a corrupted file icon. Rather than changing the database type, I would like to round the rating to the nearest integer so that Immich works with images that have a rating like this in their metadata. * Changing Metadata validateRange to always round. * Update server/src/services/metadata.service.ts Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> --------- Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> --- server/src/services/metadata.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/services/metadata.service.ts b/server/src/services/metadata.service.ts index 32a3d98f4e..c675b7200d 100644 --- a/server/src/services/metadata.service.ts +++ b/server/src/services/metadata.service.ts @@ -101,7 +101,7 @@ const validateRange = (value: number | undefined, min: number, max: number): Non return null; } - return val; + return Math.round(val); }; const getLensModel = (exifTags: ImmichTags): string | null => { From 278668b8c5c9698a38eccc17b37e7d0a0f7e2fc9 Mon Sep 17 00:00:00 2001 From: Zack Pollard Date: Mon, 4 Aug 2025 22:41:44 +0100 Subject: [PATCH 154/748] fix: improvements to sync and upload when resuming app (#20524) - App will now kick off hashing after local sync if the lifecycle is in resumed or active state - We now wait for hashing to complete before we kick off the upload process --- mobile/lib/providers/app_life_cycle.provider.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mobile/lib/providers/app_life_cycle.provider.dart b/mobile/lib/providers/app_life_cycle.provider.dart index 31342bf23e..0696a8d7f1 100644 --- a/mobile/lib/providers/app_life_cycle.provider.dart +++ b/mobile/lib/providers/app_life_cycle.provider.dart @@ -86,11 +86,12 @@ class AppLifeCycleNotifier extends StateNotifier { // Ensure proper cleanup before starting new background tasks try { await Future.wait([ - backgroundManager.syncLocal().then((_) { + Future(() async { + await backgroundManager.syncLocal(); Logger("AppLifeCycleNotifier").fine("Hashing assets after syncLocal"); // Check if app is still active before hashing - if (state == AppLifeCycleEnum.resumed) { - backgroundManager.hashAssets(); + if ([AppLifeCycleEnum.resumed, AppLifeCycleEnum.active].contains(state)) { + await backgroundManager.hashAssets(); } }), backgroundManager.syncRemote(), From 094e3a27573d6d68793f568019130d90f57a1b8a Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Mon, 4 Aug 2025 16:41:58 -0500 Subject: [PATCH 155/748] fix(mobile): cleanly handle logout when no host is set (#20521) * fix: cleanly handle logging out when no host is set on API * move conditional to auth_api repo --- mobile/lib/repositories/auth_api.repository.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mobile/lib/repositories/auth_api.repository.dart b/mobile/lib/repositories/auth_api.repository.dart index e488f69578..4b0880ddcf 100644 --- a/mobile/lib/repositories/auth_api.repository.dart +++ b/mobile/lib/repositories/auth_api.repository.dart @@ -25,6 +25,8 @@ class AuthApiRepository extends ApiRepository { } Future logout() async { + if (_apiService.apiClient.basePath.isEmpty) return; + await _apiService.authenticationApi.logout().timeout(const Duration(seconds: 7)); } From 4d0c9172e57fd62519c4293ebce55be0a968becd Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Mon, 4 Aug 2025 17:14:26 -0500 Subject: [PATCH 156/748] fix: not clearing local data when logging out while sync is running (#20646) --- mobile/lib/domain/utils/background_sync.dart | 8 ++++++-- mobile/lib/main.dart | 1 - 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/mobile/lib/domain/utils/background_sync.dart b/mobile/lib/domain/utils/background_sync.dart index 1944591c93..cbf4030788 100644 --- a/mobile/lib/domain/utils/background_sync.dart +++ b/mobile/lib/domain/utils/background_sync.dart @@ -37,7 +37,7 @@ class BackgroundSyncManager { this.onHashingError, }); - Future cancel() { + Future cancel() async { final futures = []; if (_syncTask != null) { @@ -52,7 +52,11 @@ class BackgroundSyncManager { _syncWebsocketTask?.cancel(); _syncWebsocketTask = null; - return Future.wait(futures); + try { + await Future.wait(futures); + } on CanceledError { + // Ignore cancellation errors + } } // No need to cancel the task, as it can also be run when the user logs out diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index d1f415a304..0bac282694 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -83,7 +83,6 @@ Future initApp() async { }; PlatformDispatcher.instance.onError = (error, stack) { - debugPrint("FlutterError - Catch all: $error \n $stack"); log.severe('PlatformDispatcher - Catch all', error, stack); return true; }; From 990d9ba9a8a83a1df51d2ef5e5b03fa0d5ba5918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Wojtaszko?= Date: Tue, 5 Aug 2025 00:24:19 +0200 Subject: [PATCH 157/748] fix: adjust margin and gap for trailing elements in control app bar (#20645) --- web/src/lib/components/shared-components/control-app-bar.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/lib/components/shared-components/control-app-bar.svelte b/web/src/lib/components/shared-components/control-app-bar.svelte index a28dfd45cf..ef4e26e849 100644 --- a/web/src/lib/components/shared-components/control-app-bar.svelte +++ b/web/src/lib/components/shared-components/control-app-bar.svelte @@ -97,7 +97,7 @@ {@render children?.()}
-
+
{@render trailing?.()}
From 750d21aebabc58eaf050bd22fe19d5b9a4f094f2 Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Mon, 4 Aug 2025 17:25:58 -0500 Subject: [PATCH 158/748] fix(mobile): use storageIndicator setting for beta timeline (#20639) * fix: use storageIndicator setting for beta timeline * fix: reactively update the storage indicator icons when setting is changed * Update drift_trash.page.dart * override to bool for storageIndicator --- .../presentation/pages/dev/main_timeline.page.dart | 5 ++--- mobile/lib/presentation/pages/drift_trash.page.dart | 1 - .../lib/presentation/pages/local_timeline.page.dart | 1 - .../widgets/images/thumbnail_tile.widget.dart | 11 ++++++++--- .../presentation/widgets/timeline/timeline.state.dart | 4 ++-- .../widgets/timeline/timeline.widget.dart | 4 ++-- .../asset_list_settings/asset_list_settings.dart | 9 +++++++-- 7 files changed, 21 insertions(+), 14 deletions(-) diff --git a/mobile/lib/presentation/pages/dev/main_timeline.page.dart b/mobile/lib/presentation/pages/dev/main_timeline.page.dart index 0582399eaf..dd227a7635 100644 --- a/mobile/lib/presentation/pages/dev/main_timeline.page.dart +++ b/mobile/lib/presentation/pages/dev/main_timeline.page.dart @@ -16,17 +16,16 @@ class MainTimelinePage extends ConsumerWidget { return memoryLaneProvider.maybeWhen( data: (memories) { return memories.isEmpty - ? const Timeline(showStorageIndicator: true) + ? const Timeline() : Timeline( topSliverWidget: SliverToBoxAdapter( key: Key('memory-lane-${memories.first.assets.first.id}'), child: DriftMemoryLane(memories: memories), ), topSliverWidgetHeight: 200, - showStorageIndicator: true, ); }, - orElse: () => const Timeline(showStorageIndicator: true), + orElse: () => const Timeline(), ); } } diff --git a/mobile/lib/presentation/pages/drift_trash.page.dart b/mobile/lib/presentation/pages/drift_trash.page.dart index 4d18d12d01..43e9217cdf 100644 --- a/mobile/lib/presentation/pages/drift_trash.page.dart +++ b/mobile/lib/presentation/pages/drift_trash.page.dart @@ -28,7 +28,6 @@ class DriftTrashPage extends StatelessWidget { }), ], child: Timeline( - showStorageIndicator: true, appBar: SliverAppBar( title: Text('trash'.t(context: context)), floating: true, diff --git a/mobile/lib/presentation/pages/local_timeline.page.dart b/mobile/lib/presentation/pages/local_timeline.page.dart index 67bc17cb37..c53b18d9e7 100644 --- a/mobile/lib/presentation/pages/local_timeline.page.dart +++ b/mobile/lib/presentation/pages/local_timeline.page.dart @@ -26,7 +26,6 @@ class LocalTimelinePage extends StatelessWidget { child: Timeline( appBar: MesmerizingSliverAppBar(title: album.name), bottomSheet: const LocalAlbumBottomSheet(), - showStorageIndicator: true, ), ); } diff --git a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart index b9ef1ca45a..37743c5e86 100644 --- a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart +++ b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart @@ -2,10 +2,12 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/setting.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/duration_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; class ThumbnailTile extends ConsumerWidget { @@ -13,7 +15,7 @@ class ThumbnailTile extends ConsumerWidget { this.asset, { this.size = const Size.square(256), this.fit = BoxFit.cover, - this.showStorageIndicator = true, + this.showStorageIndicator, this.lockSelection = false, this.heroOffset, super.key, @@ -22,7 +24,7 @@ class ThumbnailTile extends ConsumerWidget { final BaseAsset asset; final Size size; final BoxFit fit; - final bool showStorageIndicator; + final bool? showStorageIndicator; final bool lockSelection; final int? heroOffset; @@ -52,6 +54,9 @@ class ThumbnailTile extends ConsumerWidget { final hasStack = asset is RemoteAsset && (asset as RemoteAsset).stackId != null; + final bool storageIndicator = + showStorageIndicator ?? ref.watch(settingsProvider.select((s) => s.get(Setting.showStorageIndicator))); + return Stack( children: [ AnimatedContainer( @@ -86,7 +91,7 @@ class ThumbnailTile extends ConsumerWidget { child: _VideoIndicator(asset.duration), ), ), - if (showStorageIndicator) + if (storageIndicator) switch (asset.storage) { AssetState.local => const Align( alignment: Alignment.bottomRight, diff --git a/mobile/lib/presentation/widgets/timeline/timeline.state.dart b/mobile/lib/presentation/widgets/timeline/timeline.state.dart index ad3ae3ccf6..da1f7fcc9d 100644 --- a/mobile/lib/presentation/widgets/timeline/timeline.state.dart +++ b/mobile/lib/presentation/widgets/timeline/timeline.state.dart @@ -14,7 +14,7 @@ class TimelineArgs { final double maxHeight; final double spacing; final int columnCount; - final bool showStorageIndicator; + final bool? showStorageIndicator; final bool withStack; final GroupAssetsBy? groupBy; @@ -23,7 +23,7 @@ class TimelineArgs { required this.maxHeight, this.spacing = kTimelineSpacing, this.columnCount = kTimelineColumnCount, - this.showStorageIndicator = false, + this.showStorageIndicator, this.withStack = false, this.groupBy, }); diff --git a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart index b0886f020e..94e13c4e9f 100644 --- a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart @@ -31,7 +31,7 @@ class Timeline extends StatelessWidget { super.key, this.topSliverWidget, this.topSliverWidgetHeight, - this.showStorageIndicator = false, + this.showStorageIndicator, this.withStack = false, this.appBar = const ImmichSliverAppBar(floating: true, pinned: false, snap: false), this.bottomSheet = const GeneralBottomSheet(), @@ -40,7 +40,7 @@ class Timeline extends StatelessWidget { final Widget? topSliverWidget; final double? topSliverWidgetHeight; - final bool showStorageIndicator; + final bool? showStorageIndicator; final Widget? appBar; final Widget? bottomSheet; final bool withStack; diff --git a/mobile/lib/widgets/settings/asset_list_settings/asset_list_settings.dart b/mobile/lib/widgets/settings/asset_list_settings/asset_list_settings.dart index 9f0ed0aa87..907cd19843 100644 --- a/mobile/lib/widgets/settings/asset_list_settings/asset_list_settings.dart +++ b/mobile/lib/widgets/settings/asset_list_settings/asset_list_settings.dart @@ -2,11 +2,13 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; import 'package:immich_mobile/widgets/settings/asset_list_settings/asset_list_group_settings.dart'; import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart'; import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; -import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; + import 'asset_list_layout_settings.dart'; class AssetListSettings extends HookConsumerWidget { @@ -20,7 +22,10 @@ class AssetListSettings extends HookConsumerWidget { SettingsSwitchListTile( valueNotifier: showStorageIndicator, title: 'theme_setting_asset_list_storage_indicator_title'.tr(), - onChanged: (_) => ref.invalidate(appSettingsServiceProvider), + onChanged: (_) { + ref.invalidate(appSettingsServiceProvider); + ref.invalidate(settingsProvider); + }, ), const LayoutSettings(), const GroupSettings(), From 42b78c59b54acd3b6bc624a1fe6957e9aedb13ee Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Mon, 4 Aug 2025 17:34:28 -0500 Subject: [PATCH 159/748] fix(mobile): disable memory lane when memories are disabled (#20642) * fix(mobile): disable memory lane when memories are disabled * Update main_timeline.page.dart * fix: formatting --------- Co-authored-by: Alex --- mobile/lib/presentation/pages/dev/main_timeline.page.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mobile/lib/presentation/pages/dev/main_timeline.page.dart b/mobile/lib/presentation/pages/dev/main_timeline.page.dart index dd227a7635..3764443566 100644 --- a/mobile/lib/presentation/pages/dev/main_timeline.page.dart +++ b/mobile/lib/presentation/pages/dev/main_timeline.page.dart @@ -4,6 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/presentation/widgets/memory/memory_lane.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; import 'package:immich_mobile/providers/infrastructure/memory.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; @RoutePage() class MainTimelinePage extends ConsumerWidget { @@ -12,10 +13,14 @@ class MainTimelinePage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final memoryLaneProvider = ref.watch(driftMemoryFutureProvider); + final memoriesEnabled = ref.watch(currentUserProvider.select((user) => user?.memoryEnabled ?? true)); + + // TODO: the user preferences need to be updated + // from the server to get live hiding/showing of memory lane return memoryLaneProvider.maybeWhen( data: (memories) { - return memories.isEmpty + return memories.isEmpty || !memoriesEnabled ? const Timeline() : Timeline( topSliverWidget: SliverToBoxAdapter( From a91bb399f0f9088fc706bb96a5725d1a386492d3 Mon Sep 17 00:00:00 2001 From: Michael <35783820+mib1185@users.noreply.github.com> Date: Tue, 5 Aug 2025 00:39:05 +0200 Subject: [PATCH 160/748] feat: add server.versionCheck permission (#20555) * add server.versionCheck permission * getVersionCheck is no admin-route --- mobile/openapi/lib/api/server_api.dart | 5 ++++- mobile/openapi/lib/model/permission.dart | 3 +++ open-api/immich-openapi-specs.json | 5 ++++- open-api/typescript-sdk/src/fetch-client.ts | 4 ++++ server/src/controllers/server.controller.ts | 2 +- server/src/enum.ts | 1 + 6 files changed, 17 insertions(+), 3 deletions(-) diff --git a/mobile/openapi/lib/api/server_api.dart b/mobile/openapi/lib/api/server_api.dart index 9e250b83b5..9fa8f2016d 100644 --- a/mobile/openapi/lib/api/server_api.dart +++ b/mobile/openapi/lib/api/server_api.dart @@ -477,7 +477,9 @@ class ServerApi { return null; } - /// Performs an HTTP 'GET /server/version-check' operation and returns the [Response]. + /// This endpoint requires the `server.versionCheck` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getVersionCheckWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/server/version-check'; @@ -503,6 +505,7 @@ class ServerApi { ); } + /// This endpoint requires the `server.versionCheck` permission. Future getVersionCheck() async { final response = await getVersionCheckWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { diff --git a/mobile/openapi/lib/model/permission.dart b/mobile/openapi/lib/model/permission.dart index ec67d81be4..b0903e8f19 100644 --- a/mobile/openapi/lib/model/permission.dart +++ b/mobile/openapi/lib/model/permission.dart @@ -101,6 +101,7 @@ class Permission { static const serverPeriodApkLinks = Permission._(r'server.apkLinks'); static const serverPeriodStorage = Permission._(r'server.storage'); static const serverPeriodStatistics = Permission._(r'server.statistics'); + static const serverPeriodVersionCheck = Permission._(r'server.versionCheck'); static const serverLicensePeriodRead = Permission._(r'serverLicense.read'); static const serverLicensePeriodUpdate = Permission._(r'serverLicense.update'); static const serverLicensePeriodDelete = Permission._(r'serverLicense.delete'); @@ -230,6 +231,7 @@ class Permission { serverPeriodApkLinks, serverPeriodStorage, serverPeriodStatistics, + serverPeriodVersionCheck, serverLicensePeriodRead, serverLicensePeriodUpdate, serverLicensePeriodDelete, @@ -394,6 +396,7 @@ class PermissionTypeTransformer { case r'server.apkLinks': return Permission.serverPeriodApkLinks; case r'server.storage': return Permission.serverPeriodStorage; case r'server.statistics': return Permission.serverPeriodStatistics; + case r'server.versionCheck': return Permission.serverPeriodVersionCheck; case r'serverLicense.read': return Permission.serverLicensePeriodRead; case r'serverLicense.update': return Permission.serverLicensePeriodUpdate; case r'serverLicense.delete': return Permission.serverLicensePeriodDelete; diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index d97585a39e..7d3feb24a3 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -6506,7 +6506,9 @@ ], "tags": [ "Server" - ] + ], + "x-immich-permission": "server.versionCheck", + "description": "This endpoint requires the `server.versionCheck` permission." } }, "/server/version-history": { @@ -12631,6 +12633,7 @@ "server.apkLinks", "server.storage", "server.statistics", + "server.versionCheck", "serverLicense.read", "serverLicense.update", "serverLicense.delete", diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index d26d14aa4a..8b2ed427b4 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -3552,6 +3552,9 @@ export function getServerVersion(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `server.versionCheck` permission. + */ export function getVersionCheck(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -4616,6 +4619,7 @@ export enum Permission { ServerApkLinks = "server.apkLinks", ServerStorage = "server.storage", ServerStatistics = "server.statistics", + ServerVersionCheck = "server.versionCheck", ServerLicenseRead = "serverLicense.read", ServerLicenseUpdate = "serverLicense.update", ServerLicenseDelete = "serverLicense.delete", diff --git a/server/src/controllers/server.controller.ts b/server/src/controllers/server.controller.ts index 9a1004c280..0c184ba302 100644 --- a/server/src/controllers/server.controller.ts +++ b/server/src/controllers/server.controller.ts @@ -109,7 +109,7 @@ export class ServerController { } @Get('version-check') - @Authenticated() + @Authenticated({ permission: Permission.ServerVersionCheck }) getVersionCheck(): Promise { return this.systemMetadataService.getVersionCheckState(); } diff --git a/server/src/enum.ts b/server/src/enum.ts index 93d271f19c..8a6d361d35 100644 --- a/server/src/enum.ts +++ b/server/src/enum.ts @@ -172,6 +172,7 @@ export enum Permission { ServerApkLinks = 'server.apkLinks', ServerStorage = 'server.storage', ServerStatistics = 'server.statistics', + ServerVersionCheck = 'server.versionCheck', ServerLicenseRead = 'serverLicense.read', ServerLicenseUpdate = 'serverLicense.update', From 081307ced2de267f201a61f6dbf5b01afd866f7b Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Mon, 4 Aug 2025 20:35:57 -0500 Subject: [PATCH 161/748] fix: expand sheet when album search is focused (#20651) * fix: expand sheet when album search is focused * convert GeneralBottomSheet to ConsumerStatefulWidget * fix: cleaning up --------- Co-authored-by: Alex --- .../widgets/album/album_selector.widget.dart | 9 +++++- .../general_bottom_sheet.widget.dart | 31 ++++++++++++++++--- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/mobile/lib/presentation/widgets/album/album_selector.widget.dart b/mobile/lib/presentation/widgets/album/album_selector.widget.dart index 47ce167455..a97ca736d1 100644 --- a/mobile/lib/presentation/widgets/album/album_selector.widget.dart +++ b/mobile/lib/presentation/widgets/album/album_selector.widget.dart @@ -27,8 +27,9 @@ typedef AlbumSelectorCallback = void Function(RemoteAlbum album); class AlbumSelector extends ConsumerStatefulWidget { final AlbumSelectorCallback onAlbumSelected; + final Function? onKeyboardExpanded; - const AlbumSelector({super.key, required this.onAlbumSelected}); + const AlbumSelector({super.key, required this.onAlbumSelected, this.onKeyboardExpanded}); @override ConsumerState createState() => _AlbumSelectorState(); @@ -52,6 +53,12 @@ class _AlbumSelectorState extends ConsumerState { searchController.addListener(() { onSearch(searchController.text, filterMode); }); + + searchFocusNode.addListener(() { + if (searchFocusNode.hasFocus) { + widget.onKeyboardExpanded?.call(); + } + }); } void onSearch(String searchTerm, QuickFilterMode sortMode) { diff --git a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart index 8edfcb749e..07b0ea6da8 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart @@ -25,12 +25,30 @@ import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; -class GeneralBottomSheet extends ConsumerWidget { +class GeneralBottomSheet extends ConsumerStatefulWidget { final double? minChildSize; const GeneralBottomSheet({super.key, this.minChildSize}); @override - Widget build(BuildContext context, WidgetRef ref) { + ConsumerState createState() => _GeneralBottomSheetState(); +} + +class _GeneralBottomSheetState extends ConsumerState { + late DraggableScrollableController sheetController; + @override + void initState() { + super.initState(); + sheetController = DraggableScrollableController(); + } + + @override + void dispose() { + sheetController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { final multiselect = ref.watch(multiSelectProvider); final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); @@ -59,9 +77,14 @@ class GeneralBottomSheet extends ConsumerWidget { ref.read(multiSelectProvider.notifier).reset(); } + Future onKeyboardExpand() { + return sheetController.animateTo(0.85, duration: const Duration(milliseconds: 200), curve: Curves.easeInOut); + } + return BaseBottomSheet( + controller: sheetController, initialChildSize: 0.45, - minChildSize: minChildSize, + minChildSize: widget.minChildSize, maxChildSize: 0.85, shouldCloseOnMinExtent: false, actions: [ @@ -90,7 +113,7 @@ class GeneralBottomSheet extends ConsumerWidget { ], slivers: [ const AddToAlbumHeader(), - AlbumSelector(onAlbumSelected: addAssetsToAlbum), + AlbumSelector(onAlbumSelected: addAssetsToAlbum, onKeyboardExpanded: onKeyboardExpand), ], ); } From 3e92e837f127e2c37e6c71acfbd23c9ba93840f4 Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Mon, 4 Aug 2025 20:51:45 -0500 Subject: [PATCH 162/748] feat(mobile): create shared link for albums (#20652) * feat(mobile): create shared link for albums * translation --------- Co-authored-by: Alex --- i18n/en.json | 1 + mobile/lib/presentation/pages/drift_remote_album.page.dart | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/i18n/en.json b/i18n/en.json index a66c3faa9a..d5d02db7d3 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -724,6 +724,7 @@ "create_new_user": "Create new user", "create_shared_album_page_share_add_assets": "ADD ASSETS", "create_shared_album_page_share_select_photos": "Select Photos", + "create_shared_link": "Create shared link", "create_tag": "Create tag", "create_tag_description": "Create a new tag. For nested tags, please enter the full path of the tag including forward slashes.", "create_user": "Create user", diff --git a/mobile/lib/presentation/pages/drift_remote_album.page.dart b/mobile/lib/presentation/pages/drift_remote_album.page.dart index f1f1cfda92..5922841607 100644 --- a/mobile/lib/presentation/pages/drift_remote_album.page.dart +++ b/mobile/lib/presentation/pages/drift_remote_album.page.dart @@ -200,6 +200,10 @@ class _RemoteAlbumPageState extends ConsumerState { context.pop(); await showEditTitleAndDescription(context); }, + onCreateSharedLink: () async { + context.pop(); + context.pushRoute(SharedLinkEditRoute(albumId: _album.id)); + }, ); }, ); From 8e003f95dbee154be42add1937326c6fcf365dc6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 11:44:50 +0100 Subject: [PATCH 163/748] chore(deps): update github/codeql-action action to v3.29.5 (#20656) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/static_analysis.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b10bda81f8..9eae284efd 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -50,7 +50,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3.29.4 + uses: github/codeql-action/init@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3.29.5 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -63,7 +63,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3.29.4 + uses: github/codeql-action/autobuild@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3.29.5 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -76,6 +76,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3.29.4 + uses: github/codeql-action/analyze@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3.29.5 with: category: '/language:${{matrix.language}}' diff --git a/.github/workflows/static_analysis.yml b/.github/workflows/static_analysis.yml index 0f7b0a7a56..45109e767e 100644 --- a/.github/workflows/static_analysis.yml +++ b/.github/workflows/static_analysis.yml @@ -129,7 +129,7 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload SARIF file - uses: github/codeql-action/upload-sarif@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3.29.4 + uses: github/codeql-action/upload-sarif@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3.29.5 with: sarif_file: results.sarif category: zizmor From ae15efdf2a87b64dd6be313b895deaf1c369c743 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 10:52:03 +0000 Subject: [PATCH 164/748] chore(deps): update dependency pigeon to v26 (#20678) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Zack Pollard --- .../src/main/kotlin/app/alextran/immich/sync/Messages.g.kt | 2 +- mobile/ios/Runner/Sync/Messages.g.swift | 2 +- mobile/lib/platform/native_sync_api.g.dart | 2 +- mobile/pubspec.lock | 4 ++-- mobile/pubspec.yaml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt index 201d0a43e1..9c618d9ed0 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.0.0), do not edit directly. // See also: https://pub.dev/packages/pigeon @file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") diff --git a/mobile/ios/Runner/Sync/Messages.g.swift b/mobile/ios/Runner/Sync/Messages.g.swift index b7f4293836..19f4384672 100644 --- a/mobile/ios/Runner/Sync/Messages.g.swift +++ b/mobile/ios/Runner/Sync/Messages.g.swift @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.0.0), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation diff --git a/mobile/lib/platform/native_sync_api.g.dart b/mobile/lib/platform/native_sync_api.g.dart index 3cbd08cd68..6fc96f5046 100644 --- a/mobile/lib/platform/native_sync_api.g.dart +++ b/mobile/lib/platform/native_sync_api.g.dart @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.0.0), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index aa78658006..929180203b 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -1416,10 +1416,10 @@ packages: dependency: "direct dev" description: name: pigeon - sha256: a093af76026160bb5ff6eb98e3e678a301ffd1001ac0d90be558bc133a0c73f5 + sha256: b65acb352dc5a5f8615d074a83419388cbcc249f07c6d8c78b5bc16680a55dda url: "https://pub.dev" source: hosted - version: "25.3.2" + version: "26.0.0" pinput: dependency: "direct main" description: diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 020ce4676e..f9aa0b19a9 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -116,7 +116,7 @@ dev_dependencies: # Drift generator drift_dev: ^2.23.1 # Type safe platform code - pigeon: ^25.3.1 + pigeon: ^26.0.0 flutter: uses-material-design: true From 08fe549ed8c8453484cecfabd5d95135b62bf0e8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 11:54:37 +0100 Subject: [PATCH 165/748] chore(deps): update base-image to v202507291116 (major) (#20668) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- server/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/Dockerfile b/server/Dockerfile index c922c5b291..3d8f3eea74 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -1,5 +1,5 @@ # dev build -FROM ghcr.io/immich-app/base-server-dev:202507162011@sha256:85d4230c2208646bd6c528db41b2213d780b11b7a311397ca6a2aaba7cf697c8 AS dev +FROM ghcr.io/immich-app/base-server-dev:202507291116@sha256:e38543bdd77a02ed156cd9175ed11e9c16dccf48c418d46ecda48ce684de456a AS dev WORKDIR /usr/src/app COPY ./server/package* ./server/ @@ -96,7 +96,7 @@ WORKDIR /usr/src/app/web RUN npm ci && npm run build # prod build -FROM ghcr.io/immich-app/base-server-prod:202507162011@sha256:636f3ddb6106628ef851d51c23f3fa2c6e4829390cc315b27b38c288c82b23a7 +FROM ghcr.io/immich-app/base-server-prod:202507291116@sha256:6e80f884c6e4f05cefe4b4fc4cc06a15bdb6ec9bd7b6e9eadf996a13b69494b6 WORKDIR /usr/src/app ENV NODE_ENV=production \ From 272c8a58122ee27397e5d89ed897bc554dba6987 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 11:56:05 +0100 Subject: [PATCH 166/748] chore(deps): update grafana/grafana docker tag to v12.1.0 (#20661) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- docker/docker-compose.prod.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml index 6ed0aeee8e..ee4fb5745d 100644 --- a/docker/docker-compose.prod.yml +++ b/docker/docker-compose.prod.yml @@ -95,7 +95,7 @@ services: command: ['./run.sh', '-disable-reporting'] ports: - 3000:3000 - image: grafana/grafana:12.0.2-ubuntu@sha256:0512d81cdeaaff0e370a9aa66027b465d1f1f04379c3a9c801a905fabbdbc7a5 + image: grafana/grafana:12.1.0-ubuntu@sha256:397aa30dd1af16cb6c5c9879498e467973a7f87eacf949f6d5a29407a3843809 volumes: - grafana-data:/var/lib/grafana From 2a370087e8f90ac7e0f4a49db433f0f65f0ae875 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 11:56:53 +0100 Subject: [PATCH 167/748] chore(deps): update dependency @types/node to ^22.17.0 (#20657) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- cli/package-lock.json | 10 +++++----- cli/package.json | 2 +- e2e/package-lock.json | 12 ++++++------ e2e/package.json | 2 +- open-api/typescript-sdk/package-lock.json | 8 ++++---- open-api/typescript-sdk/package.json | 2 +- server/package-lock.json | 8 ++++---- server/package.json | 2 +- 8 files changed, 23 insertions(+), 23 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index 1271247865..a131dc5f9e 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -27,7 +27,7 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.16.5", + "@types/node": "^22.17.0", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", @@ -61,7 +61,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.16.5", + "@types/node": "^22.17.0", "typescript": "^5.3.3" } }, @@ -1355,9 +1355,9 @@ } }, "node_modules/@types/node": { - "version": "22.16.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.5.tgz", - "integrity": "sha512-bJFoMATwIGaxxx8VJPeM8TonI8t579oRvgAuT8zFugJsJZgzqv0Fu8Mhp68iecjzG7cnN3mO2dJQ5uUM2EFrgQ==", + "version": "22.17.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.0.tgz", + "integrity": "sha512-bbAKTCqX5aNVryi7qXVMi+OkB3w/OyblodicMbvE38blyAz7GxXf6XYhklokijuPwwVg9sDLKRxt0ZHXQwZVfQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/cli/package.json b/cli/package.json index a5c1b19159..46370e4b37 100644 --- a/cli/package.json +++ b/cli/package.json @@ -21,7 +21,7 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.16.5", + "@types/node": "^22.17.0", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 319f1b34b3..1a66b4fb6b 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -16,7 +16,7 @@ "@playwright/test": "^1.44.1", "@socket.io/component-emitter": "^3.1.2", "@types/luxon": "^3.4.2", - "@types/node": "^22.16.5", + "@types/node": "^22.17.0", "@types/oidc-provider": "^9.0.0", "@types/pg": "^8.15.1", "@types/pngjs": "^6.0.4", @@ -68,7 +68,7 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.16.5", + "@types/node": "^22.17.0", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", @@ -102,7 +102,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.16.5", + "@types/node": "^22.17.0", "typescript": "^5.3.3" } }, @@ -2020,9 +2020,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.16.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.5.tgz", - "integrity": "sha512-bJFoMATwIGaxxx8VJPeM8TonI8t579oRvgAuT8zFugJsJZgzqv0Fu8Mhp68iecjzG7cnN3mO2dJQ5uUM2EFrgQ==", + "version": "22.17.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.0.tgz", + "integrity": "sha512-bbAKTCqX5aNVryi7qXVMi+OkB3w/OyblodicMbvE38blyAz7GxXf6XYhklokijuPwwVg9sDLKRxt0ZHXQwZVfQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/e2e/package.json b/e2e/package.json index 8b8540e9ba..ec74aaf36b 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -26,7 +26,7 @@ "@playwright/test": "^1.44.1", "@socket.io/component-emitter": "^3.1.2", "@types/luxon": "^3.4.2", - "@types/node": "^22.16.5", + "@types/node": "^22.17.0", "@types/oidc-provider": "^9.0.0", "@types/pg": "^8.15.1", "@types/pngjs": "^6.0.4", diff --git a/open-api/typescript-sdk/package-lock.json b/open-api/typescript-sdk/package-lock.json index a9b5c295ca..f894921f73 100644 --- a/open-api/typescript-sdk/package-lock.json +++ b/open-api/typescript-sdk/package-lock.json @@ -12,7 +12,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.16.5", + "@types/node": "^22.17.0", "typescript": "^5.3.3" } }, @@ -23,9 +23,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.16.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.5.tgz", - "integrity": "sha512-bJFoMATwIGaxxx8VJPeM8TonI8t579oRvgAuT8zFugJsJZgzqv0Fu8Mhp68iecjzG7cnN3mO2dJQ5uUM2EFrgQ==", + "version": "22.17.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.0.tgz", + "integrity": "sha512-bbAKTCqX5aNVryi7qXVMi+OkB3w/OyblodicMbvE38blyAz7GxXf6XYhklokijuPwwVg9sDLKRxt0ZHXQwZVfQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index f01ec302ce..8528976a1e 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -19,7 +19,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.16.5", + "@types/node": "^22.17.0", "typescript": "^5.3.3" }, "repository": { diff --git a/server/package-lock.json b/server/package-lock.json index ed2dc2e14f..8894c6a6aa 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -109,7 +109,7 @@ "@types/luxon": "^3.6.2", "@types/mock-fs": "^4.13.1", "@types/multer": "^2.0.0", - "@types/node": "^22.16.5", + "@types/node": "^22.17.0", "@types/nodemailer": "^6.4.14", "@types/picomatch": "^4.0.0", "@types/pngjs": "^6.0.5", @@ -7083,9 +7083,9 @@ } }, "node_modules/@types/node": { - "version": "22.16.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.5.tgz", - "integrity": "sha512-bJFoMATwIGaxxx8VJPeM8TonI8t579oRvgAuT8zFugJsJZgzqv0Fu8Mhp68iecjzG7cnN3mO2dJQ5uUM2EFrgQ==", + "version": "22.17.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.0.tgz", + "integrity": "sha512-bbAKTCqX5aNVryi7qXVMi+OkB3w/OyblodicMbvE38blyAz7GxXf6XYhklokijuPwwVg9sDLKRxt0ZHXQwZVfQ==", "license": "MIT", "dependencies": { "undici-types": "~6.21.0" diff --git a/server/package.json b/server/package.json index 472b746630..05c21e6bfc 100644 --- a/server/package.json +++ b/server/package.json @@ -134,7 +134,7 @@ "@types/luxon": "^3.6.2", "@types/mock-fs": "^4.13.1", "@types/multer": "^2.0.0", - "@types/node": "^22.16.5", + "@types/node": "^22.17.0", "@types/nodemailer": "^6.4.14", "@types/picomatch": "^4.0.0", "@types/pngjs": "^6.0.5", From eb2f4c866e6a475fff06de2880b302d7a141095e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 10:58:13 +0000 Subject: [PATCH 168/748] chore(deps): update dependency eslint-plugin-unicorn to v60 (#20677) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Zack Pollard --- cli/package-lock.json | 138 ++++++++++---------------- cli/package.json | 2 +- e2e/package-lock.json | 134 ++++++++++--------------- e2e/package.json | 2 +- server/package-lock.json | 105 ++++++++------------ server/package.json | 2 +- server/src/services/backup.service.ts | 2 +- web/eslint.config.js | 1 + web/package-lock.json | 130 ++++++++++++------------ web/package.json | 2 +- 10 files changed, 215 insertions(+), 303 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index a131dc5f9e..abed77e785 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -35,7 +35,7 @@ "eslint": "^9.14.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-unicorn": "^59.0.0", + "eslint-plugin-unicorn": "^60.0.0", "globals": "^16.0.0", "mock-fs": "^5.2.0", "prettier": "^3.2.5", @@ -90,9 +90,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", "dev": true, "license": "MIT", "engines": { @@ -632,9 +632,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", - "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -705,13 +705,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz", - "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", + "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.14.0", + "@eslint/core": "^0.15.1", "levn": "^0.4.1" }, "engines": { @@ -1897,9 +1897,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", "dev": true, "funding": [ { @@ -1917,10 +1917,10 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", + "caniuse-lite": "^1.0.30001726", + "electron-to-chromium": "^1.5.173", "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" + "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" @@ -1981,9 +1981,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001713", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001713.tgz", - "integrity": "sha512-wCIWIg+A4Xr7NfhTuHdX+/FKh3+Op3LBbSp2N5Pfx6T/LhdQy3GTyoTg48BReaW/MyMNZAkTadsBtai3ldWK0Q==", + "version": "1.0.30001731", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001731.tgz", + "integrity": "sha512-lDdp2/wrOmTRWuoB5DpfNkC0rJDU8DqRa6nYL6HK6sytw70QMopt/NIc/9SM7ylItlBWfACXk0tEn37UWM/+mg==", "dev": true, "funding": [ { @@ -2035,6 +2035,13 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/change-case": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", + "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", + "dev": true, + "license": "MIT" + }, "node_modules/check-error": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", @@ -2061,9 +2068,9 @@ } }, "node_modules/ci-info": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.2.0.tgz", - "integrity": "sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.0.tgz", + "integrity": "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==", "dev": true, "funding": [ { @@ -2150,13 +2157,13 @@ "license": "MIT" }, "node_modules/core-js-compat": { - "version": "3.41.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.41.0.tgz", - "integrity": "sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A==", + "version": "3.45.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.45.0.tgz", + "integrity": "sha512-gRoVMBawZg0OnxaVv3zpqLLxaHmsubEGyTnqdpI/CEBvX4JadI1dMSHxagThprYRtSVbuQxvi6iUatdPxohHpA==", "dev": true, "license": "MIT", "dependencies": { - "browserslist": "^4.24.4" + "browserslist": "^4.25.1" }, "funding": { "type": "opencollective", @@ -2221,9 +2228,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.137", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.137.tgz", - "integrity": "sha512-/QSJaU2JyIuTbbABAo/crOs+SuAZLS+fVVS10PVrIT9hrRkmZl8Hb0xPSkKRUUWHQtYzXHpQUW3Dy5hwMzGZkA==", + "version": "1.5.195", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.195.tgz", + "integrity": "sha512-URclP0iIaDUzqcAyV1v2PgduJ9N0IdXmWsnPzPfelvBmjmZzEy6xJcjb1cXj+TbYqXgtLrjHEoaSIdTYhw4ezg==", "dev": true, "license": "ISC" }, @@ -2414,65 +2421,39 @@ } }, "node_modules/eslint-plugin-unicorn": { - "version": "59.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-59.0.1.tgz", - "integrity": "sha512-EtNXYuWPUmkgSU2E7Ttn57LbRREQesIP1BiLn7OZLKodopKfDXfBUkC/0j6mpw2JExwf43Uf3qLSvrSvppgy8Q==", + "version": "60.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-60.0.0.tgz", + "integrity": "sha512-QUzTefvP8stfSXsqKQ+vBQSEsXIlAiCduS/V1Em+FKgL9c21U/IIm20/e3MFy1jyCf14tHAhqC1sX8OTy6VUCg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "@eslint-community/eslint-utils": "^4.5.1", - "@eslint/plugin-kit": "^0.2.7", - "ci-info": "^4.2.0", + "@babel/helper-validator-identifier": "^7.27.1", + "@eslint-community/eslint-utils": "^4.7.0", + "@eslint/plugin-kit": "^0.3.3", + "change-case": "^5.4.4", + "ci-info": "^4.3.0", "clean-regexp": "^1.0.0", - "core-js-compat": "^3.41.0", + "core-js-compat": "^3.44.0", "esquery": "^1.6.0", "find-up-simple": "^1.0.1", - "globals": "^16.0.0", + "globals": "^16.3.0", "indent-string": "^5.0.0", "is-builtin-module": "^5.0.0", "jsesc": "^3.1.0", "pluralize": "^8.0.0", "regexp-tree": "^0.1.27", "regjsparser": "^0.12.0", - "semver": "^7.7.1", + "semver": "^7.7.2", "strip-indent": "^4.0.0" }, "engines": { - "node": "^18.20.0 || ^20.10.0 || >=21.0.0" + "node": "^20.10.0 || >=21.0.0" }, "funding": { "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" }, "peerDependencies": { - "eslint": ">=9.22.0" - } - }, - "node_modules/eslint-plugin-unicorn/node_modules/@eslint/core": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", - "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/eslint-plugin-unicorn/node_modules/@eslint/plugin-kit": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", - "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.13.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "eslint": ">=9.29.0" } }, "node_modules/eslint-scope": { @@ -2505,19 +2486,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/@eslint/core": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", - "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/espree": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", @@ -3727,9 +3695,9 @@ } }, "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, "license": "ISC", "bin": { diff --git a/cli/package.json b/cli/package.json index 46370e4b37..6444fc915b 100644 --- a/cli/package.json +++ b/cli/package.json @@ -29,7 +29,7 @@ "eslint": "^9.14.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-unicorn": "^59.0.0", + "eslint-plugin-unicorn": "^60.0.0", "globals": "^16.0.0", "mock-fs": "^5.2.0", "prettier": "^3.2.5", diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 1a66b4fb6b..bd05eaaf62 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -25,7 +25,7 @@ "eslint": "^9.14.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-unicorn": "^59.0.0", + "eslint-plugin-unicorn": "^60.0.0", "exiftool-vendored": "^28.3.1", "globals": "^16.0.0", "jose": "^5.6.3", @@ -76,7 +76,7 @@ "eslint": "^9.14.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-unicorn": "^59.0.0", + "eslint-plugin-unicorn": "^60.0.0", "globals": "^16.0.0", "mock-fs": "^5.2.0", "prettier": "^3.2.5", @@ -131,9 +131,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", "dev": true, "license": "MIT", "engines": { @@ -684,9 +684,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", - "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -757,13 +757,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz", - "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", + "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.14.0", + "@eslint/core": "^0.15.1", "levn": "^0.4.1" }, "engines": { @@ -2741,9 +2741,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", "dev": true, "funding": [ { @@ -2761,10 +2761,10 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", + "caniuse-lite": "^1.0.30001726", + "electron-to-chromium": "^1.5.173", "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" + "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" @@ -2862,9 +2862,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001713", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001713.tgz", - "integrity": "sha512-wCIWIg+A4Xr7NfhTuHdX+/FKh3+Op3LBbSp2N5Pfx6T/LhdQy3GTyoTg48BReaW/MyMNZAkTadsBtai3ldWK0Q==", + "version": "1.0.30001731", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001731.tgz", + "integrity": "sha512-lDdp2/wrOmTRWuoB5DpfNkC0rJDU8DqRa6nYL6HK6sytw70QMopt/NIc/9SM7ylItlBWfACXk0tEn37UWM/+mg==", "dev": true, "funding": [ { @@ -2916,6 +2916,13 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/change-case": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", + "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", + "dev": true, + "license": "MIT" + }, "node_modules/check-error": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", @@ -2937,9 +2944,9 @@ } }, "node_modules/ci-info": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.2.0.tgz", - "integrity": "sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.0.tgz", + "integrity": "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==", "dev": true, "funding": [ { @@ -3112,13 +3119,13 @@ } }, "node_modules/core-js-compat": { - "version": "3.41.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.41.0.tgz", - "integrity": "sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A==", + "version": "3.45.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.45.0.tgz", + "integrity": "sha512-gRoVMBawZg0OnxaVv3zpqLLxaHmsubEGyTnqdpI/CEBvX4JadI1dMSHxagThprYRtSVbuQxvi6iUatdPxohHpA==", "dev": true, "license": "MIT", "dependencies": { - "browserslist": "^4.24.4" + "browserslist": "^4.25.1" }, "funding": { "type": "opencollective", @@ -3271,9 +3278,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.137", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.137.tgz", - "integrity": "sha512-/QSJaU2JyIuTbbABAo/crOs+SuAZLS+fVVS10PVrIT9hrRkmZl8Hb0xPSkKRUUWHQtYzXHpQUW3Dy5hwMzGZkA==", + "version": "1.5.195", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.195.tgz", + "integrity": "sha512-URclP0iIaDUzqcAyV1v2PgduJ9N0IdXmWsnPzPfelvBmjmZzEy6xJcjb1cXj+TbYqXgtLrjHEoaSIdTYhw4ezg==", "dev": true, "license": "ISC" }, @@ -3572,65 +3579,39 @@ } }, "node_modules/eslint-plugin-unicorn": { - "version": "59.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-59.0.1.tgz", - "integrity": "sha512-EtNXYuWPUmkgSU2E7Ttn57LbRREQesIP1BiLn7OZLKodopKfDXfBUkC/0j6mpw2JExwf43Uf3qLSvrSvppgy8Q==", + "version": "60.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-60.0.0.tgz", + "integrity": "sha512-QUzTefvP8stfSXsqKQ+vBQSEsXIlAiCduS/V1Em+FKgL9c21U/IIm20/e3MFy1jyCf14tHAhqC1sX8OTy6VUCg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "@eslint-community/eslint-utils": "^4.5.1", - "@eslint/plugin-kit": "^0.2.7", - "ci-info": "^4.2.0", + "@babel/helper-validator-identifier": "^7.27.1", + "@eslint-community/eslint-utils": "^4.7.0", + "@eslint/plugin-kit": "^0.3.3", + "change-case": "^5.4.4", + "ci-info": "^4.3.0", "clean-regexp": "^1.0.0", - "core-js-compat": "^3.41.0", + "core-js-compat": "^3.44.0", "esquery": "^1.6.0", "find-up-simple": "^1.0.1", - "globals": "^16.0.0", + "globals": "^16.3.0", "indent-string": "^5.0.0", "is-builtin-module": "^5.0.0", "jsesc": "^3.1.0", "pluralize": "^8.0.0", "regexp-tree": "^0.1.27", "regjsparser": "^0.12.0", - "semver": "^7.7.1", + "semver": "^7.7.2", "strip-indent": "^4.0.0" }, "engines": { - "node": "^18.20.0 || ^20.10.0 || >=21.0.0" + "node": "^20.10.0 || >=21.0.0" }, "funding": { "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" }, "peerDependencies": { - "eslint": ">=9.22.0" - } - }, - "node_modules/eslint-plugin-unicorn/node_modules/@eslint/core": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", - "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/eslint-plugin-unicorn/node_modules/@eslint/plugin-kit": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", - "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.13.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "eslint": ">=9.29.0" } }, "node_modules/eslint-scope": { @@ -3663,19 +3644,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/@eslint/core": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", - "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/espree": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", diff --git a/e2e/package.json b/e2e/package.json index ec74aaf36b..382aea455b 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -35,7 +35,7 @@ "eslint": "^9.14.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-unicorn": "^59.0.0", + "eslint-plugin-unicorn": "^60.0.0", "exiftool-vendored": "^28.3.1", "globals": "^16.0.0", "jose": "^5.6.3", diff --git a/server/package-lock.json b/server/package-lock.json index 8894c6a6aa..e101019f5d 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -124,7 +124,7 @@ "eslint": "^9.14.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-unicorn": "^59.0.0", + "eslint-plugin-unicorn": "^60.0.0", "globals": "^16.0.0", "mock-fs": "^5.2.0", "node-addon-api": "^8.3.1", @@ -1117,13 +1117,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.2.tgz", - "integrity": "sha512-4SaFZCNfJqvk/kenHpI8xvN42DMaoycy4PzKc5otHxRswww1kAt82OlBuwRVLofCACCTZEcla2Ydxv8scMXaTg==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", + "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.0", + "@eslint/core": "^0.15.1", "levn": "^0.4.1" }, "engines": { @@ -8636,9 +8636,9 @@ } }, "node_modules/browserslist": { - "version": "4.25.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", - "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", "dev": true, "funding": [ { @@ -8656,8 +8656,8 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001718", - "electron-to-chromium": "^1.5.160", + "caniuse-lite": "^1.0.30001726", + "electron-to-chromium": "^1.5.173", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, @@ -8937,9 +8937,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001724", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001724.tgz", - "integrity": "sha512-WqJo7p0TbHDOythNTqYujmaJTvtYRZrjpP8TCvH6Vb9CYJerJNKamKzIWOM4BkQatWj9H2lYulpdAQNBe7QhNA==", + "version": "1.0.30001731", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001731.tgz", + "integrity": "sha512-lDdp2/wrOmTRWuoB5DpfNkC0rJDU8DqRa6nYL6HK6sytw70QMopt/NIc/9SM7ylItlBWfACXk0tEn37UWM/+mg==", "dev": true, "funding": [ { @@ -9012,6 +9012,13 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/change-case": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", + "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", + "dev": true, + "license": "MIT" + }, "node_modules/chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", @@ -9064,9 +9071,9 @@ } }, "node_modules/ci-info": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.2.0.tgz", - "integrity": "sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.0.tgz", + "integrity": "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==", "dev": true, "funding": [ { @@ -9547,13 +9554,13 @@ "license": "MIT" }, "node_modules/core-js-compat": { - "version": "3.43.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.43.0.tgz", - "integrity": "sha512-2GML2ZsCc5LR7hZYz4AXmjQw8zuy2T//2QntwdnpuYI7jteT6GVYJL7F6C2C57R7gSYrcqVW3lAALefdbhBLDA==", + "version": "3.45.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.45.0.tgz", + "integrity": "sha512-gRoVMBawZg0OnxaVv3zpqLLxaHmsubEGyTnqdpI/CEBvX4JadI1dMSHxagThprYRtSVbuQxvi6iUatdPxohHpA==", "dev": true, "license": "MIT", "dependencies": { - "browserslist": "^4.25.0" + "browserslist": "^4.25.1" }, "funding": { "type": "opencollective", @@ -10147,9 +10154,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.171", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.171.tgz", - "integrity": "sha512-scWpzXEJEMrGJa4Y6m/tVotb0WuvNmasv3wWVzUAeCgKU0ToFOhUW6Z+xWnRQANMYGxN4ngJXIThgBJOqzVPCQ==", + "version": "1.5.195", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.195.tgz", + "integrity": "sha512-URclP0iIaDUzqcAyV1v2PgduJ9N0IdXmWsnPzPfelvBmjmZzEy6xJcjb1cXj+TbYqXgtLrjHEoaSIdTYhw4ezg==", "dev": true, "license": "ISC" }, @@ -10587,65 +10594,39 @@ } }, "node_modules/eslint-plugin-unicorn": { - "version": "59.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-59.0.1.tgz", - "integrity": "sha512-EtNXYuWPUmkgSU2E7Ttn57LbRREQesIP1BiLn7OZLKodopKfDXfBUkC/0j6mpw2JExwf43Uf3qLSvrSvppgy8Q==", + "version": "60.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-60.0.0.tgz", + "integrity": "sha512-QUzTefvP8stfSXsqKQ+vBQSEsXIlAiCduS/V1Em+FKgL9c21U/IIm20/e3MFy1jyCf14tHAhqC1sX8OTy6VUCg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "@eslint-community/eslint-utils": "^4.5.1", - "@eslint/plugin-kit": "^0.2.7", - "ci-info": "^4.2.0", + "@babel/helper-validator-identifier": "^7.27.1", + "@eslint-community/eslint-utils": "^4.7.0", + "@eslint/plugin-kit": "^0.3.3", + "change-case": "^5.4.4", + "ci-info": "^4.3.0", "clean-regexp": "^1.0.0", - "core-js-compat": "^3.41.0", + "core-js-compat": "^3.44.0", "esquery": "^1.6.0", "find-up-simple": "^1.0.1", - "globals": "^16.0.0", + "globals": "^16.3.0", "indent-string": "^5.0.0", "is-builtin-module": "^5.0.0", "jsesc": "^3.1.0", "pluralize": "^8.0.0", "regexp-tree": "^0.1.27", "regjsparser": "^0.12.0", - "semver": "^7.7.1", + "semver": "^7.7.2", "strip-indent": "^4.0.0" }, "engines": { - "node": "^18.20.0 || ^20.10.0 || >=21.0.0" + "node": "^20.10.0 || >=21.0.0" }, "funding": { "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" }, "peerDependencies": { - "eslint": ">=9.22.0" - } - }, - "node_modules/eslint-plugin-unicorn/node_modules/@eslint/core": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", - "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/eslint-plugin-unicorn/node_modules/@eslint/plugin-kit": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", - "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.13.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "eslint": ">=9.29.0" } }, "node_modules/eslint-scope": { diff --git a/server/package.json b/server/package.json index 05c21e6bfc..503fba78e5 100644 --- a/server/package.json +++ b/server/package.json @@ -149,7 +149,7 @@ "eslint": "^9.14.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-unicorn": "^59.0.0", + "eslint-plugin-unicorn": "^60.0.0", "globals": "^16.0.0", "mock-fs": "^5.2.0", "node-addon-api": "^8.3.1", diff --git a/server/src/services/backup.service.ts b/server/src/services/backup.service.ts index 0948965f1c..c1cb94b64c 100644 --- a/server/src/services/backup.service.ts +++ b/server/src/services/backup.service.ts @@ -62,7 +62,7 @@ export class BackupService extends BaseService { return oldBackupStyle || newBackupStyle; }) .sort() - .reverse(); + .toReversed(); const toDelete = backups.slice(config.keepLastAmount); toDelete.push(...failedBackups); diff --git a/web/eslint.config.js b/web/eslint.config.js index 6b7b343ad1..e15f80e8e0 100644 --- a/web/eslint.config.js +++ b/web/eslint.config.js @@ -109,6 +109,7 @@ export default typescriptEslint.config( ], curly: 2, + 'unicorn/no-array-reverse': 'off', // toReversed() is not supported in Chrome 109 or Safari 15.4 'unicorn/no-useless-undefined': 'off', 'unicorn/prefer-spread': 'off', 'unicorn/no-null': 'off', diff --git a/web/package-lock.json b/web/package-lock.json index cfd65b63d8..18ca7c7d82 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -72,7 +72,7 @@ "eslint-p": "^0.25.0", "eslint-plugin-compat": "^6.0.2", "eslint-plugin-svelte": "^3.9.0", - "eslint-plugin-unicorn": "^59.0.0", + "eslint-plugin-unicorn": "^60.0.0", "factory.ts": "^1.4.1", "globals": "^16.0.0", "happy-dom": "^18.0.1", @@ -150,9 +150,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", "dev": true, "license": "MIT", "engines": { @@ -783,19 +783,32 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz", - "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", + "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.14.0", + "@eslint/core": "^0.15.1", "levn": "^0.4.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@faker-js/faker": { "version": "9.9.0", "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.9.0.tgz", @@ -3788,9 +3801,9 @@ } }, "node_modules/browserslist": { - "version": "4.25.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", - "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", "dev": true, "funding": [ { @@ -3808,8 +3821,8 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001718", - "electron-to-chromium": "^1.5.160", + "caniuse-lite": "^1.0.30001726", + "electron-to-chromium": "^1.5.173", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, @@ -3884,9 +3897,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001723", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001723.tgz", - "integrity": "sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==", + "version": "1.0.30001731", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001731.tgz", + "integrity": "sha512-lDdp2/wrOmTRWuoB5DpfNkC0rJDU8DqRa6nYL6HK6sytw70QMopt/NIc/9SM7ylItlBWfACXk0tEn37UWM/+mg==", "dev": true, "funding": [ { @@ -3954,6 +3967,13 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/change-case": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", + "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", + "dev": true, + "license": "MIT" + }, "node_modules/check-error": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", @@ -3991,9 +4011,9 @@ } }, "node_modules/ci-info": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.2.0.tgz", - "integrity": "sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.0.tgz", + "integrity": "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==", "dev": true, "funding": [ { @@ -4171,13 +4191,13 @@ } }, "node_modules/core-js-compat": { - "version": "3.41.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.41.0.tgz", - "integrity": "sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A==", + "version": "3.45.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.45.0.tgz", + "integrity": "sha512-gRoVMBawZg0OnxaVv3zpqLLxaHmsubEGyTnqdpI/CEBvX4JadI1dMSHxagThprYRtSVbuQxvi6iUatdPxohHpA==", "dev": true, "license": "MIT", "dependencies": { - "browserslist": "^4.24.4" + "browserslist": "^4.25.1" }, "funding": { "type": "opencollective", @@ -4506,9 +4526,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.167", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.167.tgz", - "integrity": "sha512-LxcRvnYO5ez2bMOFpbuuVuAI5QNeY1ncVytE/KXaL6ZNfzX1yPlAO0nSOyIHx2fVAuUprMqPs/TdVhUFZy7SIQ==", + "version": "1.5.195", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.195.tgz", + "integrity": "sha512-URclP0iIaDUzqcAyV1v2PgduJ9N0IdXmWsnPzPfelvBmjmZzEy6xJcjb1cXj+TbYqXgtLrjHEoaSIdTYhw4ezg==", "dev": true, "license": "ISC" }, @@ -5034,65 +5054,39 @@ } }, "node_modules/eslint-plugin-unicorn": { - "version": "59.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-59.0.1.tgz", - "integrity": "sha512-EtNXYuWPUmkgSU2E7Ttn57LbRREQesIP1BiLn7OZLKodopKfDXfBUkC/0j6mpw2JExwf43Uf3qLSvrSvppgy8Q==", + "version": "60.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-60.0.0.tgz", + "integrity": "sha512-QUzTefvP8stfSXsqKQ+vBQSEsXIlAiCduS/V1Em+FKgL9c21U/IIm20/e3MFy1jyCf14tHAhqC1sX8OTy6VUCg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "@eslint-community/eslint-utils": "^4.5.1", - "@eslint/plugin-kit": "^0.2.7", - "ci-info": "^4.2.0", + "@babel/helper-validator-identifier": "^7.27.1", + "@eslint-community/eslint-utils": "^4.7.0", + "@eslint/plugin-kit": "^0.3.3", + "change-case": "^5.4.4", + "ci-info": "^4.3.0", "clean-regexp": "^1.0.0", - "core-js-compat": "^3.41.0", + "core-js-compat": "^3.44.0", "esquery": "^1.6.0", "find-up-simple": "^1.0.1", - "globals": "^16.0.0", + "globals": "^16.3.0", "indent-string": "^5.0.0", "is-builtin-module": "^5.0.0", "jsesc": "^3.1.0", "pluralize": "^8.0.0", "regexp-tree": "^0.1.27", "regjsparser": "^0.12.0", - "semver": "^7.7.1", + "semver": "^7.7.2", "strip-indent": "^4.0.0" }, "engines": { - "node": "^18.20.0 || ^20.10.0 || >=21.0.0" + "node": "^20.10.0 || >=21.0.0" }, "funding": { "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" }, "peerDependencies": { - "eslint": ">=9.22.0" - } - }, - "node_modules/eslint-plugin-unicorn/node_modules/@eslint/core": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", - "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/eslint-plugin-unicorn/node_modules/@eslint/plugin-kit": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", - "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.13.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "eslint": ">=9.29.0" } }, "node_modules/eslint-scope": { @@ -8437,9 +8431,9 @@ } }, "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "devOptional": true, "license": "ISC", "bin": { diff --git a/web/package.json b/web/package.json index bb17955b08..bae11de984 100644 --- a/web/package.json +++ b/web/package.json @@ -89,7 +89,7 @@ "eslint-p": "^0.25.0", "eslint-plugin-compat": "^6.0.2", "eslint-plugin-svelte": "^3.9.0", - "eslint-plugin-unicorn": "^59.0.0", + "eslint-plugin-unicorn": "^60.0.0", "factory.ts": "^1.4.1", "globals": "^16.0.0", "happy-dom": "^18.0.1", From 5c76cc34e1284f051cd0e119df17cb87bd76b941 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 11:01:15 +0000 Subject: [PATCH 169/748] chore(deps): update node.js to v22.18.0 (#20662) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/.nvmrc | 2 +- cli/.nvmrc | 2 +- cli/package.json | 2 +- docs/.nvmrc | 2 +- docs/package.json | 2 +- e2e/.nvmrc | 2 +- e2e/package.json | 2 +- open-api/typescript-sdk/.nvmrc | 2 +- open-api/typescript-sdk/package.json | 2 +- server/.nvmrc | 2 +- server/package.json | 2 +- web/.nvmrc | 2 +- web/package.json | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/.nvmrc b/.github/.nvmrc index 7377d130ed..91d5f6ff8e 100644 --- a/.github/.nvmrc +++ b/.github/.nvmrc @@ -1 +1 @@ -22.17.1 +22.18.0 diff --git a/cli/.nvmrc b/cli/.nvmrc index 7377d130ed..91d5f6ff8e 100644 --- a/cli/.nvmrc +++ b/cli/.nvmrc @@ -1 +1 @@ -22.17.1 +22.18.0 diff --git a/cli/package.json b/cli/package.json index 6444fc915b..19f5c1efb5 100644 --- a/cli/package.json +++ b/cli/package.json @@ -69,6 +69,6 @@ "micromatch": "^4.0.8" }, "volta": { - "node": "22.17.1" + "node": "22.18.0" } } diff --git a/docs/.nvmrc b/docs/.nvmrc index 7377d130ed..91d5f6ff8e 100644 --- a/docs/.nvmrc +++ b/docs/.nvmrc @@ -1 +1 @@ -22.17.1 +22.18.0 diff --git a/docs/package.json b/docs/package.json index 97072d8eb5..9e4ab5504f 100644 --- a/docs/package.json +++ b/docs/package.json @@ -59,6 +59,6 @@ "node": ">=20" }, "volta": { - "node": "22.17.1" + "node": "22.18.0" } } diff --git a/e2e/.nvmrc b/e2e/.nvmrc index 7377d130ed..91d5f6ff8e 100644 --- a/e2e/.nvmrc +++ b/e2e/.nvmrc @@ -1 +1 @@ -22.17.1 +22.18.0 diff --git a/e2e/package.json b/e2e/package.json index 382aea455b..d48543e367 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -54,6 +54,6 @@ "vitest": "^3.0.0" }, "volta": { - "node": "22.17.1" + "node": "22.18.0" } } diff --git a/open-api/typescript-sdk/.nvmrc b/open-api/typescript-sdk/.nvmrc index 7377d130ed..91d5f6ff8e 100644 --- a/open-api/typescript-sdk/.nvmrc +++ b/open-api/typescript-sdk/.nvmrc @@ -1 +1 @@ -22.17.1 +22.18.0 diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index 8528976a1e..47acc13489 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -28,6 +28,6 @@ "directory": "open-api/typescript-sdk" }, "volta": { - "node": "22.17.1" + "node": "22.18.0" } } diff --git a/server/.nvmrc b/server/.nvmrc index 7377d130ed..91d5f6ff8e 100644 --- a/server/.nvmrc +++ b/server/.nvmrc @@ -1 +1 @@ -22.17.1 +22.18.0 diff --git a/server/package.json b/server/package.json index 503fba78e5..60f224f950 100644 --- a/server/package.json +++ b/server/package.json @@ -172,7 +172,7 @@ "vitest": "^3.0.0" }, "volta": { - "node": "22.17.1" + "node": "22.18.0" }, "overrides": { "sharp": "^0.34.2" diff --git a/web/.nvmrc b/web/.nvmrc index 7377d130ed..91d5f6ff8e 100644 --- a/web/.nvmrc +++ b/web/.nvmrc @@ -1 +1 @@ -22.17.1 +22.18.0 diff --git a/web/package.json b/web/package.json index bae11de984..21216547d0 100644 --- a/web/package.json +++ b/web/package.json @@ -109,6 +109,6 @@ "vitest": "^3.0.0" }, "volta": { - "node": "22.17.1" + "node": "22.18.0" } } From ce2ea989265e878eedf4aaf1e1b73802b1c53c21 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 12:45:47 +0000 Subject: [PATCH 170/748] fix(deps): update typescript-projects (#20396) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Zack Pollard --- cli/package-lock.json | 24 +- e2e/package-lock.json | 34 +- server/package-lock.json | 905 +++++++++++++++------------------------ web/package-lock.json | 66 ++- web/package.json | 2 +- 5 files changed, 392 insertions(+), 639 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index abed77e785..7af1dee139 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -682,9 +682,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", - "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz", + "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==", "dev": true, "license": "MIT", "engines": { @@ -2313,9 +2313,9 @@ } }, "node_modules/eslint": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", - "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz", + "integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==", "dev": true, "license": "MIT", "dependencies": { @@ -2325,8 +2325,8 @@ "@eslint/config-helpers": "^0.3.0", "@eslint/core": "^0.15.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.31.0", - "@eslint/plugin-kit": "^0.3.1", + "@eslint/js": "9.32.0", + "@eslint/plugin-kit": "^0.3.4", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -4179,15 +4179,15 @@ } }, "node_modules/vite": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.5.tgz", - "integrity": "sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.6.tgz", + "integrity": "sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.6", - "picomatch": "^4.0.2", + "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.40.0", "tinyglobby": "^0.2.14" diff --git a/e2e/package-lock.json b/e2e/package-lock.json index bd05eaaf62..493997c1b6 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -734,9 +734,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", - "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz", + "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==", "dev": true, "license": "MIT", "engines": { @@ -1999,9 +1999,9 @@ } }, "node_modules/@types/luxon": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.6.2.tgz", - "integrity": "sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.7.1.tgz", + "integrity": "sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg==", "dev": true, "license": "MIT" }, @@ -2030,9 +2030,9 @@ } }, "node_modules/@types/oidc-provider": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@types/oidc-provider/-/oidc-provider-9.1.1.tgz", - "integrity": "sha512-sG4UcE4AbUwAsEpyrcyoqZ383wJiQObZU+gTa1Iv288+l09HwSr88hBZE2IBLlXS+RKmLId0i4B430PBFO/XRA==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@types/oidc-provider/-/oidc-provider-9.1.2.tgz", + "integrity": "sha512-JAreXkbWsZR72Gt3eigG652wq1qBcjhuy421PXU2a8PS0mM00XlG+UdXbM/QPihM3ko0YF8cwvt0H2kacXGcsg==", "dev": true, "license": "MIT", "dependencies": { @@ -2042,9 +2042,9 @@ } }, "node_modules/@types/pg": { - "version": "8.15.4", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.4.tgz", - "integrity": "sha512-I6UNVBAoYbvuWkkU3oosC8yxqH21f4/Jc4DK71JLG3dT2mdlGe1z+ep/LQGXaKaOgcvUrsQoPRqfgtMcvZiJhg==", + "version": "8.15.5", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.5.tgz", + "integrity": "sha512-LF7lF6zWEKxuT3/OR8wAZGzkg4ENGXFNyiV/JeOt9z5B+0ZVwbql9McqX5c/WStFq1GaGso7H1AzP/qSzmlCKQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3471,9 +3471,9 @@ } }, "node_modules/eslint": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", - "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz", + "integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==", "dev": true, "license": "MIT", "dependencies": { @@ -3483,8 +3483,8 @@ "@eslint/config-helpers": "^0.3.0", "@eslint/core": "^0.15.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.31.0", - "@eslint/plugin-kit": "^0.3.1", + "@eslint/js": "9.32.0", + "@eslint/plugin-kit": "^0.3.4", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", diff --git a/server/package-lock.json b/server/package-lock.json index e101019f5d..6d418288a6 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -173,52 +173,10 @@ "node": ">=6.0.0" } }, - "node_modules/@angular-devkit/schematics": { - "version": "19.2.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.8.tgz", - "integrity": "sha512-QsmFuYdAyeCyg9WF/AJBhFXDUfCwmDFTEbsv5t5KPSP6slhk0GoLNZApniiFytU2siRlSxVNpve2uATyYuAYkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "19.2.8", - "jsonc-parser": "3.3.1", - "magic-string": "0.30.17", - "ora": "5.4.1", - "rxjs": "7.8.1" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular-devkit/schematics-cli": { - "version": "19.2.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-19.2.8.tgz", - "integrity": "sha512-RFnlyu4Ld8I4xvu/eqrhjbQ6kQTr27w79omMiTbQcQZvP3E6oUyZdBjobyih4Np+1VVQrbdEeNz76daP2iUDig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "19.2.8", - "@angular-devkit/schematics": "19.2.8", - "@inquirer/prompts": "7.3.2", - "ansi-colors": "4.1.3", - "symbol-observable": "4.0.0", - "yargs-parser": "21.1.1" - }, - "bin": { - "schematics": "bin/schematics.js" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular-devkit/schematics-cli/node_modules/@angular-devkit/core": { - "version": "19.2.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.8.tgz", - "integrity": "sha512-kcxUHKf5Hi98r4gAvMP3ntJV8wuQ3/i6wuU9RcMP0UKUt2Rer5Ryis3MPqT92jvVVwg6lhrLIhXsFuWJMiYjXQ==", + "node_modules/@angular-devkit/core": { + "version": "19.2.15", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.15.tgz", + "integrity": "sha512-pU2RZYX6vhd7uLSdLwPnuBcr0mXJSjp3EgOXKsrlQFQZevc+Qs+2JdXgIElnOT/aDqtRtriDmLlSbtdE8n3ZbA==", "dev": true, "license": "MIT", "dependencies": { @@ -243,6 +201,105 @@ } } }, + "node_modules/@angular-devkit/core/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@angular-devkit/core/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@angular-devkit/core/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@angular-devkit/core/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@angular-devkit/core/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@angular-devkit/schematics": { + "version": "19.2.15", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.15.tgz", + "integrity": "sha512-kNOJ+3vekJJCQKWihNmxBkarJzNW09kP5a9E1SRNiQVNOUEeSwcRR0qYotM65nx821gNzjjhJXnAZ8OazWldrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.15", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.17", + "ora": "5.4.1", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/schematics-cli": { + "version": "19.2.15", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-19.2.15.tgz", + "integrity": "sha512-1ESFmFGMpGQmalDB3t2EtmWDGv6gOFYBMxmHO2f1KI/UDl8UmZnCGL4mD3EWo8Hv0YIsZ9wOH9Q7ZHNYjeSpzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.15", + "@angular-devkit/schematics": "19.2.15", + "@inquirer/prompts": "7.3.2", + "ansi-colors": "4.1.3", + "symbol-observable": "4.0.0", + "yargs-parser": "21.1.1" + }, + "bin": { + "schematics": "bin/schematics.js" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, "node_modules/@angular-devkit/schematics-cli/node_modules/@inquirer/prompts": { "version": "7.3.2", "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.3.2.tgz", @@ -273,128 +330,6 @@ } } }, - "node_modules/@angular-devkit/schematics-cli/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@angular-devkit/schematics-cli/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/@angular-devkit/schematics-cli/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@angular-devkit/schematics-cli/node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@angular-devkit/schematics-cli/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@angular-devkit/schematics/node_modules/@angular-devkit/core": { - "version": "19.2.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.8.tgz", - "integrity": "sha512-kcxUHKf5Hi98r4gAvMP3ntJV8wuQ3/i6wuU9RcMP0UKUt2Rer5Ryis3MPqT92jvVVwg6lhrLIhXsFuWJMiYjXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "8.17.1", - "ajv-formats": "3.0.1", - "jsonc-parser": "3.3.1", - "picomatch": "4.0.2", - "rxjs": "7.8.1", - "source-map": "0.7.4" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^4.0.0" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, - "node_modules/@angular-devkit/schematics/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@angular-devkit/schematics/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/@angular-devkit/schematics/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/@angular-devkit/schematics/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -405,16 +340,6 @@ "tslib": "^2.1.0" } }, - "node_modules/@angular-devkit/schematics/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 8" - } - }, "node_modules/@babel/code-frame": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", @@ -1094,9 +1019,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", - "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz", + "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==", "dev": true, "license": "MIT", "engines": { @@ -1637,15 +1562,15 @@ } }, "node_modules/@inquirer/checkbox": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.1.8.tgz", - "integrity": "sha512-d/QAsnwuHX2OPolxvYcgSj7A9DO9H6gVOy2DvBTx+P2LH2iRTo/RSGV3iwCzW024nP9hw98KIuDmdyhZQj1UQg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.2.0.tgz", + "integrity": "sha512-fdSw07FLJEU5vbpOPzXo5c6xmMGDzbZE2+niuDHX5N6mc6V0Ebso/q3xiHra4D73+PMsC8MJmcaZKuAAoaQsSA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/figures": "^1.0.12", - "@inquirer/type": "^3.0.7", + "@inquirer/core": "^10.1.15", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2", "yoctocolors-cjs": "^2.1.2" }, @@ -1662,14 +1587,14 @@ } }, "node_modules/@inquirer/confirm": { - "version": "5.1.12", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.12.tgz", - "integrity": "sha512-dpq+ielV9/bqgXRUbNH//KsY6WEw9DrGPmipkpmgC1Y46cwuBTNx7PXFWTjc3MQ+urcc0QxoVHcMI0FW4Ok0hg==", + "version": "5.1.14", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.14.tgz", + "integrity": "sha512-5yR4IBfe0kXe59r1YCTG8WXkUbl7Z35HK87Sw+WUyGD8wNUx7JvY7laahzeytyE1oLn74bQnL7hstctQxisQ8Q==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/type": "^3.0.7" + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8" }, "engines": { "node": ">=18" @@ -1684,14 +1609,14 @@ } }, "node_modules/@inquirer/core": { - "version": "10.1.13", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.13.tgz", - "integrity": "sha512-1viSxebkYN2nJULlzCxES6G9/stgHSepZ9LqqfdIGPHj5OHhiBUXVS0a6R0bEC2A+VL4D9w6QB66ebCr6HGllA==", + "version": "10.1.15", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.15.tgz", + "integrity": "sha512-8xrp836RZvKkpNbVvgWUlxjT4CraKk2q+I3Ksy+seI2zkcE+y6wNs1BVhgcv8VyImFecUhdQrYLdW32pAjwBdA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/figures": "^1.0.12", - "@inquirer/type": "^3.0.7", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", @@ -1712,14 +1637,14 @@ } }, "node_modules/@inquirer/editor": { - "version": "4.2.13", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.13.tgz", - "integrity": "sha512-WbicD9SUQt/K8O5Vyk9iC2ojq5RHoCLK6itpp2fHsWe44VxxcA9z3GTWlvjSTGmMQpZr+lbVmrxdHcumJoLbMA==", + "version": "4.2.15", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.15.tgz", + "integrity": "sha512-wst31XT8DnGOSS4nNJDIklGKnf+8shuauVrWzgKegWUe28zfCftcWZ2vktGdzJgcylWSS2SrDnYUb6alZcwnCQ==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/type": "^3.0.7", + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8", "external-editor": "^3.1.0" }, "engines": { @@ -1735,14 +1660,14 @@ } }, "node_modules/@inquirer/expand": { - "version": "4.0.15", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.15.tgz", - "integrity": "sha512-4Y+pbr/U9Qcvf+N/goHzPEXiHH8680lM3Dr3Y9h9FFw4gHS+zVpbj8LfbKWIb/jayIB4aSO4pWiBTrBYWkvi5A==", + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.17.tgz", + "integrity": "sha512-PSqy9VmJx/VbE3CT453yOfNa+PykpKg/0SYP7odez1/NWBGuDXgPhp4AeGYYKjhLn5lUUavVS/JbeYMPdH50Mw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/type": "^3.0.7", + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -1758,9 +1683,9 @@ } }, "node_modules/@inquirer/figures": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.12.tgz", - "integrity": "sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.13.tgz", + "integrity": "sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==", "dev": true, "license": "MIT", "engines": { @@ -1768,14 +1693,14 @@ } }, "node_modules/@inquirer/input": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.1.12.tgz", - "integrity": "sha512-xJ6PFZpDjC+tC1P8ImGprgcsrzQRsUh9aH3IZixm1lAZFK49UGHxM3ltFfuInN2kPYNfyoPRh+tU4ftsjPLKqQ==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.1.tgz", + "integrity": "sha512-tVC+O1rBl0lJpoUZv4xY+WGWY8V5b0zxU1XDsMsIHYregdh7bN5X5QnIONNBAl0K765FYlAfNHS2Bhn7SSOVow==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/type": "^3.0.7" + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8" }, "engines": { "node": ">=18" @@ -1790,14 +1715,14 @@ } }, "node_modules/@inquirer/number": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.15.tgz", - "integrity": "sha512-xWg+iYfqdhRiM55MvqiTCleHzszpoigUpN5+t1OMcRkJrUrw7va3AzXaxvS+Ak7Gny0j2mFSTv2JJj8sMtbV2g==", + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.17.tgz", + "integrity": "sha512-GcvGHkyIgfZgVnnimURdOueMk0CztycfC8NZTiIY9arIAkeOgt6zG57G+7vC59Jns3UX27LMkPKnKWAOF5xEYg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/type": "^3.0.7" + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8" }, "engines": { "node": ">=18" @@ -1812,14 +1737,14 @@ } }, "node_modules/@inquirer/password": { - "version": "4.0.15", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.15.tgz", - "integrity": "sha512-75CT2p43DGEnfGTaqFpbDC2p2EEMrq0S+IRrf9iJvYreMy5mAWj087+mdKyLHapUEPLjN10mNvABpGbk8Wdraw==", + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.17.tgz", + "integrity": "sha512-DJolTnNeZ00E1+1TW+8614F7rOJJCM4y4BAGQ3Gq6kQIG+OJ4zr3GLjIjVVJCbKsk2jmkmv6v2kQuN/vriHdZA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/type": "^3.0.7", + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2" }, "engines": { @@ -1835,22 +1760,22 @@ } }, "node_modules/@inquirer/prompts": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.4.1.tgz", - "integrity": "sha512-UlmM5FVOZF0gpoe1PT/jN4vk8JmpIWBlMvTL8M+hlvPmzN89K6z03+IFmyeu/oFCenwdwHDr2gky7nIGSEVvlA==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.8.0.tgz", + "integrity": "sha512-JHwGbQ6wjf1dxxnalDYpZwZxUEosT+6CPGD9Zh4sm9WXdtUp9XODCQD3NjSTmu+0OAyxWXNOqf0spjIymJa2Tw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/checkbox": "^4.1.5", - "@inquirer/confirm": "^5.1.9", - "@inquirer/editor": "^4.2.10", - "@inquirer/expand": "^4.0.12", - "@inquirer/input": "^4.1.9", - "@inquirer/number": "^3.0.12", - "@inquirer/password": "^4.0.12", - "@inquirer/rawlist": "^4.0.12", - "@inquirer/search": "^3.0.12", - "@inquirer/select": "^4.1.1" + "@inquirer/checkbox": "^4.2.0", + "@inquirer/confirm": "^5.1.14", + "@inquirer/editor": "^4.2.15", + "@inquirer/expand": "^4.0.17", + "@inquirer/input": "^4.2.1", + "@inquirer/number": "^3.0.17", + "@inquirer/password": "^4.0.17", + "@inquirer/rawlist": "^4.1.5", + "@inquirer/search": "^3.1.0", + "@inquirer/select": "^4.3.1" }, "engines": { "node": ">=18" @@ -1865,14 +1790,14 @@ } }, "node_modules/@inquirer/rawlist": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.3.tgz", - "integrity": "sha512-7XrV//6kwYumNDSsvJIPeAqa8+p7GJh7H5kRuxirct2cgOcSWwwNGoXDRgpNFbY/MG2vQ4ccIWCi8+IXXyFMZA==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.5.tgz", + "integrity": "sha512-R5qMyGJqtDdi4Ht521iAkNqyB6p2UPuZUbMifakg1sWtu24gc2Z8CJuw8rP081OckNDMgtDCuLe42Q2Kr3BolA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/type": "^3.0.7", + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -1888,15 +1813,15 @@ } }, "node_modules/@inquirer/search": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.15.tgz", - "integrity": "sha512-YBMwPxYBrADqyvP4nNItpwkBnGGglAvCLVW8u4pRmmvOsHUtCAUIMbUrLX5B3tFL1/WsLGdQ2HNzkqswMs5Uaw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.1.0.tgz", + "integrity": "sha512-PMk1+O/WBcYJDq2H7foV0aAZSmDdkzZB9Mw2v/DmONRJopwA/128cS9M/TXWLKKdEQKZnKwBzqu2G4x/2Nqx8Q==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/figures": "^1.0.12", - "@inquirer/type": "^3.0.7", + "@inquirer/core": "^10.1.15", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -1912,15 +1837,15 @@ } }, "node_modules/@inquirer/select": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.2.3.tgz", - "integrity": "sha512-OAGhXU0Cvh0PhLz9xTF/kx6g6x+sP+PcyTiLvCrewI99P3BBeexD+VbuwkNDvqGkk3y2h5ZiWLeRP7BFlhkUDg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.3.1.tgz", + "integrity": "sha512-Gfl/5sqOF5vS/LIrSndFgOh7jgoe0UXEizDqahFRkq5aJBLegZ6WjuMh/hVEJwlFQjyLq1z9fRtvUMkb7jM1LA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/figures": "^1.0.12", - "@inquirer/type": "^3.0.7", + "@inquirer/core": "^10.1.15", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2", "yoctocolors-cjs": "^2.1.2" }, @@ -1937,9 +1862,9 @@ } }, "node_modules/@inquirer/type": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.7.tgz", - "integrity": "sha512-PfunHQcjwnju84L+ycmcMKB/pTPIngjUJvfnRhKY6FKPuYXlM4aQCb/nIdTFR6BEhMjFvngzvng/vBAJMZpLSA==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.8.tgz", + "integrity": "sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw==", "dev": true, "license": "MIT", "engines": { @@ -1955,9 +1880,9 @@ } }, "node_modules/@ioredis/commands": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", - "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.3.0.tgz", + "integrity": "sha512-M/T6Zewn7sDaBQEqIZ8Rb+i9y8qfGmq+5SDFSf9sA2lUZTmdDLVdOiQaeDp+Q4wElZ9HG1GAX5KhDaidp6LQsQ==", "license": "MIT" }, "node_modules/@isaacs/balanced-match": { @@ -2483,9 +2408,9 @@ ] }, "node_modules/@nestjs/bull-shared": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/@nestjs/bull-shared/-/bull-shared-11.0.2.tgz", - "integrity": "sha512-dFlttJvBqIFD6M8JVFbkrR4Feb39OTAJPJpFVILU50NOJCM4qziRw3dSNG84Q3v+7/M6xUGMFdZRRGvBBKxoSA==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@nestjs/bull-shared/-/bull-shared-11.0.3.tgz", + "integrity": "sha512-CaHniPkLAxis6fAB1DB8WZELQv8VPCLedbj7iP0VQ1pz74i6NSzG9mBg6tOomXq/WW4la4P4OMGEQ48UAJh20A==", "license": "MIT", "dependencies": { "tslib": "2.8.1" @@ -2496,12 +2421,12 @@ } }, "node_modules/@nestjs/bullmq": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/@nestjs/bullmq/-/bullmq-11.0.2.tgz", - "integrity": "sha512-Lq6lGpKkETsm0RDcUktlzsthFoE3A5QTMp2FwPi1eztKqKD6/90KS1TcnC9CJFzjpUaYnQzIMrlNs55e+/wsHA==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@nestjs/bullmq/-/bullmq-11.0.3.tgz", + "integrity": "sha512-0Qr7Fk3Ir3V2OBIKJk+ArEM0AesGjKaNZA8QQ4fH3qGogudYADSjaNe910/OAfmX8q+cjCRorvwTLdcShwWEMw==", "license": "MIT", "dependencies": { - "@nestjs/bull-shared": "^11.0.2", + "@nestjs/bull-shared": "^11.0.3", "tslib": "2.8.1" }, "peerDependencies": { @@ -2511,30 +2436,30 @@ } }, "node_modules/@nestjs/cli": { - "version": "11.0.7", - "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-11.0.7.tgz", - "integrity": "sha512-svrP8j1R0/lQVJ8ZI3BlDtuZxmkvVJokUJSB04sr6uibunk2wHeVDDVLZvYBUorCdGU/RHJl1IufhqUBM91vAQ==", + "version": "11.0.10", + "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-11.0.10.tgz", + "integrity": "sha512-4waDT0yGWANg0pKz4E47+nUrqIJv/UqrZ5wLPkCqc7oMGRMWKAaw1NDZ9rKsaqhqvxb2LfI5+uXOWr4yi94DOQ==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.2.8", - "@angular-devkit/schematics": "19.2.8", - "@angular-devkit/schematics-cli": "19.2.8", - "@inquirer/prompts": "7.4.1", + "@angular-devkit/core": "19.2.15", + "@angular-devkit/schematics": "19.2.15", + "@angular-devkit/schematics-cli": "19.2.15", + "@inquirer/prompts": "7.8.0", "@nestjs/schematics": "^11.0.1", - "ansis": "3.17.0", + "ansis": "4.1.0", "chokidar": "4.0.3", "cli-table3": "0.6.5", "commander": "4.1.1", "fork-ts-checker-webpack-plugin": "9.1.0", - "glob": "11.0.1", + "glob": "11.0.3", "node-emoji": "1.11.0", "ora": "5.4.1", "tree-kill": "1.2.2", "tsconfig-paths": "4.2.0", "tsconfig-paths-webpack-plugin": "4.2.0", "typescript": "5.8.3", - "webpack": "5.99.6", + "webpack": "5.100.2", "webpack-node-externals": "3.0.0" }, "bin": { @@ -2556,89 +2481,14 @@ } } }, - "node_modules/@nestjs/cli/node_modules/@angular-devkit/core": { - "version": "19.2.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.8.tgz", - "integrity": "sha512-kcxUHKf5Hi98r4gAvMP3ntJV8wuQ3/i6wuU9RcMP0UKUt2Rer5Ryis3MPqT92jvVVwg6lhrLIhXsFuWJMiYjXQ==", + "node_modules/@nestjs/cli/node_modules/ansis": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.1.0.tgz", + "integrity": "sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w==", "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "8.17.1", - "ajv-formats": "3.0.1", - "jsonc-parser": "3.3.1", - "picomatch": "4.0.2", - "rxjs": "7.8.1", - "source-map": "0.7.4" - }, + "license": "ISC", "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^4.0.0" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, - "node_modules/@nestjs/cli/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@nestjs/cli/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/@nestjs/cli/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@nestjs/cli/node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@nestjs/cli/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 8" + "node": ">=14" } }, "node_modules/@nestjs/common": { @@ -2800,14 +2650,14 @@ } }, "node_modules/@nestjs/schematics": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-11.0.5.tgz", - "integrity": "sha512-T50SCNyqCZ/fDssaOD7meBKLZ87ebRLaJqZTJPvJKjlib1VYhMOCwXYsr7bjMPmuPgiQHOwvppz77xN/m6GM7A==", + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-11.0.7.tgz", + "integrity": "sha512-t8dNYYMwEeEsrlwc2jbkfwCfXczq4AeNEgx1KVQuJ6wYibXk0ZbXbPdfp8scnEAaQv1grpncNV5gWgzi7ZwbvQ==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.2.6", - "@angular-devkit/schematics": "19.2.6", + "@angular-devkit/core": "19.2.15", + "@angular-devkit/schematics": "19.2.15", "comment-json": "4.2.5", "jsonc-parser": "3.3.1", "pluralize": "8.0.0" @@ -2816,110 +2666,6 @@ "typescript": ">=4.8.2" } }, - "node_modules/@nestjs/schematics/node_modules/@angular-devkit/core": { - "version": "19.2.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.6.tgz", - "integrity": "sha512-WFgiYhrDMq83UNaGRAneIM7CYYdBozD+yYA9BjoU8AgBLKtrvn6S8ZcjKAk5heoHtY/u8pEb0mwDTz9gxFmJZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "8.17.1", - "ajv-formats": "3.0.1", - "jsonc-parser": "3.3.1", - "picomatch": "4.0.2", - "rxjs": "7.8.1", - "source-map": "0.7.4" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^4.0.0" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, - "node_modules/@nestjs/schematics/node_modules/@angular-devkit/schematics": { - "version": "19.2.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.6.tgz", - "integrity": "sha512-YTAxNnT++5eflx19OUHmOWu597/TbTel+QARiZCv1xQw99+X8DCKKOUXtqBRd53CAHlREDI33Rn/JLY3NYgMLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "19.2.6", - "jsonc-parser": "3.3.1", - "magic-string": "0.30.17", - "ora": "5.4.1", - "rxjs": "7.8.1" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@nestjs/schematics/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@nestjs/schematics/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/@nestjs/schematics/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@nestjs/schematics/node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@nestjs/schematics/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 8" - } - }, "node_modules/@nestjs/swagger": { "version": "11.2.0", "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-11.2.0.tgz", @@ -5868,9 +5614,9 @@ } }, "node_modules/@react-email/components": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@react-email/components/-/components-0.3.2.tgz", - "integrity": "sha512-nVbo0KtBdZbj19lvfFpe0ZhjKPh6LE229+NyQLuTDt6dfaLzNRpSu/rHP+jlvdWBAk93slsoGyWDRldbqklpaA==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@react-email/components/-/components-0.3.3.tgz", + "integrity": "sha512-MHs5HzWroICsZmnOqsQQIepMIjqV7X3k/UVQqdzbcLyIQ6L8l1cTODZutyyDDPK1th+AF1iSZtUnt7xr8dxKiw==", "license": "MIT", "dependencies": { "@react-email/body": "0.0.11", @@ -5888,7 +5634,7 @@ "@react-email/link": "0.0.12", "@react-email/markdown": "0.0.15", "@react-email/preview": "0.0.13", - "@react-email/render": "1.1.3", + "@react-email/render": "1.1.4", "@react-email/row": "0.0.12", "@react-email/section": "0.0.16", "@react-email/tailwind": "1.2.2", @@ -6022,9 +5768,9 @@ } }, "node_modules/@react-email/render": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@react-email/render/-/render-1.1.3.tgz", - "integrity": "sha512-TjjF1tdTmOqYEIWWg9wMx5q9JbQRbWmnG7owQbSGEHkNfc/c/vBu7hjfrki907lgQEAkYac9KPTyIjOKhvhJCg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@react-email/render/-/render-1.1.4.tgz", + "integrity": "sha512-9ZFRrDB8AiRpacWDDXC5q14D5uCE1uR7iStbxAOHsL5vvAj8JGfCwl8zZ/BubVwALlIhFQiyJPCvGbyfbkPVuw==", "license": "MIT", "dependencies": { "html-to-text": "^9.0.5", @@ -6464,9 +6210,9 @@ "license": "MIT" }, "node_modules/@swc/core": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.2.tgz", - "integrity": "sha512-YWqn+0IKXDhqVLKoac4v2tV6hJqB/wOh8/Br8zjqeqBkKa77Qb0Kw2i7LOFzjFNZbZaPH6AlMGlBwNrxaauaAg==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.3.tgz", + "integrity": "sha512-ZaDETVWnm6FE0fc+c2UE8MHYVS3Fe91o5vkmGfgwGXFbxYvAjKSqxM/j4cRc9T7VZNSJjriXq58XkfCp3Y6f+w==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -6482,16 +6228,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.13.2", - "@swc/core-darwin-x64": "1.13.2", - "@swc/core-linux-arm-gnueabihf": "1.13.2", - "@swc/core-linux-arm64-gnu": "1.13.2", - "@swc/core-linux-arm64-musl": "1.13.2", - "@swc/core-linux-x64-gnu": "1.13.2", - "@swc/core-linux-x64-musl": "1.13.2", - "@swc/core-win32-arm64-msvc": "1.13.2", - "@swc/core-win32-ia32-msvc": "1.13.2", - "@swc/core-win32-x64-msvc": "1.13.2" + "@swc/core-darwin-arm64": "1.13.3", + "@swc/core-darwin-x64": "1.13.3", + "@swc/core-linux-arm-gnueabihf": "1.13.3", + "@swc/core-linux-arm64-gnu": "1.13.3", + "@swc/core-linux-arm64-musl": "1.13.3", + "@swc/core-linux-x64-gnu": "1.13.3", + "@swc/core-linux-x64-musl": "1.13.3", + "@swc/core-win32-arm64-msvc": "1.13.3", + "@swc/core-win32-ia32-msvc": "1.13.3", + "@swc/core-win32-x64-msvc": "1.13.3" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" @@ -6503,9 +6249,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.2.tgz", - "integrity": "sha512-44p7ivuLSGFJ15Vly4ivLJjg3ARo4879LtEBAabcHhSZygpmkP8eyjyWxrH3OxkY1eRZSIJe8yRZPFw4kPXFPw==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.3.tgz", + "integrity": "sha512-ux0Ws4pSpBTqbDS9GlVP354MekB1DwYlbxXU3VhnDr4GBcCOimpocx62x7cFJkSpEBF8bmX8+/TTCGKh4PbyXw==", "cpu": [ "arm64" ], @@ -6520,9 +6266,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.13.2.tgz", - "integrity": "sha512-Lb9EZi7X2XDAVmuUlBm2UvVAgSCbD3qKqDCxSI4jEOddzVOpNCnyZ/xEampdngUIyDDhhJLYU9duC+Mcsv5Y+A==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.13.3.tgz", + "integrity": "sha512-p0X6yhxmNUOMZrbeZ3ZNsPige8lSlSe1llllXvpCLkKKxN/k5vZt1sULoq6Nj4eQ7KeHQVm81/+AwKZyf/e0TA==", "cpu": [ "x64" ], @@ -6537,9 +6283,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.13.2.tgz", - "integrity": "sha512-9TDe/92ee1x57x+0OqL1huG4BeljVx0nWW4QOOxp8CCK67Rpc/HHl2wciJ0Kl9Dxf2NvpNtkPvqj9+BUmM9WVA==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.13.3.tgz", + "integrity": "sha512-OmDoiexL2fVWvQTCtoh0xHMyEkZweQAlh4dRyvl8ugqIPEVARSYtaj55TBMUJIP44mSUOJ5tytjzhn2KFxFcBA==", "cpu": [ "arm" ], @@ -6554,9 +6300,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.2.tgz", - "integrity": "sha512-KJUSl56DBk7AWMAIEcU83zl5mg3vlQYhLELhjwRFkGFMvghQvdqQ3zFOYa4TexKA7noBZa3C8fb24rI5sw9Exg==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.3.tgz", + "integrity": "sha512-STfKku3QfnuUj6k3g9ld4vwhtgCGYIFQmsGPPgT9MK/dI3Lwnpe5Gs5t1inoUIoGNP8sIOLlBB4HV4MmBjQuhw==", "cpu": [ "arm64" ], @@ -6571,9 +6317,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.13.2.tgz", - "integrity": "sha512-teU27iG1oyWpNh9CzcGQ48ClDRt/RCem7mYO7ehd2FY102UeTws2+OzLESS1TS1tEZipq/5xwx3FzbVgiolCiQ==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.13.3.tgz", + "integrity": "sha512-bc+CXYlFc1t8pv9yZJGus372ldzOVscBl7encUBlU1m/Sig0+NDJLz6cXXRcFyl6ABNOApWeR4Yl7iUWx6C8og==", "cpu": [ "arm64" ], @@ -6588,9 +6334,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.13.2.tgz", - "integrity": "sha512-dRPsyPyqpLD0HMRCRpYALIh4kdOir8pPg4AhNQZLehKowigRd30RcLXGNVZcc31Ua8CiPI4QSgjOIxK+EQe4LQ==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.13.3.tgz", + "integrity": "sha512-dFXoa0TEhohrKcxn/54YKs1iwNeW6tUkHJgXW33H381SvjKFUV53WR231jh1sWVJETjA3vsAwxKwR23s7UCmUA==", "cpu": [ "x64" ], @@ -6605,9 +6351,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.13.2.tgz", - "integrity": "sha512-CCxETW+KkYEQDqz1SYC15YIWYheqFC+PJVOW76Maa/8yu8Biw+HTAcblKf2isrlUtK8RvrQN94v3UXkC2NzCEw==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.13.3.tgz", + "integrity": "sha512-ieyjisLB+ldexiE/yD8uomaZuZIbTc8tjquYln9Quh5ykOBY7LpJJYBWvWtm1g3pHv6AXlBI8Jay7Fffb6aLfA==", "cpu": [ "x64" ], @@ -6622,9 +6368,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.13.2.tgz", - "integrity": "sha512-Wv/QTA6PjyRLlmKcN6AmSI4jwSMRl0VTLGs57PHTqYRwwfwd7y4s2fIPJVBNbAlXd795dOEP6d/bGSQSyhOX3A==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.13.3.tgz", + "integrity": "sha512-elTQpnaX5vESSbhCEgcwXjpMsnUbqqHfEpB7ewpkAsLzKEXZaK67ihSRYAuAx6ewRQTo7DS5iTT6X5aQD3MzMw==", "cpu": [ "arm64" ], @@ -6639,9 +6385,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.13.2.tgz", - "integrity": "sha512-PuCdtNynEkUNbUXX/wsyUC+t4mamIU5y00lT5vJcAvco3/r16Iaxl5UCzhXYaWZSNVZMzPp9qN8NlSL8M5pPxw==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.13.3.tgz", + "integrity": "sha512-nvehQVEOdI1BleJpuUgPLrclJ0TzbEMc+MarXDmmiRFwEUGqj+pnfkTSb7RZyS1puU74IXdK/YhTirHurtbI9w==", "cpu": [ "ia32" ], @@ -6656,9 +6402,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.13.2.tgz", - "integrity": "sha512-qlmMkFZJus8cYuBURx1a3YAG2G7IW44i+FEYV5/32ylKkzGNAr9tDJSA53XNnNXkAB5EXSPsOz7bn5C3JlEtdQ==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.13.3.tgz", + "integrity": "sha512-A+JSKGkRbPLVV2Kwx8TaDAV0yXIXm/gc8m98hSkVDGlPBBmydgzNdWy3X7HTUBM7IDk7YlWE7w2+RUGjdgpTmg==", "cpu": [ "x64" ], @@ -7025,9 +6771,9 @@ "license": "MIT" }, "node_modules/@types/luxon": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.6.2.tgz", - "integrity": "sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.7.1.tgz", + "integrity": "sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg==", "license": "MIT" }, "node_modules/@types/memcached": { @@ -7141,9 +6887,9 @@ } }, "node_modules/@types/picomatch": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-4.0.1.tgz", - "integrity": "sha512-dLqxmi5VJRC9XTvc/oaTtk+bDb4RRqxLZPZ3jIpYBHEnDXX8lu02w2yWI6NsPPsELuVK298Z2iR8jgoWKRdUVQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-qHHxQ+P9PysNEGbALT8f8YOSHW0KJu6l2xU8DYY0fu/EmGxXdVnuTLvFUvBgPJMSqXq29SYHveejeAha+4AYgA==", "dev": true, "license": "MIT" }, @@ -7172,9 +6918,9 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.1.8", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", - "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", + "version": "19.1.9", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.9.tgz", + "integrity": "sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA==", "dev": true, "license": "MIT", "dependencies": { @@ -7971,6 +7717,19 @@ "acorn": "^8" } }, + "node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "acorn": "^8.14.0" + } + }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -8731,9 +8490,9 @@ } }, "node_modules/bullmq": { - "version": "5.56.5", - "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.56.5.tgz", - "integrity": "sha512-nhcVxoE9Y0YUuNYtvaD+N0Bk2kqcU+rXzJwdQIr8i8qC/fxoghwUYb9a+CidTv24pi1eqstLnBoa8xkR/P7Mdw==", + "version": "5.56.9", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.56.9.tgz", + "integrity": "sha512-SL7OZG0x9sh/PC6ZVKqibSmPsbjViBaiFAyr3ujJRxb6nlZefb1hU0biJuvfI8/hQa4HtEG9sCHRMiz905B2eg==", "license": "MIT", "dependencies": { "cron-parser": "^4.9.0", @@ -9677,6 +9436,12 @@ "node": ">=12.0.0" } }, + "node_modules/cron/node_modules/@types/luxon": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.6.2.tgz", + "integrity": "sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw==", + "license": "MIT" + }, "node_modules/cron/node_modules/luxon": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.6.1.tgz", @@ -10486,9 +10251,9 @@ } }, "node_modules/eslint": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", - "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz", + "integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==", "dev": true, "license": "MIT", "dependencies": { @@ -10498,8 +10263,8 @@ "@eslint/config-helpers": "^0.3.0", "@eslint/core": "^0.15.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.31.0", - "@eslint/plugin-kit": "^0.3.1", + "@eslint/js": "9.32.0", + "@eslint/plugin-kit": "^0.3.4", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -11623,14 +11388,14 @@ "license": "MIT" }, "node_modules/glob": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", - "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", + "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", "license": "ISC", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^4.0.1", - "minimatch": "^10.0.0", + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.0.3", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" @@ -12122,12 +11887,12 @@ } }, "node_modules/ioredis": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.6.1.tgz", - "integrity": "sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.7.0.tgz", + "integrity": "sha512-NUcA93i1lukyXU+riqEyPtSEkyFq8tX90uL659J+qpCZ3rEdViB/APC58oAhIh3+bJln2hzdlZbBZsGNrlsR8g==", "license": "MIT", "dependencies": { - "@ioredis/commands": "^1.1.1", + "@ioredis/commands": "^1.3.0", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", "denque": "^2.1.0", @@ -13696,9 +13461,9 @@ } }, "node_modules/node-gyp": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-11.2.0.tgz", - "integrity": "sha512-T0S1zqskVUSxcsSTkAsLc7xCycrRYmtDHadDinzocrThjyQCn5kMlEBSj6H4qDbgsIOSLmmlRIeb0lZXj+UArA==", + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-11.3.0.tgz", + "integrity": "sha512-9J0+C+2nt3WFuui/mC46z2XCZ21/cKlFDuywULmseD/LlmnOrSeEAE4c/1jw6aybXLmpZnQY3/LmOJfgyHIcng==", "dev": true, "license": "MIT", "dependencies": { @@ -15046,30 +14811,30 @@ } }, "node_modules/react": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", - "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz", + "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", - "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz", + "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==", "license": "MIT", "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { - "react": "^19.1.0" + "react": "^19.1.1" } }, "node_modules/react-email": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/react-email/-/react-email-4.2.4.tgz", - "integrity": "sha512-r5x1nlWUXKZWoIU7l9jx5jkq43RuDUlroH0FRA5MMrCOaLqAfg3vOsAxAadNkG47L0iTeDkkqTKzaV6dTaYf/A==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/react-email/-/react-email-4.2.5.tgz", + "integrity": "sha512-LESJxw/Lt3pD+IlziIDfSUGBhwq+4XrwEyxjLKP4piBpqqfUzOQT9VpFDby1kyWSgFrOhABNymVgLQF5HRpmQg==", "license": "MIT", "dependencies": { "@babel/parser": "^7.27.0", @@ -15098,9 +14863,9 @@ } }, "node_modules/react-email/node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.5.0.tgz", + "integrity": "sha512-1tm8DTaJhPBG3bIkVeZt1iZM9GfSX2lzOeDVZH9R9ffRHpmHvxZ/QhgQH/aDTkswQVt+YHdXAdS/In/30OjCbg==", "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" @@ -18593,21 +18358,23 @@ } }, "node_modules/webpack": { - "version": "5.99.6", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.6.tgz", - "integrity": "sha512-TJOLrJ6oeccsGWPl7ujCYuc0pIq2cNsuD6GZDma8i5o5Npvcco/z+NKvZSFsP0/x6SShVb0+X2JK/JHUjKY9dQ==", + "version": "5.100.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.100.2.tgz", + "integrity": "sha512-QaNKAvGCDRh3wW1dsDjeMdDXwZm2vqq3zn6Pvq4rHOEOGSaUMgOOjG2Y9ZbIGzpfkJk9ZYTHpDqgDfeBDcnLaw==", "dev": true, "license": "MIT", "dependencies": { "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.6", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.14.0", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.1", + "enhanced-resolve": "^5.17.2", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -18617,11 +18384,11 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^4.3.0", + "schema-utils": "^4.3.2", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.3.11", "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" + "webpack-sources": "^3.3.3" }, "bin": { "webpack": "bin/webpack.js" diff --git a/web/package-lock.json b/web/package-lock.json index 18ca7c7d82..53b87d6b61 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -52,7 +52,7 @@ "@socket.io/component-emitter": "^3.1.0", "@sveltejs/adapter-static": "^3.0.8", "@sveltejs/enhanced-img": "^0.7.0", - "@sveltejs/kit": "^2.25.0", + "@sveltejs/kit": "^2.25.2", "@sveltejs/vite-plugin-svelte": "6.1.0", "@tailwindcss/vite": "^4.1.7", "@testing-library/jest-dom": "^6.4.2", @@ -760,9 +760,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", - "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz", + "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==", "dev": true, "license": "MIT", "engines": { @@ -2732,18 +2732,18 @@ } }, "node_modules/@testing-library/jest-dom": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.6.3.tgz", - "integrity": "sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==", + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.6.4.tgz", + "integrity": "sha512-xDXgLjVunjHqczScfkCJ9iyjdNOVHvvCdqHSSxwM9L0l/wHkTRum67SDc020uAlCoqktJplgO2AAQeLP1wgqDQ==", "dev": true, "license": "MIT", "dependencies": { "@adobe/css-tools": "^4.4.0", "aria-query": "^5.0.0", - "chalk": "^3.0.0", "css.escape": "^1.5.1", "dom-accessibility-api": "^0.6.3", "lodash": "^4.17.21", + "picocolors": "^1.1.1", "redent": "^3.0.0" }, "engines": { @@ -2752,20 +2752,6 @@ "yarn": ">=1" } }, - "node_modules/@testing-library/jest-dom/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", @@ -2968,9 +2954,9 @@ } }, "node_modules/@types/luxon": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.6.2.tgz", - "integrity": "sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.7.1.tgz", + "integrity": "sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg==", "dev": true, "license": "MIT" }, @@ -4484,9 +4470,9 @@ } }, "node_modules/dotenv": { - "version": "17.2.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.0.tgz", - "integrity": "sha512-Q4sgBT60gzd0BB0lSyYD3xM4YxrXA9y4uBDof1JNYGzOXrQdQ6yX+7XIAqoFOGQFOTK1D3Hts5OllpxMDZFONQ==", + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz", + "integrity": "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -4817,9 +4803,9 @@ } }, "node_modules/eslint": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", - "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz", + "integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==", "dev": true, "license": "MIT", "dependencies": { @@ -4829,8 +4815,8 @@ "@eslint/config-helpers": "^0.3.0", "@eslint/core": "^0.15.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.31.0", - "@eslint/plugin-kit": "^0.3.1", + "@eslint/js": "9.32.0", + "@eslint/plugin-kit": "^0.3.4", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -7626,9 +7612,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { @@ -9543,15 +9529,15 @@ "license": "MIT" }, "node_modules/vite": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.5.tgz", - "integrity": "sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.6.tgz", + "integrity": "sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.6", - "picomatch": "^4.0.2", + "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.40.0", "tinyglobby": "^0.2.14" diff --git a/web/package.json b/web/package.json index 21216547d0..f37af78b12 100644 --- a/web/package.json +++ b/web/package.json @@ -69,7 +69,7 @@ "@socket.io/component-emitter": "^3.1.0", "@sveltejs/adapter-static": "^3.0.8", "@sveltejs/enhanced-img": "^0.7.0", - "@sveltejs/kit": "^2.25.0", + "@sveltejs/kit": "^2.25.2", "@sveltejs/vite-plugin-svelte": "6.1.0", "@tailwindcss/vite": "^4.1.7", "@testing-library/jest-dom": "^6.4.2", From 9680f1290d21a159632967e5a42a8380ed9491f4 Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Tue, 5 Aug 2025 19:05:25 +0530 Subject: [PATCH 171/748] fix: exclude assets that haven't been hashed yet from uploads (#20684) Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- .../lib/infrastructure/repositories/backup.repository.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mobile/lib/infrastructure/repositories/backup.repository.dart b/mobile/lib/infrastructure/repositories/backup.repository.dart index ce38ff9311..a40e145a13 100644 --- a/mobile/lib/infrastructure/repositories/backup.repository.dart +++ b/mobile/lib/infrastructure/repositories/backup.repository.dart @@ -113,6 +113,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { final query = _db.localAssetEntity.select() ..where( (lae) => + lae.checksum.isNotNull() & existsQuery( _db.localAlbumAssetEntity.selectOnly() ..addColumns([_db.localAlbumAssetEntity.assetId]) @@ -125,9 +126,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { _db.remoteAssetEntity.selectOnly() ..addColumns([_db.remoteAssetEntity.checksum]) ..where( - _db.remoteAssetEntity.checksum.equalsExp(lae.checksum) & - _db.remoteAssetEntity.ownerId.equals(userId) & - lae.checksum.isNotNull(), + _db.remoteAssetEntity.checksum.equalsExp(lae.checksum) & _db.remoteAssetEntity.ownerId.equals(userId), ), ) & lae.id.isNotInQuery(_getExcludedSubquery()), From 9e6fee4064f41e918b56ecd25ed59804a330721b Mon Sep 17 00:00:00 2001 From: Mert <101130780+mertalev@users.noreply.github.com> Date: Tue, 5 Aug 2025 10:20:25 -0400 Subject: [PATCH 172/748] fix(mobile): use cached thumbnail in full size image provider (#20637) --- .../extensions/build_context_extensions.dart | 3 + mobile/lib/extensions/codec_extensions.dart | 10 +++ .../asset_viewer/asset_viewer.page.dart | 18 ++--- .../widgets/images/image_provider.dart | 29 ++++++- .../widgets/images/local_image_provider.dart | 78 +++++++++---------- ...ne_frame_multi_image_stream_completer.dart | 67 ++++++++++++++++ .../widgets/images/remote_image_provider.dart | 28 +++---- .../widgets/images/thumbnail.widget.dart | 2 +- .../widgets/timeline/constants.dart | 5 +- .../widgets/timeline/segment_builder.dart | 2 +- 10 files changed, 166 insertions(+), 76 deletions(-) create mode 100644 mobile/lib/extensions/codec_extensions.dart create mode 100644 mobile/lib/presentation/widgets/images/one_frame_multi_image_stream_completer.dart diff --git a/mobile/lib/extensions/build_context_extensions.dart b/mobile/lib/extensions/build_context_extensions.dart index a624337954..0a35bc1a86 100644 --- a/mobile/lib/extensions/build_context_extensions.dart +++ b/mobile/lib/extensions/build_context_extensions.dart @@ -13,6 +13,9 @@ extension ContextHelper on BuildContext { // Returns the current height from MediaQuery double get height => MediaQuery.sizeOf(this).height; + // Returns the current size from MediaQuery + Size get sizeData => MediaQuery.sizeOf(this); + // Returns true if the app is running on a mobile device (!tablets) bool get isMobile => width < 550; diff --git a/mobile/lib/extensions/codec_extensions.dart b/mobile/lib/extensions/codec_extensions.dart new file mode 100644 index 0000000000..00c1158f0c --- /dev/null +++ b/mobile/lib/extensions/codec_extensions.dart @@ -0,0 +1,10 @@ +import 'dart:ui'; + +import 'package:flutter/painting.dart'; + +extension CodecImageInfoExtension on Codec { + Future getImageInfo({double scale = 1.0}) async { + final frame = await getNextFrame(); + return ImageInfo(image: frame.image, scale: scale); + } +} diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart index 2fa54ad65d..d7e83e72ec 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart @@ -147,11 +147,7 @@ class _AssetViewerState extends ConsumerState { // Precache both thumbnail and full image for smooth transitions unawaited( Future.wait([ - precacheImage( - getThumbnailImageProvider(asset: asset, size: screenSize), - context, - onError: (_, __) {}, - ), + precacheImage(getThumbnailImageProvider(asset: asset), context, onError: (_, __) {}), precacheImage(getFullImageProvider(asset, size: screenSize), context, onError: (_, __) {}), ]), ); @@ -482,7 +478,7 @@ class _AssetViewerState extends ConsumerState { width: double.infinity, height: double.infinity, color: backgroundColor, - child: Thumbnail(asset: asset, fit: BoxFit.contain, size: Size(ctx.width, ctx.height)), + child: Thumbnail(asset: asset, fit: BoxFit.contain), ); } @@ -513,7 +509,7 @@ class _AssetViewerState extends ConsumerState { } PhotoViewGalleryPageOptions _imageBuilder(BuildContext ctx, BaseAsset asset) { - final size = Size(ctx.width, ctx.height); + final size = ctx.sizeData; return PhotoViewGalleryPageOptions( key: ValueKey(asset.heroTag), imageProvider: getFullImageProvider(asset, size: size), @@ -529,10 +525,10 @@ class _AssetViewerState extends ConsumerState { onTapDown: _onTapDown, onLongPressStart: asset.isMotionPhoto ? _onLongPress : null, errorBuilder: (_, __, ___) => Container( - width: ctx.width, - height: ctx.height, + width: size.width, + height: size.height, color: backgroundColor, - child: Thumbnail(asset: asset, fit: BoxFit.contain, size: size), + child: Thumbnail(asset: asset, fit: BoxFit.contain), ), ); } @@ -562,7 +558,7 @@ class _AssetViewerState extends ConsumerState { asset: asset, image: Image( key: ValueKey(asset), - image: getFullImageProvider(asset, size: Size(ctx.width, ctx.height)), + image: getFullImageProvider(asset, size: ctx.sizeData), fit: BoxFit.contain, height: ctx.height, width: ctx.width, diff --git a/mobile/lib/presentation/widgets/images/image_provider.dart b/mobile/lib/presentation/widgets/images/image_provider.dart index d94480b434..adb1e178ca 100644 --- a/mobile/lib/presentation/widgets/images/image_provider.dart +++ b/mobile/lib/presentation/widgets/images/image_provider.dart @@ -4,13 +4,14 @@ import 'package:immich_mobile/domain/models/setting.model.dart'; import 'package:immich_mobile/domain/services/setting.service.dart'; import 'package:immich_mobile/presentation/widgets/images/local_image_provider.dart'; import 'package:immich_mobile/presentation/widgets/images/remote_image_provider.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/constants.dart'; ImageProvider getFullImageProvider(BaseAsset asset, {Size size = const Size(1080, 1920)}) { // Create new provider and cache it final ImageProvider provider; if (_shouldUseLocalAsset(asset)) { final id = asset is LocalAsset ? asset.id : (asset as RemoteAsset).localId!; - provider = LocalFullImageProvider(id: id, name: asset.name, size: size, type: asset.type); + provider = LocalFullImageProvider(id: id, size: size, type: asset.type, updatedAt: asset.updatedAt); } else { final String assetId; if (asset is LocalAsset && asset.hasRemote) { @@ -26,7 +27,7 @@ ImageProvider getFullImageProvider(BaseAsset asset, {Size size = const Size(1080 return provider; } -ImageProvider getThumbnailImageProvider({BaseAsset? asset, String? remoteId, Size size = const Size.square(256)}) { +ImageProvider getThumbnailImageProvider({BaseAsset? asset, String? remoteId, Size size = kThumbnailResolution}) { assert(asset != null || remoteId != null, 'Either asset or remoteId must be provided'); if (remoteId != null) { @@ -35,7 +36,7 @@ ImageProvider getThumbnailImageProvider({BaseAsset? asset, String? remoteId, Siz if (_shouldUseLocalAsset(asset!)) { final id = asset is LocalAsset ? asset.id : (asset as RemoteAsset).localId!; - return LocalThumbProvider(id: id, updatedAt: asset.updatedAt, name: asset.name, size: size); + return LocalThumbProvider(id: id, updatedAt: asset.updatedAt, size: size); } final String assetId; @@ -52,3 +53,25 @@ ImageProvider getThumbnailImageProvider({BaseAsset? asset, String? remoteId, Siz bool _shouldUseLocalAsset(BaseAsset asset) => asset.hasLocal && (!asset.hasRemote || !AppSetting.get(Setting.preferRemoteImage)); + +ImageInfo? getCachedImage(ImageProvider key) { + ImageInfo? thumbnail; + final ImageStreamCompleter? stream = PaintingBinding.instance.imageCache.putIfAbsent( + key, + () => throw Exception(), // don't bother loading if it isn't cached + ); + + if (stream != null) { + void listener(ImageInfo info, bool synchronousCall) { + thumbnail = info; + } + + try { + stream.addListener(ImageStreamListener(listener)); + } finally { + stream.removeListener(ImageStreamListener(listener)); + } + } + + return thumbnail; +} diff --git a/mobile/lib/presentation/widgets/images/local_image_provider.dart b/mobile/lib/presentation/widgets/images/local_image_provider.dart index 350bcbb8fb..4da4b927f1 100644 --- a/mobile/lib/presentation/widgets/images/local_image_provider.dart +++ b/mobile/lib/presentation/widgets/images/local_image_provider.dart @@ -2,15 +2,17 @@ import 'dart:async'; import 'dart:io'; import 'dart:ui'; -import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/setting.model.dart'; import 'package:immich_mobile/domain/services/setting.service.dart'; +import 'package:immich_mobile/extensions/codec_extensions.dart'; import 'package:immich_mobile/infrastructure/repositories/asset_media.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; +import 'package:immich_mobile/presentation/widgets/images/image_provider.dart'; +import 'package:immich_mobile/presentation/widgets/images/one_frame_multi_image_stream_completer.dart'; import 'package:immich_mobile/presentation/widgets/timeline/constants.dart'; import 'package:immich_mobile/providers/image/cache/thumbnail_image_cache_manager.dart'; import 'package:immich_mobile/providers/image/exceptions/image_loading_exception.dart'; @@ -22,14 +24,12 @@ class LocalThumbProvider extends ImageProvider { final String id; final DateTime updatedAt; - final String name; final Size size; const LocalThumbProvider({ required this.id, required this.updatedAt, - required this.name, - this.size = const Size.square(kTimelineFixedTileExtent), + this.size = kThumbnailResolution, this.cacheManager, }); @@ -45,10 +45,8 @@ class LocalThumbProvider extends ImageProvider { codec: _codec(key, cache, decode), scale: 1.0, informationCollector: () => [ - DiagnosticsProperty('Image provider', this), DiagnosticsProperty('Id', key.id), DiagnosticsProperty('Updated at', key.updatedAt), - DiagnosticsProperty('Name', key.name), DiagnosticsProperty('Size', key.size), ], ); @@ -68,7 +66,7 @@ class LocalThumbProvider extends ImageProvider { final thumbnailBytes = await _assetMediaRepository.getThumbnail(key.id, size: key.size); if (thumbnailBytes == null) { PaintingBinding.instance.imageCache.evict(key); - throw StateError("Loading thumb for local photo ${key.name} failed"); + throw StateError("Loading thumb for local photo ${key.id} failed"); } final buffer = await ImmutableBuffer.fromUint8List(thumbnailBytes); @@ -94,11 +92,11 @@ class LocalFullImageProvider extends ImageProvider { final StorageRepository _storageRepository = const StorageRepository(); final String id; - final String name; final Size size; final AssetType type; + final DateTime updatedAt; // temporary, only exists to fetch cached thumbnail until local disk cache is removed - const LocalFullImageProvider({required this.id, required this.name, required this.size, required this.type}); + const LocalFullImageProvider({required this.id, required this.size, required this.type, required this.updatedAt}); @override Future obtainKey(ImageConfiguration configuration) { @@ -107,52 +105,45 @@ class LocalFullImageProvider extends ImageProvider { @override ImageStreamCompleter loadImage(LocalFullImageProvider key, ImageDecoderCallback decode) { - return MultiImageStreamCompleter( - codec: _codec(key, decode), - scale: 1.0, - informationCollector: () sync* { - yield ErrorDescription(name); - }, + return OneFramePlaceholderImageStreamCompleter( + _codec(key, decode), + initialImage: getCachedImage(LocalThumbProvider(id: key.id, updatedAt: key.updatedAt)), + informationCollector: () => [ + DiagnosticsProperty('Id', key.id), + DiagnosticsProperty('Updated at', key.updatedAt), + DiagnosticsProperty('Size', key.size), + ], ); } // Streams in each stage of the image as we ask for it - Stream _codec(LocalFullImageProvider key, ImageDecoderCallback decode) async* { + Stream _codec(LocalFullImageProvider key, ImageDecoderCallback decode) { try { - switch (key.type) { - case AssetType.image: - yield* _decodeProgressive(key, decode); - break; - case AssetType.video: - final codec = await _getThumbnailCodec(key, decode); - if (codec == null) { - throw StateError("Failed to load preview for ${key.name}"); - } - yield codec; - break; - case AssetType.other: - case AssetType.audio: - throw StateError('Unsupported asset type ${key.type}'); - } + return switch (key.type) { + AssetType.image => _decodeProgressive(key, decode), + AssetType.video => _getThumbnailCodec(key, decode), + _ => throw StateError('Unsupported asset type ${key.type}'), + }; } catch (error, stack) { - Logger('ImmichLocalImageProvider').severe('Error loading local image ${key.name}', error, stack); + Logger('ImmichLocalImageProvider').severe('Error loading local image ${key.id}', error, stack); throw const ImageLoadingException('Could not load image from local storage'); } } - Future _getThumbnailCodec(LocalFullImageProvider key, ImageDecoderCallback decode) async { + Stream _getThumbnailCodec(LocalFullImageProvider key, ImageDecoderCallback decode) async* { final thumbBytes = await _assetMediaRepository.getThumbnail(key.id, size: key.size); if (thumbBytes == null) { - return null; + throw StateError("Failed to load preview for ${key.id}"); } final buffer = await ImmutableBuffer.fromUint8List(thumbBytes); - return decode(buffer); + final codec = await decode(buffer); + yield await codec.getImageInfo(); } - Stream _decodeProgressive(LocalFullImageProvider key, ImageDecoderCallback decode) async* { + Stream _decodeProgressive(LocalFullImageProvider key, ImageDecoderCallback decode) async* { final file = await _storageRepository.getFileForAsset(key.id); if (file == null) { - throw StateError("Opening file for asset ${key.name} failed"); + throw StateError("Opening file for asset ${key.id} failed"); } final fileSize = await file.length(); @@ -171,7 +162,8 @@ class LocalFullImageProvider extends ImageProvider { final mediumThumb = await _assetMediaRepository.getThumbnail(key.id, size: size); if (mediumThumb != null) { final mediumBuffer = await ImmutableBuffer.fromUint8List(mediumThumb); - yield await decode(mediumBuffer); + final codec = await decode(mediumBuffer); + yield await codec.getImageInfo(); } } catch (_) {} } @@ -187,24 +179,26 @@ class LocalFullImageProvider extends ImageProvider { final highThumb = await _assetMediaRepository.getThumbnail(key.id, size: size); if (highThumb != null) { final highBuffer = await ImmutableBuffer.fromUint8List(highThumb); - yield await decode(highBuffer); + final codec = await decode(highBuffer); + yield await codec.getImageInfo(); } return; } final buffer = await ImmutableBuffer.fromFilePath(file.path); - yield await decode(buffer); + final codec = await decode(buffer); + yield await codec.getImageInfo(); } @override bool operator ==(Object other) { if (identical(this, other)) return true; if (other is LocalFullImageProvider) { - return id == other.id && size == other.size && type == other.type && name == other.name; + return id == other.id && size == other.size && type == other.type; } return false; } @override - int get hashCode => id.hashCode ^ size.hashCode ^ type.hashCode ^ name.hashCode; + int get hashCode => id.hashCode ^ size.hashCode ^ type.hashCode; } diff --git a/mobile/lib/presentation/widgets/images/one_frame_multi_image_stream_completer.dart b/mobile/lib/presentation/widgets/images/one_frame_multi_image_stream_completer.dart new file mode 100644 index 0000000000..851ed59220 --- /dev/null +++ b/mobile/lib/presentation/widgets/images/one_frame_multi_image_stream_completer.dart @@ -0,0 +1,67 @@ +// The below code is adapted from cached_network_image package's +// MultiImageStreamCompleter to better suit one-frame image loading. +// In particular, it allows providing an initial image to emit synchronously. + +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/painting.dart'; + +/// An ImageStreamCompleter with support for loading multiple images. +class OneFramePlaceholderImageStreamCompleter extends ImageStreamCompleter { + ImageInfo? _initialImage; + + /// The constructor to create an OneFramePlaceholderImageStreamCompleter. The [images] + /// should be the primary images to display (typically asynchronously as they load). + /// The [initialImage] is an optional image that will be emitted synchronously + /// until the first stream image is completed, useful as a thumbnail or placeholder. + OneFramePlaceholderImageStreamCompleter( + Stream images, { + ImageInfo? initialImage, + InformationCollector? informationCollector, + }) { + _initialImage = initialImage; + images.listen( + _onImage, + onError: (Object error, StackTrace stack) { + reportError( + context: ErrorDescription('resolving a single-frame image stream'), + exception: error, + stack: stack, + informationCollector: informationCollector, + silent: true, + ); + }, + ); + } + + void _onImage(ImageInfo image) { + setImage(image); + _initialImage?.dispose(); + _initialImage = null; + } + + @override + void addListener(ImageStreamListener listener) { + final initialImage = _initialImage; + if (initialImage != null) { + try { + listener.onImage(initialImage.clone(), true); + } catch (exception, stack) { + reportError( + context: ErrorDescription('by a synchronously-called image listener'), + exception: exception, + stack: stack, + ); + } + } + super.addListener(listener); + } + + @override + void onDisposed() { + _initialImage?.dispose(); + _initialImage = null; + super.onDisposed(); + } +} diff --git a/mobile/lib/presentation/widgets/images/remote_image_provider.dart b/mobile/lib/presentation/widgets/images/remote_image_provider.dart index 27f310f4f2..71c5ad446e 100644 --- a/mobile/lib/presentation/widgets/images/remote_image_provider.dart +++ b/mobile/lib/presentation/widgets/images/remote_image_provider.dart @@ -1,12 +1,14 @@ import 'dart:async'; import 'dart:ui'; -import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/painting.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:immich_mobile/domain/models/setting.model.dart'; import 'package:immich_mobile/domain/services/setting.service.dart'; +import 'package:immich_mobile/extensions/codec_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/images/image_provider.dart'; +import 'package:immich_mobile/presentation/widgets/images/one_frame_multi_image_stream_completer.dart'; import 'package:immich_mobile/providers/image/cache/image_loader.dart'; import 'package:immich_mobile/providers/image/cache/remote_image_cache_manager.dart'; import 'package:immich_mobile/utils/image_url_builder.dart'; @@ -81,36 +83,28 @@ class RemoteFullImageProvider extends ImageProvider { @override ImageStreamCompleter loadImage(RemoteFullImageProvider key, ImageDecoderCallback decode) { final cache = cacheManager ?? RemoteImageCacheManager(); - final chunkEvents = StreamController(); - return MultiImageStreamCompleter( - codec: _codec(key, cache, decode, chunkEvents), - scale: 1.0, - chunkEvents: chunkEvents.stream, + return OneFramePlaceholderImageStreamCompleter( + _codec(key, cache, decode), + initialImage: getCachedImage(RemoteThumbProvider(assetId: key.assetId)), ); } - Stream _codec( - RemoteFullImageProvider key, - CacheManager cache, - ImageDecoderCallback decode, - StreamController chunkController, - ) async* { - yield await ImageLoader.loadImageFromCache( + Stream _codec(RemoteFullImageProvider key, CacheManager cache, ImageDecoderCallback decode) async* { + final codec = await ImageLoader.loadImageFromCache( getPreviewUrlForRemoteId(key.assetId), cache: cache, decode: decode, - chunkEvents: chunkController, ); + yield await codec.getImageInfo(); if (AppSetting.get(Setting.loadOriginal)) { - yield await ImageLoader.loadImageFromCache( + final codec = await ImageLoader.loadImageFromCache( getOriginalUrlForRemoteId(key.assetId), cache: cache, decode: decode, - chunkEvents: chunkController, ); + yield await codec.getImageInfo(); } - await chunkController.close(); } @override diff --git a/mobile/lib/presentation/widgets/images/thumbnail.widget.dart b/mobile/lib/presentation/widgets/images/thumbnail.widget.dart index 8335bd406b..9965c1bfd5 100644 --- a/mobile/lib/presentation/widgets/images/thumbnail.widget.dart +++ b/mobile/lib/presentation/widgets/images/thumbnail.widget.dart @@ -19,7 +19,7 @@ class Thumbnail extends StatelessWidget { @override Widget build(BuildContext context) { final thumbHash = asset is RemoteAsset ? (asset as RemoteAsset).thumbHash : null; - final provider = getThumbnailImageProvider(asset: asset, remoteId: remoteId, size: size); + final provider = getThumbnailImageProvider(asset: asset, remoteId: remoteId); return OctoImage.fromSet( image: provider, diff --git a/mobile/lib/presentation/widgets/timeline/constants.dart b/mobile/lib/presentation/widgets/timeline/constants.dart index fb9034f179..e3bb5fe273 100644 --- a/mobile/lib/presentation/widgets/timeline/constants.dart +++ b/mobile/lib/presentation/widgets/timeline/constants.dart @@ -1,5 +1,8 @@ +import 'dart:ui'; + const double kTimelineHeaderExtent = 80.0; -const double kTimelineFixedTileExtent = 256; +const Size kTimelineFixedTileExtent = Size.square(256); +const Size kThumbnailResolution = kTimelineFixedTileExtent; const double kTimelineSpacing = 2.0; const int kTimelineColumnCount = 3; diff --git a/mobile/lib/presentation/widgets/timeline/segment_builder.dart b/mobile/lib/presentation/widgets/timeline/segment_builder.dart index c80595a446..79ffb47e95 100644 --- a/mobile/lib/presentation/widgets/timeline/segment_builder.dart +++ b/mobile/lib/presentation/widgets/timeline/segment_builder.dart @@ -21,7 +21,7 @@ abstract class SegmentBuilder { static Widget buildPlaceholder( BuildContext context, int count, { - Size size = const Size.square(kTimelineFixedTileExtent), + Size size = kTimelineFixedTileExtent, double spacing = kTimelineSpacing, }) => RepaintBoundary( child: FixedTimelineRow( From 7a7843467c6c3a92f569c1ddc09d6421137639b3 Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Tue, 5 Aug 2025 10:20:55 -0500 Subject: [PATCH 173/748] feat(mobile): remove from album in asset viewer bar (#20672) * feat: remove from album in asset viewer bar * chore: move button to bottom bar instead of bottom sheet * move back to bottom sheet --------- Co-authored-by: Alex --- .../move_to_lock_folder_action_button.widget.dart | 2 +- .../remove_from_album_action_button.widget.dart | 1 + .../widgets/asset_viewer/bottom_sheet.widget.dart | 5 ++++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/mobile/lib/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart index 0fde43b459..d1ea85bcc4 100644 --- a/mobile/lib/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart @@ -45,7 +45,7 @@ class MoveToLockFolderActionButton extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return BaseActionButton( - maxWidth: 100.0, + maxWidth: 110.0, iconData: Icons.lock_outline_rounded, label: "move_to_locked_folder".t(context: context), onPressed: () => _onTap(context, ref), diff --git a/mobile/lib/presentation/widgets/action_buttons/remove_from_album_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/remove_from_album_action_button.widget.dart index a1f6f7e7d1..2ce8b345a1 100644 --- a/mobile/lib/presentation/widgets/action_buttons/remove_from_album_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/remove_from_album_action_button.widget.dart @@ -43,6 +43,7 @@ class RemoveFromAlbumActionButton extends ConsumerWidget { iconData: Icons.remove_circle_outline, label: "remove_from_album".t(context: context), onPressed: () => _onTap(context, ref), + maxWidth: 100, ); } } diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart index 1d76d3c39d..6baac82889 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart @@ -12,6 +12,7 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permane import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/remove_from_album_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/share_link_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_button.widget.dart'; @@ -21,6 +22,7 @@ import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet/she import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; import 'package:immich_mobile/providers/routes.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/utils/bytes_units.dart'; @@ -42,8 +44,8 @@ class AssetDetailBottomSheet extends ConsumerWidget { } final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); - final isInLockedView = ref.watch(inLockedViewProvider); + final currentAlbum = ref.watch(currentRemoteAlbumProvider); final actions = [ const ShareActionButton(source: ActionSource.viewer), @@ -61,6 +63,7 @@ class AssetDetailBottomSheet extends ConsumerWidget { const DeleteLocalActionButton(source: ActionSource.viewer), const UploadActionButton(source: ActionSource.timeline), ], + if (currentAlbum != null) RemoveFromAlbumActionButton(albumId: currentAlbum.id, source: ActionSource.viewer), ]; final lockedViewActions = []; From 13d43e193e9fdb69428d8eece3c31d2cd6f2ca5f Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Tue, 5 Aug 2025 10:29:27 -0500 Subject: [PATCH 174/748] feat(mobile): use custom headers when connecting in widget (#20666) * feat(mobile): use custom headers when connecting in widget * delete log in android widget * chore: code review changes --- .../app/alextran/immich/widget/ImmichAPI.kt | 21 +++++++++++++- .../app/alextran/immich/widget/model/Model.kt | 6 +++- mobile/ios/WidgetExtension/ImmichAPI.swift | 28 +++++++++++++++++-- mobile/lib/constants/constants.dart | 3 +- mobile/lib/providers/auth.provider.dart | 4 ++- mobile/lib/services/widget.service.dart | 4 ++- 6 files changed, 58 insertions(+), 8 deletions(-) diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImmichAPI.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImmichAPI.kt index 42f5fb4b1b..54ccb1ddfb 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImmichAPI.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImmichAPI.kt @@ -24,14 +24,23 @@ class ImmichAPI(cfg: ServerConfig) { val serverURL = prefs.getString("widget_server_url", "") ?: "" val sessionKey = prefs.getString("widget_auth_token", "") ?: "" + val customHeadersJSON = prefs.getString("widget_custom_headers", "") ?: "" if (serverURL.isBlank() || sessionKey.isBlank()) { return null } + var customHeaders: Map = HashMap() + + if (customHeadersJSON.isNotBlank()) { + val stringMapType = object : TypeToken>() {}.type + customHeaders = Gson().fromJson(customHeadersJSON, stringMapType) + } + return ServerConfig( serverURL, - sessionKey + sessionKey, + customHeaders ) } } @@ -50,11 +59,19 @@ class ImmichAPI(cfg: ServerConfig) { return URL(urlString.toString()) } + private fun HttpURLConnection.applyCustomHeaders() { + serverConfig.customHeaders.forEach { (key, value) -> + setRequestProperty(key, value) + } + } + suspend fun fetchSearchResults(filters: SearchFilters): List = withContext(Dispatchers.IO) { val url = buildRequestURL("/search/random") val connection = (url.openConnection() as HttpURLConnection).apply { requestMethod = "POST" setRequestProperty("Content-Type", "application/json") + applyCustomHeaders() + doOutput = true } @@ -75,6 +92,7 @@ class ImmichAPI(cfg: ServerConfig) { val url = buildRequestURL("/memories", listOf("for" to iso8601)) val connection = (url.openConnection() as HttpURLConnection).apply { requestMethod = "GET" + applyCustomHeaders() } val response = connection.inputStream.bufferedReader().readText() @@ -94,6 +112,7 @@ class ImmichAPI(cfg: ServerConfig) { val url = buildRequestURL("/albums") val connection = (url.openConnection() as HttpURLConnection).apply { requestMethod = "GET" + applyCustomHeaders() } val response = connection.inputStream.bufferedReader().readText() diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/model/Model.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/model/Model.kt index 9595a3b696..545a1edc59 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/model/Model.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/model/Model.kt @@ -55,7 +55,11 @@ data class WidgetEntry ( val deeplink: String? ) -data class ServerConfig(val serverEndpoint: String, val sessionKey: String) +data class ServerConfig( + val serverEndpoint: String, + val sessionKey: String, + val customHeaders: Map +) // MARK: Widget State Keys val kImageUUID = stringPreferencesKey("uuid") diff --git a/mobile/ios/WidgetExtension/ImmichAPI.swift b/mobile/ios/WidgetExtension/ImmichAPI.swift index 36758b824c..19ff3d38ba 100644 --- a/mobile/ios/WidgetExtension/ImmichAPI.swift +++ b/mobile/ios/WidgetExtension/ImmichAPI.swift @@ -104,10 +104,13 @@ struct Album: Codable, Equatable { // MARK: API class ImmichAPI { + typealias CustomHeaders = [String:String] struct ServerConfig { let serverEndpoint: String let sessionKey: String + let customHeaders: CustomHeaders } + let serverConfig: ServerConfig init() async throws { @@ -122,10 +125,20 @@ class ImmichAPI { if serverURL == "" || sessionKey == "" { throw WidgetError.noLogin } + + // custom headers come in the form of KV pairs in JSON + var customHeadersJSON = (defaults.string(forKey: "widget_custom_headers") ?? "") + var customHeaders: CustomHeaders = [:] + + if customHeadersJSON != "", + let parsedHeaders = try? JSONDecoder().decode(CustomHeaders.self, from: customHeadersJSON.data(using: .utf8)!) { + customHeaders = parsedHeaders + } serverConfig = ServerConfig( serverEndpoint: serverURL, - sessionKey: sessionKey + sessionKey: sessionKey, + customHeaders: customHeaders ) } @@ -155,6 +168,12 @@ class ImmichAPI { return components?.url } + + func applyCustomHeaders(for request: inout URLRequest) { + for (header, value) in serverConfig.customHeaders { + request.addValue(value, forHTTPHeaderField: header) + } + } func fetchSearchResults(with filters: SearchFilter = Album.NONE.filter) async throws @@ -174,7 +193,8 @@ class ImmichAPI { request.httpMethod = "POST" request.httpBody = try JSONEncoder().encode(filters) request.setValue("application/json", forHTTPHeaderField: "Content-Type") - + applyCustomHeaders(for: &request) + let (data, _) = try await URLSession.shared.data(for: request) // decode data @@ -196,6 +216,7 @@ class ImmichAPI { var request = URLRequest(url: searchURL) request.httpMethod = "GET" + applyCustomHeaders(for: &request) let (data, _) = try await URLSession.shared.data(for: request) @@ -254,7 +275,8 @@ class ImmichAPI { var request = URLRequest(url: searchURL) request.httpMethod = "GET" - + applyCustomHeaders(for: &request) + let (data, _) = try await URLSession.shared.data(for: request) // decode data diff --git a/mobile/lib/constants/constants.dart b/mobile/lib/constants/constants.dart index b3d9d138c4..d984b468d7 100644 --- a/mobile/lib/constants/constants.dart +++ b/mobile/lib/constants/constants.dart @@ -30,9 +30,10 @@ const int kTimelineAssetLoadBatchSize = 256; const int kTimelineAssetLoadOppositeSize = 64; // Widget keys +const String appShareGroupId = "group.app.immich.share"; const String kWidgetAuthToken = "widget_auth_token"; const String kWidgetServerEndpoint = "widget_server_url"; -const String appShareGroupId = "group.app.immich.share"; +const String kWidgetCustomHeaders = "widget_custom_headers"; // add widget identifiers here for new widgets // these are used to force a widget refresh diff --git a/mobile/lib/providers/auth.provider.dart b/mobile/lib/providers/auth.provider.dart index 02f7920d6f..017b9a454e 100644 --- a/mobile/lib/providers/auth.provider.dart +++ b/mobile/lib/providers/auth.provider.dart @@ -121,7 +121,9 @@ class AuthNotifier extends StateNotifier { Future saveAuthInfo({required String accessToken}) async { await _apiService.setAccessToken(accessToken); - await _widgetService.writeCredentials(Store.get(StoreKey.serverEndpoint), accessToken); + final serverEndpoint = Store.get(StoreKey.serverEndpoint); + final customHeaders = Store.get(StoreKey.customHeaders); + await _widgetService.writeCredentials(serverEndpoint, accessToken, customHeaders); // Get the deviceid from the store if it exists, otherwise generate a new one String deviceId = Store.tryGet(StoreKey.deviceId) ?? await FlutterUdid.consistentUdid; diff --git a/mobile/lib/services/widget.service.dart b/mobile/lib/services/widget.service.dart index 8c5d21b389..4ff18b56e6 100644 --- a/mobile/lib/services/widget.service.dart +++ b/mobile/lib/services/widget.service.dart @@ -11,10 +11,11 @@ class WidgetService { const WidgetService(this._repository); - Future writeCredentials(String serverURL, String sessionKey) async { + Future writeCredentials(String serverURL, String sessionKey, String customHeaders) async { await _repository.setAppGroupId(appShareGroupId); await _repository.saveData(kWidgetServerEndpoint, serverURL); await _repository.saveData(kWidgetAuthToken, sessionKey); + await _repository.saveData(kWidgetCustomHeaders, customHeaders); // wait 3 seconds to ensure the widget is updated, dont block Future.delayed(const Duration(seconds: 3), refreshWidgets); @@ -24,6 +25,7 @@ class WidgetService { await _repository.setAppGroupId(appShareGroupId); await _repository.saveData(kWidgetServerEndpoint, ""); await _repository.saveData(kWidgetAuthToken, ""); + await _repository.saveData(kWidgetCustomHeaders, ""); // wait 3 seconds to ensure the widget is updated, dont block Future.delayed(const Duration(seconds: 3), refreshWidgets); From 7118dca5598b010ab9447b7d03ca784c00eba9a0 Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Tue, 5 Aug 2025 10:31:58 -0500 Subject: [PATCH 175/748] feat(mobile): album shared user editing (#20671) * feat(mobile): album shared user editing * fix: album leaving * i18n and options button --------- Co-authored-by: Alex --- i18n/en.json | 1 + .../domain/services/remote_album.service.dart | 12 + .../repositories/remote_album.repository.dart | 10 + .../pages/drift_album_options.page.dart | 237 ++++++++++++++++++ .../pages/drift_remote_album.page.dart | 4 + .../drift_album_option.widget.dart | 8 + .../infrastructure/remote_album.provider.dart | 17 ++ .../drift_album_api_repository.dart | 9 + mobile/lib/routing/router.dart | 8 +- mobile/lib/routing/router.gr.dart | 16 ++ .../album/remote_album_shared_user_icons.dart | 27 +- 11 files changed, 335 insertions(+), 14 deletions(-) create mode 100644 mobile/lib/presentation/pages/drift_album_options.page.dart diff --git a/i18n/en.json b/i18n/en.json index d5d02db7d3..3c730b35eb 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1174,6 +1174,7 @@ "latest_version": "Latest Version", "latitude": "Latitude", "leave": "Leave", + "leave_album": "Leave album", "lens_model": "Lens model", "let_others_respond": "Let others respond", "level": "Level", diff --git a/mobile/lib/domain/services/remote_album.service.dart b/mobile/lib/domain/services/remote_album.service.dart index f6c596f24a..6c5d974485 100644 --- a/mobile/lib/domain/services/remote_album.service.dart +++ b/mobile/lib/domain/services/remote_album.service.dart @@ -128,6 +128,18 @@ class RemoteAlbumService { return _repository.addUsers(albumId, userIds); } + Future removeUser(String albumId, {required String userId}) async { + await _albumApiRepository.removeUser(albumId, userId: userId); + + return _repository.removeUser(albumId, userId: userId); + } + + Future setActivityStatus(String albumId, bool enabled) async { + await _albumApiRepository.setActivityStatus(albumId, enabled); + + return _repository.setActivityStatus(albumId, enabled); + } + Future getCount() { return _repository.getCount(); } diff --git a/mobile/lib/infrastructure/repositories/remote_album.repository.dart b/mobile/lib/infrastructure/repositories/remote_album.repository.dart index 6bc6a7066d..acb1eddda5 100644 --- a/mobile/lib/infrastructure/repositories/remote_album.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_album.repository.dart @@ -220,12 +220,22 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { }); } + Future removeUser(String albumId, {required String userId}) { + return _db.remoteAlbumUserEntity.deleteWhere((row) => row.albumId.equals(albumId) & row.userId.equals(userId)); + } + Future deleteAlbum(String albumId) async { return _db.transaction(() async { await _db.remoteAlbumEntity.deleteWhere((table) => table.id.equals(albumId)); }); } + Future setActivityStatus(String albumId, bool isEnabled) async { + final query = _db.update(_db.remoteAlbumEntity)..where((row) => row.id.equals(albumId)); + + await query.write(RemoteAlbumEntityCompanion(isActivityEnabled: Value(isEnabled))); + } + Stream watchAlbum(String albumId) { final query = _db.remoteAlbumEntity.select().join([ diff --git a/mobile/lib/presentation/pages/drift_album_options.page.dart b/mobile/lib/presentation/pages/drift_album_options.page.dart new file mode 100644 index 0000000000..b8dcbd91f9 --- /dev/null +++ b/mobile/lib/presentation/pages/drift_album_options.page.dart @@ -0,0 +1,237 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/theme_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/pages/drift_user_selection.page.dart'; +import 'package:immich_mobile/providers/auth.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/remote_album.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; +import 'package:immich_mobile/widgets/common/user_circle_avatar.dart'; + +@RoutePage() +class DriftAlbumOptionsPage extends HookConsumerWidget { + const DriftAlbumOptionsPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final album = ref.watch(currentRemoteAlbumProvider); + if (album == null) { + return const SizedBox(); + } + + final sharedUsersAsync = ref.watch(remoteAlbumSharedUsersProvider(album.id)); + final userId = ref.watch(authProvider).userId; + final activityEnabled = useState(album.isActivityEnabled); + final isOwner = album.ownerId == userId; + + void showErrorMessage() { + context.pop(); + ImmichToast.show( + context: context, + msg: "shared_album_section_people_action_error".t(context: context), + toastType: ToastType.error, + gravity: ToastGravity.BOTTOM, + ); + } + + void leaveAlbum() async { + try { + await ref.read(remoteAlbumProvider.notifier).leaveAlbum(album.id, userId: userId); + context.navigateTo(const DriftAlbumsRoute()); + } catch (_) { + showErrorMessage(); + } + } + + void removeUserFromAlbum(UserDto user) async { + try { + await ref.read(remoteAlbumProvider.notifier).removeUser(album.id, user.id); + ref.invalidate(remoteAlbumSharedUsersProvider(album.id)); + } catch (_) { + showErrorMessage(); + } + + context.pop(); + } + + Future addUsers() async { + final newUsers = await context.pushRoute>(DriftUserSelectionRoute(album: album)); + + if (newUsers == null || newUsers.isEmpty) { + return; + } + + try { + await ref.read(remoteAlbumProvider.notifier).addUsers(album.id, newUsers); + + if (newUsers.isNotEmpty) { + ImmichToast.show( + context: context, + msg: "users_added_to_album_count".t(context: context, args: {'count': newUsers.length}), + toastType: ToastType.success, + ); + } + + ref.invalidate(remoteAlbumSharedUsersProvider(album.id)); + } catch (e) { + ImmichToast.show( + context: context, + msg: "Failed to add users to album: ${e.toString()}", + toastType: ToastType.error, + ); + } + } + + void handleUserClick(UserDto user) { + var actions = []; + + if (user.id == userId) { + actions = [ + ListTile( + leading: const Icon(Icons.exit_to_app_rounded), + title: const Text("leave_album").t(context: context), + onTap: leaveAlbum, + ), + ]; + } + + if (isOwner) { + actions = [ + ListTile( + leading: const Icon(Icons.person_remove_rounded), + title: const Text("remove_user").t(context: context), + onTap: () => removeUserFromAlbum(user), + ), + ]; + } + + showModalBottomSheet( + backgroundColor: context.colorScheme.surfaceContainer, + isScrollControlled: false, + context: context, + builder: (context) { + return SafeArea( + child: Padding( + padding: const EdgeInsets.only(top: 24.0), + child: Column(mainAxisSize: MainAxisSize.min, children: [...actions]), + ), + ); + }, + ); + } + + buildOwnerInfo() { + if (isOwner) { + final owner = ref.watch(currentUserProvider); + return ListTile( + leading: owner != null ? UserCircleAvatar(user: owner) : const SizedBox(), + title: Text(album.ownerName, style: const TextStyle(fontWeight: FontWeight.w500)), + subtitle: Text(owner?.email ?? "", style: TextStyle(color: context.colorScheme.onSurfaceSecondary)), + trailing: Text("owner", style: context.textTheme.labelLarge).t(context: context), + ); + } else { + final usersProvider = ref.watch(driftUsersProvider); + return usersProvider.maybeWhen( + data: (users) { + final user = users.firstWhereOrNull((u) => u.id == album.ownerId); + + if (user == null) { + return const SizedBox(); + } + + return ListTile( + leading: UserCircleAvatar(user: user, radius: 22), + title: Text(user.name, style: const TextStyle(fontWeight: FontWeight.w500)), + subtitle: Text(user.email, style: TextStyle(color: context.colorScheme.onSurfaceSecondary)), + trailing: Text("owner", style: context.textTheme.labelLarge).t(context: context), + ); + }, + orElse: () => const SizedBox(), + ); + } + } + + buildSharedUsersList() { + return sharedUsersAsync.maybeWhen( + data: (sharedUsers) => ListView.builder( + primary: false, + shrinkWrap: true, + itemCount: sharedUsers.length, + itemBuilder: (context, index) { + final user = sharedUsers[index]; + return ListTile( + leading: UserCircleAvatar(user: user, radius: 22), + title: Text(user.name, style: const TextStyle(fontWeight: FontWeight.w500)), + subtitle: Text(user.email, style: TextStyle(color: context.colorScheme.onSurfaceSecondary)), + trailing: userId == user.id || isOwner ? const Icon(Icons.more_horiz_rounded) : const SizedBox(), + onTap: userId == user.id || isOwner ? () => handleUserClick(user) : null, + ); + }, + ), + orElse: () => const Center(child: CircularProgressIndicator()), + ); + } + + buildSectionTitle(String text) { + return Padding( + padding: const EdgeInsets.all(16.0), + child: Text(text, style: context.textTheme.bodySmall), + ); + } + + return Scaffold( + appBar: AppBar( + leading: IconButton( + icon: const Icon(Icons.arrow_back_ios_new_rounded), + onPressed: () => context.maybePop(null), + ), + centerTitle: true, + title: Text("options".t(context: context)), + ), + body: ListView( + children: [ + const SizedBox(height: 8), + if (isOwner) + SwitchListTile.adaptive( + value: activityEnabled.value, + onChanged: (bool value) async { + activityEnabled.value = value; + await ref.read(remoteAlbumProvider.notifier).setActivityStatus(album.id, value); + }, + activeColor: activityEnabled.value ? context.primaryColor : context.themeData.disabledColor, + dense: true, + title: Text( + "comments_and_likes", + style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500), + ).t(context: context), + subtitle: Text( + "let_others_respond", + style: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.onSurfaceSecondary), + ).t(context: context), + ), + buildSectionTitle("shared_album_section_people_title".t(context: context)), + if (isOwner) ...[ + ListTile( + leading: const Icon(Icons.person_add_rounded), + title: Text("invite_people".t(context: context)), + onTap: () async => addUsers(), + ), + const Divider(indent: 16), + ], + buildOwnerInfo(), + buildSharedUsersList(), + ], + ), + ); + } +} diff --git a/mobile/lib/presentation/pages/drift_remote_album.page.dart b/mobile/lib/presentation/pages/drift_remote_album.page.dart index 5922841607..9d060e51d0 100644 --- a/mobile/lib/presentation/pages/drift_remote_album.page.dart +++ b/mobile/lib/presentation/pages/drift_remote_album.page.dart @@ -204,6 +204,10 @@ class _RemoteAlbumPageState extends ConsumerState { context.pop(); context.pushRoute(SharedLinkEditRoute(albumId: _album.id)); }, + onShowOptions: () { + context.pop(); + context.pushRoute(const DriftAlbumOptionsRoute()); + }, ); }, ); diff --git a/mobile/lib/presentation/widgets/remote_album/drift_album_option.widget.dart b/mobile/lib/presentation/widgets/remote_album/drift_album_option.widget.dart index c08348e2af..b82d951b68 100644 --- a/mobile/lib/presentation/widgets/remote_album/drift_album_option.widget.dart +++ b/mobile/lib/presentation/widgets/remote_album/drift_album_option.widget.dart @@ -13,6 +13,7 @@ class DriftRemoteAlbumOption extends ConsumerWidget { this.onCreateSharedLink, this.onToggleAlbumOrder, this.onEditAlbum, + this.onShowOptions, }); final VoidCallback? onAddPhotos; @@ -22,6 +23,7 @@ class DriftRemoteAlbumOption extends ConsumerWidget { final VoidCallback? onCreateSharedLink; final VoidCallback? onToggleAlbumOrder; final VoidCallback? onEditAlbum; + final VoidCallback? onShowOptions; @override Widget build(BuildContext context, WidgetRef ref) { @@ -69,6 +71,12 @@ class DriftRemoteAlbumOption extends ConsumerWidget { title: Text('create_shared_link'.t(context: context), style: textStyle), onTap: onCreateSharedLink, ), + if (onShowOptions != null) + ListTile( + leading: const Icon(Icons.settings), + title: Text('options'.t(context: context), style: textStyle), + onTap: onShowOptions, + ), if (onDeleteAlbum != null) ...[ const Divider(indent: 16, endIndent: 16), ListTile( diff --git a/mobile/lib/providers/infrastructure/remote_album.provider.dart b/mobile/lib/providers/infrastructure/remote_album.provider.dart index ca7735808c..a9c1b88a15 100644 --- a/mobile/lib/providers/infrastructure/remote_album.provider.dart +++ b/mobile/lib/providers/infrastructure/remote_album.provider.dart @@ -156,6 +156,23 @@ class RemoteAlbumNotifier extends Notifier { Future addUsers(String albumId, List userIds) { return _remoteAlbumService.addUsers(albumId: albumId, userIds: userIds); } + + Future removeUser(String albumId, String userId) { + return _remoteAlbumService.removeUser(albumId, userId: userId); + } + + Future leaveAlbum(String albumId, {required String userId}) async { + await _remoteAlbumService.removeUser(albumId, userId: userId); + + final updatedAlbums = state.albums.where((album) => album.id != albumId).toList(); + final updatedFilteredAlbums = state.filteredAlbums.where((album) => album.id != albumId).toList(); + + state = state.copyWith(albums: updatedAlbums, filteredAlbums: updatedFilteredAlbums); + } + + Future setActivityStatus(String albumId, bool enabled) { + return _remoteAlbumService.setActivityStatus(albumId, enabled); + } } final remoteAlbumDateRangeProvider = FutureProvider.family<(DateTime, DateTime), String>((ref, albumId) async { diff --git a/mobile/lib/repositories/drift_album_api_repository.dart b/mobile/lib/repositories/drift_album_api_repository.dart index 6de025fb47..10d8a54e72 100644 --- a/mobile/lib/repositories/drift_album_api_repository.dart +++ b/mobile/lib/repositories/drift_album_api_repository.dart @@ -87,6 +87,15 @@ class DriftAlbumApiRepository extends ApiRepository { final response = await checkNull(_api.addUsersToAlbum(albumId, AddUsersDto(albumUsers: albumUsers))); return response.toRemoteAlbum(); } + + Future removeUser(String albumId, {required String userId}) async { + await _api.removeUserFromAlbum(albumId, userId); + } + + Future setActivityStatus(String albumId, bool isEnabled) async { + final response = await checkNull(_api.updateAlbumInfo(albumId, UpdateAlbumDto(isActivityEnabled: isEnabled))); + return response.isActivityEnabled; + } } extension on AlbumResponseDto { diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index 4fe1673893..e75da235df 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -23,11 +23,11 @@ import 'package:immich_mobile/pages/album/album_shared_user_selection.page.dart' import 'package:immich_mobile/pages/album/album_viewer.page.dart'; import 'package:immich_mobile/pages/albums/albums.page.dart'; import 'package:immich_mobile/pages/backup/album_preview.page.dart'; -import 'package:immich_mobile/pages/backup/drift_backup_album_selection.page.dart'; -import 'package:immich_mobile/pages/backup/drift_backup.page.dart'; import 'package:immich_mobile/pages/backup/backup_album_selection.page.dart'; import 'package:immich_mobile/pages/backup/backup_controller.page.dart'; import 'package:immich_mobile/pages/backup/backup_options.page.dart'; +import 'package:immich_mobile/pages/backup/drift_backup.page.dart'; +import 'package:immich_mobile/pages/backup/drift_backup_album_selection.page.dart'; import 'package:immich_mobile/pages/backup/drift_backup_options.page.dart'; import 'package:immich_mobile/pages/backup/drift_upload_detail.page.dart'; import 'package:immich_mobile/pages/backup/failed_backup_status.page.dart'; @@ -81,6 +81,7 @@ import 'package:immich_mobile/presentation/pages/dev/feat_in_development.page.da import 'package:immich_mobile/presentation/pages/dev/main_timeline.page.dart'; import 'package:immich_mobile/presentation/pages/dev/media_stat.page.dart'; import 'package:immich_mobile/presentation/pages/drift_album.page.dart'; +import 'package:immich_mobile/presentation/pages/drift_album_options.page.dart'; import 'package:immich_mobile/presentation/pages/drift_archive.page.dart'; import 'package:immich_mobile/presentation/pages/drift_asset_selection_timeline.page.dart'; import 'package:immich_mobile/presentation/pages/drift_create_album.page.dart'; @@ -95,9 +96,9 @@ import 'package:immich_mobile/presentation/pages/drift_person.page.dart'; import 'package:immich_mobile/presentation/pages/drift_place.page.dart'; import 'package:immich_mobile/presentation/pages/drift_place_detail.page.dart'; import 'package:immich_mobile/presentation/pages/drift_recently_taken.page.dart'; -import 'package:immich_mobile/presentation/pages/drift_user_selection.page.dart'; import 'package:immich_mobile/presentation/pages/drift_remote_album.page.dart'; import 'package:immich_mobile/presentation/pages/drift_trash.page.dart'; +import 'package:immich_mobile/presentation/pages/drift_user_selection.page.dart'; import 'package:immich_mobile/presentation/pages/drift_video.page.dart'; import 'package:immich_mobile/presentation/pages/local_timeline.page.dart'; import 'package:immich_mobile/presentation/pages/search/drift_search.page.dart'; @@ -329,6 +330,7 @@ class AppRouter extends RootStackRouter { AutoRoute(page: DriftPeopleCollectionRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: DriftPersonRoute.page, guards: [_authGuard]), AutoRoute(page: DriftBackupOptionsRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftAlbumOptionsRoute.page, guards: [_authGuard, _duplicateGuard]), // required to handle all deeplinks in deep_link.service.dart // auto_route_library#1722 RedirectRoute(path: '*', redirectTo: '/'), diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index e8f0dd8b1f..f5b3728ffe 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -667,6 +667,22 @@ class CropImageRouteArgs { } } +/// generated route for +/// [DriftAlbumOptionsPage] +class DriftAlbumOptionsRoute extends PageRouteInfo { + const DriftAlbumOptionsRoute({List? children}) + : super(DriftAlbumOptionsRoute.name, initialChildren: children); + + static const String name = 'DriftAlbumOptionsRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftAlbumOptionsPage(); + }, + ); +} + /// generated route for /// [DriftAlbumsPage] class DriftAlbumsRoute extends PageRouteInfo { diff --git a/mobile/lib/widgets/album/remote_album_shared_user_icons.dart b/mobile/lib/widgets/album/remote_album_shared_user_icons.dart index 7be5db1798..9f88b23f92 100644 --- a/mobile/lib/widgets/album/remote_album_shared_user_icons.dart +++ b/mobile/lib/widgets/album/remote_album_shared_user_icons.dart @@ -1,7 +1,9 @@ +import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/remote_album.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/widgets/common/user_circle_avatar.dart'; class RemoteAlbumSharedUserIcons extends ConsumerWidget { @@ -22,17 +24,20 @@ class RemoteAlbumSharedUserIcons extends ConsumerWidget { return const SizedBox(); } - return SizedBox( - height: 50, - child: ListView.builder( - scrollDirection: Axis.horizontal, - itemBuilder: ((context, index) { - return Padding( - padding: const EdgeInsets.only(right: 4.0), - child: UserCircleAvatar(user: sharedUsers[index], radius: 18, size: 36, hasBorder: true), - ); - }), - itemCount: sharedUsers.length, + return GestureDetector( + onTap: () => context.pushRoute(const DriftAlbumOptionsRoute()), + child: SizedBox( + height: 50, + child: ListView.builder( + scrollDirection: Axis.horizontal, + itemBuilder: ((context, index) { + return Padding( + padding: const EdgeInsets.only(right: 4.0), + child: UserCircleAvatar(user: sharedUsers[index], radius: 18, size: 36, hasBorder: true), + ); + }), + itemCount: sharedUsers.length, + ), ), ); }, From a573a23c83e527d06cb8be408208e5a4663fd644 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 5 Aug 2025 11:14:21 -0500 Subject: [PATCH 176/748] fix: empty custom header prevent logging in (#20693) --- mobile/lib/providers/auth.provider.dart | 2 +- mobile/lib/services/widget.service.dart | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/mobile/lib/providers/auth.provider.dart b/mobile/lib/providers/auth.provider.dart index 017b9a454e..6c39eb0dec 100644 --- a/mobile/lib/providers/auth.provider.dart +++ b/mobile/lib/providers/auth.provider.dart @@ -122,7 +122,7 @@ class AuthNotifier extends StateNotifier { await _apiService.setAccessToken(accessToken); final serverEndpoint = Store.get(StoreKey.serverEndpoint); - final customHeaders = Store.get(StoreKey.customHeaders); + final customHeaders = Store.tryGet(StoreKey.customHeaders); await _widgetService.writeCredentials(serverEndpoint, accessToken, customHeaders); // Get the deviceid from the store if it exists, otherwise generate a new one diff --git a/mobile/lib/services/widget.service.dart b/mobile/lib/services/widget.service.dart index 4ff18b56e6..23ec0aa770 100644 --- a/mobile/lib/services/widget.service.dart +++ b/mobile/lib/services/widget.service.dart @@ -11,11 +11,14 @@ class WidgetService { const WidgetService(this._repository); - Future writeCredentials(String serverURL, String sessionKey, String customHeaders) async { + Future writeCredentials(String serverURL, String sessionKey, String? customHeaders) async { await _repository.setAppGroupId(appShareGroupId); await _repository.saveData(kWidgetServerEndpoint, serverURL); await _repository.saveData(kWidgetAuthToken, sessionKey); - await _repository.saveData(kWidgetCustomHeaders, customHeaders); + + if (customHeaders != null && customHeaders.isNotEmpty) { + await _repository.saveData(kWidgetCustomHeaders, customHeaders); + } // wait 3 seconds to ensure the widget is updated, dont block Future.delayed(const Duration(seconds: 3), refreshWidgets); From 09a5963eee7ea568f10b8d42360cf4b23604897b Mon Sep 17 00:00:00 2001 From: Mert <101130780+mertalev@users.noreply.github.com> Date: Tue, 5 Aug 2025 12:32:06 -0400 Subject: [PATCH 177/748] fix(mobile): catch thumbnail cache miss (#20694) catch error --- mobile/lib/presentation/widgets/images/image_provider.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/mobile/lib/presentation/widgets/images/image_provider.dart b/mobile/lib/presentation/widgets/images/image_provider.dart index adb1e178ca..19eed71d44 100644 --- a/mobile/lib/presentation/widgets/images/image_provider.dart +++ b/mobile/lib/presentation/widgets/images/image_provider.dart @@ -59,6 +59,7 @@ ImageInfo? getCachedImage(ImageProvider key) { final ImageStreamCompleter? stream = PaintingBinding.instance.imageCache.putIfAbsent( key, () => throw Exception(), // don't bother loading if it isn't cached + onError: (_, __) {}, ); if (stream != null) { From 02381343ffebc262faabb5e99bd2631a4a073411 Mon Sep 17 00:00:00 2001 From: Zack Pollard Date: Tue, 5 Aug 2025 17:53:51 +0100 Subject: [PATCH 178/748] fix: album asset sync must sync new assets in a shared album (#20655) --- .../domain/services/sync_stream.service.dart | 16 +- .../repositories/sync_api.repository.dart | 6 +- .../openapi/lib/model/sync_entity_type.dart | 18 +- open-api/immich-openapi-specs.json | 6 +- open-api/typescript-sdk/src/fetch-client.ts | 6 +- server/package-lock.json | 48 ++- server/package.json | 1 + server/src/dtos/sync.dto.ts | 6 +- server/src/enum.ts | 6 +- .../queries/sync.checkpoint.repository.sql | 4 + server/src/queries/sync.repository.sql | 196 +++++----- .../sync-checkpoint.repository.ts | 11 +- server/src/repositories/sync.repository.ts | 359 +++++++++++------- .../1754389095885-ResetAlbumAssetSync.ts | 7 + server/src/services/sync.service.ts | 252 +++++++----- server/test/medium.factory.ts | 19 +- .../specs/sync/sync-album-asset-exif.spec.ts | 236 ++++++++++-- .../specs/sync/sync-album-asset.spec.ts | 170 +++++++-- server/test/utils.ts | 14 +- 19 files changed, 928 insertions(+), 453 deletions(-) create mode 100644 server/src/schema/migrations/1754389095885-ResetAlbumAssetSync.ts diff --git a/mobile/lib/domain/services/sync_stream.service.dart b/mobile/lib/domain/services/sync_stream.service.dart index c21a9cade5..7c25fbb345 100644 --- a/mobile/lib/domain/services/sync_stream.service.dart +++ b/mobile/lib/domain/services/sync_stream.service.dart @@ -139,14 +139,18 @@ class SyncStreamService { return _syncStreamRepository.updateAlbumUsersV1(data.cast(), debugLabel: 'backfill'); case SyncEntityType.albumUserDeleteV1: return _syncStreamRepository.deleteAlbumUsersV1(data.cast()); - case SyncEntityType.albumAssetV1: - return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'album'); + case SyncEntityType.albumAssetCreateV1: + return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'album asset create'); + case SyncEntityType.albumAssetUpdateV1: + return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'album asset update'); case SyncEntityType.albumAssetBackfillV1: - return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'album backfill'); - case SyncEntityType.albumAssetExifV1: - return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'album'); + return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'album asset backfill'); + case SyncEntityType.albumAssetExifCreateV1: + return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'album asset exif create'); + case SyncEntityType.albumAssetExifUpdateV1: + return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'album asset exif update'); case SyncEntityType.albumAssetExifBackfillV1: - return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'album backfill'); + return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'album asset exif backfill'); case SyncEntityType.albumToAssetV1: return _syncStreamRepository.updateAlbumToAssetsV1(data.cast()); case SyncEntityType.albumToAssetBackfillV1: diff --git a/mobile/lib/infrastructure/repositories/sync_api.repository.dart b/mobile/lib/infrastructure/repositories/sync_api.repository.dart index 78b2a9d957..48c38cf64b 100644 --- a/mobile/lib/infrastructure/repositories/sync_api.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_api.repository.dart @@ -149,9 +149,11 @@ const _kResponseMap = { SyncEntityType.albumUserV1: SyncAlbumUserV1.fromJson, SyncEntityType.albumUserBackfillV1: SyncAlbumUserV1.fromJson, SyncEntityType.albumUserDeleteV1: SyncAlbumUserDeleteV1.fromJson, - SyncEntityType.albumAssetV1: SyncAssetV1.fromJson, + SyncEntityType.albumAssetCreateV1: SyncAssetV1.fromJson, + SyncEntityType.albumAssetUpdateV1: SyncAssetV1.fromJson, SyncEntityType.albumAssetBackfillV1: SyncAssetV1.fromJson, - SyncEntityType.albumAssetExifV1: SyncAssetExifV1.fromJson, + SyncEntityType.albumAssetExifCreateV1: SyncAssetExifV1.fromJson, + SyncEntityType.albumAssetExifUpdateV1: SyncAssetExifV1.fromJson, SyncEntityType.albumAssetExifBackfillV1: SyncAssetExifV1.fromJson, SyncEntityType.albumToAssetV1: SyncAlbumToAssetV1.fromJson, SyncEntityType.albumToAssetBackfillV1: SyncAlbumToAssetV1.fromJson, diff --git a/mobile/openapi/lib/model/sync_entity_type.dart b/mobile/openapi/lib/model/sync_entity_type.dart index 5368126923..f259fdc9d9 100644 --- a/mobile/openapi/lib/model/sync_entity_type.dart +++ b/mobile/openapi/lib/model/sync_entity_type.dart @@ -44,9 +44,11 @@ class SyncEntityType { static const albumUserV1 = SyncEntityType._(r'AlbumUserV1'); static const albumUserBackfillV1 = SyncEntityType._(r'AlbumUserBackfillV1'); static const albumUserDeleteV1 = SyncEntityType._(r'AlbumUserDeleteV1'); - static const albumAssetV1 = SyncEntityType._(r'AlbumAssetV1'); + static const albumAssetCreateV1 = SyncEntityType._(r'AlbumAssetCreateV1'); + static const albumAssetUpdateV1 = SyncEntityType._(r'AlbumAssetUpdateV1'); static const albumAssetBackfillV1 = SyncEntityType._(r'AlbumAssetBackfillV1'); - static const albumAssetExifV1 = SyncEntityType._(r'AlbumAssetExifV1'); + static const albumAssetExifCreateV1 = SyncEntityType._(r'AlbumAssetExifCreateV1'); + static const albumAssetExifUpdateV1 = SyncEntityType._(r'AlbumAssetExifUpdateV1'); static const albumAssetExifBackfillV1 = SyncEntityType._(r'AlbumAssetExifBackfillV1'); static const albumToAssetV1 = SyncEntityType._(r'AlbumToAssetV1'); static const albumToAssetDeleteV1 = SyncEntityType._(r'AlbumToAssetDeleteV1'); @@ -89,9 +91,11 @@ class SyncEntityType { albumUserV1, albumUserBackfillV1, albumUserDeleteV1, - albumAssetV1, + albumAssetCreateV1, + albumAssetUpdateV1, albumAssetBackfillV1, - albumAssetExifV1, + albumAssetExifCreateV1, + albumAssetExifUpdateV1, albumAssetExifBackfillV1, albumToAssetV1, albumToAssetDeleteV1, @@ -169,9 +173,11 @@ class SyncEntityTypeTypeTransformer { case r'AlbumUserV1': return SyncEntityType.albumUserV1; case r'AlbumUserBackfillV1': return SyncEntityType.albumUserBackfillV1; case r'AlbumUserDeleteV1': return SyncEntityType.albumUserDeleteV1; - case r'AlbumAssetV1': return SyncEntityType.albumAssetV1; + case r'AlbumAssetCreateV1': return SyncEntityType.albumAssetCreateV1; + case r'AlbumAssetUpdateV1': return SyncEntityType.albumAssetUpdateV1; case r'AlbumAssetBackfillV1': return SyncEntityType.albumAssetBackfillV1; - case r'AlbumAssetExifV1': return SyncEntityType.albumAssetExifV1; + case r'AlbumAssetExifCreateV1': return SyncEntityType.albumAssetExifCreateV1; + case r'AlbumAssetExifUpdateV1': return SyncEntityType.albumAssetExifUpdateV1; case r'AlbumAssetExifBackfillV1': return SyncEntityType.albumAssetExifBackfillV1; case r'AlbumToAssetV1': return SyncEntityType.albumToAssetV1; case r'AlbumToAssetDeleteV1': return SyncEntityType.albumToAssetDeleteV1; diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 7d3feb24a3..74a1407096 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -14944,9 +14944,11 @@ "AlbumUserV1", "AlbumUserBackfillV1", "AlbumUserDeleteV1", - "AlbumAssetV1", + "AlbumAssetCreateV1", + "AlbumAssetUpdateV1", "AlbumAssetBackfillV1", - "AlbumAssetExifV1", + "AlbumAssetExifCreateV1", + "AlbumAssetExifUpdateV1", "AlbumAssetExifBackfillV1", "AlbumToAssetV1", "AlbumToAssetDeleteV1", diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 8b2ed427b4..a1a04ea566 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -4770,9 +4770,11 @@ export enum SyncEntityType { AlbumUserV1 = "AlbumUserV1", AlbumUserBackfillV1 = "AlbumUserBackfillV1", AlbumUserDeleteV1 = "AlbumUserDeleteV1", - AlbumAssetV1 = "AlbumAssetV1", + AlbumAssetCreateV1 = "AlbumAssetCreateV1", + AlbumAssetUpdateV1 = "AlbumAssetUpdateV1", AlbumAssetBackfillV1 = "AlbumAssetBackfillV1", - AlbumAssetExifV1 = "AlbumAssetExifV1", + AlbumAssetExifCreateV1 = "AlbumAssetExifCreateV1", + AlbumAssetExifUpdateV1 = "AlbumAssetExifUpdateV1", AlbumAssetExifBackfillV1 = "AlbumAssetExifBackfillV1", AlbumToAssetV1 = "AlbumToAssetV1", AlbumToAssetDeleteV1 = "AlbumToAssetDeleteV1", diff --git a/server/package-lock.json b/server/package-lock.json index 6d418288a6..2fff7747f9 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -85,6 +85,7 @@ "thumbhash": "^0.1.1", "typeorm": "^0.3.17", "ua-parser-js": "^2.0.0", + "uuid": "^11.1.0", "validator": "^13.12.0" }, "devDependencies": { @@ -8504,6 +8505,19 @@ "uuid": "^9.0.0" } }, + "node_modules/bullmq/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -11261,6 +11275,19 @@ "node": ">=14" } }, + "node_modules/gaxios/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/gcp-metadata": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", @@ -17767,19 +17794,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/typeorm/node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/esm/bin/uuid" - } - }, "node_modules/typescript": { "version": "5.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", @@ -18084,16 +18098,16 @@ "license": "MIT" }, "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], "license": "MIT", "bin": { - "uuid": "dist/bin/uuid" + "uuid": "dist/esm/bin/uuid" } }, "node_modules/validator": { diff --git a/server/package.json b/server/package.json index 60f224f950..100dd33d47 100644 --- a/server/package.json +++ b/server/package.json @@ -110,6 +110,7 @@ "thumbhash": "^0.1.1", "typeorm": "^0.3.17", "ua-parser-js": "^2.0.0", + "uuid": "^11.1.0", "validator": "^13.12.0" }, "devDependencies": { diff --git a/server/src/dtos/sync.dto.ts b/server/src/dtos/sync.dto.ts index 9c304c0d3c..9ac85755ab 100644 --- a/server/src/dtos/sync.dto.ts +++ b/server/src/dtos/sync.dto.ts @@ -339,9 +339,11 @@ export type SyncItem = { [SyncEntityType.AlbumUserV1]: SyncAlbumUserV1; [SyncEntityType.AlbumUserBackfillV1]: SyncAlbumUserV1; [SyncEntityType.AlbumUserDeleteV1]: SyncAlbumUserDeleteV1; - [SyncEntityType.AlbumAssetV1]: SyncAssetV1; + [SyncEntityType.AlbumAssetCreateV1]: SyncAssetV1; + [SyncEntityType.AlbumAssetUpdateV1]: SyncAssetV1; [SyncEntityType.AlbumAssetBackfillV1]: SyncAssetV1; - [SyncEntityType.AlbumAssetExifV1]: SyncAssetExifV1; + [SyncEntityType.AlbumAssetExifCreateV1]: SyncAssetExifV1; + [SyncEntityType.AlbumAssetExifUpdateV1]: SyncAssetExifV1; [SyncEntityType.AlbumAssetExifBackfillV1]: SyncAssetExifV1; [SyncEntityType.AlbumToAssetV1]: SyncAlbumToAssetV1; [SyncEntityType.AlbumToAssetBackfillV1]: SyncAlbumToAssetV1; diff --git a/server/src/enum.ts b/server/src/enum.ts index 8a6d361d35..8c7ee85a32 100644 --- a/server/src/enum.ts +++ b/server/src/enum.ts @@ -668,9 +668,11 @@ export enum SyncEntityType { AlbumUserBackfillV1 = 'AlbumUserBackfillV1', AlbumUserDeleteV1 = 'AlbumUserDeleteV1', - AlbumAssetV1 = 'AlbumAssetV1', + AlbumAssetCreateV1 = 'AlbumAssetCreateV1', + AlbumAssetUpdateV1 = 'AlbumAssetUpdateV1', AlbumAssetBackfillV1 = 'AlbumAssetBackfillV1', - AlbumAssetExifV1 = 'AlbumAssetExifV1', + AlbumAssetExifCreateV1 = 'AlbumAssetExifCreateV1', + AlbumAssetExifUpdateV1 = 'AlbumAssetExifUpdateV1', AlbumAssetExifBackfillV1 = 'AlbumAssetExifBackfillV1', AlbumToAssetV1 = 'AlbumToAssetV1', diff --git a/server/src/queries/sync.checkpoint.repository.sql b/server/src/queries/sync.checkpoint.repository.sql index 018054ff79..e99d90cd54 100644 --- a/server/src/queries/sync.checkpoint.repository.sql +++ b/server/src/queries/sync.checkpoint.repository.sql @@ -13,3 +13,7 @@ where delete from "session_sync_checkpoint" where "sessionId" = $1 + +-- SyncCheckpointRepository.getNow +select + immich_uuid_v7 (now() - interval '1 millisecond') as "nowId" diff --git a/server/src/queries/sync.repository.sql b/server/src/queries/sync.repository.sql index 28c6f32acc..0bd5b9f47d 100644 --- a/server/src/queries/sync.repository.sql +++ b/server/src/queries/sync.repository.sql @@ -9,7 +9,7 @@ from where "usersId" = $1 and "createId" >= $2 - and "createdAt" < now() - interval '1 millisecond' + and "createId" < $3 order by "createId" asc @@ -21,7 +21,7 @@ from "album_audit" where "userId" = $1 - and "deletedAt" < now() - interval '1 millisecond' + and "id" < $2 order by "id" asc @@ -41,10 +41,10 @@ from "album" left join "album_user" as "album_users" on "album"."id" = "album_users"."albumsId" where - "album"."updatedAt" < now() - interval '1 millisecond' + "album"."updateId" < $1 and ( - "album"."ownerId" = $1 - or "album_users"."usersId" = $2 + "album"."ownerId" = $2 + or "album_users"."usersId" = $3 ) order by "album"."updateId" asc @@ -67,20 +67,21 @@ select "asset"."livePhotoVideoId", "asset"."stackId", "asset"."libraryId", - "asset"."updateId" + "album_asset"."updateId" from - "asset" - inner join "album_asset" on "album_asset"."assetsId" = "asset"."id" + "album_asset" + inner join "asset" on "asset"."id" = "album_asset"."assetsId" where "album_asset"."albumsId" = $1 - and "asset"."updatedAt" < now() - interval '1 millisecond' - and "asset"."updateId" <= $2 - and "asset"."updateId" >= $3 + and "album_asset"."updateId" < $2 + and "album_asset"."updateId" <= $3 + and "album_asset"."updateId" >= $4 order by - "asset"."updateId" asc + "album_asset"."updateId" asc --- SyncRepository.albumAsset.getUpserts +-- SyncRepository.albumAsset.getCreates select + "album_asset"."updateId", "asset"."id", "asset"."ownerId", "asset"."originalFileName", @@ -96,21 +97,20 @@ select "asset"."duration", "asset"."livePhotoVideoId", "asset"."stackId", - "asset"."libraryId", - "asset"."updateId" + "asset"."libraryId" from - "asset" - inner join "album_asset" on "album_asset"."assetsId" = "asset"."id" + "album_asset" + inner join "asset" on "asset"."id" = "album_asset"."assetsId" inner join "album" on "album"."id" = "album_asset"."albumsId" left join "album_user" on "album_user"."albumsId" = "album_asset"."albumsId" where - "asset"."updatedAt" < now() - interval '1 millisecond' + "album_asset"."updateId" < $1 and ( - "album"."ownerId" = $1 - or "album_user"."usersId" = $2 + "album"."ownerId" = $2 + or "album_user"."usersId" = $3 ) order by - "asset"."updateId" asc + "album_asset"."updateId" asc -- SyncRepository.albumAssetExif.getBackfill select @@ -139,20 +139,21 @@ select "asset_exif"."profileDescription", "asset_exif"."rating", "asset_exif"."fps", - "asset_exif"."updateId" + "album_asset"."updateId" from - "asset_exif" - inner join "album_asset" on "album_asset"."assetsId" = "asset_exif"."assetId" + "album_asset" + inner join "asset_exif" on "asset_exif"."assetId" = "album_asset"."assetsId" where "album_asset"."albumsId" = $1 - and "asset_exif"."updatedAt" < now() - interval '1 millisecond' - and "asset_exif"."updateId" <= $2 - and "asset_exif"."updateId" >= $3 + and "album_asset"."updateId" < $2 + and "album_asset"."updateId" <= $3 + and "album_asset"."updateId" >= $4 order by - "asset_exif"."updateId" asc + "album_asset"."updateId" asc --- SyncRepository.albumAssetExif.getUpserts +-- SyncRepository.albumAssetExif.getCreates select + "album_asset"."updateId", "asset_exif"."assetId", "asset_exif"."description", "asset_exif"."exifImageWidth", @@ -177,21 +178,20 @@ select "asset_exif"."exposureTime", "asset_exif"."profileDescription", "asset_exif"."rating", - "asset_exif"."fps", - "asset_exif"."updateId" + "asset_exif"."fps" from - "asset_exif" - inner join "album_asset" on "album_asset"."assetsId" = "asset_exif"."assetId" + "album_asset" + inner join "asset_exif" on "asset_exif"."assetId" = "album_asset"."assetsId" inner join "album" on "album"."id" = "album_asset"."albumsId" left join "album_user" on "album_user"."albumsId" = "album_asset"."albumsId" where - "asset_exif"."updatedAt" < now() - interval '1 millisecond' + "album_asset"."updateId" < $1 and ( - "album"."ownerId" = $1 - or "album_user"."usersId" = $2 + "album"."ownerId" = $2 + or "album_user"."usersId" = $3 ) order by - "asset_exif"."updateId" asc + "album_asset"."updateId" asc -- SyncRepository.albumToAsset.getBackfill select @@ -202,9 +202,9 @@ from "album_asset" as "album_assets" where "album_assets"."albumsId" = $1 - and "album_assets"."updatedAt" < now() - interval '1 millisecond' - and "album_assets"."updateId" <= $2 - and "album_assets"."updateId" >= $3 + and "album_assets"."updateId" < $2 + and "album_assets"."updateId" <= $3 + and "album_assets"."updateId" >= $4 order by "album_assets"."updateId" asc @@ -233,7 +233,7 @@ where "album_user"."usersId" = $2 ) ) - and "deletedAt" < now() - interval '1 millisecond' + and "id" < $3 order by "id" asc @@ -247,10 +247,10 @@ from inner join "album" on "album"."id" = "album_asset"."albumsId" left join "album_user" on "album_user"."albumsId" = "album_asset"."albumsId" where - "album_asset"."updatedAt" < now() - interval '1 millisecond' + "album_asset"."updateId" < $1 and ( - "album"."ownerId" = $1 - or "album_user"."usersId" = $2 + "album"."ownerId" = $2 + or "album_user"."usersId" = $3 ) order by "album_asset"."updateId" asc @@ -265,9 +265,9 @@ from "album_user" where "albumsId" = $1 - and "updatedAt" < now() - interval '1 millisecond' - and "updateId" <= $2 - and "updateId" >= $3 + and "updateId" < $2 + and "updateId" <= $3 + and "updateId" >= $4 order by "updateId" asc @@ -296,7 +296,7 @@ where "album_user"."usersId" = $2 ) ) - and "deletedAt" < now() - interval '1 millisecond' + and "id" < $3 order by "id" asc @@ -309,14 +309,14 @@ select from "album_user" where - "album_user"."updatedAt" < now() - interval '1 millisecond' + "album_user"."updateId" < $1 and "album_user"."albumsId" in ( select "id" from "album" where - "ownerId" = $1 + "ownerId" = $2 union ( select @@ -324,7 +324,7 @@ where from "album_user" as "albumUsers" where - "albumUsers"."usersId" = $2 + "albumUsers"."usersId" = $3 ) ) order by @@ -338,7 +338,7 @@ from "asset_audit" where "ownerId" = $1 - and "deletedAt" < now() - interval '1 millisecond' + and "id" < $2 order by "id" asc @@ -365,7 +365,7 @@ from "asset" where "ownerId" = $1 - and "updatedAt" < now() - interval '1 millisecond' + and "updateId" < $2 order by "updateId" asc @@ -408,7 +408,7 @@ where where "ownerId" = $1 ) - and "updatedAt" < now() - interval '1 millisecond' + and "updateId" < $2 order by "updateId" asc @@ -421,7 +421,7 @@ from left join "asset" on "asset"."id" = "asset_face_audit"."assetId" where "asset"."ownerId" = $1 - and "asset_face_audit"."deletedAt" < now() - interval '1 millisecond' + and "asset_face_audit"."id" < $2 order by "asset_face_audit"."id" asc @@ -442,34 +442,11 @@ from "asset_face" left join "asset" on "asset"."id" = "asset_face"."assetId" where - "asset_face"."updatedAt" < now() - interval '1 millisecond' - and "asset"."ownerId" = $1 + "asset_face"."updateId" < $1 + and "asset"."ownerId" = $2 order by "asset_face"."updateId" asc --- SyncRepository.authUser.getUpserts -select - "id", - "name", - "email", - "avatarColor", - "deletedAt", - "updateId", - "profileImagePath", - "profileChangedAt", - "isAdmin", - "pinCode", - "oauthId", - "storageLabel", - "quotaSizeInBytes", - "quotaUsageInBytes" -from - "user" -where - "updatedAt" < now() - interval '1 millisecond' -order by - "updateId" asc - -- SyncRepository.memory.getDeletes select "id", @@ -478,7 +455,7 @@ from "memory_audit" where "userId" = $1 - and "deletedAt" < now() - interval '1 millisecond' + and "id" < $2 order by "id" asc @@ -501,7 +478,7 @@ from "memory" where "ownerId" = $1 - and "updatedAt" < now() - interval '1 millisecond' + and "updateId" < $2 order by "updateId" asc @@ -521,7 +498,7 @@ where where "ownerId" = $1 ) - and "deletedAt" < now() - interval '1 millisecond' + and "id" < $2 order by "id" asc @@ -541,7 +518,7 @@ where where "ownerId" = $1 ) - and "updatedAt" < now() - interval '1 millisecond' + and "updateId" < $2 order by "updateId" asc @@ -553,8 +530,7 @@ from "partner" where "sharedWithId" = $1 - and "createId" >= $2 - and "createdAt" < now() - interval '1 millisecond' + and "createId" < $2 order by "partner"."createId" asc @@ -570,7 +546,7 @@ where "sharedById" = $1 or "sharedWithId" = $2 ) - and "deletedAt" < now() - interval '1 millisecond' + and "id" < $3 order by "id" asc @@ -587,7 +563,7 @@ where "sharedById" = $1 or "sharedWithId" = $2 ) - and "updatedAt" < now() - interval '1 millisecond' + and "updateId" < $3 order by "updateId" asc @@ -614,9 +590,9 @@ from "asset" where "ownerId" = $1 - and "updatedAt" < now() - interval '1 millisecond' - and "updateId" <= $2 - and "updateId" >= $3 + and "updateId" < $2 + and "updateId" <= $3 + and "updateId" >= $4 order by "updateId" asc @@ -635,7 +611,7 @@ where where "sharedWithId" = $1 ) - and "deletedAt" < now() - interval '1 millisecond' + and "id" < $2 order by "id" asc @@ -669,7 +645,7 @@ where where "sharedWithId" = $1 ) - and "updatedAt" < now() - interval '1 millisecond' + and "updateId" < $2 order by "updateId" asc @@ -706,9 +682,9 @@ from inner join "asset" on "asset"."id" = "asset_exif"."assetId" where "asset"."ownerId" = $1 - and "asset_exif"."updatedAt" < now() - interval '1 millisecond' - and "asset_exif"."updateId" <= $2 - and "asset_exif"."updateId" >= $3 + and "asset_exif"."updateId" < $2 + and "asset_exif"."updateId" <= $3 + and "asset_exif"."updateId" >= $4 order by "asset_exif"."updateId" asc @@ -758,7 +734,7 @@ where "sharedWithId" = $1 ) ) - and "updatedAt" < now() - interval '1 millisecond' + and "updateId" < $2 order by "updateId" asc @@ -777,7 +753,7 @@ where where "sharedWithId" = $1 ) - and "deletedAt" < now() - interval '1 millisecond' + and "id" < $2 order by "id" asc @@ -793,9 +769,9 @@ from "stack" where "ownerId" = $1 - and "updatedAt" < now() - interval '1 millisecond' - and "updateId" <= $2 - and "updateId" >= $3 + and "updateId" < $2 + and "updateId" <= $3 + and "updateId" >= $4 order by "updateId" asc @@ -818,7 +794,7 @@ where where "sharedWithId" = $1 ) - and "updatedAt" < now() - interval '1 millisecond' + and "updateId" < $2 order by "updateId" asc @@ -830,7 +806,7 @@ from "person_audit" where "ownerId" = $1 - and "deletedAt" < now() - interval '1 millisecond' + and "id" < $2 order by "id" asc @@ -851,7 +827,7 @@ from "person" where "ownerId" = $1 - and "updatedAt" < now() - interval '1 millisecond' + and "updateId" < $2 order by "updateId" asc @@ -863,7 +839,7 @@ from "stack_audit" where "userId" = $1 - and "deletedAt" < now() - interval '1 millisecond' + and "id" < $2 order by "id" asc @@ -879,7 +855,7 @@ from "stack" where "ownerId" = $1 - and "updatedAt" < now() - interval '1 millisecond' + and "updateId" < $2 order by "updateId" asc @@ -890,7 +866,7 @@ select from "user_audit" where - "deletedAt" < now() - interval '1 millisecond' + "id" < $1 order by "id" asc @@ -907,7 +883,7 @@ select from "user" where - "updatedAt" < now() - interval '1 millisecond' + "updateId" < $1 order by "updateId" asc @@ -920,7 +896,7 @@ from "user_metadata_audit" where "userId" = $1 - and "deletedAt" < now() - interval '1 millisecond' + and "id" < $2 order by "id" asc @@ -934,6 +910,6 @@ from "user_metadata" where "userId" = $1 - and "updatedAt" < now() - interval '1 millisecond' + and "updateId" < $2 order by "updateId" asc diff --git a/server/src/repositories/sync-checkpoint.repository.ts b/server/src/repositories/sync-checkpoint.repository.ts index 65fd018136..9db56e1bfe 100644 --- a/server/src/repositories/sync-checkpoint.repository.ts +++ b/server/src/repositories/sync-checkpoint.repository.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { Insertable, Kysely } from 'kysely'; +import { Insertable, Kysely, sql } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; import { DummyValue, GenerateSql } from 'src/decorators'; import { SyncEntityType } from 'src/enum'; @@ -39,4 +39,13 @@ export class SyncCheckpointRepository { .$if(!!types, (qb) => qb.where('type', 'in', types!)) .execute(); } + + @GenerateSql() + getNow() { + return this.db + .selectNoFrom((eb) => [ + eb.fn('immich_uuid_v7', [sql.raw("now() - interval '1 millisecond'")]).as('nowId'), + ]) + .executeTakeFirstOrThrow(); + } } diff --git a/server/src/repositories/sync.repository.ts b/server/src/repositories/sync.repository.ts index d72ddcfc4d..44191ccabd 100644 --- a/server/src/repositories/sync.repository.ts +++ b/server/src/repositories/sync.repository.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { Kysely, SelectQueryBuilder, sql } from 'kysely'; +import { Kysely, SelectQueryBuilder } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; import { columns } from 'src/database'; import { DummyValue, GenerateSql } from 'src/decorators'; @@ -33,6 +33,11 @@ type UpsertTables = | 'user_metadata' | 'asset_face'; +export type SyncQueryOptions = { + nowId: string; + userId: string; +}; + @Injectable() export class SyncRepository { album: AlbumSync; @@ -81,21 +86,21 @@ export class SyncRepository { class BaseSync { constructor(protected db: Kysely) {} - protected auditTableFilters(ack?: SyncAck) { + protected auditTableFilters(nowId: string, ack?: SyncAck) { return , D>(qb: SelectQueryBuilder) => { const builder = qb as SelectQueryBuilder; return builder - .where('deletedAt', '<', sql.raw("now() - interval '1 millisecond'")) + .where('id', '<', nowId) .$if(!!ack, (qb) => qb.where('id', '>', ack!.updateId)) .orderBy('id', 'asc') as SelectQueryBuilder; }; } - protected upsertTableFilters(ack?: SyncAck) { + protected upsertTableFilters(nowId: string, ack?: SyncAck) { return , D>(qb: SelectQueryBuilder) => { const builder = qb as SelectQueryBuilder; return builder - .where('updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) + .where('updateId', '<', nowId) .$if(!!ack, (qb) => qb.where('updateId', '>', ack!.updateId)) .orderBy('updateId', 'asc') as SelectQueryBuilder; }; @@ -103,34 +108,34 @@ class BaseSync { } class AlbumSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID] }) - getCreatedAfter(userId: string, afterCreateId?: string) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }, DummyValue.UUID] }) + getCreatedAfter({ nowId, userId }: SyncQueryOptions, afterCreateId?: string) { return this.db .selectFrom('album_user') .select(['albumsId as id', 'createId']) .where('usersId', '=', userId) .$if(!!afterCreateId, (qb) => qb.where('createId', '>=', afterCreateId!)) - .where('createdAt', '<', sql.raw("now() - interval '1 millisecond'")) + .where('createId', '<', nowId) .orderBy('createId', 'asc') .execute(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getDeletes(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getDeletes({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('album_audit') .select(['id', 'albumId']) .where('userId', '=', userId) - .$call(this.auditTableFilters(ack)) + .$call(this.auditTableFilters(nowId, ack)) .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('album') .distinctOn(['album.id', 'album.updateId']) - .where('album.updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) + .where('album.updateId', '<', nowId) .$if(!!ack, (qb) => qb.where('album.updateId', '>', ack!.updateId)) .orderBy('album.updateId', 'asc') .leftJoin('album_user as album_users', 'album.id', 'album_users.albumsId') @@ -152,29 +157,33 @@ class AlbumSync extends BaseSync { } class AlbumAssetSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], stream: true }) - getBackfill(albumId: string, afterUpdateId: string | undefined, beforeUpdateId: string) { + @GenerateSql({ + params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }, DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], + stream: true, + }) + getBackfill({ nowId }: SyncQueryOptions, albumId: string, afterUpdateId: string | undefined, beforeUpdateId: string) { return this.db - .selectFrom('asset') - .innerJoin('album_asset', 'album_asset.assetsId', 'asset.id') + .selectFrom('album_asset') + .innerJoin('asset', 'asset.id', 'album_asset.assetsId') .select(columns.syncAsset) - .select('asset.updateId') + .select('album_asset.updateId') .where('album_asset.albumsId', '=', albumId) - .where('asset.updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) - .where('asset.updateId', '<=', beforeUpdateId) - .$if(!!afterUpdateId, (eb) => eb.where('asset.updateId', '>=', afterUpdateId!)) - .orderBy('asset.updateId', 'asc') + .where('album_asset.updateId', '<', nowId) + .where('album_asset.updateId', '<=', beforeUpdateId) + .$if(!!afterUpdateId, (eb) => eb.where('album_asset.updateId', '>=', afterUpdateId!)) + .orderBy('album_asset.updateId', 'asc') .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getUpdates({ nowId, userId }: SyncQueryOptions, albumToAssetAck: SyncAck, ack?: SyncAck) { return this.db .selectFrom('asset') .innerJoin('album_asset', 'album_asset.assetsId', 'asset.id') .select(columns.syncAsset) .select('asset.updateId') - .where('asset.updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) + .where('asset.updateId', '<', nowId) + .where('album_asset.updateId', '<=', albumToAssetAck.updateId) // Ensure we only send updates for assets that the client already knows about .$if(!!ack, (qb) => qb.where('asset.updateId', '>', ack!.updateId)) .orderBy('asset.updateId', 'asc') .innerJoin('album', 'album.id', 'album_asset.albumsId') @@ -182,32 +191,52 @@ class AlbumAssetSync extends BaseSync { .where((eb) => eb.or([eb('album.ownerId', '=', userId), eb('album_user.usersId', '=', userId)])) .stream(); } + + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getCreates({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { + return this.db + .selectFrom('album_asset') + .select('album_asset.updateId') + .innerJoin('asset', 'asset.id', 'album_asset.assetsId') + .select(columns.syncAsset) + .innerJoin('album', 'album.id', 'album_asset.albumsId') + .leftJoin('album_user', 'album_user.albumsId', 'album_asset.albumsId') + .where('album_asset.updateId', '<', nowId) + .where((eb) => eb.or([eb('album.ownerId', '=', userId), eb('album_user.usersId', '=', userId)])) + .$if(!!ack, (qb) => qb.where('album_asset.updateId', '>', ack!.updateId)) + .orderBy('album_asset.updateId', 'asc') + .stream(); + } } class AlbumAssetExifSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], stream: true }) - getBackfill(albumId: string, afterUpdateId: string | undefined, beforeUpdateId: string) { + @GenerateSql({ + params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }, DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], + stream: true, + }) + getBackfill({ nowId }: SyncQueryOptions, albumId: string, afterUpdateId: string | undefined, beforeUpdateId: string) { return this.db - .selectFrom('asset_exif') - .innerJoin('album_asset', 'album_asset.assetsId', 'asset_exif.assetId') + .selectFrom('album_asset') + .innerJoin('asset_exif', 'asset_exif.assetId', 'album_asset.assetsId') .select(columns.syncAssetExif) - .select('asset_exif.updateId') + .select('album_asset.updateId') .where('album_asset.albumsId', '=', albumId) - .where('asset_exif.updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) - .where('asset_exif.updateId', '<=', beforeUpdateId) - .$if(!!afterUpdateId, (eb) => eb.where('asset_exif.updateId', '>=', afterUpdateId!)) - .orderBy('asset_exif.updateId', 'asc') + .where('album_asset.updateId', '<', nowId) + .where('album_asset.updateId', '<=', beforeUpdateId) + .$if(!!afterUpdateId, (eb) => eb.where('album_asset.updateId', '>=', afterUpdateId!)) + .orderBy('album_asset.updateId', 'asc') .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getUpdates({ nowId, userId }: SyncQueryOptions, albumToAssetAck: SyncAck, ack?: SyncAck) { return this.db .selectFrom('asset_exif') .innerJoin('album_asset', 'album_asset.assetsId', 'asset_exif.assetId') .select(columns.syncAssetExif) .select('asset_exif.updateId') - .where('asset_exif.updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) + .where('album_asset.updateId', '<=', albumToAssetAck.updateId) // Ensure we only send exif updates for assets that the client already knows about + .where('asset_exif.updateId', '<', nowId) .$if(!!ack, (qb) => qb.where('asset_exif.updateId', '>', ack!.updateId)) .orderBy('asset_exif.updateId', 'asc') .innerJoin('album', 'album.id', 'album_asset.albumsId') @@ -215,24 +244,43 @@ class AlbumAssetExifSync extends BaseSync { .where((eb) => eb.or([eb('album.ownerId', '=', userId), eb('album_user.usersId', '=', userId)])) .stream(); } + + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getCreates({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { + return this.db + .selectFrom('album_asset') + .select('album_asset.updateId') + .innerJoin('asset_exif', 'asset_exif.assetId', 'album_asset.assetsId') + .select(columns.syncAssetExif) + .innerJoin('album', 'album.id', 'album_asset.albumsId') + .leftJoin('album_user', 'album_user.albumsId', 'album_asset.albumsId') + .where('album_asset.updateId', '<', nowId) + .where((eb) => eb.or([eb('album.ownerId', '=', userId), eb('album_user.usersId', '=', userId)])) + .$if(!!ack, (qb) => qb.where('album_asset.updateId', '>', ack!.updateId)) + .orderBy('album_asset.updateId', 'asc') + .stream(); + } } class AlbumToAssetSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], stream: true }) - getBackfill(albumId: string, afterUpdateId: string | undefined, beforeUpdateId: string) { + @GenerateSql({ + params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }, DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], + stream: true, + }) + getBackfill({ nowId }: SyncQueryOptions, albumId: string, afterUpdateId: string | undefined, beforeUpdateId: string) { return this.db .selectFrom('album_asset as album_assets') .select(['album_assets.assetsId as assetId', 'album_assets.albumsId as albumId', 'album_assets.updateId']) .where('album_assets.albumsId', '=', albumId) - .where('album_assets.updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) + .where('album_assets.updateId', '<', nowId) .where('album_assets.updateId', '<=', beforeUpdateId) .$if(!!afterUpdateId, (eb) => eb.where('album_assets.updateId', '>=', afterUpdateId!)) .orderBy('album_assets.updateId', 'asc') .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getDeletes(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getDeletes({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('album_asset_audit') .select(['id', 'assetId', 'albumId']) @@ -254,16 +302,16 @@ class AlbumToAssetSync extends BaseSync { ), ), ) - .$call(this.auditTableFilters(ack)) + .$call(this.auditTableFilters(nowId, ack)) .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('album_asset') .select(['album_asset.assetsId as assetId', 'album_asset.albumsId as albumId', 'album_asset.updateId']) - .where('album_asset.updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) + .where('album_asset.updateId', '<', nowId) .$if(!!ack, (qb) => qb.where('album_asset.updateId', '>', ack!.updateId)) .orderBy('album_asset.updateId', 'asc') .innerJoin('album', 'album.id', 'album_asset.albumsId') @@ -274,22 +322,25 @@ class AlbumToAssetSync extends BaseSync { } class AlbumUserSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], stream: true }) - getBackfill(albumId: string, afterUpdateId: string | undefined, beforeUpdateId: string) { + @GenerateSql({ + params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }, DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], + stream: true, + }) + getBackfill({ nowId }: SyncQueryOptions, albumId: string, afterUpdateId: string | undefined, beforeUpdateId: string) { return this.db .selectFrom('album_user') .select(columns.syncAlbumUser) .select('album_user.updateId') .where('albumsId', '=', albumId) - .where('updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) + .where('updateId', '<', nowId) .where('updateId', '<=', beforeUpdateId) .$if(!!afterUpdateId, (eb) => eb.where('updateId', '>=', afterUpdateId!)) .orderBy('updateId', 'asc') .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getDeletes(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getDeletes({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('album_user_audit') .select(['id', 'userId', 'albumId']) @@ -311,17 +362,17 @@ class AlbumUserSync extends BaseSync { ), ), ) - .$call(this.auditTableFilters(ack)) + .$call(this.auditTableFilters(nowId, ack)) .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('album_user') .select(columns.syncAlbumUser) .select('album_user.updateId') - .where('album_user.updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) + .where('album_user.updateId', '<', nowId) .$if(!!ack, (qb) => qb.where('album_user.updateId', '>', ack!.updateId)) .orderBy('album_user.updateId', 'asc') .where((eb) => @@ -347,53 +398,53 @@ class AlbumUserSync extends BaseSync { } class AssetSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getDeletes(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getDeletes({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('asset_audit') .select(['id', 'assetId']) .where('ownerId', '=', userId) - .$call(this.auditTableFilters(ack)) + .$call(this.auditTableFilters(nowId, ack)) .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('asset') .select(columns.syncAsset) .select('asset.updateId') .where('ownerId', '=', userId) - .$call(this.upsertTableFilters(ack)) + .$call(this.upsertTableFilters(nowId, ack)) .stream(); } } class AuthUserSync extends BaseSync { @GenerateSql({ params: [], stream: true }) - getUpserts(ack?: SyncAck) { + getUpserts({ nowId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('user') .select(columns.syncUser) .select(['isAdmin', 'pinCode', 'oauthId', 'storageLabel', 'quotaSizeInBytes', 'quotaUsageInBytes']) - .$call(this.upsertTableFilters(ack)) + .$call(this.upsertTableFilters(nowId, ack)) .stream(); } } class PersonSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getDeletes(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getDeletes({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('person_audit') .select(['id', 'personId']) .where('ownerId', '=', userId) - .$call(this.auditTableFilters(ack)) + .$call(this.auditTableFilters(nowId, ack)) .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('person') .select([ @@ -410,27 +461,27 @@ class PersonSync extends BaseSync { 'faceAssetId', ]) .where('ownerId', '=', userId) - .$call(this.upsertTableFilters(ack)) + .$call(this.upsertTableFilters(nowId, ack)) .stream(); } } class AssetFaceSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getDeletes(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getDeletes({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('asset_face_audit') .select(['asset_face_audit.id', 'assetFaceId']) .orderBy('asset_face_audit.id', 'asc') .leftJoin('asset', 'asset.id', 'asset_face_audit.assetId') .where('asset.ownerId', '=', userId) - .where('asset_face_audit.deletedAt', '<', sql.raw("now() - interval '1 millisecond'")) + .where('asset_face_audit.id', '<', nowId) .$if(!!ack, (qb) => qb.where('asset_face_audit.id', '>', ack!.updateId)) .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('asset_face') .select([ @@ -446,7 +497,7 @@ class AssetFaceSync extends BaseSync { 'sourceType', 'asset_face.updateId', ]) - .where('asset_face.updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) + .where('asset_face.updateId', '<', nowId) .$if(!!ack, (qb) => qb.where('asset_face.updateId', '>', ack!.updateId)) .orderBy('asset_face.updateId', 'asc') .leftJoin('asset', 'asset.id', 'asset_face.assetId') @@ -456,31 +507,31 @@ class AssetFaceSync extends BaseSync { } class AssetExifSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('asset_exif') .select(columns.syncAssetExif) .select('asset_exif.updateId') .where('assetId', 'in', (eb) => eb.selectFrom('asset').select('id').where('ownerId', '=', userId)) - .$call(this.upsertTableFilters(ack)) + .$call(this.upsertTableFilters(nowId, ack)) .stream(); } } class MemorySync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getDeletes(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getDeletes({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('memory_audit') .select(['id', 'memoryId']) .where('userId', '=', userId) - .$call(this.auditTableFilters(ack)) + .$call(this.auditTableFilters(nowId, ack)) .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('memory') .select([ @@ -499,97 +550,105 @@ class MemorySync extends BaseSync { ]) .select('updateId') .where('ownerId', '=', userId) - .$call(this.upsertTableFilters(ack)) + .$call(this.upsertTableFilters(nowId, ack)) .stream(); } } class MemoryToAssetSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getDeletes(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getDeletes({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('memory_asset_audit') .select(['id', 'memoryId', 'assetId']) .where('memoryId', 'in', (eb) => eb.selectFrom('memory').select('id').where('ownerId', '=', userId)) - .$call(this.auditTableFilters(ack)) + .$call(this.auditTableFilters(nowId, ack)) .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('memory_asset') .select(['memoriesId as memoryId', 'assetsId as assetId']) .select('updateId') .where('memoriesId', 'in', (eb) => eb.selectFrom('memory').select('id').where('ownerId', '=', userId)) - .$call(this.upsertTableFilters(ack)) + .$call(this.upsertTableFilters(nowId, ack)) .stream(); } } class PartnerSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID] }) - getCreatedAfter(userId: string, afterCreateId?: string) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }] }) + getCreatedAfter({ nowId, userId }: SyncQueryOptions, afterCreateId?: string) { return this.db .selectFrom('partner') .select(['sharedById', 'createId']) .where('sharedWithId', '=', userId) .$if(!!afterCreateId, (qb) => qb.where('createId', '>=', afterCreateId!)) - .where('createdAt', '<', sql.raw("now() - interval '1 millisecond'")) + .where('createId', '<', nowId) .orderBy('partner.createId', 'asc') .execute(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getDeletes(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getDeletes({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('partner_audit') .select(['id', 'sharedById', 'sharedWithId']) .where((eb) => eb.or([eb('sharedById', '=', userId), eb('sharedWithId', '=', userId)])) - .$call(this.auditTableFilters(ack)) + .$call(this.auditTableFilters(nowId, ack)) .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('partner') .select(['sharedById', 'sharedWithId', 'inTimeline', 'updateId']) .where((eb) => eb.or([eb('sharedById', '=', userId), eb('sharedWithId', '=', userId)])) - .$call(this.upsertTableFilters(ack)) + .$call(this.upsertTableFilters(nowId, ack)) .stream(); } } class PartnerAssetsSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], stream: true }) - getBackfill(partnerId: string, afterUpdateId: string | undefined, beforeUpdateId: string) { + @GenerateSql({ + params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }, DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], + stream: true, + }) + getBackfill( + { nowId }: SyncQueryOptions, + partnerId: string, + afterUpdateId: string | undefined, + beforeUpdateId: string, + ) { return this.db .selectFrom('asset') .select(columns.syncAsset) .select('asset.updateId') .where('ownerId', '=', partnerId) - .where('updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) + .where('updateId', '<', nowId) .where('updateId', '<=', beforeUpdateId) .$if(!!afterUpdateId, (eb) => eb.where('updateId', '>=', afterUpdateId!)) .orderBy('updateId', 'asc') .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getDeletes(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getDeletes({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('asset_audit') .select(['id', 'assetId']) .where('ownerId', 'in', (eb) => eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', userId), ) - .$call(this.auditTableFilters(ack)) + .$call(this.auditTableFilters(nowId, ack)) .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('asset') .select(columns.syncAsset) @@ -597,29 +656,37 @@ class PartnerAssetsSync extends BaseSync { .where('ownerId', 'in', (eb) => eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', userId), ) - .$call(this.upsertTableFilters(ack)) + .$call(this.upsertTableFilters(nowId, ack)) .stream(); } } class PartnerAssetExifsSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], stream: true }) - getBackfill(partnerId: string, afterUpdateId: string | undefined, beforeUpdateId: string) { + @GenerateSql({ + params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }, DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], + stream: true, + }) + getBackfill( + { nowId }: SyncQueryOptions, + partnerId: string, + afterUpdateId: string | undefined, + beforeUpdateId: string, + ) { return this.db .selectFrom('asset_exif') .select(columns.syncAssetExif) .select('asset_exif.updateId') .innerJoin('asset', 'asset.id', 'asset_exif.assetId') .where('asset.ownerId', '=', partnerId) - .where('asset_exif.updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) + .where('asset_exif.updateId', '<', nowId) .where('asset_exif.updateId', '<=', beforeUpdateId) .$if(!!afterUpdateId, (eb) => eb.where('asset_exif.updateId', '>=', afterUpdateId!)) .orderBy('asset_exif.updateId', 'asc') .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('asset_exif') .select(columns.syncAssetExif) @@ -632,61 +699,69 @@ class PartnerAssetExifsSync extends BaseSync { eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', userId), ), ) - .$call(this.upsertTableFilters(ack)) + .$call(this.upsertTableFilters(nowId, ack)) .stream(); } } class StackSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getDeletes(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getDeletes({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('stack_audit') .select(['id', 'stackId']) .where('userId', '=', userId) - .$call(this.auditTableFilters(ack)) + .$call(this.auditTableFilters(nowId, ack)) .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('stack') .select(columns.syncStack) .select('updateId') .where('ownerId', '=', userId) - .$call(this.upsertTableFilters(ack)) + .$call(this.upsertTableFilters(nowId, ack)) .stream(); } } class PartnerStackSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getDeletes(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getDeletes({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('stack_audit') .select(['id', 'stackId']) .where('userId', 'in', (eb) => eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', userId)) - .$call(this.auditTableFilters(ack)) + .$call(this.auditTableFilters(nowId, ack)) .stream(); } - @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], stream: true }) - getBackfill(partnerId: string, afterUpdateId: string | undefined, beforeUpdateId: string) { + @GenerateSql({ + params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }, DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], + stream: true, + }) + getBackfill( + { nowId }: SyncQueryOptions, + partnerId: string, + afterUpdateId: string | undefined, + beforeUpdateId: string, + ) { return this.db .selectFrom('stack') .select(columns.syncStack) .select('updateId') .where('ownerId', '=', partnerId) - .where('updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) + .where('updateId', '<', nowId) .where('updateId', '<=', beforeUpdateId) .$if(!!afterUpdateId, (eb) => eb.where('updateId', '>=', afterUpdateId!)) .orderBy('updateId', 'asc') .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('stack') .select(columns.syncStack) @@ -694,41 +769,41 @@ class PartnerStackSync extends BaseSync { .where('ownerId', 'in', (eb) => eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', userId), ) - .$call(this.upsertTableFilters(ack)) + .$call(this.upsertTableFilters(nowId, ack)) .stream(); } } class UserSync extends BaseSync { - @GenerateSql({ params: [], stream: true }) - getDeletes(ack?: SyncAck) { - return this.db.selectFrom('user_audit').select(['id', 'userId']).$call(this.auditTableFilters(ack)).stream(); + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getDeletes({ nowId }: SyncQueryOptions, ack?: SyncAck) { + return this.db.selectFrom('user_audit').select(['id', 'userId']).$call(this.auditTableFilters(nowId, ack)).stream(); } - @GenerateSql({ params: [], stream: true }) - getUpserts(ack?: SyncAck) { - return this.db.selectFrom('user').select(columns.syncUser).$call(this.upsertTableFilters(ack)).stream(); + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getUpserts({ nowId }: SyncQueryOptions, ack?: SyncAck) { + return this.db.selectFrom('user').select(columns.syncUser).$call(this.upsertTableFilters(nowId, ack)).stream(); } } class UserMetadataSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getDeletes(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getDeletes({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('user_metadata_audit') .select(['id', 'userId', 'key']) .where('userId', '=', userId) - .$call(this.auditTableFilters(ack)) + .$call(this.auditTableFilters(nowId, ack)) .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { + @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) + getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { return this.db .selectFrom('user_metadata') .select(['userId', 'key', 'value', 'updateId']) .where('userId', '=', userId) - .$call(this.upsertTableFilters(ack)) + .$call(this.upsertTableFilters(nowId, ack)) .stream(); } } diff --git a/server/src/schema/migrations/1754389095885-ResetAlbumAssetSync.ts b/server/src/schema/migrations/1754389095885-ResetAlbumAssetSync.ts new file mode 100644 index 0000000000..34bdfbd4ab --- /dev/null +++ b/server/src/schema/migrations/1754389095885-ResetAlbumAssetSync.ts @@ -0,0 +1,7 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await sql`DELETE FROM session_sync_checkpoint WHERE type IN ('AlbumAssetBackfillV1', 'AlbumAssetExifV1', 'AlbumAssetV1')`.execute(db); +} + +export async function down(): Promise {} diff --git a/server/src/services/sync.service.ts b/server/src/services/sync.service.ts index fee77f35ba..c7d67e7dd0 100644 --- a/server/src/services/sync.service.ts +++ b/server/src/services/sync.service.ts @@ -16,6 +16,7 @@ import { SyncStreamDto, } from 'src/dtos/sync.dto'; import { AssetVisibility, DatabaseAction, EntityType, Permission, SyncEntityType, SyncRequestType } from 'src/enum'; +import { SyncQueryOptions } from 'src/repositories/sync.repository'; import { SessionSyncCheckpointTable } from 'src/schema/tables/sync-checkpoint.table'; import { BaseService } from 'src/services/base.service'; import { SyncAck } from 'src/types'; @@ -137,30 +138,34 @@ export class SyncService extends BaseService { return; } + const { nowId } = await this.syncCheckpointRepository.getNow(); + const options: SyncQueryOptions = { nowId, userId: auth.user.id }; + const checkpoints = await this.syncCheckpointRepository.getAll(session.id); const checkpointMap: CheckpointMap = Object.fromEntries(checkpoints.map(({ type, ack }) => [type, fromAck(ack)])); const handlers: Record Promise> = { - [SyncRequestType.AuthUsersV1]: () => this.syncAuthUsersV1(response, checkpointMap), - [SyncRequestType.UsersV1]: () => this.syncUsersV1(response, checkpointMap), - [SyncRequestType.PartnersV1]: () => this.syncPartnersV1(response, checkpointMap, auth), - [SyncRequestType.AssetsV1]: () => this.syncAssetsV1(response, checkpointMap, auth), - [SyncRequestType.AssetExifsV1]: () => this.syncAssetExifsV1(response, checkpointMap, auth), - [SyncRequestType.PartnerAssetsV1]: () => this.syncPartnerAssetsV1(response, checkpointMap, auth, session.id), + [SyncRequestType.AuthUsersV1]: () => this.syncAuthUsersV1(options, response, checkpointMap), + [SyncRequestType.UsersV1]: () => this.syncUsersV1(options, response, checkpointMap), + [SyncRequestType.PartnersV1]: () => this.syncPartnersV1(options, response, checkpointMap), + [SyncRequestType.AssetsV1]: () => this.syncAssetsV1(options, response, checkpointMap), + [SyncRequestType.AssetExifsV1]: () => this.syncAssetExifsV1(options, response, checkpointMap), + [SyncRequestType.PartnerAssetsV1]: () => this.syncPartnerAssetsV1(options, response, checkpointMap, session.id), [SyncRequestType.PartnerAssetExifsV1]: () => - this.syncPartnerAssetExifsV1(response, checkpointMap, auth, session.id), - [SyncRequestType.AlbumsV1]: () => this.syncAlbumsV1(response, checkpointMap, auth), - [SyncRequestType.AlbumUsersV1]: () => this.syncAlbumUsersV1(response, checkpointMap, auth, session.id), - [SyncRequestType.AlbumAssetsV1]: () => this.syncAlbumAssetsV1(response, checkpointMap, auth, session.id), - [SyncRequestType.AlbumToAssetsV1]: () => this.syncAlbumToAssetsV1(response, checkpointMap, auth, session.id), - [SyncRequestType.AlbumAssetExifsV1]: () => this.syncAlbumAssetExifsV1(response, checkpointMap, auth, session.id), - [SyncRequestType.MemoriesV1]: () => this.syncMemoriesV1(response, checkpointMap, auth), - [SyncRequestType.MemoryToAssetsV1]: () => this.syncMemoryAssetsV1(response, checkpointMap, auth), - [SyncRequestType.StacksV1]: () => this.syncStackV1(response, checkpointMap, auth), - [SyncRequestType.PartnerStacksV1]: () => this.syncPartnerStackV1(response, checkpointMap, auth, session.id), - [SyncRequestType.PeopleV1]: () => this.syncPeopleV1(response, checkpointMap, auth), - [SyncRequestType.AssetFacesV1]: async () => this.syncAssetFacesV1(response, checkpointMap, auth), - [SyncRequestType.UserMetadataV1]: () => this.syncUserMetadataV1(response, checkpointMap, auth), + this.syncPartnerAssetExifsV1(options, response, checkpointMap, session.id), + [SyncRequestType.AlbumsV1]: () => this.syncAlbumsV1(options, response, checkpointMap), + [SyncRequestType.AlbumUsersV1]: () => this.syncAlbumUsersV1(options, response, checkpointMap, session.id), + [SyncRequestType.AlbumAssetsV1]: () => this.syncAlbumAssetsV1(options, response, checkpointMap, session.id), + [SyncRequestType.AlbumToAssetsV1]: () => this.syncAlbumToAssetsV1(options, response, checkpointMap, session.id), + [SyncRequestType.AlbumAssetExifsV1]: () => + this.syncAlbumAssetExifsV1(options, response, checkpointMap, session.id), + [SyncRequestType.MemoriesV1]: () => this.syncMemoriesV1(options, response, checkpointMap), + [SyncRequestType.MemoryToAssetsV1]: () => this.syncMemoryAssetsV1(options, response, checkpointMap), + [SyncRequestType.StacksV1]: () => this.syncStackV1(options, response, checkpointMap), + [SyncRequestType.PartnerStacksV1]: () => this.syncPartnerStackV1(options, response, checkpointMap, session.id), + [SyncRequestType.PeopleV1]: () => this.syncPeopleV1(options, response, checkpointMap), + [SyncRequestType.AssetFacesV1]: async () => this.syncAssetFacesV1(options, response, checkpointMap), + [SyncRequestType.UserMetadataV1]: () => this.syncUserMetadataV1(options, response, checkpointMap), }; for (const type of SYNC_TYPES_ORDER.filter((type) => dto.types.includes(type))) { @@ -171,71 +176,71 @@ export class SyncService extends BaseService { response.end(); } - private async syncAuthUsersV1(response: Writable, checkpointMap: CheckpointMap) { + private async syncAuthUsersV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const upsertType = SyncEntityType.AuthUserV1; - const upserts = this.syncRepository.authUser.getUpserts(checkpointMap[upsertType]); + const upserts = this.syncRepository.authUser.getUpserts(options, checkpointMap[upsertType]); for await (const { updateId, profileImagePath, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data: { ...data, hasProfileImage: !!profileImagePath } }); } } - private async syncUsersV1(response: Writable, checkpointMap: CheckpointMap) { + private async syncUsersV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.UserDeleteV1; - const deletes = this.syncRepository.user.getDeletes(checkpointMap[deleteType]); + const deletes = this.syncRepository.user.getDeletes(options, checkpointMap[deleteType]); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.UserV1; - const upserts = this.syncRepository.user.getUpserts(checkpointMap[upsertType]); + const upserts = this.syncRepository.user.getUpserts(options, checkpointMap[upsertType]); for await (const { updateId, profileImagePath, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data: { ...data, hasProfileImage: !!profileImagePath } }); } } - private async syncPartnersV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) { + private async syncPartnersV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.PartnerDeleteV1; - const deletes = this.syncRepository.partner.getDeletes(auth.user.id, checkpointMap[deleteType]); + const deletes = this.syncRepository.partner.getDeletes(options, checkpointMap[deleteType]); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.PartnerV1; - const upserts = this.syncRepository.partner.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.partner.getUpserts(options, checkpointMap[upsertType]); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } } - private async syncAssetsV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) { + private async syncAssetsV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.AssetDeleteV1; - const deletes = this.syncRepository.asset.getDeletes(auth.user.id, checkpointMap[deleteType]); + const deletes = this.syncRepository.asset.getDeletes(options, checkpointMap[deleteType]); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.AssetV1; - const upserts = this.syncRepository.asset.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.asset.getUpserts(options, checkpointMap[upsertType]); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data: mapSyncAssetV1(data) }); } } private async syncPartnerAssetsV1( + options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap, - auth: AuthDto, sessionId: string, ) { const deleteType = SyncEntityType.PartnerAssetDeleteV1; - const deletes = this.syncRepository.partnerAsset.getDeletes(auth.user.id, checkpointMap[deleteType]); + const deletes = this.syncRepository.partnerAsset.getDeletes(options, checkpointMap[deleteType]); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const backfillType = SyncEntityType.PartnerAssetBackfillV1; const backfillCheckpoint = checkpointMap[backfillType]; - const partners = await this.syncRepository.partner.getCreatedAfter(auth.user.id, backfillCheckpoint?.updateId); + const partners = await this.syncRepository.partner.getCreatedAfter(options, backfillCheckpoint?.updateId); const upsertType = SyncEntityType.PartnerAssetV1; const upsertCheckpoint = checkpointMap[upsertType]; if (upsertCheckpoint) { @@ -248,7 +253,7 @@ export class SyncService extends BaseService { } const startId = getStartId(createId, backfillCheckpoint); - const backfill = this.syncRepository.partnerAsset.getBackfill(partner.sharedById, startId, endId); + const backfill = this.syncRepository.partnerAsset.getBackfill(options, partner.sharedById, startId, endId); for await (const { updateId, ...data } of backfill) { send(response, { @@ -268,29 +273,29 @@ export class SyncService extends BaseService { }); } - const upserts = this.syncRepository.partnerAsset.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.partnerAsset.getUpserts(options, checkpointMap[upsertType]); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data: mapSyncAssetV1(data) }); } } - private async syncAssetExifsV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) { + private async syncAssetExifsV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const upsertType = SyncEntityType.AssetExifV1; - const upserts = this.syncRepository.assetExif.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.assetExif.getUpserts(options, checkpointMap[upsertType]); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } } private async syncPartnerAssetExifsV1( + options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap, - auth: AuthDto, sessionId: string, ) { const backfillType = SyncEntityType.PartnerAssetExifBackfillV1; const backfillCheckpoint = checkpointMap[backfillType]; - const partners = await this.syncRepository.partner.getCreatedAfter(auth.user.id, backfillCheckpoint?.updateId); + const partners = await this.syncRepository.partner.getCreatedAfter(options, backfillCheckpoint?.updateId); const upsertType = SyncEntityType.PartnerAssetExifV1; const upsertCheckpoint = checkpointMap[upsertType]; @@ -304,7 +309,7 @@ export class SyncService extends BaseService { } const startId = getStartId(createId, backfillCheckpoint); - const backfill = this.syncRepository.partnerAssetExif.getBackfill(partner.sharedById, startId, endId); + const backfill = this.syncRepository.partnerAssetExif.getBackfill(options, partner.sharedById, startId, endId); for await (const { updateId, ...data } of backfill) { send(response, { type: backfillType, ids: [partner.createId, updateId], data }); @@ -320,36 +325,41 @@ export class SyncService extends BaseService { }); } - const upserts = this.syncRepository.partnerAssetExif.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.partnerAssetExif.getUpserts(options, checkpointMap[upsertType]); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } } - private async syncAlbumsV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) { + private async syncAlbumsV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.AlbumDeleteV1; - const deletes = this.syncRepository.album.getDeletes(auth.user.id, checkpointMap[deleteType]); + const deletes = this.syncRepository.album.getDeletes(options, checkpointMap[deleteType]); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.AlbumV1; - const upserts = this.syncRepository.album.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.album.getUpserts(options, checkpointMap[upsertType]); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } } - private async syncAlbumUsersV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto, sessionId: string) { + private async syncAlbumUsersV1( + options: SyncQueryOptions, + response: Writable, + checkpointMap: CheckpointMap, + sessionId: string, + ) { const deleteType = SyncEntityType.AlbumUserDeleteV1; - const deletes = this.syncRepository.albumUser.getDeletes(auth.user.id, checkpointMap[deleteType]); + const deletes = this.syncRepository.albumUser.getDeletes(options, checkpointMap[deleteType]); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const backfillType = SyncEntityType.AlbumUserBackfillV1; const backfillCheckpoint = checkpointMap[backfillType]; - const albums = await this.syncRepository.album.getCreatedAfter(auth.user.id, backfillCheckpoint?.updateId); + const albums = await this.syncRepository.album.getCreatedAfter(options, backfillCheckpoint?.updateId); const upsertType = SyncEntityType.AlbumUserV1; const upsertCheckpoint = checkpointMap[upsertType]; if (upsertCheckpoint) { @@ -362,7 +372,7 @@ export class SyncService extends BaseService { } const startId = getStartId(createId, backfillCheckpoint); - const backfill = this.syncRepository.albumUser.getBackfill(album.id, startId, endId); + const backfill = this.syncRepository.albumUser.getBackfill(options, album.id, startId, endId); for await (const { updateId, ...data } of backfill) { send(response, { type: backfillType, ids: [createId, updateId], data }); @@ -378,20 +388,27 @@ export class SyncService extends BaseService { }); } - const upserts = this.syncRepository.albumUser.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.albumUser.getUpserts(options, checkpointMap[upsertType]); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } } - private async syncAlbumAssetsV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto, sessionId: string) { + private async syncAlbumAssetsV1( + options: SyncQueryOptions, + response: Writable, + checkpointMap: CheckpointMap, + sessionId: string, + ) { const backfillType = SyncEntityType.AlbumAssetBackfillV1; const backfillCheckpoint = checkpointMap[backfillType]; - const albums = await this.syncRepository.album.getCreatedAfter(auth.user.id, backfillCheckpoint?.updateId); - const upsertType = SyncEntityType.AlbumAssetV1; - const upsertCheckpoint = checkpointMap[upsertType]; - if (upsertCheckpoint) { - const endId = upsertCheckpoint.updateId; + const albums = await this.syncRepository.album.getCreatedAfter(options, backfillCheckpoint?.updateId); + const updateType = SyncEntityType.AlbumAssetUpdateV1; + const createType = SyncEntityType.AlbumAssetCreateV1; + const updateCheckpoint = checkpointMap[updateType]; + const createCheckpoint = checkpointMap[createType]; + if (createCheckpoint) { + const endId = createCheckpoint.updateId; for (const album of albums) { const createId = album.createId; @@ -400,7 +417,7 @@ export class SyncService extends BaseService { } const startId = getStartId(createId, backfillCheckpoint); - const backfill = this.syncRepository.albumAsset.getBackfill(album.id, startId, endId); + const backfill = this.syncRepository.albumAsset.getBackfill(options, album.id, startId, endId); for await (const { updateId, ...data } of backfill) { send(response, { type: backfillType, ids: [createId, updateId], data: mapSyncAssetV1(data) }); @@ -416,25 +433,44 @@ export class SyncService extends BaseService { }); } - const upserts = this.syncRepository.albumAsset.getUpserts(auth.user.id, checkpointMap[upsertType]); - for await (const { updateId, ...data } of upserts) { - send(response, { type: upsertType, ids: [updateId], data: mapSyncAssetV1(data) }); + if (createCheckpoint) { + const updates = this.syncRepository.albumAsset.getUpdates(options, createCheckpoint, updateCheckpoint); + for await (const { updateId, ...data } of updates) { + send(response, { type: updateType, ids: [updateId], data: mapSyncAssetV1(data) }); + } + } + + const creates = this.syncRepository.albumAsset.getCreates(options, createCheckpoint); + let first = true; + for await (const { updateId, ...data } of creates) { + if (first) { + send(response, { + type: SyncEntityType.SyncAckV1, + data: {}, + ackType: SyncEntityType.AlbumAssetUpdateV1, + ids: [options.nowId], + }); + first = false; + } + send(response, { type: createType, ids: [updateId], data: mapSyncAssetV1(data) }); } } private async syncAlbumAssetExifsV1( + options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap, - auth: AuthDto, sessionId: string, ) { const backfillType = SyncEntityType.AlbumAssetExifBackfillV1; const backfillCheckpoint = checkpointMap[backfillType]; - const albums = await this.syncRepository.album.getCreatedAfter(auth.user.id, backfillCheckpoint?.updateId); - const upsertType = SyncEntityType.AlbumAssetExifV1; - const upsertCheckpoint = checkpointMap[upsertType]; - if (upsertCheckpoint) { - const endId = upsertCheckpoint.updateId; + const albums = await this.syncRepository.album.getCreatedAfter(options, backfillCheckpoint?.updateId); + const updateType = SyncEntityType.AlbumAssetExifUpdateV1; + const createType = SyncEntityType.AlbumAssetExifCreateV1; + const upsertCheckpoint = checkpointMap[updateType]; + const createCheckpoint = checkpointMap[createType]; + if (createCheckpoint) { + const endId = createCheckpoint.updateId; for (const album of albums) { const createId = album.createId; @@ -443,7 +479,7 @@ export class SyncService extends BaseService { } const startId = getStartId(createId, backfillCheckpoint); - const backfill = this.syncRepository.albumAssetExif.getBackfill(album.id, startId, endId); + const backfill = this.syncRepository.albumAssetExif.getBackfill(options, album.id, startId, endId); for await (const { updateId, ...data } of backfill) { send(response, { type: backfillType, ids: [createId, updateId], data }); @@ -459,27 +495,44 @@ export class SyncService extends BaseService { }); } - const upserts = this.syncRepository.albumAssetExif.getUpserts(auth.user.id, checkpointMap[upsertType]); - for await (const { updateId, ...data } of upserts) { - send(response, { type: upsertType, ids: [updateId], data }); + if (createCheckpoint) { + const updates = this.syncRepository.albumAssetExif.getUpdates(options, createCheckpoint, upsertCheckpoint); + for await (const { updateId, ...data } of updates) { + send(response, { type: updateType, ids: [updateId], data }); + } + } + + const creates = this.syncRepository.albumAssetExif.getCreates(options, createCheckpoint); + let first = true; + for await (const { updateId, ...data } of creates) { + if (first) { + send(response, { + type: SyncEntityType.SyncAckV1, + data: {}, + ackType: SyncEntityType.AlbumAssetExifUpdateV1, + ids: [options.nowId], + }); + first = false; + } + send(response, { type: createType, ids: [updateId], data }); } } private async syncAlbumToAssetsV1( + options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap, - auth: AuthDto, sessionId: string, ) { const deleteType = SyncEntityType.AlbumToAssetDeleteV1; - const deletes = this.syncRepository.albumToAsset.getDeletes(auth.user.id, checkpointMap[deleteType]); + const deletes = this.syncRepository.albumToAsset.getDeletes(options, checkpointMap[deleteType]); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const backfillType = SyncEntityType.AlbumToAssetBackfillV1; const backfillCheckpoint = checkpointMap[backfillType]; - const albums = await this.syncRepository.album.getCreatedAfter(auth.user.id, backfillCheckpoint?.updateId); + const albums = await this.syncRepository.album.getCreatedAfter(options, backfillCheckpoint?.updateId); const upsertType = SyncEntityType.AlbumToAssetV1; const upsertCheckpoint = checkpointMap[upsertType]; if (upsertCheckpoint) { @@ -492,7 +545,7 @@ export class SyncService extends BaseService { } const startId = getStartId(createId, backfillCheckpoint); - const backfill = this.syncRepository.albumToAsset.getBackfill(album.id, startId, endId); + const backfill = this.syncRepository.albumToAsset.getBackfill(options, album.id, startId, endId); for await (const { updateId, ...data } of backfill) { send(response, { type: backfillType, ids: [createId, updateId], data }); @@ -508,64 +561,69 @@ export class SyncService extends BaseService { }); } - const upserts = this.syncRepository.albumToAsset.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.albumToAsset.getUpserts(options, checkpointMap[upsertType]); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } } - private async syncMemoriesV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) { + private async syncMemoriesV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.MemoryDeleteV1; - const deletes = this.syncRepository.memory.getDeletes(auth.user.id, checkpointMap[deleteType]); + const deletes = this.syncRepository.memory.getDeletes(options, checkpointMap[deleteType]); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.MemoryV1; - const upserts = this.syncRepository.memory.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.memory.getUpserts(options, checkpointMap[upsertType]); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } } - private async syncMemoryAssetsV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) { + private async syncMemoryAssetsV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.MemoryToAssetDeleteV1; - const deletes = this.syncRepository.memoryToAsset.getDeletes(auth.user.id, checkpointMap[deleteType]); + const deletes = this.syncRepository.memoryToAsset.getDeletes(options, checkpointMap[deleteType]); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.MemoryToAssetV1; - const upserts = this.syncRepository.memoryToAsset.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.memoryToAsset.getUpserts(options, checkpointMap[upsertType]); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } } - private async syncStackV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) { + private async syncStackV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.StackDeleteV1; - const deletes = this.syncRepository.stack.getDeletes(auth.user.id, checkpointMap[deleteType]); + const deletes = this.syncRepository.stack.getDeletes(options, checkpointMap[deleteType]); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.StackV1; - const upserts = this.syncRepository.stack.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.stack.getUpserts(options, checkpointMap[upsertType]); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } } - private async syncPartnerStackV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto, sessionId: string) { + private async syncPartnerStackV1( + options: SyncQueryOptions, + response: Writable, + checkpointMap: CheckpointMap, + sessionId: string, + ) { const deleteType = SyncEntityType.PartnerStackDeleteV1; - const deletes = this.syncRepository.partnerStack.getDeletes(auth.user.id, checkpointMap[deleteType]); + const deletes = this.syncRepository.partnerStack.getDeletes(options, checkpointMap[deleteType]); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const backfillType = SyncEntityType.PartnerStackBackfillV1; const backfillCheckpoint = checkpointMap[backfillType]; - const partners = await this.syncRepository.partner.getCreatedAfter(auth.user.id, backfillCheckpoint?.updateId); + const partners = await this.syncRepository.partner.getCreatedAfter(options, backfillCheckpoint?.updateId); const upsertType = SyncEntityType.PartnerStackV1; const upsertCheckpoint = checkpointMap[upsertType]; if (upsertCheckpoint) { @@ -578,7 +636,7 @@ export class SyncService extends BaseService { } const startId = getStartId(createId, backfillCheckpoint); - const backfill = this.syncRepository.partnerStack.getBackfill(partner.sharedById, startId, endId); + const backfill = this.syncRepository.partnerStack.getBackfill(options, partner.sharedById, startId, endId); for await (const { updateId, ...data } of backfill) { send(response, { @@ -598,50 +656,50 @@ export class SyncService extends BaseService { }); } - const upserts = this.syncRepository.partnerStack.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.partnerStack.getUpserts(options, checkpointMap[upsertType]); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } } - private async syncPeopleV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) { + private async syncPeopleV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.PersonDeleteV1; - const deletes = this.syncRepository.people.getDeletes(auth.user.id, checkpointMap[deleteType]); + const deletes = this.syncRepository.people.getDeletes(options, checkpointMap[deleteType]); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.PersonV1; - const upserts = this.syncRepository.people.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.people.getUpserts(options, checkpointMap[upsertType]); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } } - private async syncAssetFacesV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) { + private async syncAssetFacesV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.AssetFaceDeleteV1; - const deletes = this.syncRepository.assetFace.getDeletes(auth.user.id, checkpointMap[deleteType]); + const deletes = this.syncRepository.assetFace.getDeletes(options, checkpointMap[deleteType]); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.AssetFaceV1; - const upserts = this.syncRepository.assetFace.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.assetFace.getUpserts(options, checkpointMap[upsertType]); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } } - private async syncUserMetadataV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) { + private async syncUserMetadataV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.UserMetadataDeleteV1; - const deletes = this.syncRepository.userMetadata.getDeletes(auth.user.id, checkpointMap[deleteType]); + const deletes = this.syncRepository.userMetadata.getDeletes(options, checkpointMap[deleteType]); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.UserMetadataV1; - const upserts = this.syncRepository.userMetadata.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.userMetadata.getUpserts(options, checkpointMap[upsertType]); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); diff --git a/server/test/medium.factory.ts b/server/test/medium.factory.ts index 1b669e83e4..8b0878a35a 100644 --- a/server/test/medium.factory.ts +++ b/server/test/medium.factory.ts @@ -5,7 +5,15 @@ import { createHash, randomBytes } from 'node:crypto'; import { Writable } from 'node:stream'; import { AssetFace } from 'src/database'; import { AuthDto, LoginResponseDto } from 'src/dtos/auth.dto'; -import { AlbumUserRole, AssetType, AssetVisibility, MemoryType, SourceType, SyncRequestType } from 'src/enum'; +import { + AlbumUserRole, + AssetType, + AssetVisibility, + MemoryType, + SourceType, + SyncEntityType, + SyncRequestType, +} from 'src/enum'; import { AccessRepository } from 'src/repositories/access.repository'; import { ActivityRepository } from 'src/repositories/activity.repository'; import { AlbumUserRepository } from 'src/repositories/album-user.repository'; @@ -251,11 +259,16 @@ export class SyncTestContext extends MediumTestContext { async syncAckAll(auth: AuthDto, response: Array<{ type: string; ack: string }>) { const acks: Record = {}; + const syncAcks: string[] = []; for (const { type, ack } of response) { + if (type === SyncEntityType.SyncAckV1) { + syncAcks.push(ack); + continue; + } acks[type] = ack; } - await this.sut.setAcks(auth, { acks: Object.values(acks) }); + await this.sut.setAcks(auth, { acks: [...Object.values(acks), ...syncAcks] }); } } @@ -468,8 +481,6 @@ const personInsert = (person: Partial> & { ownerId: stri name: person.name || 'Test Name', ownerId: person.ownerId || newUuid(), thumbnailPath: person.thumbnailPath || '/path/to/thumbnail.jpg', - updatedAt: person.updatedAt || newDate(), - updateId: person.updateId || newUuid(), }; return { ...defaults, diff --git a/server/test/medium/specs/sync/sync-album-asset-exif.spec.ts b/server/test/medium/specs/sync/sync-album-asset-exif.spec.ts index 808a4785ce..9e994604a5 100644 --- a/server/test/medium/specs/sync/sync-album-asset-exif.spec.ts +++ b/server/test/medium/specs/sync/sync-album-asset-exif.spec.ts @@ -1,5 +1,6 @@ import { Kysely } from 'kysely'; import { AlbumUserRole, SyncEntityType, SyncRequestType } from 'src/enum'; +import { AssetRepository } from 'src/repositories/asset.repository'; import { DB } from 'src/schema'; import { SyncTestContext } from 'test/medium.factory'; import { factory } from 'test/small.factory'; @@ -13,6 +14,18 @@ const setup = async (db?: Kysely) => { return { auth, user, session, ctx }; }; +const updateSyncAck = { + ack: expect.stringContaining(SyncEntityType.AlbumAssetExifUpdateV1), + data: {}, + type: SyncEntityType.SyncAckV1, +}; + +const backfillSyncAck = { + ack: expect.stringContaining(SyncEntityType.AlbumAssetExifBackfillV1), + data: {}, + type: SyncEntityType.SyncAckV1, +}; + beforeAll(async () => { defaultDatabase = await getKyselyDB(); }); @@ -28,8 +41,8 @@ describe(SyncRequestType.AlbumAssetExifsV1, () => { await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor }); const response = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ + updateSyncAck, { ack: expect.any(String), data: { @@ -59,9 +72,10 @@ describe(SyncRequestType.AlbumAssetExifsV1, () => { state: null, timeZone: null, }, - type: SyncEntityType.AlbumAssetExifV1, + type: SyncEntityType.AlbumAssetExifCreateV1, }, ]); + expect(response).toHaveLength(2); await ctx.syncAckAll(auth, response); await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1])).resolves.toEqual([]); @@ -75,7 +89,7 @@ describe(SyncRequestType.AlbumAssetExifsV1, () => { await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id }); await expect(ctx.syncStream(auth, [SyncRequestType.AssetExifsV1])).resolves.toHaveLength(1); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1])).resolves.toHaveLength(1); + await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1])).resolves.toHaveLength(2); }); it('should not sync album asset exif for unrelated user', async () => { @@ -97,55 +111,46 @@ describe(SyncRequestType.AlbumAssetExifsV1, () => { it('should backfill album assets exif when a user shares an album with you', async () => { const { auth, ctx } = await setup(); const { user: user2 } = await ctx.newUser(); - const { asset: asset1Owner } = await ctx.newAsset({ ownerId: auth.user.id }); - await ctx.newExif({ assetId: asset1Owner.id, make: 'asset1Owner' }); - await wait(2); + const { album: album1 } = await ctx.newAlbum({ ownerId: user2.id }); + const { album: album2 } = await ctx.newAlbum({ ownerId: user2.id }); const { asset: asset1User2 } = await ctx.newAsset({ ownerId: user2.id }); await ctx.newExif({ assetId: asset1User2.id, make: 'asset1User2' }); + await ctx.newAlbumAsset({ albumId: album2.id, assetId: asset1User2.id }); await wait(2); const { asset: asset2User2 } = await ctx.newAsset({ ownerId: user2.id }); await ctx.newExif({ assetId: asset2User2.id, make: 'asset2User2' }); + await ctx.newAlbumAsset({ albumId: album2.id, assetId: asset2User2.id }); + await wait(2); + await ctx.newAlbumAsset({ albumId: album1.id, assetId: asset2User2.id }); await wait(2); const { asset: asset3User2 } = await ctx.newAsset({ ownerId: user2.id }); + await ctx.newAlbumAsset({ albumId: album2.id, assetId: asset3User2.id }); await ctx.newExif({ assetId: asset3User2.id, make: 'asset3User2' }); - const { album: album1 } = await ctx.newAlbum({ ownerId: user2.id }); - await ctx.newAlbumAsset({ albumId: album1.id, assetId: asset2User2.id }); + await wait(2); await ctx.newAlbumUser({ albumId: album1.id, userId: auth.user.id, role: AlbumUserRole.Editor }); const response = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ + updateSyncAck, { ack: expect.any(String), data: expect.objectContaining({ assetId: asset2User2.id, }), - type: SyncEntityType.AlbumAssetExifV1, + type: SyncEntityType.AlbumAssetExifCreateV1, }, ]); + expect(response).toHaveLength(2); // ack initial album asset exif sync await ctx.syncAckAll(auth, response); // create a second album - const { album: album2 } = await ctx.newAlbum({ ownerId: user2.id }); - await Promise.all( - [asset1User2.id, asset2User2.id, asset3User2.id, asset1Owner.id].map((assetId) => - ctx.newAlbumAsset({ albumId: album2.id, assetId }), - ), - ); await ctx.newAlbumUser({ albumId: album2.id, userId: auth.user.id, role: AlbumUserRole.Editor }); // should backfill the album user const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1]); expect(newResponse).toEqual([ - { - ack: expect.any(String), - data: expect.objectContaining({ - assetId: asset1Owner.id, - }), - type: SyncEntityType.AlbumAssetExifBackfillV1, - }, { ack: expect.any(String), data: expect.objectContaining({ @@ -160,21 +165,194 @@ describe(SyncRequestType.AlbumAssetExifsV1, () => { }), type: SyncEntityType.AlbumAssetExifBackfillV1, }, - { - ack: expect.stringContaining(SyncEntityType.AlbumAssetExifBackfillV1), - data: {}, - type: SyncEntityType.SyncAckV1, - }, + backfillSyncAck, + updateSyncAck, { ack: expect.any(String), data: expect.objectContaining({ assetId: asset3User2.id, }), - type: SyncEntityType.AlbumAssetExifV1, + type: SyncEntityType.AlbumAssetExifCreateV1, }, ]); + expect(newResponse).toHaveLength(5); await ctx.syncAckAll(auth, newResponse); await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1])).resolves.toEqual([]); }); + + it('should sync old asset exif when a user adds them to an album they share you', async () => { + const { auth, ctx } = await setup(); + const { user: user2 } = await ctx.newUser(); + const { asset: firstAsset } = await ctx.newAsset({ ownerId: user2.id, originalFileName: 'firstAsset' }); + await ctx.newExif({ assetId: firstAsset.id, make: 'firstAsset' }); + const { asset: secondAsset } = await ctx.newAsset({ ownerId: user2.id, originalFileName: 'secondAsset' }); + await ctx.newExif({ assetId: secondAsset.id, make: 'secondAsset' }); + const { asset: album1Asset } = await ctx.newAsset({ ownerId: user2.id, originalFileName: 'album1Asset' }); + await ctx.newExif({ assetId: album1Asset.id, make: 'album1Asset' }); + const { album: album1 } = await ctx.newAlbum({ ownerId: user2.id }); + const { album: album2 } = await ctx.newAlbum({ ownerId: user2.id }); + await ctx.newAlbumAsset({ albumId: album2.id, assetId: firstAsset.id }); + await wait(2); + await ctx.newAlbumAsset({ albumId: album1.id, assetId: album1Asset.id }); + await ctx.newAlbumUser({ albumId: album1.id, userId: auth.user.id, role: AlbumUserRole.Editor }); + + const firstAlbumResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1]); + expect(firstAlbumResponse).toEqual([ + updateSyncAck, + { + ack: expect.any(String), + data: expect.objectContaining({ + assetId: album1Asset.id, + }), + type: SyncEntityType.AlbumAssetExifCreateV1, + }, + ]); + expect(firstAlbumResponse).toHaveLength(2); + + await ctx.syncAckAll(auth, firstAlbumResponse); + + await ctx.newAlbumUser({ albumId: album2.id, userId: auth.user.id, role: AlbumUserRole.Editor }); + + const response = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1]); + expect(response).toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + assetId: firstAsset.id, + }), + type: SyncEntityType.AlbumAssetExifBackfillV1, + }, + backfillSyncAck, + ]); + expect(response).toHaveLength(2); + + // ack initial album asset sync + await ctx.syncAckAll(auth, response); + + await ctx.newAlbumAsset({ albumId: album2.id, assetId: secondAsset.id }); + await wait(2); + + // should backfill the new asset even though it's older than the first asset + const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1]); + expect(newResponse).toEqual([ + updateSyncAck, + { + ack: expect.any(String), + data: expect.objectContaining({ + assetId: secondAsset.id, + }), + type: SyncEntityType.AlbumAssetExifCreateV1, + }, + ]); + expect(newResponse).toHaveLength(2); + + await ctx.syncAckAll(auth, newResponse); + await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1])).resolves.toEqual([]); + }); + + it('should sync asset exif updates for an album shared with you', async () => { + const { auth, ctx } = await setup(); + const { user: user2 } = await ctx.newUser(); + const { asset } = await ctx.newAsset({ ownerId: user2.id }); + await ctx.newExif({ assetId: asset.id, make: 'asset' }); + const { album } = await ctx.newAlbum({ ownerId: user2.id }); + await wait(2); + await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id }); + await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor }); + + const response = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1]); + expect(response).toHaveLength(2); + expect(response).toEqual([ + updateSyncAck, + { + ack: expect.any(String), + data: expect.objectContaining({ + assetId: asset.id, + }), + type: SyncEntityType.AlbumAssetExifCreateV1, + }, + ]); + + await ctx.syncAckAll(auth, response); + + // update the asset + const assetRepository = ctx.get(AssetRepository); + await assetRepository.upsertExif({ + assetId: asset.id, + city: 'New City', + }); + + const updateResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1]); + expect(updateResponse).toHaveLength(1); + expect(updateResponse).toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + assetId: asset.id, + city: 'New City', + }), + type: SyncEntityType.AlbumAssetExifUpdateV1, + }, + ]); + }); + + it('should sync delayed asset exif creates for an album shared with you', async () => { + const { auth, ctx } = await setup(); + const { user: user2 } = await ctx.newUser(); + const { asset: assetWithExif } = await ctx.newAsset({ ownerId: user2.id }); + await ctx.newExif({ assetId: assetWithExif.id, make: 'assetWithExif' }); + const { asset: assetDelayedExif } = await ctx.newAsset({ ownerId: user2.id }); + const { album } = await ctx.newAlbum({ ownerId: user2.id }); + const { asset: newerAsset } = await ctx.newAsset({ ownerId: user2.id }); + await ctx.newExif({ assetId: newerAsset.id, make: 'newerAsset' }); + await ctx.newAlbumAsset({ albumId: album.id, assetId: assetWithExif.id }); + await wait(2); + await ctx.newAlbumAsset({ albumId: album.id, assetId: assetDelayedExif.id }); + await wait(2); + await ctx.newAlbumAsset({ albumId: album.id, assetId: newerAsset.id }); + await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor }); + + const response = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1]); + expect(response).toEqual([ + updateSyncAck, + { + ack: expect.any(String), + data: expect.objectContaining({ + assetId: assetWithExif.id, + }), + type: SyncEntityType.AlbumAssetExifCreateV1, + }, + { + ack: expect.any(String), + data: expect.objectContaining({ + assetId: newerAsset.id, + }), + type: SyncEntityType.AlbumAssetExifCreateV1, + }, + ]); + expect(response).toHaveLength(3); + + await ctx.syncAckAll(auth, response); + + // update the asset + const assetRepository = ctx.get(AssetRepository); + await assetRepository.upsertExif({ + assetId: assetDelayedExif.id, + city: 'Delayed Exif', + }); + + const updateResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1]); + expect(updateResponse).toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + assetId: assetDelayedExif.id, + city: 'Delayed Exif', + }), + type: SyncEntityType.AlbumAssetExifUpdateV1, + }, + ]); + expect(updateResponse).toHaveLength(1); + }); }); diff --git a/server/test/medium/specs/sync/sync-album-asset.spec.ts b/server/test/medium/specs/sync/sync-album-asset.spec.ts index 3002b99071..cbc60a2c5a 100644 --- a/server/test/medium/specs/sync/sync-album-asset.spec.ts +++ b/server/test/medium/specs/sync/sync-album-asset.spec.ts @@ -1,5 +1,6 @@ import { Kysely } from 'kysely'; import { AlbumUserRole, SyncEntityType, SyncRequestType } from 'src/enum'; +import { AssetRepository } from 'src/repositories/asset.repository'; import { DB } from 'src/schema'; import { SyncTestContext } from 'test/medium.factory'; import { factory } from 'test/small.factory'; @@ -13,6 +14,18 @@ const setup = async (db?: Kysely) => { return { auth, user, session, ctx }; }; +const updateSyncAck = { + ack: expect.stringContaining(SyncEntityType.AlbumAssetUpdateV1), + data: {}, + type: SyncEntityType.SyncAckV1, +}; + +const backfillSyncAck = { + ack: expect.stringContaining(SyncEntityType.AlbumAssetBackfillV1), + data: {}, + type: SyncEntityType.SyncAckV1, +}; + beforeAll(async () => { defaultDatabase = await getKyselyDB(); }); @@ -45,8 +58,9 @@ describe(SyncRequestType.AlbumAssetsV1, () => { await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor }); const response = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1]); - expect(response).toHaveLength(1); + expect(response).toHaveLength(2); expect(response).toEqual([ + updateSyncAck, { ack: expect.any(String), data: { @@ -67,7 +81,7 @@ describe(SyncRequestType.AlbumAssetsV1, () => { stackId: asset.stackId, libraryId: asset.libraryId, }, - type: SyncEntityType.AlbumAssetV1, + type: SyncEntityType.AlbumAssetCreateV1, }, ]); @@ -82,7 +96,7 @@ describe(SyncRequestType.AlbumAssetsV1, () => { await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id }); await expect(ctx.syncStream(auth, [SyncRequestType.AssetsV1])).resolves.toHaveLength(1); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1])).resolves.toHaveLength(1); + await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1])).resolves.toHaveLength(2); }); it('should not sync album asset for unrelated user', async () => { @@ -103,52 +117,42 @@ describe(SyncRequestType.AlbumAssetsV1, () => { it('should backfill album assets when a user shares an album with you', async () => { const { auth, ctx } = await setup(); const { user: user2 } = await ctx.newUser(); - const { asset: asset1Owner } = await ctx.newAsset({ ownerId: auth.user.id }); - await wait(2); + const { album: album1 } = await ctx.newAlbum({ ownerId: user2.id }); + const { album: album2 } = await ctx.newAlbum({ ownerId: user2.id }); const { asset: asset1User2 } = await ctx.newAsset({ ownerId: user2.id }); + await ctx.newAlbumAsset({ albumId: album2.id, assetId: asset1User2.id }); await wait(2); const { asset: asset2User2 } = await ctx.newAsset({ ownerId: user2.id }); + await ctx.newAlbumAsset({ albumId: album2.id, assetId: asset2User2.id }); + await wait(2); + await ctx.newAlbumAsset({ albumId: album1.id, assetId: asset2User2.id }); await wait(2); const { asset: asset3User2 } = await ctx.newAsset({ ownerId: user2.id }); + await ctx.newAlbumAsset({ albumId: album2.id, assetId: asset3User2.id }); await wait(2); - const { album: album1 } = await ctx.newAlbum({ ownerId: user2.id }); - await ctx.newAlbumAsset({ albumId: album1.id, assetId: asset2User2.id }); await ctx.newAlbumUser({ albumId: album1.id, userId: auth.user.id, role: AlbumUserRole.Editor }); const response = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1]); - expect(response).toHaveLength(1); + expect(response).toHaveLength(2); expect(response).toEqual([ + updateSyncAck, { ack: expect.any(String), data: expect.objectContaining({ id: asset2User2.id, }), - type: SyncEntityType.AlbumAssetV1, + type: SyncEntityType.AlbumAssetCreateV1, }, ]); // ack initial album asset sync await ctx.syncAckAll(auth, response); - // create a second album - const { album: album2 } = await ctx.newAlbum({ ownerId: user2.id }); - await Promise.all( - [asset1User2.id, asset2User2.id, asset3User2.id, asset1Owner.id].map((assetId) => - ctx.newAlbumAsset({ albumId: album2.id, assetId }), - ), - ); await ctx.newAlbumUser({ albumId: album2.id, userId: auth.user.id, role: AlbumUserRole.Editor }); // should backfill the album user const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1]); expect(newResponse).toEqual([ - { - ack: expect.any(String), - data: expect.objectContaining({ - id: asset1Owner.id, - }), - type: SyncEntityType.AlbumAssetBackfillV1, - }, { ack: expect.any(String), data: expect.objectContaining({ @@ -163,21 +167,129 @@ describe(SyncRequestType.AlbumAssetsV1, () => { }), type: SyncEntityType.AlbumAssetBackfillV1, }, - { - ack: expect.stringContaining(SyncEntityType.AlbumAssetBackfillV1), - data: {}, - type: SyncEntityType.SyncAckV1, - }, + backfillSyncAck, + updateSyncAck, { ack: expect.any(String), data: expect.objectContaining({ id: asset3User2.id, }), - type: SyncEntityType.AlbumAssetV1, + type: SyncEntityType.AlbumAssetCreateV1, }, ]); await ctx.syncAckAll(auth, newResponse); await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1])).resolves.toEqual([]); }); + + it('should sync old assets when a user adds them to an album they share you', async () => { + const { auth, ctx } = await setup(); + const { user: user2 } = await ctx.newUser(); + const { asset: firstAsset } = await ctx.newAsset({ ownerId: user2.id, originalFileName: 'firstAsset' }); + const { asset: secondAsset } = await ctx.newAsset({ ownerId: user2.id, originalFileName: 'secondAsset' }); + const { asset: album1Asset } = await ctx.newAsset({ ownerId: user2.id, originalFileName: 'album1Asset' }); + const { album: album1 } = await ctx.newAlbum({ ownerId: user2.id }); + const { album: album2 } = await ctx.newAlbum({ ownerId: user2.id }); + await ctx.newAlbumAsset({ albumId: album2.id, assetId: firstAsset.id }); + await wait(2); + await ctx.newAlbumAsset({ albumId: album1.id, assetId: album1Asset.id }); + await ctx.newAlbumUser({ albumId: album1.id, userId: auth.user.id, role: AlbumUserRole.Editor }); + + const firstAlbumResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1]); + expect(firstAlbumResponse).toHaveLength(2); + expect(firstAlbumResponse).toEqual([ + updateSyncAck, + { + ack: expect.any(String), + data: expect.objectContaining({ + id: album1Asset.id, + }), + type: SyncEntityType.AlbumAssetCreateV1, + }, + ]); + + await ctx.syncAckAll(auth, firstAlbumResponse); + + await ctx.newAlbumUser({ albumId: album2.id, userId: auth.user.id, role: AlbumUserRole.Editor }); + + const response = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1]); + // expect(response).toHaveLength(2); + expect(response).toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + id: firstAsset.id, + }), + type: SyncEntityType.AlbumAssetBackfillV1, + }, + backfillSyncAck, + ]); + + // ack initial album asset sync + await ctx.syncAckAll(auth, response); + + await ctx.newAlbumAsset({ albumId: album2.id, assetId: secondAsset.id }); + await wait(2); + + // should backfill the new asset even though it's older than the first asset + const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1]); + expect(newResponse).toEqual([ + updateSyncAck, + { + ack: expect.any(String), + data: expect.objectContaining({ + id: secondAsset.id, + }), + type: SyncEntityType.AlbumAssetCreateV1, + }, + ]); + + await ctx.syncAckAll(auth, newResponse); + await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1])).resolves.toEqual([]); + }); + + it('should sync asset updates for an album shared with you', async () => { + const { auth, ctx } = await setup(); + const { user: user2 } = await ctx.newUser(); + const { asset } = await ctx.newAsset({ ownerId: user2.id, isFavorite: false }); + const { album } = await ctx.newAlbum({ ownerId: user2.id }); + await wait(2); + await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id }); + await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor }); + + const response = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1]); + expect(response).toHaveLength(2); + expect(response).toEqual([ + updateSyncAck, + { + ack: expect.any(String), + data: expect.objectContaining({ + id: asset.id, + }), + type: SyncEntityType.AlbumAssetCreateV1, + }, + ]); + + await ctx.syncAckAll(auth, response); + + // update the asset + const assetRepository = ctx.get(AssetRepository); + await assetRepository.update({ + id: asset.id, + isFavorite: true, + }); + + const updateResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1]); + expect(updateResponse).toHaveLength(1); + expect(updateResponse).toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + id: asset.id, + isFavorite: true, + }), + type: SyncEntityType.AlbumAssetUpdateV1, + }, + ]); + }); }); diff --git a/server/test/utils.ts b/server/test/utils.ts index 9f212578c0..95f9741d7c 100644 --- a/server/test/utils.ts +++ b/server/test/utils.ts @@ -446,6 +446,16 @@ export async function* makeStream(items: T[] = []): AsyncIterableIterator } } -export const wait = (ms: number) => { - return new Promise((resolve) => setTimeout(resolve, ms)); +export const wait = (ms: number): Promise => { + return new Promise((resolve) => { + const target = performance.now() + ms; + const checkDone = () => { + if (performance.now() >= target) { + resolve(); + } else { + setTimeout(checkDone, 1); // Check again after 1ms + } + }; + setTimeout(checkDone, ms); + }); }; From 58dd6f094c02fb957fb3375aba98cb784006aa27 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 19:58:08 +0200 Subject: [PATCH 179/748] chore(deps): update dependency @types/bcrypt to v6 (#20669) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- server/package-lock.json | 8 ++++---- server/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/server/package-lock.json b/server/package-lock.json index 2fff7747f9..039f214d54 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -99,7 +99,7 @@ "@testcontainers/redis": "^11.0.0", "@types/archiver": "^6.0.0", "@types/async-lock": "^1.4.2", - "@types/bcrypt": "^5.0.0", + "@types/bcrypt": "^6.0.0", "@types/body-parser": "^1.19.6", "@types/compression": "^1.7.5", "@types/cookie-parser": "^1.4.8", @@ -6547,9 +6547,9 @@ "license": "MIT" }, "node_modules/@types/bcrypt": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.2.tgz", - "integrity": "sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/server/package.json b/server/package.json index 100dd33d47..ea83743a18 100644 --- a/server/package.json +++ b/server/package.json @@ -124,7 +124,7 @@ "@testcontainers/redis": "^11.0.0", "@types/archiver": "^6.0.0", "@types/async-lock": "^1.4.2", - "@types/bcrypt": "^5.0.0", + "@types/bcrypt": "^6.0.0", "@types/body-parser": "^1.19.6", "@types/compression": "^1.7.5", "@types/cookie-parser": "^1.4.8", From 9567a2a5606d4bdc9c016dbf3b0206d2b32cb8e9 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 5 Aug 2025 14:18:57 -0500 Subject: [PATCH 180/748] fix: delete local asset show twice (#20700) * chore: better button width * fix: delete local action show twice --- .../download_action_button.widget.dart | 1 + ...ove_to_lock_folder_action_button.widget.dart | 2 +- .../general_bottom_sheet.widget.dart | 17 ++++++----------- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/mobile/lib/presentation/widgets/action_buttons/download_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/download_action_button.widget.dart index a6464308e2..7c0db5ed9a 100644 --- a/mobile/lib/presentation/widgets/action_buttons/download_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/download_action_button.widget.dart @@ -46,6 +46,7 @@ class DownloadActionButton extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { return BaseActionButton( iconData: Icons.download, + maxWidth: 95, label: "download".t(context: context), onPressed: () => _onTap(context, ref), ); diff --git a/mobile/lib/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart index d1ea85bcc4..78b9e3cde6 100644 --- a/mobile/lib/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart @@ -45,7 +45,7 @@ class MoveToLockFolderActionButton extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return BaseActionButton( - maxWidth: 110.0, + maxWidth: 115.0, iconData: Icons.lock_outline_rounded, label: "move_to_locked_folder".t(context: context), onPressed: () => _onTap(context, ref), diff --git a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart index 07b0ea6da8..f1f092d2e2 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart @@ -94,22 +94,17 @@ class _GeneralBottomSheetState extends ConsumerState { const ArchiveActionButton(source: ActionSource.timeline), const FavoriteActionButton(source: ActionSource.timeline), const DownloadActionButton(source: ActionSource.timeline), - isTrashEnable - ? const TrashActionButton(source: ActionSource.timeline) - : const DeletePermanentActionButton(source: ActionSource.timeline), - const DeleteActionButton(source: ActionSource.timeline), - if (multiselect.hasLocal || multiselect.hasMerged) ...[ - const DeleteLocalActionButton(source: ActionSource.timeline), - ], const EditDateTimeActionButton(source: ActionSource.timeline), const EditLocationActionButton(source: ActionSource.timeline), const MoveToLockFolderActionButton(source: ActionSource.timeline), const StackActionButton(source: ActionSource.timeline), + isTrashEnable + ? const TrashActionButton(source: ActionSource.timeline) + : const DeletePermanentActionButton(source: ActionSource.timeline), + const DeleteActionButton(source: ActionSource.timeline), ], - if (multiselect.hasLocal) ...[ - const DeleteLocalActionButton(source: ActionSource.timeline), - const UploadActionButton(source: ActionSource.timeline), - ], + if (multiselect.hasLocal || multiselect.hasMerged) const DeleteLocalActionButton(source: ActionSource.timeline), + if (multiselect.hasLocal) const UploadActionButton(source: ActionSource.timeline), ], slivers: [ const AddToAlbumHeader(), From 0a9cbf01d214590ce0cd68fbbe7b9fe5da536a41 Mon Sep 17 00:00:00 2001 From: Zack Pollard Date: Tue, 5 Aug 2025 21:30:19 +0100 Subject: [PATCH 181/748] feat: ack sync reset (#20703) --- server/src/database.ts | 3 +- server/src/dtos/session.dto.ts | 5 ++- server/src/queries/session.repository.sql | 9 +++- server/src/repositories/session.repository.ts | 10 +++++ server/src/services/auth.service.spec.ts | 4 -- server/src/services/auth.service.ts | 1 - server/src/services/sync.service.ts | 13 ++++-- .../test/medium/specs/sync/sync-reset.spec.ts | 41 ++++++++++++++++--- server/test/small.factory.ts | 1 - 9 files changed, 67 insertions(+), 20 deletions(-) diff --git a/server/src/database.ts b/server/src/database.ts index e7946cd8fb..f472c643ee 100644 --- a/server/src/database.ts +++ b/server/src/database.ts @@ -202,7 +202,6 @@ export type Album = Selectable & { export type AuthSession = { id: string; - isPendingSyncReset: boolean; hasElevatedPermission: boolean; }; @@ -309,7 +308,7 @@ export const columns = { assetFiles: ['asset_file.id', 'asset_file.path', 'asset_file.type'], authUser: ['user.id', 'user.name', 'user.email', 'user.isAdmin', 'user.quotaUsageInBytes', 'user.quotaSizeInBytes'], authApiKey: ['api_key.id', 'api_key.permissions'], - authSession: ['session.id', 'session.isPendingSyncReset', 'session.updatedAt', 'session.pinExpiresAt'], + authSession: ['session.id', 'session.updatedAt', 'session.pinExpiresAt'], authSharedLink: [ 'shared_link.id', 'shared_link.userId', diff --git a/server/src/dtos/session.dto.ts b/server/src/dtos/session.dto.ts index 0babbb9182..7ccc72a5f1 100644 --- a/server/src/dtos/session.dto.ts +++ b/server/src/dtos/session.dto.ts @@ -1,4 +1,4 @@ -import { IsInt, IsPositive, IsString } from 'class-validator'; +import { Equals, IsInt, IsPositive, IsString } from 'class-validator'; import { Session } from 'src/database'; import { Optional, ValidateBoolean } from 'src/validation'; @@ -22,7 +22,8 @@ export class SessionCreateDto { export class SessionUpdateDto { @ValidateBoolean({ optional: true }) - isPendingSyncReset?: boolean; + @Equals(true) + isPendingSyncReset?: true; } export class SessionResponseDto { diff --git a/server/src/queries/session.repository.sql b/server/src/queries/session.repository.sql index 24ffdcb5e1..34d25cce8a 100644 --- a/server/src/queries/session.repository.sql +++ b/server/src/queries/session.repository.sql @@ -10,10 +10,17 @@ from where "id" = $1 +-- SessionRepository.isPendingSyncReset +select + "isPendingSyncReset" +from + "session" +where + "id" = $1 + -- SessionRepository.getByToken select "session"."id", - "session"."isPendingSyncReset", "session"."updatedAt", "session"."pinExpiresAt", ( diff --git a/server/src/repositories/session.repository.ts b/server/src/repositories/session.repository.ts index edf999e265..cdc0ab12db 100644 --- a/server/src/repositories/session.repository.ts +++ b/server/src/repositories/session.repository.ts @@ -37,6 +37,16 @@ export class SessionRepository { .executeTakeFirst(); } + @GenerateSql({ params: [DummyValue.UUID] }) + async isPendingSyncReset(id: string) { + const result = await this.db + .selectFrom('session') + .select(['isPendingSyncReset']) + .where('id', '=', id) + .executeTakeFirst(); + return result?.isPendingSyncReset ?? false; + } + @GenerateSql({ params: [DummyValue.STRING] }) getByToken(token: string) { return this.db diff --git a/server/src/services/auth.service.spec.ts b/server/src/services/auth.service.spec.ts index 6e19292f71..a76fc13009 100644 --- a/server/src/services/auth.service.spec.ts +++ b/server/src/services/auth.service.spec.ts @@ -241,7 +241,6 @@ describe(AuthService.name, () => { const sessionWithToken = { id: session.id, updatedAt: session.updatedAt, - isPendingSyncReset: false, user: factory.authUser(), pinExpiresAt: null, }; @@ -259,7 +258,6 @@ describe(AuthService.name, () => { session: { id: session.id, hasElevatedPermission: false, - isPendingSyncReset: session.isPendingSyncReset, }, }); }); @@ -409,7 +407,6 @@ describe(AuthService.name, () => { id: session.id, updatedAt: session.updatedAt, user: factory.authUser(), - isPendingSyncReset: false, pinExpiresAt: null, }; @@ -426,7 +423,6 @@ describe(AuthService.name, () => { session: { id: session.id, hasElevatedPermission: false, - isPendingSyncReset: session.isPendingSyncReset, }, }); }); diff --git a/server/src/services/auth.service.ts b/server/src/services/auth.service.ts index fcaeb06af0..1e65ba3272 100644 --- a/server/src/services/auth.service.ts +++ b/server/src/services/auth.service.ts @@ -487,7 +487,6 @@ export class AuthService extends BaseService { user: session.user, session: { id: session.id, - isPendingSyncReset: session.isPendingSyncReset, hasElevatedPermission, }, }; diff --git a/server/src/services/sync.service.ts b/server/src/services/sync.service.ts index c7d67e7dd0..0a4144a953 100644 --- a/server/src/services/sync.service.ts +++ b/server/src/services/sync.service.ts @@ -23,7 +23,7 @@ import { SyncAck } from 'src/types'; import { getMyPartnerIds } from 'src/utils/asset.util'; import { hexOrBufferToBase64 } from 'src/utils/bytes'; import { setIsEqual } from 'src/utils/set'; -import { fromAck, mapJsonLine, serialize, SerializeOptions, toAck } from 'src/utils/sync'; +import { fromAck, serialize, SerializeOptions, toAck } from 'src/utils/sync'; type CheckpointMap = Partial>; type AssetLike = Omit & { @@ -100,6 +100,10 @@ export class SyncService extends BaseService { const checkpoints: Record> = {}; for (const ack of dto.acks) { const { type } = fromAck(ack); + if (type === SyncEntityType.SyncResetV1) { + await this.sessionRepository.resetSyncProgress(sessionId); + return; + } // TODO proper ack validation via class validator if (!Object.values(SyncEntityType).includes(type)) { throw new BadRequestException(`Invalid ack type: ${type}`); @@ -129,11 +133,12 @@ export class SyncService extends BaseService { if (dto.reset) { await this.sessionRepository.resetSyncProgress(session.id); - session.isPendingSyncReset = false; } - if (session.isPendingSyncReset) { - response.write(mapJsonLine({ type: SyncEntityType.SyncResetV1, data: {} })); + const isPendingSyncReset = await this.sessionRepository.isPendingSyncReset(session.id); + + if (isPendingSyncReset) { + send(response, { type: SyncEntityType.SyncResetV1, ids: ['reset'], data: {} }); response.end(); return; } diff --git a/server/test/medium/specs/sync/sync-reset.spec.ts b/server/test/medium/specs/sync/sync-reset.spec.ts index 4cfdc8249e..699c5dc292 100644 --- a/server/test/medium/specs/sync/sync-reset.spec.ts +++ b/server/test/medium/specs/sync/sync-reset.spec.ts @@ -1,5 +1,6 @@ import { Kysely } from 'kysely'; import { SyncEntityType, SyncRequestType } from 'src/enum'; +import { SessionRepository } from 'src/repositories/session.repository'; import { DB } from 'src/schema'; import { SyncTestContext } from 'test/medium.factory'; import { getKyselyDB } from 'test/utils'; @@ -27,10 +28,12 @@ describe(SyncEntityType.SyncResetV1, () => { it('should detect a pending sync reset', async () => { const { auth, ctx } = await setup(); - auth.session!.isPendingSyncReset = true; + await ctx.get(SessionRepository).update(auth.session!.id, { + isPendingSyncReset: true, + }); const response = await ctx.syncStream(auth, [SyncRequestType.AssetsV1]); - expect(response).toEqual([{ type: SyncEntityType.SyncResetV1, data: {} }]); + expect(response).toEqual([{ type: SyncEntityType.SyncResetV1, data: {}, ack: 'SyncResetV1|reset' }]); }); it('should not send other dtos when a reset is pending', async () => { @@ -40,10 +43,12 @@ describe(SyncEntityType.SyncResetV1, () => { await expect(ctx.syncStream(auth, [SyncRequestType.AssetsV1])).resolves.toHaveLength(1); - auth.session!.isPendingSyncReset = true; + await ctx.get(SessionRepository).update(auth.session!.id, { + isPendingSyncReset: true, + }); await expect(ctx.syncStream(auth, [SyncRequestType.AssetsV1])).resolves.toEqual([ - { type: SyncEntityType.SyncResetV1, data: {} }, + { type: SyncEntityType.SyncResetV1, data: {}, ack: 'SyncResetV1|reset' }, ]); }); @@ -52,7 +57,9 @@ describe(SyncEntityType.SyncResetV1, () => { await ctx.newAsset({ ownerId: user.id }); - auth.session!.isPendingSyncReset = true; + await ctx.get(SessionRepository).update(auth.session!.id, { + isPendingSyncReset: true, + }); await expect(ctx.syncStream(auth, [SyncRequestType.AssetsV1], true)).resolves.toEqual([ expect.objectContaining({ @@ -60,4 +67,28 @@ describe(SyncEntityType.SyncResetV1, () => { }), ]); }); + + it('should reset the sync progress', async () => { + const { auth, user, ctx } = await setup(); + + await ctx.newAsset({ ownerId: user.id }); + + const response = await ctx.syncStream(auth, [SyncRequestType.AssetsV1]); + await ctx.syncAckAll(auth, response); + + await ctx.get(SessionRepository).update(auth.session!.id, { + isPendingSyncReset: true, + }); + + const resetResponse = await ctx.syncStream(auth, [SyncRequestType.AssetsV1]); + + await ctx.syncAckAll(auth, resetResponse); + + const postResetResponse = await ctx.syncStream(auth, [SyncRequestType.AssetsV1]); + expect(postResetResponse).toEqual([ + expect.objectContaining({ + type: SyncEntityType.AssetV1, + }), + ]); + }); }); diff --git a/server/test/small.factory.ts b/server/test/small.factory.ts index d533f0f7c3..8b44b6eddc 100644 --- a/server/test/small.factory.ts +++ b/server/test/small.factory.ts @@ -60,7 +60,6 @@ const authFactory = ({ if (session) { auth.session = { id: session.id, - isPendingSyncReset: false, hasElevatedPermission: false, }; } From 4179c8a17d5b1d33e7ccdb8381a44189e978e8ab Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Tue, 5 Aug 2025 16:16:13 -0500 Subject: [PATCH 182/748] fix(mobile): filter people that have less than 3 faces (#20705) --- mobile/lib/infrastructure/repositories/people.repository.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/lib/infrastructure/repositories/people.repository.dart b/mobile/lib/infrastructure/repositories/people.repository.dart index 9c6ed74636..e2b8646dba 100644 --- a/mobile/lib/infrastructure/repositories/people.repository.dart +++ b/mobile/lib/infrastructure/repositories/people.repository.dart @@ -24,7 +24,7 @@ class DriftPeopleRepository extends DriftDatabaseRepository { leftOuterJoin(_db.assetFaceEntity, _db.assetFaceEntity.personId.equalsExp(_db.personEntity.id)), ]) ..where(_db.personEntity.isHidden.equals(false)) - ..groupBy([_db.personEntity.id]) + ..groupBy([_db.personEntity.id], having: _db.assetFaceEntity.id.count().isBiggerOrEqualValue(3)) ..orderBy([ OrderingTerm(expression: _db.personEntity.name.equals('').not(), mode: OrderingMode.desc), OrderingTerm(expression: _db.assetFaceEntity.id.count(), mode: OrderingMode.desc), From d430b869ac066105e281f7e6ddf0dd90e75c250a Mon Sep 17 00:00:00 2001 From: Gaurav Yadav Date: Wed, 6 Aug 2025 02:52:19 +0530 Subject: [PATCH 183/748] fix: shared link custom URL photo access authentication (#20534) --- web/src/lib/utils/navigation.ts | 4 ++-- web/src/lib/utils/shared-links.ts | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/web/src/lib/utils/navigation.ts b/web/src/lib/utils/navigation.ts index eb974c3f25..642c8165df 100644 --- a/web/src/lib/utils/navigation.ts +++ b/web/src/lib/utils/navigation.ts @@ -23,8 +23,8 @@ export const isLockedFolderRoute = (route?: string | null) => !!route?.startsWit export const isAssetViewerRoute = (target?: NavigationTarget | null) => !!(target?.route.id?.endsWith('/[[assetId=id]]') && 'assetId' in (target?.params || {})); -export function getAssetInfoFromParam({ assetId, key }: { assetId?: string; key?: string }) { - return assetId ? getAssetInfo({ id: assetId, key }) : undefined; +export function getAssetInfoFromParam({ assetId, slug, key }: { assetId?: string; key?: string; slug?: string }) { + return assetId ? getAssetInfo({ id: assetId, slug, key }) : undefined; } function currentUrlWithoutAsset() { diff --git a/web/src/lib/utils/shared-links.ts b/web/src/lib/utils/shared-links.ts index 824554220f..661ccf6090 100644 --- a/web/src/lib/utils/shared-links.ts +++ b/web/src/lib/utils/shared-links.ts @@ -17,7 +17,13 @@ export const asQueryString = ({ slug, key }: { slug?: string; key?: string }) => return params.toString(); }; -export const loadSharedLink = async ({ url, params }: { url: URL; params: { key?: string; slug?: string } }) => { +export const loadSharedLink = async ({ + url, + params, +}: { + url: URL; + params: { key?: string; slug?: string; assetId?: string }; +}) => { const { key, slug } = params; await authenticate(url, { public: true }); From a5760129f0c95e329f73979c247c5b290831e9f3 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Tue, 5 Aug 2025 17:29:01 -0400 Subject: [PATCH 184/748] fix: custom-url ssr (#20704) --- e2e/src/api/specs/shared-link.e2e-spec.ts | 14 +++++++++- server/src/services/api.service.ts | 33 ++++++++++++++++------- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/e2e/src/api/specs/shared-link.e2e-spec.ts b/e2e/src/api/specs/shared-link.e2e-spec.ts index d9f8672c66..f56a058529 100644 --- a/e2e/src/api/specs/shared-link.e2e-spec.ts +++ b/e2e/src/api/specs/shared-link.e2e-spec.ts @@ -9,7 +9,7 @@ import { } from '@immich/sdk'; import { createUserDto, uuidDto } from 'src/fixtures'; import { errorDto } from 'src/responses'; -import { app, asBearerAuth, shareUrl, utils } from 'src/utils'; +import { app, asBearerAuth, baseUrl, shareUrl, utils } from 'src/utils'; import request from 'supertest'; import { beforeAll, describe, expect, it } from 'vitest'; @@ -78,6 +78,7 @@ describe('/shared-links', () => { type: SharedLinkType.Album, albumId: metadataAlbum.id, showMetadata: true, + slug: 'metadata-album', }), utils.createSharedLink(user1.accessToken, { type: SharedLinkType.Album, @@ -138,6 +139,17 @@ describe('/shared-links', () => { }); }); + describe('GET /s/:slug', () => { + it('should work for slug auth', async () => { + const resp = await request(baseUrl).get(`/s/${linkWithMetadata.slug}`); + expect(resp.status).toBe(200); + expect(resp.header['content-type']).toContain('text/html'); + expect(resp.text).toContain( + ``, + ); + }); + }); + describe('GET /shared-links', () => { it('should require authentication', async () => { const { status, body } = await request(app).get('/shared-links'); diff --git a/server/src/services/api.service.ts b/server/src/services/api.service.ts index ced74482c2..143b470750 100644 --- a/server/src/services/api.service.ts +++ b/server/src/services/api.service.ts @@ -76,23 +76,36 @@ export class ApiService { let status = 200; let html = index; - const shareMatches = request.url.match(/^\/share\/(.+)$/); - if (shareMatches) { + const defaultDomain = request.host ? `${request.protocol}://${request.host}` : undefined; + + let meta: OpenGraphTags | null = null; + + const shareKey = request.url.match(/^\/share\/(.+)$/); + if (shareKey) { try { - const key = shareMatches[1]; + const key = shareKey[1]; const auth = await this.authService.validateSharedLinkKey(key); - const meta = await this.sharedLinkService.getMetadataTags( - auth, - request.host ? `${request.protocol}://${request.host}` : undefined, - ); - if (meta) { - html = render(index, meta); - } + meta = await this.sharedLinkService.getMetadataTags(auth, defaultDomain); } catch { status = 404; } } + const shareSlug = request.url.match(/^\/s\/(.+)$/); + if (shareSlug) { + try { + const slug = shareSlug[1]; + const auth = await this.authService.validateSharedLinkSlug(slug); + meta = await this.sharedLinkService.getMetadataTags(auth, defaultDomain); + } catch { + status = 404; + } + } + + if (meta) { + html = render(index, meta); + } + res.status(status).type('text/html').header('Cache-Control', 'no-store').send(html); }; } From fa26d0de3301f1d6f777a6d8469115578db2b956 Mon Sep 17 00:00:00 2001 From: Zack Pollard Date: Wed, 6 Aug 2025 13:34:12 +0100 Subject: [PATCH 185/748] refactor: new helper methods that work for all sync queries (#20690) refactor: new helper methods that work for all sync queries --- server/src/queries/sync.repository.sql | 490 ++++++++++------- server/src/repositories/sync.repository.ts | 580 ++++++++------------- server/src/services/sync.service.ts | 146 ++++-- 3 files changed, 639 insertions(+), 577 deletions(-) diff --git a/server/src/queries/sync.repository.sql b/server/src/queries/sync.repository.sql index 0bd5b9f47d..80021368a0 100644 --- a/server/src/queries/sync.repository.sql +++ b/server/src/queries/sync.repository.sql @@ -18,12 +18,13 @@ select "id", "albumId" from - "album_audit" + "album_audit" as "album_audit" where - "userId" = $1 - and "id" < $2 + "album_audit"."id" < $1 + and "album_audit"."id" > $2 + and "userId" = $3 order by - "id" asc + "album_audit"."id" asc -- SyncRepository.album.getUpserts select distinct @@ -38,13 +39,14 @@ select distinct "album"."order", "album"."updateId" from - "album" + "album" as "album" left join "album_user" as "album_users" on "album"."id" = "album_users"."albumsId" where "album"."updateId" < $1 + and "album"."updateId" > $2 and ( - "album"."ownerId" = $2 - or "album_users"."usersId" = $3 + "album"."ownerId" = $3 + or "album_users"."usersId" = $4 ) order by "album"."updateId" asc @@ -69,16 +71,51 @@ select "asset"."libraryId", "album_asset"."updateId" from - "album_asset" + "album_asset" as "album_asset" inner join "asset" on "asset"."id" = "album_asset"."assetsId" where - "album_asset"."albumsId" = $1 - and "album_asset"."updateId" < $2 - and "album_asset"."updateId" <= $3 - and "album_asset"."updateId" >= $4 + "album_asset"."updateId" < $1 + and "album_asset"."updateId" <= $2 + and "album_asset"."updateId" >= $3 + and "album_asset"."albumsId" = $4 order by "album_asset"."updateId" asc +-- SyncRepository.albumAsset.getUpdates +select + "asset"."id", + "asset"."ownerId", + "asset"."originalFileName", + "asset"."thumbhash", + "asset"."checksum", + "asset"."fileCreatedAt", + "asset"."fileModifiedAt", + "asset"."localDateTime", + "asset"."type", + "asset"."deletedAt", + "asset"."isFavorite", + "asset"."visibility", + "asset"."duration", + "asset"."livePhotoVideoId", + "asset"."stackId", + "asset"."libraryId", + "asset"."updateId" +from + "asset" as "asset" + inner join "album_asset" on "album_asset"."assetsId" = "asset"."id" + inner join "album" on "album"."id" = "album_asset"."albumsId" + left join "album_user" on "album_user"."albumsId" = "album_asset"."albumsId" +where + "asset"."updateId" < $1 + and "asset"."updateId" > $2 + and "album_asset"."updateId" <= $3 + and ( + "album"."ownerId" = $4 + or "album_user"."usersId" = $5 + ) +order by + "asset"."updateId" asc + -- SyncRepository.albumAsset.getCreates select "album_asset"."updateId", @@ -99,15 +136,16 @@ select "asset"."stackId", "asset"."libraryId" from - "album_asset" + "album_asset" as "album_asset" inner join "asset" on "asset"."id" = "album_asset"."assetsId" inner join "album" on "album"."id" = "album_asset"."albumsId" left join "album_user" on "album_user"."albumsId" = "album_asset"."albumsId" where "album_asset"."updateId" < $1 + and "album_asset"."updateId" > $2 and ( - "album"."ownerId" = $2 - or "album_user"."usersId" = $3 + "album"."ownerId" = $3 + or "album_user"."usersId" = $4 ) order by "album_asset"."updateId" asc @@ -141,16 +179,60 @@ select "asset_exif"."fps", "album_asset"."updateId" from - "album_asset" + "album_asset" as "album_asset" inner join "asset_exif" on "asset_exif"."assetId" = "album_asset"."assetsId" where - "album_asset"."albumsId" = $1 - and "album_asset"."updateId" < $2 - and "album_asset"."updateId" <= $3 - and "album_asset"."updateId" >= $4 + "album_asset"."updateId" < $1 + and "album_asset"."updateId" <= $2 + and "album_asset"."updateId" >= $3 + and "album_asset"."albumsId" = $4 order by "album_asset"."updateId" asc +-- SyncRepository.albumAssetExif.getUpdates +select + "asset_exif"."assetId", + "asset_exif"."description", + "asset_exif"."exifImageWidth", + "asset_exif"."exifImageHeight", + "asset_exif"."fileSizeInByte", + "asset_exif"."orientation", + "asset_exif"."dateTimeOriginal", + "asset_exif"."modifyDate", + "asset_exif"."timeZone", + "asset_exif"."latitude", + "asset_exif"."longitude", + "asset_exif"."projectionType", + "asset_exif"."city", + "asset_exif"."state", + "asset_exif"."country", + "asset_exif"."make", + "asset_exif"."model", + "asset_exif"."lensModel", + "asset_exif"."fNumber", + "asset_exif"."focalLength", + "asset_exif"."iso", + "asset_exif"."exposureTime", + "asset_exif"."profileDescription", + "asset_exif"."rating", + "asset_exif"."fps", + "asset_exif"."updateId" +from + "asset_exif" as "asset_exif" + inner join "album_asset" on "album_asset"."assetsId" = "asset_exif"."assetId" + inner join "album" on "album"."id" = "album_asset"."albumsId" + left join "album_user" on "album_user"."albumsId" = "album_asset"."albumsId" +where + "asset_exif"."updateId" < $1 + and "asset_exif"."updateId" > $2 + and "album_asset"."updateId" <= $3 + and ( + "album"."ownerId" = $4 + or "album_user"."usersId" = $5 + ) +order by + "asset_exif"."updateId" asc + -- SyncRepository.albumAssetExif.getCreates select "album_asset"."updateId", @@ -180,33 +262,34 @@ select "asset_exif"."rating", "asset_exif"."fps" from - "album_asset" + "album_asset" as "album_asset" inner join "asset_exif" on "asset_exif"."assetId" = "album_asset"."assetsId" inner join "album" on "album"."id" = "album_asset"."albumsId" left join "album_user" on "album_user"."albumsId" = "album_asset"."albumsId" where "album_asset"."updateId" < $1 + and "album_asset"."updateId" > $2 and ( - "album"."ownerId" = $2 - or "album_user"."usersId" = $3 + "album"."ownerId" = $3 + or "album_user"."usersId" = $4 ) order by "album_asset"."updateId" asc -- SyncRepository.albumToAsset.getBackfill select - "album_assets"."assetsId" as "assetId", - "album_assets"."albumsId" as "albumId", - "album_assets"."updateId" + "album_asset"."assetsId" as "assetId", + "album_asset"."albumsId" as "albumId", + "album_asset"."updateId" from - "album_asset" as "album_assets" + "album_asset" as "album_asset" where - "album_assets"."albumsId" = $1 - and "album_assets"."updateId" < $2 - and "album_assets"."updateId" <= $3 - and "album_assets"."updateId" >= $4 + "album_asset"."updateId" < $1 + and "album_asset"."updateId" <= $2 + and "album_asset"."updateId" >= $3 + and "album_asset"."albumsId" = $4 order by - "album_assets"."updateId" asc + "album_asset"."updateId" asc -- SyncRepository.albumToAsset.getDeletes select @@ -214,15 +297,17 @@ select "assetId", "albumId" from - "album_asset_audit" + "album_asset_audit" as "album_asset_audit" where - "albumId" in ( + "album_asset_audit"."id" < $1 + and "album_asset_audit"."id" > $2 + and "albumId" in ( select "id" from "album" where - "ownerId" = $1 + "ownerId" = $3 union ( select @@ -230,12 +315,11 @@ where from "album_user" where - "album_user"."usersId" = $2 + "album_user"."usersId" = $4 ) ) - and "id" < $3 order by - "id" asc + "album_asset_audit"."id" asc -- SyncRepository.albumToAsset.getUpserts select @@ -243,14 +327,15 @@ select "album_asset"."albumsId" as "albumId", "album_asset"."updateId" from - "album_asset" + "album_asset" as "album_asset" inner join "album" on "album"."id" = "album_asset"."albumsId" left join "album_user" on "album_user"."albumsId" = "album_asset"."albumsId" where "album_asset"."updateId" < $1 + and "album_asset"."updateId" > $2 and ( - "album"."ownerId" = $2 - or "album_user"."usersId" = $3 + "album"."ownerId" = $3 + or "album_user"."usersId" = $4 ) order by "album_asset"."updateId" asc @@ -262,14 +347,14 @@ select "album_user"."role", "album_user"."updateId" from - "album_user" + "album_user" as "album_user" where - "albumsId" = $1 - and "updateId" < $2 - and "updateId" <= $3 - and "updateId" >= $4 + "album_user"."updateId" < $1 + and "album_user"."updateId" <= $2 + and "album_user"."updateId" >= $3 + and "albumsId" = $4 order by - "updateId" asc + "album_user"."updateId" asc -- SyncRepository.albumUser.getDeletes select @@ -277,15 +362,17 @@ select "userId", "albumId" from - "album_user_audit" + "album_user_audit" as "album_user_audit" where - "albumId" in ( + "album_user_audit"."id" < $1 + and "album_user_audit"."id" > $2 + and "albumId" in ( select "id" from "album" where - "ownerId" = $1 + "ownerId" = $3 union ( select @@ -293,12 +380,11 @@ where from "album_user" where - "album_user"."usersId" = $2 + "album_user"."usersId" = $4 ) ) - and "id" < $3 order by - "id" asc + "album_user_audit"."id" asc -- SyncRepository.albumUser.getUpserts select @@ -307,16 +393,17 @@ select "album_user"."role", "album_user"."updateId" from - "album_user" + "album_user" as "album_user" where "album_user"."updateId" < $1 + and "album_user"."updateId" > $2 and "album_user"."albumsId" in ( select "id" from "album" where - "ownerId" = $2 + "ownerId" = $3 union ( select @@ -324,7 +411,7 @@ where from "album_user" as "albumUsers" where - "albumUsers"."usersId" = $3 + "albumUsers"."usersId" = $4 ) ) order by @@ -335,12 +422,13 @@ select "id", "assetId" from - "asset_audit" + "asset_audit" as "asset_audit" where - "ownerId" = $1 - and "id" < $2 + "asset_audit"."id" < $1 + and "asset_audit"."id" > $2 + and "ownerId" = $3 order by - "id" asc + "asset_audit"."id" asc -- SyncRepository.asset.getUpserts select @@ -362,12 +450,13 @@ select "asset"."libraryId", "asset"."updateId" from - "asset" + "asset" as "asset" where - "ownerId" = $1 - and "updateId" < $2 + "asset"."updateId" < $1 + and "asset"."updateId" > $2 + and "ownerId" = $3 order by - "updateId" asc + "asset"."updateId" asc -- SyncRepository.assetExif.getUpserts select @@ -398,30 +487,32 @@ select "asset_exif"."fps", "asset_exif"."updateId" from - "asset_exif" + "asset_exif" as "asset_exif" where - "assetId" in ( + "asset_exif"."updateId" < $1 + and "asset_exif"."updateId" > $2 + and "assetId" in ( select "id" from "asset" where - "ownerId" = $1 + "ownerId" = $3 ) - and "updateId" < $2 order by - "updateId" asc + "asset_exif"."updateId" asc -- SyncRepository.assetFace.getDeletes select "asset_face_audit"."id", "assetFaceId" from - "asset_face_audit" + "asset_face_audit" as "asset_face_audit" left join "asset" on "asset"."id" = "asset_face_audit"."assetId" where - "asset"."ownerId" = $1 - and "asset_face_audit"."id" < $2 + "asset_face_audit"."id" < $1 + and "asset_face_audit"."id" > $2 + and "asset"."ownerId" = $3 order by "asset_face_audit"."id" asc @@ -439,25 +530,51 @@ select "sourceType", "asset_face"."updateId" from - "asset_face" + "asset_face" as "asset_face" left join "asset" on "asset"."id" = "asset_face"."assetId" where "asset_face"."updateId" < $1 - and "asset"."ownerId" = $2 + and "asset_face"."updateId" > $2 + and "asset"."ownerId" = $3 order by "asset_face"."updateId" asc +-- SyncRepository.authUser.getUpserts +select + "id", + "name", + "email", + "avatarColor", + "deletedAt", + "updateId", + "profileImagePath", + "profileChangedAt", + "isAdmin", + "pinCode", + "oauthId", + "storageLabel", + "quotaSizeInBytes", + "quotaUsageInBytes" +from + "user" as "user" +where + "user"."updateId" < $1 + and "user"."updateId" > $2 +order by + "user"."updateId" asc + -- SyncRepository.memory.getDeletes select "id", "memoryId" from - "memory_audit" + "memory_audit" as "memory_audit" where - "userId" = $1 - and "id" < $2 + "memory_audit"."id" < $1 + and "memory_audit"."id" > $2 + and "userId" = $3 order by - "id" asc + "memory_audit"."id" asc -- SyncRepository.memory.getUpserts select @@ -475,12 +592,13 @@ select "hideAt", "updateId" from - "memory" + "memory" as "memory" where - "ownerId" = $1 - and "updateId" < $2 + "memory"."updateId" < $1 + and "memory"."updateId" > $2 + and "ownerId" = $3 order by - "updateId" asc + "memory"."updateId" asc -- SyncRepository.memoryToAsset.getDeletes select @@ -488,19 +606,20 @@ select "memoryId", "assetId" from - "memory_asset_audit" + "memory_asset_audit" as "memory_asset_audit" where - "memoryId" in ( + "memory_asset_audit"."id" < $1 + and "memory_asset_audit"."id" > $2 + and "memoryId" in ( select "id" from "memory" where - "ownerId" = $1 + "ownerId" = $3 ) - and "id" < $2 order by - "id" asc + "memory_asset_audit"."id" asc -- SyncRepository.memoryToAsset.getUpserts select @@ -508,19 +627,20 @@ select "assetsId" as "assetId", "updateId" from - "memory_asset" + "memory_asset" as "memory_asset" where - "memoriesId" in ( + "memory_asset"."updateId" < $1 + and "memory_asset"."updateId" > $2 + and "memoriesId" in ( select "id" from "memory" where - "ownerId" = $1 + "ownerId" = $3 ) - and "updateId" < $2 order by - "updateId" asc + "memory_asset"."updateId" asc -- SyncRepository.partner.getCreatedAfter select @@ -530,7 +650,8 @@ from "partner" where "sharedWithId" = $1 - and "createId" < $2 + and "createId" >= $2 + and "createId" < $3 order by "partner"."createId" asc @@ -540,15 +661,16 @@ select "sharedById", "sharedWithId" from - "partner_audit" + "partner_audit" as "partner_audit" where - ( - "sharedById" = $1 - or "sharedWithId" = $2 + "partner_audit"."id" < $1 + and "partner_audit"."id" > $2 + and ( + "sharedById" = $3 + or "sharedWithId" = $4 ) - and "id" < $3 order by - "id" asc + "partner_audit"."id" asc -- SyncRepository.partner.getUpserts select @@ -557,15 +679,16 @@ select "inTimeline", "updateId" from - "partner" + "partner" as "partner" where - ( - "sharedById" = $1 - or "sharedWithId" = $2 + "partner"."updateId" < $1 + and "partner"."updateId" > $2 + and ( + "sharedById" = $3 + or "sharedWithId" = $4 ) - and "updateId" < $3 order by - "updateId" asc + "partner"."updateId" asc -- SyncRepository.partnerAsset.getBackfill select @@ -587,33 +710,34 @@ select "asset"."libraryId", "asset"."updateId" from - "asset" + "asset" as "asset" where - "ownerId" = $1 - and "updateId" < $2 - and "updateId" <= $3 - and "updateId" >= $4 + "asset"."updateId" < $1 + and "asset"."updateId" <= $2 + and "asset"."updateId" >= $3 + and "ownerId" = $4 order by - "updateId" asc + "asset"."updateId" asc -- SyncRepository.partnerAsset.getDeletes select "id", "assetId" from - "asset_audit" + "asset_audit" as "asset_audit" where - "ownerId" in ( + "asset_audit"."id" < $1 + and "asset_audit"."id" > $2 + and "ownerId" in ( select "sharedById" from "partner" where - "sharedWithId" = $1 + "sharedWithId" = $3 ) - and "id" < $2 order by - "id" asc + "asset_audit"."id" asc -- SyncRepository.partnerAsset.getUpserts select @@ -635,19 +759,20 @@ select "asset"."libraryId", "asset"."updateId" from - "asset" + "asset" as "asset" where - "ownerId" in ( + "asset"."updateId" < $1 + and "asset"."updateId" > $2 + and "ownerId" in ( select "sharedById" from "partner" where - "sharedWithId" = $1 + "sharedWithId" = $3 ) - and "updateId" < $2 order by - "updateId" asc + "asset"."updateId" asc -- SyncRepository.partnerAssetExif.getBackfill select @@ -678,13 +803,13 @@ select "asset_exif"."fps", "asset_exif"."updateId" from - "asset_exif" + "asset_exif" as "asset_exif" inner join "asset" on "asset"."id" = "asset_exif"."assetId" where - "asset"."ownerId" = $1 - and "asset_exif"."updateId" < $2 - and "asset_exif"."updateId" <= $3 - and "asset_exif"."updateId" >= $4 + "asset_exif"."updateId" < $1 + and "asset_exif"."updateId" <= $2 + and "asset_exif"."updateId" >= $3 + and "asset"."ownerId" = $4 order by "asset_exif"."updateId" asc @@ -717,9 +842,11 @@ select "asset_exif"."fps", "asset_exif"."updateId" from - "asset_exif" + "asset_exif" as "asset_exif" where - "assetId" in ( + "asset_exif"."updateId" < $1 + and "asset_exif"."updateId" > $2 + and "assetId" in ( select "id" from @@ -731,31 +858,31 @@ where from "partner" where - "sharedWithId" = $1 + "sharedWithId" = $3 ) ) - and "updateId" < $2 order by - "updateId" asc + "asset_exif"."updateId" asc -- SyncRepository.partnerStack.getDeletes select "id", "stackId" from - "stack_audit" + "stack_audit" as "stack_audit" where - "userId" in ( + "stack_audit"."id" < $1 + and "stack_audit"."id" > $2 + and "userId" in ( select "sharedById" from "partner" where - "sharedWithId" = $1 + "sharedWithId" = $3 ) - and "id" < $2 order by - "id" asc + "stack_audit"."id" asc -- SyncRepository.partnerStack.getBackfill select @@ -766,14 +893,14 @@ select "stack"."ownerId", "updateId" from - "stack" + "stack" as "stack" where - "ownerId" = $1 - and "updateId" < $2 - and "updateId" <= $3 - and "updateId" >= $4 + "stack"."updateId" < $1 + and "stack"."updateId" <= $2 + and "stack"."updateId" >= $3 + and "ownerId" = $4 order by - "updateId" asc + "stack"."updateId" asc -- SyncRepository.partnerStack.getUpserts select @@ -784,31 +911,33 @@ select "stack"."ownerId", "updateId" from - "stack" + "stack" as "stack" where - "ownerId" in ( + "stack"."updateId" < $1 + and "stack"."updateId" > $2 + and "ownerId" in ( select "sharedById" from "partner" where - "sharedWithId" = $1 + "sharedWithId" = $3 ) - and "updateId" < $2 order by - "updateId" asc + "stack"."updateId" asc -- SyncRepository.people.getDeletes select "id", "personId" from - "person_audit" + "person_audit" as "person_audit" where - "ownerId" = $1 - and "id" < $2 + "person_audit"."id" < $1 + and "person_audit"."id" > $2 + and "ownerId" = $3 order by - "id" asc + "person_audit"."id" asc -- SyncRepository.people.getUpserts select @@ -824,24 +953,26 @@ select "updateId", "faceAssetId" from - "person" + "person" as "person" where - "ownerId" = $1 - and "updateId" < $2 + "person"."updateId" < $1 + and "person"."updateId" > $2 + and "ownerId" = $3 order by - "updateId" asc + "person"."updateId" asc -- SyncRepository.stack.getDeletes select "id", "stackId" from - "stack_audit" + "stack_audit" as "stack_audit" where - "userId" = $1 - and "id" < $2 + "stack_audit"."id" < $1 + and "stack_audit"."id" > $2 + and "userId" = $3 order by - "id" asc + "stack_audit"."id" asc -- SyncRepository.stack.getUpserts select @@ -852,23 +983,25 @@ select "stack"."ownerId", "updateId" from - "stack" + "stack" as "stack" where - "ownerId" = $1 - and "updateId" < $2 + "stack"."updateId" < $1 + and "stack"."updateId" > $2 + and "ownerId" = $3 order by - "updateId" asc + "stack"."updateId" asc -- SyncRepository.user.getDeletes select "id", "userId" from - "user_audit" + "user_audit" as "user_audit" where - "id" < $1 + "user_audit"."id" < $1 + and "user_audit"."id" > $2 order by - "id" asc + "user_audit"."id" asc -- SyncRepository.user.getUpserts select @@ -881,11 +1014,12 @@ select "profileImagePath", "profileChangedAt" from - "user" + "user" as "user" where - "updateId" < $1 + "user"."updateId" < $1 + and "user"."updateId" > $2 order by - "updateId" asc + "user"."updateId" asc -- SyncRepository.userMetadata.getDeletes select @@ -893,12 +1027,13 @@ select "userId", "key" from - "user_metadata_audit" + "user_metadata_audit" as "user_metadata_audit" where - "userId" = $1 - and "id" < $2 + "user_metadata_audit"."id" < $1 + and "user_metadata_audit"."id" > $2 + and "userId" = $3 order by - "id" asc + "user_metadata_audit"."id" asc -- SyncRepository.userMetadata.getUpserts select @@ -907,9 +1042,10 @@ select "value", "updateId" from - "user_metadata" + "user_metadata" as "user_metadata" where - "userId" = $1 - and "updateId" < $2 + "user_metadata"."updateId" < $1 + and "user_metadata"."updateId" > $2 + and "userId" = $3 order by - "updateId" asc + "user_metadata"."updateId" asc diff --git a/server/src/repositories/sync.repository.ts b/server/src/repositories/sync.repository.ts index 44191ccabd..13e933fd2f 100644 --- a/server/src/repositories/sync.repository.ts +++ b/server/src/repositories/sync.repository.ts @@ -1,41 +1,47 @@ import { Injectable } from '@nestjs/common'; -import { Kysely, SelectQueryBuilder } from 'kysely'; +import { Kysely } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; import { columns } from 'src/database'; import { DummyValue, GenerateSql } from 'src/decorators'; import { DB } from 'src/schema'; import { SyncAck } from 'src/types'; -type AuditTables = - | 'user_audit' - | 'partner_audit' - | 'asset_audit' - | 'album_audit' - | 'album_user_audit' - | 'album_asset_audit' - | 'memory_audit' - | 'memory_asset_audit' - | 'stack_audit' - | 'person_audit' - | 'user_metadata_audit' - | 'asset_face_audit'; -type UpsertTables = - | 'user' - | 'partner' - | 'asset' - | 'asset_exif' - | 'album' - | 'album_user' - | 'memory' - | 'memory_asset' - | 'stack' - | 'person' - | 'user_metadata' - | 'asset_face'; +export type SyncBackfillOptions = { + nowId: string; + afterUpdateId?: string; + beforeUpdateId: string; +}; + +const dummyBackfillOptions = { + nowId: DummyValue.UUID, + beforeUpdateId: DummyValue.UUID, + afterUpdateId: DummyValue.UUID, +}; + +export type SyncCreatedAfterOptions = { + nowId: string; + userId: string; + afterCreateId?: string; +}; + +const dummyCreateAfterOptions = { + nowId: DummyValue.UUID, + userId: DummyValue.UUID, + afterCreateId: DummyValue.UUID, +}; export type SyncQueryOptions = { nowId: string; userId: string; + ack?: SyncAck; +}; + +const dummyQueryOptions = { + nowId: DummyValue.UUID, + userId: DummyValue.UUID, + ack: { + updateId: DummyValue.UUID, + }, }; @Injectable() @@ -86,30 +92,44 @@ export class SyncRepository { class BaseSync { constructor(protected db: Kysely) {} - protected auditTableFilters(nowId: string, ack?: SyncAck) { - return , D>(qb: SelectQueryBuilder) => { - const builder = qb as SelectQueryBuilder; - return builder - .where('id', '<', nowId) - .$if(!!ack, (qb) => qb.where('id', '>', ack!.updateId)) - .orderBy('id', 'asc') as SelectQueryBuilder; - }; + protected backfillQuery(t: T, { nowId, beforeUpdateId, afterUpdateId }: SyncBackfillOptions) { + const { table, ref } = this.db.dynamic; + const updateIdRef = ref(`${t}.updateId`); + + return this.db + .selectFrom(table(t).as(t)) + .where(updateIdRef, '<', nowId) + .where(updateIdRef, '<=', beforeUpdateId) + .$if(!!afterUpdateId, (qb) => qb.where(updateIdRef, '>=', afterUpdateId!)) + .orderBy(updateIdRef, 'asc'); } - protected upsertTableFilters(nowId: string, ack?: SyncAck) { - return , D>(qb: SelectQueryBuilder) => { - const builder = qb as SelectQueryBuilder; - return builder - .where('updateId', '<', nowId) - .$if(!!ack, (qb) => qb.where('updateId', '>', ack!.updateId)) - .orderBy('updateId', 'asc') as SelectQueryBuilder; - }; + protected auditQuery(t: T, { nowId, ack }: SyncQueryOptions) { + const { table, ref } = this.db.dynamic; + const idRef = ref(`${t}.id`); + + return this.db + .selectFrom(table(t).as(t)) + .where(idRef, '<', nowId) + .$if(!!ack, (qb) => qb.where(idRef, '>', ack!.updateId)) + .orderBy(idRef, 'asc'); + } + + protected upsertQuery(t: T, { nowId, ack }: SyncQueryOptions) { + const { table, ref } = this.db.dynamic; + const updateIdRef = ref(`${t}.updateId`); + + return this.db + .selectFrom(table(t).as(t)) + .where(updateIdRef, '<', nowId) + .$if(!!ack, (qb) => qb.where(updateIdRef, '>', ack!.updateId)) + .orderBy(updateIdRef, 'asc'); } } class AlbumSync extends BaseSync { - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }, DummyValue.UUID] }) - getCreatedAfter({ nowId, userId }: SyncQueryOptions, afterCreateId?: string) { + @GenerateSql({ params: [dummyCreateAfterOptions] }) + getCreatedAfter({ nowId, userId, afterCreateId }: SyncCreatedAfterOptions) { return this.db .selectFrom('album_user') .select(['albumsId as id', 'createId']) @@ -120,24 +140,19 @@ class AlbumSync extends BaseSync { .execute(); } - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getDeletes({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('album_audit') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getDeletes(options: SyncQueryOptions) { + return this.auditQuery('album_audit', options) .select(['id', 'albumId']) - .where('userId', '=', userId) - .$call(this.auditTableFilters(nowId, ack)) + .where('userId', '=', options.userId) .stream(); } - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('album') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + const userId = options.userId; + return this.upsertQuery('album', options) .distinctOn(['album.id', 'album.updateId']) - .where('album.updateId', '<', nowId) - .$if(!!ack, (qb) => qb.where('album.updateId', '>', ack!.updateId)) - .orderBy('album.updateId', 'asc') .leftJoin('album_user as album_users', 'album.id', 'album_users.albumsId') .where((eb) => eb.or([eb('album.ownerId', '=', userId), eb('album_users.usersId', '=', userId)])) .select([ @@ -157,132 +172,96 @@ class AlbumSync extends BaseSync { } class AlbumAssetSync extends BaseSync { - @GenerateSql({ - params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }, DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], - stream: true, - }) - getBackfill({ nowId }: SyncQueryOptions, albumId: string, afterUpdateId: string | undefined, beforeUpdateId: string) { - return this.db - .selectFrom('album_asset') + @GenerateSql({ params: [dummyBackfillOptions, DummyValue.UUID], stream: true }) + getBackfill(options: SyncBackfillOptions, albumId: string) { + return this.backfillQuery('album_asset', options) .innerJoin('asset', 'asset.id', 'album_asset.assetsId') .select(columns.syncAsset) .select('album_asset.updateId') .where('album_asset.albumsId', '=', albumId) - .where('album_asset.updateId', '<', nowId) - .where('album_asset.updateId', '<=', beforeUpdateId) - .$if(!!afterUpdateId, (eb) => eb.where('album_asset.updateId', '>=', afterUpdateId!)) - .orderBy('album_asset.updateId', 'asc') .stream(); } - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getUpdates({ nowId, userId }: SyncQueryOptions, albumToAssetAck: SyncAck, ack?: SyncAck) { - return this.db - .selectFrom('asset') + @GenerateSql({ params: [dummyQueryOptions, { updateId: DummyValue.UUID }], stream: true }) + getUpdates(options: SyncQueryOptions, albumToAssetAck: SyncAck) { + const userId = options.userId; + return this.upsertQuery('asset', options) .innerJoin('album_asset', 'album_asset.assetsId', 'asset.id') .select(columns.syncAsset) .select('asset.updateId') - .where('asset.updateId', '<', nowId) .where('album_asset.updateId', '<=', albumToAssetAck.updateId) // Ensure we only send updates for assets that the client already knows about - .$if(!!ack, (qb) => qb.where('asset.updateId', '>', ack!.updateId)) - .orderBy('asset.updateId', 'asc') .innerJoin('album', 'album.id', 'album_asset.albumsId') .leftJoin('album_user', 'album_user.albumsId', 'album_asset.albumsId') .where((eb) => eb.or([eb('album.ownerId', '=', userId), eb('album_user.usersId', '=', userId)])) .stream(); } - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getCreates({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('album_asset') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getCreates(options: SyncQueryOptions) { + const userId = options.userId; + return this.upsertQuery('album_asset', options) .select('album_asset.updateId') .innerJoin('asset', 'asset.id', 'album_asset.assetsId') .select(columns.syncAsset) .innerJoin('album', 'album.id', 'album_asset.albumsId') .leftJoin('album_user', 'album_user.albumsId', 'album_asset.albumsId') - .where('album_asset.updateId', '<', nowId) .where((eb) => eb.or([eb('album.ownerId', '=', userId), eb('album_user.usersId', '=', userId)])) - .$if(!!ack, (qb) => qb.where('album_asset.updateId', '>', ack!.updateId)) - .orderBy('album_asset.updateId', 'asc') .stream(); } } class AlbumAssetExifSync extends BaseSync { - @GenerateSql({ - params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }, DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], - stream: true, - }) - getBackfill({ nowId }: SyncQueryOptions, albumId: string, afterUpdateId: string | undefined, beforeUpdateId: string) { - return this.db - .selectFrom('album_asset') + @GenerateSql({ params: [dummyBackfillOptions, DummyValue.UUID], stream: true }) + getBackfill(options: SyncBackfillOptions, albumId: string) { + return this.backfillQuery('album_asset', options) .innerJoin('asset_exif', 'asset_exif.assetId', 'album_asset.assetsId') .select(columns.syncAssetExif) .select('album_asset.updateId') .where('album_asset.albumsId', '=', albumId) - .where('album_asset.updateId', '<', nowId) - .where('album_asset.updateId', '<=', beforeUpdateId) - .$if(!!afterUpdateId, (eb) => eb.where('album_asset.updateId', '>=', afterUpdateId!)) - .orderBy('album_asset.updateId', 'asc') .stream(); } - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getUpdates({ nowId, userId }: SyncQueryOptions, albumToAssetAck: SyncAck, ack?: SyncAck) { - return this.db - .selectFrom('asset_exif') + @GenerateSql({ params: [dummyQueryOptions, { updateId: DummyValue.UUID }], stream: true }) + getUpdates(options: SyncQueryOptions, albumToAssetAck: SyncAck) { + const userId = options.userId; + return this.upsertQuery('asset_exif', options) .innerJoin('album_asset', 'album_asset.assetsId', 'asset_exif.assetId') .select(columns.syncAssetExif) .select('asset_exif.updateId') .where('album_asset.updateId', '<=', albumToAssetAck.updateId) // Ensure we only send exif updates for assets that the client already knows about - .where('asset_exif.updateId', '<', nowId) - .$if(!!ack, (qb) => qb.where('asset_exif.updateId', '>', ack!.updateId)) - .orderBy('asset_exif.updateId', 'asc') .innerJoin('album', 'album.id', 'album_asset.albumsId') .leftJoin('album_user', 'album_user.albumsId', 'album_asset.albumsId') .where((eb) => eb.or([eb('album.ownerId', '=', userId), eb('album_user.usersId', '=', userId)])) .stream(); } - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getCreates({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('album_asset') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getCreates(options: SyncQueryOptions) { + const userId = options.userId; + return this.upsertQuery('album_asset', options) .select('album_asset.updateId') .innerJoin('asset_exif', 'asset_exif.assetId', 'album_asset.assetsId') .select(columns.syncAssetExif) .innerJoin('album', 'album.id', 'album_asset.albumsId') .leftJoin('album_user', 'album_user.albumsId', 'album_asset.albumsId') - .where('album_asset.updateId', '<', nowId) .where((eb) => eb.or([eb('album.ownerId', '=', userId), eb('album_user.usersId', '=', userId)])) - .$if(!!ack, (qb) => qb.where('album_asset.updateId', '>', ack!.updateId)) - .orderBy('album_asset.updateId', 'asc') .stream(); } } class AlbumToAssetSync extends BaseSync { - @GenerateSql({ - params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }, DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], - stream: true, - }) - getBackfill({ nowId }: SyncQueryOptions, albumId: string, afterUpdateId: string | undefined, beforeUpdateId: string) { - return this.db - .selectFrom('album_asset as album_assets') - .select(['album_assets.assetsId as assetId', 'album_assets.albumsId as albumId', 'album_assets.updateId']) - .where('album_assets.albumsId', '=', albumId) - .where('album_assets.updateId', '<', nowId) - .where('album_assets.updateId', '<=', beforeUpdateId) - .$if(!!afterUpdateId, (eb) => eb.where('album_assets.updateId', '>=', afterUpdateId!)) - .orderBy('album_assets.updateId', 'asc') + @GenerateSql({ params: [dummyBackfillOptions, DummyValue.UUID], stream: true }) + getBackfill(options: SyncBackfillOptions, albumId: string) { + return this.backfillQuery('album_asset', options) + .select(['album_asset.assetsId as assetId', 'album_asset.albumsId as albumId', 'album_asset.updateId']) + .where('album_asset.albumsId', '=', albumId) .stream(); } - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getDeletes({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('album_asset_audit') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getDeletes(options: SyncQueryOptions) { + const userId = options.userId; + return this.auditQuery('album_asset_audit', options) .select(['id', 'assetId', 'albumId']) .where((eb) => eb( @@ -302,18 +281,14 @@ class AlbumToAssetSync extends BaseSync { ), ), ) - .$call(this.auditTableFilters(nowId, ack)) .stream(); } - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('album_asset') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + const userId = options.userId; + return this.upsertQuery('album_asset', options) .select(['album_asset.assetsId as assetId', 'album_asset.albumsId as albumId', 'album_asset.updateId']) - .where('album_asset.updateId', '<', nowId) - .$if(!!ack, (qb) => qb.where('album_asset.updateId', '>', ack!.updateId)) - .orderBy('album_asset.updateId', 'asc') .innerJoin('album', 'album.id', 'album_asset.albumsId') .leftJoin('album_user', 'album_user.albumsId', 'album_asset.albumsId') .where((eb) => eb.or([eb('album.ownerId', '=', userId), eb('album_user.usersId', '=', userId)])) @@ -322,27 +297,19 @@ class AlbumToAssetSync extends BaseSync { } class AlbumUserSync extends BaseSync { - @GenerateSql({ - params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }, DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], - stream: true, - }) - getBackfill({ nowId }: SyncQueryOptions, albumId: string, afterUpdateId: string | undefined, beforeUpdateId: string) { - return this.db - .selectFrom('album_user') + @GenerateSql({ params: [dummyBackfillOptions, DummyValue.UUID], stream: true }) + getBackfill(options: SyncBackfillOptions, albumId: string) { + return this.backfillQuery('album_user', options) .select(columns.syncAlbumUser) .select('album_user.updateId') .where('albumsId', '=', albumId) - .where('updateId', '<', nowId) - .where('updateId', '<=', beforeUpdateId) - .$if(!!afterUpdateId, (eb) => eb.where('updateId', '>=', afterUpdateId!)) - .orderBy('updateId', 'asc') .stream(); } - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getDeletes({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('album_user_audit') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getDeletes(options: SyncQueryOptions) { + const userId = options.userId; + return this.auditQuery('album_user_audit', options) .select(['id', 'userId', 'albumId']) .where((eb) => eb( @@ -362,19 +329,15 @@ class AlbumUserSync extends BaseSync { ), ), ) - .$call(this.auditTableFilters(nowId, ack)) .stream(); } - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('album_user') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + const userId = options.userId; + return this.upsertQuery('album_user', options) .select(columns.syncAlbumUser) .select('album_user.updateId') - .where('album_user.updateId', '<', nowId) - .$if(!!ack, (qb) => qb.where('album_user.updateId', '>', ack!.updateId)) - .orderBy('album_user.updateId', 'asc') .where((eb) => eb( 'album_user.albumsId', @@ -398,55 +361,46 @@ class AlbumUserSync extends BaseSync { } class AssetSync extends BaseSync { - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getDeletes({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('asset_audit') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getDeletes(options: SyncQueryOptions) { + return this.auditQuery('asset_audit', options) .select(['id', 'assetId']) - .where('ownerId', '=', userId) - .$call(this.auditTableFilters(nowId, ack)) + .where('ownerId', '=', options.userId) .stream(); } - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('asset') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + return this.upsertQuery('asset', options) .select(columns.syncAsset) .select('asset.updateId') - .where('ownerId', '=', userId) - .$call(this.upsertTableFilters(nowId, ack)) + .where('ownerId', '=', options.userId) .stream(); } } class AuthUserSync extends BaseSync { - @GenerateSql({ params: [], stream: true }) - getUpserts({ nowId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('user') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + return this.upsertQuery('user', options) .select(columns.syncUser) .select(['isAdmin', 'pinCode', 'oauthId', 'storageLabel', 'quotaSizeInBytes', 'quotaUsageInBytes']) - .$call(this.upsertTableFilters(nowId, ack)) .stream(); } } class PersonSync extends BaseSync { - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getDeletes({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('person_audit') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getDeletes(options: SyncQueryOptions) { + return this.auditQuery('person_audit', options) .select(['id', 'personId']) - .where('ownerId', '=', userId) - .$call(this.auditTableFilters(nowId, ack)) + .where('ownerId', '=', options.userId) .stream(); } - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('person') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + return this.upsertQuery('person', options) .select([ 'id', 'createdAt', @@ -460,30 +414,24 @@ class PersonSync extends BaseSync { 'updateId', 'faceAssetId', ]) - .where('ownerId', '=', userId) - .$call(this.upsertTableFilters(nowId, ack)) + .where('ownerId', '=', options.userId) .stream(); } } class AssetFaceSync extends BaseSync { - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getDeletes({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('asset_face_audit') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getDeletes(options: SyncQueryOptions) { + return this.auditQuery('asset_face_audit', options) .select(['asset_face_audit.id', 'assetFaceId']) - .orderBy('asset_face_audit.id', 'asc') .leftJoin('asset', 'asset.id', 'asset_face_audit.assetId') - .where('asset.ownerId', '=', userId) - .where('asset_face_audit.id', '<', nowId) - .$if(!!ack, (qb) => qb.where('asset_face_audit.id', '>', ack!.updateId)) + .where('asset.ownerId', '=', options.userId) .stream(); } - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('asset_face') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + return this.upsertQuery('asset_face', options) .select([ 'asset_face.id', 'assetId', @@ -497,43 +445,35 @@ class AssetFaceSync extends BaseSync { 'sourceType', 'asset_face.updateId', ]) - .where('asset_face.updateId', '<', nowId) - .$if(!!ack, (qb) => qb.where('asset_face.updateId', '>', ack!.updateId)) - .orderBy('asset_face.updateId', 'asc') .leftJoin('asset', 'asset.id', 'asset_face.assetId') - .where('asset.ownerId', '=', userId) + .where('asset.ownerId', '=', options.userId) .stream(); } } class AssetExifSync extends BaseSync { - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('asset_exif') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + return this.upsertQuery('asset_exif', options) .select(columns.syncAssetExif) .select('asset_exif.updateId') - .where('assetId', 'in', (eb) => eb.selectFrom('asset').select('id').where('ownerId', '=', userId)) - .$call(this.upsertTableFilters(nowId, ack)) + .where('assetId', 'in', (eb) => eb.selectFrom('asset').select('id').where('ownerId', '=', options.userId)) .stream(); } } class MemorySync extends BaseSync { - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getDeletes({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('memory_audit') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getDeletes(options: SyncQueryOptions) { + return this.auditQuery('memory_audit', options) .select(['id', 'memoryId']) - .where('userId', '=', userId) - .$call(this.auditTableFilters(nowId, ack)) + .where('userId', '=', options.userId) .stream(); } - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('memory') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + return this.upsertQuery('memory', options) .select([ 'id', 'createdAt', @@ -549,38 +489,33 @@ class MemorySync extends BaseSync { 'hideAt', ]) .select('updateId') - .where('ownerId', '=', userId) - .$call(this.upsertTableFilters(nowId, ack)) + .where('ownerId', '=', options.userId) .stream(); } } class MemoryToAssetSync extends BaseSync { - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getDeletes({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('memory_asset_audit') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getDeletes(options: SyncQueryOptions) { + return this.auditQuery('memory_asset_audit', options) .select(['id', 'memoryId', 'assetId']) - .where('memoryId', 'in', (eb) => eb.selectFrom('memory').select('id').where('ownerId', '=', userId)) - .$call(this.auditTableFilters(nowId, ack)) + .where('memoryId', 'in', (eb) => eb.selectFrom('memory').select('id').where('ownerId', '=', options.userId)) .stream(); } - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('memory_asset') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + return this.upsertQuery('memory_asset', options) .select(['memoriesId as memoryId', 'assetsId as assetId']) .select('updateId') - .where('memoriesId', 'in', (eb) => eb.selectFrom('memory').select('id').where('ownerId', '=', userId)) - .$call(this.upsertTableFilters(nowId, ack)) + .where('memoriesId', 'in', (eb) => eb.selectFrom('memory').select('id').where('ownerId', '=', options.userId)) .stream(); } } class PartnerSync extends BaseSync { - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }] }) - getCreatedAfter({ nowId, userId }: SyncQueryOptions, afterCreateId?: string) { + @GenerateSql({ params: [dummyCreateAfterOptions] }) + getCreatedAfter({ nowId, userId, afterCreateId }: SyncCreatedAfterOptions) { return this.db .selectFrom('partner') .select(['sharedById', 'createId']) @@ -591,104 +526,71 @@ class PartnerSync extends BaseSync { .execute(); } - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getDeletes({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('partner_audit') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getDeletes(options: SyncQueryOptions) { + const userId = options.userId; + return this.auditQuery('partner_audit', options) .select(['id', 'sharedById', 'sharedWithId']) .where((eb) => eb.or([eb('sharedById', '=', userId), eb('sharedWithId', '=', userId)])) - .$call(this.auditTableFilters(nowId, ack)) .stream(); } - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('partner') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + const userId = options.userId; + return this.upsertQuery('partner', options) .select(['sharedById', 'sharedWithId', 'inTimeline', 'updateId']) .where((eb) => eb.or([eb('sharedById', '=', userId), eb('sharedWithId', '=', userId)])) - .$call(this.upsertTableFilters(nowId, ack)) .stream(); } } class PartnerAssetsSync extends BaseSync { - @GenerateSql({ - params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }, DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], - stream: true, - }) - getBackfill( - { nowId }: SyncQueryOptions, - partnerId: string, - afterUpdateId: string | undefined, - beforeUpdateId: string, - ) { - return this.db - .selectFrom('asset') + @GenerateSql({ params: [dummyBackfillOptions, DummyValue.UUID], stream: true }) + getBackfill(options: SyncBackfillOptions, partnerId: string) { + return this.backfillQuery('asset', options) .select(columns.syncAsset) .select('asset.updateId') .where('ownerId', '=', partnerId) - .where('updateId', '<', nowId) - .where('updateId', '<=', beforeUpdateId) - .$if(!!afterUpdateId, (eb) => eb.where('updateId', '>=', afterUpdateId!)) - .orderBy('updateId', 'asc') .stream(); } - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getDeletes({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('asset_audit') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getDeletes(options: SyncQueryOptions) { + return this.auditQuery('asset_audit', options) .select(['id', 'assetId']) .where('ownerId', 'in', (eb) => - eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', userId), + eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', options.userId), ) - .$call(this.auditTableFilters(nowId, ack)) .stream(); } - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('asset') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + return this.upsertQuery('asset', options) .select(columns.syncAsset) .select('asset.updateId') .where('ownerId', 'in', (eb) => - eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', userId), + eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', options.userId), ) - .$call(this.upsertTableFilters(nowId, ack)) .stream(); } } class PartnerAssetExifsSync extends BaseSync { - @GenerateSql({ - params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }, DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], - stream: true, - }) - getBackfill( - { nowId }: SyncQueryOptions, - partnerId: string, - afterUpdateId: string | undefined, - beforeUpdateId: string, - ) { - return this.db - .selectFrom('asset_exif') + @GenerateSql({ params: [dummyBackfillOptions, DummyValue.UUID], stream: true }) + getBackfill(options: SyncBackfillOptions, partnerId: string) { + return this.backfillQuery('asset_exif', options) .select(columns.syncAssetExif) .select('asset_exif.updateId') .innerJoin('asset', 'asset.id', 'asset_exif.assetId') .where('asset.ownerId', '=', partnerId) - .where('asset_exif.updateId', '<', nowId) - .where('asset_exif.updateId', '<=', beforeUpdateId) - .$if(!!afterUpdateId, (eb) => eb.where('asset_exif.updateId', '>=', afterUpdateId!)) - .orderBy('asset_exif.updateId', 'asc') .stream(); } - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('asset_exif') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + return this.upsertQuery('asset_exif', options) .select(columns.syncAssetExif) .select('asset_exif.updateId') .where('assetId', 'in', (eb) => @@ -696,114 +598,90 @@ class PartnerAssetExifsSync extends BaseSync { .selectFrom('asset') .select('id') .where('ownerId', 'in', (eb) => - eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', userId), + eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', options.userId), ), ) - .$call(this.upsertTableFilters(nowId, ack)) .stream(); } } class StackSync extends BaseSync { - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getDeletes({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('stack_audit') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getDeletes(options: SyncQueryOptions) { + return this.auditQuery('stack_audit', options) .select(['id', 'stackId']) - .where('userId', '=', userId) - .$call(this.auditTableFilters(nowId, ack)) + .where('userId', '=', options.userId) .stream(); } - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('stack') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + return this.upsertQuery('stack', options) .select(columns.syncStack) .select('updateId') - .where('ownerId', '=', userId) - .$call(this.upsertTableFilters(nowId, ack)) + .where('ownerId', '=', options.userId) .stream(); } } class PartnerStackSync extends BaseSync { - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getDeletes({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('stack_audit') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getDeletes(options: SyncQueryOptions) { + return this.auditQuery('stack_audit', options) .select(['id', 'stackId']) - .where('userId', 'in', (eb) => eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', userId)) - .$call(this.auditTableFilters(nowId, ack)) + .where('userId', 'in', (eb) => + eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', options.userId), + ) .stream(); } - @GenerateSql({ - params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }, DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], - stream: true, - }) - getBackfill( - { nowId }: SyncQueryOptions, - partnerId: string, - afterUpdateId: string | undefined, - beforeUpdateId: string, - ) { - return this.db - .selectFrom('stack') + @GenerateSql({ params: [dummyBackfillOptions, DummyValue.UUID], stream: true }) + getBackfill(options: SyncBackfillOptions, partnerId: string) { + return this.backfillQuery('stack', options) .select(columns.syncStack) .select('updateId') .where('ownerId', '=', partnerId) - .where('updateId', '<', nowId) - .where('updateId', '<=', beforeUpdateId) - .$if(!!afterUpdateId, (eb) => eb.where('updateId', '>=', afterUpdateId!)) - .orderBy('updateId', 'asc') .stream(); } - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('stack') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + return this.upsertQuery('stack', options) .select(columns.syncStack) .select('updateId') .where('ownerId', 'in', (eb) => - eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', userId), + eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', options.userId), ) - .$call(this.upsertTableFilters(nowId, ack)) .stream(); } } class UserSync extends BaseSync { - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getDeletes({ nowId }: SyncQueryOptions, ack?: SyncAck) { - return this.db.selectFrom('user_audit').select(['id', 'userId']).$call(this.auditTableFilters(nowId, ack)).stream(); + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getDeletes(options: SyncQueryOptions) { + return this.auditQuery('user_audit', options).select(['id', 'userId']).stream(); } - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getUpserts({ nowId }: SyncQueryOptions, ack?: SyncAck) { - return this.db.selectFrom('user').select(columns.syncUser).$call(this.upsertTableFilters(nowId, ack)).stream(); + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + return this.upsertQuery('user', options).select(columns.syncUser).stream(); } } class UserMetadataSync extends BaseSync { - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getDeletes({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('user_metadata_audit') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getDeletes(options: SyncQueryOptions) { + return this.auditQuery('user_metadata_audit', options) .select(['id', 'userId', 'key']) - .where('userId', '=', userId) - .$call(this.auditTableFilters(nowId, ack)) + .where('userId', '=', options.userId) .stream(); } - @GenerateSql({ params: [{ nowId: DummyValue.UUID, userId: DummyValue.UUID }], stream: true }) - getUpserts({ nowId, userId }: SyncQueryOptions, ack?: SyncAck) { - return this.db - .selectFrom('user_metadata') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + return this.upsertQuery('user_metadata', options) .select(['userId', 'key', 'value', 'updateId']) - .where('userId', '=', userId) - .$call(this.upsertTableFilters(nowId, ack)) + .where('userId', '=', options.userId) .stream(); } } diff --git a/server/src/services/sync.service.ts b/server/src/services/sync.service.ts index 0a4144a953..6b8512eacb 100644 --- a/server/src/services/sync.service.ts +++ b/server/src/services/sync.service.ts @@ -183,7 +183,7 @@ export class SyncService extends BaseService { private async syncAuthUsersV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const upsertType = SyncEntityType.AuthUserV1; - const upserts = this.syncRepository.authUser.getUpserts(options, checkpointMap[upsertType]); + const upserts = this.syncRepository.authUser.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, profileImagePath, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data: { ...data, hasProfileImage: !!profileImagePath } }); } @@ -191,13 +191,13 @@ export class SyncService extends BaseService { private async syncUsersV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.UserDeleteV1; - const deletes = this.syncRepository.user.getDeletes(options, checkpointMap[deleteType]); + const deletes = this.syncRepository.user.getDeletes({ ...options, ack: checkpointMap[deleteType] }); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.UserV1; - const upserts = this.syncRepository.user.getUpserts(options, checkpointMap[upsertType]); + const upserts = this.syncRepository.user.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, profileImagePath, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data: { ...data, hasProfileImage: !!profileImagePath } }); } @@ -205,13 +205,13 @@ export class SyncService extends BaseService { private async syncPartnersV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.PartnerDeleteV1; - const deletes = this.syncRepository.partner.getDeletes(options, checkpointMap[deleteType]); + const deletes = this.syncRepository.partner.getDeletes({ ...options, ack: checkpointMap[deleteType] }); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.PartnerV1; - const upserts = this.syncRepository.partner.getUpserts(options, checkpointMap[upsertType]); + const upserts = this.syncRepository.partner.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } @@ -219,13 +219,13 @@ export class SyncService extends BaseService { private async syncAssetsV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.AssetDeleteV1; - const deletes = this.syncRepository.asset.getDeletes(options, checkpointMap[deleteType]); + const deletes = this.syncRepository.asset.getDeletes({ ...options, ack: checkpointMap[deleteType] }); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.AssetV1; - const upserts = this.syncRepository.asset.getUpserts(options, checkpointMap[upsertType]); + const upserts = this.syncRepository.asset.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data: mapSyncAssetV1(data) }); } @@ -238,14 +238,17 @@ export class SyncService extends BaseService { sessionId: string, ) { const deleteType = SyncEntityType.PartnerAssetDeleteV1; - const deletes = this.syncRepository.partnerAsset.getDeletes(options, checkpointMap[deleteType]); + const deletes = this.syncRepository.partnerAsset.getDeletes({ ...options, ack: checkpointMap[deleteType] }); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const backfillType = SyncEntityType.PartnerAssetBackfillV1; const backfillCheckpoint = checkpointMap[backfillType]; - const partners = await this.syncRepository.partner.getCreatedAfter(options, backfillCheckpoint?.updateId); + const partners = await this.syncRepository.partner.getCreatedAfter({ + ...options, + afterCreateId: backfillCheckpoint?.updateId, + }); const upsertType = SyncEntityType.PartnerAssetV1; const upsertCheckpoint = checkpointMap[upsertType]; if (upsertCheckpoint) { @@ -258,7 +261,10 @@ export class SyncService extends BaseService { } const startId = getStartId(createId, backfillCheckpoint); - const backfill = this.syncRepository.partnerAsset.getBackfill(options, partner.sharedById, startId, endId); + const backfill = this.syncRepository.partnerAsset.getBackfill( + { ...options, afterUpdateId: startId, beforeUpdateId: endId }, + partner.sharedById, + ); for await (const { updateId, ...data } of backfill) { send(response, { @@ -278,7 +284,7 @@ export class SyncService extends BaseService { }); } - const upserts = this.syncRepository.partnerAsset.getUpserts(options, checkpointMap[upsertType]); + const upserts = this.syncRepository.partnerAsset.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data: mapSyncAssetV1(data) }); } @@ -286,7 +292,7 @@ export class SyncService extends BaseService { private async syncAssetExifsV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const upsertType = SyncEntityType.AssetExifV1; - const upserts = this.syncRepository.assetExif.getUpserts(options, checkpointMap[upsertType]); + const upserts = this.syncRepository.assetExif.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } @@ -300,7 +306,10 @@ export class SyncService extends BaseService { ) { const backfillType = SyncEntityType.PartnerAssetExifBackfillV1; const backfillCheckpoint = checkpointMap[backfillType]; - const partners = await this.syncRepository.partner.getCreatedAfter(options, backfillCheckpoint?.updateId); + const partners = await this.syncRepository.partner.getCreatedAfter({ + ...options, + afterCreateId: backfillCheckpoint?.updateId, + }); const upsertType = SyncEntityType.PartnerAssetExifV1; const upsertCheckpoint = checkpointMap[upsertType]; @@ -314,7 +323,10 @@ export class SyncService extends BaseService { } const startId = getStartId(createId, backfillCheckpoint); - const backfill = this.syncRepository.partnerAssetExif.getBackfill(options, partner.sharedById, startId, endId); + const backfill = this.syncRepository.partnerAssetExif.getBackfill( + { ...options, afterUpdateId: startId, beforeUpdateId: endId }, + partner.sharedById, + ); for await (const { updateId, ...data } of backfill) { send(response, { type: backfillType, ids: [partner.createId, updateId], data }); @@ -330,7 +342,7 @@ export class SyncService extends BaseService { }); } - const upserts = this.syncRepository.partnerAssetExif.getUpserts(options, checkpointMap[upsertType]); + const upserts = this.syncRepository.partnerAssetExif.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } @@ -338,13 +350,13 @@ export class SyncService extends BaseService { private async syncAlbumsV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.AlbumDeleteV1; - const deletes = this.syncRepository.album.getDeletes(options, checkpointMap[deleteType]); + const deletes = this.syncRepository.album.getDeletes({ ...options, ack: checkpointMap[deleteType] }); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.AlbumV1; - const upserts = this.syncRepository.album.getUpserts(options, checkpointMap[upsertType]); + const upserts = this.syncRepository.album.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } @@ -357,14 +369,17 @@ export class SyncService extends BaseService { sessionId: string, ) { const deleteType = SyncEntityType.AlbumUserDeleteV1; - const deletes = this.syncRepository.albumUser.getDeletes(options, checkpointMap[deleteType]); + const deletes = this.syncRepository.albumUser.getDeletes({ ...options, ack: checkpointMap[deleteType] }); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const backfillType = SyncEntityType.AlbumUserBackfillV1; const backfillCheckpoint = checkpointMap[backfillType]; - const albums = await this.syncRepository.album.getCreatedAfter(options, backfillCheckpoint?.updateId); + const albums = await this.syncRepository.album.getCreatedAfter({ + ...options, + afterCreateId: backfillCheckpoint?.updateId, + }); const upsertType = SyncEntityType.AlbumUserV1; const upsertCheckpoint = checkpointMap[upsertType]; if (upsertCheckpoint) { @@ -377,7 +392,10 @@ export class SyncService extends BaseService { } const startId = getStartId(createId, backfillCheckpoint); - const backfill = this.syncRepository.albumUser.getBackfill(options, album.id, startId, endId); + const backfill = this.syncRepository.albumUser.getBackfill( + { ...options, afterUpdateId: startId, beforeUpdateId: endId }, + album.id, + ); for await (const { updateId, ...data } of backfill) { send(response, { type: backfillType, ids: [createId, updateId], data }); @@ -393,7 +411,7 @@ export class SyncService extends BaseService { }); } - const upserts = this.syncRepository.albumUser.getUpserts(options, checkpointMap[upsertType]); + const upserts = this.syncRepository.albumUser.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } @@ -407,7 +425,10 @@ export class SyncService extends BaseService { ) { const backfillType = SyncEntityType.AlbumAssetBackfillV1; const backfillCheckpoint = checkpointMap[backfillType]; - const albums = await this.syncRepository.album.getCreatedAfter(options, backfillCheckpoint?.updateId); + const albums = await this.syncRepository.album.getCreatedAfter({ + ...options, + afterCreateId: backfillCheckpoint?.updateId, + }); const updateType = SyncEntityType.AlbumAssetUpdateV1; const createType = SyncEntityType.AlbumAssetCreateV1; const updateCheckpoint = checkpointMap[updateType]; @@ -422,7 +443,10 @@ export class SyncService extends BaseService { } const startId = getStartId(createId, backfillCheckpoint); - const backfill = this.syncRepository.albumAsset.getBackfill(options, album.id, startId, endId); + const backfill = this.syncRepository.albumAsset.getBackfill( + { ...options, afterUpdateId: startId, beforeUpdateId: endId }, + album.id, + ); for await (const { updateId, ...data } of backfill) { send(response, { type: backfillType, ids: [createId, updateId], data: mapSyncAssetV1(data) }); @@ -439,13 +463,16 @@ export class SyncService extends BaseService { } if (createCheckpoint) { - const updates = this.syncRepository.albumAsset.getUpdates(options, createCheckpoint, updateCheckpoint); + const updates = this.syncRepository.albumAsset.getUpdates( + { ...options, ack: updateCheckpoint }, + createCheckpoint, + ); for await (const { updateId, ...data } of updates) { send(response, { type: updateType, ids: [updateId], data: mapSyncAssetV1(data) }); } } - const creates = this.syncRepository.albumAsset.getCreates(options, createCheckpoint); + const creates = this.syncRepository.albumAsset.getCreates({ ...options, ack: createCheckpoint }); let first = true; for await (const { updateId, ...data } of creates) { if (first) { @@ -469,7 +496,10 @@ export class SyncService extends BaseService { ) { const backfillType = SyncEntityType.AlbumAssetExifBackfillV1; const backfillCheckpoint = checkpointMap[backfillType]; - const albums = await this.syncRepository.album.getCreatedAfter(options, backfillCheckpoint?.updateId); + const albums = await this.syncRepository.album.getCreatedAfter({ + ...options, + afterCreateId: backfillCheckpoint?.updateId, + }); const updateType = SyncEntityType.AlbumAssetExifUpdateV1; const createType = SyncEntityType.AlbumAssetExifCreateV1; const upsertCheckpoint = checkpointMap[updateType]; @@ -484,7 +514,10 @@ export class SyncService extends BaseService { } const startId = getStartId(createId, backfillCheckpoint); - const backfill = this.syncRepository.albumAssetExif.getBackfill(options, album.id, startId, endId); + const backfill = this.syncRepository.albumAssetExif.getBackfill( + { ...options, afterUpdateId: startId, beforeUpdateId: endId }, + album.id, + ); for await (const { updateId, ...data } of backfill) { send(response, { type: backfillType, ids: [createId, updateId], data }); @@ -501,13 +534,16 @@ export class SyncService extends BaseService { } if (createCheckpoint) { - const updates = this.syncRepository.albumAssetExif.getUpdates(options, createCheckpoint, upsertCheckpoint); + const updates = this.syncRepository.albumAssetExif.getUpdates( + { ...options, ack: upsertCheckpoint }, + createCheckpoint, + ); for await (const { updateId, ...data } of updates) { send(response, { type: updateType, ids: [updateId], data }); } } - const creates = this.syncRepository.albumAssetExif.getCreates(options, createCheckpoint); + const creates = this.syncRepository.albumAssetExif.getCreates({ ...options, ack: createCheckpoint }); let first = true; for await (const { updateId, ...data } of creates) { if (first) { @@ -530,14 +566,17 @@ export class SyncService extends BaseService { sessionId: string, ) { const deleteType = SyncEntityType.AlbumToAssetDeleteV1; - const deletes = this.syncRepository.albumToAsset.getDeletes(options, checkpointMap[deleteType]); + const deletes = this.syncRepository.albumToAsset.getDeletes({ ...options, ack: checkpointMap[deleteType] }); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const backfillType = SyncEntityType.AlbumToAssetBackfillV1; const backfillCheckpoint = checkpointMap[backfillType]; - const albums = await this.syncRepository.album.getCreatedAfter(options, backfillCheckpoint?.updateId); + const albums = await this.syncRepository.album.getCreatedAfter({ + ...options, + afterCreateId: backfillCheckpoint?.updateId, + }); const upsertType = SyncEntityType.AlbumToAssetV1; const upsertCheckpoint = checkpointMap[upsertType]; if (upsertCheckpoint) { @@ -550,7 +589,10 @@ export class SyncService extends BaseService { } const startId = getStartId(createId, backfillCheckpoint); - const backfill = this.syncRepository.albumToAsset.getBackfill(options, album.id, startId, endId); + const backfill = this.syncRepository.albumToAsset.getBackfill( + { ...options, afterUpdateId: startId, beforeUpdateId: endId }, + album.id, + ); for await (const { updateId, ...data } of backfill) { send(response, { type: backfillType, ids: [createId, updateId], data }); @@ -566,7 +608,7 @@ export class SyncService extends BaseService { }); } - const upserts = this.syncRepository.albumToAsset.getUpserts(options, checkpointMap[upsertType]); + const upserts = this.syncRepository.albumToAsset.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } @@ -574,13 +616,13 @@ export class SyncService extends BaseService { private async syncMemoriesV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.MemoryDeleteV1; - const deletes = this.syncRepository.memory.getDeletes(options, checkpointMap[deleteType]); + const deletes = this.syncRepository.memory.getDeletes({ ...options, ack: checkpointMap[deleteType] }); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.MemoryV1; - const upserts = this.syncRepository.memory.getUpserts(options, checkpointMap[upsertType]); + const upserts = this.syncRepository.memory.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } @@ -588,13 +630,13 @@ export class SyncService extends BaseService { private async syncMemoryAssetsV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.MemoryToAssetDeleteV1; - const deletes = this.syncRepository.memoryToAsset.getDeletes(options, checkpointMap[deleteType]); + const deletes = this.syncRepository.memoryToAsset.getDeletes({ ...options, ack: checkpointMap[deleteType] }); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.MemoryToAssetV1; - const upserts = this.syncRepository.memoryToAsset.getUpserts(options, checkpointMap[upsertType]); + const upserts = this.syncRepository.memoryToAsset.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } @@ -602,13 +644,13 @@ export class SyncService extends BaseService { private async syncStackV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.StackDeleteV1; - const deletes = this.syncRepository.stack.getDeletes(options, checkpointMap[deleteType]); + const deletes = this.syncRepository.stack.getDeletes({ ...options, ack: checkpointMap[deleteType] }); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.StackV1; - const upserts = this.syncRepository.stack.getUpserts(options, checkpointMap[upsertType]); + const upserts = this.syncRepository.stack.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } @@ -621,14 +663,17 @@ export class SyncService extends BaseService { sessionId: string, ) { const deleteType = SyncEntityType.PartnerStackDeleteV1; - const deletes = this.syncRepository.partnerStack.getDeletes(options, checkpointMap[deleteType]); + const deletes = this.syncRepository.partnerStack.getDeletes({ ...options, ack: checkpointMap[deleteType] }); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const backfillType = SyncEntityType.PartnerStackBackfillV1; const backfillCheckpoint = checkpointMap[backfillType]; - const partners = await this.syncRepository.partner.getCreatedAfter(options, backfillCheckpoint?.updateId); + const partners = await this.syncRepository.partner.getCreatedAfter({ + ...options, + afterCreateId: backfillCheckpoint?.updateId, + }); const upsertType = SyncEntityType.PartnerStackV1; const upsertCheckpoint = checkpointMap[upsertType]; if (upsertCheckpoint) { @@ -641,7 +686,10 @@ export class SyncService extends BaseService { } const startId = getStartId(createId, backfillCheckpoint); - const backfill = this.syncRepository.partnerStack.getBackfill(options, partner.sharedById, startId, endId); + const backfill = this.syncRepository.partnerStack.getBackfill( + { ...options, afterUpdateId: startId, beforeUpdateId: endId }, + partner.sharedById, + ); for await (const { updateId, ...data } of backfill) { send(response, { @@ -661,7 +709,7 @@ export class SyncService extends BaseService { }); } - const upserts = this.syncRepository.partnerStack.getUpserts(options, checkpointMap[upsertType]); + const upserts = this.syncRepository.partnerStack.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } @@ -669,13 +717,13 @@ export class SyncService extends BaseService { private async syncPeopleV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.PersonDeleteV1; - const deletes = this.syncRepository.people.getDeletes(options, checkpointMap[deleteType]); + const deletes = this.syncRepository.people.getDeletes({ ...options, ack: checkpointMap[deleteType] }); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.PersonV1; - const upserts = this.syncRepository.people.getUpserts(options, checkpointMap[upsertType]); + const upserts = this.syncRepository.people.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } @@ -683,13 +731,13 @@ export class SyncService extends BaseService { private async syncAssetFacesV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.AssetFaceDeleteV1; - const deletes = this.syncRepository.assetFace.getDeletes(options, checkpointMap[deleteType]); + const deletes = this.syncRepository.assetFace.getDeletes({ ...options, ack: checkpointMap[deleteType] }); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.AssetFaceV1; - const upserts = this.syncRepository.assetFace.getUpserts(options, checkpointMap[upsertType]); + const upserts = this.syncRepository.assetFace.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } @@ -697,14 +745,14 @@ export class SyncService extends BaseService { private async syncUserMetadataV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.UserMetadataDeleteV1; - const deletes = this.syncRepository.userMetadata.getDeletes(options, checkpointMap[deleteType]); + const deletes = this.syncRepository.userMetadata.getDeletes({ ...options, ack: checkpointMap[deleteType] }); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.UserMetadataV1; - const upserts = this.syncRepository.userMetadata.getUpserts(options, checkpointMap[upsertType]); + const upserts = this.syncRepository.userMetadata.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); From 0d9ebdc46a33574f8bebba5ace201858e9573379 Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Wed, 6 Aug 2025 07:58:54 -0500 Subject: [PATCH 186/748] fix(mobile): show video controls when in locked view (#20707) * fix(mobile): show video controls when in locked view * const constructor --- .../widgets/asset_viewer/asset_viewer.page.dart | 7 ++----- .../widgets/asset_viewer/bottom_bar.widget.dart | 4 +++- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart index d7e83e72ec..a56c778e48 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart @@ -25,7 +25,6 @@ import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider import 'package:immich_mobile/providers/cast.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; -import 'package:immich_mobile/providers/routes.provider.dart'; import 'package:immich_mobile/widgets/photo_view/photo_view.dart'; import 'package:immich_mobile/widgets/photo_view/photo_view_gallery.dart'; import 'package:platform/platform.dart'; @@ -594,8 +593,6 @@ class _AssetViewerState extends ConsumerState { }); }); - final isInLockedView = ref.watch(inLockedViewProvider); - // Currently it is not possible to scroll the asset when the bottom sheet is open all the way. // Issue: https://github.com/flutter/flutter/issues/109037 // TODO: Add a custom scrum builder once the fix lands on stable @@ -623,11 +620,11 @@ class _AssetViewerState extends ConsumerState { ), bottomNavigationBar: showingBottomSheet ? const SizedBox.shrink() - : Column( + : const Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.stretch, - children: [const AssetStackRow(), if (!isInLockedView) const ViewerBottomBar()], + children: [AssetStackRow(), ViewerBottomBar()], ), ), ); diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart index 881ed4d150..b220d4e6a5 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart @@ -10,6 +10,7 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_b import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/routes.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/widgets/asset_viewer/video_controls.dart'; @@ -28,6 +29,7 @@ class ViewerBottomBar extends ConsumerWidget { final isSheetOpen = ref.watch(assetViewerProvider.select((s) => s.showingBottomSheet)); int opacity = ref.watch(assetViewerProvider.select((state) => state.backgroundOpacity)); final showControls = ref.watch(assetViewerProvider.select((s) => s.showingControls)); + final isInLockedView = ref.watch(inLockedViewProvider); if (!showControls) { opacity = 0; @@ -66,7 +68,7 @@ class ViewerBottomBar extends ConsumerWidget { mainAxisAlignment: MainAxisAlignment.end, children: [ if (asset.isVideo) const VideoControls(), - Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: actions), + if (!isInLockedView) Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: actions), ], ), ), From 6ddef3a7e4fe5ee8c9dbb80f2b427db51c6279b0 Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Wed, 6 Aug 2025 18:31:16 +0530 Subject: [PATCH 187/748] fix: server version not fetched after auto login (#20713) * fix: server version not fetched after auto login * wrap get info with a try catch --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- .../lib/pages/common/splash_screen.page.dart | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/mobile/lib/pages/common/splash_screen.page.dart b/mobile/lib/pages/common/splash_screen.page.dart index 2bda4f90f9..159a5b4fee 100644 --- a/mobile/lib/pages/common/splash_screen.page.dart +++ b/mobile/lib/pages/common/splash_screen.page.dart @@ -6,6 +6,8 @@ import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/providers/auth.provider.dart'; import 'package:immich_mobile/providers/backup/backup.provider.dart'; import 'package:immich_mobile/providers/gallery_permission.provider.dart'; +import 'package:immich_mobile/providers/server_info.provider.dart'; +import 'package:immich_mobile/providers/websocket.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:logging/logging.dart'; @@ -43,17 +45,24 @@ class SplashScreenPageState extends ConsumerState { final accessToken = Store.tryGet(StoreKey.accessToken); if (accessToken != null && serverUrl != null && endpoint != null) { - ref - .read(authProvider.notifier) - .saveAuthInfo(accessToken: accessToken) - .then( - (a) => {log.info('Successfully updated auth info with access token: $accessToken')}, - onError: (exception) => { - log.severe('Failed to update auth info with access token: $accessToken'), - ref.read(authProvider.notifier).logout(), - context.replaceRoute(const LoginRoute()), - }, - ); + final infoProvider = ref.read(serverInfoProvider.notifier); + final wsProvider = ref.read(websocketProvider.notifier); + ref.read(authProvider.notifier).saveAuthInfo(accessToken: accessToken).then( + (a) { + log.info('Successfully updated auth info with access token: $accessToken'); + try { + wsProvider.connect(); + infoProvider.getServerInfo(); + } catch (e) { + log.severe('Failed establishing connection to the server: $e'); + } + }, + onError: (exception) => { + log.severe('Failed to update auth info with access token: $accessToken'), + ref.read(authProvider.notifier).logout(), + context.replaceRoute(const LoginRoute()), + }, + ); } else { log.severe('Missing crucial offline login info - Logging out completely'); ref.read(authProvider.notifier).logout(); From 1ca46fbd9862f6e0c5ce7991963694c60ef1a2d1 Mon Sep 17 00:00:00 2001 From: patrickgoering Date: Wed, 6 Aug 2025 15:10:49 +0200 Subject: [PATCH 188/748] fix: video thumbnail generation for short videos (#20629) fix video thumbnail generation for short videos ffmpeg gives conversion failed with error 234 for short mp4 files (less than 10s) that where converted from m2ts. Longer videos work fine. It looks like ffmpeg has no frames left to use for generating a thumbnail. This change fixes this issue and seems to not change the behaviour for other mp4 files (same thumbnail before and after change) This might also fix all mts file thumbnail generation. --- server/src/services/media.service.spec.ts | 6 +++--- server/src/utils/media.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/server/src/services/media.service.spec.ts b/server/src/services/media.service.spec.ts index 97351af37b..42f7c7bba3 100644 --- a/server/src/services/media.service.spec.ts +++ b/server/src/services/media.service.spec.ts @@ -368,7 +368,7 @@ describe(MediaService.name, () => { '-frames:v 1', '-update 1', '-v verbose', - String.raw`-vf fps=12:eof_action=pass:round=down,thumbnail=12,select=gt(scene\,0.1)-eq(prev_selected_n\,n)+isnan(prev_selected_n)+gt(n\,20),trim=end_frame=2,reverse,scale=-2:1440:flags=lanczos+accurate_rnd+full_chroma_int:out_range=pc`, + String.raw`-vf fps=12:start_time=0:eof_action=pass:round=down,thumbnail=12,select=gt(scene\,0.1)-eq(prev_selected_n\,n)+isnan(prev_selected_n)+gt(n\,20),trim=end_frame=2,reverse,scale=-2:1440:flags=lanczos+accurate_rnd+full_chroma_int:out_range=pc`, ], twoPass: false, }), @@ -403,7 +403,7 @@ describe(MediaService.name, () => { '-frames:v 1', '-update 1', '-v verbose', - String.raw`-vf fps=12:eof_action=pass:round=down,thumbnail=12,select=gt(scene\,0.1)-eq(prev_selected_n\,n)+isnan(prev_selected_n)+gt(n\,20),trim=end_frame=2,reverse,tonemapx=tonemap=hable:desat=0:p=bt709:t=bt709:m=bt709:r=pc:peak=100:format=yuv420p`, + String.raw`-vf fps=12:start_time=0:eof_action=pass:round=down,thumbnail=12,select=gt(scene\,0.1)-eq(prev_selected_n\,n)+isnan(prev_selected_n)+gt(n\,20),trim=end_frame=2,reverse,tonemapx=tonemap=hable:desat=0:p=bt709:t=bt709:m=bt709:r=pc:peak=100:format=yuv420p`, ], twoPass: false, }), @@ -440,7 +440,7 @@ describe(MediaService.name, () => { '-frames:v 1', '-update 1', '-v verbose', - String.raw`-vf fps=12:eof_action=pass:round=down,thumbnail=12,select=gt(scene\,0.1)-eq(prev_selected_n\,n)+isnan(prev_selected_n)+gt(n\,20),trim=end_frame=2,reverse,tonemapx=tonemap=hable:desat=0:p=bt709:t=bt709:m=bt709:r=pc:peak=100:format=yuv420p`, + String.raw`-vf fps=12:start_time=0:eof_action=pass:round=down,thumbnail=12,select=gt(scene\,0.1)-eq(prev_selected_n\,n)+isnan(prev_selected_n)+gt(n\,20),trim=end_frame=2,reverse,tonemapx=tonemap=hable:desat=0:p=bt709:t=bt709:m=bt709:r=pc:peak=100:format=yuv420p`, ], twoPass: false, }), diff --git a/server/src/utils/media.ts b/server/src/utils/media.ts index e43ecba49f..1ecd005315 100644 --- a/server/src/utils/media.ts +++ b/server/src/utils/media.ts @@ -403,7 +403,7 @@ export class ThumbnailConfig extends BaseConfig { getFilterOptions(videoStream: VideoStreamInfo): string[] { return [ - 'fps=12:eof_action=pass:round=down', + 'fps=12:start_time=0:eof_action=pass:round=down', 'thumbnail=12', String.raw`select=gt(scene\,0.1)-eq(prev_selected_n\,n)+isnan(prev_selected_n)+gt(n\,20)`, 'trim=end_frame=2', From 0121043d7d7cb8acf0972a645083d2458b64f0b3 Mon Sep 17 00:00:00 2001 From: Mert <101130780+mertalev@users.noreply.github.com> Date: Wed, 6 Aug 2025 11:05:49 -0400 Subject: [PATCH 189/748] refactor(mobile): sqlite-based map view (#20665) * feat(mobile): drift map page * refactor: map query * perf: do not filter markers * fix: refresh timeline by key * chore: rename * remove ref listen and global key * clean code * remove locked and favorite * temporary change for stress test * optimizations * fix bottom sheet * cleaner bounds check * cleanup * feat: back button --------- Co-authored-by: wuzihao051119 Co-authored-by: Alex --- .../drift_schemas/main/drift_schema_v7.json | 1 + mobile/lib/domain/models/map.model.dart | 18 + mobile/lib/domain/services/map.service.dart | 23 + .../lib/domain/services/timeline.service.dart | 3 + .../infrastructure/entities/exif.entity.dart | 1 + .../entities/exif.entity.drift.dart | 4 + .../repositories/db.repository.dart | 7 +- .../repositories/db.repository.drift.dart | 1 + .../repositories/db.repository.steps.dart | 352 + .../repositories/map.repository.dart | 66 + .../repositories/timeline.repository.dart | 59 + .../presentation/pages/drift_map.page.dart | 38 + .../presentation/pages/drift_place.page.dart | 3 +- .../base_bottom_sheet.widget.dart | 57 +- .../bottom_sheet/map_bottom_sheet.widget.dart | 42 + .../presentation/widgets/map/map.state.dart | 61 + .../presentation/widgets/map/map.widget.dart | 191 + .../presentation/widgets/map/map_utils.dart | 138 + .../widgets/timeline/timeline.widget.dart | 12 +- .../infrastructure/map.provider.dart | 24 + mobile/lib/routing/router.dart | 2 + mobile/lib/routing/router.gr.dart | 39 + mobile/lib/utils/async_mutex.dart | 2 +- mobile/lib/utils/debounce.dart | 5 +- mobile/test/drift/main/generated/schema.dart | 5 +- .../test/drift/main/generated/schema_v7.dart | 6453 +++++++++++++++++ 26 files changed, 7573 insertions(+), 34 deletions(-) create mode 100644 mobile/drift_schemas/main/drift_schema_v7.json create mode 100644 mobile/lib/domain/models/map.model.dart create mode 100644 mobile/lib/domain/services/map.service.dart create mode 100644 mobile/lib/infrastructure/repositories/map.repository.dart create mode 100644 mobile/lib/presentation/pages/drift_map.page.dart create mode 100644 mobile/lib/presentation/widgets/bottom_sheet/map_bottom_sheet.widget.dart create mode 100644 mobile/lib/presentation/widgets/map/map.state.dart create mode 100644 mobile/lib/presentation/widgets/map/map.widget.dart create mode 100644 mobile/lib/presentation/widgets/map/map_utils.dart create mode 100644 mobile/lib/providers/infrastructure/map.provider.dart create mode 100644 mobile/test/drift/main/generated/schema_v7.dart diff --git a/mobile/drift_schemas/main/drift_schema_v7.json b/mobile/drift_schemas/main/drift_schema_v7.json new file mode 100644 index 0000000000..77f57c34d9 --- /dev/null +++ b/mobile/drift_schemas/main/drift_schema_v7.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"has_profile_image","getter_name":"hasProfileImage","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"has_profile_image\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"has_profile_image\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"profile_changed_at","getter_name":"profileChangedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"library_id","getter_name":"libraryId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":5,"references":[3,4],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":6,"references":[3],"type":"index","data":{"on":3,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_owner_checksum","sql":null,"unique":false,"columns":["owner_id","checksum"]}},{"id":8,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum\nON remote_asset_entity (owner_id, checksum)\nWHERE (library_id IS NULL);\n","unique":true,"columns":[]}},{"id":9,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_library_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum\nON remote_asset_entity (owner_id, library_id, checksum)\nWHERE (library_id IS NOT NULL);\n","unique":true,"columns":[]}},{"id":10,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":11,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":12,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":13,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":14,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":15,"references":[1,14],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":16,"references":[14,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":18,"references":[1,17],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":19,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":20,"references":[1,19],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":21,"references":[13],"type":"index","data":{"on":13,"name":"idx_lat_lng","sql":null,"unique":false,"columns":["latitude","longitude"]}}]} \ No newline at end of file diff --git a/mobile/lib/domain/models/map.model.dart b/mobile/lib/domain/models/map.model.dart new file mode 100644 index 0000000000..ce0834f0cb --- /dev/null +++ b/mobile/lib/domain/models/map.model.dart @@ -0,0 +1,18 @@ +import 'package:maplibre_gl/maplibre_gl.dart'; + +class Marker { + final LatLng location; + final String assetId; + + const Marker({required this.location, required this.assetId}); + + @override + bool operator ==(covariant Marker other) { + if (identical(this, other)) return true; + + return other.location == location && other.assetId == assetId; + } + + @override + int get hashCode => location.hashCode ^ assetId.hashCode; +} diff --git a/mobile/lib/domain/services/map.service.dart b/mobile/lib/domain/services/map.service.dart new file mode 100644 index 0000000000..8c50a5aaeb --- /dev/null +++ b/mobile/lib/domain/services/map.service.dart @@ -0,0 +1,23 @@ +import 'package:immich_mobile/domain/models/map.model.dart'; +import 'package:immich_mobile/infrastructure/repositories/map.repository.dart'; +import 'package:maplibre_gl/maplibre_gl.dart'; + +typedef MapMarkerSource = Future> Function(LatLngBounds? bounds); + +typedef MapQuery = ({MapMarkerSource markerSource}); + +class MapFactory { + final DriftMapRepository _mapRepository; + + const MapFactory({required DriftMapRepository mapRepository}) : _mapRepository = mapRepository; + + MapService remote(String ownerId) => MapService(_mapRepository.remote(ownerId)); +} + +class MapService { + final MapMarkerSource _markerSource; + + MapService(MapQuery query) : _markerSource = query.markerSource; + + Future> Function(LatLngBounds? bounds) get getMarkers => _markerSource; +} diff --git a/mobile/lib/domain/services/timeline.service.dart b/mobile/lib/domain/services/timeline.service.dart index 9fa4106d17..53a8bc671b 100644 --- a/mobile/lib/domain/services/timeline.service.dart +++ b/mobile/lib/domain/services/timeline.service.dart @@ -10,6 +10,7 @@ import 'package:immich_mobile/domain/services/setting.service.dart'; import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/infrastructure/repositories/timeline.repository.dart'; import 'package:immich_mobile/utils/async_mutex.dart'; +import 'package:maplibre_gl/maplibre_gl.dart'; typedef TimelineAssetSource = Future> Function(int index, int count); @@ -57,6 +58,8 @@ class TimelineFactory { TimelineService(_timelineRepository.person(userId, personId, groupBy)); TimelineService fromAssets(List assets) => TimelineService(_timelineRepository.fromAssets(assets)); + + TimelineService map(LatLngBounds bounds) => TimelineService(_timelineRepository.map(bounds, groupBy)); } class TimelineService { diff --git a/mobile/lib/infrastructure/entities/exif.entity.dart b/mobile/lib/infrastructure/entities/exif.entity.dart index e8c7541349..87c32461da 100644 --- a/mobile/lib/infrastructure/entities/exif.entity.dart +++ b/mobile/lib/infrastructure/entities/exif.entity.dart @@ -95,6 +95,7 @@ class ExifInfo { ); } +@TableIndex(name: 'idx_lat_lng', columns: {#latitude, #longitude}) class RemoteExifEntity extends Table with DriftDefaultsMixin { const RemoteExifEntity(); diff --git a/mobile/lib/infrastructure/entities/exif.entity.drift.dart b/mobile/lib/infrastructure/entities/exif.entity.drift.dart index d45d6e8ef6..c31050c321 100644 --- a/mobile/lib/infrastructure/entities/exif.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/exif.entity.drift.dart @@ -699,6 +699,10 @@ typedef $$RemoteExifEntityTableProcessedTableManager = i1.RemoteExifEntityData, i0.PrefetchHooks Function({bool assetId}) >; +i0.Index get idxLatLng => i0.Index( + 'idx_lat_lng', + 'CREATE INDEX idx_lat_lng ON remote_exif_entity (latitude, longitude)', +); class $RemoteExifEntityTable extends i2.RemoteExifEntity with i0.TableInfo<$RemoteExifEntityTable, i1.RemoteExifEntityData> { diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index 0458a5b254..17cd590d79 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -21,7 +21,7 @@ import 'package:immich_mobile/infrastructure/entities/stack.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.steps.dart'; -import 'package:isar/isar.dart'; +import 'package:isar/isar.dart' hide Index; import 'db.repository.drift.dart'; @@ -66,7 +66,7 @@ class Drift extends $Drift implements IDatabaseRepository { : super(executor ?? driftDatabase(name: 'immich', native: const DriftNativeOptions(shareAcrossIsolates: true))); @override - int get schemaVersion => 6; + int get schemaVersion => 7; @override MigrationStrategy get migration => MigrationStrategy( @@ -112,6 +112,9 @@ class Drift extends $Drift implements IDatabaseRepository { await m.create(v6.uQRemoteAssetsOwnerChecksum); await m.create(v6.uQRemoteAssetsOwnerLibraryChecksum); }, + from6To7: (m, v7) async { + await m.createIndex(v7.idxLatLng); + }, ), ); diff --git a/mobile/lib/infrastructure/repositories/db.repository.drift.dart b/mobile/lib/infrastructure/repositories/db.repository.drift.dart index 2b7203eb46..fd170fc22d 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.drift.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.drift.dart @@ -98,6 +98,7 @@ abstract class $Drift extends i0.GeneratedDatabase { memoryAssetEntity, personEntity, assetFaceEntity, + i9.idxLatLng, ]; @override i0.StreamQueryUpdateRules diff --git a/mobile/lib/infrastructure/repositories/db.repository.steps.dart b/mobile/lib/infrastructure/repositories/db.repository.steps.dart index 32b309881e..9d6b02ab32 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.steps.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.steps.dart @@ -2705,12 +2705,357 @@ i1.GeneratedColumn _column_86(String aliasedName) => true, type: i1.DriftSqlType.string, ); + +final class Schema7 extends i0.VersionedSchema { + Schema7({required super.database}) : super(version: 7); + @override + late final List entities = [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + idxRemoteAssetOwnerChecksum, + uQRemoteAssetsOwnerChecksum, + uQRemoteAssetsOwnerLibraryChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + idxLatLng, + ]; + late final Shape16 userEntity = Shape16( + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_84, + _column_85, + _column_5, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape17 remoteAssetEntity = Shape17( + source: i0.VersionedTable( + entityName: 'remote_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_13, + _column_14, + _column_15, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + _column_86, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape3 stackEntity = Shape3( + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_0, _column_9, _column_5, _column_15, _column_75], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape2 localAssetEntity = Shape2( + source: i0.VersionedTable( + entityName: 'local_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_22, + _column_14, + _column_23, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape6 localAlbumEntity = Shape6( + source: i0.VersionedTable( + entityName: 'local_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_5, + _column_31, + _column_32, + _column_33, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape7 localAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_34, _column_35], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLocalAssetChecksum = i1.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + final i1.Index idxRemoteAssetOwnerChecksum = i1.Index( + 'idx_remote_asset_owner_checksum', + 'CREATE INDEX idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', + ); + final i1.Index uQRemoteAssetsOwnerChecksum = i1.Index( + 'UQ_remote_assets_owner_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)', + ); + final i1.Index uQRemoteAssetsOwnerLibraryChecksum = i1.Index( + 'UQ_remote_assets_owner_library_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)', + ); + final i1.Index idxRemoteAssetChecksum = i1.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final Shape4 userMetadataEntity = Shape4( + source: i0.VersionedTable( + entityName: 'user_metadata_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(user_id, "key")'], + columns: [_column_25, _column_26, _column_27], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape5 partnerEntity = Shape5( + source: i0.VersionedTable( + entityName: 'partner_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(shared_by_id, shared_with_id)'], + columns: [_column_28, _column_29, _column_30], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape8 remoteExifEntity = Shape8( + source: i0.VersionedTable( + entityName: 'remote_exif_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id)'], + columns: [ + _column_36, + _column_37, + _column_38, + _column_39, + _column_40, + _column_41, + _column_11, + _column_10, + _column_42, + _column_43, + _column_44, + _column_45, + _column_46, + _column_47, + _column_48, + _column_49, + _column_50, + _column_51, + _column_52, + _column_53, + _column_54, + _column_55, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape9 remoteAlbumEntity = Shape9( + source: i0.VersionedTable( + entityName: 'remote_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_56, + _column_9, + _column_5, + _column_15, + _column_57, + _column_58, + _column_59, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape7 remoteAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'remote_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_36, _column_60], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape10 remoteAlbumUserEntity = Shape10( + source: i0.VersionedTable( + entityName: 'remote_album_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(album_id, user_id)'], + columns: [_column_60, _column_25, _column_61], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape11 memoryEntity = Shape11( + source: i0.VersionedTable( + entityName: 'memory_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_18, + _column_15, + _column_8, + _column_62, + _column_63, + _column_64, + _column_65, + _column_66, + _column_67, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape12 memoryAssetEntity = Shape12( + source: i0.VersionedTable( + entityName: 'memory_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, memory_id)'], + columns: [_column_36, _column_68], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape14 personEntity = Shape14( + source: i0.VersionedTable( + entityName: 'person_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_1, + _column_69, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape15 assetFaceEntity = Shape15( + source: i0.VersionedTable( + entityName: 'asset_face_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_36, + _column_76, + _column_77, + _column_78, + _column_79, + _column_80, + _column_81, + _column_82, + _column_83, + ], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLatLng = i1.Index( + 'idx_lat_lng', + 'CREATE INDEX idx_lat_lng ON remote_exif_entity (latitude, longitude)', + ); +} + i0.MigrationStepWithVersion migrationSteps({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, required Future Function(i1.Migrator m, Schema3 schema) from2To3, required Future Function(i1.Migrator m, Schema4 schema) from3To4, required Future Function(i1.Migrator m, Schema5 schema) from4To5, required Future Function(i1.Migrator m, Schema6 schema) from5To6, + required Future Function(i1.Migrator m, Schema7 schema) from6To7, }) { return (currentVersion, database) async { switch (currentVersion) { @@ -2739,6 +3084,11 @@ i0.MigrationStepWithVersion migrationSteps({ final migrator = i1.Migrator(database, schema); await from5To6(migrator, schema); return 6; + case 6: + final schema = Schema7(database: database); + final migrator = i1.Migrator(database, schema); + await from6To7(migrator, schema); + return 7; default: throw ArgumentError.value('Unknown migration from $currentVersion'); } @@ -2751,6 +3101,7 @@ i1.OnUpgrade stepByStep({ required Future Function(i1.Migrator m, Schema4 schema) from3To4, required Future Function(i1.Migrator m, Schema5 schema) from4To5, required Future Function(i1.Migrator m, Schema6 schema) from5To6, + required Future Function(i1.Migrator m, Schema7 schema) from6To7, }) => i0.VersionedSchema.stepByStepHelper( step: migrationSteps( from1To2: from1To2, @@ -2758,5 +3109,6 @@ i1.OnUpgrade stepByStep({ from3To4: from3To4, from4To5: from4To5, from5To6: from5To6, + from6To7: from6To7, ), ); diff --git a/mobile/lib/infrastructure/repositories/map.repository.dart b/mobile/lib/infrastructure/repositories/map.repository.dart new file mode 100644 index 0000000000..9b8cdcc19d --- /dev/null +++ b/mobile/lib/infrastructure/repositories/map.repository.dart @@ -0,0 +1,66 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/map.model.dart'; +import 'package:immich_mobile/domain/services/map.service.dart'; +import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; +import 'package:maplibre_gl/maplibre_gl.dart'; + +class DriftMapRepository extends DriftDatabaseRepository { + final Drift _db; + + const DriftMapRepository(super._db) : _db = _db; + + MapQuery remote(String ownerId) => _mapQueryBuilder( + assetFilter: (row) => + row.deletedAt.isNull() & row.visibility.equalsValue(AssetVisibility.timeline) & row.ownerId.equals(ownerId), + ); + + MapQuery _mapQueryBuilder({Expression Function($RemoteAssetEntityTable row)? assetFilter}) { + return (markerSource: (bounds) => _watchMapMarker(assetFilter: assetFilter, bounds: bounds)); + } + + Future> _watchMapMarker({ + Expression Function($RemoteAssetEntityTable row)? assetFilter, + LatLngBounds? bounds, + }) async { + final assetId = _db.remoteExifEntity.assetId; + final latitude = _db.remoteExifEntity.latitude; + final longitude = _db.remoteExifEntity.longitude; + + final query = _db.remoteExifEntity.selectOnly() + ..addColumns([assetId, latitude, longitude]) + ..join([innerJoin(_db.remoteAssetEntity, _db.remoteAssetEntity.id.equalsExp(assetId), useColumns: false)]) + ..limit(10000); + + if (assetFilter != null) { + query.where(assetFilter(_db.remoteAssetEntity)); + } + + if (bounds != null) { + query.where(_db.remoteExifEntity.inBounds(bounds)); + } else { + query.where(latitude.isNotNull() & longitude.isNotNull()); + } + + final rows = await query.get(); + return List.generate(rows.length, (i) { + final row = rows[i]; + return Marker(assetId: row.read(assetId)!, location: LatLng(row.read(latitude)!, row.read(longitude)!)); + }, growable: false); + } +} + +extension MapBounds on $RemoteExifEntityTable { + Expression inBounds(LatLngBounds bounds) { + final southwest = bounds.southwest; + final northeast = bounds.northeast; + + final latInBounds = latitude.isBetweenValues(southwest.latitude, northeast.latitude); + final longInBounds = southwest.longitude <= northeast.longitude + ? longitude.isBetweenValues(southwest.longitude, northeast.longitude) + : (longitude.isBiggerOrEqualValue(southwest.longitude) | longitude.isSmallerOrEqualValue(northeast.longitude)); + return latInBounds & longInBounds; + } +} diff --git a/mobile/lib/infrastructure/repositories/timeline.repository.dart b/mobile/lib/infrastructure/repositories/timeline.repository.dart index 663db9b82f..b4188f7ac4 100644 --- a/mobile/lib/infrastructure/repositories/timeline.repository.dart +++ b/mobile/lib/infrastructure/repositories/timeline.repository.dart @@ -11,6 +11,8 @@ import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/map.repository.dart'; +import 'package:maplibre_gl/maplibre_gl.dart'; import 'package:stream_transform/stream_transform.dart'; class DriftTimelineRepository extends DriftDatabaseRepository { @@ -427,6 +429,63 @@ class DriftTimelineRepository extends DriftDatabaseRepository { return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get(); } + TimelineQuery map(LatLngBounds bounds, GroupAssetsBy groupBy) => ( + bucketSource: () => _watchMapBucket(bounds, groupBy: groupBy), + assetSource: (offset, count) => _getMapBucketAssets(bounds, offset: offset, count: count), + ); + + Stream> _watchMapBucket(LatLngBounds bounds, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { + if (groupBy == GroupAssetsBy.none) { + // TODO: Support GroupAssetsBy.none + throw UnsupportedError("GroupAssetsBy.none is not supported for _watchMapBucket"); + } + + final assetCountExp = _db.remoteAssetEntity.id.count(); + final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy); + + final query = _db.remoteAssetEntity.selectOnly() + ..addColumns([assetCountExp, dateExp]) + ..join([ + innerJoin( + _db.remoteExifEntity, + _db.remoteExifEntity.assetId.equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + ]) + ..where( + _db.remoteExifEntity.inBounds(bounds) & + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) & + _db.remoteAssetEntity.deletedAt.isNull(), + ) + ..groupBy([dateExp]) + ..orderBy([OrderingTerm.desc(dateExp)]); + + return query.map((row) { + final timeline = row.read(dateExp)!.dateFmt(groupBy); + final assetCount = row.read(assetCountExp)!; + return TimeBucket(date: timeline, assetCount: assetCount); + }).watch(); + } + + Future> _getMapBucketAssets(LatLngBounds bounds, {required int offset, required int count}) { + final query = + _db.remoteAssetEntity.select().join([ + innerJoin( + _db.remoteExifEntity, + _db.remoteExifEntity.assetId.equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + ]) + ..where( + _db.remoteExifEntity.inBounds(bounds) & + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) & + _db.remoteAssetEntity.deletedAt.isNull(), + ) + ..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)]) + ..limit(count, offset: offset); + return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get(); + } + TimelineQuery _remoteQueryBuilder({ required Expression Function($RemoteAssetEntityTable row) filter, GroupAssetsBy groupBy = GroupAssetsBy.day, diff --git a/mobile/lib/presentation/pages/drift_map.page.dart b/mobile/lib/presentation/pages/drift_map.page.dart new file mode 100644 index 0000000000..30da6410b5 --- /dev/null +++ b/mobile/lib/presentation/pages/drift_map.page.dart @@ -0,0 +1,38 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/map/map.widget.dart'; +import 'package:maplibre_gl/maplibre_gl.dart'; + +@RoutePage() +class DriftMapPage extends StatelessWidget { + final LatLng? initialLocation; + + const DriftMapPage({super.key, this.initialLocation}); + + @override + Widget build(BuildContext context) { + return Scaffold( + extendBodyBehindAppBar: true, + body: Stack( + children: [ + DriftMap(initialLocation: initialLocation), + Positioned( + left: 16, + top: 60, + child: IconButton.filled( + color: Colors.white, + onPressed: () => context.pop(), + icon: const Icon(Icons.arrow_back_ios_new_rounded), + style: IconButton.styleFrom( + shape: const CircleBorder(side: BorderSide(width: 1, color: Colors.black26)), + padding: const EdgeInsets.all(8), + backgroundColor: Colors.indigo.withValues(alpha: 0.7), + ), + ), + ), + ], + ), + ); + } +} diff --git a/mobile/lib/presentation/pages/drift_place.page.dart b/mobile/lib/presentation/pages/drift_place.page.dart index e85bb90d54..f540cbd46e 100644 --- a/mobile/lib/presentation/pages/drift_place.page.dart +++ b/mobile/lib/presentation/pages/drift_place.page.dart @@ -92,9 +92,8 @@ class _Map extends StatelessWidget { child: SizedBox( height: 200, width: context.width, - // TODO: migrate to DriftMapRoute after merging #19898 child: MapThumbnail( - onTap: (_, __) => context.pushRoute(MapRoute(initialLocation: currentLocation)), + onTap: (_, __) => context.pushRoute(DriftMapRoute(initialLocation: currentLocation)), zoom: 8, centre: currentLocation ?? const LatLng(21.44950, -157.91959), showAttribution: false, diff --git a/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart index 82f2e1c3b2..acbf2ad74f 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart @@ -69,7 +69,7 @@ class _BaseDraggableScrollableSheetState extends ConsumerState shouldCloseOnMinExtent: widget.shouldCloseOnMinExtent, builder: (BuildContext context, ScrollController scrollController) { return Card( - color: widget.backgroundColor ?? context.colorScheme.surfaceContainerHigh, + color: widget.backgroundColor ?? context.colorScheme.surface, borderOnForeground: false, clipBehavior: Clip.antiAlias, elevation: 6.0, @@ -78,25 +78,21 @@ class _BaseDraggableScrollableSheetState extends ConsumerState child: CustomScrollView( controller: scrollController, slivers: [ - SliverToBoxAdapter( - child: Column( - children: [ - const SizedBox(height: 10), - const _DragHandle(), - const SizedBox(height: 14), - if (widget.actions.isNotEmpty) + const SliverPersistentHeader(delegate: _DragHandleDelegate(), pinned: true), + if (widget.actions.isNotEmpty) + SliverToBoxAdapter( + child: Column( + children: [ SizedBox( height: 115, child: ListView(shrinkWrap: true, scrollDirection: Axis.horizontal, children: widget.actions), ), - if (widget.actions.isNotEmpty) ...[ const Divider(indent: 16, endIndent: 16), const SizedBox(height: 16), ], - ], + ), ), - ), - ...(widget.slivers ?? []), + if (widget.slivers != null) ...widget.slivers!, ], ), ); @@ -105,17 +101,42 @@ class _BaseDraggableScrollableSheetState extends ConsumerState } } +class _DragHandleDelegate extends SliverPersistentHeaderDelegate { + const _DragHandleDelegate(); + + @override + Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) { + return const _DragHandle(); + } + + @override + bool shouldRebuild(_DragHandleDelegate oldDelegate) => false; + + @override + double get minExtent => 50.0; + + @override + double get maxExtent => 50.0; +} + class _DragHandle extends StatelessWidget { const _DragHandle(); @override Widget build(BuildContext context) { - return Container( - height: 6, - width: 32, - decoration: BoxDecoration( - color: context.themeData.dividerColor.lighten(amount: 0.6), - borderRadius: const BorderRadius.all(Radius.circular(20)), + return SizedBox( + height: 50, + child: Center( + child: SizedBox( + width: 32, + height: 6, + child: DecoratedBox( + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(20)), + color: context.themeData.dividerColor.lighten(amount: 0.6), + ), + ), + ), ), ); } diff --git a/mobile/lib/presentation/widgets/bottom_sheet/map_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/map_bottom_sheet.widget.dart new file mode 100644 index 0000000000..0017621c39 --- /dev/null +++ b/mobile/lib/presentation/widgets/bottom_sheet/map_bottom_sheet.widget.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart'; +import 'package:immich_mobile/presentation/widgets/map/map.state.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; + +class MapBottomSheet extends StatelessWidget { + const MapBottomSheet({super.key}); + + @override + Widget build(BuildContext context) { + return const BaseBottomSheet( + initialChildSize: 0.25, + maxChildSize: 0.9, + shouldCloseOnMinExtent: false, + resizeOnScroll: false, + actions: [], + slivers: [SliverFillRemaining(hasScrollBody: false, child: _ScopedMapTimeline())], + ); + } +} + +class _ScopedMapTimeline extends StatelessWidget { + const _ScopedMapTimeline(); + + @override + Widget build(BuildContext context) { + // TODO: this causes the timeline to switch to flicker to "loading" state and back. This is both janky and inefficient. + return ProviderScope( + overrides: [ + timelineServiceProvider.overrideWith((ref) { + final bounds = ref.watch(mapStateProvider).bounds; + final timelineService = ref.watch(timelineFactoryProvider).map(bounds); + ref.onDispose(timelineService.dispose); + return timelineService; + }), + ], + child: const Timeline(appBar: null, bottomSheet: null), + ); + } +} diff --git a/mobile/lib/presentation/widgets/map/map.state.dart b/mobile/lib/presentation/widgets/map/map.state.dart new file mode 100644 index 0000000000..b849f954ae --- /dev/null +++ b/mobile/lib/presentation/widgets/map/map.state.dart @@ -0,0 +1,61 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/providers/infrastructure/map.provider.dart'; +import 'package:maplibre_gl/maplibre_gl.dart'; + +class MapState { + final LatLngBounds bounds; + + const MapState({required this.bounds}); + + @override + bool operator ==(covariant MapState other) { + return bounds == other.bounds; + } + + @override + int get hashCode => bounds.hashCode; + + MapState copyWith({LatLngBounds? bounds}) { + return MapState(bounds: bounds ?? this.bounds); + } +} + +class MapStateNotifier extends Notifier { + MapStateNotifier(); + + bool setBounds(LatLngBounds bounds) { + if (state.bounds == bounds) { + return false; + } + state = state.copyWith(bounds: bounds); + return true; + } + + @override + MapState build() => MapState( + // TODO: set default bounds + bounds: LatLngBounds(northeast: const LatLng(0, 0), southwest: const LatLng(0, 0)), + ); +} + +// This provider watches the markers from the map service and serves the markers. +// It should be used only after the map service provider is overridden +final mapMarkerProvider = FutureProvider.family, LatLngBounds?>((ref, bounds) async { + final mapService = ref.watch(mapServiceProvider); + final markers = await mapService.getMarkers(bounds); + final features = List.filled(markers.length, const {}); + for (int i = 0; i < markers.length; i++) { + final marker = markers[i]; + features[i] = { + 'type': 'Feature', + 'id': marker.assetId, + 'geometry': { + 'type': 'Point', + 'coordinates': [marker.location.longitude, marker.location.latitude], + }, + }; + } + return {'type': 'FeatureCollection', 'features': features}; +}, dependencies: [mapServiceProvider]); + +final mapStateProvider = NotifierProvider(MapStateNotifier.new); diff --git a/mobile/lib/presentation/widgets/map/map.widget.dart b/mobile/lib/presentation/widgets/map/map.widget.dart new file mode 100644 index 0000000000..6eab5741d2 --- /dev/null +++ b/mobile/lib/presentation/widgets/map/map.widget.dart @@ -0,0 +1,191 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/bottom_sheet/map_bottom_sheet.widget.dart'; +import 'package:immich_mobile/presentation/widgets/map/map_utils.dart'; +import 'package:immich_mobile/presentation/widgets/map/map.state.dart'; +import 'package:immich_mobile/utils/async_mutex.dart'; +import 'package:immich_mobile/utils/debounce.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; +import 'package:immich_mobile/widgets/map/map_theme_override.dart'; +import 'package:maplibre_gl/maplibre_gl.dart'; + +class CustomSourceProperties implements SourceProperties { + final Map data; + const CustomSourceProperties({required this.data}); + + @override + Map toJson() { + return { + "type": "geojson", + "data": data, + // "cluster": true, + // "clusterRadius": 1, + // "clusterMinPoints": 5, + // "tolerance": 0.1, + }; + } +} + +class DriftMap extends ConsumerStatefulWidget { + final LatLng? initialLocation; + + const DriftMap({super.key, this.initialLocation}); + + @override + ConsumerState createState() => _DriftMapState(); +} + +class _DriftMapState extends ConsumerState { + MapLibreMapController? mapController; + final _reloadMutex = AsyncMutex(); + final _debouncer = Debouncer(interval: const Duration(milliseconds: 500), maxWaitTime: const Duration(seconds: 2)); + + @override + void dispose() { + _debouncer.dispose(); + super.dispose(); + } + + void onMapCreated(MapLibreMapController controller) { + mapController = controller; + } + + Future onMapReady() async { + final controller = mapController; + if (controller == null) { + return; + } + + await controller.addSource( + MapUtils.defaultSourceId, + const CustomSourceProperties(data: {'type': 'FeatureCollection', 'features': []}), + ); + + await controller.addHeatmapLayer( + MapUtils.defaultSourceId, + MapUtils.defaultHeatMapLayerId, + MapUtils.defaultHeatmapLayerProperties, + ); + _debouncer.run(setBounds); + controller.addListener(onMapMoved); + } + + void onMapMoved() { + if (mapController!.isCameraMoving || !mounted) { + return; + } + + _debouncer.run(setBounds); + } + + Future setBounds() async { + final controller = mapController; + if (controller == null || !mounted) { + return; + } + + final bounds = await controller.getVisibleRegion(); + _reloadMutex.run(() async { + if (mounted && ref.read(mapStateProvider.notifier).setBounds(bounds)) { + final markers = await ref.read(mapMarkerProvider(bounds).future); + await reloadMarkers(markers); + } + }); + } + + Future reloadMarkers(Map markers) async { + final controller = mapController; + if (controller == null || !mounted) { + return; + } + + await controller.setGeoJsonSource(MapUtils.defaultSourceId, markers); + } + + Future onZoomToLocation() async { + final (location, error) = await MapUtils.checkPermAndGetLocation(context: context); + if (error != null) { + if (error == LocationPermission.unableToDetermine && context.mounted) { + ImmichToast.show( + context: context, + gravity: ToastGravity.BOTTOM, + toastType: ToastType.error, + msg: "map_cannot_get_user_location".t(context: context), + ); + } + return; + } + + final controller = mapController; + if (controller != null && location != null) { + controller.animateCamera( + CameraUpdate.newLatLngZoom(LatLng(location.latitude, location.longitude), MapUtils.mapZoomToAssetLevel), + duration: const Duration(milliseconds: 800), + ); + } + } + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + _Map(initialLocation: widget.initialLocation, onMapCreated: onMapCreated, onMapReady: onMapReady), + _MyLocationButton(onZoomToLocation: onZoomToLocation), + const MapBottomSheet(), + ], + ); + } +} + +class _Map extends StatelessWidget { + final LatLng? initialLocation; + + const _Map({this.initialLocation, required this.onMapCreated, required this.onMapReady}); + + final MapCreatedCallback onMapCreated; + + final VoidCallback onMapReady; + + @override + Widget build(BuildContext context) { + final initialLocation = this.initialLocation; + return MapThemeOverride( + mapBuilder: (style) => style.widgetWhen( + onData: (style) => MapLibreMap( + initialCameraPosition: initialLocation == null + ? const CameraPosition(target: LatLng(0, 0), zoom: 0) + : CameraPosition(target: initialLocation, zoom: MapUtils.mapZoomToAssetLevel), + styleString: style, + onMapCreated: onMapCreated, + onStyleLoadedCallback: onMapReady, + ), + ), + ); + } +} + +class _MyLocationButton extends StatelessWidget { + const _MyLocationButton({required this.onZoomToLocation}); + + final VoidCallback onZoomToLocation; + + @override + Widget build(BuildContext context) { + return Positioned( + right: 0, + bottom: context.padding.bottom + 16, + child: ElevatedButton( + onPressed: onZoomToLocation, + style: ElevatedButton.styleFrom(shape: const CircleBorder()), + child: const Icon(Icons.my_location), + ), + ); + } +} diff --git a/mobile/lib/presentation/widgets/map/map_utils.dart b/mobile/lib/presentation/widgets/map/map_utils.dart new file mode 100644 index 0000000000..1c18fc48d6 --- /dev/null +++ b/mobile/lib/presentation/widgets/map/map_utils.dart @@ -0,0 +1,138 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/widgets/common/confirm_dialog.dart'; +import 'package:logging/logging.dart'; +import 'package:maplibre_gl/maplibre_gl.dart'; + +class MapUtils { + static final Logger _logger = Logger("MapUtils"); + + static const mapZoomToAssetLevel = 12.0; + static const defaultSourceId = 'asset-map-markers'; + static const defaultHeatMapLayerId = 'asset-heatmap-layer'; + static var markerCompleter = Completer()..complete(); + + static const defaultCircleLayerLayerProperties = CircleLayerProperties( + circleRadius: 10, + circleColor: "rgba(150,86,34,0.7)", + circleBlur: 1.0, + circleOpacity: 0.7, + circleStrokeWidth: 0.1, + circleStrokeColor: "rgba(203,46,19,0.5)", + circleStrokeOpacity: 0.7, + ); + + static const defaultHeatmapLayerProperties = HeatmapLayerProperties( + heatmapColor: [ + Expressions.interpolate, + ["linear"], + ["heatmap-density"], + 0.0, + "rgba(103,58,183,0.0)", + 0.3, + "rgb(103,58,183)", + 0.5, + "rgb(33,149,243)", + 0.7, + "rgb(76,175,79)", + 0.95, + "rgb(255,235,59)", + 1.0, + "rgb(255,86,34)", + ], + heatmapIntensity: [ + Expressions.interpolate, + ["linear"], + [Expressions.zoom], + 0, + 0.5, + 9, + 2, + ], + heatmapRadius: [ + Expressions.interpolate, + ["linear"], + [Expressions.zoom], + 0, + 4, + 4, + 8, + 9, + 16, + ], + heatmapOpacity: 0.7, + ); + + static Future<(Position?, LocationPermission?)> checkPermAndGetLocation({ + required BuildContext context, + bool silent = false, + }) async { + try { + bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); + if (!serviceEnabled && !silent) { + showDialog(context: context, builder: (context) => _LocationServiceDisabledDialog(context)); + return (null, LocationPermission.deniedForever); + } + + LocationPermission permission = await Geolocator.checkPermission(); + bool shouldRequestPermission = false; + + if (permission == LocationPermission.denied && !silent) { + shouldRequestPermission = await showDialog( + context: context, + builder: (context) => _LocationPermissionDisabledDialog(context), + ); + if (shouldRequestPermission) { + permission = await Geolocator.requestPermission(); + } + } + + if (permission == LocationPermission.denied || permission == LocationPermission.deniedForever) { + // Open app settings only if you did not request for permission before + if (permission == LocationPermission.deniedForever && !shouldRequestPermission && !silent) { + await Geolocator.openAppSettings(); + } + return (null, LocationPermission.deniedForever); + } + + Position currentUserLocation = await Geolocator.getCurrentPosition( + locationSettings: const LocationSettings( + accuracy: LocationAccuracy.high, + distanceFilter: 0, + timeLimit: Duration(seconds: 5), + ), + ); + return (currentUserLocation, null); + } catch (error, stack) { + _logger.severe("Cannot get user's current location", error, stack); + return (null, LocationPermission.unableToDetermine); + } + } +} + +class _LocationServiceDisabledDialog extends ConfirmDialog { + _LocationServiceDisabledDialog(BuildContext context) + : super( + title: 'map_location_service_disabled_title'.t(context: context), + content: 'map_location_service_disabled_content'.t(context: context), + cancel: 'cancel'.t(context: context), + ok: 'yes'.t(context: context), + onOk: () async { + await Geolocator.openLocationSettings(); + }, + ); +} + +class _LocationPermissionDisabledDialog extends ConfirmDialog { + _LocationPermissionDisabledDialog(BuildContext context) + : super( + title: 'map_no_location_permission_title'.t(context: context), + content: 'map_no_location_permission_content'.t(context: context), + cancel: 'cancel'.t(context: context), + ok: 'yes'.t(context: context), + onOk: () {}, + ); +} diff --git a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart index 94e13c4e9f..b35aac59ee 100644 --- a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart @@ -107,14 +107,10 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { super.initState(); _eventSubscription = EventStream.shared.listen(_onEvent); - WidgetsBinding.instance.addPostFrameCallback((_) { - final currentTilesPerRow = ref.read(settingsProvider).get(Setting.tilesPerRow); - setState(() { - _perRow = currentTilesPerRow; - _scaleFactor = 7.0 - _perRow; - _baseScaleFactor = _scaleFactor; - }); - }); + final currentTilesPerRow = ref.read(settingsProvider).get(Setting.tilesPerRow); + _perRow = currentTilesPerRow; + _scaleFactor = 7.0 - _perRow; + _baseScaleFactor = _scaleFactor; ref.listenManual(multiSelectProvider.select((s) => s.isEnabled), _onMultiSelectionToggled); } diff --git a/mobile/lib/providers/infrastructure/map.provider.dart b/mobile/lib/providers/infrastructure/map.provider.dart new file mode 100644 index 0000000000..e774cec756 --- /dev/null +++ b/mobile/lib/providers/infrastructure/map.provider.dart @@ -0,0 +1,24 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/infrastructure/repositories/map.repository.dart'; +import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/domain/services/map.service.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; + +final mapRepositoryProvider = Provider((ref) => DriftMapRepository(ref.watch(driftProvider))); + +final mapServiceProvider = Provider( + (ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access map'); + } + + final mapService = ref.watch(mapFactoryProvider).remote(user.id); + return mapService; + }, + // Empty dependencies to inform the framework that this provider + // might be used in a ProviderScope + dependencies: const [], +); + +final mapFactoryProvider = Provider((ref) => MapFactory(mapRepository: ref.watch(mapRepositoryProvider))); diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index e75da235df..da24617824 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -89,6 +89,7 @@ import 'package:immich_mobile/presentation/pages/drift_favorite.page.dart'; import 'package:immich_mobile/presentation/pages/drift_library.page.dart'; import 'package:immich_mobile/presentation/pages/drift_local_album.page.dart'; import 'package:immich_mobile/presentation/pages/drift_locked_folder.page.dart'; +import 'package:immich_mobile/presentation/pages/drift_map.page.dart'; import 'package:immich_mobile/presentation/pages/drift_memory.page.dart'; import 'package:immich_mobile/presentation/pages/drift_partner_detail.page.dart'; import 'package:immich_mobile/presentation/pages/drift_people_collection.page.dart'; @@ -331,6 +332,7 @@ class AppRouter extends RootStackRouter { AutoRoute(page: DriftPersonRoute.page, guards: [_authGuard]), AutoRoute(page: DriftBackupOptionsRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: DriftAlbumOptionsRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftMapRoute.page, guards: [_authGuard, _duplicateGuard]), // required to handle all deeplinks in deep_link.service.dart // auto_route_library#1722 RedirectRoute(path: '*', redirectTo: '/'), diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index f5b3728ffe..8c5064e752 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -892,6 +892,45 @@ class DriftLockedFolderRoute extends PageRouteInfo { ); } +/// generated route for +/// [DriftMapPage] +class DriftMapRoute extends PageRouteInfo { + DriftMapRoute({ + Key? key, + LatLng? initialLocation, + List? children, + }) : super( + DriftMapRoute.name, + args: DriftMapRouteArgs(key: key, initialLocation: initialLocation), + initialChildren: children, + ); + + static const String name = 'DriftMapRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + final args = data.argsAs( + orElse: () => const DriftMapRouteArgs(), + ); + return DriftMapPage(key: args.key, initialLocation: args.initialLocation); + }, + ); +} + +class DriftMapRouteArgs { + const DriftMapRouteArgs({this.key, this.initialLocation}); + + final Key? key; + + final LatLng? initialLocation; + + @override + String toString() { + return 'DriftMapRouteArgs{key: $key, initialLocation: $initialLocation}'; + } +} + /// generated route for /// [DriftMemoryPage] class DriftMemoryRoute extends PageRouteInfo { diff --git a/mobile/lib/utils/async_mutex.dart b/mobile/lib/utils/async_mutex.dart index c61e6b13f3..b97ab9b052 100644 --- a/mobile/lib/utils/async_mutex.dart +++ b/mobile/lib/utils/async_mutex.dart @@ -5,7 +5,7 @@ class AsyncMutex { Future _running = Future.value(null); int _enqueued = 0; - get enqueued => _enqueued; + int get enqueued => _enqueued; /// Execute [operation] exclusively, after any currently running operations. /// Returns a [Future] with the result of the [operation]. diff --git a/mobile/lib/utils/debounce.dart b/mobile/lib/utils/debounce.dart index 5451b569ca..4c80601424 100644 --- a/mobile/lib/utils/debounce.dart +++ b/mobile/lib/utils/debounce.dart @@ -27,8 +27,9 @@ class Debouncer { } Future? drain() { - if (_timer != null && _timer!.isActive) { - _timer!.cancel(); + final timer = _timer; + if (timer != null && timer.isActive) { + timer.cancel(); if (_lastAction != null) { _callAndRest(); } diff --git a/mobile/test/drift/main/generated/schema.dart b/mobile/test/drift/main/generated/schema.dart index d59002bf56..87de9194d3 100644 --- a/mobile/test/drift/main/generated/schema.dart +++ b/mobile/test/drift/main/generated/schema.dart @@ -9,6 +9,7 @@ import 'schema_v3.dart' as v3; import 'schema_v4.dart' as v4; import 'schema_v5.dart' as v5; import 'schema_v6.dart' as v6; +import 'schema_v7.dart' as v7; class GeneratedHelper implements SchemaInstantiationHelper { @override @@ -26,10 +27,12 @@ class GeneratedHelper implements SchemaInstantiationHelper { return v5.DatabaseAtV5(db); case 6: return v6.DatabaseAtV6(db); + case 7: + return v7.DatabaseAtV7(db); default: throw MissingSchemaException(version, versions); } } - static const versions = const [1, 2, 3, 4, 5, 6]; + static const versions = const [1, 2, 3, 4, 5, 6, 7]; } diff --git a/mobile/test/drift/main/generated/schema_v7.dart b/mobile/test/drift/main/generated/schema_v7.dart new file mode 100644 index 0000000000..c91e1ac53a --- /dev/null +++ b/mobile/test/drift/main/generated/schema_v7.dart @@ -0,0 +1,6453 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class UserEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn hasProfileImage = GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn profileChangedAt = + GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + @override + List get $columns => [ + id, + name, + isAdmin, + email, + hasProfileImage, + profileChangedAt, + updatedAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_entity'; + @override + Set get $primaryKey => {id}; + @override + UserEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + hasProfileImage: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}has_profile_image'], + )!, + profileChangedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}profile_changed_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ); + } + + @override + UserEntity createAlias(String alias) { + return UserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserEntityData extends DataClass implements Insertable { + final String id; + final String name; + final bool isAdmin; + final String email; + final bool hasProfileImage; + final DateTime profileChangedAt; + final DateTime updatedAt; + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + required this.hasProfileImage, + required this.profileChangedAt, + required this.updatedAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['is_admin'] = Variable(isAdmin); + map['email'] = Variable(email); + map['has_profile_image'] = Variable(hasProfileImage); + map['profile_changed_at'] = Variable(profileChangedAt); + map['updated_at'] = Variable(updatedAt); + return map; + } + + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + isAdmin: serializer.fromJson(json['isAdmin']), + email: serializer.fromJson(json['email']), + hasProfileImage: serializer.fromJson(json['hasProfileImage']), + profileChangedAt: serializer.fromJson(json['profileChangedAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'isAdmin': serializer.toJson(isAdmin), + 'email': serializer.toJson(email), + 'hasProfileImage': serializer.toJson(hasProfileImage), + 'profileChangedAt': serializer.toJson(profileChangedAt), + 'updatedAt': serializer.toJson(updatedAt), + }; + } + + UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + bool? hasProfileImage, + DateTime? profileChangedAt, + DateTime? updatedAt, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + updatedAt: updatedAt ?? this.updatedAt, + ); + UserEntityData copyWithCompanion(UserEntityCompanion data) { + return UserEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, + email: data.email.present ? data.email.value : this.email, + hasProfileImage: data.hasProfileImage.present + ? data.hasProfileImage.value + : this.hasProfileImage, + profileChangedAt: data.profileChangedAt.present + ? data.profileChangedAt.value + : this.profileChangedAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ); + } + + @override + String toString() { + return (StringBuffer('UserEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + hasProfileImage, + profileChangedAt, + updatedAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserEntityData && + other.id == this.id && + other.name == this.name && + other.isAdmin == this.isAdmin && + other.email == this.email && + other.hasProfileImage == this.hasProfileImage && + other.profileChangedAt == this.profileChangedAt && + other.updatedAt == this.updatedAt); +} + +class UserEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value isAdmin; + final Value email; + final Value hasProfileImage; + final Value profileChangedAt; + final Value updatedAt; + const UserEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.isAdmin = const Value.absent(), + this.email = const Value.absent(), + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.updatedAt = const Value.absent(), + }); + UserEntityCompanion.insert({ + required String id, + required String name, + this.isAdmin = const Value.absent(), + required String email, + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.updatedAt = const Value.absent(), + }) : id = Value(id), + name = Value(name), + email = Value(email); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? isAdmin, + Expression? email, + Expression? hasProfileImage, + Expression? profileChangedAt, + Expression? updatedAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (isAdmin != null) 'is_admin': isAdmin, + if (email != null) 'email': email, + if (hasProfileImage != null) 'has_profile_image': hasProfileImage, + if (profileChangedAt != null) 'profile_changed_at': profileChangedAt, + if (updatedAt != null) 'updated_at': updatedAt, + }); + } + + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? hasProfileImage, + Value? profileChangedAt, + Value? updatedAt, + }) { + return UserEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + updatedAt: updatedAt ?? this.updatedAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (isAdmin.present) { + map['is_admin'] = Variable(isAdmin.value); + } + if (email.present) { + map['email'] = Variable(email.value); + } + if (hasProfileImage.present) { + map['has_profile_image'] = Variable(hasProfileImage.value); + } + if (profileChangedAt.present) { + map['profile_changed_at'] = Variable(profileChangedAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } +} + +class RemoteAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn localDateTime = + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn livePhotoVideoId = GeneratedColumn( + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn libraryId = GeneratedColumn( + 'library_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), + libraryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}library_id'], + ), + ); + } + + @override + RemoteAssetEntity createAlias(String alias) { + return RemoteAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String checksum; + final bool isFavorite; + final String ownerId; + final DateTime? localDateTime; + final String? thumbHash; + final DateTime? deletedAt; + final String? livePhotoVideoId; + final int visibility; + final String? stackId; + final String? libraryId; + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + this.libraryId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + map['checksum'] = Variable(checksum); + map['is_favorite'] = Variable(isFavorite); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || localDateTime != null) { + map['local_date_time'] = Variable(localDateTime); + } + if (!nullToAbsent || thumbHash != null) { + map['thumb_hash'] = Variable(thumbHash); + } + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + if (!nullToAbsent || livePhotoVideoId != null) { + map['live_photo_video_id'] = Variable(livePhotoVideoId); + } + map['visibility'] = Variable(visibility); + if (!nullToAbsent || stackId != null) { + map['stack_id'] = Variable(stackId); + } + if (!nullToAbsent || libraryId != null) { + map['library_id'] = Variable(libraryId); + } + return map; + } + + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + ownerId: serializer.fromJson(json['ownerId']), + localDateTime: serializer.fromJson(json['localDateTime']), + thumbHash: serializer.fromJson(json['thumbHash']), + deletedAt: serializer.fromJson(json['deletedAt']), + livePhotoVideoId: serializer.fromJson(json['livePhotoVideoId']), + visibility: serializer.fromJson(json['visibility']), + stackId: serializer.fromJson(json['stackId']), + libraryId: serializer.fromJson(json['libraryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'ownerId': serializer.toJson(ownerId), + 'localDateTime': serializer.toJson(localDateTime), + 'thumbHash': serializer.toJson(thumbHash), + 'deletedAt': serializer.toJson(deletedAt), + 'livePhotoVideoId': serializer.toJson(livePhotoVideoId), + 'visibility': serializer.toJson(visibility), + 'stackId': serializer.toJson(stackId), + 'libraryId': serializer.toJson(libraryId), + }; + } + + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + Value libraryId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + libraryId: libraryId.present ? libraryId.value : this.libraryId, + ); + RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { + return RemoteAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + localDateTime: data.localDateTime.present + ? data.localDateTime.value + : this.localDateTime, + thumbHash: data.thumbHash.present ? data.thumbHash.value : this.thumbHash, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + livePhotoVideoId: data.livePhotoVideoId.present + ? data.livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: data.visibility.present + ? data.visibility.value + : this.visibility, + stackId: data.stackId.present ? data.stackId.value : this.stackId, + libraryId: data.libraryId.present ? data.libraryId.value : this.libraryId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.ownerId == this.ownerId && + other.localDateTime == this.localDateTime && + other.thumbHash == this.thumbHash && + other.deletedAt == this.deletedAt && + other.livePhotoVideoId == this.livePhotoVideoId && + other.visibility == this.visibility && + other.stackId == this.stackId && + other.libraryId == this.libraryId); +} + +class RemoteAssetEntityCompanion + extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value ownerId; + final Value localDateTime; + final Value thumbHash; + final Value deletedAt; + final Value livePhotoVideoId; + final Value visibility; + final Value stackId; + final Value libraryId; + const RemoteAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.ownerId = const Value.absent(), + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + this.visibility = const Value.absent(), + this.stackId = const Value.absent(), + this.libraryId = const Value.absent(), + }); + RemoteAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + required String checksum, + this.isFavorite = const Value.absent(), + required String ownerId, + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + required int visibility, + this.stackId = const Value.absent(), + this.libraryId = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? ownerId, + Expression? localDateTime, + Expression? thumbHash, + Expression? deletedAt, + Expression? livePhotoVideoId, + Expression? visibility, + Expression? stackId, + Expression? libraryId, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (ownerId != null) 'owner_id': ownerId, + if (localDateTime != null) 'local_date_time': localDateTime, + if (thumbHash != null) 'thumb_hash': thumbHash, + if (deletedAt != null) 'deleted_at': deletedAt, + if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId, + if (visibility != null) 'visibility': visibility, + if (stackId != null) 'stack_id': stackId, + if (libraryId != null) 'library_id': libraryId, + }); + } + + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + Value? libraryId, + }) { + return RemoteAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime ?? this.localDateTime, + thumbHash: thumbHash ?? this.thumbHash, + deletedAt: deletedAt ?? this.deletedAt, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId ?? this.stackId, + libraryId: libraryId ?? this.libraryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (localDateTime.present) { + map['local_date_time'] = Variable(localDateTime.value); + } + if (thumbHash.present) { + map['thumb_hash'] = Variable(thumbHash.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (livePhotoVideoId.present) { + map['live_photo_video_id'] = Variable(livePhotoVideoId.value); + } + if (visibility.present) { + map['visibility'] = Variable(visibility.value); + } + if (stackId.present) { + map['stack_id'] = Variable(stackId.value); + } + if (libraryId.present) { + map['library_id'] = Variable(libraryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') + ..write(')')) + .toString(); + } +} + +class StackEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StackEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'stack_entity'; + @override + Set get $primaryKey => {id}; + @override + StackEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StackEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, + ); + } + + @override + StackEntity createAlias(String alias) { + return StackEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StackEntityData extends DataClass implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String primaryAssetId; + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['primary_asset_id'] = Variable(primaryAssetId); + return map; + } + + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StackEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + primaryAssetId: serializer.fromJson(json['primaryAssetId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'primaryAssetId': serializer.toJson(primaryAssetId), + }; + } + + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + StackEntityData copyWithCompanion(StackEntityCompanion data) { + return StackEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, + ); + } + + @override + String toString() { + return (StringBuffer('StackEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StackEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.primaryAssetId == this.primaryAssetId); +} + +class StackEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value primaryAssetId; + const StackEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.primaryAssetId = const Value.absent(), + }); + StackEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String primaryAssetId, + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? primaryAssetId, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, + }); + } + + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { + return StackEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (primaryAssetId.present) { + map['primary_asset_id'] = Variable(primaryAssetId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StackEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } +} + +class LocalAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, + ); + } + + @override + LocalAssetEntity createAlias(String alias) { + return LocalAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String? checksum; + final bool isFavorite; + final int orientation; + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + if (!nullToAbsent || checksum != null) { + map['checksum'] = Variable(checksum); + } + map['is_favorite'] = Variable(isFavorite); + map['orientation'] = Variable(orientation); + return map; + } + + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + orientation: serializer.fromJson(json['orientation']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'orientation': serializer.toJson(orientation), + }; + } + + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { + return LocalAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.orientation == this.orientation); +} + +class LocalAssetEntityCompanion extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value orientation; + const LocalAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }); + LocalAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? orientation, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (orientation != null) 'orientation': orientation, + }); + } + + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { + return LocalAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } +} + +class LocalAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); + @override + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), + ); + } + + @override + LocalAlbumEntity createAlias(String alias) { + return LocalAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final DateTime updatedAt; + final int backupSelection; + final bool isIosSharedAlbum; + final bool? marker_; + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['updated_at'] = Variable(updatedAt); + map['backup_selection'] = Variable(backupSelection); + map['is_ios_shared_album'] = Variable(isIosSharedAlbum); + if (!nullToAbsent || marker_ != null) { + map['marker'] = Variable(marker_); + } + return map; + } + + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + updatedAt: serializer.fromJson(json['updatedAt']), + backupSelection: serializer.fromJson(json['backupSelection']), + isIosSharedAlbum: serializer.fromJson(json['isIosSharedAlbum']), + marker_: serializer.fromJson(json['marker_']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'updatedAt': serializer.toJson(updatedAt), + 'backupSelection': serializer.toJson(backupSelection), + 'isIosSharedAlbum': serializer.toJson(isIosSharedAlbum), + 'marker_': serializer.toJson(marker_), + }; + } + + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); + LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { + return LocalAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, + marker_: data.marker_.present ? data.marker_.value : this.marker_, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.updatedAt == this.updatedAt && + other.backupSelection == this.backupSelection && + other.isIosSharedAlbum == this.isIosSharedAlbum && + other.marker_ == this.marker_); +} + +class LocalAlbumEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value updatedAt; + final Value backupSelection; + final Value isIosSharedAlbum; + final Value marker_; + const LocalAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.updatedAt = const Value.absent(), + this.backupSelection = const Value.absent(), + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }); + LocalAlbumEntityCompanion.insert({ + required String id, + required String name, + this.updatedAt = const Value.absent(), + required int backupSelection, + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? updatedAt, + Expression? backupSelection, + Expression? isIosSharedAlbum, + Expression? marker_, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (updatedAt != null) 'updated_at': updatedAt, + if (backupSelection != null) 'backup_selection': backupSelection, + if (isIosSharedAlbum != null) 'is_ios_shared_album': isIosSharedAlbum, + if (marker_ != null) 'marker': marker_, + }); + } + + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_, + }) { + return LocalAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_ ?? this.marker_, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (backupSelection.present) { + map['backup_selection'] = Variable(backupSelection.value); + } + if (isIosSharedAlbum.present) { + map['is_ios_shared_album'] = Variable(isIosSharedAlbum.value); + } + if (marker_.present) { + map['marker'] = Variable(marker_.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } +} + +class LocalAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + LocalAlbumAssetEntity createAlias(String alias) { + return LocalAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + LocalAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data, + ) { + return LocalAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const LocalAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + LocalAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return LocalAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class UserMetadataEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserMetadataEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); + @override + List get $columns => [userId, key, value]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_metadata_entity'; + @override + Set get $primaryKey => {userId, key}; + @override + UserMetadataEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserMetadataEntityData( + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, + ); + } + + @override + UserMetadataEntity createAlias(String alias) { + return UserMetadataEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserMetadataEntityData extends DataClass + implements Insertable { + final String userId; + final int key; + final Uint8List value; + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['user_id'] = Variable(userId); + map['key'] = Variable(key); + map['value'] = Variable(value); + return map; + } + + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserMetadataEntityData( + userId: serializer.fromJson(json['userId']), + key: serializer.fromJson(json['key']), + value: serializer.fromJson(json['value']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'userId': serializer.toJson(userId), + 'key': serializer.toJson(key), + 'value': serializer.toJson(value), + }; + } + + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { + return UserMetadataEntityData( + userId: data.userId.present ? data.userId.value : this.userId, + key: data.key.present ? data.key.value : this.key, + value: data.value.present ? data.value.value : this.value, + ); + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityData(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(userId, key, $driftBlobEquality.hash(value)); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserMetadataEntityData && + other.userId == this.userId && + other.key == this.key && + $driftBlobEquality.equals(other.value, this.value)); +} + +class UserMetadataEntityCompanion + extends UpdateCompanion { + final Value userId; + final Value key; + final Value value; + const UserMetadataEntityCompanion({ + this.userId = const Value.absent(), + this.key = const Value.absent(), + this.value = const Value.absent(), + }); + UserMetadataEntityCompanion.insert({ + required String userId, + required int key, + required Uint8List value, + }) : userId = Value(userId), + key = Value(key), + value = Value(value); + static Insertable custom({ + Expression? userId, + Expression? key, + Expression? value, + }) { + return RawValuesInsertable({ + if (userId != null) 'user_id': userId, + if (key != null) 'key': key, + if (value != null) 'value': value, + }); + } + + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { + return UserMetadataEntityCompanion( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (key.present) { + map['key'] = Variable(key.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityCompanion(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } +} + +class PartnerEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PartnerEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [sharedById, sharedWithId, inTimeline]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'partner_entity'; + @override + Set get $primaryKey => {sharedById, sharedWithId}; + @override + PartnerEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PartnerEntityData( + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, + ); + } + + @override + PartnerEntity createAlias(String alias) { + return PartnerEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PartnerEntityData extends DataClass + implements Insertable { + final String sharedById; + final String sharedWithId; + final bool inTimeline; + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['shared_by_id'] = Variable(sharedById); + map['shared_with_id'] = Variable(sharedWithId); + map['in_timeline'] = Variable(inTimeline); + return map; + } + + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PartnerEntityData( + sharedById: serializer.fromJson(json['sharedById']), + sharedWithId: serializer.fromJson(json['sharedWithId']), + inTimeline: serializer.fromJson(json['inTimeline']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'sharedById': serializer.toJson(sharedById), + 'sharedWithId': serializer.toJson(sharedWithId), + 'inTimeline': serializer.toJson(inTimeline), + }; + } + + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { + return PartnerEntityData( + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, + ); + } + + @override + String toString() { + return (StringBuffer('PartnerEntityData(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PartnerEntityData && + other.sharedById == this.sharedById && + other.sharedWithId == this.sharedWithId && + other.inTimeline == this.inTimeline); +} + +class PartnerEntityCompanion extends UpdateCompanion { + final Value sharedById; + final Value sharedWithId; + final Value inTimeline; + const PartnerEntityCompanion({ + this.sharedById = const Value.absent(), + this.sharedWithId = const Value.absent(), + this.inTimeline = const Value.absent(), + }); + PartnerEntityCompanion.insert({ + required String sharedById, + required String sharedWithId, + this.inTimeline = const Value.absent(), + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); + static Insertable custom({ + Expression? sharedById, + Expression? sharedWithId, + Expression? inTimeline, + }) { + return RawValuesInsertable({ + if (sharedById != null) 'shared_by_id': sharedById, + if (sharedWithId != null) 'shared_with_id': sharedWithId, + if (inTimeline != null) 'in_timeline': inTimeline, + }); + } + + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { + return PartnerEntityCompanion( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sharedById.present) { + map['shared_by_id'] = Variable(sharedById.value); + } + if (sharedWithId.present) { + map['shared_with_id'] = Variable(sharedWithId.value); + } + if (inTimeline.present) { + map['in_timeline'] = Variable(inTimeline.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PartnerEntityCompanion(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } +} + +class RemoteExifEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteExifEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn city = GeneratedColumn( + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn state = GeneratedColumn( + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn country = GeneratedColumn( + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn make = GeneratedColumn( + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn model = GeneratedColumn( + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_exif_entity'; + @override + Set get $primaryKey => {assetId}; + @override + RemoteExifEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteExifEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), + ); + } + + @override + RemoteExifEntity createAlias(String alias) { + return RemoteExifEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteExifEntityData extends DataClass + implements Insertable { + final String assetId; + final String? city; + final String? state; + final String? country; + final DateTime? dateTimeOriginal; + final String? description; + final int? height; + final int? width; + final String? exposureTime; + final double? fNumber; + final int? fileSize; + final double? focalLength; + final double? latitude; + final double? longitude; + final int? iso; + final String? make; + final String? model; + final String? lens; + final String? orientation; + final String? timeZone; + final int? rating; + final String? projectionType; + const RemoteExifEntityData({ + required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || city != null) { + map['city'] = Variable(city); + } + if (!nullToAbsent || state != null) { + map['state'] = Variable(state); + } + if (!nullToAbsent || country != null) { + map['country'] = Variable(country); + } + if (!nullToAbsent || dateTimeOriginal != null) { + map['date_time_original'] = Variable(dateTimeOriginal); + } + if (!nullToAbsent || description != null) { + map['description'] = Variable(description); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || exposureTime != null) { + map['exposure_time'] = Variable(exposureTime); + } + if (!nullToAbsent || fNumber != null) { + map['f_number'] = Variable(fNumber); + } + if (!nullToAbsent || fileSize != null) { + map['file_size'] = Variable(fileSize); + } + if (!nullToAbsent || focalLength != null) { + map['focal_length'] = Variable(focalLength); + } + if (!nullToAbsent || latitude != null) { + map['latitude'] = Variable(latitude); + } + if (!nullToAbsent || longitude != null) { + map['longitude'] = Variable(longitude); + } + if (!nullToAbsent || iso != null) { + map['iso'] = Variable(iso); + } + if (!nullToAbsent || make != null) { + map['make'] = Variable(make); + } + if (!nullToAbsent || model != null) { + map['model'] = Variable(model); + } + if (!nullToAbsent || lens != null) { + map['lens'] = Variable(lens); + } + if (!nullToAbsent || orientation != null) { + map['orientation'] = Variable(orientation); + } + if (!nullToAbsent || timeZone != null) { + map['time_zone'] = Variable(timeZone); + } + if (!nullToAbsent || rating != null) { + map['rating'] = Variable(rating); + } + if (!nullToAbsent || projectionType != null) { + map['projection_type'] = Variable(projectionType); + } + return map; + } + + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteExifEntityData( + assetId: serializer.fromJson(json['assetId']), + city: serializer.fromJson(json['city']), + state: serializer.fromJson(json['state']), + country: serializer.fromJson(json['country']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), + description: serializer.fromJson(json['description']), + height: serializer.fromJson(json['height']), + width: serializer.fromJson(json['width']), + exposureTime: serializer.fromJson(json['exposureTime']), + fNumber: serializer.fromJson(json['fNumber']), + fileSize: serializer.fromJson(json['fileSize']), + focalLength: serializer.fromJson(json['focalLength']), + latitude: serializer.fromJson(json['latitude']), + longitude: serializer.fromJson(json['longitude']), + iso: serializer.fromJson(json['iso']), + make: serializer.fromJson(json['make']), + model: serializer.fromJson(json['model']), + lens: serializer.fromJson(json['lens']), + orientation: serializer.fromJson(json['orientation']), + timeZone: serializer.fromJson(json['timeZone']), + rating: serializer.fromJson(json['rating']), + projectionType: serializer.fromJson(json['projectionType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'city': serializer.toJson(city), + 'state': serializer.toJson(state), + 'country': serializer.toJson(country), + 'dateTimeOriginal': serializer.toJson(dateTimeOriginal), + 'description': serializer.toJson(description), + 'height': serializer.toJson(height), + 'width': serializer.toJson(width), + 'exposureTime': serializer.toJson(exposureTime), + 'fNumber': serializer.toJson(fNumber), + 'fileSize': serializer.toJson(fileSize), + 'focalLength': serializer.toJson(focalLength), + 'latitude': serializer.toJson(latitude), + 'longitude': serializer.toJson(longitude), + 'iso': serializer.toJson(iso), + 'make': serializer.toJson(make), + 'model': serializer.toJson(model), + 'lens': serializer.toJson(lens), + 'orientation': serializer.toJson(orientation), + 'timeZone': serializer.toJson(timeZone), + 'rating': serializer.toJson(rating), + 'projectionType': serializer.toJson(projectionType), + }; + } + + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); + RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { + return RemoteExifEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + city: data.city.present ? data.city.value : this.city, + state: data.state.present ? data.state.value : this.state, + country: data.country.present ? data.country.value : this.country, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: data.description.present + ? data.description.value + : this.description, + height: data.height.present ? data.height.value : this.height, + width: data.width.present ? data.width.value : this.width, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, + fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, + fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, + latitude: data.latitude.present ? data.latitude.value : this.latitude, + longitude: data.longitude.present ? data.longitude.value : this.longitude, + iso: data.iso.present ? data.iso.value : this.iso, + make: data.make.present ? data.make.value : this.make, + model: data.model.present ? data.model.value : this.model, + lens: data.lens.present ? data.lens.value : this.lens, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, + rating: data.rating.present ? data.rating.value : this.rating, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityData(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteExifEntityData && + other.assetId == this.assetId && + other.city == this.city && + other.state == this.state && + other.country == this.country && + other.dateTimeOriginal == this.dateTimeOriginal && + other.description == this.description && + other.height == this.height && + other.width == this.width && + other.exposureTime == this.exposureTime && + other.fNumber == this.fNumber && + other.fileSize == this.fileSize && + other.focalLength == this.focalLength && + other.latitude == this.latitude && + other.longitude == this.longitude && + other.iso == this.iso && + other.make == this.make && + other.model == this.model && + other.lens == this.lens && + other.orientation == this.orientation && + other.timeZone == this.timeZone && + other.rating == this.rating && + other.projectionType == this.projectionType); +} + +class RemoteExifEntityCompanion extends UpdateCompanion { + final Value assetId; + final Value city; + final Value state; + final Value country; + final Value dateTimeOriginal; + final Value description; + final Value height; + final Value width; + final Value exposureTime; + final Value fNumber; + final Value fileSize; + final Value focalLength; + final Value latitude; + final Value longitude; + final Value iso; + final Value make; + final Value model; + final Value lens; + final Value orientation; + final Value timeZone; + final Value rating; + final Value projectionType; + const RemoteExifEntityCompanion({ + this.assetId = const Value.absent(), + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }); + RemoteExifEntityCompanion.insert({ + required String assetId, + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }) : assetId = Value(assetId); + static Insertable custom({ + Expression? assetId, + Expression? city, + Expression? state, + Expression? country, + Expression? dateTimeOriginal, + Expression? description, + Expression? height, + Expression? width, + Expression? exposureTime, + Expression? fNumber, + Expression? fileSize, + Expression? focalLength, + Expression? latitude, + Expression? longitude, + Expression? iso, + Expression? make, + Expression? model, + Expression? lens, + Expression? orientation, + Expression? timeZone, + Expression? rating, + Expression? projectionType, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (city != null) 'city': city, + if (state != null) 'state': state, + if (country != null) 'country': country, + if (dateTimeOriginal != null) 'date_time_original': dateTimeOriginal, + if (description != null) 'description': description, + if (height != null) 'height': height, + if (width != null) 'width': width, + if (exposureTime != null) 'exposure_time': exposureTime, + if (fNumber != null) 'f_number': fNumber, + if (fileSize != null) 'file_size': fileSize, + if (focalLength != null) 'focal_length': focalLength, + if (latitude != null) 'latitude': latitude, + if (longitude != null) 'longitude': longitude, + if (iso != null) 'iso': iso, + if (make != null) 'make': make, + if (model != null) 'model': model, + if (lens != null) 'lens': lens, + if (orientation != null) 'orientation': orientation, + if (timeZone != null) 'time_zone': timeZone, + if (rating != null) 'rating': rating, + if (projectionType != null) 'projection_type': projectionType, + }); + } + + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { + return RemoteExifEntityCompanion( + assetId: assetId ?? this.assetId, + city: city ?? this.city, + state: state ?? this.state, + country: country ?? this.country, + dateTimeOriginal: dateTimeOriginal ?? this.dateTimeOriginal, + description: description ?? this.description, + height: height ?? this.height, + width: width ?? this.width, + exposureTime: exposureTime ?? this.exposureTime, + fNumber: fNumber ?? this.fNumber, + fileSize: fileSize ?? this.fileSize, + focalLength: focalLength ?? this.focalLength, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + iso: iso ?? this.iso, + make: make ?? this.make, + model: model ?? this.model, + lens: lens ?? this.lens, + orientation: orientation ?? this.orientation, + timeZone: timeZone ?? this.timeZone, + rating: rating ?? this.rating, + projectionType: projectionType ?? this.projectionType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (city.present) { + map['city'] = Variable(city.value); + } + if (state.present) { + map['state'] = Variable(state.value); + } + if (country.present) { + map['country'] = Variable(country.value); + } + if (dateTimeOriginal.present) { + map['date_time_original'] = Variable(dateTimeOriginal.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (exposureTime.present) { + map['exposure_time'] = Variable(exposureTime.value); + } + if (fNumber.present) { + map['f_number'] = Variable(fNumber.value); + } + if (fileSize.present) { + map['file_size'] = Variable(fileSize.value); + } + if (focalLength.present) { + map['focal_length'] = Variable(focalLength.value); + } + if (latitude.present) { + map['latitude'] = Variable(latitude.value); + } + if (longitude.present) { + map['longitude'] = Variable(longitude.value); + } + if (iso.present) { + map['iso'] = Variable(iso.value); + } + if (make.present) { + map['make'] = Variable(make.value); + } + if (model.present) { + map['model'] = Variable(model.value); + } + if (lens.present) { + map['lens'] = Variable(lens.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + if (timeZone.present) { + map['time_zone'] = Variable(timeZone.value); + } + if (rating.present) { + map['rating'] = Variable(rating.value); + } + if (projectionType.present) { + map['projection_type'] = Variable(projectionType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn order = GeneratedColumn( + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, + ); + } + + @override + RemoteAlbumEntity createAlias(String alias) { + return RemoteAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String description; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String? thumbnailAssetId; + final bool isActivityEnabled; + final int order; + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['description'] = Variable(description); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || thumbnailAssetId != null) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId); + } + map['is_activity_enabled'] = Variable(isActivityEnabled); + map['order'] = Variable(order); + return map; + } + + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + description: serializer.fromJson(json['description']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + thumbnailAssetId: serializer.fromJson(json['thumbnailAssetId']), + isActivityEnabled: serializer.fromJson(json['isActivityEnabled']), + order: serializer.fromJson(json['order']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'description': serializer.toJson(description), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'thumbnailAssetId': serializer.toJson(thumbnailAssetId), + 'isActivityEnabled': serializer.toJson(isActivityEnabled), + 'order': serializer.toJson(order), + }; + } + + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { + return RemoteAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + description: data.description.present + ? data.description.value + : this.description, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, + order: data.order.present ? data.order.value : this.order, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.description == this.description && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.thumbnailAssetId == this.thumbnailAssetId && + other.isActivityEnabled == this.isActivityEnabled && + other.order == this.order); +} + +class RemoteAlbumEntityCompanion + extends UpdateCompanion { + final Value id; + final Value name; + final Value description; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value thumbnailAssetId; + final Value isActivityEnabled; + final Value order; + const RemoteAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + this.order = const Value.absent(), + }); + RemoteAlbumEntityCompanion.insert({ + required String id, + required String name, + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + required int order, + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? description, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? thumbnailAssetId, + Expression? isActivityEnabled, + Expression? order, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (description != null) 'description': description, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (thumbnailAssetId != null) 'thumbnail_asset_id': thumbnailAssetId, + if (isActivityEnabled != null) 'is_activity_enabled': isActivityEnabled, + if (order != null) 'order': order, + }); + } + + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { + return RemoteAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (thumbnailAssetId.present) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId.value); + } + if (isActivityEnabled.present) { + map['is_activity_enabled'] = Variable(isActivityEnabled.value); + } + if (order.present) { + map['order'] = Variable(order.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + RemoteAlbumAssetEntity createAlias(String alias) { + return RemoteAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data, + ) { + return RemoteAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const RemoteAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + RemoteAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return RemoteAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumUserEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn role = GeneratedColumn( + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [albumId, userId, role]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_user_entity'; + @override + Set get $primaryKey => {albumId, userId}; + @override + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumUserEntityData( + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, + ); + } + + @override + RemoteAlbumUserEntity createAlias(String alias) { + return RemoteAlbumUserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { + final String albumId; + final String userId; + final int role; + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['album_id'] = Variable(albumId); + map['user_id'] = Variable(userId); + map['role'] = Variable(role); + return map; + } + + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumUserEntityData( + albumId: serializer.fromJson(json['albumId']), + userId: serializer.fromJson(json['userId']), + role: serializer.fromJson(json['role']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'albumId': serializer.toJson(albumId), + 'userId': serializer.toJson(userId), + 'role': serializer.toJson(role), + }; + } + + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data, + ) { + return RemoteAlbumUserEntityData( + albumId: data.albumId.present ? data.albumId.value : this.albumId, + userId: data.userId.present ? data.userId.value : this.userId, + role: data.role.present ? data.role.value : this.role, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityData(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(albumId, userId, role); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumUserEntityData && + other.albumId == this.albumId && + other.userId == this.userId && + other.role == this.role); +} + +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { + final Value albumId; + final Value userId; + final Value role; + const RemoteAlbumUserEntityCompanion({ + this.albumId = const Value.absent(), + this.userId = const Value.absent(), + this.role = const Value.absent(), + }); + RemoteAlbumUserEntityCompanion.insert({ + required String albumId, + required String userId, + required int role, + }) : albumId = Value(albumId), + userId = Value(userId), + role = Value(role); + static Insertable custom({ + Expression? albumId, + Expression? userId, + Expression? role, + }) { + return RawValuesInsertable({ + if (albumId != null) 'album_id': albumId, + if (userId != null) 'user_id': userId, + if (role != null) 'role': role, + }); + } + + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { + return RemoteAlbumUserEntityCompanion( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (role.present) { + map['role'] = Variable(role.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityCompanion(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } +} + +class MemoryEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn data = GeneratedColumn( + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_entity'; + @override + Set get $primaryKey => {id}; + @override + MemoryEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), + ); + } + + @override + MemoryEntity createAlias(String alias) { + return MemoryEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final DateTime? deletedAt; + final String ownerId; + final int type; + final String data; + final bool isSaved; + final DateTime memoryAt; + final DateTime? seenAt; + final DateTime? showAt; + final DateTime? hideAt; + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + map['owner_id'] = Variable(ownerId); + map['type'] = Variable(type); + map['data'] = Variable(data); + map['is_saved'] = Variable(isSaved); + map['memory_at'] = Variable(memoryAt); + if (!nullToAbsent || seenAt != null) { + map['seen_at'] = Variable(seenAt); + } + if (!nullToAbsent || showAt != null) { + map['show_at'] = Variable(showAt); + } + if (!nullToAbsent || hideAt != null) { + map['hide_at'] = Variable(hideAt); + } + return map; + } + + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + deletedAt: serializer.fromJson(json['deletedAt']), + ownerId: serializer.fromJson(json['ownerId']), + type: serializer.fromJson(json['type']), + data: serializer.fromJson(json['data']), + isSaved: serializer.fromJson(json['isSaved']), + memoryAt: serializer.fromJson(json['memoryAt']), + seenAt: serializer.fromJson(json['seenAt']), + showAt: serializer.fromJson(json['showAt']), + hideAt: serializer.fromJson(json['hideAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'deletedAt': serializer.toJson(deletedAt), + 'ownerId': serializer.toJson(ownerId), + 'type': serializer.toJson(type), + 'data': serializer.toJson(data), + 'isSaved': serializer.toJson(isSaved), + 'memoryAt': serializer.toJson(memoryAt), + 'seenAt': serializer.toJson(seenAt), + 'showAt': serializer.toJson(showAt), + 'hideAt': serializer.toJson(hideAt), + }; + } + + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); + MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { + return MemoryEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + type: data.type.present ? data.type.value : this.type, + data: data.data.present ? data.data.value : this.data, + isSaved: data.isSaved.present ? data.isSaved.value : this.isSaved, + memoryAt: data.memoryAt.present ? data.memoryAt.value : this.memoryAt, + seenAt: data.seenAt.present ? data.seenAt.value : this.seenAt, + showAt: data.showAt.present ? data.showAt.value : this.showAt, + hideAt: data.hideAt.present ? data.hideAt.value : this.hideAt, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.deletedAt == this.deletedAt && + other.ownerId == this.ownerId && + other.type == this.type && + other.data == this.data && + other.isSaved == this.isSaved && + other.memoryAt == this.memoryAt && + other.seenAt == this.seenAt && + other.showAt == this.showAt && + other.hideAt == this.hideAt); +} + +class MemoryEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value deletedAt; + final Value ownerId; + final Value type; + final Value data; + final Value isSaved; + final Value memoryAt; + final Value seenAt; + final Value showAt; + final Value hideAt; + const MemoryEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.type = const Value.absent(), + this.data = const Value.absent(), + this.isSaved = const Value.absent(), + this.memoryAt = const Value.absent(), + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }); + MemoryEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + required String ownerId, + required int type, + required String data, + this.isSaved = const Value.absent(), + required DateTime memoryAt, + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? deletedAt, + Expression? ownerId, + Expression? type, + Expression? data, + Expression? isSaved, + Expression? memoryAt, + Expression? seenAt, + Expression? showAt, + Expression? hideAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (deletedAt != null) 'deleted_at': deletedAt, + if (ownerId != null) 'owner_id': ownerId, + if (type != null) 'type': type, + if (data != null) 'data': data, + if (isSaved != null) 'is_saved': isSaved, + if (memoryAt != null) 'memory_at': memoryAt, + if (seenAt != null) 'seen_at': seenAt, + if (showAt != null) 'show_at': showAt, + if (hideAt != null) 'hide_at': hideAt, + }); + } + + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { + return MemoryEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt ?? this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt ?? this.seenAt, + showAt: showAt ?? this.showAt, + hideAt: hideAt ?? this.hideAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (data.present) { + map['data'] = Variable(data.value); + } + if (isSaved.present) { + map['is_saved'] = Variable(isSaved.value); + } + if (memoryAt.present) { + map['memory_at'] = Variable(memoryAt.value); + } + if (seenAt.present) { + map['seen_at'] = Variable(seenAt.value); + } + if (showAt.present) { + map['show_at'] = Variable(showAt.value); + } + if (hideAt.present) { + map['hide_at'] = Variable(hideAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } +} + +class MemoryAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, memoryId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_asset_entity'; + @override + Set get $primaryKey => {assetId, memoryId}; + @override + MemoryAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, + ); + } + + @override + MemoryAssetEntity createAlias(String alias) { + return MemoryAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String memoryId; + const MemoryAssetEntityData({required this.assetId, required this.memoryId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['memory_id'] = Variable(memoryId); + return map; + } + + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + memoryId: serializer.fromJson(json['memoryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'memoryId': serializer.toJson(memoryId), + }; + } + + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + MemoryAssetEntityData copyWithCompanion(MemoryAssetEntityCompanion data) { + return MemoryAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + memoryId: data.memoryId.present ? data.memoryId.value : this.memoryId, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, memoryId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); +} + +class MemoryAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value memoryId; + const MemoryAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.memoryId = const Value.absent(), + }); + MemoryAssetEntityCompanion.insert({ + required String assetId, + required String memoryId, + }) : assetId = Value(assetId), + memoryId = Value(memoryId); + static Insertable custom({ + Expression? assetId, + Expression? memoryId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (memoryId != null) 'memory_id': memoryId, + }); + } + + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { + return MemoryAssetEntityCompanion( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (memoryId.present) { + map['memory_id'] = Variable(memoryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } +} + +class PersonEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PersonEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'person_entity'; + @override + Set get $primaryKey => {id}; + @override + PersonEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PersonEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), + ); + } + + @override + PersonEntity createAlias(String alias) { + return PersonEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PersonEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['name'] = Variable(name); + if (!nullToAbsent || faceAssetId != null) { + map['face_asset_id'] = Variable(faceAssetId); + } + map['is_favorite'] = Variable(isFavorite); + map['is_hidden'] = Variable(isHidden); + if (!nullToAbsent || color != null) { + map['color'] = Variable(color); + } + if (!nullToAbsent || birthDate != null) { + map['birth_date'] = Variable(birthDate); + } + return map; + } + + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PersonEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + name: serializer.fromJson(json['name']), + faceAssetId: serializer.fromJson(json['faceAssetId']), + isFavorite: serializer.fromJson(json['isFavorite']), + isHidden: serializer.fromJson(json['isHidden']), + color: serializer.fromJson(json['color']), + birthDate: serializer.fromJson(json['birthDate']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'name': serializer.toJson(name), + 'faceAssetId': serializer.toJson(faceAssetId), + 'isFavorite': serializer.toJson(isFavorite), + 'isHidden': serializer.toJson(isHidden), + 'color': serializer.toJson(color), + 'birthDate': serializer.toJson(birthDate), + }; + } + + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); + PersonEntityData copyWithCompanion(PersonEntityCompanion data) { + return PersonEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + name: data.name.present ? data.name.value : this.name, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + color: data.color.present ? data.color.value : this.color, + birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, + ); + } + + @override + String toString() { + return (StringBuffer('PersonEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PersonEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.name == this.name && + other.faceAssetId == this.faceAssetId && + other.isFavorite == this.isFavorite && + other.isHidden == this.isHidden && + other.color == this.color && + other.birthDate == this.birthDate); +} + +class PersonEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value name; + final Value faceAssetId; + final Value isFavorite; + final Value isHidden; + final Value color; + final Value birthDate; + const PersonEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.name = const Value.absent(), + this.faceAssetId = const Value.absent(), + this.isFavorite = const Value.absent(), + this.isHidden = const Value.absent(), + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }); + PersonEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String name, + this.faceAssetId = const Value.absent(), + required bool isFavorite, + required bool isHidden, + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? name, + Expression? faceAssetId, + Expression? isFavorite, + Expression? isHidden, + Expression? color, + Expression? birthDate, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (name != null) 'name': name, + if (faceAssetId != null) 'face_asset_id': faceAssetId, + if (isFavorite != null) 'is_favorite': isFavorite, + if (isHidden != null) 'is_hidden': isHidden, + if (color != null) 'color': color, + if (birthDate != null) 'birth_date': birthDate, + }); + } + + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { + return PersonEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (faceAssetId.present) { + map['face_asset_id'] = Variable(faceAssetId.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (isHidden.present) { + map['is_hidden'] = Variable(isHidden.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (birthDate.present) { + map['birth_date'] = Variable(birthDate.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PersonEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } +} + +class AssetFaceEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + AssetFaceEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn personId = GeneratedColumn( + 'person_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn imageWidth = GeneratedColumn( + 'image_width', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn imageHeight = GeneratedColumn( + 'image_height', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX1 = GeneratedColumn( + 'bounding_box_x1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY1 = GeneratedColumn( + 'bounding_box_y1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX2 = GeneratedColumn( + 'bounding_box_x2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY2 = GeneratedColumn( + 'bounding_box_y2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn sourceType = GeneratedColumn( + 'source_type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'asset_face_entity'; + @override + Set get $primaryKey => {id}; + @override + AssetFaceEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return AssetFaceEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + personId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}person_id'], + ), + imageWidth: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_width'], + )!, + imageHeight: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_height'], + )!, + boundingBoxX1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x1'], + )!, + boundingBoxY1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y1'], + )!, + boundingBoxX2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x2'], + )!, + boundingBoxY2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y2'], + )!, + sourceType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}source_type'], + )!, + ); + } + + @override + AssetFaceEntity createAlias(String alias) { + return AssetFaceEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AssetFaceEntityData extends DataClass + implements Insertable { + final String id; + final String assetId; + final String? personId; + final int imageWidth; + final int imageHeight; + final int boundingBoxX1; + final int boundingBoxY1; + final int boundingBoxX2; + final int boundingBoxY2; + final String sourceType; + const AssetFaceEntityData({ + required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || personId != null) { + map['person_id'] = Variable(personId); + } + map['image_width'] = Variable(imageWidth); + map['image_height'] = Variable(imageHeight); + map['bounding_box_x1'] = Variable(boundingBoxX1); + map['bounding_box_y1'] = Variable(boundingBoxY1); + map['bounding_box_x2'] = Variable(boundingBoxX2); + map['bounding_box_y2'] = Variable(boundingBoxY2); + map['source_type'] = Variable(sourceType); + return map; + } + + factory AssetFaceEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return AssetFaceEntityData( + id: serializer.fromJson(json['id']), + assetId: serializer.fromJson(json['assetId']), + personId: serializer.fromJson(json['personId']), + imageWidth: serializer.fromJson(json['imageWidth']), + imageHeight: serializer.fromJson(json['imageHeight']), + boundingBoxX1: serializer.fromJson(json['boundingBoxX1']), + boundingBoxY1: serializer.fromJson(json['boundingBoxY1']), + boundingBoxX2: serializer.fromJson(json['boundingBoxX2']), + boundingBoxY2: serializer.fromJson(json['boundingBoxY2']), + sourceType: serializer.fromJson(json['sourceType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'assetId': serializer.toJson(assetId), + 'personId': serializer.toJson(personId), + 'imageWidth': serializer.toJson(imageWidth), + 'imageHeight': serializer.toJson(imageHeight), + 'boundingBoxX1': serializer.toJson(boundingBoxX1), + 'boundingBoxY1': serializer.toJson(boundingBoxY1), + 'boundingBoxX2': serializer.toJson(boundingBoxX2), + 'boundingBoxY2': serializer.toJson(boundingBoxY2), + 'sourceType': serializer.toJson(sourceType), + }; + } + + AssetFaceEntityData copyWith({ + String? id, + String? assetId, + Value personId = const Value.absent(), + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType, + }) => AssetFaceEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId.present ? personId.value : this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + AssetFaceEntityData copyWithCompanion(AssetFaceEntityCompanion data) { + return AssetFaceEntityData( + id: data.id.present ? data.id.value : this.id, + assetId: data.assetId.present ? data.assetId.value : this.assetId, + personId: data.personId.present ? data.personId.value : this.personId, + imageWidth: data.imageWidth.present + ? data.imageWidth.value + : this.imageWidth, + imageHeight: data.imageHeight.present + ? data.imageHeight.value + : this.imageHeight, + boundingBoxX1: data.boundingBoxX1.present + ? data.boundingBoxX1.value + : this.boundingBoxX1, + boundingBoxY1: data.boundingBoxY1.present + ? data.boundingBoxY1.value + : this.boundingBoxY1, + boundingBoxX2: data.boundingBoxX2.present + ? data.boundingBoxX2.value + : this.boundingBoxX2, + boundingBoxY2: data.boundingBoxY2.present + ? data.boundingBoxY2.value + : this.boundingBoxY2, + sourceType: data.sourceType.present + ? data.sourceType.value + : this.sourceType, + ); + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityData(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is AssetFaceEntityData && + other.id == this.id && + other.assetId == this.assetId && + other.personId == this.personId && + other.imageWidth == this.imageWidth && + other.imageHeight == this.imageHeight && + other.boundingBoxX1 == this.boundingBoxX1 && + other.boundingBoxY1 == this.boundingBoxY1 && + other.boundingBoxX2 == this.boundingBoxX2 && + other.boundingBoxY2 == this.boundingBoxY2 && + other.sourceType == this.sourceType); +} + +class AssetFaceEntityCompanion extends UpdateCompanion { + final Value id; + final Value assetId; + final Value personId; + final Value imageWidth; + final Value imageHeight; + final Value boundingBoxX1; + final Value boundingBoxY1; + final Value boundingBoxX2; + final Value boundingBoxY2; + final Value sourceType; + const AssetFaceEntityCompanion({ + this.id = const Value.absent(), + this.assetId = const Value.absent(), + this.personId = const Value.absent(), + this.imageWidth = const Value.absent(), + this.imageHeight = const Value.absent(), + this.boundingBoxX1 = const Value.absent(), + this.boundingBoxY1 = const Value.absent(), + this.boundingBoxX2 = const Value.absent(), + this.boundingBoxY2 = const Value.absent(), + this.sourceType = const Value.absent(), + }); + AssetFaceEntityCompanion.insert({ + required String id, + required String assetId, + this.personId = const Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) : id = Value(id), + assetId = Value(assetId), + imageWidth = Value(imageWidth), + imageHeight = Value(imageHeight), + boundingBoxX1 = Value(boundingBoxX1), + boundingBoxY1 = Value(boundingBoxY1), + boundingBoxX2 = Value(boundingBoxX2), + boundingBoxY2 = Value(boundingBoxY2), + sourceType = Value(sourceType); + static Insertable custom({ + Expression? id, + Expression? assetId, + Expression? personId, + Expression? imageWidth, + Expression? imageHeight, + Expression? boundingBoxX1, + Expression? boundingBoxY1, + Expression? boundingBoxX2, + Expression? boundingBoxY2, + Expression? sourceType, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (assetId != null) 'asset_id': assetId, + if (personId != null) 'person_id': personId, + if (imageWidth != null) 'image_width': imageWidth, + if (imageHeight != null) 'image_height': imageHeight, + if (boundingBoxX1 != null) 'bounding_box_x1': boundingBoxX1, + if (boundingBoxY1 != null) 'bounding_box_y1': boundingBoxY1, + if (boundingBoxX2 != null) 'bounding_box_x2': boundingBoxX2, + if (boundingBoxY2 != null) 'bounding_box_y2': boundingBoxY2, + if (sourceType != null) 'source_type': sourceType, + }); + } + + AssetFaceEntityCompanion copyWith({ + Value? id, + Value? assetId, + Value? personId, + Value? imageWidth, + Value? imageHeight, + Value? boundingBoxX1, + Value? boundingBoxY1, + Value? boundingBoxX2, + Value? boundingBoxY2, + Value? sourceType, + }) { + return AssetFaceEntityCompanion( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId ?? this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (personId.present) { + map['person_id'] = Variable(personId.value); + } + if (imageWidth.present) { + map['image_width'] = Variable(imageWidth.value); + } + if (imageHeight.present) { + map['image_height'] = Variable(imageHeight.value); + } + if (boundingBoxX1.present) { + map['bounding_box_x1'] = Variable(boundingBoxX1.value); + } + if (boundingBoxY1.present) { + map['bounding_box_y1'] = Variable(boundingBoxY1.value); + } + if (boundingBoxX2.present) { + map['bounding_box_x2'] = Variable(boundingBoxX2.value); + } + if (boundingBoxY2.present) { + map['bounding_box_y2'] = Variable(boundingBoxY2.value); + } + if (sourceType.present) { + map['source_type'] = Variable(sourceType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityCompanion(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV7 extends GeneratedDatabase { + DatabaseAtV7(QueryExecutor e) : super(e); + late final UserEntity userEntity = UserEntity(this); + late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); + late final StackEntity stackEntity = StackEntity(this); + late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); + late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + late final Index idxRemoteAssetOwnerChecksum = Index( + 'idx_remote_asset_owner_checksum', + 'CREATE INDEX idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', + ); + late final Index uQRemoteAssetsOwnerChecksum = Index( + 'UQ_remote_assets_owner_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)', + ); + late final Index uQRemoteAssetsOwnerLibraryChecksum = Index( + 'UQ_remote_assets_owner_library_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); + late final PartnerEntity partnerEntity = PartnerEntity(this); + late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); + late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); + late final MemoryEntity memoryEntity = MemoryEntity(this); + late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); + late final PersonEntity personEntity = PersonEntity(this); + late final AssetFaceEntity assetFaceEntity = AssetFaceEntity(this); + late final Index idxLatLng = Index( + 'idx_lat_lng', + 'CREATE INDEX idx_lat_lng ON remote_exif_entity (latitude, longitude)', + ); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + idxRemoteAssetOwnerChecksum, + uQRemoteAssetsOwnerChecksum, + uQRemoteAssetsOwnerLibraryChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + idxLatLng, + ]; + @override + int get schemaVersion => 7; + @override + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); +} From 89598cf0be6c7a3ef7a897f8f2e35729511db5fa Mon Sep 17 00:00:00 2001 From: Thomas <9749173+uhthomas@users.noreply.github.com> Date: Wed, 6 Aug 2025 16:12:52 +0100 Subject: [PATCH 190/748] chore(web): remove arbitrary search result limit (#20719) The search results page can become unstable with large amounts of assets, and has therefore been limited to displaying just 5000 assets. This limit is arbitrary and may be too restrictive. --- .../search/[[photos=photos]]/[[assetId=id]]/+page.svelte | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte index 48ead6e6b4..e7798b1ac7 100644 --- a/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -50,7 +50,6 @@ import { tick } from 'svelte'; import { t } from 'svelte-i18n'; - const MAX_ASSET_COUNT = 5000; let { isViewing: showAssetViewer } = assetViewingStore; const viewport: Viewport = $state({ width: 0, height: 0 }); @@ -149,10 +148,7 @@ // eslint-disable-next-line svelte/valid-prop-names-in-kit-pages export const loadNextPage = async (force?: boolean) => { - if (!nextPage || searchResultAssets.length >= MAX_ASSET_COUNT) { - return; - } - if (isLoading && !force) { + if (!nextPage || (isLoading && !force)) { return; } isLoading = true; From f2067221c58e68e7d0a8cf31b659f0120472d6dc Mon Sep 17 00:00:00 2001 From: Lauritz Tieste <84938977+Lauritz-Tieste@users.noreply.github.com> Date: Wed, 6 Aug 2025 17:36:07 +0200 Subject: [PATCH 191/748] fix: disk info is cleared when profile picture is uploaded (#20411) fix: update disk info on user profile image upload --- .../widgets/common/app_bar_dialog/app_bar_profile_info.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart b/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart index d12ee7b0d1..b1f5b192dd 100644 --- a/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart +++ b/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart @@ -4,6 +4,7 @@ import 'package:image_picker/image_picker.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/providers/auth.provider.dart'; +import 'package:immich_mobile/providers/backup/backup.provider.dart'; import 'package:immich_mobile/providers/upload_profile_image.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart'; @@ -48,6 +49,8 @@ class AppBarProfileInfoBox extends HookConsumerWidget { if (user != null) { ref.read(currentUserProvider.notifier).refresh(); } + + ref.read(backupProvider.notifier).updateDiskInfo(); } } } From 3cd7f5ab904d2aa294af44b642456c73ee33b479 Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Wed, 6 Aug 2025 10:49:29 -0500 Subject: [PATCH 192/748] feat: use sqlite for logging (#20414) * feat: use drift for logging * fix: tests * feat: use the truncate limit from constants.ts as default * chore: move setupAll to top level and restructure * chore: code review changes * fix: inherits * feat: raise log line limit to 2000 * limit getAll to 250 lines * delete DLog and make LogRepository not a singleton * fix: drift build settings and `make migration` * fix: tests * remove sensitive log --------- Co-authored-by: Alex --- mobile/build.yaml | 1 + mobile/lib/constants/constants.dart | 2 +- mobile/lib/domain/services/hash.service.dart | 3 - .../domain/services/local_sync.service.dart | 10 +- mobile/lib/domain/services/log.service.dart | 18 +- .../domain/services/sync_stream.service.dart | 2 - .../infrastructure/entities/log.entity.dart | 68 +- .../entities/log.entity.drift.dart | 697 +++++++++ .../infrastructure/entities/log.entity.g.dart | 1322 ----------------- .../repositories/log.repository.dart | 71 +- .../repositories/logger_db.repository.dart | 27 + .../logger_db.repository.drift.dart | 27 + .../repositories/sync_api.repository.dart | 2 - .../lib/pages/common/app_log_detail.page.dart | 6 +- .../lib/pages/common/splash_screen.page.dart | 1 - .../presentation/pages/dev/dev_logger.dart | 68 - .../pages/dev/feat_in_development.page.dart | 62 +- mobile/lib/utils/bootstrap.dart | 9 +- mobile/lib/utils/isolate.dart | 2 +- .../domain/services/log_service_test.dart | 2 +- .../test/infrastructure/repository.mock.dart | 2 +- .../modules/shared/sync_service_test.dart | 38 +- mobile/test/test_utils.dart | 2 - 23 files changed, 879 insertions(+), 1563 deletions(-) create mode 100644 mobile/lib/infrastructure/entities/log.entity.drift.dart delete mode 100644 mobile/lib/infrastructure/entities/log.entity.g.dart create mode 100644 mobile/lib/infrastructure/repositories/logger_db.repository.dart create mode 100644 mobile/lib/infrastructure/repositories/logger_db.repository.drift.dart delete mode 100644 mobile/lib/presentation/pages/dev/dev_logger.dart diff --git a/mobile/build.yaml b/mobile/build.yaml index 76cc0a9988..cb718d9d3e 100644 --- a/mobile/build.yaml +++ b/mobile/build.yaml @@ -19,6 +19,7 @@ targets: - lib/infrastructure/entities/*.dart - lib/infrastructure/entities/*.drift - lib/infrastructure/repositories/db.repository.dart + - lib/infrastructure/repositories/logger_db.repository.dart drift_dev:modular: enabled: true options: *drift_options diff --git a/mobile/lib/constants/constants.dart b/mobile/lib/constants/constants.dart index d984b468d7..616a306d94 100644 --- a/mobile/lib/constants/constants.dart +++ b/mobile/lib/constants/constants.dart @@ -3,7 +3,7 @@ const double downloadCompleted = -1; const double downloadFailed = -2; // Number of log entries to retain on app start -const int kLogTruncateLimit = 250; +const int kLogTruncateLimit = 2000; // Sync const int kSyncEventBatchSize = 5000; diff --git a/mobile/lib/domain/services/hash.service.dart b/mobile/lib/domain/services/hash.service.dart index 36a4b9efba..a8eea2c25e 100644 --- a/mobile/lib/domain/services/hash.service.dart +++ b/mobile/lib/domain/services/hash.service.dart @@ -6,7 +6,6 @@ import 'package:immich_mobile/infrastructure/repositories/local_album.repository import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; import 'package:immich_mobile/platform/native_sync_api.g.dart'; -import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart'; import 'package:logging/logging.dart'; class HashService { @@ -46,7 +45,6 @@ class HashService { stopwatch.stop(); _log.info("Hashing took - ${stopwatch.elapsedMilliseconds}ms"); - DLog.log("Hashing took - ${stopwatch.elapsedMilliseconds}ms"); } /// Processes a list of [LocalAsset]s, storing their hash and updating the assets in the DB @@ -101,7 +99,6 @@ class HashService { } _log.fine("Hashed ${hashed.length}/${toHash.length} assets"); - DLog.log("Hashed ${hashed.length}/${toHash.length} assets"); await _localAssetRepository.updateHashes(hashed); await _storageRepository.clearCache(); diff --git a/mobile/lib/domain/services/local_sync.service.dart b/mobile/lib/domain/services/local_sync.service.dart index 13ebecfd46..119954cb47 100644 --- a/mobile/lib/domain/services/local_sync.service.dart +++ b/mobile/lib/domain/services/local_sync.service.dart @@ -6,7 +6,6 @@ import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart'; import 'package:immich_mobile/platform/native_sync_api.g.dart'; -import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart'; import 'package:immich_mobile/utils/diff.dart'; import 'package:logging/logging.dart'; import 'package:platform/platform.dart'; @@ -30,19 +29,17 @@ class LocalSyncService { try { if (full || await _nativeSyncApi.shouldFullSync()) { _log.fine("Full sync request from ${full ? "user" : "native"}"); - DLog.log("Full sync request from ${full ? "user" : "native"}"); return await fullSync(); } final delta = await _nativeSyncApi.getMediaChanges(); if (!delta.hasChanges) { _log.fine("No media changes detected. Skipping sync"); - DLog.log("No media changes detected. Skipping sync"); return; } - DLog.log("Delta updated: ${delta.updates.length}"); - DLog.log("Delta deleted: ${delta.deletes.length}"); + _log.fine("Delta updated: ${delta.updates.length}"); + _log.fine("Delta deleted: ${delta.deletes.length}"); final deviceAlbums = await _nativeSyncApi.getAlbums(); await _localAlbumRepository.updateAll(deviceAlbums.toLocalAlbums()); @@ -83,7 +80,6 @@ class LocalSyncService { } finally { stopwatch.stop(); _log.info("Device sync took - ${stopwatch.elapsedMilliseconds}ms"); - DLog.log("Device sync took - ${stopwatch.elapsedMilliseconds}ms"); } } @@ -106,7 +102,6 @@ class LocalSyncService { await _nativeSyncApi.checkpointSync(); stopwatch.stop(); _log.info("Full device sync took - ${stopwatch.elapsedMilliseconds}ms"); - DLog.log("Full device sync took - ${stopwatch.elapsedMilliseconds}ms"); } catch (e, s) { _log.severe("Error performing full device sync", e, s); } @@ -150,7 +145,6 @@ class LocalSyncService { // Faster path - only new assets added if (await checkAddition(dbAlbum, deviceAlbum)) { _log.fine("Fast synced device album ${dbAlbum.name}"); - DLog.log("Fast synced device album ${dbAlbum.name}"); return true; } diff --git a/mobile/lib/domain/services/log.service.dart b/mobile/lib/domain/services/log.service.dart index ff72ec5502..98cb24d9cc 100644 --- a/mobile/lib/domain/services/log.service.dart +++ b/mobile/lib/domain/services/log.service.dart @@ -14,7 +14,7 @@ import 'package:logging/logging.dart'; /// writes them to a persistent [ILogRepository], and manages log levels /// via [IStoreRepository] class LogService { - final IsarLogRepository _logRepository; + final LogRepository _logRepository; final IsarStoreRepository _storeRepository; final List _msgBuffer = []; @@ -37,7 +37,7 @@ class LogService { } static Future init({ - required IsarLogRepository logRepository, + required LogRepository logRepository, required IsarStoreRepository storeRepository, bool shouldBuffer = true, }) async { @@ -50,7 +50,7 @@ class LogService { } static Future create({ - required IsarLogRepository logRepository, + required LogRepository logRepository, required IsarStoreRepository storeRepository, bool shouldBuffer = true, }) async { @@ -85,7 +85,7 @@ class LogService { if (_shouldBuffer) { _msgBuffer.add(record); - _flushTimer ??= Timer(const Duration(seconds: 5), () => unawaited(flushBuffer())); + _flushTimer ??= Timer(const Duration(seconds: 5), () => unawaited(_flushBuffer())); } else { unawaited(_logRepository.insert(record)); } @@ -108,20 +108,18 @@ class LogService { await _logRepository.deleteAll(); } - void flush() { + Future flush() { _flushTimer?.cancel(); - // TODO: Rename enable this after moving to sqlite - #16504 - // await _flushBufferToDatabase(); + return _flushBuffer(); } Future dispose() { _flushTimer?.cancel(); _logSubscription.cancel(); - return flushBuffer(); + return _flushBuffer(); } - // TOOD: Move this to private once Isar is removed - Future flushBuffer() async { + Future _flushBuffer() async { _flushTimer = null; final buffer = [..._msgBuffer]; _msgBuffer.clear(); diff --git a/mobile/lib/domain/services/sync_stream.service.dart b/mobile/lib/domain/services/sync_stream.service.dart index 7c25fbb345..5625635e49 100644 --- a/mobile/lib/domain/services/sync_stream.service.dart +++ b/mobile/lib/domain/services/sync_stream.service.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:immich_mobile/domain/models/sync_event.model.dart'; import 'package:immich_mobile/infrastructure/repositories/sync_api.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/sync_stream.repository.dart'; -import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; @@ -26,7 +25,6 @@ class SyncStreamService { Future sync() { _logger.info("Remote sync request for user"); - DLog.log("Remote sync request for user"); // Start the sync stream and handle events return _syncApiRepository.streamChanges(_handleEvents); } diff --git a/mobile/lib/infrastructure/entities/log.entity.dart b/mobile/lib/infrastructure/entities/log.entity.dart index 6a38924e24..e578459827 100644 --- a/mobile/lib/infrastructure/entities/log.entity.dart +++ b/mobile/lib/infrastructure/entities/log.entity.dart @@ -1,47 +1,29 @@ -import 'package:immich_mobile/domain/models/log.model.dart'; -import 'package:isar/isar.dart'; +import 'package:drift/drift.dart'; +import 'package:immich_mobile/infrastructure/entities/log.entity.drift.dart'; +import 'package:immich_mobile/domain/models/log.model.dart' as domain; -part 'log.entity.g.dart'; +class LogMessageEntity extends Table { + const LogMessageEntity(); -@Collection(inheritance: false) -class LoggerMessage { - final Id id = Isar.autoIncrement; - final String message; - final String? details; - @Enumerated(EnumType.ordinal) - final LogLevel level; - final DateTime createdAt; - final String? context1; - final String? context2; + @override + String get tableName => 'logger_messages'; - const LoggerMessage({ - required this.message, - required this.details, - this.level = LogLevel.info, - required this.createdAt, - required this.context1, - required this.context2, - }); - - LogMessage toDto() { - return LogMessage( - message: message, - level: level, - createdAt: createdAt, - logger: context1, - error: details, - stack: context2, - ); - } - - static LoggerMessage fromDto(LogMessage log) { - return LoggerMessage( - message: log.message, - details: log.error, - level: log.level, - createdAt: log.createdAt, - context1: log.logger, - context2: log.stack, - ); - } + IntColumn get id => integer().autoIncrement()(); + TextColumn get message => text()(); + TextColumn get details => text().nullable()(); + IntColumn get level => intEnum()(); + DateTimeColumn get createdAt => dateTime()(); + TextColumn get logger => text().nullable()(); + TextColumn get stack => text().nullable()(); +} + +extension LogMessageEntityDataDomainEx on LogMessageEntityData { + domain.LogMessage toDto() => domain.LogMessage( + message: message, + level: level, + createdAt: createdAt, + logger: logger, + error: details, + stack: stack, + ); } diff --git a/mobile/lib/infrastructure/entities/log.entity.drift.dart b/mobile/lib/infrastructure/entities/log.entity.drift.dart new file mode 100644 index 0000000000..d04cd5b7a2 --- /dev/null +++ b/mobile/lib/infrastructure/entities/log.entity.drift.dart @@ -0,0 +1,697 @@ +// dart format width=80 +// ignore_for_file: type=lint +import 'package:drift/drift.dart' as i0; +import 'package:immich_mobile/infrastructure/entities/log.entity.drift.dart' + as i1; +import 'package:immich_mobile/domain/models/log.model.dart' as i2; +import 'package:immich_mobile/infrastructure/entities/log.entity.dart' as i3; + +typedef $$LogMessageEntityTableCreateCompanionBuilder = + i1.LogMessageEntityCompanion Function({ + i0.Value id, + required String message, + i0.Value details, + required i2.LogLevel level, + required DateTime createdAt, + i0.Value logger, + i0.Value stack, + }); +typedef $$LogMessageEntityTableUpdateCompanionBuilder = + i1.LogMessageEntityCompanion Function({ + i0.Value id, + i0.Value message, + i0.Value details, + i0.Value level, + i0.Value createdAt, + i0.Value logger, + i0.Value stack, + }); + +class $$LogMessageEntityTableFilterComposer + extends i0.Composer { + $$LogMessageEntityTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnFilters get id => $composableBuilder( + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get message => $composableBuilder( + column: $table.message, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get details => $composableBuilder( + column: $table.details, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnWithTypeConverterFilters get level => + $composableBuilder( + column: $table.level, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); + + i0.ColumnFilters get createdAt => $composableBuilder( + column: $table.createdAt, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get logger => $composableBuilder( + column: $table.logger, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get stack => $composableBuilder( + column: $table.stack, + builder: (column) => i0.ColumnFilters(column), + ); +} + +class $$LogMessageEntityTableOrderingComposer + extends i0.Composer { + $$LogMessageEntityTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnOrderings get id => $composableBuilder( + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get message => $composableBuilder( + column: $table.message, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get details => $composableBuilder( + column: $table.details, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get level => $composableBuilder( + column: $table.level, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get createdAt => $composableBuilder( + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get logger => $composableBuilder( + column: $table.logger, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get stack => $composableBuilder( + column: $table.stack, + builder: (column) => i0.ColumnOrderings(column), + ); +} + +class $$LogMessageEntityTableAnnotationComposer + extends i0.Composer { + $$LogMessageEntityTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + i0.GeneratedColumn get message => + $composableBuilder(column: $table.message, builder: (column) => column); + + i0.GeneratedColumn get details => + $composableBuilder(column: $table.details, builder: (column) => column); + + i0.GeneratedColumnWithTypeConverter get level => + $composableBuilder(column: $table.level, builder: (column) => column); + + i0.GeneratedColumn get createdAt => + $composableBuilder(column: $table.createdAt, builder: (column) => column); + + i0.GeneratedColumn get logger => + $composableBuilder(column: $table.logger, builder: (column) => column); + + i0.GeneratedColumn get stack => + $composableBuilder(column: $table.stack, builder: (column) => column); +} + +class $$LogMessageEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$LogMessageEntityTable, + i1.LogMessageEntityData, + i1.$$LogMessageEntityTableFilterComposer, + i1.$$LogMessageEntityTableOrderingComposer, + i1.$$LogMessageEntityTableAnnotationComposer, + $$LogMessageEntityTableCreateCompanionBuilder, + $$LogMessageEntityTableUpdateCompanionBuilder, + ( + i1.LogMessageEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$LogMessageEntityTable, + i1.LogMessageEntityData + >, + ), + i1.LogMessageEntityData, + i0.PrefetchHooks Function() + > { + $$LogMessageEntityTableTableManager( + i0.GeneratedDatabase db, + i1.$LogMessageEntityTable table, + ) : super( + i0.TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + i1.$$LogMessageEntityTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => i1 + .$$LogMessageEntityTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + i1.$$LogMessageEntityTableAnnotationComposer( + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value message = const i0.Value.absent(), + i0.Value details = const i0.Value.absent(), + i0.Value level = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value logger = const i0.Value.absent(), + i0.Value stack = const i0.Value.absent(), + }) => i1.LogMessageEntityCompanion( + id: id, + message: message, + details: details, + level: level, + createdAt: createdAt, + logger: logger, + stack: stack, + ), + createCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + required String message, + i0.Value details = const i0.Value.absent(), + required i2.LogLevel level, + required DateTime createdAt, + i0.Value logger = const i0.Value.absent(), + i0.Value stack = const i0.Value.absent(), + }) => i1.LogMessageEntityCompanion.insert( + id: id, + message: message, + details: details, + level: level, + createdAt: createdAt, + logger: logger, + stack: stack, + ), + withReferenceMapper: (p0) => p0 + .map((e) => (e.readTable(table), i0.BaseReferences(db, table, e))) + .toList(), + prefetchHooksCallback: null, + ), + ); +} + +typedef $$LogMessageEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$LogMessageEntityTable, + i1.LogMessageEntityData, + i1.$$LogMessageEntityTableFilterComposer, + i1.$$LogMessageEntityTableOrderingComposer, + i1.$$LogMessageEntityTableAnnotationComposer, + $$LogMessageEntityTableCreateCompanionBuilder, + $$LogMessageEntityTableUpdateCompanionBuilder, + ( + i1.LogMessageEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$LogMessageEntityTable, + i1.LogMessageEntityData + >, + ), + i1.LogMessageEntityData, + i0.PrefetchHooks Function() + >; + +class $LogMessageEntityTable extends i3.LogMessageEntity + with i0.TableInfo<$LogMessageEntityTable, i1.LogMessageEntityData> { + @override + final i0.GeneratedDatabase attachedDatabase; + final String? _alias; + $LogMessageEntityTable(this.attachedDatabase, [this._alias]); + static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); + @override + late final i0.GeneratedColumn id = i0.GeneratedColumn( + 'id', + aliasedName, + false, + hasAutoIncrement: true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'PRIMARY KEY AUTOINCREMENT', + ), + ); + static const i0.VerificationMeta _messageMeta = const i0.VerificationMeta( + 'message', + ); + @override + late final i0.GeneratedColumn message = i0.GeneratedColumn( + 'message', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _detailsMeta = const i0.VerificationMeta( + 'details', + ); + @override + late final i0.GeneratedColumn details = i0.GeneratedColumn( + 'details', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + late final i0.GeneratedColumnWithTypeConverter level = + i0.GeneratedColumn( + 'level', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter(i1.$LogMessageEntityTable.$converterlevel); + static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta( + 'createdAt', + ); + @override + late final i0.GeneratedColumn createdAt = + i0.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _loggerMeta = const i0.VerificationMeta( + 'logger', + ); + @override + late final i0.GeneratedColumn logger = i0.GeneratedColumn( + 'logger', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _stackMeta = const i0.VerificationMeta( + 'stack', + ); + @override + late final i0.GeneratedColumn stack = i0.GeneratedColumn( + 'stack', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + message, + details, + level, + createdAt, + logger, + stack, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'logger_messages'; + @override + i0.VerificationContext validateIntegrity( + i0.Insertable instance, { + bool isInserting = false, + }) { + final context = i0.VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } + if (data.containsKey('message')) { + context.handle( + _messageMeta, + message.isAcceptableOrUnknown(data['message']!, _messageMeta), + ); + } else if (isInserting) { + context.missing(_messageMeta); + } + if (data.containsKey('details')) { + context.handle( + _detailsMeta, + details.isAcceptableOrUnknown(data['details']!, _detailsMeta), + ); + } + if (data.containsKey('created_at')) { + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); + } else if (isInserting) { + context.missing(_createdAtMeta); + } + if (data.containsKey('logger')) { + context.handle( + _loggerMeta, + logger.isAcceptableOrUnknown(data['logger']!, _loggerMeta), + ); + } + if (data.containsKey('stack')) { + context.handle( + _stackMeta, + stack.isAcceptableOrUnknown(data['stack']!, _stackMeta), + ); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + i1.LogMessageEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return i1.LogMessageEntityData( + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}id'], + )!, + message: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}message'], + )!, + details: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}details'], + ), + level: i1.$LogMessageEntityTable.$converterlevel.fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}level'], + )!, + ), + createdAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + logger: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}logger'], + ), + stack: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}stack'], + ), + ); + } + + @override + $LogMessageEntityTable createAlias(String alias) { + return $LogMessageEntityTable(attachedDatabase, alias); + } + + static i0.JsonTypeConverter2 $converterlevel = + const i0.EnumIndexConverter(i2.LogLevel.values); +} + +class LogMessageEntityData extends i0.DataClass + implements i0.Insertable { + final int id; + final String message; + final String? details; + final i2.LogLevel level; + final DateTime createdAt; + final String? logger; + final String? stack; + const LogMessageEntityData({ + required this.id, + required this.message, + this.details, + required this.level, + required this.createdAt, + this.logger, + this.stack, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = i0.Variable(id); + map['message'] = i0.Variable(message); + if (!nullToAbsent || details != null) { + map['details'] = i0.Variable(details); + } + { + map['level'] = i0.Variable( + i1.$LogMessageEntityTable.$converterlevel.toSql(level), + ); + } + map['created_at'] = i0.Variable(createdAt); + if (!nullToAbsent || logger != null) { + map['logger'] = i0.Variable(logger); + } + if (!nullToAbsent || stack != null) { + map['stack'] = i0.Variable(stack); + } + return map; + } + + factory LogMessageEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return LogMessageEntityData( + id: serializer.fromJson(json['id']), + message: serializer.fromJson(json['message']), + details: serializer.fromJson(json['details']), + level: i1.$LogMessageEntityTable.$converterlevel.fromJson( + serializer.fromJson(json['level']), + ), + createdAt: serializer.fromJson(json['createdAt']), + logger: serializer.fromJson(json['logger']), + stack: serializer.fromJson(json['stack']), + ); + } + @override + Map toJson({i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'message': serializer.toJson(message), + 'details': serializer.toJson(details), + 'level': serializer.toJson( + i1.$LogMessageEntityTable.$converterlevel.toJson(level), + ), + 'createdAt': serializer.toJson(createdAt), + 'logger': serializer.toJson(logger), + 'stack': serializer.toJson(stack), + }; + } + + i1.LogMessageEntityData copyWith({ + int? id, + String? message, + i0.Value details = const i0.Value.absent(), + i2.LogLevel? level, + DateTime? createdAt, + i0.Value logger = const i0.Value.absent(), + i0.Value stack = const i0.Value.absent(), + }) => i1.LogMessageEntityData( + id: id ?? this.id, + message: message ?? this.message, + details: details.present ? details.value : this.details, + level: level ?? this.level, + createdAt: createdAt ?? this.createdAt, + logger: logger.present ? logger.value : this.logger, + stack: stack.present ? stack.value : this.stack, + ); + LogMessageEntityData copyWithCompanion(i1.LogMessageEntityCompanion data) { + return LogMessageEntityData( + id: data.id.present ? data.id.value : this.id, + message: data.message.present ? data.message.value : this.message, + details: data.details.present ? data.details.value : this.details, + level: data.level.present ? data.level.value : this.level, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + logger: data.logger.present ? data.logger.value : this.logger, + stack: data.stack.present ? data.stack.value : this.stack, + ); + } + + @override + String toString() { + return (StringBuffer('LogMessageEntityData(') + ..write('id: $id, ') + ..write('message: $message, ') + ..write('details: $details, ') + ..write('level: $level, ') + ..write('createdAt: $createdAt, ') + ..write('logger: $logger, ') + ..write('stack: $stack') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, message, details, level, createdAt, logger, stack); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is i1.LogMessageEntityData && + other.id == this.id && + other.message == this.message && + other.details == this.details && + other.level == this.level && + other.createdAt == this.createdAt && + other.logger == this.logger && + other.stack == this.stack); +} + +class LogMessageEntityCompanion + extends i0.UpdateCompanion { + final i0.Value id; + final i0.Value message; + final i0.Value details; + final i0.Value level; + final i0.Value createdAt; + final i0.Value logger; + final i0.Value stack; + const LogMessageEntityCompanion({ + this.id = const i0.Value.absent(), + this.message = const i0.Value.absent(), + this.details = const i0.Value.absent(), + this.level = const i0.Value.absent(), + this.createdAt = const i0.Value.absent(), + this.logger = const i0.Value.absent(), + this.stack = const i0.Value.absent(), + }); + LogMessageEntityCompanion.insert({ + this.id = const i0.Value.absent(), + required String message, + this.details = const i0.Value.absent(), + required i2.LogLevel level, + required DateTime createdAt, + this.logger = const i0.Value.absent(), + this.stack = const i0.Value.absent(), + }) : message = i0.Value(message), + level = i0.Value(level), + createdAt = i0.Value(createdAt); + static i0.Insertable custom({ + i0.Expression? id, + i0.Expression? message, + i0.Expression? details, + i0.Expression? level, + i0.Expression? createdAt, + i0.Expression? logger, + i0.Expression? stack, + }) { + return i0.RawValuesInsertable({ + if (id != null) 'id': id, + if (message != null) 'message': message, + if (details != null) 'details': details, + if (level != null) 'level': level, + if (createdAt != null) 'created_at': createdAt, + if (logger != null) 'logger': logger, + if (stack != null) 'stack': stack, + }); + } + + i1.LogMessageEntityCompanion copyWith({ + i0.Value? id, + i0.Value? message, + i0.Value? details, + i0.Value? level, + i0.Value? createdAt, + i0.Value? logger, + i0.Value? stack, + }) { + return i1.LogMessageEntityCompanion( + id: id ?? this.id, + message: message ?? this.message, + details: details ?? this.details, + level: level ?? this.level, + createdAt: createdAt ?? this.createdAt, + logger: logger ?? this.logger, + stack: stack ?? this.stack, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = i0.Variable(id.value); + } + if (message.present) { + map['message'] = i0.Variable(message.value); + } + if (details.present) { + map['details'] = i0.Variable(details.value); + } + if (level.present) { + map['level'] = i0.Variable( + i1.$LogMessageEntityTable.$converterlevel.toSql(level.value), + ); + } + if (createdAt.present) { + map['created_at'] = i0.Variable(createdAt.value); + } + if (logger.present) { + map['logger'] = i0.Variable(logger.value); + } + if (stack.present) { + map['stack'] = i0.Variable(stack.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LogMessageEntityCompanion(') + ..write('id: $id, ') + ..write('message: $message, ') + ..write('details: $details, ') + ..write('level: $level, ') + ..write('createdAt: $createdAt, ') + ..write('logger: $logger, ') + ..write('stack: $stack') + ..write(')')) + .toString(); + } +} diff --git a/mobile/lib/infrastructure/entities/log.entity.g.dart b/mobile/lib/infrastructure/entities/log.entity.g.dart deleted file mode 100644 index 02fa817a08..0000000000 --- a/mobile/lib/infrastructure/entities/log.entity.g.dart +++ /dev/null @@ -1,1322 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'log.entity.dart'; - -// ************************************************************************** -// IsarCollectionGenerator -// ************************************************************************** - -// coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types - -extension GetLoggerMessageCollection on Isar { - IsarCollection get loggerMessages => this.collection(); -} - -const LoggerMessageSchema = CollectionSchema( - name: r'LoggerMessage', - id: -1606100856208753787, - properties: { - r'context1': PropertySchema( - id: 0, - name: r'context1', - type: IsarType.string, - ), - r'context2': PropertySchema( - id: 1, - name: r'context2', - type: IsarType.string, - ), - r'createdAt': PropertySchema( - id: 2, - name: r'createdAt', - type: IsarType.dateTime, - ), - r'details': PropertySchema(id: 3, name: r'details', type: IsarType.string), - r'level': PropertySchema( - id: 4, - name: r'level', - type: IsarType.byte, - enumMap: _LoggerMessagelevelEnumValueMap, - ), - r'message': PropertySchema(id: 5, name: r'message', type: IsarType.string), - }, - - estimateSize: _loggerMessageEstimateSize, - serialize: _loggerMessageSerialize, - deserialize: _loggerMessageDeserialize, - deserializeProp: _loggerMessageDeserializeProp, - idName: r'id', - indexes: {}, - links: {}, - embeddedSchemas: {}, - - getId: _loggerMessageGetId, - getLinks: _loggerMessageGetLinks, - attach: _loggerMessageAttach, - version: '3.1.8', -); - -int _loggerMessageEstimateSize( - LoggerMessage object, - List offsets, - Map> allOffsets, -) { - var bytesCount = offsets.last; - { - final value = object.context1; - if (value != null) { - bytesCount += 3 + value.length * 3; - } - } - { - final value = object.context2; - if (value != null) { - bytesCount += 3 + value.length * 3; - } - } - { - final value = object.details; - if (value != null) { - bytesCount += 3 + value.length * 3; - } - } - bytesCount += 3 + object.message.length * 3; - return bytesCount; -} - -void _loggerMessageSerialize( - LoggerMessage object, - IsarWriter writer, - List offsets, - Map> allOffsets, -) { - writer.writeString(offsets[0], object.context1); - writer.writeString(offsets[1], object.context2); - writer.writeDateTime(offsets[2], object.createdAt); - writer.writeString(offsets[3], object.details); - writer.writeByte(offsets[4], object.level.index); - writer.writeString(offsets[5], object.message); -} - -LoggerMessage _loggerMessageDeserialize( - Id id, - IsarReader reader, - List offsets, - Map> allOffsets, -) { - final object = LoggerMessage( - context1: reader.readStringOrNull(offsets[0]), - context2: reader.readStringOrNull(offsets[1]), - createdAt: reader.readDateTime(offsets[2]), - details: reader.readStringOrNull(offsets[3]), - level: - _LoggerMessagelevelValueEnumMap[reader.readByteOrNull(offsets[4])] ?? - LogLevel.info, - message: reader.readString(offsets[5]), - ); - return object; -} - -P _loggerMessageDeserializeProp

( - IsarReader reader, - int propertyId, - int offset, - Map> allOffsets, -) { - switch (propertyId) { - case 0: - return (reader.readStringOrNull(offset)) as P; - case 1: - return (reader.readStringOrNull(offset)) as P; - case 2: - return (reader.readDateTime(offset)) as P; - case 3: - return (reader.readStringOrNull(offset)) as P; - case 4: - return (_LoggerMessagelevelValueEnumMap[reader.readByteOrNull(offset)] ?? - LogLevel.info) - as P; - case 5: - return (reader.readString(offset)) as P; - default: - throw IsarError('Unknown property with id $propertyId'); - } -} - -const _LoggerMessagelevelEnumValueMap = { - 'all': 0, - 'finest': 1, - 'finer': 2, - 'fine': 3, - 'config': 4, - 'info': 5, - 'warning': 6, - 'severe': 7, - 'shout': 8, - 'off': 9, -}; -const _LoggerMessagelevelValueEnumMap = { - 0: LogLevel.all, - 1: LogLevel.finest, - 2: LogLevel.finer, - 3: LogLevel.fine, - 4: LogLevel.config, - 5: LogLevel.info, - 6: LogLevel.warning, - 7: LogLevel.severe, - 8: LogLevel.shout, - 9: LogLevel.off, -}; - -Id _loggerMessageGetId(LoggerMessage object) { - return object.id; -} - -List> _loggerMessageGetLinks(LoggerMessage object) { - return []; -} - -void _loggerMessageAttach( - IsarCollection col, - Id id, - LoggerMessage object, -) {} - -extension LoggerMessageQueryWhereSort - on QueryBuilder { - QueryBuilder anyId() { - return QueryBuilder.apply(this, (query) { - return query.addWhereClause(const IdWhereClause.any()); - }); - } -} - -extension LoggerMessageQueryWhere - on QueryBuilder { - QueryBuilder idEqualTo( - Id id, - ) { - return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between(lower: id, upper: id)); - }); - } - - QueryBuilder idNotEqualTo( - Id id, - ) { - return QueryBuilder.apply(this, (query) { - if (query.whereSort == Sort.asc) { - return query - .addWhereClause( - IdWhereClause.lessThan(upper: id, includeUpper: false), - ) - .addWhereClause( - IdWhereClause.greaterThan(lower: id, includeLower: false), - ); - } else { - return query - .addWhereClause( - IdWhereClause.greaterThan(lower: id, includeLower: false), - ) - .addWhereClause( - IdWhereClause.lessThan(upper: id, includeUpper: false), - ); - } - }); - } - - QueryBuilder idGreaterThan( - Id id, { - bool include = false, - }) { - return QueryBuilder.apply(this, (query) { - return query.addWhereClause( - IdWhereClause.greaterThan(lower: id, includeLower: include), - ); - }); - } - - QueryBuilder idLessThan( - Id id, { - bool include = false, - }) { - return QueryBuilder.apply(this, (query) { - return query.addWhereClause( - IdWhereClause.lessThan(upper: id, includeUpper: include), - ); - }); - } - - QueryBuilder idBetween( - Id lowerId, - Id upperId, { - bool includeLower = true, - bool includeUpper = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addWhereClause( - IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - ), - ); - }); - } -} - -extension LoggerMessageQueryFilter - on QueryBuilder { - QueryBuilder - context1IsNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - const FilterCondition.isNull(property: r'context1'), - ); - }); - } - - QueryBuilder - context1IsNotNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - const FilterCondition.isNotNull(property: r'context1'), - ); - }); - } - - QueryBuilder - context1EqualTo(String? value, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.equalTo( - property: r'context1', - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - context1GreaterThan( - String? value, { - bool include = false, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.greaterThan( - include: include, - property: r'context1', - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - context1LessThan( - String? value, { - bool include = false, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.lessThan( - include: include, - property: r'context1', - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - context1Between( - String? lower, - String? upper, { - bool includeLower = true, - bool includeUpper = true, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.between( - property: r'context1', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - context1StartsWith(String value, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.startsWith( - property: r'context1', - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - context1EndsWith(String value, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.endsWith( - property: r'context1', - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - context1Contains(String value, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.contains( - property: r'context1', - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - context1Matches(String pattern, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.matches( - property: r'context1', - wildcard: pattern, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - context1IsEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.equalTo(property: r'context1', value: ''), - ); - }); - } - - QueryBuilder - context1IsNotEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.greaterThan(property: r'context1', value: ''), - ); - }); - } - - QueryBuilder - context2IsNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - const FilterCondition.isNull(property: r'context2'), - ); - }); - } - - QueryBuilder - context2IsNotNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - const FilterCondition.isNotNull(property: r'context2'), - ); - }); - } - - QueryBuilder - context2EqualTo(String? value, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.equalTo( - property: r'context2', - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - context2GreaterThan( - String? value, { - bool include = false, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.greaterThan( - include: include, - property: r'context2', - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - context2LessThan( - String? value, { - bool include = false, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.lessThan( - include: include, - property: r'context2', - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - context2Between( - String? lower, - String? upper, { - bool includeLower = true, - bool includeUpper = true, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.between( - property: r'context2', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - context2StartsWith(String value, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.startsWith( - property: r'context2', - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - context2EndsWith(String value, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.endsWith( - property: r'context2', - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - context2Contains(String value, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.contains( - property: r'context2', - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - context2Matches(String pattern, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.matches( - property: r'context2', - wildcard: pattern, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - context2IsEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.equalTo(property: r'context2', value: ''), - ); - }); - } - - QueryBuilder - context2IsNotEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.greaterThan(property: r'context2', value: ''), - ); - }); - } - - QueryBuilder - createdAtEqualTo(DateTime value) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.equalTo(property: r'createdAt', value: value), - ); - }); - } - - QueryBuilder - createdAtGreaterThan(DateTime value, {bool include = false}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.greaterThan( - include: include, - property: r'createdAt', - value: value, - ), - ); - }); - } - - QueryBuilder - createdAtLessThan(DateTime value, {bool include = false}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.lessThan( - include: include, - property: r'createdAt', - value: value, - ), - ); - }); - } - - QueryBuilder - createdAtBetween( - DateTime lower, - DateTime upper, { - bool includeLower = true, - bool includeUpper = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.between( - property: r'createdAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - ), - ); - }); - } - - QueryBuilder - detailsIsNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - const FilterCondition.isNull(property: r'details'), - ); - }); - } - - QueryBuilder - detailsIsNotNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - const FilterCondition.isNotNull(property: r'details'), - ); - }); - } - - QueryBuilder - detailsEqualTo(String? value, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.equalTo( - property: r'details', - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - detailsGreaterThan( - String? value, { - bool include = false, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.greaterThan( - include: include, - property: r'details', - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - detailsLessThan( - String? value, { - bool include = false, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.lessThan( - include: include, - property: r'details', - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - detailsBetween( - String? lower, - String? upper, { - bool includeLower = true, - bool includeUpper = true, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.between( - property: r'details', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - detailsStartsWith(String value, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.startsWith( - property: r'details', - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - detailsEndsWith(String value, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.endsWith( - property: r'details', - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - detailsContains(String value, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.contains( - property: r'details', - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - detailsMatches(String pattern, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.matches( - property: r'details', - wildcard: pattern, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - detailsIsEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.equalTo(property: r'details', value: ''), - ); - }); - } - - QueryBuilder - detailsIsNotEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.greaterThan(property: r'details', value: ''), - ); - }); - } - - QueryBuilder idEqualTo( - Id value, - ) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.equalTo(property: r'id', value: value), - ); - }); - } - - QueryBuilder - idGreaterThan(Id value, {bool include = false}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - ), - ); - }); - } - - QueryBuilder idLessThan( - Id value, { - bool include = false, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - ), - ); - }); - } - - QueryBuilder idBetween( - Id lower, - Id upper, { - bool includeLower = true, - bool includeUpper = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - ), - ); - }); - } - - QueryBuilder - levelEqualTo(LogLevel value) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.equalTo(property: r'level', value: value), - ); - }); - } - - QueryBuilder - levelGreaterThan(LogLevel value, {bool include = false}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.greaterThan( - include: include, - property: r'level', - value: value, - ), - ); - }); - } - - QueryBuilder - levelLessThan(LogLevel value, {bool include = false}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.lessThan( - include: include, - property: r'level', - value: value, - ), - ); - }); - } - - QueryBuilder - levelBetween( - LogLevel lower, - LogLevel upper, { - bool includeLower = true, - bool includeUpper = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.between( - property: r'level', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - ), - ); - }); - } - - QueryBuilder - messageEqualTo(String value, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.equalTo( - property: r'message', - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - messageGreaterThan( - String value, { - bool include = false, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.greaterThan( - include: include, - property: r'message', - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - messageLessThan( - String value, { - bool include = false, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.lessThan( - include: include, - property: r'message', - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - messageBetween( - String lower, - String upper, { - bool includeLower = true, - bool includeUpper = true, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.between( - property: r'message', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - messageStartsWith(String value, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.startsWith( - property: r'message', - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - messageEndsWith(String value, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.endsWith( - property: r'message', - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - messageContains(String value, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.contains( - property: r'message', - value: value, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - messageMatches(String pattern, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.matches( - property: r'message', - wildcard: pattern, - caseSensitive: caseSensitive, - ), - ); - }); - } - - QueryBuilder - messageIsEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.equalTo(property: r'message', value: ''), - ); - }); - } - - QueryBuilder - messageIsNotEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition( - FilterCondition.greaterThan(property: r'message', value: ''), - ); - }); - } -} - -extension LoggerMessageQueryObject - on QueryBuilder {} - -extension LoggerMessageQueryLinks - on QueryBuilder {} - -extension LoggerMessageQuerySortBy - on QueryBuilder { - QueryBuilder sortByContext1() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'context1', Sort.asc); - }); - } - - QueryBuilder - sortByContext1Desc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'context1', Sort.desc); - }); - } - - QueryBuilder sortByContext2() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'context2', Sort.asc); - }); - } - - QueryBuilder - sortByContext2Desc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'context2', Sort.desc); - }); - } - - QueryBuilder sortByCreatedAt() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'createdAt', Sort.asc); - }); - } - - QueryBuilder - sortByCreatedAtDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'createdAt', Sort.desc); - }); - } - - QueryBuilder sortByDetails() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'details', Sort.asc); - }); - } - - QueryBuilder sortByDetailsDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'details', Sort.desc); - }); - } - - QueryBuilder sortByLevel() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'level', Sort.asc); - }); - } - - QueryBuilder sortByLevelDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'level', Sort.desc); - }); - } - - QueryBuilder sortByMessage() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'message', Sort.asc); - }); - } - - QueryBuilder sortByMessageDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'message', Sort.desc); - }); - } -} - -extension LoggerMessageQuerySortThenBy - on QueryBuilder { - QueryBuilder thenByContext1() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'context1', Sort.asc); - }); - } - - QueryBuilder - thenByContext1Desc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'context1', Sort.desc); - }); - } - - QueryBuilder thenByContext2() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'context2', Sort.asc); - }); - } - - QueryBuilder - thenByContext2Desc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'context2', Sort.desc); - }); - } - - QueryBuilder thenByCreatedAt() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'createdAt', Sort.asc); - }); - } - - QueryBuilder - thenByCreatedAtDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'createdAt', Sort.desc); - }); - } - - QueryBuilder thenByDetails() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'details', Sort.asc); - }); - } - - QueryBuilder thenByDetailsDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'details', Sort.desc); - }); - } - - QueryBuilder thenById() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'id', Sort.asc); - }); - } - - QueryBuilder thenByIdDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'id', Sort.desc); - }); - } - - QueryBuilder thenByLevel() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'level', Sort.asc); - }); - } - - QueryBuilder thenByLevelDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'level', Sort.desc); - }); - } - - QueryBuilder thenByMessage() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'message', Sort.asc); - }); - } - - QueryBuilder thenByMessageDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'message', Sort.desc); - }); - } -} - -extension LoggerMessageQueryWhereDistinct - on QueryBuilder { - QueryBuilder distinctByContext1({ - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'context1', caseSensitive: caseSensitive); - }); - } - - QueryBuilder distinctByContext2({ - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'context2', caseSensitive: caseSensitive); - }); - } - - QueryBuilder distinctByCreatedAt() { - return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'createdAt'); - }); - } - - QueryBuilder distinctByDetails({ - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'details', caseSensitive: caseSensitive); - }); - } - - QueryBuilder distinctByLevel() { - return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'level'); - }); - } - - QueryBuilder distinctByMessage({ - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'message', caseSensitive: caseSensitive); - }); - } -} - -extension LoggerMessageQueryProperty - on QueryBuilder { - QueryBuilder idProperty() { - return QueryBuilder.apply(this, (query) { - return query.addPropertyName(r'id'); - }); - } - - QueryBuilder context1Property() { - return QueryBuilder.apply(this, (query) { - return query.addPropertyName(r'context1'); - }); - } - - QueryBuilder context2Property() { - return QueryBuilder.apply(this, (query) { - return query.addPropertyName(r'context2'); - }); - } - - QueryBuilder createdAtProperty() { - return QueryBuilder.apply(this, (query) { - return query.addPropertyName(r'createdAt'); - }); - } - - QueryBuilder detailsProperty() { - return QueryBuilder.apply(this, (query) { - return query.addPropertyName(r'details'); - }); - } - - QueryBuilder levelProperty() { - return QueryBuilder.apply(this, (query) { - return query.addPropertyName(r'level'); - }); - } - - QueryBuilder messageProperty() { - return QueryBuilder.apply(this, (query) { - return query.addPropertyName(r'message'); - }); - } -} diff --git a/mobile/lib/infrastructure/repositories/log.repository.dart b/mobile/lib/infrastructure/repositories/log.repository.dart index eefe2b0ab0..9b3985ba48 100644 --- a/mobile/lib/infrastructure/repositories/log.repository.dart +++ b/mobile/lib/infrastructure/repositories/log.repository.dart @@ -1,27 +1,43 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/domain/models/log.model.dart'; import 'package:immich_mobile/infrastructure/entities/log.entity.dart'; -import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; -import 'package:isar/isar.dart'; +import 'package:immich_mobile/infrastructure/entities/log.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart'; -class IsarLogRepository extends IsarDatabaseRepository { - final Isar _db; - const IsarLogRepository(super.db) : _db = db; +class LogRepository { + final DriftLogger _db; + const LogRepository(this._db); Future deleteAll() async { - await transaction(() async => await _db.loggerMessages.clear()); + await _db.logMessageEntity.deleteAll(); return true; } - Future> getAll() async { - final logs = await _db.loggerMessages.where().sortByCreatedAtDesc().findAll(); - return logs.map((l) => l.toDto()).toList(); + Future> getAll({int limit = 250}) async { + final query = _db.logMessageEntity.select() + ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) + ..limit(limit); + + return query.map((log) => log.toDto()).get(); + } + + LogMessageEntityCompanion _toEntityCompanion(LogMessage log) { + return LogMessageEntityCompanion.insert( + message: log.message, + level: log.level, + createdAt: log.createdAt, + logger: Value(log.logger), + details: Value(log.error), + stack: Value(log.stack), + ); } Future insert(LogMessage log) async { - final logEntity = LoggerMessage.fromDto(log); + final logEntity = _toEntityCompanion(log); try { - await transaction(() => _db.loggerMessages.put(logEntity)); + await _db.logMessageEntity.insertOne(logEntity); } catch (e) { return false; } @@ -30,19 +46,30 @@ class IsarLogRepository extends IsarDatabaseRepository { } Future insertAll(Iterable logs) async { - await transaction(() async { - final logEntities = logs.map((log) => LoggerMessage.fromDto(log)).toList(); - await _db.loggerMessages.putAll(logEntities); - }); + final logEntities = logs.map(_toEntityCompanion).toList(); + await _db.logMessageEntity.insertAll(logEntities); + return true; } - Future truncate({int limit = 250}) async { - await transaction(() async { - final count = await _db.loggerMessages.count(); - if (count <= limit) return; - final toRemove = count - limit; - await _db.loggerMessages.where().limit(toRemove).deleteAll(); - }); + Future deleteByLogger(String logger) async { + await _db.logMessageEntity.deleteWhere((row) => row.logger.equals(logger)); + } + + Stream> watchMessages(String logger) { + final query = _db.logMessageEntity.select() + ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) + ..where((row) => row.logger.equals(logger)); + + return query.watch().map((rows) => rows.map((row) => row.toDto()).toList()); + } + + Future truncate({int limit = kLogTruncateLimit}) async { + final totalCount = await _db.managers.logMessageEntity.count(); + if (totalCount > limit) { + final rowsToDelete = totalCount - limit; + + await _db.managers.logMessageEntity.orderBy((o) => o.createdAt.asc()).limit(rowsToDelete).delete(); + } } } diff --git a/mobile/lib/infrastructure/repositories/logger_db.repository.dart b/mobile/lib/infrastructure/repositories/logger_db.repository.dart new file mode 100644 index 0000000000..583fc42813 --- /dev/null +++ b/mobile/lib/infrastructure/repositories/logger_db.repository.dart @@ -0,0 +1,27 @@ +import 'package:drift/drift.dart'; +import 'package:drift_flutter/drift_flutter.dart'; +import 'package:immich_mobile/domain/interfaces/db.interface.dart'; +import 'package:immich_mobile/infrastructure/entities/log.entity.dart'; + +import 'logger_db.repository.drift.dart'; + +@DriftDatabase(tables: [LogMessageEntity]) +class DriftLogger extends $DriftLogger implements IDatabaseRepository { + DriftLogger([QueryExecutor? executor]) + : super( + executor ?? driftDatabase(name: 'immich_logs', native: const DriftNativeOptions(shareAcrossIsolates: true)), + ); + + @override + int get schemaVersion => 1; + + @override + MigrationStrategy get migration => MigrationStrategy( + beforeOpen: (details) async { + await customStatement('PRAGMA foreign_keys = ON'); + await customStatement('PRAGMA synchronous = NORMAL'); + await customStatement('PRAGMA journal_mode = WAL'); + await customStatement('PRAGMA busy_timeout = 500'); + }, + ); +} diff --git a/mobile/lib/infrastructure/repositories/logger_db.repository.drift.dart b/mobile/lib/infrastructure/repositories/logger_db.repository.drift.dart new file mode 100644 index 0000000000..8389d3a827 --- /dev/null +++ b/mobile/lib/infrastructure/repositories/logger_db.repository.drift.dart @@ -0,0 +1,27 @@ +// dart format width=80 +// ignore_for_file: type=lint +import 'package:drift/drift.dart' as i0; +import 'package:immich_mobile/infrastructure/entities/log.entity.drift.dart' + as i1; + +abstract class $DriftLogger extends i0.GeneratedDatabase { + $DriftLogger(i0.QueryExecutor e) : super(e); + $DriftLoggerManager get managers => $DriftLoggerManager(this); + late final i1.$LogMessageEntityTable logMessageEntity = i1 + .$LogMessageEntityTable(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [logMessageEntity]; + @override + i0.DriftDatabaseOptions get options => + const i0.DriftDatabaseOptions(storeDateTimeAsText: true); +} + +class $DriftLoggerManager { + final $DriftLogger _db; + $DriftLoggerManager(this._db); + i1.$$LogMessageEntityTableTableManager get logMessageEntity => + i1.$$LogMessageEntityTableTableManager(_db, _db.logMessageEntity); +} diff --git a/mobile/lib/infrastructure/repositories/sync_api.repository.dart b/mobile/lib/infrastructure/repositories/sync_api.repository.dart index 48c38cf64b..2175e77e82 100644 --- a/mobile/lib/infrastructure/repositories/sync_api.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_api.repository.dart @@ -4,7 +4,6 @@ import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/domain/models/sync_event.model.dart'; -import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart'; import 'package:immich_mobile/services/api.service.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; @@ -107,7 +106,6 @@ class SyncApiRepository { } stopwatch.stop(); _logger.info("Remote Sync completed in ${stopwatch.elapsed.inMilliseconds}ms"); - DLog.log("Remote Sync completed in ${stopwatch.elapsed.inMilliseconds}ms"); } List _parseLines(List lines) { diff --git a/mobile/lib/pages/common/app_log_detail.page.dart b/mobile/lib/pages/common/app_log_detail.page.dart index a9cf634faf..c9773f36e1 100644 --- a/mobile/lib/pages/common/app_log_detail.page.dart +++ b/mobile/lib/pages/common/app_log_detail.page.dart @@ -65,7 +65,7 @@ class AppLogDetailPage extends HookConsumerWidget { ); } - buildLogContext1(String context1) { + buildLogContext(String logger) { return Padding( padding: const EdgeInsets.all(8.0), child: Column( @@ -86,7 +86,7 @@ class AppLogDetailPage extends HookConsumerWidget { child: Padding( padding: const EdgeInsets.all(8.0), child: SelectableText( - context1.toString(), + logger.toString(), style: const TextStyle(fontSize: 12.0, fontWeight: FontWeight.bold, fontFamily: "Inconsolata"), ), ), @@ -103,7 +103,7 @@ class AppLogDetailPage extends HookConsumerWidget { children: [ buildTextWithCopyButton("MESSAGE", logMessage.message), if (logMessage.error != null) buildTextWithCopyButton("DETAILS", logMessage.error.toString()), - if (logMessage.logger != null) buildLogContext1(logMessage.logger.toString()), + if (logMessage.logger != null) buildLogContext(logMessage.logger.toString()), if (logMessage.stack != null) buildTextWithCopyButton("STACK TRACE", logMessage.stack.toString()), ], ), diff --git a/mobile/lib/pages/common/splash_screen.page.dart b/mobile/lib/pages/common/splash_screen.page.dart index 159a5b4fee..87ea7849c6 100644 --- a/mobile/lib/pages/common/splash_screen.page.dart +++ b/mobile/lib/pages/common/splash_screen.page.dart @@ -49,7 +49,6 @@ class SplashScreenPageState extends ConsumerState { final wsProvider = ref.read(websocketProvider.notifier); ref.read(authProvider.notifier).saveAuthInfo(accessToken: accessToken).then( (a) { - log.info('Successfully updated auth info with access token: $accessToken'); try { wsProvider.connect(); infoProvider.getServerInfo(); diff --git a/mobile/lib/presentation/pages/dev/dev_logger.dart b/mobile/lib/presentation/pages/dev/dev_logger.dart deleted file mode 100644 index ab9849f87c..0000000000 --- a/mobile/lib/presentation/pages/dev/dev_logger.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:flutter/foundation.dart'; -import 'package:immich_mobile/domain/models/log.model.dart'; -import 'package:immich_mobile/infrastructure/entities/log.entity.dart'; -import 'package:immich_mobile/infrastructure/repositories/log.repository.dart'; -// ignore: import_rule_isar -import 'package:isar/isar.dart'; - -const kDevLoggerTag = 'DEV'; - -abstract final class DLog { - const DLog(); - - static Stream> watchLog() { - final db = Isar.getInstance(); - if (db == null) { - return const Stream.empty(); - } - - return db.loggerMessages - .filter() - .context1EqualTo(kDevLoggerTag) - .sortByCreatedAtDesc() - .watch(fireImmediately: true) - .map((logs) => logs.map((log) => log.toDto()).toList()); - } - - static void clearLog() { - final db = Isar.getInstance(); - if (db == null) { - return; - } - - db.writeTxnSync(() { - db.loggerMessages.filter().context1EqualTo(kDevLoggerTag).deleteAllSync(); - }); - } - - static void log(String message, [Object? error, StackTrace? stackTrace]) { - if (!Platform.environment.containsKey('FLUTTER_TEST')) { - debugPrint('[$kDevLoggerTag] [${DateTime.now()}] $message'); - } - if (error != null) { - debugPrint('Error: $error'); - } - if (stackTrace != null) { - debugPrint('StackTrace: $stackTrace'); - } - - final isar = Isar.getInstance(); - if (isar == null) { - return; - } - - final record = LogMessage( - message: message, - level: LogLevel.info, - createdAt: DateTime.now(), - logger: kDevLoggerTag, - error: error?.toString(), - stack: stackTrace?.toString(), - ); - - unawaited(IsarLogRepository(isar).insert(record)); - } -} diff --git a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart index 2ab1eeaaa9..d3f0e3c1bc 100644 --- a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart +++ b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart @@ -2,19 +2,16 @@ import 'dart:async'; import 'package:auto_route/auto_route.dart'; import 'package:drift/drift.dart' hide Column; -import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; -import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/extensions/theme_extensions.dart'; -import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart'; import 'package:immich_mobile/providers/background_sync.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/providers/infrastructure/platform.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/routing/router.dart'; +import 'package:logging/logging.dart'; final _features = [ _Feature( @@ -37,7 +34,7 @@ final _features = [ DriftAssetSelectionTimelineRoute(lockedSelectionAssets: assets.toSet()), ); - DLog.log("Selected ${selectedAssets?.length ?? 0} assets"); + Logger("FeaturesInDevelopment").fine("Selected ${selectedAssets?.length ?? 0} assets"); return Future.value(); }, @@ -159,7 +156,6 @@ class FeatInDevPage extends StatelessWidget { ), ), const Divider(height: 0), - const Flexible(child: _DevLogs()), ], ), ); @@ -174,57 +170,3 @@ class _Feature { final TextStyle? style; final Future Function(BuildContext, WidgetRef _) onTap; } - -class _DevLogs extends StatelessWidget { - const _DevLogs(); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - automaticallyImplyLeading: false, - actions: [ - IconButton( - onPressed: DLog.clearLog, - icon: Icon( - Icons.delete_outline_rounded, - size: 20.0, - color: context.primaryColor, - semanticLabel: "Clear logs", - ), - ), - ], - centerTitle: true, - ), - body: StreamBuilder( - initialData: [], - stream: DLog.watchLog(), - builder: (_, logMessages) { - return ListView.separated( - itemBuilder: (ctx, index) { - final logMessage = logMessages.data![index]; - return ListTile( - title: Text( - logMessage.message, - style: TextStyle(color: ctx.colorScheme.onSurface, fontSize: 14.0, overflow: TextOverflow.ellipsis), - ), - subtitle: Text( - "at ${DateFormat("HH:mm:ss.SSS").format(logMessage.createdAt)}", - style: TextStyle(color: ctx.colorScheme.onSurfaceSecondary, fontSize: 12.0), - ), - dense: true, - visualDensity: VisualDensity.compact, - tileColor: Colors.transparent, - minLeadingWidth: 10, - ); - }, - separatorBuilder: (_, index) { - return const Divider(height: 0); - }, - itemCount: logMessages.data?.length ?? 0, - ); - }, - ), - ); - } -} diff --git a/mobile/lib/utils/bootstrap.dart b/mobile/lib/utils/bootstrap.dart index 8c4ca077c4..9cab9caf9e 100644 --- a/mobile/lib/utils/bootstrap.dart +++ b/mobile/lib/utils/bootstrap.dart @@ -12,10 +12,10 @@ import 'package:immich_mobile/entities/etag.entity.dart'; import 'package:immich_mobile/entities/ios_device_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/device_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; -import 'package:immich_mobile/infrastructure/entities/log.entity.dart'; import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/log.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; import 'package:isar/isar.dart'; import 'package:path_provider/path_provider.dart'; @@ -36,7 +36,6 @@ abstract final class Bootstrap { UserSchema, BackupAlbumSchema, DuplicatedAssetSchema, - LoggerMessageSchema, ETagSchema, if (Platform.isAndroid) AndroidDeviceAssetSchema, if (Platform.isIOS) IOSDeviceAssetSchema, @@ -49,9 +48,13 @@ abstract final class Bootstrap { } static Future initDomain(Isar db, {bool shouldBufferLogs = true}) async { + // load drift dbs + final loggerDb = DriftLogger(); + await StoreService.init(storeRepository: IsarStoreRepository(db)); + await LogService.init( - logRepository: IsarLogRepository(db), + logRepository: LogRepository(loggerDb), storeRepository: IsarStoreRepository(db), shouldBuffer: shouldBufferLogs, ); diff --git a/mobile/lib/utils/isolate.dart b/mobile/lib/utils/isolate.dart index a57c3ebbd5..01903cfc74 100644 --- a/mobile/lib/utils/isolate.dart +++ b/mobile/lib/utils/isolate.dart @@ -56,7 +56,7 @@ Cancelable runInIsolateGentle({ log.severe("Error in runInIsolateGentle ${debugLabel == null ? '' : ' for $debugLabel'}", error, stack); } finally { try { - await LogService.I.flushBuffer(); + await LogService.I.flush(); await ref.read(driftProvider).close(); // Close Isar safely diff --git a/mobile/test/domain/services/log_service_test.dart b/mobile/test/domain/services/log_service_test.dart index 87b32b8298..b4feac4e2f 100644 --- a/mobile/test/domain/services/log_service_test.dart +++ b/mobile/test/domain/services/log_service_test.dart @@ -28,7 +28,7 @@ final _kWarnLog = LogMessage( void main() { late LogService sut; - late IsarLogRepository mockLogRepo; + late LogRepository mockLogRepo; late IsarStoreRepository mockStoreRepo; setUp(() async { diff --git a/mobile/test/infrastructure/repository.mock.dart b/mobile/test/infrastructure/repository.mock.dart index ed20f177b7..29ef9462a8 100644 --- a/mobile/test/infrastructure/repository.mock.dart +++ b/mobile/test/infrastructure/repository.mock.dart @@ -12,7 +12,7 @@ import 'package:mocktail/mocktail.dart'; class MockStoreRepository extends Mock implements IsarStoreRepository {} -class MockLogRepository extends Mock implements IsarLogRepository {} +class MockLogRepository extends Mock implements LogRepository {} class MockIsarUserRepository extends Mock implements IsarUserRepository {} diff --git a/mobile/test/modules/shared/sync_service_test.dart b/mobile/test/modules/shared/sync_service_test.dart index 22fd3cacfc..767a52b8d8 100644 --- a/mobile/test/modules/shared/sync_service_test.dart +++ b/mobile/test/modules/shared/sync_service_test.dart @@ -1,3 +1,5 @@ +import 'package:drift/drift.dart'; +import 'package:drift/native.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:immich_mobile/constants/enums.dart'; @@ -9,9 +11,10 @@ import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/etag.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/log.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; -import 'package:immich_mobile/repositories/partner_api.repository.dart'; import 'package:immich_mobile/repositories/asset.repository.dart'; +import 'package:immich_mobile/repositories/partner_api.repository.dart'; import 'package:immich_mobile/services/sync.service.dart'; import 'package:mocktail/mocktail.dart'; @@ -49,6 +52,28 @@ void main() { ); } + final owner = UserDto( + id: "1", + updatedAt: DateTime.now(), + email: "a@b.c", + name: "first last", + isAdmin: false, + profileChangedAt: DateTime.now(), + ); + + setUpAll(() async { + final loggerDb = DriftLogger(DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true)); + final LogRepository logRepository = LogRepository(loggerDb); + + WidgetsFlutterBinding.ensureInitialized(); + final db = await TestUtils.initIsar(); + + db.writeTxnSync(() => db.clearSync()); + await StoreService.init(storeRepository: IsarStoreRepository(db)); + await Store.put(StoreKey.currentUser, owner); + await LogService.init(logRepository: logRepository, storeRepository: IsarStoreRepository(db)); + }); + group('Test SyncService grouped', () { final MockHashService hs = MockHashService(); final MockEntityService entityService = MockEntityService(); @@ -74,16 +99,9 @@ void main() { isAdmin: false, profileChangedAt: DateTime(2021), ); - late SyncService s; - setUpAll(() async { - WidgetsFlutterBinding.ensureInitialized(); - final db = await TestUtils.initIsar(); - db.writeTxnSync(() => db.clearSync()); - await StoreService.init(storeRepository: IsarStoreRepository(db)); - await Store.put(StoreKey.currentUser, owner); - await LogService.init(logRepository: IsarLogRepository(db), storeRepository: IsarStoreRepository(db)); - }); + late SyncService s; + final List initialAssets = [ makeAsset(checksum: "a", remoteId: "0-1"), makeAsset(checksum: "b", remoteId: "2-1"), diff --git a/mobile/test/test_utils.dart b/mobile/test/test_utils.dart index d932e2ffc7..9b59773d3b 100644 --- a/mobile/test/test_utils.dart +++ b/mobile/test/test_utils.dart @@ -14,7 +14,6 @@ import 'package:immich_mobile/entities/etag.entity.dart'; import 'package:immich_mobile/entities/ios_device_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/device_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; -import 'package:immich_mobile/infrastructure/entities/log.entity.dart'; import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:isar/isar.dart'; @@ -48,7 +47,6 @@ abstract final class TestUtils { UserSchema, BackupAlbumSchema, DuplicatedAssetSchema, - LoggerMessageSchema, ETagSchema, AndroidDeviceAssetSchema, IOSDeviceAssetSchema, From c5c9a522c1a40305800485f6939d519caeb9078d Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Thu, 7 Aug 2025 00:10:06 +0530 Subject: [PATCH 193/748] fix: remove drift map scrubber (#20723) Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- .../bottom_sheet/map_bottom_sheet.widget.dart | 2 +- .../widgets/timeline/timeline.widget.dart | 86 +++++++++++-------- 2 files changed, 52 insertions(+), 36 deletions(-) diff --git a/mobile/lib/presentation/widgets/bottom_sheet/map_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/map_bottom_sheet.widget.dart index 0017621c39..19cce3392f 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/map_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/map_bottom_sheet.widget.dart @@ -36,7 +36,7 @@ class _ScopedMapTimeline extends StatelessWidget { return timelineService; }), ], - child: const Timeline(appBar: null, bottomSheet: null), + child: const Timeline(appBar: null, bottomSheet: null, withScrubber: false), ); } } diff --git a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart index b35aac59ee..c859ae0e80 100644 --- a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart @@ -36,6 +36,7 @@ class Timeline extends StatelessWidget { this.appBar = const ImmichSliverAppBar(floating: true, pinned: false, snap: false), this.bottomSheet = const GeneralBottomSheet(), this.groupBy, + this.withScrubber = true, }); final Widget? topSliverWidget; @@ -45,6 +46,7 @@ class Timeline extends StatelessWidget { final Widget? bottomSheet; final bool withStack; final GroupAssetsBy? groupBy; + final bool withScrubber; @override Widget build(BuildContext context) { @@ -69,6 +71,7 @@ class Timeline extends StatelessWidget { topSliverWidgetHeight: topSliverWidgetHeight, appBar: appBar, bottomSheet: bottomSheet, + withScrubber: withScrubber, ), ), ), @@ -77,12 +80,19 @@ class Timeline extends StatelessWidget { } class _SliverTimeline extends ConsumerStatefulWidget { - const _SliverTimeline({this.topSliverWidget, this.topSliverWidgetHeight, this.appBar, this.bottomSheet}); + const _SliverTimeline({ + this.topSliverWidget, + this.topSliverWidgetHeight, + this.appBar, + this.bottomSheet, + this.withScrubber = true, + }); final Widget? topSliverWidget; final double? topSliverWidgetHeight; final Widget? appBar; final Widget? bottomSheet; + final bool withScrubber; @override ConsumerState createState() => _SliverTimelineState(); @@ -265,6 +275,45 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { const scrubberBottomPadding = 100.0; final bottomPadding = context.padding.bottom + (widget.appBar == null ? 0 : scrubberBottomPadding); + final grid = CustomScrollView( + primary: true, + physics: _scrollPhysics, + cacheExtent: maxHeight * 2, + slivers: [ + if (isSelectionMode) const SelectionSliverAppBar() else if (widget.appBar != null) widget.appBar!, + if (widget.topSliverWidget != null) widget.topSliverWidget!, + _SliverSegmentedList( + segments: segments, + delegate: SliverChildBuilderDelegate( + (ctx, index) { + if (index >= childCount) return null; + final segment = segments.findByIndex(index); + return segment?.builder(ctx, index) ?? const SizedBox.shrink(); + }, + childCount: childCount, + addAutomaticKeepAlives: false, + // We add repaint boundary around tiles, so skip the auto boundaries + addRepaintBoundaries: false, + ), + ), + const SliverPadding(padding: EdgeInsets.only(bottom: scrubberBottomPadding)), + ], + ); + + final Widget timeline; + if (widget.withScrubber) { + timeline = Scrubber( + layoutSegments: segments, + timelineHeight: maxHeight, + topPadding: topPadding, + bottomPadding: bottomPadding, + monthSegmentSnappingOffset: widget.topSliverWidgetHeight ?? 0 + appBarExpandedHeight, + child: grid, + ); + } else { + timeline = grid; + } + return PrimaryScrollController( controller: _scrollController, child: RawGestureDetector( @@ -303,40 +352,7 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { }, child: Stack( children: [ - Scrubber( - layoutSegments: segments, - timelineHeight: maxHeight, - topPadding: topPadding, - bottomPadding: bottomPadding, - monthSegmentSnappingOffset: widget.topSliverWidgetHeight ?? 0 + appBarExpandedHeight, - child: CustomScrollView( - primary: true, - physics: _scrollPhysics, - cacheExtent: maxHeight * 2, - slivers: [ - if (isSelectionMode) - const SelectionSliverAppBar() - else if (widget.appBar != null) - widget.appBar!, - if (widget.topSliverWidget != null) widget.topSliverWidget!, - _SliverSegmentedList( - segments: segments, - delegate: SliverChildBuilderDelegate( - (ctx, index) { - if (index >= childCount) return null; - final segment = segments.findByIndex(index); - return segment?.builder(ctx, index) ?? const SizedBox.shrink(); - }, - childCount: childCount, - addAutomaticKeepAlives: false, - // We add repaint boundary around tiles, so skip the auto boundaries - addRepaintBoundaries: false, - ), - ), - const SliverPadding(padding: EdgeInsets.only(bottom: scrubberBottomPadding)), - ], - ), - ), + timeline, if (!isSelectionMode && isMultiSelectEnabled) ...[ const Positioned(top: 60, left: 25, child: _MultiSelectStatusButton()), if (widget.bottomSheet != null) widget.bottomSheet!, From bbfff64927bb0f5138d940a65d0a3cf6fbe3bd21 Mon Sep 17 00:00:00 2001 From: Xantin <56741168+Xiticks@users.noreply.github.com> Date: Wed, 6 Aug 2025 21:16:28 +0200 Subject: [PATCH 194/748] docs: update TrueNAS docs (#19990) Co-authored-by: bo0tzz Co-authored-by: Nicholas Flamy <30300649+NicholasFlamy@users.noreply.github.com> --- docs/docs/install/img/truenas/truenas00.webp | Bin 0 -> 2694 bytes .../install/img/{ => truenas}/truenas01.webp | Bin .../install/img/{ => truenas}/truenas02.webp | Bin .../install/img/{ => truenas}/truenas03.webp | Bin docs/docs/install/img/truenas/truenas04.webp | Bin 0 -> 103115 bytes .../truenas05.webp} | Bin docs/docs/install/img/truenas/truenas06.webp | Bin 0 -> 5264 bytes docs/docs/install/img/truenas/truenas07.webp | Bin 0 -> 50209 bytes docs/docs/install/img/truenas/truenas08.webp | Bin 0 -> 27162 bytes docs/docs/install/img/truenas/truenas09.webp | Bin 0 -> 24690 bytes docs/docs/install/img/truenas/truenas10.webp | Bin 0 -> 17070 bytes .../truenas11.webp} | Bin .../truenas12.webp} | Bin docs/docs/install/img/truenas05.webp | Bin 9106 -> 0 bytes docs/docs/install/img/truenas06.webp | Bin 1646 -> 0 bytes docs/docs/install/img/truenas07.webp | Bin 19086 -> 0 bytes docs/docs/install/img/truenas08.webp | Bin 9922 -> 0 bytes docs/docs/install/img/truenas10.webp | Bin 6824 -> 0 bytes docs/docs/install/img/truenas12.webp | Bin 4930 -> 0 bytes docs/docs/install/truenas.md | 418 +++++++++++++----- 20 files changed, 315 insertions(+), 103 deletions(-) create mode 100644 docs/docs/install/img/truenas/truenas00.webp rename docs/docs/install/img/{ => truenas}/truenas01.webp (100%) rename docs/docs/install/img/{ => truenas}/truenas02.webp (100%) rename docs/docs/install/img/{ => truenas}/truenas03.webp (100%) create mode 100644 docs/docs/install/img/truenas/truenas04.webp rename docs/docs/install/img/{truenas11.webp => truenas/truenas05.webp} (100%) create mode 100644 docs/docs/install/img/truenas/truenas06.webp create mode 100644 docs/docs/install/img/truenas/truenas07.webp create mode 100644 docs/docs/install/img/truenas/truenas08.webp create mode 100644 docs/docs/install/img/truenas/truenas09.webp create mode 100644 docs/docs/install/img/truenas/truenas10.webp rename docs/docs/install/img/{truenas09.webp => truenas/truenas11.webp} (100%) rename docs/docs/install/img/{truenas04.webp => truenas/truenas12.webp} (100%) delete mode 100644 docs/docs/install/img/truenas05.webp delete mode 100644 docs/docs/install/img/truenas06.webp delete mode 100644 docs/docs/install/img/truenas07.webp delete mode 100644 docs/docs/install/img/truenas08.webp delete mode 100644 docs/docs/install/img/truenas10.webp delete mode 100644 docs/docs/install/img/truenas12.webp diff --git a/docs/docs/install/img/truenas/truenas00.webp b/docs/docs/install/img/truenas/truenas00.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f3db057b0fccb974dc244a450239f4f22535cbb GIT binary patch literal 2694 zcmV;13VHQXNk&F~3IG6CMM6+kP&iC+3IG5v5kqPKug0d0BuTiJ_=e?dzdHbA(l-8m zPQ%WIOxni3q#4`U%zq;Ms#1Yv1pq1l6?jpBKg}P&*l-lIZ6n3}d2ja+h=>X3s=^87 z(4Ul}{cAgU*2H$xiy%j~?M4yBiq%-$%9{w31PKfoP=w&uo;6h}wk`_q{kV|kqxW>h zFWwir5&eh6wvi)=Rl9qs=W^a?)QjT(|L5E2ou=*;^G=_p&3g50+qT)e*|u#fv+ZK& zkSW8jdH+kvH{V~9o%%gIBKi-BZ6ik#dulID&v|p0P1m?ruh;E%0z=V%^dJ3KOmS!N zRZ^wzEc%cBqyOkX`j31QZKMARze2=+27{QVS1y+Q(_pR0bd&Dpsr%;Cx@-2Xt&7G2 z^t}=3ZA>kb!mDr0eYmFT5|CWab`wePf_V z_Yhq#dapGVSrY15Cy`kuY(X0BQL2Gjzdv5@`Ot5l+Y&s|-xSGwt_p79y&^qBRQ3^> zV#L;iXmkUQ*LxzQe!sM>oyaxSGxhp@qYy(yM!zv)YX)`t#1XfyqT5Z~tGkCbt#2lB zMfF_0O*lu@Q6#(62(Bxit4vuEFxUHgcYScC63>P=l+wl zaCx_h6qTC7$7TGkdDj<-$ZoIqcw-TfPB+G6WsM7)cc92f11rmOLU?_1`@yJOV>iq! z`Wz3UfA5h}ec%;BgoyK-rT^$Z`j7sj|LDKJs}PY$+y4b^i!b_*{-gisKl+dDIG&Au zthr-rmGJj+{iXyjS6U5<4W*jouqGRE$Pqv&?D9RxD`hR(J}_8x7EL_Jcddb*eQeCL$W zW*uI4pJvW_SHp*P9yWBXk3g&;ZUdE>v&!Xi728aAi(Rfrrj~}5^;+f%PIt9c;0jn4 z!Er&uddkn>spT)^)9yLbX4Q&IZgZHWlYdA^?)ZR=C{2mtdaqisv2~}sRgIgKnUL7< zl+pTPzAMlQZiTv~)O?07(U7G6y~ju11!_6q035q&I~)KKZ!n@X#mc#-6L0`%_(hQw zqomkaJ)R}sogXq?%a63$R~oK_;<{J*mjll1W!C#nKu3m|M&}Zce$@ZC12967GRRWb-bG?x6XuU$~yD843q$;&8LhrpR;P&W11fzx z;(%l1yV91x*2iyb;uyyTA!a^z1^C`a^0mrZv!;qcMyQ$3 z^|Dg}qz|39Y?KXM9K#4U^SMdDA{{FcI7{+~4P6|w2siV&Z9oT-w336$Lhf|6-G(lX zQ3Ra%ob@BHI$$Q0jac9q`Kg)BhAxgt1fBWZo*1BBQLP$jK&g^FzEQi>nyE2ytt!di zfYkaUZR3~#zRP@$p4tGM{oM-RPzQjdg?oMi%E*e24j{)ijtStw%;%o%_k7mfD0Xt| z4ZkK^cyUYspJqO1P9DwbkId&Ri8nHzGbP^0e9n}3Bl9_%mk3wdyer|#5OFU3^dJ34 z|IvT+Ur8kTpV6E*L_C=OqyOkX`j7rAGeZ9}nxpp_&4r>T`C5J>&~Cr_C&5LiE!MT5 zDm)cu9~$2=n#+Wx`%%z;n$6Q$q03BL| z!1{1x8$yutG3V))mQZV8nd)xWwAiG2HQo9RB|&{FpmKgeQfyksn*m??)&lTj?hI`l zGY4}%<`Urmkb0IRcRGQ@7{KkL9@Bs)WaZCLGCH=9KB%pUK8}e4Iv;b{$N6p;Q1>%g znF5@muM05VPGjIC*$0lLzRIaU@@w+V<3!+5={uV^=50Z!^D%dVBs&AE$X!4ql4pQS zJKceG(8qZ_b+B>gWvn^(67mg(5UF>J8sV~#8XQb?;Nu=L~oLST-aV!%Vb z9hU$Au%U}%+?lNxwU}y+x9Cd@f)oxe4gSfDwAy zIA#s_e9U$KN}h{@vz)vHbbe#+>+$+Pp$yX^* znzPQtHws5WLOcL;drR_b9+1|mX-X|1b&z`8ui;*07pHp6gYz+0k_aGa4M}pn6Hccy z2EZ?$coAw#Yqnht`J@>D)HylxJLiuiJvjiz*u^ocV>Bn=tGcrHzF$dE5}$ILB%cYe zH@uk7_Uep`l&6r+7yb4Gl8{ z4GlXU7aNq|l-jt1f6(1D6=cy0dZ?Gdf9_c7-Lq0vMPma$_!k{jmWlEEx0oTBn7@CM%W`|jTh9-@6?~aVNCpuyrE79o3(cu;;t|Jx( zgkb&p7`}|sce*S2dM@k_#$J3SyR~j$6l@ngL)1LZQ~|+*Z1*1Y@t=QkE||)e7EwtHVxrif&~FV&MP#ZBEIo)X4ZV`KJq|w@9!Ev1=}qsY|0{ z{r(n2jZZ%l!FR!e%JM@*8eK}-I4$G z;r~abjr0b!iCd@1_FRk_t@pt?jpXU>0FUqCW|fIThNNF(s#^~wbGqluCUUhfJi){} zui&_Mv?nLkwS(svQ4%h7GEHpW7R~T|XSJ{{l2A+awvO-VQdZP+kic%4Wth^8TupuYsXcjl1wvAm+?Wuhh9XN7i{sXz}zNQdP2R1~zZ&G0(&{s!2`!kLII;bMGSxMg^BA>jH#V^Gh1Zv{b$?P^Y^>taVOO*oJP(KSOf`j*PN=Ba|jBnx0THG*=v(rS}2=j zaA?S`=|f-htXIrNy5d25?u&1Ia~^je{vkms+R?C5WY)#oN|>AB@6AARvi4vfE!WvM(bB_R z+nEwV^{vxx3(iuDdpdjW{m?Z;!gTe_!<>KG=7trw(tbs__oZ%wrVxB8to0tTN^w|8 z#^dzqyDpjdWWr1C5k`ljjfY`sjl|JhOcUrS@(nv!XnZ-wAV0kSGNXLxPOp?yDtOXC z_i)llVr}E?+}$emUttY=vni6+!U_5Gx!hoBxX={AlHhl%daH%XqWqavs8ijkN67PJ zivwbbU3ERj%K7A3=p*r$szT!e)E*<|VKvdW8Nd2~p$R*f@Zs(obWJe!z=P<yyT`GGJZ1d{6k0g>X2QzP*#+rcS34EG)DDW`N+eUHnKYg$y(>LbmvQN zEPP0Q2R7-TbjH5H2D4uCeWf>Os%T)^p6uG$pCw&_5YR>Vmh?&XN+PwPb|MIsiaJasmlWyqw(Bw-`~rjEUUlEa~Di zuh|=ViB73sNGx^2BiNgL&)PbtoWiMkt1;ftF^TB1W9`wx=1;rAtNz^nO(ITZKOPjr zr&641;f^t8$VHcKD2Z)B`xGZ@h;R4p;n4jYm&}ZxZZ=5#Xoa24^t|Td zFhlnGHllp6cQieqg2gv%y00OtFMo??qn?cvTgMd|f2@@kWg(dAHkE9Z?zJ!?wGTey zxydd~w)jpL|CT77*WBwyNuCKXZJRI%I7o0Ps&@O8v*gUf&TEo<)+?Jpc%rMAgr{@Z zm%m5Mf&s-35Y`Kfp9Tf)~|bTcDz9h2E@;tA9=v_6#)x+Eh*9p)-lV;b|<$B zTkP@rS8k-=KHNHNZy-fvA(7F(r~x}j#{5I8_SwmzRI6Ps7oGp%wDUrebxzbntr^6m zmrnq(-|_w>yTTfe%#4-Y0X4dcgrxP5x`37Ay7N;f^SWlIHWfEovK4|)IuG=jc#vEk zsZ^LE#=cw4TRSsrr^bJlmsOAn_9rmT<_$+l;lnL!MF{jvHAp&DS zffni7cy6fPQ8= z1z*<I=eO z%-huQ(eos$WY+s?yqz2+rC$j!xRW)k3AEfmNF)urE5Kq||8@ojB1Y?LPyHVE%Og+% z8TtlqHlMXJ-4PH#enPGBa&T|4`cBse3a{bq@3p7yORJ%&cx^V|UL=&BqJFYSnj-|P2PkmvI|l4VK~CH@1)KSXQi!P zEj%ZKJy&t64|(E4>}Bkg@A;7FjDcsKLF6jT9E#~h!u+y5bek-gWnt^<-jLzIM+cKu z3X2GyVIwyzJ`JdBVq=Jk*7d%S@NGFFk@wxjKM|Q-+|8Kzv39fuRy%KA=1K8+=Apu9 zkaYYM6l^$`pkZz5#S3{5Say22~Y$qd3I=Z(lGshPdAzD@;efb`{U z-X#v=MQ4?{y{b{+0o#VyLehU+t1S}toFiw<+iWH|Hm|?Im&ICfGCFy7jsK0W$afQS zD%gvSx^w@0;*}Q>e5YotcKOBf)d#Q;qL0#1l{{XnckE6tM#IB^jk zVd6=OS-5PpLJV(mo@EIK{4#etM}Jp-O@ngPw%*pamuC!tOa!qVthuRI==$1`?;A12 zREb%u7tK#&)FkiGk(+o?Sfi2CzR@ZVWe$WoQG5%NZ3zoJf?H>B+7EwEvFBF`zL{(} zq={P(3!?81DuGJ{lko))N`)On+!-~A(O7xiWhO|fV&-wY_Juq+R$_mI5sDek7$wfP zDQD)H{~HL$mbQINVm`tj3G{rSV921l+~vLO%`%LZ<9%7`{t%^i?o~)=AIkrc55*8FdZgeoBFLFy_|FnJ;T5Y15Y( z4Xc2D$YZHh|Cb7NbM<*7_E<@c-cOzUu0Usu3xTKNAZE@q#JempWri%GBh{o%W4{#l z*%xH>$F;Ial4GO^Br_4uKz!iR(R=OGuAiaii8*hz=@a*rontU$;xAXz_#lcM_LFk_ z6-OWMCU+C|KiOH#2w1m?fo1HVWh5`buK3B71kra^_$Qx#UwkDi9dn6frtw|Ch=L%= zoCWL^LMjdf9`2gY$95ik?ig!ixH*{LiTOsIUeo8Ze3tz%l|>o;k=QtuPFUgKUGO3N>lOw? z?`7ko{j0A1-mo*TLara*8S2J>_;(rV8o^Xg4kMU@n!|7=5ONNWZs=E=AZys8;v$p$ zy7zzF*wLrPg>mUqEo?9nG)*ziD^=Pt2EsAceE7sy6PKJxAA1l z?uT<&SIP#oX)+9DH<}yK~>E+n@6FxQmAfk+DWwM7b_5?sABsBTqac zfm5I`f3Ze=r5Z*#3)ZM(ESjK0leA`@nVMFg>--O0Q_dret=qcCPc&R}xwQW*7irXT z$;P;$@=`&7$E~RUc_Bx83}l zU_u#@YmzOD`>62Td?-XiGZ%n&SgywU&zB`1ewg^cF|YYWU*cFY)%yrdh+##%(6lRQ zo#AlqcqlY8X4{iQGnXxqHPdV3Xk4J}?exC3a(;i-^-t&OL%bhnG`O}kcaL>^)?J@2 z97ATuKWQ$0!GGD-EJ|}xYZazcm^4HHQL!SbbRs>vucJL3Tw6Cp~U`pFKJ@Np=34QC&S^su_H|a`)`2NeJi@ttca5R zUA&u87Ud8us$fKritx%!o(4~sme2h=ZISly?}E+fEHcU^*RaLk!TD6pC=p9NVjI+! z0^iBZzCYm^O(+&Ce7>x^4>m^G&Ed2~&F&fv-}G)@C$fxBb$>po41agY>Rs7PX~#aH zdj{t#+EhlL{6`bkAu{EwALy)nj(Y?*X1yt~N{!w|8I(i6baOd9Js?zU{P14IRgJ-- zS+QJ!+T{*)5WgzB41LAv{%FD)!EoI0cCOSZ>;1~t!P-b7zIRLZCEeF>w9KWiF5E_Z z)Et8*28H}&hxeu&P1tN)KT-J&ES4fb=E8a~**;}P#`O^%?7EjNga^J5Pq;X#@fJQ& zKI%w?`5sXPN57|+PUFe+)g-U?#TQPKx~~D&Xc;MvRe3zPBL1Z{9>pZzSX^4+QY$ zs}ubL8{bcL?K~)`qf27WK~Dn2ZWj3|AEspf&L>%OyFLT^hB;ZAEAck`@(q_8MURGL z2l^U9a9U*?KV+N(5cV=ARoA=L)c?q`S`xXc0do_xI?(7{h=xl>5C&?EaU{%IVHO~~ z1ZE`Ac@LgcIsHtz#_Z$d%r!3`iychBb$QqOk;0Lt7=>0WDigC;J0Nz7;d*Is#n zA$H75D)WGH-*-ycjypmorl#c-?visjVi>jGvrZB1|5jQ7=c%*Ky^#maSo<79lbJiF z_&E+5G%6-vmXYS#!5XGI1UR(dK=gZJ8v_-oR_()g?DPD`qTi?Y&?xxYd=XD8KfU$`MzYs}f3GOqvzSGyW=X@1)3Ls@7qe zkjlO)sS-XVPx1rTI`s;9jA)qUMV8WzDr)X3dstr4!8dwcC0Dw7S9A|cD#Dect&k2L{IK>4IoA`CApHAh2LM6%qr zKnb=Az=o0NCDW__WCDYXa0T4?HRK<+OfbH7oOadvDaE40l?2T>e5M=^1JNwAUJ1U~YrV}; zRFAQij^&4YFd1+z=%#!{v4j|d7GG8Ms9o?AQ;P12)2H!}xT9#qI_UUoA!0p8i58J~ z3N-H8hkzi=B;4kh?Zm;3XE@B=3`6sy2O~Ev=u-nI7XEN`%XPbNGH!Z7=LAbH^;F^R z!^X3dm@fS3H{!EH zU^i^(U)xK6^O68C*&KlF2;!M>_jKxn+F(o^B0}h@X36)N9R@Q7vzwc}RuQmO3BU%; zkLDAIyLuBA!rs%FSFM93$t1{P45;BKLF(B{7U?uh19pu0||!qv+EQIR~bGdpc$$Ndg#bVoe$LHwz1E_6-oLUVSn+VTO*C zOeSi1{$chC{8qvG*x&QY`o6^zvJEFQ*Rb%Y2GO@fiEk^Oq`ZZf-u)dV+ft+pUx!3S zw2j_foc8uwvb@=o9dE#9)lK0XMsEi4TdGfvQrSZ$oeKmmL1R^rIQ;>ijQZ49Zg{rk z9x=k1Y0vP;lwVcoKN_{tlb2SAu3mcgvL*1j$_SVbq8)iIX@>U2ce`+Nh@XHg$QxBN z+iTT*vIKV%0Pzuwls9n9Oe-xkFlkrfu9ylgmMmsb=SZA-K|_7%mqdb6eT=iy9E^pFOn zY$LTGYyzG(RkqXPgQ%>xIg=%Y5!X7WZo%_pmIxkc{01mw>Y3NkQ=UJ_nyrSL`CppK92V;8IVzpmM==n>{X!5q!*KEKPw*2dyPU>- z=6}_o7<&WP;H7`uT>)fCYPm8GE*!O=LDUb%j z#emLpmiNB{$gfWpFadRJwb07^S%s*&Y(&A-&iQmjP4QxJHK=3ul|=CG1p2R!=z9!0 zNmZ9AJ^Es?C#d7Uj{e=-|8?|*`S{gV2kwh~emCm~RIcf*Rytx7*k;c! zl1+U0g0&G)4V~}h1p*)I3+v9VdmlKij9LuFcs&1mxodOu4XPajhTzz>ZCUs8UZMpZS=L>-;q#yfM9oJxN!1Wj#OtC9!h1H9(Ag_gkpqNc}}YgJd3Gr*uiTk zX-P$5Z_p*7?qplpbLKfqqu;?ed5`F7zBhG?--Vth1^C~ph+h&#HWI=6H%>K#G;K~d z&2qsy7t(#!M`CxuuLvOVP&(GxC@FtEt0c=tj*_k`D21WI?<8|61E_Ce(J~fNlfFBu zo?z2yddXcoV)YrY1wj;gafr`0l{`P$*&NhT%Ql_$MiH*DNuWrP1Fa)z6J+wPN~$?s zGxgm%+<&b=i|5vTTbFR;DNrJN`Hkfaglc*O22{*n-@S&1QX9DIPFxX~ZM+?EK#yfb zfVS}xcn%vs2}5ZasUR^nJz^2X`Q>@%Z-#@4;F))^yp61)ts;T^q)Okp2NJ-okM4Eu zgU@yz)KCZmDi*H_PXi>#--XJl+T{>PA^_Kqk>D0EC)>QM=+2Z0X#}E4e~tlOK@X5u zcz`Su4#Yy1bqiQKGXaVt3InwYzk#K}1n9;;?Nj`L-r&=?km5uekOo?2k)hbL2ZYHN z4de|Aak?gyL?DwrqQdX+WT#uG^-J|S=)>YXj*vNLPB5T;6+kK+_*ndW5tV@Hr3+z@ zdm0M#D@k9Vo&rARf%}URj?W0~lFl76%17XWtd>%BIp>!#KIe%)xj)oSwj_cf;VAHZ z|DzR1niHLL0SUSIoT}k6<{+YM*u)$tS}!|w4Q#U$Weh>T%XPFAs|CMD>BsB-mGkig z&~udZ>|BqI^Ucrlcz}0P_5AE~^u);hhYqja&BXT+lE|fLFbG7D00FNwFe93%-MTJ& z02O#d!RgDm$IDgAbB0-w77B}RUG%FPr33eQGbLu8G0vRzSJ{0odti19=oyy@DdtMF z-_4kIUcgSrN>^?b5^xkrcST-to&ZWXFVFks4}ZuC3Ice?OBV#o@EJqV13;!a@!nTZ zNh3jsm%Q`55hF%N(nm)Zx8hQ@S3h^OHmgXkWZNrRZauuA$crP?9t- zNqDjCcQ;b3l5J-`5}q`QykQlejo^RyG>W*52xcm;KMtg#lCKUs>6i6wv6UBMT-zAJ zLqC`QWUD=(`x|0`@u|eet~;E0DR}z;SO;1rUnY=}Xe`+!;Ok2xN8?s{pLBA5`L>YF zO0PV+sidXO_ZW1mVOK|X?+9H;3G|par&pL8N!=qf>1(Uu%PFaLt$*V+-+ZiAi*8TS z)MgM4?Xx=TbB4qD#N8+Mg(Q!2WcddN5BNBD{v|q~M-71fdWl|3H4Yk*j_}wmVN_1w z^YfL@5W`La990+gspY^{vi3D2NDjWA#!R;0!(i#wXdkj9kh#$zjccO6gEKIqTgM0|Vg@8>x7WJUPiV{lctl`yyQs((vfxSvZ|PF2t5_NVA|Cx$?#*=<8Pnw1DEE(?DAjx{R5C2D z(yr{-+3&)T9AQ+h@arkNqvnmv&E89bBWAJV)ZR;P9k4CUZ))T`tpLM?sihsuw#)FI zkXqO|HcEdV>>920Zn4B-dYi!P@D#k5aDGXTsLRI}h!sLTw+1ZoZ!%C=r(fZ3K!l3I zN2EObdgf{T_)DL^Ok77vmOMYSwVIg|N!7$%P{OeVcJ%iQQ}DQmX!0_e32*dd6}Wv(^~KJFWs zb9#S6e)>s_8u>^LksFhcAEpwR)jZdiz#d6k3&wrriF)bXpLC2ND9Um-V`!fp-r3Ha z!V#Sf4b^`6_cnVgFt8!>Am~C-v>ZB4bh~-BPx}Qrf_m`pz6P{N4j^*s2UQ9A7s@FJ zy@~(c5(6=1ot--pYkP z1xc;!l^vUoF8ZJ;phZ8T*K>ETx-eFA_X6iAi%EI4%d%NRWcY$F1O))eIv}E?aH%QdAXBOo{;C;3ieX6)RAA8=krHE`_(JSD7L%&sg3kv@d~IzFF@G5H2<^W)*CE+1Giyu{=o;uWlWOz6TsJtq`Xmd&Iso1}h!6sFDqh+nCa$mexd?yqo zO8cH2Ee3^;*y}&a%WedIsdQuQ{Zo`(>?t9o=KhX&0MAdGtS|r!q#{5Le;WABUXqQ_ z0-x8`DR9?}KV3xOaX=HiJ;Aowh{BM-fRiAYv3Qg)A8*L@3onI|-v97CNECRO>|_f~ zYGfrg#_?EEdF(5upKgskFmh>j?h#oWe4Ot1Z=CAe!>Gi=MxmWe;5uX=q!E2gHqYBa z!RX(DlRs=SJ^fSD!0aid6)p;*dxH4g2n232-A2JU`}mjA;YgHi4P^yt3Pi_jM8}~f zd4btd`nfQTbjm*+<+avWuj5`n}gv)0Qjw~7yVCqka!ugGh018{{ftPOr z;Qe|5fR{xpA3r_Cx~503B6Yfp;u^mgiMcV-sv981w*lv8=8+-`xEs|w_dQWL`vEm6 z8cARlZS-DN^_Uzs@o`9WAJVs2c;!b*R^)_?j{7ON7P63nGKtVvWWibVYk;4LFM3P4 zTWX*RuFDr9pp5dnz+Q^^o&&nl?2huIQj|hdwjIB&m74=o#-+uyCnhKMVOJmEQZE8# z6V*))|CKz3e?s|Mn0lkSW>I?SF;gwTEe<{5IiFBY#zdgaWl5dN;N4$tI&3C4E!7t~ zN_;tMH(ytNOuu3J4hfE{lxN}Y?C z44`a-6M*7+92;wHP?z*eCV|2CN(AF4H(Bd9`xX$i@sWrGQ&{Zfel8N-}5!VVy@5vvqc5h0UVpko@B!gDe%w*yrlejh3xZyYe3d1 z>Q`b9v&~6j9wLrBIW<=|dH^I5Y18RQ{BHA?4iz3}%tv2m^~MIq&QE-Sl{O3E3RYv} z>h?LB>C;WI;hyplU}-zY11xLf)@U|X+G&dN=<&yYCdVE*ih4Ji0^?*A;_%%|E_@Wf zGv3$fO`Bt;P?1sb{)}_OZ|-b2S1Js2Pyv)j(Uep29H?fakPva3lGLCFxR1}a6Y9wB zhZEjs6W`77_-X4x6Tc;`rY=Xn35=%UD9I1|k*%Le$+1=1xW_1`e^R~vX-xJI_LiAwml0I%*Y0tt#{oYP%>@9Yga;!>32(;hxwYLcg05Ys5@q_Tdi_}6seCs=FLnA?!L4LfUF4^ z8v|Bk0UXcM;Zgm%kySV9b2o0upyxnDA-fO>1zIm?e#&FV>MQls?t>aG(ut+1-cwSE zfH0NujNC`&*XZsy+k74~gz%&`p>hJp##aJ2;Rdt>;r{yjYnIS4IsrqDv7eM zxh)jdB0qLJR^kX>OIrUWh96MLNDlW%G}p*190Ne(R6Ms&pIu^@ha9eZS+;kW-hh?yutI{6ZI zr{iD)j*(Ol{V=jH;FKNv0i+b=!K^k3b_lMEtz=Au1NU6~{Q82hW2B=MzlR1zC@k>ZBs&_Sb+mPlR%%9)gW- zR)E%spn+1qX2w4OI;MnmHcwOvm_^G@bEpb))@F|V1TfF4JC+D`RwfXME@e7{kkovf z;2#drijY<+PpD7fxXnCfp`um{7E4S0b8fbx-fOk>Q?%dG17lTg2eBLo58wAl?6TND zv$**nRq1LugD10c`lZ;);XG~(nX_5{sUVz(*OI5wc3KnB-Fa>t>l;-_%5y9zQ`CAY zCRwjjVHmHM+4HX$oGrYFTHE*sVW|fMp$3u&^1VZqM=?G>tHC^xiW`eL+mVUS0xGkj z6Z ziVW&EKQJg7dcZ9e@HFw9TR>{W8PLFRuoHF#4K3zYY+B+6(aY$U z9|^9yL`8x?HVy0KB3PCRh>vN1pLZ(&)0eeA0W!wYjKz;J5Gyiw2tgW=1_+&;LPHuZ zj!+*((5_0fU(bg*#co<$*GpMCt_)uK;Sm(7)g7ms^t!#WF(sNo^sSiz^kH51@FBCo z=ch5j;*2u$nkH+Z!twkrMtm0#~%(-8#yv`Ks=nV=~-reS9O&B5qpq z7_?Va@8H&KkgEchZ-(4C@1BvBkDw2cmGIE+-?Dw69586lkkw@rKo0Etx7-tq3)v@Q zzH@PPvEG}m8rXa9-sY^;3aN(LXf>qd-wI%dOY`{ogT&T1t2|$?{t1b1Na!QHA>8VA zG^_J++@J00u1+Me*T{Q>umYDiN<3+Lc(H=vz5TdfeWznNmnT-cCXGC#elwe(7W~Z< z6=z*rTi{o!&E-pACz;B~%yV!IggI&inI!7NE zWX+-Dp2EycmB7CbVdiG}sZVnbXQ6dW7B&w zB`m~a`;w_>A6aR|`&zGTSSDmq_zIy_-IU@1RggiYWT3rVgE*E+b1Snt>R7--dNq1I z`}mcY=QzR%noQ&2^T7wM-F-1w2eNEsC*9e8hrc&-W0vSEY&4d*hD;=Pddzs-&Pm$d z*J>m+U-C&wZn#PpC>^1KOWY-{oxmQEsmP;P_hmOad5H6N)x#}oh=n-48zGD>kL+Mr zsdIoG9%p|~zrmGwI$wF<0n8fS$cL}$WP3xAt^<{K6-?_YXgzj)(7dX%BZe8?hx01X z`8iYkGb#J1agY$AJyx*_5f6sgFAc~`E1vWCx83}Xh`BdXlEe-R&O>~e^j~&pAAUHX z$$nY!dqk2|1L3nKuN}FD^e#?xNB&svAzjxwlfrMsgMNzYJ>|Yf(o&6@j1xKa)irf# z0O|`238_9i!U#PJf;ZNve~iWFvO7v)cPC8vM?C-nizq&>AuGupU<2GbQ2C*tNSwGE zcf%*zp(B1=|N2(Rpupwj5(IKtLQ7cPrZy27oZW@hX)CY$aZ|cm&S5KrGw3qYDEY_1 z{rULI&flxwnDL0YT2}6@mZh#{+A;+U5`L6ZSJpDa;74xp3b&j1to>kKRXCT1B|_yW z;V}*rezE>GyLc8wSFxYJy{VPDtflwyrWTUzfu>%R1yAy=ER+7h_tePa;uUZF zgjPk{Uw5&hKgoMT-D&BL;&>DY{yn+!I!h1J<&A=xI+|K>^Erd$lI^>)oJ_{##~=2c z3gTz0KL5kr2btBm=rsjkTIF!xfpN=Y!iq!_S*4e~V9p8kxfM_RZ2Hks1oLd5oHBlREhQN_&N~ zKebx&`H7a%D~wD_Pd6;ucUsJfo*pw=&Us3+97l5s z(A`uXfXiwLCcoqTV&I~#U?#J37hguYvXSQqP9Lf=T_1!wYsS3mP_`DV3>2{YtM1iLaS2Q-hh@G( zw}pjxyzj-eZ~u@6WGIJVLrInqrBnJ6KSd#sGpJDYco9)5C9japlc`)z>F|+mA%}4w zg%gq>u>7r5_*r{F!o&bateX{E#7yQh4rcc|<*b$!{T^w4strutmRfGWHo<)8_n84?>}j!%vA_3u2y`}YNSCOBV0wmqP@v0j}?vYQz(tc$lJcqHxMMHtTJ2H|1LRr;kDQH(|Vb<^ZCLh`}+U4TN)N$zK`EUU=IT)?yT#jeq)S z$w{%O=~jlgI_cE=vtSjQjNlKY5{tdA3oVSBS$VfG-$k=BOj$Zg%lAV0klLDkgBm=F zMU6*R1dP%-^v^iRR0Wm?bR6E&Q8rM(scVzWAFE-Y!|p=aMHYeot*G0v{l%M8M#iR_ z3xv01Nt!c7cd6#8*QrDdSh~@*%&YMB`OWGBDWu;t-<K^haKXP z(A-3$^wq_R2wWxR=$$McI@1o4W-QAy22YkmmK_Bx^{!>cR_U9BfpR1rd)6V}`M!R% z;YFu7ZpO9fNu$rxX0CcifsSea;&IY6%fvwaq1^%IEc#6Z2a`ga%kiIII;^@&zcM zbj?Y;*bf?>(TCVl;r$ZoDF#^PLy3A5VB4z+l9R z<)j<6yx}|n9;|uHjV$^3l@WeUB?TETcv`|?;8`@33hy07*R$gkZi=cbl0J=AXh2@> zpjsUAU3!vfu`pvtrPk6wC%x1>e!u}-w(nky2J94q@I{wviL1@(vK>i6xO<#8Z+tpU zVV{_o{GBlk>cQW=o}@$Z9sY`{j3y~od+Es!wlI5Q;#}z9N?h8#;rk>?Ew3}*)daEQ zQnxd|*?U(=78Y(?&Sa(u_f3&5!TX$wdlmzeSgJ zGXg)tqH@(-*Zw=h%XS4Ml%KW7DdD~MH{3(yK3Eav`g52GB$tDb1S8fUFFanQfYjjq zd6Uslu*!=n-6z+*_u!2^NAsaX6XVWjq4fp{7d~QeU9HPw{&dfCmNS z>#g3sa&a&Gs|?5yTBtO-`<|!k#bOI!UZa{_G5J0EzuFy?16+jmD=S-b{|W-AcDCSx zI$D0zT+ElNa)LDa;E&n%zwH)*7_TmaIwC&#{B0aT$4WK;De$~ctucRh06Nxnlo^vk z_ux17{GUb&lz}w5mTnYF?!O)IKW}JO#9Od_L53x>L3t{33TV~t|K0awHmGw97DnTr zE(jBSgDAbiSRy_3Vlf4%qotzij~D(|b|W(nrB_sWi;G;;D|j(f=?M4N$G`IGuK%wN zymk#GpL8hNjTD)i14;J9kA5J!Tm!B9-u6M}c&FBnp17jemw* zJxE&13Y%TnN%t@7E$R(F$bdn?78klWR0cYrRL8Do=ij71N|<;=0%kLMuj<7a^WZ8N z?H|cI)fbDegXv{g@1}LJ<8(lO8kQ}HJ6$Zs2AzL>bL_&}A+yl|r4K%f^cr61ng5#n zZ+8U!*UNu&%m3!`?}RKWVE;EU{@*t-f+lX;kGiKsPKXhvct>uYB058-WA2M>SJYhh zMO=P+K)w;aWl|{>u{Yhxa3WgW(CT(@amyaY{~se`fdq6uR%;etN^L&57+cFA8SEZ>z_Z)eYx#>A4R^V{WV zCesQS`V(%Amggp?MJzTu0HUZeI?Wg#A>n$K*lzK zY|uIn)FFpN;HGRs`ASEuK;9Y;IQ`u8fC%yHHU!C&&8Eiv+JCmppmng(AxzHW#k^6% zFUmQGQmHplJFf3p+IYbXX6fMzfa`5DH?E$322!f83QPIh=d^p~wnXTBRzT=<)7cxL zwl+EAKpt$+aRFhGXF8O$B@P#jSPW6Lx!uXAVCa|&&Zmh_PlXT@5pb5O6B2@ZHClkz zp6W@3vDnvL#(&23LzeVtB(3|H3;X(`pmsL9{lO1*R6zb~0`l|NbT8f6X1IKsZ1}Nd z=Cgp)Ra5gQiTx$#aRh$aIM>%GrAd}1)S=^d;4X#%L5oNctGlCvKG|83qHI->Kp(%l zk-Q@b?oWA0D&A8ESE|ZD84Rt&%ATeXCl#T5!s;0k+0o04jENWxKsuL1Y4BIE9;1Y8 z3UGIaKtq;Vh9vKYxlZEWHl)XO;QYk-kA~~~5#%08Px_sAd?v$4GmhA_(^K+Hna;ok z6+{6?ou;B=4EprRhWbhb;J`6!khJaL=CnEL>KEX1y}Nw%{L5qD{3Hg~ocNxsfyqRC z4*u|`ah<;8P0VIY(Igq9t4>Vb+6z0ygB_rq>fmBm%~XtgMLcItzBS}1WH=)?zHfqNPUfoKEXXl5lQcr=|SE%duu#t-h`V?@~QqqEpI~u)#3EBq1T>+*_ z7?2pnCy2q#LHAFnIP>SI{uzoTR{&F9&w(sqK$$;jXoh@wh1h9WM~d{&*CAXJ&GQt^ zOh&T3tyV42mZG)Q+BoTGzXF-HUJ_pKLrgwv0c9M(tk3Bd_4W5jvuQ5sO69WZ2IBc)gLEaP;3rb?>H0c-E}FF$n6{?AW5 z%@E4wTEF9`;HaKZA0ifULsgCQpBU2Thfzt44G!D?*la3fY)sn-nDo2Nx7`L3p^qK; zSYL&n0q;R0Und9YQm(dR-wTvo!)iz`A^-lTIIxnx{n<7GwTBP8_AQBvfZ4Vb4>AdQ^>FL)fGW9Z`w3vSKGQ%Yo4ScXJjvkVfvMm|9bk*%@6KZ zE7q%4XU;lq*JK9_1DW}{3ApM)dO+|c#^;;TMII}8x!B1jeB*L8ohTK&qz92;l@1Sj z)dI{8MawxUEpmK%MEbR;+}>G{c$tYAK0daHQkc6|fD-n*hoKsOCTxbZBYz0{!EsHL z**g}Jv3V)F@F%rY>DS7-*ok$-BY2BWEB13t=-l)0@~96J zH5&MJRTW6PID-Lq|LAnt@7$)b<;cYW;s+0=MBU{RNn8x?g>Be6R<+%Dc_#}Tq2(7v z+5cQ**K*Un*}gtXIT+b45(B1MZ0za7{ZXe`k*TKr`3{u7xP(RE3!|OvgQv_-nt)H- zG6t1^CmIg`ev5M64z9(YdAUmat>{au@^nf=8Nir2Bf{g3M*3{$=Mo<Q$$N`xTyE*~iMGYQSM+ z4jlW0&~ahYGr>05T5u1~G;k59e)_Q&S9iAqht8Mh#VP8Jp4F$l+*M2OglixhtSD8! zV!MNV_QP93kLF30{mytGq2H$V+x;FCxUCj|)H;z`J>Wdm{Ra+L8PF#*023YU>G`>h z$7gWkL(`^n#h|8gbGRP%Wd@SWNd~`mT#m!(hU7p7L)-_4EgAmes9o+5IEZJOn2A#n zQLowPJg0&X%nlO+5I5i(A{Hoq-z9M{wq=bnf&;6wJ<8=inm;+g-F2_LTX6K78}o zo}r|hwrRVeiBn7^Nh~}rD?yD6n&mSV^|@qB#1q! z(Feqkk}tSfyO%>rGP7SZwUh9@Vy*LSn&{GZSq6KQ2aIt;?)SVOR#kz-f{9V+>+tsmGjggXBD?J?y4}aa7p}ZmOy!?N+2_;i4`$92H7mqmgLBT6EsMX*BT;I6># z7Y=|9t003a@hLmqR__+ynGRU{B#~im$zMZ&e%gb2;7p%0T3PujP(tnLYoKp$BE&sMBPuI%Q?a9pcdWOZHfv@U6YX@|a5x#-9U4z|@J(d@=ZyPG zILnqbdu;Ea;(!LM7vi+XfrwcM_4U$VJEW(-8KPizhpr73D{Kv68rd+DWj7O$M(%Jl zT=6SAR*or2K=$9@WE^cvpx)zv`|yjQsra;J_HyexBLy`RqoQ*bvXwl}Pw344pz6yu zaI1G>T@o&O);V~MqWt$MJiMl2Hru1>tWA}8-&`J(7*N5d6l+bLq4TN1K&Rt~bXK);(ILZv)S=1*2;jwHOfck zt-pTCo%%E$*Ws%;)tGseFhFhpO^_uUD};r;pyDB7G8SWaznoE65na~R#_3c^l3h0- z&ff4*n(MK$`)i~R@LzIfIj&L?=2#36$7Sp*?Mr?r_y0NSbUP!CKi`TD?%uf;b(RE4 z-<{5?WA!sz4p*4Ef3BqD6C!RIrO|zCAJ~E>Pjj`$Iy>&ci%(lmhD_+&l;D0j+)<*U zPc#0x7DxIN3%t3neVf{JhfwXM)!q}ImJhunS>XX(KZFq5Q!LXZDo>rmY+Ek*9BgQ* zGcs1}VA6#hgva;Zxb|Ru+Q|mlOwVF_M>SdCN@$Kf9&tL2MC_A0+U?MO0iEnVGT7jU zRJaB7xX3a=7nzQ3w#2PiSMzu*vK%l6_a<eSv?t=YJ1gpmkp)yGfe+(z<4$mfD^RIC=C~SVz_%!9v$iYEN{s;3sj? z9eFX!T{!9_lf9s8ARY2{jy;WPTnXmN;A-_@U7$>GMQhU8+bkcE(Z@A|OIh%p87D@p zNP$z;17i=G$>04kj9Uuj!R^-PK9l?)gO7V0i4gO2Wv0JLGWH}$l9BGAf4BgNy^q{h zw|Ede`lw4Q4Y_HF-R{Pu)Uln;MiOC)<~{!Q*sJvX@TJRGl&8@P@8Wtoq8aX$es=N@}Zf%}>evz0;?>v`B6xbY4a2 zVQWEs_x)x*8p@0d;%sBiiNsBHq{k;p>&uz`%$VYJYl)x~FYTiypkocbhNoKC70mvxRo~v)1gxBd%Y|@q+3HGTnobRnamYUi!uMdM& z|MIp`fZ?L5ELYEMuhZNPc29|OQGC*q`n5&NsSZVah{}_VDF2U+9@V~a_j!I~l&#GU z<=mqHB=eq_NjH%F6^*?T{@Y{`C`@U;Xi1q#YQAs#t^0%(>+>oGcgo21I9v42!VNLX zL(cco4<6DvS9Y=M4@D~TKE+qWIwdfDx7>v@#{$kQ!9<_@o7Tzbm86qM_8^6SoDbVG-|#$2!$^d#yW*g5#4= z)C`iJIM;_ya`f(o)bTGmD}n}E^i5(l&5xuGWn9{%f<8FzyCB%AmRn*}741tEf`A+L zp=D8nOK^t~*1dL2hFOS*^dk-%;v5vTdm)Gq_dLZNxrY)SOv`N6 zz>!!tBSk~U4I??in>#*?8yU-%si(-2C(|Yq2@&G@TX`=i*cr6D;v8<%`<6X{HFgZP zP#$#DKsM#(+#I=)Zn5Tb8(zr}3$pR>@JDxZgTAq<=q8BC{OF0>?>mUW<}ZD#@w8c` z!e)!#B36V2FJd>coLIG07adI0S-;*9nFrq%qJ{Y*ws_@ysEPV~;)&aV(tn>m`pjAqVI2UgB&2Rxi#{2s zh^n)If{WEurt`jEm?sr^FB)mCAyu~5kP!NcfjOpE`yzL6R?-n=G?l3PRjWR465wAI zn+eH%4f7@&Z%`4&?{L<}RPVO9*8z;ls$W|=f+IQC)z0ju0I;i8F2A~gcL4AY9$Oq@ z90S#O%Zv_YR|)Y{8%cES!No6Yu}Z1dPQZ)@vTupXX>*wy7=NrFW_^Y*SgS8aykjCA zn(Ayua-Op8TSv1Y)iSMPgEJ?O3rYRE5vcuXl=m}{CwtHD01ZpncI=Q1f4Yk=LF;3h zJt!@+sOv-?vOsHY=V=cw{>>)JYiA9&5EfB&`dtG6=5B{F*L?L5*-S`;T_*2}akG;1 zX8Z1|Z#_aaZkD?QXsA7io)i4NSuy zj@VL0>0_d%@Aj?HXv42{yrLSQ?0~mRqJIeNYSeB9=&DwZ=Y%B6+M4Q=Tcuv54(N$5 z;aD?W;$bdlut27^CkYWUKhS%>_Ov^uo+ecx-@F8ZubO+)wTrOx5W=cd>vmS+T7y4U|yY`)X51XygSb`(Q&Xl^AU zF^rVK5PL)4K5?w&(ojrp1_7~YM@Vhs|-l8EzCrN|K6;$O}C z@A(?D^Ypu7sPX-OWgmvJ&nlWO>LY zs_f&jeZ^*-H&<`!aN$d<+K0mj!~2{}zv9NStOG&sLdbR+k5Box&m6_M-`{pKTbRFg z2>%=Lmp`Z);9uM!;nhpS$qCT`mi+s7sKp}3E ziD76&UNv{fK{fuDdoXS8wU;q7xc4Xd4peiJK@BqIB{hFq3u%e0;D5%~;GWt9ku=Hm zGP_UzAcb)yP~-T2QW`8EBX6#BsMhx1ghdNvqFZ&s#r?Mkfh`IU_nw7r#U}sopMU`e zF1+LeGbNlq{yqo%J&VIvhkx_efnz`iF?W2H@?SI=_@TVu@0(3`Q~&)#*>=HKRo9<_ z{lg&vN=*)Ge)!*?`oE+4zjO6pkI?_=vFeeHqWdlh9bL95=>cZj1nL&3K*o4kRSlp9 z1j!i0IS)Ys`}Rof!SAPx5h{DoGxq@7M0ssX5ZO!yIsBk{fW}5rk#nj{ukYfA0qQ_V z4q%Q}>yr&uz)Ca)5Tox%n0eISdY&!HK=UK)HdC)k(WiFkqZzqH!T%ltI4m(out9)C zIjEPb=?p{aQq_3V?d0Mq_jayz<&fI)IIDs!vnoG}Kiw z#Ryc@5$iy`9}LJGf%hlJ2lw|uLo8!2PC4}l3)s(z1Bvt-0l77{itPS333zi2ygVtOjFpftZ)yw_fBTS^z~W_m zRm%f7H03n|v?d_Qy#0BNi{NsbK;E_f;1w1CXIp_2Y512Oe`j#mCV_~w>j%y@zps=E z@9tFv`;Sy!lM2mXD4YSoM#r))^X}unLxGrIFU@}^ibht0kV3rTv!@t^paP4>V2@Wq zNfdipkR0}tLWA)6_W-Nf2sxp%Kc6ZDk!Myl5N~#D(Npu*+QTNo=a(|+D@PA?!z7fj z1MMY7;05xz1x4@pi#&ix`0ZQM&P*{cHe7Y!gA^DYhWxh>#2S)SY>$#Dt^hdVMG zu*-XO^lK-eO>i~MYu-8ETy2n~ z&+n7Uq(^i0fJ+C!SkB9B4;V}Vh_u*7`q^kAt`g`psM?#<&D{ma!)b^`v-H#zhM|B> z%z26$qT2R85nx0Kbkt`7TBPaZ{m}vHT7klNi-WxsHd{Ep+#Cj$SsEh~n}kMjaa25D zyM*gDPmys0Ypl!RGk?Ga)t7vW)%>h%e+=O5X77cAwR#bs)n7cOURD863DCo4LBvfM z#Wk?a@jf>$PkN=K9Qbd8!OmU=iWyh2RdArHaPdu&^ur{oOM_yF^z2l zB=!`#dkB>uL!{jv_0vld-BcXMCuDN)-Er`zGj_em!!4^)H$2&TtS;X>!mB3;>; zbh=>;?x65a;P{MJDEcBmgX{*Z+I`_Sz}G1Z!Z$gB2}f6y`Nhyq1AuH|?M_mP+{e)0 ziix2IbF{q#KvK=OT*y+t2mx(_6;K)W^4pm8i4l(y?GR%X!4$8$P!s-rp-B=j)t|FA zpsoxp9P^vp&mQ#A_dpdRV(o_~BQ+HE)vq?KW@I_p<0&<$->DVzUw_J03sX*I!#iIq zz-F1PepydRNeLMgM2jX>i!r|}qmL%a4yGo`eoRe0*R$AuwCs)uiouSk@J3? zmLi+?{fF$lE#QA{odrzM$hl}=YG*04zG~;wt%k0}RMCLjx-|v!P&5so8KEh0sL1IZ(fYD4{Mm^$$T*TmHNfV72}rj> zm8^o4A1suY13ADaK})P{Mu>RoO)9N*gZy<%k5OdaC)-%EwJtVtxo8$({hbp$O*VG#4`E7)ZEk+1lgSdnYQeINFtb&MefD68A_qh!#fSx%YQLP3`Z1@kh zxDVe3kPBX^iO)b58Ajz3;Z-a<)j7(K;u8#kLmO2vr!PQ)P2fXV=6F_BQEOXxNB3LD7|3fSJz>a1 zH*PnhjyA7+1djx@eO(J39ipc>iZ#`dhU3>PG{E!i6WSweCTov zk3e7_ZC-MZP^g40#3S4=Hc7!8kr&QGuNyJ5$HcoAhk4D2(!H?9y3HI8Nm#T|o1X$` zE!r?GQD|`TwDRPKJ;DV20F#g5{sk z$-vLS9(^}^|2q28`bU%kgGw0>qPAs}l4zZ+t7jvBy+_-*86`xKS=en!0m%0|BVckj z0i8j*QAF_;!);&yFkMLs83g(w9s==OMui)s+MimCW0|L{G6~i3iXmG@#hJiGkWj{g zVp_WV^(CDNL6CIi-76QNv$|~rPQq~#Vo?hC*sD%fvg9I?bG=!0f`2_S) zC<)-pe)f|vvKSZNRCcG5_233nxJK>S{$!T{f)1VqPGTmCj%1!6UuU;0e8D#rEb+or_=eneF<^u=qFS$J^!YryG&@C^zcvkWwe! zuXj1kd#HFRxAOf9h2pWOZSqUk>nQwxXVjRo>%Fv$rkTsef_L<3vbIa^C@mV_xAtAe z`O){kKrah?$p+3@u+$TWi0}2|a~*Kl&j!X$iYGv&Cqt-}8;s6D+-?X8q`sPzxRZ%x z;HlfHKjIB^LL07)vYH5|&Qk~EUZY_W(>|C=hjttE#{Cn*w@4G?8X6-*?7EgA>4w{6 z_WN<1h%C8no)`Yy%B;a`d}o$)X|54GXV+9`PgAvx?6}7b(kA4F7%S=t=Sq&~{Ml=v`CTPhTP*4g5I?c&>J`AlvR-DT>+88vsIGpXc1 zg7Mn*f!{ubdso+eol86#j+z@Ul3vg`DBRL0Y4%&Vks_2%v5WK+eL}&|G*FmYkBZ^A z`dPb`B(IMN+?2D$7|XvEK^$thX={Ix=ce zQ7u{Ja}=#|TC89#m8^Xj-9?Vm(6-2Q;3y$nc6sFlfUS#Vij}n>1LXE(^sVTisx5}) zUU-btf#=@qym(!OXIR#l?^?m(#7E!s*^(9P^k}+JxvxD*87`*Q_9>RVkG9D{L>Uw# zN9h`L-^@AB5;&?T%4!U;`{~VQZu&<1vg0RrzNg$9X2E-MR)l zp~Z&hHp)M`Te;3~)U{xcD)gp{$6FW{1G||}CptCSS6DfoCF^~-xeH76@%%6;iD_J; zkI%e;_N#(R%$j$PJJjAkv0F~7)m=UvTdj-KPwjeUJ6pl~(y;7co|NyGT!^>l243)z zL~?YCXpdu@?L)Z-RJ4Qq@GCjQ>^WONL+TB!2-v`IcMYgVbLkfy9bMD*}wuICV_aBCM%>vI%3upGCsVm-Pv zQ4Mdq{YDG_dOLY+pVoDTfk%yOf(hbMg4WU08XURzQBwOM2ao;SJFcq}g!zSqcW9Jc zIk}jswd0n}LKdy^ITA@o)bmpMNX?)mW=W04P~3iN{zblM%B3LDvQHR=ikoIZlaBrE z6;WFn217=)$DbEw)cW;O)u{)ldwT2Nj9&_9qoW?%t#t!;Jn?i-nXgCrGGcOdPSjs!qDCSm$MwiDr!QPc>&Jxt)Z?)L0&Grk2aogYULXS<7Y~oM(9M4 z$F8J?l6zs}yRE^d^f;Jo2&^d+)`M!kt@hZ_ulE_nzL%0nS6j;=AGd4~!WiYMBm%TN zFtgkg!#%bzWimF@36M&CwK2n%ClaCxyWOl2*KM?~QZ(cJ`)~-J@L@Yq(*9dLjqk|BRn&jH|75{^qVnU-W}}Kg z@U=_j!M5lQoa8&!VqyeZImMlcQ759T zRv@S476Hz42C+`-c2aE_hR`<&!MV>5eW@s8uhdOH72sdZaBDkI7S|?Csr zUOjnGxRn|tlbbS0`rI^lGKqWcP?T5q^(ANZxLX>jwmC!Ce~egO`dK9@4PMviql2rz zCAY5tO;LE$zJxxHZ>+s=+a%)_`h3Z^>_}Wg``BiDNtAg{di)|WeaYiM2kdiEAsmX$ zE&Q46OhHPFrov3%yYUsg9JyR7|Q$MBP}R|Q3k_4aJ`t&!v@^XIBr z8Qwn$2vjJ&U|@lT$1+<#xQ-_sB$Nv8-wn46!+9j*-0c#v@Z)oB8bQ%i0?xy4Eiqy} zx8XN}MCal-+uQA*zZQh)!`tVaBDL64hBbl(;kyz!&Ei#L{dS@*yt}t5)gKhBo@lte z++pCT=t~VKWHTsQKe_ZKcLX7xvK-&YTRDi$*t8+(4B)$2a7WqYW`qAPfHu&aa$ygI zcx=NZKUT-_jDT?msiE}lWA~d!-aftf{UMRA2|(!k=0gb)WpFv1KEg0QLHgcO5fX}H zm0+|) z2E+Vg_F)hjq|YvT-c{9KJzZd%nNuK5l8qy&%;hx+K|Y1@sUyp30@_^e3rjC^ZY#M# z2`k~;7{yiMTp+5dfsRqEL-QHh9N)y*>XAc9*#xc2lZVL!90HfuK;9wYe zTwlG#b!lI$Whm|$*9gZ;H09Rj+mu>@*Y8f+qb!y^QmJStH?L53DnUEz3!*ycyU9hI z+E$GRcY7Llm)|^%b#^oIMC0tTsN}SSE(}U+U5R7oB6^BvDQs-r}HV6 zLHUMz=L5O(5VK_jNY4WER#Tco(P%9+pAfzMH zTP>q*Nq0HRTa(U^kr&*=e?f=j?aaF+byx9AZvsll+xsNg(7z(`p4y13Cyf4(*SWKaMNoMUF*5Pa`EiZ6Xf~SvU0MgDi%5f zrmwa?;i6=z)XMIWz*>F!Xj7JN|2F20DqFrna*vXa3}ri=TIjw~uNcIGC9)+wnzujS zmL7kFZ_CcwuVH{fXfREoWD8^cqGvVNT>VSMQbMn3%I6@zms{E|gcCG8C3-cMEBfui zUD}Rfo;}KN)pSjY#eB>S6H~gs7kztHAZOpHK0EXx z3DzBfTB$=1o#_iu0o6CBJ9Zp0 zU?BTmU|~1^3_MNNfDw?47HV)F0y!I0Svh_65TyMcK#8@kdQrbb&?H;Wl`=+< zN+Lt$x)cx~`xpwcek!AtEXoe`gG~W&oD}dr4po5nX#!Eq3ZJ|@71=1|pn_}&JNDQH z#)#n7z=z{srQXQnYJueUCXiFwGq95&l?o_}Mkut5TRWAu2z0=$gQ@{^-yH*=#|==U zRxT*dtNNM=G7|ZQ79%Jidr%2fvjEjo9fG8;f`e#Ky}SFf%^1R8oIyb3VC^lZ*DY=U zo_3v{s{8S5ZL6jBtJ!?t!hMjvN?i$J600y!(J>^wR`uu=ST3#{y)*RzpMnh7G7Klf z82>rKn&BB}i(UX-?u0Did>Bh70M8PGEX1qEP^v3#{8RBSPpIN>!w zkoDot+xok_llI6fcUH)9AkjpTa`X=n?LBNuY9ILMU*hatG4J`hS7#e>Wu{ zKSy7D4SRqb_A(Xu`$=1n3(N-*lSz>)gPA9X^IHzdcPlZCfUje&+j&zQ3}7O8pG8dz z5=fi`705RN4Yi4>$XXhhZGx-Q06MjASBh?J2{wmYxk)z;n%9rLx5*+gd+}xe^!AwoZB>Akd+JqCLwyV zwECtKc&lowhY*!+(?z^_T|c3riS>twG?H;sK_+ zQJv=*EwJchh~EDAWF+df4^yfe$iS*oXbPc-($;_y_13}N)DmMO^F7mA35Z9Z`_dB! z|jB?hINOtBF%mgTgptpj`R=|e>VY>Clg3-eYXxgl(~b`0@^wNx@leq z=;fn3M=O4ZGpvNEezDB^U=#n&G4m++$9}7>AN2uMZuer-$4T)wJDEeBb&<@nQnvK1 z6;Rm!#<{Zg@->4Gdm!8OQ-`Cw$$Ct$n%msO5uh@`i^efK5Liq%gZ?Zu*nXU-~S6tlg*iPB%AV^Ac*O_ zU%yqrp~lst5gZU~y;+ik7g-@#tMAXk3(>kmWCRRak9}=Ozv$oju2|#erHixww*E+n z^2t5W<1qzbQ1!5P@2Rt>CTpR_fS$7~MeU?RFf?oy)7T_>z)f_}>EtJIW#V$B>|C<- zZM_C}W0wdhv02GNjet5kGJynRStQcwMf`5Tpl##f2Sw~cj`gkbFQqK*;hZ43vw9VZ zEv^E5W+Pb1StYWN{&FP}S;P%GXzpAX1EABm&fuLnR+$dN#is8(KyE4IDRkZ}2F$Ji zb_0FNB0!W9h)@K95nbwqBRFjjWDj(3h}VzAi1$`=CbsMQ+G#sf-j;Va)fO&~CK z3kg>b!eI$2OQ8pXV=VqEsk$1^~h9Md?#7szI!vZUdFgD0x`eD>Yu?)cIr`F(%XSyFI+z1iA7C((q* zw@olu@NS$??UL@wnJBJa?+O%2PR4R3*q#)({=9Gbb$=T%dWP{l@f+RAVWrQO4#> zw&YD>g_OrZx%UQ1<~4gzl4rxoTU9P#;Hk9wWelc%c9V>e>H%&eRw19}(%_|Bt|4M5 z{A)z3{AXASF_E_J15q>g?^g4?ls2hVCD|olxRQE6z2$OuzcFA@D;XOga;_}cm5Vno zBVATIDPOVQ=n%v_M`<$=%Mji3eg${UG)S^7x*j7drEtYpt0D$ z4n#__M3j`&1Cpkr@|53vyoBG`8x}+ib(3$#o+hZ@Oep?n8r%5_9=Hv4t|kEy#q{ZC zr+s_(j51d)drmR=vs@siEbG*6A9i>C>&6pRO<4G_t4g0nzeGGKssx~kE5F(*idS7gp zQM>88zOHJa%re?p1+GR*gfbgj&vqxeM%y9tF$bNz0 z7KUlZyTolxnvU!8*Q{w!Y$841d?#w*J69jm_x0EwFPUWcnIL(ln5Z`Tk_4Dt=P`X5 zysxXemut=Qj|T~4)Z6OtyswawlG>?}8*aht72``i3&*&KBv{w-36 zT*EIGo|VzQc?V&;U$-f^L|5};mwFd`$2+z)=Jkr5z%@<`niJ=~Vt(7Vt-fpd+R^>B z&EQh+*ZIMvk+1H9iPG4g*Rf;%%E&|UFETT$_|!X6qA*&b+elI4sYng&^##RvU*2zD zJh7g=nFXfYsL4ln7k(HzWuGu=(|PvTkl0ro?@-*Pi~w7>nx(e~+blUELkD25 zbw+|QyzC*QK~V4y$J0>nRC5>%Q;pgo!bJ|C# zsoLUt#3&$&z?>!M%Tm7~r4mi>0@zc~jUVV7`;)j%QDE*plsD`c|BC4!*^dbfOTkfOH^?Du}1#88sx&cxq^&r^hUjomHdj51Zk z9lt3!?RdR;w<=$)sG7{x|7k+N^=cwrZoCyX*Dfhp6nk;*p<1rLg;esSa@c-F zGG+Qj?2-`K?FQaQ@msHbM-yYX3$~f9or&MlRJS*IR}VU zDbOimvf&IO9`Jn|RsfctBZu*qN#RU1gQnZ?<#Wy zFOs*wDFH^Y#XV?6h(q5T?B8b7DnG3Oz`?JBjM~vTa^Q@Jcp!3= zY%X1sAxV}0>(`5}6tTf~=b#B9sej^bOt%Y>`t{d?T#r?GeHN87)l*gbMMTnUoIZ$ZN`-V%;D2D zG2mbte3&poL17BBhl|~PK`98bA0rTrdshA(k{+Oi<)d#|{U)K-9DL?xB`{@OU_Pb2 z-EyZ)ac`w#`vV^z*kxgv@d)Nv?L3%+gDHbww3C zc0Zz_)=}{_)&|RJ;x8_OMZF-cg&}PvSCEGySF2em%NQx#@L;UYPaeEEXE+zO6gei< zP_fQyRDv#Yfer$LMGq`0 zoA$wYoOW~C`T6uTw8{Jhaf(V3ZKaK!psU4xc>pV+2)>cR!Y#d>xcs zbHSoqqBcCDQ`hId+DUwo_R5s@u1(E1bGoO(I%pcmB1EwRj>OKrSLlL)2)JCjU}(LR zalbw{VgBnv?ZA3y_21=C1}P9y^{Sn$5cp~L;9f6mWm>8&r*MHZ7iu&d{^g++nN8YNo=Gom6Q0zN2l!~L3BUGeTo zs!^s#&aS@Mv!$@%EG`Wmr1D%6w2~b@xAo+A>Pu+VF*+ z=l%6E`vq*nAvOBTUVWm?T#f z{>029fRjXuf9`jJ?az-^A_N(m?jNpf(f;BJp{r2}R0W_g(;3%4+V=Fa@PR;GcJjOz z$)5;v4!HJ+r`2`8u&KWgYh3WJ%+6Hur~bN^e}8_~H2`F*e)>=F3D~%iH3vs+Ars001j3W3H zCJlh!$oeP$=?{y(8bJT5w=XfJVPmFQ+Kc5sAEWgKuk)iG;3IU>FNlRRNejbizUsWW zF+uZNCk72&zD_`L=t$_#59+*pkyvLA;K|4>pZ{F4UKV*^We3^Nwf_;1*#asOlkxwX zc=;UMzO&nHCVz%lvW0-LApdWa=2I|9of&6nU4uGG2={TYt=zd!iz$1?DLNBHlD{eP##|4xbj`&S83w&u!v00d$_ z-&5~@BHDb;_0d#QNV5c^th*x+o=Z0Zi_8$a-2(ETH*Gv1NE5^MYVZs~KiqWfJR|k> zL4V24s6zfqCmd zMVuSu^G{SWFZ51U7D*p8%DNT^6^EyTc5SPcX!iRCcpof|mTpXeTCi8u1;u{b1y(=| z1}G<^C(E@Ejd?I};`jE;;8;HCuM5RJg}n)WX17S-8NICeQ!QIZKQd3kwJ%>B1xy|* zM0!a-n6x;Yri1brLm@la$0t-)0I@m!y(!_J4;j6hwfuzYyFqc|AOiF*Q9v-?qE)ps zk7cmg`4YenG#{{iAuAt^qyuIts4Nj??p+3vfKy;RgiZw^@Z0elAo6VrDo}(dv%@L# z+)(?M16?5fWXw2LL2!}QJvb(Jcm{uD|E|&Z9^WMhH zPnrxB0J$=r&ieISw*+RJzynKAcfb5DtI-LBogkAzpB`SZya`l_4lHKYhad=q3`|A$ zMQ40Max$o4d=H@b*RkQ!=}i;IJ8zvK;{rtSqWQR6csJix4H}u9N__@}CzdWElOm4( zRTN2y7u-U&X?X;I^p9FF%oC_#Tn_NFR#0jV%IX}b$IiB>_tQpoUziCmCH@M`Q$1S> zPwUu!bi{mp55`)v%NRN$KMibvdihF*OdZY~!4KXaBNn0Wqqh`$gCBeYPguduQ-HK# z5!44#dF%s*S^5wVwX0_|2*h$@d!_^qRi3CrT3pxATQ<0(LWX~Z5HC#C6``zRkojPTkJn(TrYl2FVvIK2uv zf)JmnSWV&3gN=V0#RI2_Xu0VNFSm`^A*7N;4*;JUj;RHfnZk-C$l3Wb))rt41Vg^S zC)xyrjmD;3Ox$DKO={W5G2o(OO$$>1Qq~QL;}j@PwoRb;Tpff)hrVr1j8V=vdKh4v z35Aw+Wc7r*btH=!6q@TE^{HwKy)lHsX07W*#N{aD)|DQD$ie>UJ^>R$tk<<0sqbCt z3N!P1e9p@Hw6Jl{7!7B>>%VzF@YGo%>*mPQu)V_m#ewOVJLn1(u6EB?>$1?VlznFB?s0h6 zxtN+QL$+F%Jtr*J1?uaHV=^dj}%xqVfmK z6o*bQ-F|1w)mEY`gIpo!UK2+(etyDPQ>(TXP zsB`;B-*pV!f?nZbfHy5@(p<)q3TlEZt5VtD#)yj+hpT`$Ct=5q3Gq(d7WJ_4i9 z{tz8?4)-(*XkkBI^{5ZSB_ED+=&p{j%H6-u5A%0yr@)lj};oz1g`+Q5$C4U)=FCI>GQ{Jt6zRaRDiQ!Er;F{1=QDTF9B1vV3fP5Hl+S9Ie(ku|vgg{SeRWb=1Bl0o*Ein?qBb`>71Nv1t#?$20z1 zl(X+!5ckieo^13xvS)Gr`26DAVqpE?b{_E(c!e?&)fw2v;Y0=Somxg9_|p9=o$C;9 zhXD1t(tKaOPLM53gS|ivXBVtZJfoAK&&1LbyxGniT#nu6*yH@r9E}s|$Y?<2 z^-Q~^z>xj%i1MNP4Why-!jxBmM;mQxv$!`r4i@k2gRWWw(_k_?&#ACecxHY?;pM>x ze5an$aBD5Zp3zzsE1?Xok6XdfMOOa`s{}*%T}W@QgY7+;uu3ZditwDX(xIRv6T%#h zmETy`Dc3LarxkZZGGp(cxdQ}2kX{3GC#QjhKPa#MN^cF_I&m*2gyOz)4BfLPot(2UIucD-Zx^30KDx z5Vyz=wL=D%NyDsx;>L2(082{U(?MP4 zu*4}AbsgOY3jgMCsGl?A>nGYZWS!=Z>*AkjyEecA$#T)*Rk?(MZPuzw+i^Z;Pv09O9@@SxKvJ9(eC*zoBOlS07UH;9qxQ-%|^; z9OVWsDUs`*+3I&Z+11!ot|-LUF zz5Gf~<}-H~N6-&iqTIMCY%H@GSn)JnH_$c+PXj5qZ(&2-HF*9+Vjk2!|nh0{4MSt6p$m|m*^RqQ_6 z4G6RPdd{5x(qjJ<-iuJ&;wso}kYRo@iDDD`QdlvLrTPH%;F6Ke>q^CC1zN^yfjMDr zOk%juBn`Hn`kAc{9Ll3jdW7Uuqe)Br3{hv2ly?Qxgvkz0a0~D-~;}=q^sCz_TYQ4EsGH{E%MSZ)l z@}BNM=CX=}E?;?X^vnF3y=8N7MzCT&^$}JzQlzKGfBB8lZ-B1 zaFfG!i(_itP=#mkZBF_Z_1r1u*!@zW;;pD}?YOCr#HDYy-6=cAQKW)yoWJ0nbkW8v zQd3DQT4P7tY!un0QAmF?)Gtr#r03RpDmL%78_3VH(RFybJ2nqmy`N;d%D1Hw-{c{kYZvPPi;R*(N+evwf+W8u=BxP4WQeMuVAcIyuoq z`}x8o;^7z6N1f!i(?D?iZU0)wXypJErAFQ~7#D47#HK<74uj(1$w~B;F(9-oXPxz$ zpxMqF5C4!V1V7@;xuKxX)p?2dBScFk?`xV^1gWKd>w6I2Vd*|Dx8n`u4hgEq9V{U~ z*f~^j47-y~Ez0CBr+%eVwwFzSqhY%wG_Aw@T45WrBK{ZGXHVf%4Dy36IL+Li=APdY zPF_8^z~eZ=vkaKk!Es>9wLTFcAYvz)v;#R9WLmh&QqaMT6sS;b^IKU=NK{u}Owi-f z;ht*1MV0ixVjWqP@loY^MEu~|=aau$j$a58!kWb#L5Wk=CF(Rcc;>Ls*{rQumj7a;b4iw7OYZvP8DivW4qwD9T`IV_Q2|WNUn#>R2Ugwa&qa zBnTF=GxM|l}kZ|g`&Y<{2IWh?R5kF;toDt z_Y-%_!S=n64LLA;m^`bT(4ds8KM4}Rbiy=>$-e4R#t?K_9zng`QUZu+7d&}!wS3A8 z>y#boNEjiKgor!L&XfR8IZ|^63HFg7%rNk5N)p&>Umz%eCt0(|&P7^N(gx{Nvd4X@ zAV5Y_M`F~Dl7)k;#g6bOwrL5}r`Y9W)rcq-wQTE}$YnpqNM4k3lksrLY@&sKju1Fh z>Cphgi0-%%JgC9?=G`&uy4Jg{_kPG|*7tjY+8ipswVW`0%41tf($ujsCbFeX8&}MGy_aZq`2H^|roz zs_*wuEX_(ES(2mhaAAgRTIWD!05wM)XG{M?gg46J(!_h<7m}Dy`uf9 z6L5@}>(kzIaR?nPk|PAtC{5@lQynDUl%H-!e1(eVg)LkV!@IS=i3BBY(gzPjN5;2? zbOOg|vi!MB-p8BdVnq8B24w5mT@^DP^r@E;!{2bPqci96Ddc**d8T6ixlmJnXy+*; zZ|#x$!I1X@Z$6wacb^}33aTcbpL(&z>8oB~BZ(x}Jki2_6TeOxtR;JN5u6%^^7Z-* ze&gXXS3h)E&fnDRz8rY(6unQVUtqq75R>TJ+o@M2w9^_4nY6wuD>lM!3oVzWbH6|y zt16WLv)<7f0DL2PxYJairvk^9kYWEp-ZS?l>V?5kc81FYSI?`oc>fkdXDMv}+9k~Y zv^@T)RBE<^W;cxq8$_vm zXesH4QTuT%_e+*PyW$|0y7F7r>gAuL&QG377ktA(`~>Pxe#w_ZRD0swP|lzFeUdFS zzneBtj20FhKZNq}|m&0j112k?P-76njqx5Dv|qTp|i?5{8QfBBO|vqUJ& zmq#Gj1Q-LzLA>{?H>vIa%pmBC@{RuRtuR&C*hVWkFZ5&+1f90f`GeLA%vTTi#!yNl zW|)H(LMe*}{Z57XTNr?arG{pYmZjKNA{Rhz$QdBYH_Qxh__N9mK!=c`w#2Flzz5bZ zy>#n=QwGzftnIdCb%Z zN)xzrR}i8jZ30tu{~ntxYuGpdT1ud%6ev_o19vJ*8FkyB2dXTw#Zd$~==58q_bPiT z;jd^*;R6jSI#s|e!=FKy6JG=nq%%p|b0NP>k-zs+ zdRebeC7RyJPd+LzYz2A@MJNyI{xvB!C?L4$tH%U05hqmZtz;Y1NP7kroX`e3a7^!C z?$cQQf7pA=sH)oTeH0K85EQ{+ODX~a5)vXIVE|Gh2uOo;BPs0$1Ow@Al#otEkT&RU zB&FF3N;hXNAAQ}vXPhz4|HFyjInS48`0T;nYpr|TG3Pa}d0lJW#suD17%?8$dvlTk z9@2afcj<+CYBZCV?st%z3CJRY#`%;6X54YG%Q!g#;2Y9r6zTW_u)(mA$SP>DRlbL# zw&!fUYsJ;C(Mc=$pF<0&W~l21-#GbXnNTI`T(mgxGFe)~BDKnXsXck2$EaOcU>bf0Im>;eo4Z&+FGte6`Z9u^78MsK;4x zk@72}$)JTzPI2tEQsLEh`*E>L0eU%Fly_8Q zLsy15scqq1!ul?=WF`Yft$@D|1@>igHm}bvcS`@kFMm!vb^7X*?}*NH6e32~kEqX_ zl00NcQ|Jk#=&SV0b+gH)BVzYZ1QE{Abu*E~omaVKMjc=5)_Rp7;!IMr#?l9Q&@^2g z{--tI*C-20kAafHQ3G6}y~ZYZEE-DXFcEKA*0z;nkfmLxHnazrk_1_a_$CHKM1(}A zo#Ub7>E2lV9_8a{XtXlBsnV{SzeBwF4tsK+Z%@JTu0~2sh>n}yST$N}?J9u{0yyE) zplU7jsPLqi!kMM0K);NHdVmi1+<-*PixGelkwr5g7weJ8O!<{x;LGldIfnIiU)X4j zP1|6vVyRev{`ZA6@K)9}z_Uf)Ms+Ug1FLoArV%B-m1Z1g&~z#S@3I`Izhr>2hMHT{ z83RQZubbRYb6B14F@xQw*1`3^UOf$Sb@x$7+%)gnHZaB}&?h|n4%>m%&7yYOG2xmd zcY-Fq-M@`hH}>(GDe8I2d2tPsC<(lxjbc7A8S2LJzLIrXK5~gTF+KVfFj)rwCX^_} z<8(=Jku@vbx^m)E01Xvl{RZ`%v4F84)oD@#8%hZ>LxpH)>Ee`yR_FT)D7>p%WQBn$ z1U}AQ;;~QU9ZCwAv$3aa1xr477x{s4t1xY0kDSCZFNNiWs)5*hw0}3o&f=gEG+r2{ z@DJH(z(w*wHLv8O8mY-M^}9saOGhEe`yisWbvgb6(%S~rnu0f%m7tyVEs4ri>;)>Q zH3pK4BrheNn+;HVJ6BjR#i+J_!`>Cd(1Him`3BtL{jy49YINmjPyHP~#v}1#bYD-= z-(ACI5GnRf-cSDfWk*9u81Jlgh3)T~O@o-={}VslzE(2^F`%H*$bSuoE#c(z@Img* z7%si5h`pf!3Ru8LX#dMgS5W6)BGa$g`7e>_S6K8fRqS`b^Y4`ScS`)P(;a!A_Kzmv3fJ{PCvmH^BFqNo%@%e8NL?Ls6>M7GAxN~825SLJZGym zt^y@nH~G;?;!~P7;!$8OCFA;Fp>(De%nld7L)zlk@lvv`t!gEe^AQxXF1t-ojS^af zHom(3vKBOn>5$_7N-$Q1m3Zo^-(I=ifhbaD@W zO!*}Ij~P;tx(m#vFD`%5Cugt%GxfC%k2)N7SRK8*I7Wj z*KGrsSEKD`JbpaW@j(E2t84o;NUnc>;uBx-QB^EREGa~+uY-*>=i&)Z$GOx<-^86s zW-a^&muKoaMeukt${}>CMff+CsfY^n|4CvKH6oHNN$YH2`VQ%W-pqBX_|suGAY&nQL_%P1seLtH`*vG2G0I111bLTDr%L); zLFpXbmv8}b2m`~}i+hMMF4Adc{}##I!Rd<84Wlv2>i&tUb==x>TcdnIba(ESjFN27 z?a4i)R)E(c54GdZ{VT;ypZ5NgWRk?Z@g5Npq=Ui*2&2^&)Y6 z%T?dw&C;O_#YZs@y7F|Yeu5*&n?kD19N*BohVa0ok~9u?qM$7E9wCTza*y(Ih~+QhpLD%i`NIk{D_T%YGu;_xFJI=8G&8EmnlxRxU#? zWfZi8)RHO@0^s;mq|T9FYT1Zh)PpJ>!j?eKoe3M@D*1sDcYg5@Kh|301x#BvDD>>L zb>KTWK3xV9(nKwY2Ge+_BkC4Ur*ig^n5W%cK^H3RatXqw2s)Np)sG0yLun~qC9x5) zDzsX74z9yIhzZ<}@`mawh-j@{M2YRZ@1pynE1Hw*yg^V=^e}wu;?CG1(Z@~o@`wb& z{X4fsfb7I~k4J&qPgeYgpXg5V%E3dJrBoa_IpbK%HlPjN1z5ot^bSj#iPSe`l6-~EScsX2W`Kx=Dhi!yQJbrh2)#j#C>3}9Xz0&{lzJQya7AR$ z=j>u^?n&xYKv|4&2vVhs+sf*COR>f5(S?YBOKV-7NsQ(@gPTdkh_C5ZA#ID#J2sNH zwjjg);o&yGR!4NU2m5NFvT<@THldy=z4F|k{-uN7HRmRNH%WmO6%XE{qXb>=ci?7J1wHZN5Va3PGoXNF7}nY`q6x~Ddn)XG0Mgq#f}QLwA)dln+7RO^$6{&p4cv=f^~JPNDbPpY48 zE~7{P&vGGwBdRqLjI;pQW5MYwkkR#10FIR^fu3V zt+Fw^=yB=*b($1?(T&75MA#Wo0ygcU+$mre#gEeDDRji&#t)3+Zz6n!aH|M?8=^XV z3KOo1mftw3cJ2~OCE^HBRZ>R8fyGIhxu%y}vsAluhmf$yuruZ6IMUlMr-5?wb&YKM zZSst?rtmT-Y;;^#I_c{-c?VHCwyDNF^g)*F==4ezgSlk26qh(%;T=M}%`@lEhDyA7 zM$(IrS199BRx>L&0K~XRc21@U%!KPY=!tUch1o&CyXR6C#;)~w(j2^n=NLN2e!Yva zpB{%A2yc$RB{3@k4q*72nsxkatb`U3HPO~(r!8%AH}VIHigZG~O%b5(BSg^FZ*&-Q zEGbo%$({CMy3RQ@5f2*?rzNRa{9*j3Q;)>_#zL8bd0)!fE$sqN!~c{2cWI`m>QJUg z;8bdCa-?mzgGfPugg<`M-h+?+Z5nEbd`stoqE2<^q0rHw)N}b$e$|&XY-Y4mf}*Er zSvlvl>7_X6?qs-W{gF9_KBfpUJ#KH3#^7m<37`6>WXF&3k`ao4mi7Q6-E0jA zSXQjh*{N8c(QQefI#AzE62ugapJA#kfTt;K7I8Bz)WeVjgjWR-V&24(3+96hek||n zsqm}4Vf4I)sD_1$W^CfLN|p-s?Z?`G2#NFTPIQ(Q(YB~`Edo!-vw19-T~5xvVK(^0 zxhTe>`qzyoyf$3o0hABb2z1PG#`0Z}t=5nQQ64Zi;nc`MO z@0;fr-ZPyje7_n2?Lh~(s>sI_DTSG0zsYYSdMTZstA&04ViKKi_eW&exjCkmw=c$g z0kZ56<7Ik{+Op<46>ZAvK1S%3z?bdkLL5-h7Y}-wQsJHQidbkwm_(FP9Vx*~X)EJ0 z=sQlI)20m0g7vxx1J}fv#I$gH*NhZ;#5s3>*^8i+G28`ny{Zxcy@T$bOOkF8Y{J}f z3`!C@QRqw^`tXrhST-B_1g@a?08p-DRGvqR#fYSdaj}n48q`rbgnC4DFXE-S&e!bW)xw$yCZ8^PeQK7Uvvat5PK8;sHZs2a^tNPPMC2b3dMwQrg ztC$n8f7e$tnFff;N9YE2oFY13$aqD8;#|Zn$~G5aF`g>AM@;@<((WZ+yae~oPy**? zx3|9em~MM{;7=OSb=axF$6y5u<`FIU|;<0>kg-(Mo-5~nd8oACS4t&46 zp%qzo6hGP396XiXe~$Z|T$xHnw1K0VPz4=u^fLqpF6hm@C$_G?n!X*u^bX&*hq&*` z3GV*?-Q(T$@G@wb&6D8y(CrrDpm=E!Sb;GwHuVN28wpd>{0-`sj&D_I*|qRpye6@H zb2>*~8n-ZD%C1>i@W_FWDK4t&t;VMLr(yy^8qR%}b#I;3P}QE#pg>7YXY0Gh40T=) zF5_9JbzfCnmWwe8Y0xlD4gaFcXFwz%N{mUVv9Bi%xhTt^YpZRbnPpf{c#>tcIWjU= z`L+8;n%6a=131s=OWs|5cm>3m-kxrWH$Ui=9X8C|68ziE?B}M&7Q{(xw)Tp>f8hyp z0wJQr3w7si$$;UsN^g>Tw!m6d-MPtdDFC3@-gBSvxiwx(cwNWiiQyvOxrjp?{d&=D zMyO!Uz%j9<(%9w{K?K*SF#%(?IL(O+3 zXzHcX#DY}|9c3qK#<;+Y+9_lI4DKXo5Rvgb{hD4*MtFN&`an)tUTp3OImIR!hQ189 z&no)?%8k5Ti=k;bucg;Ua(K5)95wL9k6*UE!pcY0n!AWC&Syq+{1WA4O8pUXLWGWz zYBz}+{m>_v8+HGpMwH6K3=$-R*|<2s6cbH8nbOv|wQ`$nRD`{$vXG`r+28;90qXmsv{HjTMU{}6d$UKu zhnU;j;8@Y4(jWB^9Kh%t2-J^9z4Consm>KUJce*v>R9qPF*dZ{?kFnMt=46!oS~{8 zySx(zXXa~_=ibB}RUudRRsR(CAL2RV6Vs4%o_g+gFNTlYCZ$#?9G^#3^ZpHsS}B$1 zbBCfPV{hM7;j8`cM)AT$;+uvxI)`Yl#)4(PW#M22{La`HbRhRi&ahA;8u6O@+Voo^(pAkyagQ}w>P4NMP1E! z(3b=UF}-kz?HRwzn|F@t=J)#g#0)k17JgI5fy}F2asl_j6vr1a1z(83)g-hy%VCFmWrw(nwHAHBsYG71xX-^8_FZC)BV~=T=ib# z!5GEPz-Ru_Ikk@iV8hB;&17dd&u~pr;R7$lmD`^sm=>Vipll3v7NzQKBk_W;tkr*Q6^`A|VwAJqqZI>*o!;Q0G~M_9wz5SFn+R`BPjxcdNq9S=+;Kyvd=D z!nB~&b`ZBkBRV!jJji6B=~WSLkKz20Egd7tWJ3Wtsji*prgXpUs*xHsbMz$$ZsE?wE$vDYh`7ERxDA_uKtP&0a}kW zkax=>lSxi;j-tMs7U9JG7u5#zKpCM*!FqH3CFGX=PZ4vcy6Km)(c=48Ju+7rEo|0v zf7{JM&L$=Y*N743_*>~xP)RqYRhYd^8oPdigkDg*V5f=vn+rmN^n)j#>h1ckst6HO zMLO^QN9z_j0?xN51LQ#CqIZQI3x@cA&kvKldY#W1NlJa0i-cDAGU8^*nKk}jq@Fh? zqL9LdsE1qB<@@*EJ6JR|)1$}QldqBDQ&q-U{d~;d|4|CXY2dlXeD9Tnj0Q+E<6}=w zgio+a#7lAQwINT3MOQoGng)Fp86voSe-C6DIT{EDK6>wcKNhuoQdmv!&@dt>3g;(l zHrx2CKze*b6kT4R`1z2xw#i?^EA~HB6!m``edd2@pvA0-n?Ze4&ztlTA%YNC)4u5o zH00{XOw#@(!#k*~ixjqju1pBgo$6g^Boh*(r*6+ZRr5>E=J#}~U`K>2eUX;Q3lI+= zCRi8gK{A%=J)}dYvpNv~3AO`2q%*3r6`Bk6kXA%QcoJ#OtVLS;Kq9slU{Mrengqpw^cFln|ou%}eiX?#Ry_CX8_J<`L3hdch^3B@%;)U~jQg9_<1WI2x*|5dj^ zGYLiHF~UknN5_Gsdl-?oJC0djJ+#ZL>S&;&`eJ7T(YtlOZeaP}?3FP@?DGHt$>Rah zn1D`;6(}0}M~i6t9icf5y#k6w(m-vI`zdOQD49RF^(Khq9-B-;e}is*xN5CC_AVt> zNBC2C6oPOPgh>~USBr=&q$MG0ugy3f-)jzCoYfz;NH%4O{va?N#enbu5SV%3W9i5Y z5y3VB3g?i@*~K^RTgJ(icDD9nfq1a9t)z*amNH6Y~w}&kIZCXOxO3*Srx^m{6 z#@!r$AWsvxtz5joxx!-L8k4l<0CW%x+t`m^x;HEPXSC=~T<HRu9roH&MlU_+um8wtUSNj+q^A=CQVYIvvW2W z`CixBF@uIdPPm_cehwEep8T#!@}o>s28m4jv?o%O<=|qFrA$uP^)!kjk;n&OXaiSb zNb%B;&HH36O+ePK<+cUES# zJGh5vF{$UE2ze~8FRRr!x3Kk-1Y8q}dvU6E?6|lP3D%dq*=+zqk3f1hKS*ClJQ^@)<{gG0|Ng^*;aNy@~{__*<2hsM+!cdlw$ezDuSA3Lx z8!#+YMEf$iYWXZW-@`XwTOp7`eSdfd_;$wY4tQJBm32!lXuptoTz$3stbJMfyd?3`2 zJHHv2Woo3Xuu=8hc^SIasuqPdBtQQ-;?lrPN{cLOSFE2+3$F%TNu!fE>U*CCg42cE zzOme_%M=cT9?y;UbeXp$PJvxsVKaAA7=CdvITINZ@ANuwJhTqhedRpRv9ppc`b{P) ziL2lB(T?1oSBHZpA~qn4f)>QgY!G!egf$iv88EH4ntgrh zKhqBW5ygrYUMzv>e`nn4tv*v0Ofb?ZVB+LXAC_J3R0d6`R|mO1r-c6iIoJ$!LLL_P zS_Adt+scCm%zF7csx=G~)`MCKCSh?mym4Tx3`JTcS5#ZpCK(gB z^6mchp!*c{!suv;vsz+23bMfI@!b0%*BE3uFK#%^w>b7-{Ifm934*#TOLwecH;NNW z3$7RAf=2V(rEoShf%c2qmHkMYNSmn9Xnrp5_XhYKB&^FwukwZB_NzTf+(bUkzFgb0 z#`G2x1P&DO_v!SRW&ezK^&Mj2Z>G1{VCu;`)O$FgEW70F-M+;tu}t6nYp4kDjN|T0 z2ms|TlSd8kg+`-T%S&X&eCG6K$>e|iDSkE@8Ig$+@U>8gngka2YqVhoVDUlVE?wn39|pOKdC$Rek@z9C@sCb z=Mf)=!%e=THxU?KgV@qx{Ijx&gaZ-hID-5U|de$e)Q z>#uiLfJf1wZ~#Ss9Q261Y2=fX&nVZ`_{&&>$0JW4%`(7(GAlQvfA?hioGmv*znBMY zb$|U7Jr@BQZGob`R*`cR>jrS~*ztX8PCmHAh<)p-@Q!k|1y-4`KRk@9mwzxp4*O-c zE5m15s`3?oeRx%j(gJO)gtbfv?UltSzs`p%Sz~`#Oh!haF*XqPd%7UEr3Dk=oy$~r zEcS>0-r_%R?B7xRHGRbXos0i$v;QuaU;9M+{{@?l(d+)+G<=B@cfpFY5v;)#5RDGA z3W&rDG$C)CLWJTSgs@MyC^tCpmYy4TA5#IH!a}G>MXs7y@C&!VU3!ZiGsm zn!LbNm@Vx1;Ybl*V)L@vpN-M`EY~P&R`$|Z(jAE_Wo(ESb0mM|kLQNKd0;CtZlmpb zG6c}?R6RrB1;BdmmO>=;srY#~R`4?$_>ilgt`1cZ30Yr-+iI2$J0wm^LGMApN5x|o zqZ5fl@l)XZmO0|ttWT?iNSZA98wlze9m@6iiTTK~M6z%RD0E*@SzDS|MSpJ6#2XXiFGE7hKGYIYoC z$BGhGvzI_rDIcsVJTp8L6YY;#MnIBeWAYBz2$gQ*hjq>u8KL@KnNITk=X^yskj-AF zMA$%0kZ}v4W<-8^eLDioFsj4#<@7}Rkc`PaXB8AP!=!Nq7vqK7 z-f|+HV6)d@j=QA9p)-Vu8y}dDR3vU8{G{`A+x>OYY4NXQ)Q@8@u~z9wu2NBteMBf@gyEr+ymeL-28e`7X_{P=eyZtC9V9RsAPN&WPlA{zL>zS+J75?04Vo=f zwKr-{9jQ<)A?dX~Cu=;}VKe~5hbBZFXg;T;ay7>gT58NW)?)sgvuKgS_Gd}fHsd6R zhM^5vbp-xa^}vhruZEmd_bZ|+bz!odCz@rMn+D5{eNV>_pSM6~N>yJ2sjDorsn6h? zF72_j2a$>fd(-jbExk3_+cif$^3~q}ToDQMgfy3LqS%uupt{)gvN>e+tZi=7JWZhq zD=D@hP7{eZParB&@8?s6CV)b-QwYST2!EsKRf?+|rIR7HKwltz*K&QNc@l$U5j|zm zWpN%`bI5|sb9@aVIx0=A(A%STNdBg3H&zaE?DS$( zDw4lWLA4-iLV?O6U%Y`&YO^!4@{Szm?Y!^Lbaq}quh7MwSIMAcYT#T;k)Xf_#x zSKY?_L{^eBmA{v^xm#_5`VwDpGj^J{#m1FWJ2pYP{&p$4%`J@>h4}gOg!8c9C89^Y zUV?|33DTba0IZQpl|s2%9lT!9JyC+`lt}7;rdD_M;#?{pkrdtPSn@hmo`m%LOo1?M zhB_@pQl}g@E^kh-ADzd8%}HiawO!t$;et#GhBgsCgJC`?!gi5XnFyB6SE56p>Ae1K z^vcSRmL1)$N4(0rbm1wM9xKnK(8T7~+mLzYTU70?^=wC*=a(2bYmpPiGUD!lUl6F) z))?Dr?~+Wl=sW*Ol62D!%f?#0n6r&S%aZRcl@G##RHcQP7gH9< zom(x#l~q_YEMF@-k1Qvswm7Xnx#jl40w&!QBd2o%HAa3VaJ_uy3S%I^4fM zx;=VV-qE@HB`u%*<6`Nx2-(BI`LT&U!$u8!EXP2~j9czze3u&#N?K`3r*!yC|I(1QwEE{x zZa&4EeJyVo*`1Jv7=draUDJa-h?>=Wg>YX~NJIrmcBci6Y}Pa&r%C~^{g}n?7z3&8 z0vJ8DDv)KL(gfisWj2k9s;%9Ijp46v2B>k2iuH4fJEa-5q;Z4 zPc#<%?_m|@i89{rH0vdy;8QE(NK1Lm!I{Ro*`eVf8<8r|^p>a()Sr9h1~ipC)!6TgTX&sR z3KpgZ-W3UR-i`G61UOis0dK4Sg_zrNx8O)O^XbYC#fi0vnmH_KrkvH6wJw0Fip6Ee zb<<|_9_*G z>rKD~)g3UhS=tg)&2C<5>#9{-%01F=L8IXgyRWrNSL}3@F&DQQA0-XCjqZMuZX|84j zYR=!xbu_N9^$XR@;hDG2z=d7-xMvVj^L%t&c?mG)+B!laPL_v3pi+KXIQ>Xdi;Miq zD#9sS0>!(^(>1(Z#s)!g_WCrrKan15cZ^2Xjp#B4?@ba_a{n#|e#SOgSjwX`R(y+C zXi7UXdXx6_hshd|Z|0qjp*X?ESp3+C&LXCmug{~;w#`DcY1NbR)tP5}L2J=Pb#+I} zZo9e72}{v zZ*Ee`HJM1OQ}8^OC^}b+xY}i_%Pf1S^CFP^yg=);i#7#Tkg0<kj<-;1x*(ms!+pA)Z^>38!Qq++1&$l}_xfZC;P;ts%jC>3<<; zhLvYgSK_`Ly{2)zLuOEXjSUhF>CLz`kKC((w!uhX!pCF`eAA9`dQO^+8`PW z>=D~PXk^f&TNb;B<5~qW47&XxS8Wp=O@?>*B|W3B8pbU+Fp$9?{o4 z6SeRXX}0Z|kBR6T**sw!^KAJK%Ed*9S35?pdC=j{I~IFbef)5<=*K#{%UByAAsHnO zWOXwa%PhdR`Jy#E0>CQ7D+W+Jf@DBnpF-q!( z&yt9-0%Ec#@Her^{74h{=S+a=2R%K>k?I=?2eB_C`x-9P^CdtM3(7;|0-kng?CdK% z>8RR=QZZ{fSAJ9s7}*$v+Az!;GAUkI_ewTf1=+EulIuC_A*nu0=zYNR$sMe7 z1{ZJl6dA#`wOJtcw<#aMOB&-0CdNuZ#gg{%YIXPJz6JmF<~ZTa?Jz2eW8eLX4j`db z8CS5stvd#{j;^}SDuw+z_u!6p`Cm(7f19Kn-uw4Rse^w3uAYFx?x(H&^=&lV`k_7Y z40`P0*n&G+8||jW{x;Wln4Z--XHQ@c$NznUiAI6U5S&U&i0JB9m}>_-Z~y%gLfN_W zYsbz$yP$|zDi(r7;jK3ryV&m@#?MF=-ZNYBU++2UD!eJ;0-C4TF9pyQa|KrJ`c=eZ zu@TwLdO=Ng39{!(P3NibDYzAO2!G{~YK~;~76SWRg7l=YLxSq(+(gikDWD*-Ks8Ev z0JIGnp`3NM5*m-f&2vkQKDgh-o>kq4%fTL$J~Nst7a5!n8}BjY!-rWbmR;`GgHt%q6*}4_eNNtTz%SzG$ow8eqm}wdvbB#5Ybh$ zck3`>Q_#)hq6W3NMi5ZuBxy~ZA^kI5j^>Hcj0_Ugij#VEoFI_g^iRFI6O% z>brlUPe=;*v#$4y{muE$)>ZDC)rUkBMpn}Jm2)1g=F+adk_`7gBKV5fi#gt2Q2;zp zniwNB6?;MI`e$PLwQ7HF3aX-6s_R&YJsS782^HZX0YYC5O6sX=C6J^?D66QYnr#Y! z4o4GcVe91Vg1~adq%kCfY@99~&l*96S8HTvn*ZoGQxG2E41Dl`p%Gfbv>!Eq4NsDK z1buYkK#_6ejL3Rvk1JF{M%x{=vG!xGyLc3&KHjwWFD-DQZ;O~ai*QXnW(c4p_G;-l zD)cpfz76aZvzkTs_tRya+QVK32xfMR=etbo?=3?HpSzjfhqcB@^2PA4SG9XX>;TLrIUod=X+=mLV;T>Kic!`=*T?X=5Lb$8l!5X5B)A{%PG97V*!R;3A` zgF59ZMBa3M9TcZU=Izi(UFY$3G4>`=LlLD)|5cI9KbPxrt#P(hsf^4h-r);ewd$$e z<9P}9&W>}v^hbiYo;DjQBcF~k#`|xlMz9V8U9N)?#6DVwe@>ANe`u?+oPvZjNBt0z za~*xBl0f11Ri93S{T-kH>PL*BRrR$a z0x9YqaGmg@?z7jXpy=-;0M@rM35R9#WyrGM`61vbCx8*P)Z4DWbIJPFslI{WrWT;` z({oE@3)Z7LB4(ScZ=i)WGPrn3>|7K7>|#qiiU>Ji=B|AsOZpwVgK1f_NM#o3pfG!g zc*3T~-T#D|0#q+ve|Ia8TAn1?v{$bVrDZV3o+2>6ScSETM@geMRX|Y% zZ62e3|KkZi67Im!q%ac7&>90^5db15WcHeTO@i1(Z4H_ctB6wp6#Mo{N!8zl>@WDb z1JANB6xZO>j}B)5sxzGr&YZw^g7>vll(?IXh@%?<7MXw{gavi&=HW?%cq?whL1T@* z5KrLo&+3oiDYfPO?yL>b)~Ub8mBH(63p9TISKS?y7cJaFW460#ZFl`kIsv*+Ox z%7#BQCSX=`fiyZ#f>sMkIK*^gMPPhh~8br4tV7{q>YmNF1$2 z>hRZvj>8*N!vDsF{gjmF(CDMyMMf>B&vE|!*J6alEz$>~i=m-6f!lTMEAaymnW7;y z3I8iLPa;0|GrVjXqj4I#II^5Cg8jTk+11y%yTyEK0DvqRg5A{c?408#ZFgq*Kig%s zKZTT$w4=1|X=@Y&qXJLjnsZ7<1V@$&{`x6Ge{o$1b?X$Yv#`emD*FFcz<+L`|G%p` zez@Mv@LLN2CjY;*hu=HEzs!t(c9(yd8NW^`U_Gg^zj@7qY|X;W5Q*lCsey1pBgDe0 zJ(J+IaG_7f)4dc>G`i^+0pWjkaIRT6Ly~mPtU_EW7FaFKmUHI~1foBQu+J|IzJjnl zE?$@xphR0}bBzFnfYhG=kS~?h?0~oE?-M<>xulUbOPJ6LIYtR|bU_Y{6M+YtAlOj> z2G*4{Vx;l;|DbS=jy^+A(&~@-dVz>+_HFfP^X%J5GkF+rFiQnP57D{hYdQ`XPJs)q zLn)LP8-b3fT(KWjC!CfIsOc1{?SaIqYvty2)<_iy-8FH%`>Ke*sR1&tejP6DoN#>T zgsj2YjlN0Vuj@_P7zue}A-D&G`BdXqgADOoP5X`;FKK@?-14w>h8mQr17pim1TYx@ z{mALk;a5Vxaqy^1Fl5E0Ve&rJ8~B}__V8fg2she|s&(vjSL50TW|4w!?_$}5AFpX` zHX%)AAsjD=ypWP}BV=B40!Oq;_Ij+1D#{E7T%kg@RMuCK>%!*UR6cf)^E}cmR`K*# z<-eNbM3h}O8ugS*Ej`|PYT44mgUy>WDAxh0vpYk9Ta+=}9f||DE5ASD3*ol zaJ`K7QiyFW5p!5-gnH)YS~~{+u{}Q|@Qi*VvCXG0KE5GKkmCl0$P1e{Z`QlC+9fD? z08dp#Bvh*pj3|oTKrSQOOE)0Aw|azrBir~&79vdW>Z-ff?mCpnGM|}fm2__=c^^4F z$iWc!$))$ggkO)uKS=~Snzj82*Ms?t^ayClcl=;p46VNr&p0dJmVdLfrn(>Rr(dI# zuoPAZ{uGfr9+w*-=}|5HVDvT-sgEC(eErG3S#x=OUbAXF`KY^z>^7=Ry7Y&>lYh;0KpN92H1Z?B76O^p`?JMYX&~L7g5v-A zDhP8Mp;JKhCm$OU;HnEyN4Cor3Ko^fw8oRZkg)sJ_?AIQXqlY4!m?7NPeK3WvjmT* z?roZdbtwD_*nj;Goj{$vt~H4Zv0lpRZFCXe^IU#z?e-Eu<{7)>Q@YuouI@|1BHJyT z2oUOx-S`F0HY=dz-S~jwc{oteR@+xu#~}E|Cx>goC;GNyM|X@i5q>iV6i72J4{6rb zKiAdl#9Kis!MRf?LmLH|+)cdpcvqb9pQ z5dUY_ZoYt)_o?w)4k-uthJAChD{+svujOu$_MA!y6l(9V%OG`NX%Fr>JN3dXEnlHt zle&7+(KgS$ZriV6lfU?fwyn>}OcqXZ|5xDrC>zXPATq8zO06ZDH@ zmA->X&|8GQ(u2d`kY6ut$|76RdCPrXX^P$2S!Q)-fSM|BBCuorbMHOs9+L_)f_pdF zw9=gG36b)JsK#QE(;+0~YKT0`f|wHbl&R*0TD?Z0co9+n3sgtU`CiHH$v?B3kBE3* zzF)P!U3HD39kFF%rGsUY#qm;xv%(&92`nXcY8kf`|2e+VbL?C>?{%%7B1TQNAnDqC z|3p`gJv6}Qmbt8Yuq8x10~@crvLAvV2+)K8G|$T;sd9}Po~tCVYXB=f>)2N3({QRR zAV;K}4r9P@fEWfiCrQ7xjCz- zt)Hz|= zE^yz-7M{C#D^p#r>zno4&LJnO6pA$43ht-+Z{~J)y0;N)nyj|l7rLgnXX0zrV+=us zm&NpB!-uie!fDqz!RR&{OQav=sAU3I&{h@f^4TLYS=xBg>dO+bg7jy&x{gg24PPKS z8C@?to{YlBI4n`Ct1oD;W~zqUt7fNPH0kB>5@*^z4Y95eRSKY z(qO~^)Gn0f(H-v}cPJo$m z9=SN2Z6}woJv2k?&FBBmeyvMDEQ{faQb`?pZ0t2{)F3zG3bXj3M;w@(hbCxMA641UG?IQt_)W=HT51Aq3kHb-9#&GZ zi>W6riu3RrAXTd@!_wr>u&K=u>gr!ndF@TK$DDK2bhuvb2htwW!@_=p$Opj`SG6lJ zm%l+CKUKAdsmjC&?ae{Tp*{6JoUQ{+zE))v9HW5zsq!}w$)ak$BeR#SjlX=HRQW;n z`oNmgH(`}7{Bq66A%#&$?v%--+GS3ajVLY(Y0cYCB%0HBN2P9COPE*?JlAadS&+WL z>2jN|#%V2Z#fY;UwSZ_vH0k5>D-$MO-;)N6^w~Li5We{Z=KY3ood^_Mq_Edv{E=dLa~qk z%tiLco5CrTb1&kKaV?|TTwykdSeG(wX0$PGw^bF`YdycM&Q6i5pJ>-yHE#55=-tR_ zu7x5gURc}JuXb1NA%$U&fZiW&XI8#hd%oMMWlC&87-?pPaH*+?So( zT(ws}Hk9|$wJjA7P8CJM@wsD=D+fZbwezD!C(jM>%gk%v5(qXQ5h7F&F`xSqbL;sAtRWgI_0stS5RieV--Z8;PA#BGniC>hvq$OqHL>TG`lz_EV8BeU&E|tbguc zg`bGf_u4!dHPeKK?@AL`OR>pXZFGJK>A>llzf-c(y$w~>dH3flLhOX4bFI%k@DvFm z>z_NSSAXfaaIs-opJ>pO24OB`jfGMzLT%D*wzLKRJw7@+(#~JGbrC_29Ns6^sBdX~ zGTRc}Ulvuapt#pn7%r|)Ub(j{x^MS^m9f}xxA^i@IZwVMo;*$KJn1M{E~zHI^a;d| z+DM7Lk2h;9x;=2w2_ws9C*qf(f#+cu#(PGFV!DK`~J9 zJa3=OVMFUzCLC=T7iW>}3Pz98Po~O&x8}q1gU1rNgSVIzn5da{rUu@+jLv1ow3&(M zR4~p2N33Y3XYZ%+S7dD2GU67|$})bQ6x?0S^UP7NU1pT>SaLJl+nsiK{yOCZ4|~Ke z$EHP3>ZakfUG0R=vgUdooo9zG5IN1=iwTM77*QE3*AcCYvx#qe^b1^RmPNPv)ZmAb zY>N)vx+?WKgu{@C)s+2}jKu9`xd#zFjQr|z($TqhS4ox29MiH?5{@4w51o>6-tR(b@@$|rDQ;Ad1or~1ez%csWdV1-+=oD1<(XYY*L-;5Mz_R(lb6O?Xzqhm=X7N*?>Fb-y0!Kt z1m$-<5@0DT*}dO~v8o{ELBz6NON*GBe#|+9E1jjiCx4zUBjvNHbddwMXZkxmyqfZ| z#oi{r!``N(a&M(?;@p&Ky}$HGvUceDd@SejPEsKqsmZOB4p~LWCvCql?$8F=@OX<{fN^w~7SbXeH6j6G{(nDsnO`mioV!@R~Kt6(BW<;jmy!Be7aieYM;P#H< zo2If=QrFfq{G&jERkw@m!3dv=u_MX)-u=_O zd-qI8eq?X@xZAv`@Tb$fE^VnEOc(leU!)$PY~g4dP!;~PRob$(shCu*pn^}>(a9OP(@^}T~C!zvVXUhofk_%ahhb#xD)V}P%XpND_#UyS{FO6&0acg~u z%?Ws)!#}b<&&g6ae0M#bV`_6cN?ulSKDR1w2~JwUvg84Mi!~O`T0g zHdZlrO<7Pa%yqK-?}x5W`*QQYCpzRDQ6L~DtzN0nr37n{#L!_pHyDZ+USQ zgwu2~_P-ephR9m-D4M=`wMMWR{N~56Y>#UfUyy=;IacIAZOS9&mtRxa4AhD3UPjMY zavygsnsk0SG-iZf<4>!8ls+^z&t3*&9Q{;(iV`)+)W)8MF|bR8=3E|qe*CJdM*5cNrNPSilVi^D?@hK}MM7(+w0osP|7p`v=PFb#W>>anaVt$! zJn2Z`YC+dW9vv%x6>e?|>0uiCVuzw^$5-ytuRRu*9`tU{YN=G|#IAGDeOq)x1>@Ru zk_vc#f#o>N$gdOUpZ=lHk*3XYS-qsRt-uW(q|ydpY1yDGprn+4 z7B1<>z^HY#H|q#p%X*h>oZ@1U$YOEsw2YTg!zqHf-n)5SnwOOG?79-$Z0&49w)(*V zz>F`$Ey0;4*VVdLrs@8kR`)Om#&X&9@;l1(Zjm0kiH%zX@eO0rD2rG8D?Kv`fds+< z!zu^9rN~$llr}6ov~X*(8Q3{5V!HKk2{a%f5gtRw7qzp@iuN3rm*8=!X1 z@;^1*#E^OoEI>k=Y30#%g6)8D%0UZmudzGSY}dA!Qzoa2)NJiWEw0oTW`}N0*m@_R zSz{OvyfmPXA(={yZ6c`*V+LosT-cU}JyUL6-8MAn|3Travgb-CD37n>KJ>N~tT_*m;-Sv=g z9y6;->UtcR&CV2Lw<$|!bEqA>{$9~Ce2&cpcDuLn^;Pf6WK0%~MTRe83@hrW78c!t zLpUY4#aax)1iKAmp19cCtrp*bj=4-)fE$`O_nAP z#qB{Dm!LmVYNq#WA7&Lf8MmIBrX!#tto@T}HNv;TWA}>P3?|#`Q)Qg0>|eI%{lU5_lsh-> znf)W3R}FP%w?zVu>YO`}i$LKTFJGa?SnQt<+9Za;$OSeFF%=^g;GqKR0XnmOH< zYtq=GIcMK9erf+`J+%j-vsq``e?KpD-_!7ENHMVy?_hC%z>38^i8HMZa!)pGyeSRU z@GQz6rB9Ykj;3S1J{kfFi6u>iX&)JL?J~6~UtxZ+iX3m0ipaGAm4=4r)*hYeQ4>9*^G9>IYECKL&xVshEn~{GpFmCdHNSa+SJkx-h3-(ZiS~o^VU~LMP$2lgy@n$q)|g9xfl85@<*{&9>(!2 zO6HTh!L_O)=|mw!%^8w-jsZ%CGPf>In61(7D|ER(h|Hm z>8TBvVCmM<1nILn2L7v?ZNY0Zzq*l=KI1K4Sz+I(Kl;Lcd&t);F({%&m;3xRyRb@^ zNA}Lmjx!(e;(eXcVok5mcKY#SO1 zJT4CiK$DsLJ3+T|^ixG)I9x+3CtF;mi$WJdO!a6|mRoUd3M9H%wHU=?`U-BEt9e=0 z_DM0~GA5tb$^F39Cz$wT!+$NB($tfoB5`JDe#>C8%OfmKUmbVZu=+uwd2=LTG=}Tt z_vvSW4Xn{=jL9Vnz0p>4tC4Ii`bG=QE(y)WOw;?uEg_zxrkgpE2J+^{61U{Jez6PD z(a@BkL4awBiS6#N>hC1wNY;Hj6WD3UyO=Jct4`cS92#3cf_v4rC^7dLkyk}qSI;Wt z;$nudiq%jo#qB`g%?499wHTCj_70oY6^>@8GY>y>&6_9>h|dkzk7v>=PgoXotcn10 z-Erk-vuFIM6<^;w+PBJe$#bhK@soRfLs@Tz{vzS4iCoP#0)e_G#q(1#p~ZyhsZPcF zc`MXbS3Q|Fn1)gfFQKhq=VAv$z!B-%nVB_<|E>yV&aO%LE5)+bd!9KX0*m+?1v{U= z4M+as0ifp?xpEE#p)je^O_H?=We=vQ_J@R1-`!Q8Uizx3e!CSo{+Zhb2}hesR_1cU ztzMYkY}zsv7y^maM$i=I8ee!;u^Qi~ze@Xgv`dv<>&kMpy!je<@9?V{sd;Zj|xnsff(GD2;D3x}5bjyC*ZytHI z|16VWL{r?1ZKr##M?%^NQ_fS=ai($3w(`_>>Q{9VVlnYaf}>xIJ@SNwlt0(lq@p%pu?4l7pu#~%UVvZG=>ROQF-V`BKQ-91e{G801!~M5(&eGPSwj-xQ&&OUO})QK?ax9nVn9lT8&U=^Q{jR@kw}A8!Sm z2G|5$ajk4))AxQ~0Vw|8hzIyLHU-*N{HuKVdn5R-I`E%$?f*J;Ao$|{=PCmAp1!fN z0+Cd{cYRUt)H4c8+in;^Np7?CP@Ea)h}7K%9*<0?hF@A-0ut~Hw=I5|guRyX-)OZk z|L0n*8Qy%>pGOhBx*LD1wRp4@2V8Gau<{}SeU6JO$i+|xYMg3lyU#|t3>C9L-8H;h zrR{&^3g=oCO8oQeV08r=sO70I9KL1@P@`cygxrJZ0yQ$eW}v?+JOi{UGtg?J_x`5U zD(fKnORLq*BAFXD>J zkm&VTC|YDp#SQSUS~q#-efGxkgg$8Q0F%N!;Kv;ms3vyG`%gqkC6G-&+nq{i>ANK* zI|p^W04)|n5Q)7M0;og;nSK6~aw7j*%1N=Odg4Eavg4mOMKBySD7KGXK zLSJ_yw^5_hE8)TMeIQW8C~ecp>?Lp>R=8cTI0*rRXb)tG%G_W9 zim4Tq^dC=LK~M|NfsW4dTsxGDvj=hJTR>hQt|FKHknJ)}7dXh{9QnTj6K+9qo`dDq zFMoiqG`{+LB+v+4jrNL(yWSOQ>L-m_!~^_iy*r?aVh>6(u>{3NHkc-tiC*y;3=PIRMu@j1kA=cBN|5C75#h&OF&@7x2Tit{0NGs3?3dr&UP;Vf?2_keq7 zK)cpVs!5lg12tg?EZz#I2U{eaLmU3fj$g?PzVC6@BzpCDih&V#5BhXB6CL}ZcmrK1 zM#dg;zCem02PX1O@# zeQ8+}&37aD@#$PN*YY@f8FhAFmxEFm)zfpJWE|1rEYjI2ZNCYbM~Vj+z6xxzAn!@m z-JUGUK=`eGDDkvkB;?jb zccg{ggh%R?&v~7yMr$_%bzcE$bq!FL20fLx_zG|>B`#28oPUR|h}Z0cS)6029EO36 zSUa%oJ$1qt1hS@}r?S{?Uq;iOg76tS)oA0ql^tLvC()cerQ~j&f&-LV9Wy=sX)k2+ za{j;`_hXzmXLA+U_WJT4G8b_GBlc6UT0YzOB;M>Wng*|YJXtvq@*&n7D30E1Y9))Z z^7n^&BZ*Xmd}7*!pN(!r4|xF_*^|2qz%*yEoxfZY?zv@9Al+TDmFddT3%Lzbb)U** z$WHd9s2oMd)dI7#EBzBrXS`$RCNKg?piG2CP)}EhSO?_9a|=(NDBj}O%%x$x-@Z;e z6CM@(IejlCFT^Eu&$aKvAe?fr7fR+-C1qb}Qy1cXzhpSkJV}wupr-5T#0zT2SJhLz zb$R1sSUlHd#YI`YFBkEf=^>fk19MYN^ii>dK~e3!zte8+DM1REJE#@}zm`fB>Y;U3 zf~-o`>`x{ojYs*`Tl*o;dO)s}reHxOfP!N|PE?a&>1*US(EYUnZl-;u%??PY7Zn#A zQ)#`JpBpWIO4*zPbu0PxI%0C#?D5l(PjF+ZIAGkdwHJ|> zM)*C88+&|u#W@6r!oiDh#jcW_rJ@qgbD4^q0kR+FcYksS#X-gCSQ<*vWiGO~Jk-5Z zbv0t&gIt3*NY-B@=$*!vc;wvJM{k<4`Krp%o2<{{FzX4zAQ;2lXO?8zblXOt1Rtk4 zxn8>pYL{ga9Ok7ME8L!0NU$=P!8MuMwdsL+Q~2z=d1ZfP-~(B3BiMz5%$0kvRgXQG z4y}P={EVxob$Lm5v3EriqB>G9nO$oW4z<~hJ>+!2Jc?r~k^O2_dBLCF356!Cps=`z zcF%LvqGowYZhkxamDRWw-cm^*U)h8%U+`Gl-ry1_mOq%EOUuf6jbMHQ;XBf<>$19< zB~R1#Q{XTs2S^>JhG`mUlJR)M`W;kUR)ALwsjdZfTn7tQC3`P8#=LBxD4yQ;Sb|uw z6NQzWtCGh+m{*8ov~TQEcnJ9O@qCJeIw+=L9FZN%em~yKsX!Kp^5`Fr*vIepq*(JR z)NQYrR7MTO#(jWwRe-PpHBs~I!$%ywT)f6&@@e;FEtED$_mM}DHv@7&woB5)K)`n> z&1!tHPdwiC7$l7~m@o9}Q6(l(__Ez^>yNUsKOeQ?n52haK*2)gT@OlRHSgl`aR}?2 z;S`q7RX)xkwlCJ0j>7AJQI4LajGxM#?=`GAH=lm}QM7TaF3hc>d};g%GPq)%C*qCG zMzPQVA}ZZOV}uD3tsgd+&DH~KF@Mm~!Bqz;w;z9osGcC9&UY5{{&g-r(AConXsv9> z;cTCW^pPkTb$9L+X*)5>i)m|*w8a%?G64OP+V~eokf-+4JCE4hdIVQwLs@ckBgWSf zCk2PnO;!^3e3iSIcdRWkE)vL^d;*f|LOysw)ba=ERcBdQ1DUk+8KAN&&{J*P9rDIW z7-3H^S%%u(e|*a~y8rcbgeWO_KT@XdgcLMeslL%nyCnb&%=x~`Bj;6wXyxao#^{V@ zA9{J|^u`C{b5vPR<*yXflX7iFDbs=S_se`$u0ikIW!DPiT3Ao?IpFnb8?{E=_Vxlz&0LGxKS4>-PS5137b^M2Y4kZJ*%c0*6na;{Lvi;>1(9LsV^X;su2hKGtYDt@JztG$vkgstI z)gsFHjS7+u>E6c5A$^eS{SQyhCj`9dT*iEJb!xrRN~cEDSY(}hXZL{2=iSCYl68U; zp~d-Lh?bvxo!Tm*aFuuBv}Y$ED`1gN>Os7bUaG)xg7vGcSV!GHwh=3iOvbBRciFRy zZsN}ePOG))h?(qE7->|K$(Iv$YORqUFYO+`D9_z}BOqcNT17Ni0$x#dp44ssQYYllfkXAMWOH(> ziuX%A`r!{mstN6APNg+rKj&@m=rBntfHhig3+tBt6dZwVo(kO%!UR=3)@14N$o)!Y z*BvNy#c4kkKEL4vW$d}dvYjhfEEU&pYdSggd5tWxO(ZY=xPoq0J7rVl0cH$(DNxW| za*Ub302s@YOq%;IT_uZ~5(hY5q1zmCL?4#z^WQgN;Ei9X`=N?)xg(6O3R$`jqvP5j zSqITF)!>y}3!^Y$P3h`^7)NrTPS7*6DwCb0H5RiA1zm_J3K*LA=5! zB`nw7PzI<>+Ea^DT&tKIR##T*z0w%`>q9e@Rg zbkL`tcG+8Dn-x1oWt82La-1w`UFo?p)h;I;!MB{)jXV{QFXyiu6zXdxwDVYo+ zqFxyeWyGW?YHp5*@jjMSZPfW7+IzX|5N{&66mLK~maDqg6NFksXkq4Mom+SD-hd+` zl;$H{B^)ClCgA(9pWcMoHpkcWKelNe1acPAnz}f*H{hT4m~`tHjl&v3h^CmFWHt92 zBX!`bg4(AzrAb*Yj)bmH%y`vc$IM41FV}4@OKilmx}WWKstFT@2@j4MQ4dspUP;+Y z`O8Ed`k6QKX;I~q+;JBDg6gp%kN)kr#?3MBNfni45BGKx$rHKFXN?Mt{kF{x+WR3! zZ6=bBt>P|E!Y>mTVYGTCJ)vjWO;DKKgCfZ^)k4{U`wE1^4mz1tv$_2{auVrq-2E9d zAbV7s72xL8BJ715=WnLF8GoFB4aJzKjqW84w%Dde{z%y{(b_+@%?h8`TyY+#y&2|O zRaO1W*+IMH7jXE41?)zgd7a$lO1tP|z;0LXj?+*@*!aCdxX?H5I+hT~_lJ zBzm5$4!Tf~HK_|E1MUfNZJEuNwff%Bc72N0w7-D*`)UKoRI0Ci+VB3}JuNduP1<=z zYfdMNhx1wV#`&^Cgwc9jf=^1MYO-jl4ojy3{U+u?V%6fitg!r5$-+siQ~peW`*nn( zoZ1rm2szn(TO+3QWJ~T?urz1{)p^XaN1mC|6!{WL&nn9=N|!a>BWJ>{M17A3v8FMOr@dXVwNw3TX|BTl zDTd@z-gR|WU=15DN3IPiOg!eubdjWyC+$>eCJ`=@Hj|gDk$R)C5h1EGa>)9XPUUeB z$eXWk*mf!@$o<$=$9u`$xGt|sI+Fi+`mvhT$&fc#Z{Z1b#+t9k_ntKY>%xLB3b5 zwVBfwP&HO?ZZe0yZuUW@tH*?T)A*prF;D(VFa29K=>l^>B1_au={M$LzyV>A!ngb- z8^an&&gM$d=9g3cuf`mu3P?J|JGL}HVNe9sM-?G(hv;uVG%k$=CEYzi%%AFL zNwz9$lk3wD7u6H>+PQ+=CM#giXEaWfgg3k(Bh-lwslF~}aTYf%IA?zRCq!Ybxy1uZ z5TDz*x#fa9Z=7uOON@we{LHIWT8{gwwY38d&l_p>XFP$ssaW$`1=Hpw-21}h0lEim zh$5QQXAu7xXMZgjF~B~jC5ZVDNltQ14!h3|_)oU>z67PE%PPqX$rN<+&mjix73LN^ zqs49pb(k`%Omb^|T#%ylfw#`ruk4*9is$T&VqvkZ${WQ=gelbAL;Y8U?EOt;E>1%g^r*51>S7-1(=vgbTok?4iMkPl%wFS7}`B&T)jn4n|QOEU)I z;*e@DLi7jl=%+2i8<9TI6tQ?^!xO~C3a%`z+)j#J=7If@6Xf5{1^5$uQcVt%?%Gyb zCH3Ou?mZOIytgm6W_v27E@z>tNxo?&Tpq6F52K8sa9Xf8K}huRkoE4n>Vv@6*%ki-=ep6{J0;es3X@Bp2QBI%e~_?0 zkkWJ<4dtWx>OQK?E%cwm^%#bu+m*!kaQly1B?isaroPzj~tvNrPR56PNbueUfF!3;FU}S}D;n3L;X;hNpeg$Za^BDK!98JnrV0{)QT2%y}NVA6jO)xWNVKOG33n0=s3_4_Z9s{_Ai{o~D_SL?4_1m9L5 zCAq$n^zPR0`Vv}gpe1p9e*G6-4F1sa-+UtwJn?7miiqBKWMfR7*bD8f-=oR=H6I@S z-mTEme$RGL@Hhq>pR=I8=B46Jkt!H18Z#*8^_#C@$;13@JQAdl%mieArarJH;EDdD zuKD}BbkP3AK7N%*))uz`MfXs;aHxEX<{#s97YXE6+ykJprBmke^p}6K0IK?74<<&* z0pn=Ag3_8B_otG*!I|7P>h|H%#hZF6D7aPB z9vfK#IhG#ntxtxOQu*ubYqy)Cs9zzWM$uEC$>79LWGshJ5MI67O}OB5`c=i-;)#juuZ)J?M^yi4Id*Av5D?T3BF}JW(_9wY%E7djRR=cuHV{;;&Qn5oTj{To z$-n>OyMRZ)F@r*|ac%>0=Ji206AYxmDiR2~InJDelxPC*7Qwcl^(+9y*XNi;8Yk!n zLgn8tjp8(DdBPXb#>$31N?O1g-gph+4GWzO+$3}3n zD{$qv15}gH9R*aYU9g@XjCQV=q`4aNfHJjft(siJZ(L3mf`Sr_lNbQ&aGj0CcLq%A zDO`0S@X`gKkG}zG(B!x*ctAzYBA+FPmGj$R+8bwvqaYKEB#dw4-S>%SHb(fTf42FV z(ks!tAYv2rhahnnGQf+!*@E`=04Vn7dUS>*NEE{Y^(?ZV^@B*;0QrCm>B-UtT^&H` z+DDP^fZ2JC0NvsJX@h{!I3hrXDq{7)pU}bT<2-0G#SJ|VYXC~@hSv{%TptA3gbId2 zEk5c19cu%H%|uOzej6g}BYK47@3#@ExZcMZjv6r5lFcBcxmk^(OU!MPxo+(()L+%% z1f?FGBwc9OwxV3SbqszRei+ZLA>=4Jt7%KRT7cS_CQ# zyfA^iu9~mm!WTT?i;~#Un>Q1l|Ivka`yD;&b2n5dVob^j6b5tC@B=1f$906w-dc6FB zF9tU|h|G;1D(chU>!!o%Bd7zD7L_)eFeJwKwzm2E98`mSYn9*MPV@FVBJ3CT6lkq$ z=;2ZX({DdT6mg9|Q90atgRc7}5+c7& zQvLl?1d94}(`*F)Gm8WFks7TeRE*w@JZDspTO zq1cJ%7yASJC6h1C9{%zBbuvsvc%JxD;Gp`W3|#jUbX$W!#*;!3w?mf>3gH7Hh%@qq zpRbs}jPM06cR*k`8orK;eho-sYXh16_$HL8U`bhoz3o#6na$CGPUDTW^}_pfF?X8Jbs!pU35&26%N9({ z@OB|Az_XSD;F$X>nV^pV3#%Z9ZIW3oI%IyIkGu^|j`B6&UzbWpa*5aA*J$70WlIeG zW7hCJqUcBaVi`A_c@w#yCnN#g5Y_KF+B(>{lB!WrfK0M()Si)j-o{s*WbT$OAU`oZS%A~hi=Rs;D>CUDBnyL3XSTuvl!$ISr90*wy zEU?ESKSp6Esnh^P6ya@vPbU{%)$lhO0r?`g@H9;`6AnSz-u(*n{r@~c=`&g=FqW^i z@uB)*hrZH(3&z*_GoY*%Eil*p=wZiT)Q+~ zY7E}7t9-bB*Br-ZlTWA^Yw`li5#oz-4Zj+VnQxi1I?A=${T_V@jQKoJO}tBz6)BAj9vPIG-!>N6?2z>W+c0W!qYlvXFVO zK?=0?p2oOeL4-jYn2-e5M7J>wpFZx9)Bsa+frohgbf=jdq@R$#gcv-v$aOB-D=V{N zWb%=Zy?7Xo(ZfjjxuAkWvpc#!+aG^ERCAG3pXc4eg}v>}W_8~xd*tCFFi6x#OU7n5 zuB!GDe2m8Rc6hw57orrf1RX75*A;UmY!M*#zf;|^R`T^} zZujq=Jxc|@e{=c>WEg6+gs<9ALgC9aJ~#AHxr`!9zYKez#CU~O48Ldt0rc!4#N5TP zg~eHaZY$)$j?5~?TI%;Z`1ZSSkyyRbV>$XmUSrw(KYoB-Nfi79Qkuq}-?Huo=oS93 z%2fTw=lb`xf8UJ%yT`%NgO$g)FQM*>ruFZ4r;^?fa(;&lDWO>-V7B!~r0GIUIr=$A zGZt#~|9D*nG4iX-*STR^gdSi!@`fyE^RMmuua$-BX@(qZJhF8jDy9W#K{!1U)l^kS zg0{cE3xOTdN4bv*AC(@eJi3XyLcx;&_KHPoyERRJ%#jQc3?$Mij?ZGX+J8JoK>xHK zs2WBQsJQp*NC15Rr#q|!g%UhBCev`Kqr9V zE!rFX{zy^E;0HUM`YbZR8-MkE3dxuHZv z{j-zZ1sm_vXEoqlj1k{JI!)g&dbdZ9l}BP|`ws}@#|npm-EBPcA!Zh zf`rFSjB)CK+j0m1rHQ<&DWGgy*79z^3(Ksrupo-jSERIcAl2+80A`tK;&EbFdI6bArBZ$}g znY0t%;4@yb$-hx8u#uuoGJRXRo|_y9RCerr5Tpa(n43@@Tm8b2xgG2SCMqZwng0%i zX&h%D-3Bv7;vx9-{kIjXx@nUOa|+jy`-{&&Hr?It(ehc6p*J9TbHa-+W)*LiP9-;w zHOvU`pmWZ!U-@x-6_CoAhPhXy^TJaqn&qEd6slk{l7FVK)XRUL`x?HOz;=#xrwd#v{vGwjmmRtmxT5M z9!S4*I;QajkDvE?bL{6#)~rY2VQSd3;5}IZuJy@!1rXgTzRkQkZ{1edy_%U0N=r( zbXj;CaSq1Dfd?diw*#>l$z~PYzmIt4z59ffNx9JU`}L~77r1xnEp#A7Fior(fROru zgvPI$E6#wH51?vIge1P2sQ#Sx>rf(*@}R43$t*l4_5|8zBpUbx4@w zpfiWl13;CSIW=vipo-`vz~h8x08zcLnrvfTqodi?-6>xP9o|!%)<8XjBKe`4HUI+` zcg)=TI4eO^=|>}&F4Sdk4b$pKx~GI3|6+&ys20qHPaQnmeeBc97cL-8vy2tsaUBM^ zrzst$P}m$)<7EppK8D=*bGR^8!`S&mom3!_r%nRV8IL?CNJCJjmW&d# zX2cEl4!b~+ioFfu>D3hm5+CAeP#1ec$6o*{)|fa2>J$S&p)4rIV1ew(#xhXShjg~u zMSZ^nYWO?74&d}31R&lf6XGduFp2ckLA=0HL??vKk?lOQX^hpTsnonYP%t?=}fIm130dVDOu)LvLwOvm5Z0f~}^I*C(9xu=#FX1-(26xw1P7i=4KTDBpfThW~u=98gDEUG+bAN0{x1#(6Gl!n|RQ2Chk-H`A z4OW5YChbs!s>rEtW>)3bbbzksB1W%Qwwn5nZa;UgBsBBpHYd*$Kuo@{q-yTxJJYGx zvyGCb4OW9C4880?KwV)3un}9Ev1gygQ3HF1hu|OQm)~BUG}Y7)f*LcitH7G(a%T|; zEHDf@!!V)NE20UgmyjUdd)p!XO$oUAT8fiLpyh15We{{7kbvTlk`q@iPrMOZVSuq* zo2EE9oA<|B>{e9l2Of&q0(TG4@PRgW(!ss>0x|-iy4fZ;1v>)VCU7ZSSf~k3PE-w{ zxG+{z#|m>%`O88`pFmU~1%s$A>znWn1_G)7XRNHt0nq5hD=)M93s||=6aJhS7svZNM0-)kE zwumrtxf7E_qd4@ubT2n1#!Zz8#~r%XH8qgEOK_!BD6Y^Qw4Hpi=g=Z_7`TgcYa{K) zyco*W+ujMRp8@8xh=@OuwrypDlH=L*&cK3$yP3MDyzRbSOIJjsS>%m03ns<~I^DWK ztYZjXH*r0}ygDy>o%D8ZG8QUG0cc;7YDvIx+Jmw`Z6AhwUxK)VTk16)JD~)0;>RGIoiPX0<2QpUUX>LQ~1ZXv0~6Z?gBjTWt$4F z-xR?6`7Rpgc_6Gl?Mi~rJ`@`a^Fb(-=`wc+fD#maaMNucYLl9#dlml#YVKBLMv2!E z41W3y(0eZCTq_6H96lo8A=mgV|^$y-)XV_v&7<-kY)mPrM!cDWmLc3 zGlWPur7xW?Q!72{)|*)dq<4-VnCb_8H`2+JH%_yo6<$xFr65u=vc z;^%v*dM@Z{`0w}6FxL^DI1LVEuRfNk%pGXilf*=)$0UdZ?|g+M$1Y_0Jx#pf@MK%I zmh7LGy9i@QDEnJ9PF8^w@=JzYZ!7O=y9(V@xdSD3ZiLzsWpnnZP!8$y+}CBQlI69{7#@_?rCAWQ(Y*-LiScNQzT)Hdb~2hG zZI;Nbq~^K!qL*UefpYbf#8OI{qMP@185jyJfxyPQtVKjbVCIOe2?0xE)mHk)CNvqg zr3dL=pw^=!Gm{B!e!G<{ScC#x>2_Nq9QcjX)?|?s514n(^>{MgKrQS87ppgfDzSU_FNe6`eoiuH>2k!e)0(C>BR8|Y$xdf$n0Im|S=BT5 zTHJxziw(Vrb3?&pZ$(O9agXEamO>nA#HH8xE$ATABgY|5y$S5&h$fb;)F?k^WlD?0 zE6#|>_Mp0$rGm0wn#RzQ_ji*#BWN+l&tP&)OI{LTA7q|iTJm2;v*4eaRx-(pMfJ$G zC-E%`Ra;J>txi2+Txuk|NJ?jmE}KJ6Sx+0-VpCyuyZ&s~NN^xtGMrUP)b$IOplQs@ zdB*g2CKnX_JWC1bjO5(wEq;{Idz_rPgpe5OWL(!vnR{yHaB-h?_;q;Ed#uGiFT$sLr6q%q11KZde2}Sj)VHv70)VqSg*K)?@0hZs1 zs`hYYH!HN#$HdIXOaa-EU6RFd6HIc5lW^gBTqj^x~J`)AnQa+J54X>Il z90@}K;ge*~78A#IKEu*=%ucF?QNQ6F`@%B$2c?uHr{RNohYcUl23*~pci{YTZ)mg+ z+b}TH8@M`N3u~d(oF7duc?$v1)=H?p`6mD@f_v2(W8rC1OnECs?}?yxX4zXJK(8u#==F)4Li;OgS1%~#kTMgiETP$O-279sw&qM^%;WBP5@h1oV^r_Yx4%xZsO7kb$@h6bd*>27~BFjVE*^=7$z#Nmu2 zF1?Pn=;y?h6Zw?MpxB4~JvgdX%qjG!BFIb0l`_XeTtckm9mJvRF z45y=|y)i806xnwJxBT79_ZXVJF80}*^QI!fgvT7HB&FqASXSK3UMXrA$!cwUJoBbA zBxb4-2m_rAS)~Zk&uW6gXJy@d*Ne=?459_oE@tx{$+F(AP=4;JnKumgZ)6(3-#FR6 zo6;xQ*{P)LC%Qc;C+Iab`(kfsx=%?7iJojY5(l^DiM&x&>IFylb}|N;M+p&T>p*d5 ztVGlPOkQ&HVuCiyX_}j?Q|I|7*#63_u77J`)Xv`okSTKn>5py_qA9{%mEx_-ox*f1 z)QW06BNolPv!aG71LC5syy!{9W#_t+Jp)}#C>fO>i5oQ$jWOn1dacv&Y2?cM>oIc@XGYl7D7Z!-}snXzf9rlEE z{D9rb2_xL@rKVMNLt!8nRO1|5Z|t5VU+OE9I_}2o*o9TfyOpBnzT5Ob;6WrFXl>Ra zF-=w#HW|6yv5pI4mx#%XKwvPKp;=zsBf~9&#-Kl2;NZeU<;Or?^VO-r@|{G2APh;^ zc^@58;}){eS-;e@{N@N%&v;j6abBhOPp@|DZU(E;h;%Xaj$J>hi+opr*#>exL8 z55q~DLr?yy;f*Ffr^YV_un{ziF;@9fWWT-*si7{|E6yRbDpSk-8Z8l?`=s!%Re`7PjVZ z_9mNbi7!`PtaV>cYmrPpt`3>S3w*Ojfa*7u-%4@1cnb|V@c@sboth(9^wF`4% zIPS6Y-bu`;0{{JiD;+gne(VE+J&NJhiL(Rj)5a$pPQA8Ij|Q<7-0mlK(M=ajtqIT# zt@yQkE;BGtr1Yk$k?9&zWM*PZHkE-Qz#EiWcXz7;_0kSqv@i#6JYe{n8)|7mntD@H z>5$~)F=TAQE~36UaB-BC42E0^DMXr|(Lg+rh!KgJ;#I>vkTTJVC88INHB zht|{kk1Edk>_3t7kx7fPN-Zj5Ykh3-b;z`&JX!h->J8^kf@0>YWA(6;p>40-^E*iG z9(7)AeHkf}$TBG`$OdEhb8Jm4u)~}gBfnNK-g9FdfiT@1fdjN1a4IZw7L0A4a#e7x zz0&HOu0OsE-@VPWBqk=KC8Ec}dl;KW*He1{TsM?Ll|n`Ee>i7O{H~j~j3}!(dqG=- zV~=gPP_PF=k99?wV=Qm3qQsGvQ}#V>&|t51)>PGIxyM^{T&|E3S^a{TXkw+r6u&94 zXXsaI;IB}vridT+#>8gCGuyiD5KwA982&a8EblB3{>ZeGt02ydXCIe-zSYxj9r2)D zBkindJ@TzuvP0!fr%%qW?Jf?ga&@ZD*kaEc`>JZ~yDF{#5g6hzK6TAT*N{Has&81( z&b??=yIm6=y1Vx_ErcLs#&`RUUU7(meBaeJhu12L&pq9^95v%SX|BlO>(#2u(mmR6 zoX5jt*2(+$@bXLxcjkte$)h^Vmto|RS8~U~tg@|~dMt+D>}Juy$VsLdUXu6O+oGgP z>J)@px|sylMRs<}Q^iC#Y6n}{k7uP$TdGY3y79yAtdgviIJ>^8^iUzAZ3f*3OZv5>7UVx4Llno+{{BHo*spmPcMV)jn+Z7;^w%U{#=^^CqmY z;1lbxLhA>T-_=w~3KaYjF^LV=W%{N>zeAYeC)IB6c1-RA!=)EDB^h?VyGl-e1NDq{ z$P`&u=zt*PGx`C5$vQI1=v21l`KuzQ4U4>r`Yqpnc>JOC_jewm62T%9bY%cqF70)z z=8~z5$myE$LDXz`g!D}Lz5}Z-l`qBByAG_`ot+P+rNP2zIqjq_^n9DJP~1pe@;6XF zAI))3&|fvoBBKG>7x|4qK{5hHprCoc_5OvWL5BU!BlyD|LQVpbEh4+q-#!5>vPB9L zHRx6KlCugYfn$Pcr`mgzCx7q_Us&{5KYOWE|EV^0YycEOr&{E{FNRjs{|iQ=MFNOh z2T#ISa{kLDzinFJBj|6n;%M;OyrRz#5d_rJrm|w(<3FS_;Ki+a&v?Hk{7GM60ZFsl zSai;RnR@rY)O)JHo%@HRW(*?0$PNt|{*=^I0ZC0&>}crEN8HaJIm8C?hev`MGQZs` zfb}UtLjCt6|GY3?{{J(|dUAAl`&r>a8y}x*(EfSF4NxS*$+X{D9{tOb{yi}N62`v> z^#6|xMavp!n|ooINg`glce1+_q{ot4{RYA-8i4$40Tj7xHMC(G1WSez-Fd2Mmct5o zgFry^2!+J#8vsD$CSV%W=T9KDyndnY_Vgj-W55H7%@a>|u=m9vod@I&8RThag;c_j zx=d-doKcZ?B}N~pZn1#0P2~h2`6@3sR#kqgF`4l*zN%B-c~;*Q$8W1m^aMcp-e;M2 zxs@n+49=fUkeWWb<_WOqguz7p$~PhS=)_)3?DtEYoMv{6;0y4>hJB3aJGf zLr(boVAp1B88{z4y1#J#`G`__swaWsuaRBivBId|O%vb8Of+!}s*!Bk0Set;3W8<0 zNKg@=#*6QW^*p4ts8Rp9e)7rdNuc_Zd!N}s){hfV zIrf=N&t(8t+p?nWI-8!mz#{`%U9o9{+&-X!7H$CHTMH1o=L#}(CQ26qi90l~5Ge;M zEZ)43@56VtABdUhm_hsqc;E*?Wodsg@D1LenYs;-B6!~An>oiIS&FXhvF!ajll4H< z-fkkap9pY!;*Zh}f6=61a~uGOCB7oEnw=>2vTu-}_6+dUHz6hTARNKn=XH|9;>kdE zO=>|tE0e${#Z4PKx$M+qze%4c(5`!jH9 zUPm9!0Mkt4k+dt|m6u$ESk2@sDnVCWAh>+YKo~B#>r%eSrm_Mlz6a6c929IzHUQ_A zrd{;ieIwW*^jzq0Ol1L5(@$zS$hYf%GFy_XX?OjEsBJ;Q_{gqgOnZ;WbV4%>_Q!Jxj`a?v-8C*u?Gcg2;#) zP+`4K=K!LQO{v5K3o4lJs2lwDcA+HwY?*F}scMXFj;d*Lt2*|{5sBW>XK=>kxAi(d zox2QcXP&Z&{OAm#ptx?>H0(;&EJw*E8K(l~XR#>_$T8GQrtZ8sBb@^DU^|SX4||ZK zsth!3zXXwLjMia%i!*FLH$oyNH{>`o?Z)i}?dXZAVG?pwmmojuO{gZ3EU?tPPzOON z?NG9qEpXf%bSqGK0ml+79aNh=&l0Xj!mafH&FfeYfJ#BpP21xQ-1{$~56@=W>ou_@ zxRN2T6>T47dPNsBXku0jMdY!?%Udpim^7H&a)ctXtXxvN?U*GW$USQZ140Do{q#?F zD`u(_j39|0^V|#U#}DN+XbjEDA=I zG+(%-dmvDd@xq0Em}<#UNMJXEYQ|+B|^BeE!Hlp-` zgFVnl%;>pgAlrnx)3~a^AiF3>u)}k3`7nS`-J5_T_t*&!=>)8?0x4j{wfp+?qI&`p z{xB<%x;CN=D9r;wV1M2qE&4@Qy8-rIa7LgT-Fz3FUkRA8d=t7C9Ap-yz`lU&hp^1o zd^BZcG>03Z&(%#GZHV7_jGTu|8R~~gtMvmA?zjB&EhPsoA-~GRtvURSw7NdpqeGCZ zLJu`>_A!U;)Vh2u{5aMRR1I_RhJ5>*AcF6v{?_L*&w*5s`x3qYh^X-Pu5|D*U388Z z)QzvsaS!PCbW>?03BL9-SYE_|(@ZPAUz@wtGiLdmu@GdI9aWnBqq5hFK&cVYr9QMS z!|R?x*@PCSvb;B_G_4~*Rx!4x4B95W%5EvJinoZfU^Y4lV1&#s0SmkWj2cVuU?{FS zQ8zM;8;K1G!;M`Ux#+zKYrT-#k{M?>F*h?*V6_0$0k0cF4zQz4lC7$OR}fz&o8tN> z_S4A3h{8D!YHcFZe4%RCGPsXBqwB@p@aFj%(*Rs1wyFN7&#dl*A8!iq&LVU05n(|va8I84t=k*Ws;PB6DMp?=O6~_t4YfxnfEQIX~(8{UYTHJ(u2*g_l zJ*jpbgk{b_fFXP1L4w;UJFn0do~BV%0rWuupu?7idGJEKAr` zve<)|19Z$3N}XD^6kQp|%+pyiS!p{+aJI%=WEiX<7asKJo(G`Y!tharfKaD%pQ3*7 zpvjap#{;1jwhI+~3e52at>tvY@xk3iZQ$s1DcIMNEOk6;UWu_#wlZ(p!hn5GqY$GB z=%tc!48nM6Ez3UPEdWW)8#qd<(9U|%(y4ffeFa!RGL8j2J9nFbh_D@lr2*UB0de|d z*}csPHz-y%-3n2O;f8O^8mY zWGjW}t%<@%deW34P`K05Py?hm@~x8%PNQSywAIN@dq;`INJn6pjC`G8cv`H4qTP`Y zPi{Vjw5{vY4J6SXMcX)>r;uZD3;nHg%KRZn?~^C*ba*Ke@eOw?PsgoC*5(+J_zym0 zX8rD(^`tV4$jaa7falgbhiwygN<`DG6exEp7@zS(NC?>e1d}thko}h7DT6n!(z%$t zV)C`klCZgVs1VMBf8)DM@EZyN>p(Zln4Yw29^UI;X@=bgAHIo!ym16$+&9)n_(FBi z1JO}=<0M@X1A9EgO>^s7_fMSznlq`9H2p*`E?7OL{-M58Sy=P;-|*DXP%C9-)`NM{ z*`dqmZa_~XsDn&1CBnM0(j-i@aG*4V!SOlRCx+rxpf(q_+-c52N+7|A$^?BRH!)iI zP2Ve_x;U5%mJ|f|3MX)YbA{P}UFbz6eG?9KRB0Dom8|QYsFhcvWHYz-&z16quhGauX=;hxuOLbhw-VQ4wD%1RC#3Q5d;D;h`hY>9U<3t5Mk8 z2k{#%GS>uF2EAtI+>x3rL9S*_(CfPc*_MqXj8g3>6t8qjB^mAyt{ODnj=}cH5n)YE zKYfCZhJHkhDQxuhty9cbuOq>B;q^x77dtSUX7lBwl{1jy&V7U4Ye8!@PkHfe$2;Y$ z=Qieid0?-AL*YqR-s`G+L^7YIFd)jTlmd!$5&az)Pqc2|i$d#KuQfJo0mTTP=@>;R z8TcitNWCBZ*y{SFRvJd45+ca9i+59~!Fm^~!LLG9qin!7&ef=#o#_&%e^0T>gg%z4 zF<1YYoaHe619Q#_oejJ0>#xm-*B#uZ${8M1p?@T>mS)lCe_UaoEfTBq*28>aS{92^ zP~>2bO@fQY?ewuoG*SOL2d!OeM8o?qL?NkD#8ITT5oLri!?fJ*hKo5(CLUb5a_gJ4gy@qPzcdft&q5b7#GT(CA(3x;XB`KPnR>>JMi|pGuH*$V zjO%WU_a5CS_sYMvZIn${r7UyRL9=?8?_qq-EZfF|oJvn5V%lj!`ZA%$#C>^!n<3Ya zN7;}oG-U8^k9Z{&#p}-MlCDhlUnmTci|Z$9$CH`(JsknlK27_66MWxs;qKId#`|=X zgQ3WtWqGoPgT7CkL*2Yj88&>#hxhwg_5515l=K3xbCmm%4Os4Y+`H5}BPA>?E&iK| z@j+^*1lnBR)Pgn6c~g>atoo9|+eQ|gj5H4<^w=Ov22bNK$d{z z$YE-4_y(IiwteKuC36tMhCpc*5T*8I+8j}$HS!mY8*P_-J4u7WXeYW#j`VyZLoOq7 zO0J}Ooo9_Si{vbA;Cj#S93q0uJE|&j?-u=WeE1uRGtQ*eO0|W17j1|>Lw?9~J%b~9 zPrPo_xj>n4)VX4-SVa)8Uw4Is9>{=tx|~Dqjv@D(iBarjV-T4oIDS#czf?HokLvkF zl|X?9-@S8T!_V=A-Ov(A!xK4)X@~~o{KR%BLUWB74nGbru_yRaoWf_>j0=~&J~(1& zRM=y#*t?OhJFQ&D>Pz?#BW=C*jTann7tcNXRcW`Us7v$UFQu7l6q)Bv_rQtjFtJBC zHpnMDQNK3+wO$13gGLpYaNhuZH*c#U4RQ){%|qiH`O|yA&5aeDwsw2WPL7)PB-6QK zJZA6K@~%hIf&$2)y~GEG$5TVUY)3Y#*f%?j`1s`vnE1wot+pGXeZ`}5?t7#4pVyS} z&Q}WTZ)UUOe>L%5kxsw$o$V}jR&?OmfNolKHwwBNyyCseWtd^_n)^?%%Cw~ARkI0e zuk=#1y90sb!}ewknF{{_Zhdv_3QkCl8lD7dje6CLUB5i4n|t5Kh02e0mF-wPjaUDR zE7^U~rNr+0Bj@J+W!`QQXvpjGlr4kT{jPnB)-R?WxQ=ry9JMESkBPXCPfA>KN>M1s zAwrKcr`%|v4aJv2P}K}6qq0BKD<>~BQAApx_%1mb7b6y&5O*dbNdt7`~TkL`SqtQKCBJ|vX(+#;RQ%=2c!u6PQ|EM1t0S3%*_;5%>L~7BT z4|7Cv9q1)P-;A~|5En%Y5x-`bNJNi8B^Olhr)_}}kC*(bdb#cqlNxyUe#RP|^fYE9WD*IFda#daUiGdU;yt_ z!#9t?03oyI368qvUZ?A1eP{N@Bh)K&3euaqjRS)sNkX!&t|YP*870Gwub0WY!kmaw zuWd!Yp#p{Unp?F7jUMJzgJ*7tx!NmjwXL;H5KHtZsPtxdT324?5B`y@9rGrEi+?nS z{(w|T;4of<1%|NSl5NUNd1~ZBbX>>w9tD@vkT{gn(IuBuZQgZDf8$q8leq5asZ-E^K+EphB!uV>C)5qqsiF@KsYa#E4}yGUoeu z2CHO>h|HjVq|i#84N}ABcm2cyCmy(Ip{iUugx|f&DlWS(%8h>=ibrkPXL@r~S5!G7 zqYrAyWgosSNhnW;4x~atciq$MY)G*b;9ASRn+OeI^h)(~tK3f3>`(_b!QEYqbiKB$PwO zG~B}rds)7^gKKVdX*98LAbxN!L_Ewjte>Nbj%i%)R?570uSjR7PK(@E9 zNXRJzTP+YzA~ineTyr%hYZbma44oE=8}@o^fm;0d3sWlvT%3q9DV2w|o{}RA@?7jr zMFTv{1}3)M@IUtXoJF$M_pKitirNy4X8J8oFxtKLPxZU?3uwI=rI_xo?Y15YJTx|X>ac;3X%Rexk3%0vSP zSd!)%Zkc{0qtzta;JMOsozDH7cet~Hpo5SPfm>K}9{4|S$DfQj~#+9Db_melCv9e^ac-gero=x*wFMM8b z$wB}3LVI0IqNKl?7~YwrFo~h%;WCzojy%23r@Q-R|))7EPPj>7a(`-w0hbH6$TXp z1qaWA*5Zy8da{j52X7>(%N;nU;yY#jZ||QAuTOI5oM)8QY70Ex{?VHmFMm%zb>w?P z^@A1ldTk$^9o!Rq78C&oY?g%n-cxk2_1;0yqVjWU-U{b87W)}H2JG8zxVv%FbB0E% z`;q&W&#?xb!g5FFyZUDTzO6ZrYCu~*9)F+s3)r;D*|Jxw-d%#TLG*s3$sg-#$KLl2 zUN^sI&V)7V48AZ4`_4#6{&V5|w*R)5KCB8}bi7ecyp8RW_Qk$?2egi#FR*KCGqRbK z(B}5BVD=^D1IBkaCj69q^-17^;GBz_4<{?LRsT0>lN56|IQ(&2wuO!}Cui4;pPNs# zSj^$(5eJ^t5&rn6L&6oWM<01vg63R)Gx?A6+=@Jf{vQkYIF+JwtF_cCC1;&>m1{bw z)LFAbQ0DUOc$KNP>?@MbSf*bJxT*9zcgs8A88ou`K?Ye1_IJnJ+|&FK*b!ptIt9ZX%B=&M$iw(f`dOT#P=9a2CT&=0rZGYE!@PrQ z+Cb~Y8ubJpSgt%X;Wuyz_x4<-#d-@r8bFskL@~B^mgQcpN&q?kHPe|tRx+FSZ;Nxf z4xK>-9ZqC=hqd58v{4RgY&rug9*Zx}-k}~TB~b=+R^oalZkRHNE}p#&2OnIRz1tmS zDlOp#D-T=yUM?M!Sy>HW`e18z&VMI|IxU+hZXhA^O=|-KibD=u;FjRw+q<(Gb;7qT z_CP|xjnxO3;9KXxx<(z1Ja<6bkTytrQEvG`&K-D?$_KA?3sK`ZAnL%pgO^IuP-E?t zc7j2~{au@%phU4lCNNEns$DP|4v<1+G#p050l8QwEj={;XaC`);if8hVG#omc)I$z JtaD0e0syMF8fX9j literal 0 HcmV?d00001 diff --git a/docs/docs/install/img/truenas11.webp b/docs/docs/install/img/truenas/truenas05.webp similarity index 100% rename from docs/docs/install/img/truenas11.webp rename to docs/docs/install/img/truenas/truenas05.webp diff --git a/docs/docs/install/img/truenas/truenas06.webp b/docs/docs/install/img/truenas/truenas06.webp new file mode 100644 index 0000000000000000000000000000000000000000..f13e04f424b44b4c5605294f4eaad7abb0d20236 GIT binary patch literal 5264 zcmV;B6mRQNNk&G96aWBMMM6+kP&iC`6aWA(gTr+I+$RLyGw_^wC!+tfXxon4Hv4AsL_?Gy zf*`511y(=Y#J24!PJXPYUiX#Zj6n`IaF8<`xGSRnj@-79B&Pcr%VbI?u?6$5|M-vp z`0ro;RUX=oo!ayNMnT;t&Wa8em%CNX<965o*#U+n&EB6stXU1_o*w^Bcr}YJ18|4t z!J3-p7#yuf(7J$966>b`-(TFd#d4?Pb%)I5$POtPsOCBjvu04XnXM%E0eFYm^vmOp*Ey#A5%IFPrY zU$fDtfL9%AoCCmSzqLYuWl2!{-lZTjUu;j+;!n`XhLsc!eq;F|8Fy(RnUtdI; zJo={`d-n21V*9`rfv>-4!e&kuC994nM>}G^Jd&trv+mO0`FFm1N3-xq%5b$4_tW(4 zZoQaYKfC?v@xify3@J^X=RZm9SJyYogPJ4ZYW+HanmZj82~fDCJW$6KFp#zUg-vng9UW@JlzL%Aj0eJ_0xecUMMUm&;zTaN}C(Z4F=Mqq(T<~2;IZ_0k z7;CGk`QCd4PKrSrCxjm&pU}oa{~@($p5UZL&U^;rQTbO{7t$F z*rvPRLPS2)AJ=n$)&8!lWwj`Wu$69l%Ia=ip*s z+u&6%|93eBx1APNQ=oUh)f|IN|Basobh9Af9TNG#Tj1ZcN94j4rEtwr-)(9P;9suu zGbnRwN(YHZcMqi1>7~OSwNg68-yHP7c%>X8fN0Hl36!W&k@~~>gibl@z>x(9{Ws#C z$)ou~AZ}oVMOR&-BpI^RB76xCf~`BCuB}YCVCoSZ4yc_6W~jTjMKIUGYO?ATBuS-k zU``c`*L#dcy(9WI*=dzC3wEmO>N@_D@lUoLVn^nhkDo;>V`dT08 zqD0#R%dFHHn4tP9kbx#*a|--b`Xs=r~qD0&0Y)@e=h1%Y-vJytA&GNG|+tyXEd4cv)1JymVE}f zx&eyM;Yp>DKv#w4Gc)>njNZW?Pq_iUE3=$+O~H$P(37cxYiYS^ecN z(_iAPD-?6u`tD%WL?BW%8gQGhy-`4?IZ?UdG`iIuN}vZ9eZuTK(&7B74fqw>l3Jneg#ejbOKImR)8-wz-p?3?@iy` zqdGMG`1*??kEgsU)`M46{eY3?hSj0De6U*g6J#qK)9<4sw_l}e;49ZteAKF-y=bv+ zbrpyKsbZ2#7K{xfxmwIJa}e$YbTt54RG0pRCSUfgRD%^B)KtZGGkt++b#;;lUz=`~ zPKlQA5!-J7_d}Hj_7O3QRbPPbn{KYkH>{fqw6rP>ZkV}dL04derW6vYte^f=VR9=y z&Or9UMa^0$km@2mU!Z1RST&dcoERKd#W&^yE>$!zpw8}==}wvfd&D#gfuTxe$gT#j z;Y$&zk|BpskrHz$o4LRne7_;JIceT(3S& zcb8Wb6g=8LNR&(l+F18H+;E6gQ4G*kH3NvKv)c!rNV84A8gt>LN}tDMsL(c?efn6| z4w$ZPh^=elHL0BY0}6cxr*;3q0=Wr4n)PnBQ)|yj^?<0UFIC&2&~$_AG~kM+QxU!d zkvO2W>N=!Uo2ZhCGbUZ6T3H7v(ye%;ud4A?5m^D#O}nw{>;?nhDuVxnq9)1_gWQ|S zW~D&|{6{1xTR(kfgx7Z>!lH1r`r*1`o$Q9!&HP&A2fkioKJZ_c^gs{)1kaewMm7UVyY2Z{ccuh#TET1 zY;sk|_wXoGKlVGP?oO@(=Z1tg8J7cmq3rb)!OPfCY5zMEG}N|vX|1IBf)dDZT7Ba- zlm^TF*d z60z}>sp8B567>FSC~j^p=mv0ge3S6F9N=rcVftI7xDq_@l~kMaP;`1N*m%gt6!vcI&Q%K%>mJhGx$9^ftT z`f^X8?e(Ssu+IEE)e&!i3NMh;Me6XRK}wnG)m+iyPWJ)uV66nGiq_1)icXn?cei?^CfPZ9*u5!+-q8fBeUP{KtR%$AA3CfBeUP|N4*r`0vdVk{oT^o*ror?0dm^eaH8M9sXiuSy>Joo`q*wd z6u`$~ny=t_q|GKg8&)bK#gGZthiT&ucG+c1d9=W?&FYxH1Rq4nFaCx842$iA|D@rY zgL9bJudedHXczmlMP!cu8|0n(B*N6wc~-S3QO70YOW^)})bR`pdCbcQ`+)bbYB$xw zwPFL8RtyoD%l^+k`}&OUuRfZzl$d+VmfaX;bu2MsL?oh&vf(15J>>g;n-1|d4UrZk zi};OI24myZ9j7*4f~U(McV|CKuJK44Hk2>4adjCWBCbLX+ZDn-_+)XEb|~?qO%s)2 z(C8iKl8%Edl6lM$EuWXZvUuK2b8YBeB4c7&Xrq8OQXSy4VwXLk!*g;=*c&F|*jVu< z%ai-Ai0q*IUCvtNDnFzd=N*0FqRWy2u+;oq)$5ct(Z&j;y*(k-(_4aP+;oe4z*>j2 zzWFTN@AMC|qO1}lQ~$tD>HK<#CF25&1W{RY{=sDVgBv1Am?HalVbp2-MIeF&rM3A(X>lFzy_6mzL-7>*!=)h zO51nPs{fUjHkEhTGDJz=s&bY8E>%x9FCAv>;YIrQEa0Zt1~UG3Da(6!TeQ5(5@~EQ z+_hCToA_$51;&VlOoSx+Kq4Rd#qo)V%A87Wr;+yGHw|m|+7ipYbxGsA15$_<8{+^ptqZ4wkMFilQzlyW- zqy2U#CVu`$MQ)T-M}CH{Ooo7Pipj^n7aJiR&Q?IhDdmFtCcnLbzm*O0fIEk?Zq|N4*riZ{W{+So{G^R4bBOmm~FrAfQlnHB}vfA~M~$ z_QUpLM$Siy?q`h5)E~|pSu^X$%}lb83AKVV`?#2Aw~^XJMB;L?-F`=n|27bj53-H+ z_2GIX`HfzzT0B}=QoGuqnemIcfyQ06k|!CTiiq2|9i|lHJ7ZpbxMhAZ61G5DPM8e! zi?0p38ruWR0!2fg=}Hb|f{Pj2KfL+^V@Ex>7D;|f!X#u~QqsH-30tBRq(Ln($XG4n zaGK1{Mx&RAn0#g1oGf)NfTbAm_2Ax1QnD~iTyy5QBy3@lqE{_2!I(X8@9oSVJ`7QM zUkxLy^UiC=pPRJV7`4hc*hVB|opCg*9^7cX4>nX>E(u+bq#9BS#2epb-#&K3Fm?rt zNC)H0kSyceABJ&rfHu$Z&KhTrW!+s8EcM{xlC;bZ6$zc6q=~Hs))=?rgTxt?VSFPZ z{fz94-`k5c{lduXq8zE2e8Q+q=IR#p;U-u$p%z$nSbcu!a@S}q(#bH=8!CrNQjC@U zB_$i@XZP>&{?A56>w0iQH4D`@7HVQ@(>Zc`jo~68hOtF0#&~1Dh|DvNHB+7nHW&x% z!S%BGDLrbFwZ?Mg%i>iXjN1;8y~cFy@MVtCto~c5#RY8)sRd?z6roBsVnt++v0OF6 zIHa6F7p)zoykhj1`tN+8HhDwmYl|4_H$!THFO8Xsu&YL-()2y-(oPasEh5{Eb*g^G zjo^B4ZuK0wMcTC*W0L$@)&?=gZh9c5(AR+{K%jjyCW zJX31zx#CGi*3SuFXBdB|`P2?`aFmev-lkRiK-b};DKDN|ZUw-ZI(m)o5*PiS5-D3j$du}xyJhY*h z#nGX6sMMb)Y>v4ZA$HwsNp*?W&1z@oy{2O0fw42D$3}-b*}8UO1clko11YMup;#9o;_ zWQ(%AaDB`_VN9_5&RZPU{_`CVyRmHfy_l3Vu}x+V*&;r5ib#`tZ@bDrkZDX4@l%GBV+r4@`tHmH z&*{}-m&_ir2G6fK)nSWS5+ve^>n)+fn~TVMD-uU5-7`_+MMN6Kbr5M8*Rsjvq__y{ zB-PVVD^RQa($B>{nLT7NEEBUtu;b%mVkF`KMfqHAS7~pd`oHaig(9(haIc_*tWMSq zWvSM%zIb7@cbwQJvxh96kpbK~yMAmF`LTGiL!uvDaRhX0h&U$|U9_&1KKGV}yzVSJ z)5JEJJ!GGv43t)%%Cz^p>k^Sp6&uxhIx(R6{xa-lkYx=xqMwK=(!gb(%pS7O6;6OO z9dMV2GThozGEQ?{eJu`dA!vD^2X#F^5CNNf=mtdPkQhY5$q@U`2b^h|j zTlQ0VSDR+`kgXt5`H^g@(aQtM=oWeVh$Jd~H}R_NerG)Gtcv&T5}RiBkcFJ&?8?~r z=YRXljLc{6=$LJuWD)7)$@?@W;k@efj^X1{N!Irx=kH|ae7E1G#Kf;Jb2iepnLT9A zjnClkeG!H!nU)8IiOQKfQ{O_yaMk#4ODX%xrHKU~)(^-_U+J`UW)E4bZVm|?cQ?Hz zyLxxl>w2t&_71V_W)GQ(nml{RlA;FB9wk8_Nnwj#{XW(cI(ds|9c_ZekSceikN^0O|M>5xn*acQw54qT literal 0 HcmV?d00001 diff --git a/docs/docs/install/img/truenas/truenas07.webp b/docs/docs/install/img/truenas/truenas07.webp new file mode 100644 index 0000000000000000000000000000000000000000..24cceb7faa497aa37c470dc0c79b1ac8a77b5690 GIT binary patch literal 50209 zcmeFZbyQVd`z}mukPaoKL^`CU8%Zfa8tDc}>2iaJigY&!Y$T*XkOt{aLAq1vt~2*@ z-sgGn{l*#N`^GoUe?P|%w+onStvT;G@9VyDz#Nra+)@(u8xC+6Dn7D`G8%-}N`0%EW=0tEge;O~9#7yNn# zA_6k_7ZH9f1L^PYB8Oxk|NR;16#j$J>mA^(;t29jB%XU9ZqJ}3u(nN>EFj_h27hpx zk*Oi>Uo)D~aRjPsQGU!*>Ic+HmC%Yix}T)y?vGto)^nGa^Ny6S zP-XB$sd6cQJP-z>QOUZ%e_a}sZ!#*q_x|1=uZf6)MI>_Pzb*-)ld!rybPBljitv|h z{Ld>BL-PN+v`%YOGV%U9`oEp_*C)`K$p3Zee>(Hep!xsV znW(NwAd0@>CHU-jwH}VBP_21w;3qWQMIRSE5ECk z!b&z^$!^{hB05IvnZYMhP)60CMv);)P8}3{SdT|l7q*HUakh$T?LGFp7`7;@p0d@@ z_VUg7EUcyb=Pu{qtB>MPu3&FYV{1&Lbd~J?p}^PN$>CYvaEj7 zxs9N2_BcK2e^pJjAYrl&=5D!}-`vY!S_Qpw?|9aGhog%&`nkw8`3|9$KIgR8))(XV zba7Ug-{!7#8BB?h?z1oNu^cJeqQp1zJaLYX znze4XW#M?rAzVk&{_ZFrkYY-`9WFAs#l>pS>nd#1>%SN@l&bf-u>tGftm9 z^<%i;9@jQ)OCOS@s;6@$=_I2t{m9w!_=o;IvmiFmw&R@d35q>xA?O~?rOu0m zw`6Q7AP|hNj(*^RVDQ(?jwv>i+lqCc^W`h79M?UvApIn^gj4_ z<4gUIb5q?g$m~iaPQg?+C0g6@zwu50^ThO? zd{URl@r3C@vHz9rDH!;Xhp}2oN^A|Mi_n*bZj-R}e&wqa@X~EcY!s6+>+%6wje3z;9!d1g)bXb?}OS&JFXCa$v#$zdz*zaO$ z?k$*9e0YV1-oGkKZ}h*o66CViZkHK@jtqD8yFOcM1>YwI;q+zEPQ%}Fm-xI8CV4aH z738`k%Nr`1jpg|Kiy}ue#CS|YD-@BSBL#~}N8$XAb1&(ON=>Hwss7hz#-(jUA${cN zE!xBv!H&w&0CTyDncC@@g(_P;(zFZKL&vD0W+X|sI?8R1dIprmn% zQ~P`5l?c}rpVP~OfhB$S6HMRs@lLU#CX!19s3f_q z2A#VUGMRCpZ+jMYbM>&^vl8sLs`Oayak78eaJK41Ijinb(bjl#wKLJ1a72K))=H#j zhkwF-vdGECqXZ-PQk~hVbf=-%r`QU1*?fnJK#RhEr)NjSeIPWS(D~Ef5vPg-twJO6 zJSHa&(sxwH5nRt8AsNHK_~v|dwc}4EJFtwz$$l7ZN~Zr{BxUa-cmF#Y4VWjI-|z!k z>-)ud26YT9G9_}a+u9x>_I?UsyN~<@MB{56=AaO#V>;|*DTHreL47W&;((Th=FEuY zC8?T7j4GR1Lk*QOSby5ZeOaB+c=5Y->6=c2_G+SK+Qg6W4M?!r8VHrfZn-E$%WP#- zJ?m_|iZsU-^w_J#GiWI4he9!txWE$?v8z``XGIEHf$?~!cVBU#sTuIELij#bDID*w zXN89$YKh|RxX{Q<)2V-*55ROw2wf8BVJ-iT;0)5v2Hpew61%#+c8b+CVteLY^#aG= z98F4$#a+VsBVzx!x_b(rROij z_|ry1)hZTOn|P<})X%m-3M#zMdc1Oy%djR>m~r#ZQbkGV{XouaQqH)jE44=xCot6SFOMtw#K+ z1MAX~EK@=JDhn!+A_I3UlC)p0icaOV-C3EL&Vgimh|xoo4`%FL4-G()<}{5%vZ<18 ze~XY|u<#5y@DA>8JVw8RKEAy@x}&(K>7}?G6z=-wjhz!KeNRSwjtmYEjU0cyq;{m) zRe?T)vXhpWpn6Wd{2D6Zt~Et7NNwBB3Bonm)PhZn!Rol?=K8{28$0~yft88>ej&}I z7;Ht1sbsSxpoJY`24?Kj6(8yXnMUP))3Vojg%!gJoKh)%I|Ew%%_bRg?Hor;E6<3` z7}PW5;=;*Tz_OzoEE~9cwMyPi6}>;EWhiZ#qHW1sC^U(W6~**Rd1GrwMWsLV_vjDL zf7Uz;j0$NBd^|4GOT0tX_1exh<4Q%Hl+1219?zcidb#6}%oERkilB_h)oim1#=6`t z+i$1z2us1QS^QU*L@&1QA(vLG9^*o3a3gYi#cnPq8E(9vx-qkxA$1w8^r3Wpj&jOW z9WZf=3a-sXd_f5^YXV`PQYYo!rF@d;z7+k3LgO!7An$doR+#ypQrAZN*gT4cMQczq zK1F1Q5(M6T{7ra^TAPk02BVL}Tixp0Qwp_(XXNB_!bVH{5s7sC)Dt<4hV>m9cz6-! zG}X&z?+60?kRDTBDpNLbC#}7O^)7-WaF2VXx8uyUlR9{}O3lhb$Kirx1(6?- zWKDuOr2`UX{X}DYPGj})qp4)Aehnn?26f01q{VY#S~-iH@yY3gU`HkSqEe2DsufB*Ron$^KaH7EZ~_L(zvdz|c#524bW zs%Gft(Bbq)vGvD43!<(RMm`injF?oHxl_;v_f6&LzldR6Q67k;%(Viox&yT@Vx~^_ulCPt9UHifRO=+v$h@J@T)`^-w~dpMJlmQi$avfhP-N9C*U1QYkqyM3A!QSudf! zjvR|I*;I%7B5AghB{8l}-g=hcvH$7}Hs3VfdbBjPZH$;*^2q*=%ZGIa z7LkH$q~$&oW3wZWIPpkCIYvkh&)gLN^#y&iD-{HzZUUyr+^P&onJ~41&!;*fSvFAR zpE8uJu47_6k49#+P_Me4-hWmxTXA{1c5t-fjViPGa^do6&}tPJr=`DM;=jg;nALeL zZS`~BAOpuypTbCf{VF0!dbAGhJ*{8Ce(s)m#Qe(#8s6!A(tXJ>tIZFQhnEj>V4ux2 z)nK8l6j!rq)Z+bH8e5SF)_$n2@^p=I$|GLq3I{*y-qRrdd>FzyJTj&s)2GNH=Q};T zGvnFkt6h|aPde{2AG25%YX4PpwQOjGEHV6bOow82lx62IkIZkSAk%c*rfAsL&Db5p zz@Lu&BjhP$d_4H8!3TMyIfQfR7Aa(XB`#$DscSiw(JEe)bY^CTYuzD@pIWHzwb0?cm5azZabvT&%hMC9K;II)H4)@r9M#wZ zN-Go2A3JA96qD1_c+Y;@A9e3vZP?eR8ve~ZSi!_^stz_gM2w%PunE^R(Q+&_FCQtC zictmXaTqE36C*W;i=H!%M`Awb%JekkMb%o6O!L#vi}0k*in|lyxf*$9wJQHQF6n_A zA^&PRak4(_*YV*IsO{d<7&!}1$fg~*?>65F{!;ZbU$$U5@naskr9(j5BIf{hz6R?} zU2$ms%VNF}wCc_`T%XwI5YM%VE_9q}-blEU#8!<3HI#ki@8N#Lgk{$HIZs$Hu-DS2 zLB4Sq0QB0q0zKG{O5;u;*+2Az7vo@DqV#cWvwgqtL2tMHy@b)Nr6=bBbVZS44_w@ZG^+Dyhl$d;(%|}=gM&RJ`75f%xslKT0 zqcyEbOumO!E%Nuw&(DW;ViBU2dT1m)8U_Qui(L%}CX$Clq`!Jq>2c2Sk%a z^Xp*qQRrr|Hjfvw7WPSw4g05e zj|10ahKo-?`B$XVR)=f)<)Dr|c@bvuBTuqT!fW_Y*hppMeCzQIWd&IC3@=u_{(%z> zcvf3RkuWm=`Tg7ANI$Qx`1oz)b9Ei}8CT@VFcWH_t|ftWmQZAP5t z-Rr>#Vp?qGq&m37mwCdwO~@MM?1y?!vt;jT7lCwS9f?yc9L{C_!rH)d#`TBmXyVmeY7>hm2utGu33gITT;*W4%hs2#MbqT`J9P)l&8}Il{mGNgOSDwkj9+N5 zD`Nb1`fNp;?rtRl*e&!WtT_eED_Nua_{w_Rua@GUF@6TE!XcWJ4-lk8n3K!&C&*$~ zL&ICQl~tE#YapeFqz~tr)3eG9TI4yO)9@xoK<>-7yTA1bolJ+~{2>g{Fd+?tG;Oqy zlWK;0fR>A0N1*Ioj4)07o#egNn~UJ1)qzyn+A4swHBcLnHtG~sp?Nskh7!!IxA z>*xzYaK$<(z3un`yhf7X9XRN{Fo9th0;VHY!Xe7RCaF)L4%9gi`_G3OnKjOSvrf?r zFT-D|4gl^a+xZ~SI;D~4rniw>aX|LV3U`AyB(Vnlz*VFB^lBr&->>LzdXq;z;| zu1J#Y)i^!-JhOrQf&CN6Jx~hyZnH?!hlQAMsqk3RvB(T25TDPlAP-~@bXv`VVWk$i z4nUwIj1vI0!EkY`7E;6F63M>eL+-n;%j!)G8@-x2BnXK|u_mNADgZOpPF3a&LCh=J zk)?2bPENq6v{n2=T}9;aAPzkm1&GJ`G}JzoM*FAE>b7Hbl3o$pfXb-uJ${v^g%Nx*`+o zVIn=*lE<<0cB!u8@1r?Zr(0=#an;5JAB`kyafLui&C-}4!FKGSyFH<#7?TY=+JuQp zex2tAuZE=eT&|5D(8>7aI0n*FxNPb@av`@Y+*2WK6nmV%Y4%SfZXmr(*Lm{Mjz~h9 zCMY<1geoFrU{pVfRsEZ|1qIf9$@CH%3B#Z5#=#UsRPK?MBqNq%1|gFYNxG{8-A{W6 zkmZu3V1;H$4E`(6?m_tV->l(Q3?NIRrt6r7K1L#L?_-46k1}BEORln0UBZS0jh^83 zmRd7eZL*vZ(uJC6!srI*acT(PIYBLS1fbfT*5Rk@GYk0iW8)fe){teh z_Rm6V57wNoqqpxOqz6SwEcbi;&0gUV>23ol{76Tbu* zJQcSTS>I7x)XPzImA11@5kBrOjRilr{QSS=m#4G*O}O{nCE5HzAJfQVYid4?qaz^0 zkfTF$Rc%$)DeX~9L@6M_t$V)DS&exiCDs5{?Svc@Dxl_PdU8l0BM-fDzaiPSjFcuq zrwQdz%a~g~7!aGwd~Y`SrBirBCEDl&PTDES6g-Y!orZ~-+WeLu?DZqYY`zbhxa(uu z@>;>Od}xuY{w!6(u5*7lJ0T-Zv}QVkNs6>{E}iRC_s)L?%8DIIEvju-+-m-k5Y6ek zR+6FG`aNCtL0#h@!j>+Ebn>+oswC8rk3awBFaeqo*Fr-6L~5fYw6TVPMSzH^t^j>_ z@%-iUAs<8aba5D*K|%c>5VrI8ij0V|38anjl|3nxZaVRM8+8Lzf#(FWm`aZ=@NzGQ zxWa|^<<2{`{tg{L^L-;IA+?Er(w`w-10g|=v9yG>H1T4|N#V(-OPxcwHn%_yjI%(; za<2GkQxMcZLI5jMoBO9s$H1}zas;BU$4m{Bxv8AR6HD=a9cOY(P4yup2Cpf5-bIsBuv{FF5U%TU-r<}zv8Qn;8OiPw7OlFLKV$@n2Sn>lklNot$__X@$N z$_(zMMuEeU_0J{v@W)6sA=-C4krxL24aENnivROZ8T4HTPWeW z^_Zah1%S-d^FK}h)3N_wJH$v;Ab+n>7tSu4=ZCpI`1J&J%vh!ECc_`&@fu0#9!L2Ej}2e;A0bc?4n)2Sa+X`c0*?V6B_O|9kJ`)q z4Ws{ppjK2MjE)5~$DjXsISI5e@_8oWpO=N-Vo(%leZO1^3-kWG%mdnhJ*%a@HIsnW z=nKMSYE4^5{m;wRpbe!gH>p3~Mf?Jdgtcm*v<&v=)+nZcmv{rPMA?M&lBmMGQY95bxv$c)KfOn)l~Ocj>AEU=wEs;>}o^BctnX0yA@Z=N6Phd zHQAvBR2%rpB1faUM?VbtDn4@Gik875czk;;7rKX|z|y=n9T$R#-@d0$`dk(UFPj&@ zZE=h1_7#BCz_)lP&ydOkv?3nk%pf8R*68jY_C|8v38TQ>aXp6|`G#BJ#xW1VxRyWL zG+e}u#5y|?>FP(r)$tVD>hV$;=3jy%5Cd?tLV0xynGqMEcPL2=JQjk!X{5W3>$J`{ z2eyLB@ZQ?RZYypppgQJOoL#jxgKtfg810Pb&5I?iIArbQF1@?pLB7uy#!bN$5wUZC zO!WfxO=+$^9oI~F>q{1qE<@Qckb900c~e(!nr zGg%5kW#PT=_P;LtE`AXMX1lx^>xwuYFxK$o=W0vQU-<8GB`$>aio7qY?Xd+xw=9(} z&ox}=)v)CTdWz=i*%H-_wkst)rQhj-Qv=ZV%2$Q2&YH`5*vP*6gTNY_QT(~){ktZ& z)#otxDN);xoH*V5*1AeRYFkZx-mB}y5can~sC!iSZ8N`;XeWMy<|Bt>f^#=hD~ajH zA@;4}O0}}qwKQ*gI#t?aZ7c%f7q5mK!O*__Vc`AfTHpWbs1)6%p&uECKckGi`BcX; zl@Jx%IpyqHDp}tLFn^PuloEb!M1y*o2Hr zAZ8OXmkxAjN1%bDA`xMbD)_B29Oc=*l101U^#PV%`}-RUT=q8pwgJP07a9{FX70bx zC|+BtF1u3Voy`8Rwb_qR1>ncTO@z6!*wvvUP)jt47_#~@9v_;ffVNpiti8NI8^KxO zqJq2Hq0c+z41h$ir9bKrx%UI<=S1U`nC*(I=Wk0acFW`)Wn)uPVLp{6Jq{Q)XR<^fUg&+ zyFTAqI9&=4Z#)<<@cu=}PfSW{36~SK3_Q(GHw%iH(=TLhO?WqAMhy#m#Q3gzlB=?_O!~rnxXq2B+q@Pg5{Fo3!BgkuyO4G5&M2TnQdXI z$$aYgM!~K8hb)m1lPGP3jzoGH6#OZd>tegSL)4O|zz421!j(9y58YBD2T=iSHcG0b z({EOQz8A*vt74=bi%v<@I;j&a{s=>d7749NKE0bE-G|F!8BJlT5-(**T&%@J$M#F5 z$>0+gMO1XJ<4H?+k&k~NL9~`?6oB3WdX$vgk!R|AVume1?C+H4}cWj({Px7V}5W278RB5R~mUy!2$(mb zAthdqsED;SJ#wJz?5zxtW;@@i2%;DRP0Jy{JWj{N<);}>D;77zj}ek_MaUX;l>D-4a?#Dc{%HI(=>|xk0VWs& zx~xQ^Dmna&xLI9X978*PC$&xU{ALZo75Em>p~bhRwMxdTK2>Vv#BV0*&jX~-gYY9b z#4(BYJuTjudmf@n&?8ddYO4e_gu<04xWe5E7lc;9h&0LN2|K2W`aQhw@la1DL#pv{K;YHMT;tu-=XzCzBUVW@K%Vc|T!9V)h)0Gjos_rObG<0tC>n71 z!hNRFqWmcmGwN$kc=-rcZ_*2lVY`I+=w6QEn8xd);yrg5r-MIXm^*VGyUBT~)_J9K z!=8FC>&W&iczsG1YZ9BqAw6R_TwTcJXTKxrZ zGt3|)Uh+6;O^*nZ<`fiY3PiU6Ch7-qroc9yPVB5ySd@%tUtm*AWH(jjvjTKxnh$Co z1d^4KH{2L~=mmu544N!p#ebGk_xAz_ zfk9+R?lasL0e2R#Pz}9_SNqask;LTNzUO&3Bs;QvsynS5jH|cXbBD@DbKBN=1pvG9xPIKwJ58%o2in}KPmD6qVz{(v2Or{@&Eu76eN>P?ju78& zBQ`9Yxc7M|{3CFVkeLafaxY{a@qAGX5|3#3NRX5~&H{%taKN+Zg1yxj?;nRm%caJo ztvE1kIvDLmXI?ved9?a#!;*(V>1fl2Vq-EOug0qKY{b%f*5)9_T$i#5f7(sMD!)hW z3qioP7xdx5@|Mq9s(XUj?Pn858x1$ptqwU{CFzqOH7p`)NrjOv;8J6=m{1ds86Qg4LPAY?c=z*g z1$Qg~Yg8VyC_7hQtL=RPIz!%@y2%|${_*`Jwshxm|vl2E{_u}*`e z_P9Waw{++uQ!`K*Qc%(IYfQxc(H1}v)(24#Xu^Jr71grD*-5hRfr+8>-DIH@%M5Ab zYr;7T4C=7&IfC%G67^H&*#_11Zm>@z&W^h3597t+Mz~jvGh#&mdS=7`34y#WjL-O_ zX}tWb8&HHAwsA0JI0Rfe-uKJ!MCqI!=1G8kg~Q~7aD|JsHGY`g!*CSŐ(i$?bP z!{Jl)p4Tf@2E8=ejME=8c5-hSUZGH0xN{EE5mklSrB!MK*p0>_#9$97AOS?HG|2Mv zGF1h-(aMy*O?3c6SduT(KN?^mz801VkQEul*oF%dr&ggp0_TjB9`jJAyFF_`&9C|a zpyjvTsXr+?CzlzQ-&NqATwD^~cYS;SJYa@Y>4r7k%mr`eu8+S`@G3>LHAJ`ygVZh6 zo#vL1_omTN8IMD)lcNl`5+jfRpLW0XLU#q=?_#qcc7W~RHo_lCW4CFXiX3t4Z5e=`dp~i8$(26G1ectY1lix_IkhDtbF-8i=jUsjs5V!ryCmF1j|EpNODX%#)PeSg!}U;*Ea?x|5X7Dze$EQ};A>QKRss@r+iK?+ zWpE@{1Z#BTnmdLsWz9m9Q_dj6KJZ$G>|Dv-3Vkq$=SG~yTDAd6qBPoyIlA{o4@n1l zmIPj#A#3Sc=kO=azYFfWH<1dI1nY6-;@(zM0vtf3lQ4222d+loPy63onW}$ZloZ|% zM07=K=p5|>mET_Q*#sMM@vcs#;KAo`u;kR0DV*EE=B()_(}Q52vDluxmVTX3^%OPc z^?61h&WDTHXh9A~d{V8;5ij)p3nA@nd3Zrav8B@V74F)@I)H=A2B3R)7dfhb(oC0@ z8KvV;Jc3Q`0kAf-D_0&HdU_jY^5Ow9HMrnr81-oTfSGFoT}Qes1mZfw1i5EMu%VTB zqP}fI0q5&{W@&VuB#qs*i>r^Aq{UPam)4AV@HMpuSj~t7U!YQaa%2q?Jzsa6G>>m< zC>}cwE&L5&7)cd)swVGlxIqxu;Nl$tCe_sYZv8J5!KkQj!Xsm?x{7qUlP$Mr#H@PKhtNdMFHe`4)_I^_ROgrLS2D~!uQ_c5@5 z*V-85Ox&d~A^XB*fWz=W1y#QbDyG=4!{ds#7E?;VhaMyJNxx-q!`zOlipOdjCiE1 zk6a3;Bk9)!@SMp3s(!wTD!|;(1A>Qi0cc9thW=NsQa$|9w`Pht0Z;>$o|P&;B2Mty z%xeuIHXxx-vihX(0$5yc-T=8usosM0-Pp6TSu~1!i^#Bw1dMga{EVFdN+-dr&p{uQ{&P%Lm>_cs zD9&c`xBuUeQ3WW1VWUHvKd~gH7&7K_#W1$$xX0O6;JX3fc3wS$nMj%YOer9n#)?PU znQ*G=16{Heyf+ERDV3)#09bU;fA(C#1zD}z88Ar({NU+ZG9<$2V##j7#EeU%92}yZ zZ(BvXfTMCd^@LLe03X~x2B?(-jOj`8Zj8F}WM-poJvN2YcYKP?gw0;GLR^@Nx6QNk3?ZO}3;=)Pn@c@^Jty`Fxje6CE$9 zRX1OCpwWE91E%c|@scPE(j*WI{C0r(gR@>31x4R%PFLa^s0NRgqgBEXhXDjj;eB!7 z3TPEfnlOHo(=EfB`+i^obo)RELs-k?F3)Cm{Qjb4v_$m)N5!(EcZ6!Vsr0}Fa2>(G zc3!Ej@t3C?#Dh(3w|*JNHR&k-cD9Eda%*p^cxWG zgMl?(gd70WJ@@NLu-Rm>ru%^a5a86SL8!KZmNm|%`EKRd7@dsK=7)~J1EI_h20(~3 zs!tD4FD+_M9}&WnoO1e!o8+F3S7_l5Kt7nlg4FE7={s-kDZhhu0#;4;>@(}Y;>C}~ zYeMeNaPz8<;**?!#s+G2czO?FNb`7zx_V}b{Obxh_9)QOn0RRV9K<7B48ZWIo6iE92M@121>m3lB5ZN^L>CpWZz+G` z@+b;3Se0p$aQB=T8}CO9J7N#8Q?N9~@~hViK+p@h?Q$(sgV`SyE}3^|`Op1)@_B^#PN2>_GCY z%Hy%;PT%`pex2d%1?JLJN|N-|f}`#x7BfOl7T)+5@cj-IBg$lVJo74rM<&^jzwIhY z7H%F5m>MK`-}NC>`Y`ptL(jR3t`%s>RJ=c}vT*vuLJp3P-TUu!Cp$EdQUc`@r+_9f zwJaZ0dj@o>X0Y0w1|fZ5N_M*DY$u`V~Lnm!{e3Cn0ZR zpAf)TmW;_-B5r>6yN=X-+Q}3FP)s|n+IFCp2y48Tof8G)w8GGlE7LZs?EH<-KMQCy z>+#cX{eoNHWd$R~&Oz`a0Y^cL+t)!p2VgOOKP6Af6F`%sEP6U0xR?7=FprGX;23&m zP~C7F#A}yTl9*`(!ahK+7^8`W6h{=o9*$UV3%(?aNBJ(0z6Hj9|5kv>@0SRl{1DAJ zEbUFc(9~-X8sS&502Eg>+LdugkY8B}89`d5Hmgq>#zi{urYG3@0ZF3f=EuSD)j9Fe zYq%}Bu%rCsEl=Q*;K|B{`>r@ov$|*pYLszBbXq4aGhsb=8NkuVC*U-<$$u1R)X(~6oNFu`B@0A zN~lUOSemOK9Uy$?IWmb#All`jv?6T$ks004cHn_7Zf7h3%*tYS_!crPowZ_uCr_%?TKNEh3?^ z!`xd4v=Q3Cq+}A&@i~j>UIj?f?biW9+a*%u1i_vgVsa6W=X?%OP#KvlR;vdM9qNw+ z&ArshC6J24#jX=H9zXAFS@E6e)UF3&Eob#Y^Y73YxYDvefwJ28``ea@y7qZ3K3=ZT z+h%RFb~F-eL3v~U4L&#l7!0=Kh$-71QgIcV!72EYl4ex0@4XM2xtgsiuahB_W>X}C zL9n>I2o1Ar-EmoQ6>fbDM7j$ZfAIijj7d!-PgJIE%*JRPvx*6yK?)@Mq>^FQ*rVKS z=LS2K+2~Tm6@W059BO^&I_b*0-+d-vMXL*0fm_@ZO-6sbs$;(AdwVTGM^T^x^eK~Q z_%4_$?<5HN?yE{xEv%}Fkyt+vIht-P`TM*W~-Su}BK1t)rTIWS8TQnh0=4Do+ zu6=l9xAUKSdt3m|0`VPL>XCTBpArxE9)P#psZc;2*sw2=L+Dtdv}*6+zF+zfFJGh6 z?5-X0#R=yP(CNsDlfhOC(-MBblcR>wWIXM~tdfcMsaPF@*-dvi94w;7^H+!{$X0Ze(yC`2(GjUIv0Ib za$AfJA(Z|$&ujF{#p$J)PGs_{cZw5gY!&SBr(}kJ>W|Jw{ZU zmY>xk?ZaChS=!fyv4jeUGByUf`VC_KxduFI+13m+Up0H;j?6CR{4QShUH$4h*M18e zY{bGxqv}FDL0_swQ7bY>xh%c*VMQ^*6QTEPE?d8$OIm-^EvN&bS1G!Ad?@_-DWZLS zZ1ywf#9%w8dhtlleOE5+sM`#U8y=%?$pqR%2^`<4>UYJO3CikC4c$*Prex}^J;mC+ z4qVr`%lxqJwq`K38GRedDExVBwXZ-mRCNBk9%Ni6C%X_R*_9%x(&N5pt{5!(?=EN| zIogI{bO*yXd%;wGjQz7~jU()_g$y*sfTcPQ7#FhWjaS&@PWEGOxXY7^7-dD@w_f{S zWBO87>d_)sCq@>OD806mhM2@^1?1bU!Ss@9AO9<5s>i6g)Ov9;ZQCI)hvuSWwFViu z*dgKkhp<7rssc$v>3~9@Jk^gBD$>yS08OiHpp%tLtj{sTmx)w5R&Xsy!pU*TGrp|_ zK>)su)5*EmUqyl{@UH8TzEatHtld@>t*q(0KEv5@&OML59)3$J`nPfxhXS9_yWWS| zhoz>kl-XuCgye{V8=Qe0s)UtK^0-1MNS0O+*z`C#GB=EzXvb`}OtSlh@3kZFLGnSU zx$y&VZrY^%vL;PY2!P1NrAJo0-EQk4@-hyUW11lY9A>JC*Q|$G@DgCjs*nd+Iq=7B z0wz~9oYVFpkk3a(UW0Gq+Y8=)NE4vUH}h(*l&VeiGggGuu1cdgSNue|m~cUatP96| z8^d^(qUwjd-gE_>Rz+|jUQ-6EkGluVjs0ukfV~IygxTX{~3}Z>=OAPx+yc#D6+$SIS2u2 zw+Kp_L331Q!=lI&9!q`mSdB%r6s89YoD{s=*>bejg_GQrhb9 zR9^x~G!|d$>?49Z$feHK915k}U#yU$m%BF2-_Mg6hxq+ln} zDx!Ih_0km?q`ZMn^*kwbe?7t`yj2@K-qHhcyBGu$8fK+^{hZBCv`?=!@<07(R)5?F4v0G+_QU*mmgtTnzCa z!wAHN?{1`=G{-gXxNo3AzV z#YZN=o@Y_vlW7-d2b9BvrO#mvsGaE~T&(}N3Tkx{R-De(P31q^t_MD)?bP(Ax1J5$5m~Lt$Vr@1(_P`8 zVjsTrtfzt^c;`}I_m8V25#WZ%`z7DhZxuX%7TW=|nD^S+@E=#3UW0OSEOWga1QBxqokcfF%EUpXvcDm&)(d_!M9W zvU|>Ke|8-jJ_(PNDhVhJ7iU*FpBwJ@<#C0a^^-EL-oK4|K@VLs3>IqVRWyHMxQzk9 z8;Ea#&d{^miMvH90QcQ67?il#@Dg?H(my z`~sS=#tkLBMdN}u*JS_}HbL*Nx6C)7u^3IzL`87>hd+bv3)~!+TCb;hE1w~vD6jyM z_NSC-+aH4j9f$Kc{j-n$jL;lHz#9F^EH(MFkNz)qGt^jy%J;DO%al*Yqi}GDL@YYk z8Ed}cTU)PV7Af5Vs3n58Acl6kS&Cm3oWs{W_z4BApt2+bw0u@~8Sv5nj?4(z)Fq+1Ys%#Xf*aYyfL)$wZb zxB9uuRX*cb4NY|T0RjnN+k6Yq2WCfq)s|ipl0@~dT*top^;frCQ4fBcuvYbdUZ=P< zZBy{2Mc4KVVZq@>Zn|dvF;IqrZ>~=o;S~%z+|1$uOhe4+N5|O2p%8b7Hz>LEzH0KV zrMSEWREQ8TL{TijeHiAGfFKitV+&*80zd~fkfFJk%B<>FxG4`#^N8q7n=3G@ZcSUe zx%$^55)Rw|57-=>8L|V9UnSBIqGwksJx$5?;xyTWR2xaEyGFPqEXn(?8euRV3AurE z^Bql?_TGIU?Mj}L!cSk&cj+VKo%L991gAfI51H!zNay1M9``L^S|00zZl)`I;aR`~ z=oOiAuuZwFF4nHZVjH<5-M8$}AcHRr@Eflt02g9P2e;;x3@fm-_4@DFj#1i@$jucT z<9&_$-(gfCW7K3xbZ-lVSRbz+I4%xtdkd9#hMtt7llFZKpOu+rHngz|78g$N}n=_7k`b z4v&~o6VK=GiVE4l=v^1anj>8f604|DhvJ6?T-oF>8Ym>)ub9oR9Vz*ZPI2!s& zQ`(n7Jwd}NIwL&{Ex`l&-H%0H(tC* zi5Ja03~h}!2P^w_3E0;ioCix{rW}DEeq!$ufKHEwwJilV9@LJ-SJb+)c(GPZm_S{O z;J${AKtNZhd+@2jH7JgSH}_DFp=Is&ZNDRa@bK^D?0DnQcop}R0z;?#FS-hhGnD=Z zAT;#1ll2^$Nc&OBwo?uoFZZ7Un>(Uj8aR@{0OiT{g(T)9f^e2K`ZW@refEaaPg1EP z>}Fq1*_?&*!~zAK*mmnZ!6^_UcBuxyws=`R!0$3mina+PMJyj&=dCmkSZE(GT9$S( zks5S^eXIHc#T*uTo(l;>)2Y6|>n6Y0P)>E}1C-da!o4dv2RX0+ju&D12q3sIi1jek z=?Osqfd%^vZ6&ihxUR4-fK1#9mszcw3anv?^nT$WI+Fj|s}K6JPH&E-UnFUL|0GEH zbU^gN&I7&+=~_$3fo$?-)~|8mP6OE?;@S?1?~gOGmbqPq1MXjRLEPyq@+PF;6b!ooo}$R6Tu2FI6viNgnNgC$GWKq`O{^af zeGS?gPsfc*PFXI%1fyb)TAz~}d0vJ>z_t&3wGEDd9@E4ovi{KSUj-*L`;45x8XHEe zRTz%BoB8%W^R;Y`GgiaM`sZSOgmNt6CQo&pP49W=o%Z>6M{p+r6tfOaCmUo_yc(3) z6Z5Y!WF5T$>kJxT@olhBJByq%%Qo{1+J{(c-q~d1b*l1RmcUQYSu(Nqui0H&48Agt z`C+o~@CHsk6WjZ=J8!f1l!(eeR~~Y+>vNPT$t2GJCBnopQq)qc9T3TJ6jyjx7tG66aI6M7`}XkuMr_8ypcHOsU-<}67@0qK(%;tUDr~GLz#v+!u-Sr* z9bjqi2Ml#=&__-(@e%rA38oY|()@1kK{10Tkn z@$|S*hx@0wLLK`cqu}_TkEA2{W1b3m3`rAfR5f{)gI|%BwO5$`nYu%Wh}OY^GG53( zBlmE9&c61SAPOi?`~^`j>o3O7gAvnLAobi8X1%;T5`+}kV z{yKW-*;#ljl=Cz^5px2>a=Dfc8QWp!rx~Emm&!l7p?2X*e;xkdgLueIiFy^!93Pu- z=k6t7Uc(W&i1XmWL7ggy>Z@8`*`U`J%_ziJaIvKtjEt$#QyF6*+J`~;pe#py>~7T) z5BTSC#N3nMn=TW&7K_BPs-k9A0s=657TYi{T9zK7RZkLgCGfDX2G=g>M>sYsm$bBP zz`1aDu>r0X%C?jGFJu?WgDy>ocfPYy=xExDIx6y8Y)=6d#Bka!cMg!!9Ui69(bimd z3d1|nR6jHmjZCO;Ve0Qj;xnjP@Vw|p@8{!6K11Og2d8#~P4NRCcPn`5dn5u0nDWy# z>&vJLdy)|^$5>KSwpp@%!+B{=sm>sF;uMIIS!5VEsHdB`IwIRI;hDc(=No5oN1F9j z$@CrkH7#cSQ|FIe`V??w#b=fL!B)qajH5DdWK-zyi-8IXgRXC4U$&ilt9He;5ch9pQm9vbOWni+J4!Jc}=0rlr}h;rgFSRV?^C=(LEjzt(grQWd)v zTW~-t%EKer|C)60QT&GL=2TYL?=;GK*}L^5yfM0ldyRF@;o2e%)R%h}-Qd(~rw5P^sIgO7M*(&~C4l(M zZ(%C6&q+dtJ_1vo%@2)$Nq?O?0T&7({yDvnduCYbSo%bUovjglwo*?H*vqZ<}~9eGC?F3D-p$}t;6Oyaa#vqy32%bBya^FyJ3p}Whn?Bf zJNv=N?pjl_kqBs^tD>dsw{TAX<5< z?nr`c74Y=nk@>;c*?N@wOSo3Q%Jzuh@XncV9fMR`5D)S0h8P)aq%gjbn`dKL+mVKW z$|J;+5t0c}9IX|~Vd#k3Azh{K4#VnCG{HpV7Yt@5GVa zAqbG@-+68is3{N8UA~wbb~IXbybghulaH%6(=m!c;|wXbW*1hzElPEx4}<6z%$}hi z^BbNvJgd%ZpF_VUVg;vv8H1t!?Z|BjR^`wGr@iR1Lm%Ag=B68oX|#CM zWj}ucd!%e5dPD!8_I~>4R-Mk#eR6AN4Oo*IhZ$!+H9l#&I|6BDAt6E6!35{8hJu-5 zmzDhc2^e7m9!dk0IeE;k%5*4f`q?gl^z0BJ{U^~P8%hWf#pMMj`gY|GK4LZ6>_nT% zNZ%_4+B~b9e%ohEB%6AI{W<7>$B%t#QnvLC=ELpnQjkNT#( zXKN^377mGOYx!U7y=PQZS=TMBhzJ5E6hshEBxw-%x<8266be|q#dQYY-Q&)#dzHRoL8$3mHf z2Dcb8B0?#JHq=t%BqpPm8Ph#%yEkE+jFvm~eve=I+wIVOoZ^?mgTC%1NwoWoRA?c^! z`(=34&iRX!X2f)inKfx*g_jciW6yHYm`LSE!`vrd{(7@*;(j_l(OUZSyd2FfejCN7 zCe7)QyreA2>8~HyE7LoJ>oaC+eHMeOT=5AV^<#ad72aCI*sxnIfeDm7 zPY-;06+~q8*TSD#l{`6vCU5qpB~tI83OLW@ffLI~QCp5%rIo%#;7g@=3cDzD`?e(M z2h#hXUR5SyZu~7od50~&Zez$Mi75Ty(~_`KqLnoSsy@8GWHgyxk;tm3z%m(O(av?D z;@m?G%#N!D4eBp6Q=D@Uvi2p5WX8*sA70G6=NaQAPk@GvS z&~+%E?06N$k>|$zEHxHkHKsU?B5+W~Y0MwKsA2dC0JYtaYLJ_~`SEra(Y+1Cb292d z9WEvHYf@%wf(ONuL~;FfT>SGn8e3-9%Epv<*>iTk>jZol<#YsUJ$QR{8Z z>VmfN(c#U~GCl0gdYvyCM!8GL*`;%h2O8OmrshAYUL1PNif$BPXC_(U-x7q5TC?G@XvyAo(Fhu2Pz>i<@S&eF+^@DAcNY ze%JEtlbIB!pHT`KlG&^KD^eNO8JXQ6Xu=lATT*zz6MRo5X@w*sS0_|^)_|_ zT-$a%qOYyyVqE?-=*kV@AT$Ga*bx@mDR54sG>aZ^OGN*Teu}Qgp4Vq(CrX8 zo|h?R*~#i^$<}O$%g-)f#z|=Csd{o<)k*Ie=Q3b|e`6ke-cyrR;jQaXihj|A*qz(t z>(9;K2w#5(<7;{9`&aL2n&*j@`WH7W8xwQbTBu5vN-z_tSCnA99jfiA_;pVDUeJ3om|n`yeP{jTw|ftt(*PJh>}go8W=q6 zkFTsJmG^>$#=FaKKUxDNCDIT)l4haXhphWNmoLnm5wcTDxi_7G;NCBu6>5E-yLb5M zbMsp5yZW2sgLU>_KDu!NEJMnVtmpLQQ`V`^C(lvAHm2UmH!3EFxA&_ro4nq(9u=4= zj-q|DEMs)B=obH7d{7+|Kh;|uS{f|HJ6y=&>x`{ZS|%FW-XbC^KP!u93Ck6FnG|z& z?gn_V_z9$sqY%ZbI`KJ@fVE;th)<-4U4o{pEHqE%#g(|Am?>4X1m3bm%Q z$eU>wfz6Y19_^vfrTNKaXw)2tUml;7I&;zSN|liI37^4>H9givf8kJ`Pnp;RdRu2E zKin8?Tvk5z>6p0KjT+tk%YXfimbge%XmN3eH2wU_%i+nd-=a)X5?vbAv+L<;a?QjZ z`^ND_G`aTgiW_Ww5z4Giub=N5-C46RvbGCeAV%_Hz154$zSSfeg;XiJrAjKUPFFwR z{P=MLAw$Q4#dyuNY^Hh;o-u`>9XW7X^cC+U#WK$-E48{fb%Y}_HM=*jjfYtiTrG7S zLRox6t|-dHQoxXcgZEIZ84|V#gQ_CM-Ft53x^L2t&pgbPNa&53#q#gG&zLSWy9AUb zw8-mE=?uj=#Ya_Ezp*k3b-sMMzM7+6lrp<4J|m!WXAV2wYWd^><8!%y&$z z-IUMii?>PcrRBveFITyi?th!HGJ32#5=*1q8q=bGk^5og1hdD)9RbG68QSG_TS>=x zNlR`!AV`>Lax)9;P&tlw1CV^HNSnuQZJYifBv?@b2(~1UW)IYwxqZfrl5-^EBOF_~ zQ@2A}s9*cMweq-fDQec|LAP7AGpsUh=KSeIb+O1Wri4bt%Qv3W8LmBo#h7W$do{TS z#n6gnhgtkUeDzrIAno7?NVB*|@<8E4XnMQR5Z7@)bQgJU-%sxB#3=_u5q1}|1>pY?<2I-bm+l-Vi8EqFKA z`ES~-XRO-9v}^8W1r%H>TS>t;o-uUFYIEkz0mvrUM-=!p(PZtN!Zw|E57_REC2w{4 zqM=_FKHl2xh-~!h;m`fHZD$))<%n!PDcJb9O3vCWsW$!;Hdpk9~L2is$^oa#ioJ zTWRNG*0y6Z^bRCJ6I;$_)^ALr@>33K%&03l0tDvwm-Ewtr{l#)B;;vdcn{WjtH%7D za6;eM3I0sl`txgCMs&#a8R~KAj@X(fCj#g$=wHOaLMNM>gyL`1R9wb)82xx#QZJTC zw?4tkAoiKws4mc~Wn1D2bI`J+P(;=6C;0fX^|F@}tP=6`>IoBkn8ZXO?H=3b!ii-H zKO?o-Ij<(i$z7Fm3dVBk4@uzHh84$4d*%FlptJ&aKOtNi|5@+M(6ETi?lOM*VcP`*&ViIncO|8y+Q&VMd|L7i@%CkR; zuU5>a(M^8RLC}|pQGV*XO8jImrBPV~6}p;q?eRMn3cvjAgm@t~JPFYZf4ssDlhen* zokz{2nTe9Nu`R>zKBOgVQP>=GPK}r_!R@7fY*FsKTpdvihH5}km7u6JPtSOeHZ7^a z<kX7fpN^tk_gscE%G4)uk^$Rzp`3RV)BvW(^ z-C&=yyV|{~e~dtSmf2sV zz8~DcbDl8&v9bx|GBtenHJQh6*|)6-A(x$Z42$iHK8PtGrGnP^0W7+mpd$FnmRr&` z#gpgtS=YDkSj)t7wN%SL9@6a9-;t7;e#I32VOyCbbV8icIdfCN4~I6 z6DA?Sr;1KD<*4YEZM}qJ&R1Lv4EReT@^){X7z`!B#C-&@DHss#?~$nWDt(Iv2L=o9koSTxdJx z)g)HCy{lt$=qZx4Kzky0rMxKruw%+~Zr1Jdz>o4oLTvrhSBdb;xPSoXctXhZ{wwOb zW!vBo6J|d;E6=t=%RQsdhc65+%{3n`=TzGsmb+`XOG1lV8w`}bT8#_aEu|8Aa&-#^ zST)K~V8Z{m!U^sCL5Hr>&}D?2FYQs3LF9VVi{B%j5OEncel?-hSuJ4&U&;(fa44bA^ylTr)S zWy6pHN3?YipaY7k+EsT=p8k)DC^@Kz9$eNwarDafQ(d5v@4u)1RjmdCl1@o|it@@s z5!kK&;bEhjs#B_27)dzZfypcYW}^y~cb#*d#wF@ZTrY$o*PkH0TT0Mn-WD)0QCr{R|d zRd=$__IG{_1(-`09}9shzniE@pVps;1Hd_`UXT9$2PL4(9!#c1w@*3we;y_V=U`>3 zCi`O)Qbz%~VR-Smj?tfo$-p_3rQE3g9MWp2oHZW8y$Pu_MIc%DB!U2IdBf5qJ%%cts%&-Xf!l#9YRij zU-aLBJ~~Xue+T;a)BsiZe`oZOPvzp{f0vH`bC-@-D=Nr8ejVqsL<^J^sx$c6-C4WdJ|-~dIgYFV%uhX)~7+`{S07U zm_TKDa1)^NMI%G+_^AMN^J%K81A4xsm`6qs2}=bR^ob0zUV=o6A0b*XWQg^+9C*b1 z4VeMa1INs-kP+4eoe|3;Q(A%L{+#joOsEG5Jz^?6sq6D}F6iF!C^J3gbosciS4O9o z=%ep%s>kna0$Cm@lxS0U?lAi=E=ok3BLW&(VzjU816dPN$_p4@e1$amfUByy1q9P= zYRdCRcJGFBoIuWA*!QIp^o}tp0J0Of8!7cqvJ2=T@eDF>5Y+MkO@=;zD$Cd-0W&r- zKB(tNocN7Np-wRZF<$_nDeJ@m($xnbth@?&X%zrM(o;xoIRa|@SNk3;(hvhg1BoHhZ+{-;3-scIw5fu zmUQDK)|s&51H5G(0nSaxdMyyVh~%z<#w};}766T&_wF7zS5UN;p|Ay=D0bnGUSN-3 zNDY$&XQm7T){#1rSFxwc&F z*SEhoBQ_(^nY4jes@tqJ%=hZ7^wRcP+#H&z+t|!0?QdJP6ra~I2cHZ#okPtJq6BC% zDQ_FDXK6);`Rz?&t3bb^5}lYB48&m2ajPC4%|l>|6BxG|PN61NI^64=1!q{eAIoScY$L0--F)Ai0 z|6|9Ah)K^e75q)iI^OR@cFAR>Ak2G;IpJRY?&vbAA9710LwB#nXnxqUEox1?^` zq$t!v91%GC=B&@-LBKX_1Ac6y#GF<-P_vLWz~M3M9@wk%TgaH4XyvfcxD}I}x!p@5rbjsw1Ev9usPG)FnzGB+kI1{D>+_Qdz?i z=Rr%TdM8kVjs|Mpf*^+ff{k4RKZLcd;SPE8W~_YHwqk# zPicjL6c)5gxq~yIBSZ;sW?cBslm2|<;=9IuB#;PZ}XvNjeR*HAFPR*LySe@qm~klwm#6H zq&OP83L5QMBgZGoQno&^wLyM(7LHY!tn5nJmR0SV!4wRHjq>_hj=LQ`JWY5&?zyU$ zs5wB~GYbrE>fjx>({yNXVt3<=^XwvG+9_Zc=PCGmR3vu?JKJsG1r?36()+89;)D9g)s>aUCPq%Ci*$8)%yY8+`c9-$Q;3Qsv=d2fs!*V@l%K84 z9DSLO%!*QMi&8XiR@7wev_L)w;01caKg<Z@p0Qt4;manX<{ckG5_euX zc$*?Er-p`I)(=ieZb`%6&IKE;y^Y9A3gqY{W0e~QLQZP!Xu@|s^E0jOfdeYEq-@fz z$&)#=Kn89l6MdB*Tj~A%E~B-~gWO?HmfufhY){r6Z~O!3TkTk19uBlfXeMuzWC(PQ ziEk8!*-aBEJt3flYOT}+Vq@wc({wRBhbpbeV)I{cOwDsd9(P*R8MG2Md}g1|^RW!Q zGR6>H6*nvGND`LdjCqcZ59F0Lsp;4hTiW ztfJ8nF^;xYAn+?hq{BnxY*B^eOFr$U4C)2|MFUaW?@BAdn3Ty?|xj9ng+#W!{yi~|6YYcRY}B7gI(}p|CAQF)0+G!-z8gnw>HnVFj7hS~ zfI_P1XieQr7~lgJ=RR(`d-O%|`m_%qa>f83i>y5myWBbi(zhMQ>yhl~#*P=X1&^+~ zzGM)`$~>5}j2&#<%@u5QSi#|_V6Lk7d*DZ0G{C^OVtC!|sd1r$0J`SLOmg8W0Nzuv zNp3@#P~S-^5zR78Cy+DTSCQDBS_RRE*PUJL^sAW4(M5ar&$b-Kb{)!y?svl)Rg+hj zwPSQF;^noB+uX~mM{`H;T<#oD3rGU|3!jSR_zQ-QZbz=9WMm9XHMu}UE= zhx$_%0h`rW;3AU`eGrjjes4uXuDhXxID>&B=p#g5ak2(v$PjT_X$P2f==y@lEDF&5 zNAZABHC(aoG(0?3CI*5V`#K$M&)Gvds_!6X=Unz0-)#dM=9kvG!1z313X2DQ_qeii z%XEw9PB3ms5ptU*+}4*9XO5quzYmfAoj}GIJ0r1K=?Z#O{J!UBq!1wvPp0Z|r!xJ4 z-RGefoVws!#Q-DVEhoSMj7_kwvN93He#o7^S5N^qv{%Bb9|T{id`%uDWlJxi|fU;_bsUG*WI3c3`&3#mEGfp8ZFWTXbT?h3%c zkAf6CGMfhM#d_2?1{AyW)KSeXinCfiUDBtbMMu7#!Ll0oX7d`-!hVMqT|!7%$(=8z zuM}|{UY>BUE0*Eu#55GMjV0SCnXypfSRtyfYFRZ}a$yb=nQ*lIi*dh(^t>zGfQ zAnU?b(VMO`_RlU1n+@$mA1fd!f~Hc-Gkh($o}S+R@^EbJg1K^PT&fck!=vhoEe6WS z7j1uUR7eDjCnp>qB)Ru!8>C$762R44D9nv=_1y!G`66Y{YiH0wVpG=wgFOkGiD#+d z_dD1me!N(X@AS5KoIX|A6!I|2KSu(iEmYHAyVOhk8YFg%L4u4BD;*C8;ie*I>BCCU zz*fuM#xSFJnTU3DwFShxgT!e@`*T_eUp-FsmmjJGTXOY?r686nXUen?N7nL&+eEZ9 zW&_SI|n=EK!-5k zbOvLDY^=eoXJ zRmbH}-{b-7#{3cq*Q1`6_bu1_9t zq$RMj?~Tkz2nmjRQeG^!f54&sW1Sb9ZOqI0BFwWVt$B{aKmd}z37r@X;k2!-BqS}5 z>_yKCHY>Usur9RIhtMMVtweS$4#gznGx_rB4|Z0;9E1no`2ATCi(GiW`NP0$JUP2I zc(1z6+3;krB=`v0z*n}0_OhI2907epP~mrR0h>UBRW~~~_^b}}&s7L$__4!aIb6jx zT*h56yO7|WvRN||&HohgTH!8T=fkiZxrl|Kv7@Q))InX@O|I&N!)Fhy23m`NNw919 z=zX@;+k@>XU)G!xwK`!3V6|ii8_*;8DTQf;?Vb#YUSjijl1XS)rKRr^9A+H_8wagc zRbKV?x53l{7U1b_^p^OuhW(InG?*|x_-|$kUZb%tQT#2(nkXsWz`+~0l~T7VTEz3V zTX$iYDqFZ=R_*x~JxRUT&g__`N71jhN$bHnCooPT1|HsUVzBATu|BT!j~n^(b7igx zJMIQXvE@edRaJHdn?RmYk4PXBWBl=4wx`K7A4-1-64H2(1a?t)J3}I|+|6gd_iJP; z83fzib^n}0Ohy71m$7&t$E-bEq27dvardv_3B3fautPwTY5fBUf;054-O3E=TSt(_E8 zI~87e86dUuPl<}AszD$ruxrNE?1t*7oAs=E|Hex_24Hgav@GkuYRVOU%5-<&C>q3r zKtdj_V1D`ey)pOE;mHVWdiVjH zYn@el1gP%XfX}(^1^rT-ddf~u;djXs6D8_9)R0LLzA((k7SZ9HtPbfeBW4&PyNUM$ zXlhasDf{4(lKji_`Ctk=uu?~NQwlVia80-WxPJcEz%lXoAi=)WiM1tj2Phi#=Nv&U z&Vqt<*?`ZFkW<`#+ZZS$C@)*+9~NeWu?|!N6wA;JENm7fIAT|jefd_d=Q@m*Tt?5{ zR&EA10ot^JDc?ZHP(8@Wt_bMXVnDK(=RL1CeiLYM)xmZS2yBAUKz2)w8!R`94evhk z76B{HRu^oX)SeqpiY#N9cnj~Q;GnJJSo`qUM%gM@$Iw>SMu1ujHNhJl^VJcj>j5xQ z{&6OX?9UX=kZLUkK)B_zPAE$zo1IowU$wCxq3%%>5pbBY<@lp*9hu)tkyJU+3r46V zf(27TyN^V46z_uw*(>ooi7{2_%Zu1zJn`K7I~=N7&g5Jm_%R1|<+q^b@Rh*RK~aVc z^;luEb0I-qfUq*}!OTjDaS+)(gX!cuvNBXr0rlr4g=te&hYzjqC?pV|7T_ZB9?Nc$ zfuMn@px@dl{=S=rJ!(1AFTm>bfhJIt-n(Pw83N%Lkbh3_h&@^}^tyH;Vf4Ob7T?s7 zRH|W*N>1^SucjdaUj*gIoqQJ40c-c$Ct@L0YwOw-$oO}LKGfK;LnD*w`tOXy7=03Q zV~F5YoL1NMlt{{PJ`zO(z~luPDK*=)*{vgwL)v}|q&qcP_H0LP``^F(|2su!{f@v%&w(Y*Vd@eBn8>)b65>e8&cs6m zW(w)+s{uzJ)Cc6n8@bI*1uoR(G}wVjb#IAF8hQc=yzBq zm3kM+Sw2$qal~AF_$1VVSmot=^Fk`k_@k?S>NrtAlW%`Y_mRq@R4Nr%k=b(ij&A5u z^$^CLCz<{m9+C2wVgtWc(KbJlo*hH_l9N*J8u%$3$)t}VRfvg$lM33Rj=q*n&T*Ty2Ht&z2LsjA%66r$oX?Hw+}bfkM28bdH^(E-ck2FdeG%M z`1Ri!=%ZWV_i}&Z!twQtwLcF21%6GIt#ot=>2d%=Q9rLb`^Ui-;Mf1F2NL5Jk*zcn z-N1)|sV{lYh{lS7z!QrA6%#xjc(ZeY*cVsh^rYzb9ZyI28#{9KPLp=x^W1A~1_3n# z1C^G69Y=7{S~t0%+To1kAqOPd69%4v7|_=2AxJQb&c}4TIkExArhPDNCGzV+W%-ar8+hUKxs79E#Mu(@YLlffoY_ke3B76UjxQ?1d&Z_fHfG(YE4s|QLnJ@3lv_ZJ}) zH2FRP^3DK-s_Llzx{Xr2PN%1)4#+)Fse}h%o)0U*J+Vav{T`+1WT`96d>i+!FoOiM zKALg=yDZCQC_KEID(_5PT%jO3!s2>ZCcV4hV)4ZL8^xJsZG5+gH38LU_3K$b57>Pr zn{URcTU|jCC5celrs}t&P9Wg9{VKOvGkl1GfVb{|uX`0PsSOk&8;cf->arix{lc^2 zxQQS%3^6jvpatQy5zk(%$#{Xn8r(>wUT9`8*q9gF{FAeA4}u;r4!(qhqQisTuxhMQ zuH5{P_%foC?4(G_?#K+vDcyFR4P#wp{^U&|-qpttGAD`U_WnwaCHQPJakiEK5|dCT zBbow&k1!yW6V*%v^COS8gf%yGJGDRR>~4m{dZrT~l*B4!KTy}bVIw}B@Gb+?Fpr?d zNW_f-2-4O7;5HK8@z556!)9ZO6|hH{)PJXp7g1!524(((A-K#N!K#T71a;n zZ}<=7+%Q=Ngc-rzw{|r;A9fb;`7TP@$#69yHj20tsx~zDdHu?|cs5T$PCKuXrT{-m zW9dndD8HX&W#hS)$1z(8h9xjA$8^o(thnyjzDAewy_Cc4lxeo*Vq2blr~u*MH9Xwx zyXtL3lts=}k&5V829s%jf{S^3S64R{QIVXg`7EUwap;qm0ljL=y)`R*vWG zA{J#*X*h#%;_Lb@kU|yrGB(Hp9x(&RSQYontaf~-m>G3eQC18Ph%ilC1G-tUBf9{> zZD^q!U=#egf7fp{akl~aYR!E^oWbh`Sm$xH1|RPJjZ`Itm6$%r86saFTI?((cXbHx z(Mif-NoslF`k%0etrNfr%SYxk%dKO4z?H0&=1Il@WZVY%O@?@)mQMa;`JBBqQ!@tf zR?uM{pP1V+K$pm>>|bRFq%ar6SBnk_4v*R$gn-4VWS&W#sWINo%J>*NM~=;fvmOm6 zP_myQ#vzj>u?|49vgEYyFI$dh3=t9R~=zBquce zX&!&;IEk`FmAJmPsGp34kgMXkzEE)_avTD4p04iq=KE?u9xWD# zX?NpUQIQXxw<5kX{L*2DsI1C+pDX#Ga)1FG zFD;0+B$TUHc?>?M_UwzY@qqm|m6h0FJ{n7+IEwCw;|8mC_eh-N6-`kXs3yVSMKFR9%&z z+|*ikBsYh&sd8nD{VAj?!UwxI8ed**5Bll!^Vg0*US!j}c_WgbIN+Y2aMma3h1fF- zu@>qLv^npkWDeSA=|FYh`=*K3jD^#PR-a{*!A2qbn~fm>;Wmjx`rG6FJO8>WjxqpH;bA)q;T$-!I2jZu$ zJw3JwGeqjsk2DMJ@<5-schMrBs*=-pdnbzG`QPj=$_VC;R%o5 z>&GqP?tur4=56~EpmF0+)Gf~7*^_auvsXRByhhS~Zh!CF$<7U%8bWU9So*P2Swpwa zyvh3umfR%{20T=Vo|@?$8;5`rtfjh2gQZ`Q93@r$!*D;dYt zq^$hS#@|U|krhhMok{W%%XoF)vn!C{;i|fN9i{xrTBv(XTFtz{a#nDH6ff1tGWcwi z{XL+TM!5;ILW}lqO6eB9g@%)<0we9Mq9soC_#5V^S5V~yyW42AxJRIj{6#PUtKyKs zeVc5P4v|+(w86}qGxCG>+68f%Eh0|Vo(1L$!EUK>K5+DBP%>Hu9<(%jGqzIm}q`9UCdibGvK8?G&r1OG0fIqq`fK*OaZEr&Pb|CVWfcshe zLyYe3Z7QFX)SJ%d`l2Sw12e0{)U|G~`;N{VYMlYbITpGviR(C=7yER|la_241oJXS z+q7dTy~II*ubHR`>SdAQ5c!43cdPK?0?LO)$*3fOh+& zDJ>80X47I6qRmD@>h{=0+ZV@&Hg{Z8YC8U6SlpXl-4)(#{|3Er^jNdaCCutZlPlnV z3U=Ov$)aS9zEdNR(_4laK5uZir4&eF)|F%ZmOV517A_8dN;kVRks@b zlvNI@8jywu|NL7yVL-woLj7&U>@Ke>A0_NX19{TpZ=q0jfU zNhw_{EdGHC+)g$*#a6Ofc;WJ39{N#QMxQGJEBWGzwna~mSQP9R(A;i^bff7B;dc6T zUKZx^K_IUErEQAkh2c!`!!6@Y+=g4Qrz8~ucJr{Z#J-~pHm%b{ET-!Hjfy5KAG{%C zMxv8KF;{1M?e}}!y6;`{LWxB5WahoQBa=&{a6YF~Ea?c+vu{HLzAM(RwBM?HAJzBC z`#XUEVdD=H1RG}nEw{&(P@3;!7e1^j|6fq6%MVF05!ZOdM&a&XL?0VdswDD3V9?*| z*5>>Yrwmq67fe^|{){c=+c*e9S3$6mJDV48Jm;LUs}B#tK!sO}sV`97&-5%7z3hVS zmy7KXJB)SU=~gQPRcn3&NqB&@vMcI&$rQ_FWhsr=Ub{IPbcGyz6PR6&z}obAW+la= zk8vQxW`sLAX#Zjj9NU{XCHZ(2>YRbntf#mnhMO%;*|(jHIO{C)8X|61cb)jO9tV{X zFp3~RneHrUJ42_PiGf({mCteFt~JKIjTT9F zuD7%~KZA&hQ2|X+*V0N^!-&ghrC2#=b}@z{x$bMw=JClBD!*SjZ#yqEx1e0mWP-RgHWAPnla(z z;eNDi`%oZ9SwDVXX^J;)Mt|R{L!$V$_M`YUIH&nXqNa^9+LHp20TzFEJd#%R8fICS zz_(4n$-GhEl&T`8+vI7+TqIxrN>Zomq(rrCPkBxzAxKi?8{!@uMi%)k$=d8doLqi2 zvvbXB^zHf!WLyL&x8Q$u7nHRJub=Wyd?99ym|mUdEB|Xes2xw`Vh?A2ZOI9LuR=nq zFcEw9+fO<-V0#NP_PG$0X#!m?4FK;s#p&$!C!fh~+NAt&iO2ejEuP(Nk5b|KCw)h5 za~1$!%ytETF)Hm0CB@_L?`B?NukT%|Dr3(pYP+d?_wMu!)i#@HIn^;Jr$d=rb$yn4 zNp{(}6XmFoC2w(7Ze>sf9xf|d&$532+I!BvBUQ|;-53&bk0@4}c_)Rg836qz5{!BF z8`&cGD(67Vb>IZ40hCKb!uM@|#}d2{O5|ih*HM^zfeFOxRV;+}Ge0t29CO1|mDe6x zUJrZ0ke$7FLCpO>S$&`wE+X#HD`-zeGa_~(%hl_8<+X57zK92HZLYX~8rYEdUAk0K zHm)NHR>z^dyvV3$43j$I;cex-vqVo?M8^uZS!X~CChl$d2KHa;ybCvgYsNeT8Q)|Z ze>o;pT-oQPR!01k)=p6J*MtX=t@B;uF{LD4p)8Ef5&T=qm>(cgUv7(@{x5C-1;L&y z;FKD!9I&GYWtf??>0cG69sPBx7WlQHM(l@QsSs2t|M!$f;I#iOV_DDxcrZTB`3gA& zzu_(L;dWK}$24`kxccAi_Gk5RAF2Z7cSObgH@|`YH3@)@qHJ!*j@&8fnkvBa>P6H| zjvjR30&^j8;P|g)0lJJN#LAshdhsiM`_HwF3!P0)9YIt9(HFp&``K`oqs&aH88B)7 zy~&@0{C~+vBF!z;!ySOP-{3=f^P^1eh8!Agi+FPpjh}N3*!G)0>|)OKR&nz3DDrU44MRUwq3Ewc>oMt z>Z7)ud?22&Ik5A-DSQY8c_jDDT>&$x))sV`!pO>k)j5XB_Q2a;&1c#dZ-6+onu7H^Pb2=&4iWdB*|E`9FCJ+%C0NXux2Ga$Y8|MpL$?bj;Ol4qC z0FYm|&FBij!=Gf`ZvW@uQf-ik^RTk>n?P9u)N=_XG@M^H0VOJ<4a!=Rfl%zsqQy2; zv#>(AFqi$6-P%ZY?y~FzP|sNrB`mWs8(^mS=eXeO314bao+5Os}vJC|Ab)aIZ z`~X)Jk1;xMf|?-q_uI%-9@$vp0*MGWwf6vSuLB61DbY}1rmSelIOhxsFLQT!qr;CO z5jURzI<{MKawYCRJB_oE@nEIj%R~2Pw8{iZ2amfT+>}r^+-}o~fuK1&1hgO8@w{-S z^)LbC?3)l^h|`T{S_AZL!|g=}KhYQZ3h?e3U`1?{SMlv7z^J{`2R9*^Bcdmbp*LhG zE?g=y84N=VM44pCO@k6Kl=-5Od(Gt4pLwbipn_LV?L$=*CkU93TwI*n1zav)JcQmA zkAgmL1`OMuWn&IE4-bss?>m7n??)%V_lpJDR)M|Y>WF1qR1E}$$2fRT(&3@9&A?8h z5N+)b2)A}VGkXWL-<%Od?tmcutLmd7o;h=CJfNU6Xvf4&V|H+6l6CFV#fW%!M01vF>(n% zp?RC@7-!`vok55_zKCVrmspRo;cTe|`rkGk%Onj59(*$zV>3;Aj=f(auM=YCaolZ7 zL`G2y&DnQ+1>|Q3oWU*>yF!Rf!lVJW0X9MUEhjK*?R|7Z-Rm6eW!3xkB!7B2**ZMG2Z^vB?-|3EiR7rSW3ul>(5kB z$*b3r@a-%L4%@0b`4|8qmvVUry*nRA3ihh|Na0@R;hw56XjIWW>N@J$Zx`wcB;i8q zLt{_)4rP-8DjoZi*X|l41~QatsJ0%l1DEFTj3siJts_?9~eeGJjy-X&K8n2lq z05E1{@97L*{}Sd~Vhi!z?=tS(|0!SWTB>X{f++hcqKiGmCl@LCy4szS35lyOwQAnp zFt*BwIs};*H_*gZqf+x~?Q1qUWZNQ5(TVbv(gzSv*Clr3`;0|?QbbF*EQ1bT*C%5I zo@i;G?$OeupG+ zrwg>+@I&Ianax!OgVKrr2KwM}ZSiz2DkbyOH^ecO;S4bnKm!qTN@4}6jByE=d#6vu z?a@fy1{CYsF$Qjp%SIO_7y+_?2hh!v;>EvwOXpepn+n2t2=`j*c9--Z1tKiUJmBL( zq!j~+dO&iI7g0oG)WkxrgO826U*J5S-N&B-LF4))w4Ur@&CoJcLDPa}d6kBjBSE6k zhA8?`qa4ZKB7i}jkeXds=Z3AX;4r zh-yFF>F;&PLxK!2o8Ia3HF5({qrGo57-)4tC+ys#G3e2cCBKwY%9K*B|B=HIok;7D z^(xV{&b!Sue$NKAa2OtCRf8-y*#{is3X8>miU;4dnT#gf!>MbE7r&7f^K|}H4s&2A zEdw$kxg-@A9_T*0M&7Xv$V)|OCYNlObCnRYaeED7P*McYZX)?fav&WV&xO<_3M=`W zmF0adXYk@8!Oqt{%K`L`T?G=xOm85LwbA9keTef(-kV|*!Xu{PDT8Bn3qHD+ot@*Q zm^ziw=3gs2ee7|ouPsI4houLc;iEo$wAPo6h+tv72mOvx*iJL1=KM|fVn)m&T7)a+6m$El5RX&-1)4<>$m^GZt2sYieo7$b{?MmBV_uSM6M^~ z+kg{^@>Iyy*}w?ny{QJ+nz%WuTP=aUm225|vpNC4Xk;ZI&y*s}h||?jue&W*5SKuJ zDl^K#HoAq*d6j3>>WI@I8TlL+m8Wt+Y~R>aI1_jOU0uwD@9(08jz1!?wa(}<>R>iF z0m@UVLj5v%8QzVrMLK9MklpFt>9-HZD*H*0SBB!|Q2y6P%WzIn9L$E`T?!e|i%s)} zS>*+8@XqhH_B|MNSP_^3glyk}$}wlk^k~ttOoBI8x9$#D){DtJhIX6TjEde};%j_T z)+(F>v^&ZMQv_6Jm&dG~gO?s)5*oCDo;dybG~0Yk%ebM~PF3CX7Dce+bnh5SPX3St z?K<0_w~Q9JeVD>Z5N@>;;PiwI85^%yf&{cgC1&$F7hkEAlz z;SakYG+_FE<@7&X`qV=r0-ts}B^D}{PRocQ746Tl@qOI|(a2cIOFXNn(EMUwhD&k# zt0Abmj_zg7eX73b>jCaD+GQ4_Y+oC$L~gV@EfH&2C;hp`vGS1@q-O#vQAyz~KBR{3 zR`)*FaoK=(k4A^OFA$!~ioMkba(&ZX^g8!$u9u>JcnsP_+OGUBbHmRr%ZiGB>8@Xp z>x^zp&Z z^=V33Z1^H8K}{u@jKNyLMp^d~j`H?sD3x|;wcIo&g!m7BRlrf4DIKsF|^ z-Kr{RWHCk90?R1^mJoFdX|mBNFur7xJRxE5go^#FqSY(q+KhYRMmIiXqrK%LW?j_6 zK6XFMM|%6>L=vO=4!tuU;Gm#_^*bZ=O0V6sT2^h9f;eZ|G6#v`_&m0hWzSrd^+UOX zAXb(M<$}0F$yo-@f6yCeS>xGC=bMqDk;HtJ=f{9JJ|W@bi-n{-lohXHSNEBV^L2R5Byg;8@!7uk0;?oayEd~E>nqCgT|TuJTxMrS$;AE% z)b*#Tf=sE%Cz-LjMSO|3tfo%>TzHRtc&kA8Qt)htooqM&a zQUl=kWmaDmXy)Fz)uJ_cg_g)7x?c*ydhv_co>Dnus1TA`xR^Cg@O#cs5rSG;LtpEQ$82pvlJjo zbtp?M^7`a|Ht>eTb8+R3CiVx`7{%nfsL$`y)>#n)ShLzI-Kc2(mzDH3h#^kJQu_5$ zH*!n^-N){NggIL+JzJ6!tteT86wNBPETKuVp6(=gZU{WQ`IaoD8Lo{#uGxlD1=ujW z?pBE7st`yoEg%tFM_1)xnBctab)})j_N;TM45ljrC!#}Qt&_@61Q1tPu_*Y9Y=u~3 zGm^am`b9@)Rj}V&l$9iSGuO4s^Yh7=Y%q%x&S|a*nLQ6i-B(;|suYFi_~DBWZmY-k znmk!~>oztiekQTx$9rNzqAMsT+hSGZE<6EhPYWV`adsJIyJgH6TwkdsGt`4*I^kHJ1 zv0TZM={ryFig$PnQ*s$Syh>?5d?3mhG;5vk1&K3BN)?<<-mJ0oDxUV~>*r?8!rr0d zG?CWbia-}Rtj8?~+@H7Cp8s+EWz~R&m9VPNBysR!Uh}MPV)A*bYj)*J{j2Uj{LBiU zmi{BZ1H5(e%&Bndeh5^Bod0 z$9BNCvO|4QlV%g~Kh$(l5#0t(HU1XhRTYT^zL|<|rKpRb?jyF{Ze>rjnr*kSOGhaj zPz|yjSFf_}qx;%(m!vtf(P++DTLGg>G9y=z!URX!mtkMIsEKN~uaPE5`x$xE%P>b z8rdGsDN_3OYT}sjJQj$83%VYjEEOXog%{Z+;}r_x=$16_ecu z8Fh#mDr%OUI7=4LyPOHnPV=Y?#Yv!osN8BhXqlZ1tQ^|cC>V5qb{-OmsqT7 z`qD$Lp!!;VbOQ=zKXC;aJ>O#`U^EbOhK>@ho_>Qe;d8ByBPQR)u|s*+O#P~d*?@0+ zGjpc6$>XY6bi(60vCGSfVSo!(TG>a~vk>K661(uYX2~_5j;<@Cc3G*e)BmpP1Zqcr z!T4aEwyP0OOU7^Y#5lIfK%zg8`8|+rjw=bl4e#Gx&}m($zy1HUcb!p9W@#7#qf*oz zDT)Kgk)lWt2_=Gw8!SM8fCADnNC^g%mN+WnC@=#{?`Tu_kOwW`;__G%CK2c^^*-xF$%tl`z*Y0oyQ6u{&{!)-Ox+utXR zjC}?4fymCvs?Ih2_Wpp^De@W}HSE@uGu(w1>n= zPa&oP*P#L9u9Fod<*0$YgfWsnuz0CEYpd{&fR#~q7UdVpcuOW=nBHl+@f!ZJbv1F) zl17?D1f1$Nw{|}65UXqPwlpKOd=Mn1ebV^$D6Y|N**#8kl$Nk+zzOKH2hRN8UjbHH z1`Dgin6+drb2x5suXPw?X5?f5@{ENOZ>_c{Qri?!CP$LpVJ}(-kKX@vfhVHcc3_th~7K*-F(I20NCeuVSzrh!S4r(uQT;m zxCD5yG9_`pr9J)W@rYaiZ&kI5ziqrDZv;9#sn18E|3M->xSN(^4#5MTIb7&@BgL}U5dN$ zJb5}2h~-R4p|c=6$E8Pr2Ea(o8t#)SyI#g&hRSrHTWMcg@cd22BRe_Y7$__{voXX@B^`nQ+5 z5R2*YsG{Zl`|;U!T=z!ml0SYJ=%ce+m-2%rBzIoJ7`ZvNq4A?dyK4as-$|%x`>fI` zc!8CPLu};%>cccp9IwVNSC7&J*RADzEJxLP`1JPRn0o2UW+64bjpaW~4pE6tov?iD ztimrlnUh{pD&a^d3FVNlT;s(eD%oBAsEWi_$@g@>|L}5U1z6#Q@W|ECm-|<6<3ejr z8Bm+n;OKX!!W<)yy*Zmk(&tif&B7m1Nr%M8;x(XpddSm_cK6uB;AgWcau0tpMViaB zBD8LX%<}{>PYj!D_si_zm0QcrnpHlFkb(+E-Wea5|4ual(O#f^&|G9GfVq1-Fz=st zf6~!U&7$|-2-02~{tcmF7qU&^qV`etU_`kovJP0`Uh_lI{p|dQrNd;35ZIe)DJI1( z6e~kX3lCOjb9O=h1^siAZ4?b7Nt>#2okCTEF@jm$={EgEXDgpqA7%LGLB5A98JYR} z5I@R2{rxoHP~#PhdpcZ%KhL3zJ+L`K{HV5gi)X(uVYV?9T}I9`3M*h zRxiCn#wREYGQc7X@USN7U~iV)8OWl@aUC^Gg(@O-(rs)#Ei2K$;gH&ao*FO7)rDeF zv3Z}{^RxjBoL6fW-3l5_x+1+NOz@3GS3ZC?ffp^G;|d{Cy9r$~`Nh-_HJKT?T`Rpk z&=l(OIwo>>aN?l$(fznyENaAWqS|Zd@ig|Fx$uwDRi@yU7FxzPryVnMerWw)`cO2r ze{KpaHHvBmqqAxu1rw_V(?6tjVE-07I4~GRb1rX0-#Kz+Xv;q+t%ID6(Ov1 zCf+9mc8mU=gz=;-!G_rY$)XUT@J30b-+5s>K+rywU>J9YK;?j1m(W>h($1Du^i~%K z(-}f4h>L@e^b@~g}2`pcX zh)u%rAKN5$RtH3d8(!BF?Z4tQM03E_@os9~q-#Zs)KQQ5iu5lx7F>7(QT5Zz9D}ck zERk@)Xb2zEHUE0BY8OyPX>V5LF1P_c#7MIfd1E7O`~b3u8)M^ABi^I`o@1g6gym%p wYhu%&4fytdIjDaHQham&)7AOE707v^rqX9TMP$J#BxAbHXR)PptjZasU7T literal 0 HcmV?d00001 diff --git a/docs/docs/install/img/truenas/truenas08.webp b/docs/docs/install/img/truenas/truenas08.webp new file mode 100644 index 0000000000000000000000000000000000000000..6956eba48bfbbeea4d021ec75b61a3cc498f30a3 GIT binary patch literal 27162 zcmYg%WmFwa&?ZiBE*ji5xVv5;xVyVUkl-5Z;_mM5?(XgccM0we%ln=4?e3rMneMKd zr)p|WS69_2Ns5c3S%HD6i-{|T;Y-G*9(2!Fmbu7#kr_d> z+`lePiN?_=*A!wIR=|%VbN4-Qc9>KU(jmXf| zU{pjLzXX6)27@n4oxx_MNOs(-w#92?X-jNN5HS0Jh6*`8s-{f@iPE;B{;Mtv>}v`-R%?nA94a!DFhTt}DJfqF zzAtQ4g{}@6M6J5W0zYYl;%u62Uz9$W3Qjw*MF*l0M_p0D=;uGCUCX4aOY}$1+Fs}z z(p0-^eI$+|O}u@(KkX|6cg|SPjPsaEE16W}6`2>*zN_RJm1N~HE#xzm7qm!znsl-OVf5mdE`GRc}wbsN)m%PzrL%`!X!!z0v#}58sP(W6`8dQ|3e@uv16q0 zA958$t8`WE!81a|6cykny-*xolkH-$k`$d}D=!$-74@rj!v_Kl&4=%@T;Tj#+2dw8 zNH3_cGu8Ft2UYi0=aDryfklsaydP(7#baAR) zomnaJB}l(o`MWP;tEHHC3Be-qefNuZclPw7@8xG!em5FgHp~DP4M&Yd@DGFym;nju zMeYaNT&wH>To$!;rplbHvz^Gvp((Y8^Hlr{S5gO#n>W z{UegN^K9~;`1?16GH!p1T(hv(j96N={6hGCM>X$PAG`8vm~bMA_lJm6eanahfZhqH z-9u{5i3(d9Y$`sEAN%))|L|Hw!VKdG)nx5S+ZFWNt)BN97u&M#uS-EZjUPna8)gQCKw_9*92red%*hu133N z{zXgN2@CAP+U^uQDa+^!+?jvU>ffMKHhXO@zc9Xw2_C-ACck^Wv4eHT3k>QH=)bVh zr^ufKJz~r!p6Ae-6+mK0Y`4F6$dwy^;w8XLXTYsoPHJOX=hEEL-$?se&7M}c3-1>8YF!l z4R3^O72KZ+g1x2M-x%_GRT&O~A+!3hUb$;I6TLM;Tr=+LnvayE8 zZtP@H*T-SL*xYOGQh>Dc8ryp#pGBXjXPAOP;L(JL8NPhg?&s${=SgpA=G{CpmAp?z zM~nV%==NT!C%cdjd{hTeq&;4>+I*%U^CR(|sMokCvrhn{+Y~perT1<$_cjVDQT>%X z#rtMBVZZ-m? z_!A_ZANiDO`H6@D`JOTOQv-}3j$8QWH!wBQa&()wu|6;&F%{@0et9MV7p9e`qgmQT z&J7LNZO<Ck{KRV9;2t#ONsnR|7(O`v2SZ z`))#9T?&H$*xoPBBedH_(ffXD1IFCGd(dGG#K+mOk ziuF8bB0}@Px~-v5>y+3+K8zgIVnJ>#Dr7QqY~Nn%yAElBbOcd*&_)OIk@ndaiCNCa zr}O-t{s~ouXF?2qnn_{z>C&-5Tj0q859|@vfu%2$=~vOF$wAZY@fOhhnLXHs#fxG7 zwi%ZrC^6Zsu)gNYTi6EtyaGS(y20r8G;@w1>*Lhn?xQuyJXWY6xFTp=j290X_q ziXBMbb3}5B{;lKaY;e5W6G2~5fL|8auy5N8Clnx|YE~(Eqsz(YnHe+uil~TvkrIlM zXWB>^dtR7x)JO2k?i>el3trihhCyFBfQU@rk4WDWY_>zVUY@|LH0_$m0X?p2xeCg^ zT_xt+G>Fyz9`tBcIiK__E0=ajQjG~*F7bi6^^vBE^)MUh5>X!v9~Vp^E1(gJdPO&d zr&eAz)JH+Ki#?QVj!)_q$ffCZERZ<3ZblMfkX5^C2|{%Z<0?q?p7U*nHBv2GHp zX{5`|s`a#2$uf2?4TA(3cfaY86O@YippK$d&CTyW+eA9#_w3uiJh@7Hq~a5J;S?yZ zW&Ig4E)n=DCZb`pX2i(V^mZmjc~YN$`mks~66-5|i@}^kke?u~lYrRM!~v@+L?}#~ z6E~=7(*Uz=(C@%G9{LL&!A213y{JU?91^2Vy#S#xjSx12A1S!xY3nUHupQwBA6VM)F>a(Q({mJvyEb2ijoR* zVIBAtM2}LmiZdaYcHAVO7VFG;@8>tt6P81}A82asHmS49`MOg8RYdyH`=n za7~j;>p9uh=Cw#_+VF{&?1Gw*H+7DWJtna339B%MHRNNo!E0NlPS<#JR`%oW_NTZ3 z(J?&#S%IiUh2>3HRfu&W0@3Q8oSt(P!zY0m;pRR`yeU*TAsP?5kY;&T5v6oHn9c4w znuQIGwH2DVE97+2Bt!Tn5}obWJ#Mrs#CF87kBt$#pPq#FSX$c1-Y&s5DL-X*VMXZ) z-wjIeB@Tejy7 zu)_Uu_4fVN*V!>4Q}~(gobxKHcc-=NF}s-x#^RY)==|AG6&*&wAlCsOgN?Tki*q)^ z1qI&}72*cPNtUSh-oofRbSu5#=G{W+%VD(ht5)H!_?!qrN>^jp@P5&a@J3>Q%O5j=u# zZo&9m7EbUnRAEKZ=JPG|_zZsAa9Kg<47_LsvJv+=)GY+=7973ybI8W*uwhy@m;Rdx z7tyA>dl2Ww5`U`~l8P_$DwI_V&nCh-fN!I52yCY@9$O++#&P`z)h<2^K!xC(#bb@AmM;CQRvB zQB-s}xBd3elGfqRJ>4U(8ZjA~-vn|#&(W|tj|s`rix z(4@g)j(M|}rdvJFv}zYK2C8*P8wCvwZk5FEt%dbO0+G1ad#o<)`2 ztvn?08A5`5LXGXFVT=5dpJHi=-pzY>9f%B1$RGO0>&$l+gxJ^4XQC1#+53tdcxjLI z_Ml5WHc8i_-A6M$G<3SBDSxNKDxh&lQ`Zl&AP(V%oboDl!tY!)9^hbYcG-zdWU{py z7mBSn{(eQC>>>o9=+n!MS+3&#X!N81=H7Exi2zejs8g%ATJzlF031M*#!aUue!vaf z*qQ-cYib7XsD_&I*9^yng08XB&)qH+fi_!NdR``NNC0&Wfo+y>3M>p`8UGt^yJ@im zR2IKIkSfJtl=?yP4s(a8*zLpJpHqLM50OiUwb~2_$q=VxyPxBheE!s@L5FA)7*>i} zH;nEovmUyz%PG%l)`h)JkmmJ70i|n%ATuI^DM@1iqF~Ub*@Kg>4n_<~{A5tx;2DE5 z?%?`zFKZ!0YCHwcUsU4xFbHIeo4qrdZ}S4<6Pqb(1K`-HIGg-7N2&80IE4Fu$GMrQ zNTcIz&>%Yi6eK?=U=rebJ6&M{a93u)Jt?G(l5_b__*x9VshL;r6XaAGO^G#lIvS$h znuF$3dB;-JT}WM6L3qJNW0AB9htSCuX=8&hKZ0AR8dK&Wkn*0?b@375w+9eYe4o8% zJk6_f`;kOK^a~t%Q0tGR6COmGpbi>K=$|eB%CYNPZR}hmg^2kcmiIdvh!C$L>bc`M z(D85hgRp!yLtC0t%tP_<&9N~ZqbJY_+rm=P@3e+EBGV5|rC40{Yn#c?XO{*}Z7iZE z?Wm!Y^yR1AO!|5GO3|H`YqL39cByYACeF7lfa`r1lb}|Kr_ceLspNWg%t?S0coL$k zkp|qO38|WU$PG?N|Bfya-2o&a==rQ!FO)m)ZCiJ}7r;6DOd7I9yxP&duZ=80WuW6f z7h4pN#?v|yY=Hm|0uGo5Sf&cikmCi*j_tMgNX~dxBAY)TZld%rhPZ1LH(=LMl$fP1 z<;eLY1T5Oj-v4IFmF?k;?S%w)f|zZ(C9je4Al}t;Y3|Q7Ejxz59&e&CD=(=pK4w@^ zDw1@i9(_!KB;5qLrupC_oPL`3jb=exIQp@v4gcxTzvTdCz)3g`qUe zK)YNTIf`TAO<-mUBFN3si~_a>zKCOW-Mi*efucbg3Y)ey$$dV*n7;%;TXV0Z^rX)g z%9vW)5g(xFK?OtcNZK{s3Gp=!~HkrXTMXd=dG#FZ~phmft2o zSIht1&E4otciR$THtUK&QqEqZ#d`U@MR}(3Adp(21A>cO(8h_7AGK#9k#}G22EJwK z2#E)2Dxc5nPdb#BJ9dYbrI-#~*6Z^Pj(_5Fp_hS}npD7ikF>=J4YZFAbaSmC1+M6E z>w5lZ3j~Gub72^^h3k>Y&c9iL6bO?#q8eDFCOCqo?rHNG9~9&*iw6-ovy=B@$|whFI?ZxnM2!!ZaEz3XONtv_H;BqaBO0Kujq{z z!lZS^@H*+Ina)QeD^U@R7AI^`NbJKd!Ha+RYx-++cF6Gs?#vr>vky!lp~M3maHaFK zV@8qU!UMRG#Fn}AFiExM-OFVt>j|We29=`DnR9>!w6D-S=jz5!K|8t>vp;CJeM)J~ zc=wgUCPHYh=-NZ_CGd*5BODGfOO*T_prxwm8tWmip*}R5KTvL(v@0pq!>l5n;sFU^N)B6Z4ofX%Cauy+6_@q-kZ=CpMlWSt%k2J2)y( zuB!Xv?5Di8%J-xMC^Fe9F1==jg32dD;@lU@5z!)PdoEutNrPqFeing$b17ep`9F?gjZ8VbUNNueU7&nY*fU|!}B^N zmtV-Zr^M1BSWSJ}uQm?29R0J%zL;9jWo@HJje#hCwDAs|@V2?WnSu?wQ;b1bJFk$M zc;sp?A<)DG@jmBJszeUAeMmWG9|{C+Z-qeF5iOgU)k6Y!=?!KkyXvb6e1bNGfk(zq z(P+=r95@FgHH~~>JN?vaeDr))q63VurgRzJ_!V20>y)ZrI{-R>pt(hxHs&4i0Ja!h z{^{#Om=o=wLIhw_$<<3cUhn31GGrvTK?;lua5UNzyliyb}VvE>T)CgdWpf~4I*#VlLce{h9cX2p$Hg_)3bQK znl2utn;6yKb~L3Tf9&`4%;EPWLSj3FwW~Zgp7wX!VtYVvT{9=tRqj((M~Dh5yFD-u3{GSuW2{84xi%5rg9emwIkKJtK*+Zh9NkzyOy7=P(D8)okD|!S>R}^{djGhMDwA4c_5C^cc z{t7a3a#N^IyX?H{`q$e_{Yh+Dp*=edrxZzAKBmd-bj~YIh-x#g-o4<0dL@ep4sc;~ z$uH7zL0r#{QxXSUFSS*$zlnu`#SojA{u{#HLH)9hk=$zq$c0vjvz%?&V%X3?DDU95XN-iJvMp_jxYrx zQ}rvC-fAMW4*9~J{t3nA2E_-bnEM&A!L#+fbdbf6yxC3a{J_b=H#+o0IO`i!=3wO< zSW|#;ZV_JH38+r}>(pk(xG#NGB!%U5^Z3{vBo2JW)s-iTnYhWwhUSt-fD%Oib31Tt zLM26dCejT#c&xJ)0YCfKcR3h7)_pPeCh=+K*{b=23nDQp{EcFCMZ35c%?z69XOFvU znQr0K8^WAOC#0-nNh1Co{{Cgg6!%q`?L3?cXs-R4V*yba%Pk;3kyY!xkHj~5A0chA z)cNVAsGUQTF~OQd{-ZM^dT1bDuo6ut6~%`=MEV2rs>U5`;^tiw`hxbm)kPM zTW`H_W#7Ys(Xjfop4YNA$=KROZtSZZha~qc@2R3L=efR;aNpSwWp>uUNgH?2xUbO* zYQrw$*x?fY2yR`Ifwi*@bi)Q`J`D zn&r7Y;DeFpF9GLp$N4cr z<1@G6#C|Y09YtN1;IFw{a&pvCIaR?)l|!HxR{s@`{9Vo5o`q=)PHbrl_c|9-MAfW7 zxkG$%PTl9mJCh%DeYNwd>UXl%+SU45J@nDP`DkJoqXkq^Sy-{kaV-h^;gQfr$USnJ z#8riG(^SJ~y17cKN#xGmSob_L(S=&ogdlF-G7yVbu$1l%oyr)blgYlFsRP_^Gv`a6 z)@aIhQ8iVZic{m=$QyMbKaPa2CmqMsVfizxHt~+*ZI$96O?Hd}N8QasW>cuvEX9A{ zlSj-|Cc03n#IKCj0r&Sc@47Zr_?zH}#>`U*OnvKwb+f%H?;(_IWo2OuOmD zYPiim=MBvsilvLcR(I3gGE}lSIpVqKxFG>B>s(cC1(CH1Wqzo{2)G3r9k0#;6r&z?F!KO>nZ^?I7s>jM7Otn{P9~iI~Zg} zFd#l4GUt0X&;F9uUndMng$3$hPN_lHZ9t2flfT<)#RNE1er!+J>2_BuBJB!LQC1=K zcbFVX|HPu)l)x|grmwTIabR*=`i2?{%SIj^n6KVZGx@i}Z zbr`w&H-zf=MP1<8Es<7(y3$RXE5H_%PHde8#k7N7&YfPNh=lcFKS4647nV$w=e^0GM*8{_7+PS?;yb6X zR_AylYukhB&$A|-7}Mg=K!e0c`sx(I9R)PYj@$nmYr!46+K3M9fzpv$%eXkzAb-95Z+kIzsBCZ!1DuBH_wd#?A z^j{zW(3$qJ?|97!i4o&7gT6<`?0V4c?tp@8FLfX0z8mF(JQ&T~H3!{QWYi zWy`7F99_lhJb1x9Ks)Nk6{L7%uZ}a^q!sS{-o(Q94gvVaNn$(V4uNVYR~pZD0z9tw zzBrGc<++oM-w93N@CQo0tnC*kKBBtNi(>Qs(A*jR#n6i7hz=uK-~@lzs^gt7fQ*T{ zj%Yn5z;H#QII}Q+PB<3lmksy!HgI;1_t7J>96zqY?6Jyy9i)F|gyJNp5=&cjKHd16 z0na7gKuBgcI$+a5Y0vg>Dbt;GG#2dRvdhGK3nwf5%M`gfbA;W11d)XG^u+;ep-yTH^G`Eo z*|r21iUG*4fg_2J&lM*zdXN{;=ugZ8uh&JHmcz01TX$K^_hn=2W)nJ+C2(weUl!Q9 zVrR3{$(MBNo?I)#`U><;1YqE%K|CRnPv#G3$8B-Hm&jmHVKEXxHA5FpeAhg|9CH5W ztkKtn^H{rW$@AN)FFcOVV>O1P4$J)Z_IzrOW}(Ol%FzW zm2r8(VbSfQBL=Yq-HX&~KpKwcnfeMHs#P#;gr0CHCP@O5n>j*ivp0N(<_6z5n#E83 z4@Y#UMp*p3C0lk{{1kYC_b44D}uhli##@F@TBop59~9xQHRso%zg1l`!Dn&o3o!Z)CUFm(q^1u^>AxOO#g8)5IpYJ@O{ zvHR)1MZ)hscXpmdEPDi-J;>j2?X;IG<<8AeW{gI;MgZ8O)1*3qBSs8mBfq~A^6psV zhUD!rua+R{I|1?s((){j)Esb=pozB@UGbfIog(xJ7vCE+QuhrOl&_Gk6;X~|#mljz z06MqV#%H`j^{5t({pj+4HpW>meO)n`1e6E0^Ru!I8=_lF6y}NyD^TIqV37<-0AKIu z+RYxCd@GX4NV7gPf}})wC6HIefH;f?HdI!G6r8@}RGxrq$K;Pzc+XxuWmGL4QBhGw z*pBE6WAhF63lyR{Pe(mHD}h+d-qKRSI(37cH_Ldb>Y$51f1`Yt5gtoCyE?$od24)) zlMCfY^L!S6>I(Qm;qqKzSh2p5&upE`owlt1T3ZILo>Ejz)Ky|trMIT<6t5AW+mDtp zzSg$5=gnbxm#aAi3huyV(=}Hkp~zGWG_>n{m@Ha60A^{ZWo3!OE~3I>$5BqU{#MQG z)^E)Sve>WabLZ`BWJ5D+*5^9E{%jl9_711BiRYA0<>hDpom_C>xzw@j+;)Th&Rq6W z$VlPU!B(;wtuK@f-ocX<{;C5I~ZlJZ1|O>vY2tP^=|7pEPXBFkxq7;JD^o&@rf zi|Vk}zJ(Tz8X4}()3wFAi~a@@hs6~M)zO{zuThMn$ye}(Sz+_hTJ{}R(8$@k)FFO; z8NFZ^#41q@CWhGu7usaQrrObJ_y5qz6PdI(xge{DZ3nmSmrXP?I1^mD-{Gn8PRgt;(IkdFlX3);_diRRXBU;T9BcoGi@5>3s zMfd%xI3E*OkGH0<;W@5}VMtS^nL* z?L&1so6P#T7RuGdXg&eYkaA{pm>?51n2f{7D4(6N<7b5&IfWp~yxMggDf@DF5Fx#i z%xVUWouA#SQZc0b(s=pw&wf^-s7)iY=yQDQlbI7?=%c*aK3_k(E8{QC**K09L^SU} zj;m5#cx&{G`{$(#iP4n$8ftqtF4@QVH*YO1o=f}Vxnx!s2mPrOOa?ov2K2vsj-!Jv zzyXvy-4@qLuP1EoUpjAd^!?f%cLy?c1^)r%AnE<3Ti#2PGUCoxVgD!n0?GY>=L??x z(tq%hIw$uRX0h(zMw?!iuZ@=EAm z5;NcZ#TLOM=b}58l?=^C`Wr<`?o*`8zb~T0;KH5Mjm=%dP(zMwB81`f?x)r5Uxl)A zk6+s4bi0e6;x)vyb!#&HDK9Z_@Dev|?CndZySLnM@*gn_1+qQGX;SzL*-=Yo>j#X% zxmVB?a*8WQZA$c9Dp@=QLpv9M3@S7yKSH?%|4Ba#~ zrRnZ@KESW;wMCq}UR;$hrn=Q%{8^*0-mTM8mL}C|ve8^V<5!Pl5m5~@Q_2aSB)%7G z4vvqUmE#vbDLfteW&F%jGKiT`gjx>QVzlMxN`P>ZZGnD~N;6i>ke_+UQ=Q04K~5fs z8f2r`+N{^!#uDkox`Rb(g#R-WlvJspF#lVzU==df!5jBT_uRc|L2g-3-OasETqq~Q!|dH3H*1bOV(b=R zJ}@ByTsMl!=c?z{7h_rE2F$i|F3fwoLz5<@X@_b}UeAQ3%3?pRFX{}+=i!^pNClKm zU*4w(vL*11>juJ=2p0vbtgRBKh3@x+QMWtx;Klhbt8R&dG^-~vN1iMiK%q2*7v08Au_(b zeb2LKmTuw5)J|ex|KNmQ0xRLq1z!$CJx)V=QDcSU_bQ_FiT>~I1r=QowVy((4F|0? zO|kEV=(wh+>Z#af5vYZz6o1%F>C%;aVvco?jL`g7O8Nvucu||*iAR>r@$wRdOduHv z6~4{3VmhK&wkHkV1%TYRzzg(V(U_BjY@o5?NbV_AAv94C#V@}*7E-rN0zm48Qr_?v z#4$S|EQ@!PAdn4+v0W&MITbXA1+hF5Gg#+!^^bicSUSW=q3;3wr^hG!FaE`YND!Xo zOqg2`AsHzHQtsude8MV?ZSg(kEH4>boyr9&b?jUw1N>+)zrP4+*M{{O9tC!f=OH(L z;4nBnnLNOnaHe=1R5y$||82mE2^oeI4RDRo`iNU5<>tT}s08Bc6kg53h(zy5o^$F8 z&n~=u&zyZMp+g7!ulGAUs1&LexIIdH34v>XVm{b_0{@1s zQU_*J;*or{>kjH%h(iZlfI2@4)GJ+E2)wuBl#N#ys3p$s=Wlid(-u6d%Cf?sReG5M za_7+EaEk#kJLx~Uxg6Os_j7v}e&fHwqeTYBFK2K%L?fvID5v_bUTqDT={xQ#vq$FS zZ^}w$e%h3e^BCBDZ7x$FWaBOBRYKX<*+_Y(oBx;O)PxkJR6Sg@sr&-Md9Q^C4G%hG<3zk~?aRF4)wTAN zt?m7uhfMrX_OE^caZbJPmp7myxGfGMZp0e{rK^@V=`cu^V{*9_J*jFv5fu1qk{Vfj z59uPO?G+Pp?-yj#?V>ZDyadD@F50W(JCx8v3wQWSkYn8D#_1^yI7y5L!fb%%H#b+t z^~Yd~G8n;>`RTasw9zXp=4%Bmx1n?H#Ri#Jr@{IN>rf2ZMxl;2P4-dLOXYYogyI+~ zh~ZrGedH*_=8=gaNaB+dVBt3`wV&hA38`(_VYtN_jWVQKN{)*(%@SwIFdHl>*03;p zRzqE-`xjSPt$apqtZ4WU$q%!x{Cxka2gAcCzm9swsNl{DoE>u(s2*5>=ncs%68dMj z1pa4m3lYaQ6Qp@9g=(sBaBxn^MBqQ)+X){pgaW2-zYW1R z%BvLr<2l3K&iE@vT$fORydh7lR2b~vNgnEyMvsq$9mr`i?&v=MV9*`%)}oEr(mZ~b zJ5L?tFA7YU@zB{r7vn&kt(!GT)owt6FA%yvuTtZ zKfx%uBb=^Ev9Hrw9qSUwL5zxK+{<_9TEeOW(qg9W*K)WUMRKT%e@W=Yl8qsrH`qqd zj7W2Bu$N*{wA4U_aC|eW>GL>A)TDc&5Sc`yT`>Y(w(MO%oh!HOc&@pMfs%@&YsljS zp9d&P9(M3Y+nS;-aJle8p)ke04j@pBpW)Y;kVW;!5@$iqGVZL+Hz^92F)Zp^`ZYd! zkGy4L99Ij9!^@1y@-AbB$U%8$Z44J8GC?4%P?5<2R+J*vT`O_+`fM_m^rAA1RQ^?O ztD<8vD%rA3BYbnPy4##(=uh|{51P>epriASXVIOG+6uWA!^RCZs%{duG%Wcj?hab2 zL*Kp9;n{L9V?IrfNtg&7A9D2$=Zp)}cLR;xke-EL*65Bz7=H_q5!OUj z7-j|&ojEF!NKGN40|EwrN&S*qwdD7~+;0nWBrj50@=&OuqF*pNOP9R3c*^RXZ}^ef zlcw%~k38-J^^Ye1D#dm@XjL>R}-aS`9?iq?%Ky?jATsdM0t^zV%_?nZQhD04y0{9 zT{xV%O7AR3b|b?t@meCbk|GM~QzZ244)-FZ-O3f?1lTWmYk^})MFC%@mHaO6C->(W zY>yzMB=I6~3%ZJ_;jD2RF;tcm)mf~VACTgyuv^T@$#mwyB><+GHqIz;+z|$k(wfob zsO5Yo^MBOc7tq4RpFl&Dgxurl0)-Xv>OyD_af~1(+`#fFNpK)u;*QodM&809fWD}Qf_)$P|?Pa;Sv_*XqG4#0%tFfc~uc6T15-R>tj%PS+Y17Wl-{k zzSy(8Yy2u&$4t?v4(Vs*3F=DBR$|*dq4_ALos)WAg69ki-44&jAUS|4O6r6u&Rb5x%+C ztIc;B#7O;mvsN%6MVS#~m?EZf-PmT;!B;X5N&oKaprY}-2ACthJ)kR>Qv zG@;Y+=OV=N=9mets-)!T*LmQJseFHI`qX-@b{{w8opbzq@lU5o`DgOE$D#?sXXF2r z-2dMqwyjDG6UI@dwjGK@;DdtkZ3>wCE_61dh_hE@m#7^d`ZI%U@NT5)d0WFYKo@cS z8y_;}-j!c4Dtj_H+6o!=-h;fkYjOhxoMHltO~7 ze0j8~tGfD^znAbxqnV);O@YP4k`CGzHrTEh(u7IUP2&c&J~K%sH0cdQffb zq}1Ldm+UB*(y06;;lv=KEzM3POW)6!V!39`h(@OG4c=P^%MH(#PY+OdQ%~TK*ReF!B6R1uUHX$Jw9v z^5w5^wG6Cvt(H()>4sKo6$&sgcbk|7?WnjJ!cxj@Jeu!!TWbpj9$Wct7vQ_xZ14GA z)01^ayL{5u^4TQ4{4_>=z6=Nq?!Ntj=ab4Sy#FEkvPJWn@!EM?m&ymS3<8uz;jXpT z@I|EMWK*kVHC6&;NmP3TL1lLm!!KYlO~r2`%EfD1-rczqI`J}dR?O-0_sexBeuu`p zSfn?V{e2jcMK0D!{~A}J)piBHk9DcaBxG;Go19y|kbS+*<0jBZXM7Qtj?GNO!on}r z_PQ&Z(9i+VIJ?WB(ddw+K!gY*@_=7r`^CcS;)DssfdL4HdfUU!}su>Z=1$~D` zn&zgLlpAYetm@3&f7UH(&jJuib~T5cQwO)p^Qvd(1zSsaN>J5&eMX}tOKa)}C;kT| z$ZE~L|I18eWpJ z?n@8IwdKxCul)Hpa=&<#r>8P$@5kirHK8X79I(1K3#)T@@T_G;5Cy~DBBtt=PrU1H zhc5X(Y~K4T{%TrJNl}4i==}aO(Mb24x#m71gH`$dKLxtd{=L39MeUr?LS@^$Xs;=O zB40hSr{kbs^&k6O+&k5)N@d9I`YY;8`W5{j2dRu#YGmdE-=1RLYj zFbUOiG`u$$$FL9B^aw4pb z&)B|q=zZgyPjPZI)iE^NQDm6(PScJXDCUx2K`Oa&;U%q_-WgRY%GPwl?_EkZ>mKgo zOsG-G@Ua-$m2(+O@iE((t-DV`Tjy#eW1EWt#la;??EcN1lnCZ!U+25r-0_sOR@n!5 z?g#|?u1?7ac7=+BxgKTnxtg$2k>uHk8~_>|G;wYl?v>QvxTx8K$=HUT z@>_@yxPPX6{0UZh{JX=85S`*~cP|k!wdVyq2^@n|W{D<&32{uA&@fu11g2vzOPd}d zkr>fdvx7|Q%dRqmB)S|s3>qMPwqvgNk}YIP_=NN*S6t@ZLIy+)^_U%w_HHD54`HMz zBQmA=%4ogJcVe%D5XF7 zIg1?3&JA_tStJ1qf#k19IHY)p8#+54wpI7aSCv+CUu@QXD+*+~}Rx6aA;HdjbesSpaI zB#h0*L4}~n_gqeRCk>!Wk|Yal>h0PvI=jv=VtYk~|9Hpn8c+Lz1Gp04ir`*jaEMVx zD#a`sd=6K0%rUV~OAdA8j`r+Fy*{wX3iLaD!|+1TkU@_4Mz4iV8!cQ&(YZXH3E2jy zU8uI-h9gyT(%Nf#e1FP0dm)*O5^1?r1oENxTXtPTg0Gh-9?5LeKB?V&TF<{QRl+60 z7MQGYkE|!4_^t}TKe3UnW}C1c5w!lvL)#=9-4S?;2)g%~&}o_~QatL;QN5zSWF>jB zo>JkI>Gq|&`F0E}z^87i)?rpg zx#y+Q$ZAxr&Z=K)vh}l>ksbQRdmG8e3pZnXQm66NaO!xjDKX5yFY+>uy-3Z2lu8Dr z2i9;lRsF-$Q_oXfZz1UoSlTfx&QDWD*J)v6QWA;}6_J+R<*GAjqh& z)1%X`Dca>r_)uF8V)xd|#!^t!Yoo{IUjp1KKfM5pEoTdB@$aNr?)B-@P2f8RNxFHB zO<9$n^_q8=y6~a5HewEKS(q0_a~JIT+PA!T%=(-INc1T88Vx=LpUaPTN%tUWXWeDr zo}BaHue8l4;8MuL<^}xSGg)L;Z+c@>Y(vm#D(dMHeJS77TgGzb#p-?hK2u9;31c@` zKJPq=-vc?%`2Aw{ULMIYjrqZxY+Mt&bHUPiRa&>;OGzbZuZF2B7BdVtXfE5l;hQfo zzYaq{%Lmv_US4ne?;bpP{Od8x?KH<|?Lpy}q=1f`JP=rosQe~$7{7Cat&vfs!^Xtr*!U>>lDN*&4F(x%Jr=J9nqTa zc4^bn#Dt8S_xOKHf9cj{)wX{JRBGoXouD+N7NT42pFXV>C*kz} z&$Mlf!3-TvmdsprS^fFPw+=%@vU`T0fkHMr)8G?!e+?^UuLbW;*-TMJQJ{G^bHrjh z2HL0a-mGo>@9p6^K(lvZlfB2>jUl1Eo&wcBoDiMJWI=PwtU~M7pHUQz_c4#eH;KEvCxjeqW|G7 zM-ni#$9Zm{vNBx7Bde4()hTV5QD}XsiFyM?VY(tlC2D#K{)Jb=ixK>xUa8=dqI_uH zGru{^oNI4KN-G=2HZFpnc8eIf>nxRTN13)8=_-OhcDpFUp4g)N6ZqECD8@-9u6j7L z{2#CWt>~%z-XA>Qs4`F+MV<|-l&giWBz{TdmI^0I#+tdv^m_&e>lCxSnB~!|mgEo) zK3lV;tVoi&sA6qId{<3{tboR4E8lml+_5_fl(!3pjjWbhdX7DABV5(q&9L4w2J?(XhxK|*kcV8Jc81x=7` zp67k{+uh&(1>IGB>eRjGR!w)^DqAZ58!YTrHJfe#XkkGyKkWApth1<>5440@kl(?1iwudAR}K#{L1xC}JvfsEa4pNhPw$BL2Oel}kU*hMiVqY=$@{*9cIt zCPvXSVwzO2kQ&i9LrD|ddE|9L&($&aBf?|~(NJP=x7)vUsvIs*a(-xexy&!HEmkNq zLuAL1XP}*4ZRuOsUB8uuV4)5F-1uDJL{y>CkZ#zdhEnY-Co*LlOwsiaHo&0yynEIY zlQoA4C{V2m$N2Y=DXN-fL2{!|;sgz32hPd8dJIrWh1MCeVUm+>Jr(rj16>#qGe@GR zV@$rJ+_x$%3IS-X0t!@!O|%RNFjf*D^iSU;_j}DuHSv1`6HSr~Wfg`cPsbaPz z5ot`QN))@FtzYma+&c}Hf;btCDm2v6H8k(3KiDK^=^`4Rz*G&;{pJB0pb((Bs@~m%>bNZVqPQ^s$JpddPSK~F`0yaro z5fnYaM9~0H>XK!@i(7-0^@6KPzPeV8V;D^utZC13TBA|4p;6kbyEV>3q9zq(N^ql} z=ub&wv^C=UbQ=DZsjS@GdA3L~XivXdnmK4GFI`zdhjcCCD~YwWgDYIBBj#NjmjRh# z!t@zEySqDw!!s+RVgX}-m57Sa2WTV;MdQ?~IRq$*E|Eo!ZHSLgk)}{`w3PS6q zc1n@bx9l(j6T)z*>d76&p*^9koAa99-_gQO2`G@$#q)ZM^8~V$k^ob+jUk(~6AH?i z7L|vGAF+#-6cJOs@$G&({aPxGvI3|=?K+Z@s!0!ybjeq=_w!W#PkfIz+!WKI#Q#ow zdNNKLT4=AMqEs4PU^-Fu!w7iQRGCESNP|=IGQ6cliho^9;q?9Ov5MxdV!{{GiOR|X z5Hw2Vsp0P;Vao)8e@-ZDD{2*fTpn9uPIA7{gC|1ncKANIC^Oc4h3I#^tqjo&?5s5g z6_eetDuiV#)YC3wyURL%bo`4rK_sr>yYkV#)uQ7lQKFkmlMw+&q4rp#8-xpA*9!O3 zQU-_BQu96n-PnLcDX!b_sL(p35-Y)quun7!l-DKvn9ku-ZTBnqSKH6X(=a9Do#006 zIS!`unOu32S6|o6GVZ&gAC0=?eFZMXc*F_Y2h3pYVob_F9k^sCu5c{W#qHS8erhbY+u*3PiUoz+Tl@t7D*f&QgD}J zSm>|16D`n?l$MG$5f>^JOxt^H^h+U6IyJI`um1kPLhfkv{M^5Z2knaW9U@#knO@Sv zOk4>GUcz0syyV6QMWValO>Fg*Jcd_FMD}rmlh@W*aor8c@{C?LbSDNHzPku;&G^4>72q7*@zvW&33Y;{lJ$*Y_)8H zyPl!Q=Z4Q0(D2_!42`3G7cfKz(%|_y7-!BS&H}=mNtL(O=xLP8VM8h8!h~8hp5Zg0 z3B?ynz9zMPF_zUGpkLFdA$Y-w_9i5~Et_K#qkDLS#3Z?ZeVyS-W_{#>?ZM8}@=UfW zeXd#}+^PJ?EeZiiZ!m{0d>&%r!&$g_!Jvg$Ut4CCh8z@@@%aztItcm$--O60G9E;k z5?k_ek~bS>U}|KF>o~M~#x541=W`k2C3>#n$suimN=x1!lEH3{Wz74jT?sXD&#ZgQ zB3?cPN288Fgcp@XDOW7@6bs#M)SUP?Qw#)b!Vwf#&@B%(V}(JP>qbigJ4)ntXAm#^5j_y*+zDo_^}3%{T-F6JH9WbEIep&#p~SfS zkP77sc{9(Xc_lsIhRV9Pm>Fncag_ky(jsbWUa>oZ%QZKPGAhDcUucQ_lM74=zZ#Jy z(-akEHH~Uo=PPW02$vpYNPIQx>fsv=yOlg3E!|*|)*7DXj^pvjX+p@ciZ|b#pz4)( zUitAxc%n<@MB=ybM3Od|uNJOm zy84CNOVr`t6TjuVTDY-LE&TI-&}H3RvtD#Zi2elZoK5DLWg(p2TOFWc!o}bnq}nMQ zHGf^6V#C!wTsb<;LO49t87-pr1w{+{Q^eP0!93r&h*~ zyNY3d=S%h!(n)qDhxMFXz5f>xRbhr{$ofIERNYMH2SzY1J;wFA!3t7(Zz980x%zv( zHNvV82W0HeTp+Q!b=bp-^Ua%M@V6)Zlg)9koe7854TR#I9QYb}B(}l02P6Ilm%*a* zOOJB#r({*ca+(UKpS9TtF5mA;;bK%j*unKK)d%elFLO?Wvt0Ow>J-^CFkHoY&K^2T z73}wVNp6ow;w{w+?M6w7S>o9@=Gy=LdbhA{!a+HgKmFG40esE-;T4Z1q+;lA$e$Ep z3q9^?9od2}CT*Yg8Qse25f8KZM~+%N7you+^32sK9tEjk;_Df=EL?JWt3Rk^wX5gi ze_Y_u)#^xn6AIy_s-ct+YYW4W!(On^)TLEk(-X?rnL1qU6s7;Z zP37yL^~M4FTYJ4I`8NKuYZEID!^88C&!zNmnm0&!${VH zPYrT|+BZm(4X6H**1UwVy%?BKfa0Glp8vj{GEr@GMLhWw-`_sD4Xa{TDJP)XWWq>8o1@M5@l0q(M^Qm;S%@2BBvQll?~D^Jmri^A&H+Y&r~ z4!AA)QsM_ksV@R?A_j#iX68N(zYCJc_(tnNzq3EO!r#Xkc=fp=?zI2P8>}sYpdUFD z*qC{tyRpL}#%0`rjM6auCVPOr{+zb8_2K(Ok{y`2;uzq9N>Y=Jvue~pZB4h7U*^F4 zaT#7BEgtOx%^A5&56xO%8f#9C75bMp>E!AZb?D2;v0OqCcjLXP%j$a1 zP^?Ml79nLq-XV`ed2X>yx|%PmNRr`UNhfqVkAfZX?NZ^760h+&Rphug>VN9^zcwlD|_Xe$r#TM%+%$I?W|ruBqK!<tj#?V$ZwWtPMmz=j*3Kx^ z(2!Bdly6sj?;*3bj`hNP5WsNjefXYys-rUN>U$q#gUi6%tR<=W|KiRsBA#R)3q4aC{WmE%B#Dt92B#es?l@3?EgwTWH0|pCF>F551AOnG>I z(kUq^qAv5oHl&wEr;swK9k%y6wj2@Y#rs}2A17k`y`@cS-j5?!Xrhv;oa;yTce;{g{p#`xo+p00_G=8wBuOkobDY{AtGp$APl znyD7m>b0XsvDi7SKx0NBlNsQF&0IjGp)|>e#@gunvWa%gqVzZnraw=C?;N)ZtVY?*Va~^x@Pj$W8{fP$EC4z8$;OpDX9PfNw=kMMrg|8T;)6 zz<+Gf&>e`IBv9kwdO>?Uq1K7rs`sfhBlNh)iTOU{>HXDNS9;Q7MS9Y|=TE$g>n_Fp zVgjYFyCTo%9qBJu6EZVGPAp|Rv^(`aDigAL|8^lw!V%;p(<3HURi=o`MK>RzDf&;< zM}m(fL|x&p`xj4W&y5CSXBo8KxH)ZtwP8h?Fj*`|PpYd_$(O1`uMh`IR!qfqG%T2S zZ5(yvw@UIUhZm08O-u**4- zm*yOOEE2iOG8|)yGbXQ0A*(aJ*v*kVRy&uWpJ@`eXtt!2f>L0pw9$>bS==F7$P5@^ zI7QR=)CUxEIOq~5E7#jHi$1-dIrU;Q4u%X)q|$woGxtY^OSDEnwvHQTRQS;4`jhs0 z(sV4r^J{m#Yw~)+U8;c3A_D%=QVpYZ19lZ})5oI1AU#ZF$DV0;gE zS_8smmCNkCb$cp0WOx?bplVV6kw@=`H#4zS{vC&x^ZxCVhNu=!2{}CS9}HZ*;HJm| zys5@gsRY?=e$4TH&E&xtd6EkHJ5(7G&&L^^?-rWJX>6H3vS1066nerlXJ!aW@L(W@ zH2YKmX^x7Rme|2Z(570&8~#cfq)--_`BL2_rA36PJ(V(@>&fiU=+!%I5A@OE%9#FH zV`f9HGCh9s`#j|ra4v;;zZ8FtL5yD&G2?~3jOHGQZ9Cy-6VbO}B4hSeNFR8L9!@RG z?)D^>yIz8cqGqVy&@E{WAGi{P*+OqRmPmhzXZwub=i*m>P>Mv{G zp??P}xU=QN>j1l|`#+oxO!*#}@Ws>rX=;FD>ycZVkn@tlP;;7bH9-g}#T+c@Zf9 zoEb@LFI<1QnOKS*m^Utm;F7JpE+LCwXgvv`A3uM{cPgTql%w@8G*AqJ1%c=B@B>N) z5%IZWqN&e`rqpU^sTjGz9da@z`?#gTGepVgOM)};3<_SOh5wS4R`{>2iF}zd_kZBm zFWb~wh6Vl(BuOH?QgIb5=fHcyyG<$ElT{7xaxb$CmGTQSQT>ZIoklU*x8pyW7WzA! z*#Pc<9sZYLo>MQtHOlc_yD4QjL z4=b4@wna1RDhN}ng&W?5$ONj$z4QagtsvFQ%(dKPuB;hNzd1k#T%=1Csa|wZ1nYoU zkwjiD!7Rxd5=URL9!M}s-=q>`TnVg$W$=%wyI!(e4I{oIfFi^y{lfmLno`s%McC9J zyO*oYzkrn*lg$s$#|LKChSLsZ)`N?4VHSPKIR}y1JSHO* zI}AmI)vv<0s71^jrlP#!tLM<#xvFfrWF zyuXmRK16LxMGagidG#|F77JSSh#dH@;HYTTa$H1*vsie@Vc&FBBG(fk`6?Y(_ z8-XqaKlx-csbR=l9H9clC*@fbl}No+sRAzlj~W=Ir&EcnYc2U^#7vH7^BSb>5ZL>&@fRBj$_1xKw z@JzzFwlG68g`E6ue6eFtf+dEt*gROB?kG)Jj$e+so2!mabjy>9F0XhSO;lZo$od=};K7PikuK|AWIV&xt$W4> zKEd2qv!9iGPxwQttw%#Ta%%90sXjhC9oOh`3JS)$%q{O+2KmT@&4@l5cY;Q{GA301HE_wq_F@SQ#Y20A21W# zQn?GB)X-?IFR2Q+uBy-D3dnp8Qt(co=Xvm6#Vby{`+VtJVEAd#--dsBMPxk>ALf&!={8v8S2<}A1fbJ!FH3aIxdMz=HyVo{R@Q`G<)64Qkem9iPq*I0y+=w|v zc7mgdsWDIJI2O4VCOO~U7A~GvW6M0KY&!7~G)|t~k8ExNgXBC11;w*KuuR7qczszd zx_z|60Vbo=w+Vagud}2V<$RbW#foM^aB>+103zssX&T-Mlp^O;30&TDbnpq5<_`Xj zDFIkKy&h&6s>WF6_2!Mm6MWKaAIOIO<;7HGF=wq=g2+|paT6P;OJ9IL?G zBfEv^Os9^Tj+y&>?)m0|Sx|2Zd29aLw13Y;Purxfv}aVYdPeA0KK`|DXL-~Rt>f<> zivnw(=Iqv}!~}(#j+bF(dHZg%JV`%2%?B?FZrutoN^(udF6j%mkoZm6Fjxp$I~9W= z$35A?FTj;VwncOKw_#VvGU0s5Z^G|wqGrudPJ>#90x;WG9Wx770jxob&FV=C$HbkM*4S|pnr}B3x zjU>O`W7JfLJc7fg<-TkIf(AHRcE3=rX67G!E89PK)&8pst0k z|7PcMyb_(a6v=YmMQJ?Yth;C6#7SjPX9900s#zenJ!I{%u9;fgC!C<{J6-reC^r1v zQaBxUqaA%8OC-`}JSW2HP%5lMs^~PLT|eQ|kMiA-={5bQMTD zbYvD@zAJcsQs81LC*5@o%K7c=%a#1n6+=Jln@D&8i#}3EJU_N#O0Kem@?cC+SF_Et z7rO@x|6BrS9#ogS?i6+;+fmIDx$XHi@vUKXin?O8^y}|D@$sP1sc3X8z}i5T9sZfx zcmp!=viBzz2i`^v~MaK-3&3zIJUNomI+ltNVLlm%;a~)GLlrek%id=L=98+@fD+{iOt$9ZPLo0H@hC;o_rz;;|tRb!i^ny}k{hKEa7H zrfefsnOXZu)+(6G>_HvJZ&4t%vC=6&>Or6Z-8(T^1KW!*e29kq09iOj7w>6Om= zf_=S?<=5XnUfBKcPTlK?1*}Szk-^4TnWa6w_LCKe78Ws}{mkg9Ma}8Jlko-jQFSnz zatV;Xr3Z`R0zE6{q>)s{jyFvr81&s(5#No^hYnz}o|!SE+2QH0$_AJ-R-e!*Z-!oU zW<~_Axn0EcsQu?>0PQ*`$78uozB5J@B#MoVJ&@78QxDTtw?iA8H$q~$z+2?q9`d07 zg@o@jtwbla2&xegohgS?YZmwJD{~?Mbi?apE31A9IEmslH9nql_u)AaLEGQDFeSPp z>AFkD597fmuHb?%fC|4}gHjyyf7?lKw*Y(afYAd6_=`hrT>C_zL7kMlZa8TF^g|#J za4SmV+duCENox@+2(R7}wEe8VQEKWRcD?^}XKAT6mN0FTovCyTxJt+^o$=HDr$teB zMyZERXi2M}39`=^sscyjbYAOAVcZ>rjv{oSX6Dyhk*(hc0{)OTE}4V;u7qnAkbn_x zil3+K{r&BZ#{Qm4rO>bZ^_}8KpQ&XOq=9r(XIoR<6JD0_=BeE$)ok#DnuFs8$wZ~SKx5`1oor&Y-Y={9>BE!h4)ZBw&F!aZqG$Z!-0 zAl$3lAbBTFqn*7h#AhVeqJaOG@BcB2{ZG10xXvv%OQ0t;_A~Ph0P*=Dtet1-Y>~$9 zMgOAh_a`XC>>arZ0Es$3zHazD<<;svV5bFz6cF?gW7hYu>dkN2s+l~fpb{OG`=6`>Q%6t8O=T3WVa0b7A!dm0eV zhcJVEi%Wi>CO*@u_rdg zGlv(NJtL33pn#rQdOREz98M=$T;r>v~g0OTkD*?%Lp4YrAQ z5!ls4KgWdpXLAv&4Xwe&)%6Gd)Swvne44=(Dae*uQ%P=KdIwTqG$C+BmsJ( zeY9om@p(Fb)*T@2XWgyuZ(}^`QC{ZLbG2tlN(2h>U;qM#2ks+e7Xq^UCzQgCxzgj~ z&!2P1t_Ys%rrrM=2t5k{ek{*7RNgZWRzD*Ym@4Ny$0x$QtMwSybByrI5&s`()32Xv z2~24I|Kp{r`SJ&K8^uv`&e&xA$I@}3=JUBB>Dg&fSHsyM>7^>&ZC5_LTv(?XN*>|V z{E#$~>5FXdi360+3y~Ewk>loc7Y;lHPUocb3-=F_%M}NCio=EegN^O|e%tE;`O8+- z&2^T1G4@%iqJLB1_)?-vqc4ctlE%|w8U8%j$Q`QCyWK_Z_(gTxZxu5ubR{K)=VVU{(|1x1Lu@C7oVxgCmT-4 z#LGN^R(Z$SjfKdSbSLPy89jd1QSxnA!*=jG21Yn{U|O8@tplc^f@rJGbz9xl`+>jV zgDk!qu0>65{IIT8#t>^&F>zw+;%kj?=19Vb0o4dgfjGSvauHsxQ-DV~c-=bKyEu}9 z+~zrTsy=oWA_?q5J>Hhh8_kYxsE+V&yzD%_qAoQGV%;|FY%uxlw>4RP_JKmod+zn1 z-Lui;7J9B=k;^EGstbhfL-qLW?4+2j(F>^X!ndrOjzZ-%qDzY_8+%|0 z{7J0RHmuNdxr)EXfyY0|;DhB6fLS)b#BJwps199TFF5Kghtx z!fx$qI0!|iXBXzGUT61N-7h0%9!o}EP%+v;JH~^r7mk8ujfWgq#8g{Y2zh}uI?%4v z>He5kydPRmEK`-@N7TPwY=z5Y?)+buc>FK;@Q=Ghd3E78kvBHqXTd)rPkhFNYx3hx z{f2L6_-!d(Rn!Q9G(4&sw^_k;M1Z)hFEk=tg4|K;BkE$u27z%MwMMN}C>jl-xP8Je zI>)4!a^SwtcJQNTu#BIETwCI3b)#S6{r0+}otS1^_l|F`uM`wkam+|ENLz0lf_d%a zy*Cv;Bc+(e49U}_MtT{VIkD*)@6cTR+)tT#z+Vw1&k}S(dKCZ*yHcM0&4@%lCbP@< zt^SqgYR*hIrbu5g-ru{%u|!KgJn{uD1-6~{MK=SBwyJD99xSInrLWHzZujlU%}~_? zUjMcoio)lG6Z`gqB3`xCzmzK1MLe@wRoipS8Dt8XsRV6n90Z; zM1GVRG1Z-jcFmi-xk`s`4aKZ{r&tzc(O6Y^OSb09#`{ftO)L1~^FL_lUw4gLzDJa% zO?>d(%GrNI-J5dAo%D+iIET^CVO-|J8OcSvmo$F;khB<8|60Gn3-hnoR;10t%;oo# zuv@7X&M_&y5eEB8*}gNfu92_;z*%Wpc1<8tPgAXwyxpFo-Na_G}%R+slHzW&C_wz*? z`O(iinHN+A`u{mM5TmAbY37xwG5xdQXot$z-`(9QWNi2)>pam~?(+0(YuYa-XxLvG zh}HcG8uB?#5j?XmR=|0?!Bd1Oj5z-jDh@>9c7;?dF#7~LHT@_#G^g7Yb8b0)`t_^p VW$6w(nNWLP{16E+S0cf||1W*yrXTs@q*{mKy+6BrYB^Vs8Sb3JkB1dohRnFL1H^g54Z1Iv1d?47-qukPm(% z4lV|}c!UCq0&RZ&&yA-*pK1?4Pe5NnUoxwp51?}z?*{FN=Oyn7YI2$}&;a@mk*}Ws zmg5nZ9?`Fjx3kZhm zuu)QV7%rI@Cg7C2=}A4}>1CpZ#tHdVKXh1N=z7lD-t)TV5Gn1r7lKz~+bAXV0hGi^DzOp{M6h zMba(J9pEbG(r?hO2vGOM0RVjF%sLzrO!tiY*#Rg3Bp(8R>vsVsgZHjh-z~uEv%se# zVBqx&HQRyto_}+uh2?Vc-T8kCwNbn9tMoTMGM!-v9!DER0R;|d;i=SiW@jb~f_Vm* z`~lfh(PwA2l;CYCX-~&KZ8`R4$wFY1>>@QANvTuZd^=w_RypS1MwC$mK626sj#!QH zdqELu!)UH%fA=Bxxjc6QJ8-|-4BQLJ-FK$D2l{9xinlK<(hPqqk4AY8#=fDNFl#)! z&!#FIt7Q51kVPKgvf%N5hql;Az%+uHf09eA=5l>pg&q*8 zFXH~5cAY`(!h;dN((KNWG1)o(BABkE0d|w{Z_UykkMa6s+5|C(b~=Tk0{wN;JaX(U zHvUJWe-#ePeoWZ1&R(NRS!C3l91yDw{g<+T`OSaTnD&tQ-K;!mOW51d}GR$e` z#Q$5v|LJGxc^N$05QO>sABLW7m;@60M$B_Y;rurM{8!^i;M^f#g7ZHvul>%EfOHhOcunZlm1^leCc zW+0I(YR5(BWn*xwT6+O%S6JF#7ndnNSeCYmuQW4Ck~ZOJT=8+mGQqstTTHtFK!Set z!M^&0q8x^NR4WJ6350J##B_aX+2X*dESC{`+tCI=ezUvLwi zpCOg`etB#Bzae&&{!~9}xF;M8DXRUSkaZ6fh@7Iw@gF{O>}!Z5{Ev8>nFP)Pg{hwl zPSV?^%!|eF1WNhp!}^OSr#<6YJ$LnkV%@5}SkC)52%&?|xAq+oAGQ3VE^;a6v8`fn zL9WZ0e(N>V`vmb%EQ(K7iQ>~r5~e%^RxO6NeCICs_V3J)_H+)7F;T*D*1S@S7-exc zR|&*$&U72)4bv^l2e~0g_%;ejW=0Ta1$H=JUvV%0v-&I0_qS;u8jR#Zqt&Wo~7uCFQi({8mbXF;xga z!x-Ssj|1*Z_8(5p{u^Ja1yqB3F&8?IlYn3uVm=+PVTJNy2g?82No~oLLP{Z0TxG+z zFlQK1Q&Zd9Kw{VVH#O}owGLr>L1XyH;$C#Xw~_uX+Px{6maQyPF#oVyynj#p1vj!g znr7(bDEehYPf4f4hAoHX$(cHQE||y$U%8x))J9IMNUbEo+F8tiTUzGO_O%9)ozRA6 zFmHI3aUkx*lfpA%ZcWr8*h5F>@otLv^>2Js9V6AJr?f-mLuo%a{fJUZGuVyh+rN?l zJkSSR;n?pVt?Tqu^d>5X2c*q*(`%2EUn6+c5br;2F3h?#M>kEx2=J7KY32{>mZF2( z>RR>5>$mH}h}J7Nd;fBZY9O-0OO^GuIFF*fU62=WaL+d+8vfyoxb zU5GBlUFct*|4)8or_27nO3X*jX8I>b2>Jgb-hV_wI=cJ5VD5s0^NL*a&UM`G5;ejB za*DH{16LmwTDthH&3j6{VUL4&e`p1AwYFow3p8Yk_fIx;15^AIS<0m`=JQEn`OM|t zp_0_UmkzmYXIO;FMAtJTidVxoDeLBjF_6`yElL1>Q}!}he{qvP2z)GO&qW(6aii?s z%$1o5v^g8xRl*YVzKV{O^;O3}!baLaAvB2RwAm4v?i zF918J;(u-xG~wq$$@>h>X=duw7$Tpl!(6zPzPW@7*FD4r`Q6KFyUj)9nPRceq$^L; zgT1{z z*qmYGidbO?xBNXgyW(Eht@WJ6!HX#}xwR~U^2mrP!qzFXG$>S;cC+QFnp{owl#%3!6v9XEu6Q`LmM!I(Y)UN|wlI3PoW+bT=zSerU?w-)-( z4Lu}VbRsUdqaW7rAEoatsAt8c8)6H722RU7z7WU|E>)7o`ET)#eRbeH>n3h?)x~J5 zwKUXyVus|q?TU-t4{#h&Li$y@La_PGNJJ4aU;6eL+)PBFazbO|Z?Umz?W#BMjxsZ& zd%<KPOW0-nA=lNWUNw}I= zIr}=m+_9iWgW0VZW5B5Zz#$86p=dhcZ&974w$?$^P)gb!I!9jQ|Dx@~wHtW^C+zj1 z+?>I`vsDXeFV?r&oqrJupDu|Qkkem7N+G-6`N8LbLKDq*tU2X9rkpwcpyG>dpjwlT zyX%cjzdZS!3DM;;?ci%Ce@zp=`atOYMIsq4Ium-hFW6hm14JUa{GcG-*}q0}W6Fi1 zdcmNO+>)nCi$X;*3#^tSzimX32~#3eqFSHjGdwVzDfFwKr3YwL$l{^N&;VQEL_Uir z(oC(GEfuqiOjKv*F#qfv+vr!;N4oXyy@$ ziAmz15Pbby2w#V?<~P{lk$!*4P5y+YTSiqk81BTD=t@mM0|UJ6$udt2+|ogcpO1cj z4d0J_+6QztCbhh};Qqd${oiNpF z-X3`E{~{F{rl5aa{C`UH8C(>~PJBF`ymWGwZU^Dtu9(bO zfxYGvI&3g|TZO=zhrR}$zVG*Q$$BEEbe&!y89=hw9;e2)pv6$+_5Zy7danJ{dd?wY zs;qh0ponU94XxQhnsUxZM49#DCgad%`w4+BV8BR@p;y(m&1zzNz#;^Rk2Gf&^72Kl z+cP_!?{3`zLu>c|M=dVDwwZ?=*!1}<=ps0TNjIZe?-bgBqKvS)u8<0+PBPfZ>xA?p zbDgN}Gr)9)e(3Tj@-`KsPc{p3m)+1#T_HQ5R%r5tw`p@%H&=Af+OLj*E$^a^)!lcj zGDSKWLRe~lL}fjU#wYEVT&O3eQ<^FqJ|xo}4u6+~BlU`_{gbfjdx9?kIgSuI zsvhsY@AO}fRrPvF-^4m*pCk&%H$_0B-vlfrs3O^p%o|Z!#F#(;fzx9Ej0`r1nd&ba zr>i_qHG8rI)Ez`Q5_~j6G1=*E(ftsVpE_P_?4d5IKtB4PC`b!zHCk?yWo~x@CCohi zzIZyXlfRqLX5NMbwaMPPw zU@i=sy+)^DESJ((Q4HUQUCl`K{)XSH`*N}~p~u1Y-n)4Vw8=0iA8JsMnS^r5BjqS* zh1f~HyB9)lQ$D+%TJ;{v)Jyyzw96(%aA`Snr%S<{ooCGqN1&uZ92R%TRx}6g3vjWE z32&;W@z_U>ujPvD%_BYtBN?YU`d$`+C5t`CSOHHvq|!w=8-r(_8R+agyWi+BpD*5z zxs;W_V*zqMrGzk^iTevp%#MSj5D0q3@STB^Yg0$X39`P!!$gV|Kp`YZPWi4e5Nj58M)>A>@zh=xAZA`0C}h zcSaxuPl8ju6N*~v-6yo_s4Fq!OL!HLH)pZ5AvZ21qJFT6lFMc`i4A!mNbp&sb_3x? zvXC>9%VaMdOTx$k%kB_*2huN7RwHLF@`}@PYDGivK;RKx+AJDWKn8o>&0jEZ5!Z5T z5?eRMuP+t+@{C6Jaa%rV9+I+Zq&9@P2{#K9!w7Ev3j}qlpMP)FtG*X0Y+*YIe~vG%OE zGMWqttHvPU#P9Px#_%Kl+X{PbQkCK|%P>zuLpJf5E)~g< z=lkbwiWizsCDf5sfCG3Vun;Vitw=8UVBuIIUl95k0s42J_Skm$imfMf51GQtPr5u7 zh_QhV$`Y$!^BQX)(8+r_DCl5+jQqJ`XGA_PPpBH+vg1R;{@4C9g9J>LOeg1V(7i9L z0%Dr^`=qY%Hw*BDs`c|$*Dbu*66t3Bq%4qPNq0q;Zs2B@RTU25>@J#$rB?^SCPw0! zNuJ`{3t^;u<*7`tfuPxQD8@>W>@mJDmofSGqfPP@WN;xDd#25E4)RxLS|h#hcf|2N z;wna&Z{Pys0t$gr_zyB6s{MA?s3^E$Yg~n?x?`&jV)T#^Q+F8{l{RD3JhFJDodac$ z-oQYLK}~L=Wml z{M5>cPSA9|c}MtJ)MT>7$M=i!(|wGwmPSALqG;%I-#nv=nFJE*gW>6d*JbDMHlS8W4yJ;#fmsN;-PXx3wDy&}?Prfw_lIQbc zYDllO@&n)2mdBHLA28@sajz{bR(@D(rCp6YILjZ;g)5LmPK&^EGpRU8r=f{{NZ-w2 zMd@&0JsYSvZ-P|{Smpcg?hCH zkY--JNOWIFD6W206Q<0*Z@RUPJB28a&Z&9BOh=8YsP? ziyPxFoFqb8zwFD{jhDGxN1ar|ErjXfTY7&rP9-fW1&sM)jw(12A$Z4?oJn#EU*!Yw zY+~B8=&I@-L?>PzpH^Jk6j-UNO#RppaXhYm-hvXncs*XYJaY>^<{bIHZ0dAB+PVGMS!xw(*$=&P8gVPZE@ zz**fPtL8d3J7DVe?KPZT7hbaz((hw>4{lvQ4mrl9_P(XO|4^mHo{0qs(A=LIF1ZBX z%7Dzr7m==gfjT^>x;|aaLQfc(0sgJl0XQ({X$HSQ7YZ_-h)G?FOitx(7Z_WPTV^0dA)l}bozyvM zqOlva#k}AM?A~d+d?{@Q&}IW|gFs0)X{sM_rN@@Ob8Sa%eE4u@jkhWkdgOWY2STz! zEU4lLUE>2C4u`C2{JJ4`B3YBT_KBDU5@C)$P!wm3vBn5DcZL7Njxq%*&o)&?>^J1U zJVQkh)+-W1{XsU$4yoy%IgKA?d855+xC?|pL@`PO$N}F$`B+n#f{+D2i7NZ9A1A10_{TcWYE*^&^hDPp77iVUQYdzA-NU*iIfVm%$)5aE zrQxbsB4_xhT2EhVZ48HIz9#V(&jV)N-&dNyRMMIgz<+M*;RXr{_6j8ZBIos)?GRKt zucr|+qs@GN9@iB~iMu&wIdrW)<{rPq0kEwFHNEMV;^|-oZskaf+@0y`8nTvjpYZ;2 z%P&hE*-Q#QFD#`O^q7?Y@bE!VOJ~0*lI?Ig*Ji;fpb4VVdG66=guHn)P})`rHp?j- ze$Y*GdIHLsj?dKn4IOd=VJQ{h!p~=uZA+q$%MP2D*XQP`8rzAX8S&d$ zTSOxz&5=WKLf;=jG^*WK9X@%c6yS~5+M~8EUG+;^v!VvEp{UG%rQ)U0YR3rq1?c{mzU<1sM*1mBKdgm0B`TG-z-cc8HZg24-B zw(`H^DFtuIa>?K*SC4+HVm95Vl5aZhVj)*}+Qqwd)6g@*E36mT%iX+XL!NEg0gw7B zm+Ol||5oBfxZeVd_dV)!@{Z)Q>9u#yTSa68q_wFxsOQ23ECs=(bxVsNjb9D!@87q} zRMaZ{2sB%V&v8|R(6&cLKRj!{Bmj!Os6Y!B$2O-D%(Xy_0>sU7cDEWcs(oIV5&?QL zp4~~h)x(#LiGc*H!U*OB+h^h~ScFR^2YiSE6Ld1Y(I5E3AemFG_~eo8HXtTxl5ia7 zlnZi?V>oWfgR(!htOAfOhYCi7@P?d}`jqC;>1~hbCgX>h#g=CQ(F8@Ol=iBql3-;ARXrG8`fXwcI6a-SxqK_y{Lvhg|K`#CnMPFE zSH9g@oLSj<;3@3^6JN= z#$tX4Q~hfv7w9hUyo{On__y2Ld!SlQR}8CZu~~rBTN+M!3>nJ$$zI4*p`aJzj0vGs z*6HuHuI&drlvI($R{3j17a|eRpNw}BpMBvN1oqA7$!oA9e5S)=Lub0AxVrM@Qnzj9 z?0j^kQQxBlsok?Dio^ztAVSxe-)`2?o(&@25m=Kk(J?kX1V5n1olL=}>M?$KjzuR# zF1B)X=TQ@53rpr2my4%1iG7Pk<1}Rm^<8|v4Z*VcDFW^OWaPLHVPj0sahj2rfv?c1 zi`%sOg*z}2kMIM0bhj`{=BmOJsaF*Qv(P%wuMfrfFcj=Y0PI`W*=oKUUcg3})$A2<5+zC9ITJ$_iY&sNQ5Mm!gw&rV-CPu$_-w zOb=8^z8@=Leu&Gz(Z{*f%0?4gPrTGLx+L5Mr$B-b zZr8iBKy)>yucBCG$ta$%DhDmvO@L`5cT*8|SzIr9(lWsJ4pQ^z8;@5X#+C=4uRMM) zCx-0imfNBTZVhOHM9}bNU0fPUsAQ@)cKG=vn0xFYXq|DHhMFxDnV1KOCW=q>{_8CU z>?XZq15@SHj7&?#XpAma_EKrr4+wGc<${Mh->b|0#=2QiB*M}YOE86uXLSEE1P zNGUG`!C8_G?n9uY_jbUm9eg*E-!;+O=9_6Rj9SXrJ%t`r^;SG-hI>8>#j88|i6O4} z?4nzzDbQM-rGChPDVv~?ur8J2a&ibw)~zP|Ga`8km})b=RD}wi4~Et@01KhsCa^f} z>nWHFyKh(H%$L@a-j9L8kd?Bn{1_Db>D46Ea`yrRBb(Ogms`B7p9ld8#ZQ-`k-#w* zs+Bf-G6M0A5J2-BHaOnYz_B)lTX_q#}@q>=&q}7KUY!LDqP!F zX+}-sTgbo=^-UZIXDYE<>rAZ=a=hX$3bcz2cN^<3DL=#F@!IVC{lCjQP;fbBG`^4G z#kYVOU5x^#sexE-z+%z>(U}b;92MGX(}CkUKtU`1D7n8Rg_J1+&7>A(-4yX$Z>*@O zIzOH;8hU&@M(C`E;WLBtk=3nYp|mHxn$`bRo|I;#-h^Lp)=l^Uu2KdQ-!101^i7CE z#V+y;W2+^I25wkTsCdPtTU*BW(D$>(AK*+Z+yV^cw%DUnvFxjABBQ z*C0ZUpKKmx3hX|Dg8I9aw({JbMD>kI>K&TC`cVR2JL=XP(9UyipO-aB-* z%3+T%l>!@ae8)jKUM46Q%GMksBXQ-dfq4EknX$sbr%+r!Xb{Px{e~3!bMkyDbv5&0 zTOPp#>H&4>J#|1-p5FH@2wzxv^lNTI^a!_41o#DY1bA(jEUE*l&T88&JQ*5e;GBA| zHz&;-;x@rg+rWrwNX+~i+93pD)HcdV8pKwwozeGKSJc0Zn7bZz(yN64ENp>8%b3s0j zCsw`*%@@toWtV4)KMXdC7;z%+7J4$DE}%ll07{{9YGw5)1oKR6IGk}~O;6hNCU6$v zsDg>VZrmVA_|W28Xj zv}R~yPUPHshi}fQR&u5^?V?!Jwk&lC>$u~DC2kE%(;zmPl{CM7FqXtT&7(QRW>_y@ z341EdA}V*eUgLCFvjhG3IM}h#z2VYe-ChPMlF~fE;vl<)kYoM%%10aFfMb|>R!ebb zl>3qJaRLm#fuZBQgDB5%3VRw)ZAEJyfvb+^^UJ!v3yeU2tc1BtM7&{Evd|5#Y%|=Q z+N0u)N%M`;A6+{TZaW`!z8U*|v#Yv;1KL`AC{UZ#Vqa{KcP@VVot<}E@?cF=qz*w; zIdMf|19Et_G<~^Ax%B~up8E2Hl>H053JZ8^fZT#4yaPBUiL%6d89Sfs9yg@k)YO?j zFF;YUYFY=>)wVOJblg1A@3x%m_d>1;9X%7=Qn}60Pv~_0`Z#K1Tq_{RJx~%xL(4NM zzh~eD!FX1@ zpu_KP&uJ1GO-lj#(6m2RyYT6ur9PBzE#E0anNB{cgG;{u@@z4sZi>=jP@s^k)*o|v zE~D|oV)3l38n#15gl9p&1Ta0$xAb>~I(G{~3;TIUij}*O4fA_ z>CW=mfKwHULGP4@p81#H;)o(gVxiuZQx`nPms#w^weA;@aaOFiI8iz^oEA5uTI}*g zXX3%A!|8Gy)ffavqhD#V;aVk#ym3v;@b2 z)*XH)J?o7xV)Za1c8)M`X<9zJkJ9fVVz08j65&iLOfp9xM zNVS~&kMnJuwA2!3(Y1*s1fV-M>29S-r#p(_KHby^7%@bp-YAuK@88h9D$u;z-<^uu zo}sB!&#rrjY}3C3C)-Q+d5W{8P7DS|l;}W0W?K4db_9KBT|34!Gsp%n!q~}Z&a42b zD5;(a7uLCq@sISHxKT3x*gBP>>Vp4VaOhxC66ohw(b=20<=kwp0eO~DG_u`)av=oU zTJTkZaE#DA#F!<9F&Mj4qL^UvdkGr5o~dKT909RUX!v-Zxr(`}ok`&pi#X%oPv&BhK02#)3L5g|g8q3`lv=3}m*3kE z=pwrt21rG?8Si-aKXQzj!Btb8(_RalMiYNc4ZrGq%V!^_$`dn`&CHXYICp6PvwZ{v zGPGX_pd}^|R`qYL6|ET!^j4OiReocff zcc^=y<&oqyG*YP%-&W%=mv{rOK;bW~2)%2?9X2@r;5M;tq};LIrUdT^)C>dul=&$y zM^=xvOxMeu0fITE=*k2Y;#TjlW#*H^1Wtd(m>N3-)_LiSf>KX<4e_;J)lcBd%nHHX zH~kIw@jgeIT;MmsN8er}8gRDEPERq!2~@ljCaiS?7gK5~9wjm#jwrc1NkNEM7ji?Zok?b9s9A|ykE?NK^>|h) zp>oy^6r^;2;#jjLC=i6Pj4{zs5X#XV$Gp^<+Zc(bAK5qLrLA_fKkHDEp`M~EU!+C4 zp$||&CV@JQX?^g~G5y$nDT;WInHW|3hIWHz*0 zNVr+{W|c+*J0=3{p@e=;NZt<03q&7Hd-W~!;7=nU>cU4`E*R$>n}bfkN8(-0_4Hn0 zzbN1nD?B#NJt` zEFrUJ_>sh6Lb=tGl!QazvcAC&S2`EUu+l0W zW#va=@Rr3qTYl45M~85LTo43V&O!?OiBZf8d-BA!K%4S0WS%qVY?}`piQ&Q?3&rGS z4A=&F>RbugtK1d@wt9=QI}nlbODDfu_h{N7G_g5}9#k#bA%(qErlcV08P_lFt_{}% z+ai;tRQrx7IseCG6$V5iEyf4ubnxb$bh+r9Doe4#(Z-eTIm`IRdqERa7-5_5*cGa} z&UnyfJ6kT1713VvSp#K3an5lf#e~VMndidJ&k1wL1Vm^d^K31j62xGIYZKw>VV&j; z*teIBH>GRNx;Rx8j@WBqB#$=i-+z5UpB#Bg|5| z136r5sT8-BgoWQ>_!JpQnfPK9XDsJ}vHTUsvqH6icFxbn)uJ+B2xljP_ZhygA-mDn zS4)Qm6ll?0!tTLjEz>?{?O&@yXXJ7pywQ@+*AX+NNhdHD)Gk8{mQqX)cy z*iyhOe)$G27w3Mymo=v^5xm9A@1|YC`{O|{#CY0}DkyLk72pRkTl1PZSV2NcHQ++jbS=}k{MX<$^g;D`PZG5z5 z*o{23BlNmp6_3J-oI_FR8%gP5&&=&^(Fo1TOd3H|Dd)MD-dUVI)L~a4$*ZM|j$g)- z_1qUEuQ7GQkp`<>IEs9^an`gLKik=u_XnHtAaqaElZ&AA{Mbs9i?C9=C^t~YGtYl> zp)0B>V;p?WwNiydII0e>L0D?fQ@0Mdg^@;ym+JRd3#9hKc=aE>ygMf-R(8wuhl0%^ zUd5Nsr&hIId-BaTBi-h0U1S~7FhkJ*qd_xUIH4iM${tL-l+)W=9T2~& z3qZqgvU*C)vE#6;fDlc+aSF6u?JDU=`JVjXB1YvP&dX8jb2|WQb0upwLy$q(nc1hG z791|t@AmPxVJ}Gr2fgUJ#WpcKNx(%`!Myqz$FwD05Hw(zjKy(Yib#4H@$`% zc;tjkYkKf(ubm+2@9xUms~oq;SGsAKF)fxV9OIS~hezK0z;2@0@hKYvIc_^QgtNWd z^~>f2UVS}_6(8W~^sZRRm?H7!`K`WtNOH>p5! zcY_9`w=e(ulz=ZZ`aX!R4Biwzzp()_>2O5xVtYIc4;@soaZPuE;OQkb6DQqGbyB+I zWh_MeEW5XVA(jrj9U#as#5;VpbAh^{Q@3#RUbDXwB)JX87K9_&GMhB{3KB`MBmB>q zBkL*&d7&4QeHB#|4P0?6p@<3R`&*?o!@lDv?}!wXwrgpI)9X3aM-TW~UcV=;1!Ziy zjp?9>ajo5c2oza;x<~JYH;G(EY1)QER^tVxQ38XwZs=Iq0R(@DMS{;An)wpRZQ*yG<@Uy9Xzb&c8duNhkW?Rdgp6fgzHSr2nop6|Yo*2FO z0#%x2*tbPG8h@%wfKOGySITcJ8mju@)c7#cL=(%+DwQcnwDaq>Ow^+iRVyj)r0%dL zR>s6x?D+Zri_L+6;3392Q5k3u$64uz#&AuyQ-9u;4!B$SgsX)ZKiX5Pba-C8?O>Ya z2;L_aH0i2^U@q`&2&Si+{!%4iS6m0R^PxNMaJ%i-Yn$e^UTwu;ohmr=H;--_s?pUl zVc4?8=zzPf(qgHjC~BcPLZT}HiTvqKs3z=C%jiDNq`pl{n{7Bhm)@T)2)h}LR|Qjv&e43oXHBzj-S8C zUBr{5Q~M{X+9cUe61VQO$aDxEquU$2$Uq zBphs=Ea>?n^mb3%w!BE3i-8ozac*^3cb^JYL59;nsm(AGN@5fFcFpg=1XM9xYCXy~ z2!*aSR<=2Ty>LE{NX^WshNGC5MC=5kr&}=wZ&-t1qMaP3Mx#S;;U9F$>vY4z+9!i_ z^m!9UyyQoPA?qflkrS*vwIkW+!J@SXkNC%2iw1bawr<8W+=Co%s=q~tB)X~{3pUhP z$@j3A)i01K@SztPsZqh>E`+PVoV|*LA`lhpb@&+qTF!sS@X>L%?sw&zF~g_fw>7m= z9xT(pcz)AUS)ROp({X(bYVOWR2tU0EN(qCC7jU}gLKG5o78hC3BDY1u_+&dKe-ZUN z*txhE6tsMrv>?-8-8w$i@4#{~M&FNDVY{d>NM%*>UM{tZbtp>Yn7?&--897UI}60* z7Uk<{j3MeifXF4pP5rSB8ccfwwCFMK)dvJ$LAbZHQLf|Qgj1e^0yZA^aB(e8a=3B& zi{_R#wvs+9i^(y99>(5M(_#NM4V^<3p%vkW+Z057@_;_{b**MWk&*h4^1B|~8UY0T zlYd{LAbZFfEf9o2tnD*wvu^ladQ8e?O;^ot z@{XX^QIga5lqDI4k%Z5$l4T z`nZ{-r9uK!Xv&Sh`zy@sxVB)v**k*k;|&SKq(djuw0ROr5RK5wA{61oo`wk5>uiDT z5(fpB__mTyyy$yJ@gGc@!Vh)BIWX)J-R>`FN`0LX@*}NLay}SCv?yCq6^>en8eEyC z+lP3fYHy@W{X%b1=vqc(q4FmybTa-Ow&J!sEi5kcz z$m>9!9}KJVI%PF-E@wxVBW+f&aIf|~y0TR^+&ranLR7z6F@v6qtSB=H~k!bT87pv#yN=-oY?Uyf#MY@q5H_Xj?c9JugBpK^R&ie6E0mF@zL?>e9bDoS_t_Zy{_KN{O;N%Ur+wzXa!o; z(7R&519;k5b8e)>jZ^)uh|TFT--_EEIqVdj;swVK6251+QNVb{%vWevkz)&*Hs&PZ zlqIxKnhq=1aJ^b;&kWm2K}?^GLT)SH^&)E1L^`1}^W(Jy=0%p;C!HvZs^7-`C7ug` zY61?xe7Qxca})cC$V7T6MiRqE*gYZdFm;#53X`MPiUBJz6ehaa+azT8hJ5wn5q^`r ztR^K?1EdeVELS;gnLqr9t7quV%c)ce%E! zN87qR1;OAqe7EJ1q0TgmgFaePL~=ox6Lgi~(I znR#-!A34^*H&0ZybvREtV&nQE0{CJg-Lbfe&|ulcWy$iTFvUyAN%1^u+~BO`994D5 z4VnHct)oj;8OG%pG!bKd5Fe@pjy+iQ0?m|mXY?D0o(jbBF*34hK@=QmY1RUC7Zt$5unA1hsDV?=~cnr1A1=&>7dUGo_XX(g%A0jb;IQXsS zDpq$qQMv`$FWqye5)GwL0B^t!nU*oU4u06^EG&keaUgB3DNJv-mnZf*gMb}cv0dgZ z&DwP@0O~CF9YC~_)B)m#wNdD=IW9kG_jSPi0q?M-*7GdIJ*wZ$D2}M}?WZnF7W9|4 zRDU-rOpbZD^E&9kS?~b}9;Q56-CitYgkNGI7-~O0cb2a;b8&Dro_eTWVdxH3@2W3S zduSsDm&BdMcXQay*|R*-ZO0grRPaa+MDDzm56jy|s=$%6@;j%8)=s(Ux#5xihDmy6 zuIC*o&OxmBg!Xjkek3T$y*2I57Qcq?dY2(^eW^5^2(rer10`$V>rg0I3tX$z^IV5{ z%0v+=P0c+uEAbKiY6b;CUXwdEAaOG|M5yc`QhW1{(h^!)B3a9n5ei9iSHGus|v+4}u zu8$grVuNu?Dk%|F3N0)?eOxx9gRveO3DuvRCO3tTZg-QdhHvFfzSF4kULIEcrRM~& zdi@ust~DX}6qcgTf>|6@BGx42ei9Wkm;|mnsD{hO=H|Yxv8XOv-}jsoNsH*>gWO=1 zsH$_hB1>EzQpUf&-51}{z&>d^fYf8Uu~Z%xyR-u*XdImgk0?*#4z!tZ8SzoS%8jT7 zDCQK|^!vvllcw25U#U{i1X+V*<1z`3IXC|d7Mw{6MCvyVtzQxdyH3-GrxN4KUtXR3 zfeEyVkDoiOoEBI9OEU^+Ns(H80m}ZP1PWg^X){jimbMp~#bO@5GQ)I*r?l4NkVSQoygvaH8UN3%4s*y`QO(9c%-P;cfU6oXgWTrduyn25)1n+Jn_o&x2JRnoN1WyH+cRo-jQW#Ze#qo4PO}`B8a#7kf zZG2g!knZRI7_(r-!PMnFY==}or;F4P@dT-2MRP#hg>@Kmz46d|LQGKTiXsJedIhe$_HkvRt8b0H8}PB4Z8&sxa%OMfUz zY-lJpz$Pg6^PBUWM_sOyj$_&Ia355#QVR@8Y|*Ef=^_^@L4z3IrWGjBipqqs?HGED zDuCNF2z&^%pe`#kCa}iB1c1OwDb}v{bwU%AM!<*kwIqV)g zKuUn^n|HZefDI^uLIJPEP+Via{K)DJ89YOC?#iBntsji)EiSP%;U(HIM37igPBAxH z*^fpTn}a6w2}+cvr9=s;!O@VRC$cVQ2zBt9vjpl{Vgik@3?0JtJV<}proz5qcYRWJl8m`RCO!wC^_UA5Rl zSh)*U&B24%l03P#r~lp4MaZ!DI$aB-CV1G(mUXB*AdMkp=j2E8Lr?6IM%^6 zdo73}>)NG(VMGfhD_#t+^({-~=5UgCyg4rhj})urkhNz`Fc+CnF^Ja(Ss%w@e4))u zhTSrx@|1M!p9Ms07ChT+xKchDqai^gtLJ7hV)xw8s9(4KMXF`-yXH0ZsEe;fr+e1( zRmAoJ260G8(yc4nx`-wPw{bjE5aE)(h3D4ui5(WnP|lSKh}a$vj4X!6Z1mM~mb1g< zkEZVfUnJ(dY6K1K&;7_bSdgP?0`aXwnbF7VWtZOQUIdPtA8Qy8b)Nu|O1c#PV$yC< zu5e&>>~bb^{gPA4V`v-|$q*L-(HA0$MSzUZLaw-x8mFKEh6dqxh5k#Io}VFKSF&NH zjuLpu{C2QJnSc@>)@lb%i=Q!vy3oHTntS5$u+op2utQ#3Tb%yjK!rx|*hy`!_0Ux0 zipV;M;dkZrgs@Tp|2jCm^*mI@@!s%l}v>Za_86Kd4n^3|GG1^)oTbf^vzKSUi^U-p}t$>nNT2@?+JS=dkA$*A-DC$8cs-v_jxkrVM zX9+=G)fSc(NzKblBz!PV;J1g#hlGwbbNV%>;%eyoR9A6e)`2Hl33PLYv@iNCy zLmA0jfl@U5ZbxDz*)iIV$Qz%iMbMCtQ}SuA~8^eH!{hGaI0pqCD+ zy4S&CmFd`xsl*wVT^7*8I29o!^)8`!OmctpYz+d8qRjrH{UcpU7SQ^TGzSYE`vMJY z)(3F(!G=()$!XsE_HX5i1k^_Mp==rB@C{aVTWJa&Q@*CL~Kp(<0g_7e& z5QZ2V9|7*KQfn*jHQH#@x7F>XHPO>3%%T~uk((W@%+=q#af;iqqWPn*cX=>jk}^LR zVvWI!QkI&2eedm#wNA+T^Pe`fo?Kf} zyDLp^tI~qH=CCkdBReKD>!2XbBt-5TY{UhRt=Qfa<6^HSRF>0>?iD2yxV7YJjV;@$G8^_R%HeC^K~3Y_ zsf3y%dD+9$h!@((m30h~-+;j<8%U|{3iyZ*>GzRb)fHjny7&)xP>YRRDclnqx)|&* zDeZWtZy^IWkEu%!4DZ-u*t+~$QkEfK7JMA7e#{BQ;z**@ZOsrm-Ovmy&xPScG4v0a zsybWH6}Kw$w5!$fDWnyV!8|WX4h{{);>YFIgJC1}Y#;!M9p_Mcr!(*#YC8gazNYfl zt4-cnYy}eL?(b-7v*U;~wor0YLBLaiaY3CCQj7D~2~k|(Si$)XNw}z?CF}{*wfwo< zAT_HVqq>sEB#85~Nt|Vxb1Uf_;%bGLR;heJ)uCku`h}?Pf--Qz7#w#G*Zw*DsVQeu z_y`@3kT25CE%id-*w)r=75J6pzu4T~fyRr_qd-%wx>eHnr`o-D27 zFvyb%)*yRAF{I5WsZuH6-8rNyT6;_poat~hyO-g%LJ0x&DMdRC5 z(i&+bcEufxK~~dr$HwV1$R)p`1@BHjXuhMJqg!3vsRP^_d~4gIlameU&h8S;l9(9l>&*pfByCV&>=MU3GA&y!w!lJCzx+p=p>kcNTvwi$ttJ6p z`<`kgn^HOyxSa?)vj^4}oN4i;5`rXr%K6*VEB!SwwCsH8rTrWUp9EAw8&1--OgtL4 z+TAhTlJxqS!BW$4y+ZRh3C^-dwd@+>MqE?~78vjh`u=Kq|EG6z?u�*KnFV*^_HC zHhc1hjhpS-Y-@J1ZM!zxu1%Y5ZEf~uocEj$asGNf!u`AM`+BlHkFQ20CPL7%2m%I~ zbs6P6z=IZk6#u!qDJz`4JI-|a+5|-7M|lo*o1%vwVt4+tvWea5aAxoniK2P!6JyZI z4@w#38-jfjuI<+Nv1~K(GQ#_}6s=aZb%_k5b=t6_?)dx^m+2`=`zq|{1 z141Uwe2D#ZL|oseyg^73b^5QipP7;REI~ubd<1mu(I;`q#y$4V{6{%r8bK^&&POz9E!N3;%Ya`Bt%A(w);tQJ(<)b5FkZ|$xu z;xA`Nol@y6|Ikkp1?}jv5Og0`M5{ZKH^Ll&y$mbi>-hHubJ6LW>Vw!#$LhN)rJ?{+ z`f?FdnKO6xT0oBE1z&B5&*v3MzB5nquPBV-$dsr!#Tr|MWxwx_*h-u;s)=i4FHIx!31PEW9^ zY<@(?dp|Kw(mB!YGvP39%XGq44pZ{N!?x-b4;}j8kI#y%@f|oWs#mJ)``uX z<6=ZqD;Cb_2M(jrbG~5S-BOcQ)>Ft1PZP zVY`ilR;g`$9BL1-n8UNjjz+EkncY}gErC`? z{@FSuA;I+BKEmz(!f!-cZ_m!V0T967K^=_EofLI(b<8c^q=$2yLS zv2;|t#dqJ$WAYBhjb52%{L?qaRO648tet0qX`h-phHtIr37Lni(VG+TMBs_f92=|L z(3uEu{Y=nbCLWI4=9tr=`1K?snIdqf#1~{eYDuY;GaRK;0q?blgZza)T5ZnPvJ7^j zdOjFTb}-fw4cZ)Ls!V>@pWvv_PIUw_U=mC{^LOYByLkWf({{Iad4BT3Fbb2F9vaY6 zqFig=`2OLP9WNbauR!b#H}xolU}TQho- zIOl+^0>$k=uk>CV0v7ZR-CSujv8LP z`MF!Letv)}nR6T^V1r=3X;H8`c)W(+3xYB^eA0adt1r^SnZ)-F?+!%Db)t#je);@h zsfMeeE>b5a8F`S#<+ld)0@>MO8z9|rf+jaiB3y8G@ySqs=9@d^Z@ocgx!-4 z5TUJTR=^ERYcPMgNg>Co;fav5@uUDEmp+~5QWs> zt6K~HT$+T-&NWf#lR`GxQ~t;t$!k?+w7GGu9;|9bk5?nn$7s97?an`OH@Fyl)--!D zt}+cpP|vGjL0GKt%R3og8~*lZq6wN(Fz<6AF*y|xc|g;ZI%eJ}Ci5JUWf9XY?WfZ| zzj%}y!C&78ij^8q7#=m^LkD`~R4G2|8EeT%gWnQw7m4q}+m-s*+z=#})S=M(Z% za5SGg2kCj=M2Bk`nLxaEKRP;Da2KNSR}zU98b|!oVh$v@8q&PiC7U4&14G! zhfnVChHjE(?Wj%WltN)fJF3-vjuUr@ob9+|`S{;N5Wh&2K>|Mvs;}c;9Ov5|7Cr8*-(Uv=!-cuNwfN^XY>0ra zKdRy>YtfPc@A>6H3_A@n$?;&c)+|YD0=We;$CN|$LvIMU4#i`tIWhMrxadY1CP_=$ zeC8bU+&bTe-^`W942L7@Ys_0;J=j`ponqF~hAf>Gg*oiDcU8(XxfyBB8+s-@(A{;E zmk>j??%=PoZRpw&i*Z$(FA^b=VFdCO^%T+ZF=sCdzMWk5$GDDWeYbHSqlqzf4{;iH zUtvRw#ySf4iWhEo1nXLvm_rJnIN@>a#i$oXbtUuRjW*ifsaa`o;k9jCX8E=Vs)0V% z0u3J;AHosWH>DLC!Gs@)Xm*sjm0RCx-A0693=sk{Py!>(tVp;<@gHe9S^8R3kIQ~N zHwftD>6fQ!X*PF)!mVP9Nj)bScfQ2ghx&84axZMOI@$Cc3=Z8#Vr>6IMRVaHns2Vb z9Iy{$jKcX_&qubm?(sy=nxp=mv^eymPNMA1j|F_~O%tpb%S4#Gz3f$q+(ml-uUZFI zic?w(5q%hT?vkWRRn&87HlmTh`IA-Z=-8Xn304kPzY_G{(0?&KB;-PPEp_p zt9O;Qghn+o956BETs)&UgVPYJcuB1gIb9x(Z4|U0S1+Xv?M%|G22>4`ZgREf^?oN~ zLKRB|af(M_+o2xx`;|``s3=lnBp0;HC~j!;d|;?qn+=DEGjC62J(*81&+aF=`hK^I z={rx4-{{~txP>m?F487%IKJC=*6zd>YD(CCkTcZ?v)X|cwo4wcQPQXp2;73Z;*t3t znVTQRmiVo>$CLToKMKz({l~`{9hOyefuC^O^~Pb+oP~>9#WkBVg2ZMbV&94vTLl%{ znJ(rC?ov4-er`@o*#4%J#NWybc7lg0XhIK>VE0Sx=c;U_4nqx!#?FGxCTw9oibWM) zM?|wJHS*YK9f>2QpD0PY{o+`(q8;MucXUN0^q9qQ_ZW}VT%EUIsx{lqV02tIVt-UM z?$+bDs45;~2qaoX`@+84<>S$j-W6F>UVkU8rrSC7!Rn2|@NxV#P2Szs2v6NmKmAnH zVLPQ$-b6&xI|4J;P=}LTIaKkrFuN~ADEI6W{>fi^rOw~!9wN_8%9plpa1JsmGY!XG z9zs=Vn-=jcU2~bl1ohW5$oTjPYL_-dh=`^kM2r-h_@Q!AY?=YI#`k%7L-y?wjaA}U_|Fvwx^8${enR?w79Uhh7* zGHFW>SE*oQv~`DE9rVdb8dDbvc1&UVi8YAkr}O**kX88{{s@=Q((;~}?}Rtuq->9S zBEF)~8)AwrBd4Qh&b9iLJLl@3JX7U_wX_pyZtPA)HR34q|5%|`Y^MQ7DRJ~a^}qs} zWfVr_2SN>hu8LC~4d6hx%5)glJ*1UV7nZ+17hMPqW1ez@WukFX=~vUCw*gPIgUItO z+h?FsOuT2J)aF5Y(?7~NOz=D_7kSL4G9>F&%))i8d3{O4pA-)ZDKtHx2gOh5Gm$5( z3C(1>jc$1IpN2M+N;5OS0!pPj9|fv!`!wCUS)D|MuCXP^zwLTz{5iR)ir@f~I0(w} zE0wnaP{eLV%YZJ{U;5AGOiY2ki05H!b)4f+ zP#{1JQ9^sDfvC{)Y%I!Fyi9y70I$1lr{(>R$@uEdBAoyxJ^L@Ve;Xl+yxgUQV4+Yu z|KbfRt?-(8U1}Y`{d)V58plhbwx0h1RWQ#Pz-(0|#+B%csyg$$&4zF;Cfq44b?s;S z&AYCk2Jo}Vfgu734V&P5XSN;JqBlX3{1y*ywL!VAC{iKztBAaa&}Q{`0_xL<{BW}n zdO{Jt*b1DN4oEqa@mR>fvURw4yG3>&sw9Q+7b7gnNf(wxq)>B^u)bnlvq6)EBg;R}M#X0&Cj_mYd1|@L!66^GYY(ovqeFdfHoy!3gk)eZ ziPouNMW6>Kd88)i6Vdq-dc>bFE4{-Mi7M|V$U-`$ifY~#Xus!TUvAH+1juGgDaXFg$ zg_wCp{3DheQTAJSlIbMj4{VUI@UGp~o)gjnY;VVWd+!$!JL2wqR)BldtrHnYz=ok^J507J+3*17& zTzveGuRq*pthsni&Q*1aD3>?GN5IB6NW(RLF&ah>9=p;e0Hf^pbr~3_1Dj~N3~QzU z$x}Hz`pW;M;A+Mb<5ernEvvb*yUD80Aug{V*?-ilC3@R-s9_z>{P>uJJ(l8(_1AwA zqJYIf;ofIAWukGZ5Omgz9PD!}Q!=rP9Lo!CkE`Pv>VQ(VqV2actoB6lO5yLNHbxR4 zE9VjI>TAZgcT)hKmh(EO!J4NC5P>Lh{LTF9hsSsP*QYm~=hL$iR z269wJwYLbF%J z%C9w#Gii6Nj+^;rn}#b_#!zBn$nIk?(v*|z4vbkess~I&o^M}_P$P(iv`rs~X*9a8 zCPBB|?q7Sl;WO~bhZb4eH3|QCl3fm1wx%YY02=>$y;6IGBL@Eb=6Es17?)A=GN3u( zv7$b77UY)9KPOTgPY#Wf`u!uZ<9-L0SY6GHa8$Z2#kFDc(-E>BUJXK>4<1?m z$Np-kI+Wf>xcHHZ0Zry-N~>TioKlyYROLUgdYlMS7(GGev8MCo<8) z?*OYygO0j77;^j?SpJP{tbTR*A)hanxzf~-iGvZx z|52ju44=t{PV-0i(63eDir;Y?ltWDSF6g^iKv>p3N(j~;_bKd~T=%_tSdNveM1p)F zj%-rf8883~apS%Dfpdj6qIFnG0DO811Q)s@%(G5(5o%7SG@{$F!8~v45b0KCwJq z>$Z}Vu~r&iY?BD_lNI%EC>#hElDys0oUCve!&Wu>-Gl*t>9h%}>9x8-4jY_Z=-I1( z`5x3YJa7z+NsFBz7o+n*HVj>wci4DPAgiiY!*R?sMH}j(eB`c@IKGK*Ic-e}`H8k5 zfuEeZFC+Gjx3}$bo}-Mj3LbIlZJ_F)pPJBL_B5LXPY$UtOW4bMMQp;)_}(5#*P}Y= zE^rcbRcBd15XFC<5vKQWxnHL|ARWX3AbXo%yX`&l;FRNn{nGp4yx;foplWs7dpX^< zGBsEOsyTKPrb&h@n7WP%a?91hri7e)_@rWj!t9N1IEc#UD6&PwKzwpcHZag(HdL6^ zN)UX!NakFREg`yu=>LKaw)$J?cHV~K3qO0a9a0z_b_3htH^su;42x`nb_yEiwuKzf z=~|4UVTCZvE>yf`8ppsvsA<>1bi&7rSLYTb>owFC7UTg+ZXpO--aS~K-ag#v5x%|g z(~!0-elvDlGxL|0T5T!G?+DaaqJO4}o-Qv>rZnN1Q9ZvVEWx8HvpdIew2J%{NM_~dIay@-Nd8s&I}9}!qrwJDq_e~(NRjNd=; zYh;qNhGEFO*A6@}C!o55UeCWSQ1X;4(*I?Y6A;R3V)A_eT;izMWt#OBbUtdq*C*Wr zsl_l&%!JBj`quaLvBOc=t;V4E45zU#>E%jfy;ts6BSpm;Gd~3cXwpL!8$M@+41^8Q zG~XIt&J+yt)LtV6kDQLL!IZ~)Sw8ey0!M%H2-)Jhdu)>Tlth=LY`#fsY)vO^e=*kl z5W^6ce{l0Kx6ybo_Bl&%<-%LMyf>HjKgs?V|5a{n`w{aSGeh49EhT48KyE=gj^85H zW~b-fP@;7_>iNQRa=LSvB4a|rZfG;IzI}zZkw$w=J9Q#2m8dwPH`@q_n5WkZkJK^iwp^TTzu zA9fb#;SRQSGizE>0SS`!sXyjm4=Hd<{9;vS_O*1BcfcjB{u0TG?$B55WNr}Dp?@u_ z;x0J;X*laDgMrqQC0pIa%5zn88Zp6Nz6ldpFvq#A97GkTSTMdTjsciDcqBSup_gMhSK=56)O3pZznA2OF!6^L}QiQ~%AfWShSor6hY{4+fJiv9ya0Iof;C!cqV?^EwnGaa=ED&>>e z0}^h#b1ncvr87L7)pUkDtllY8eK(~kesr74P)hMxsK!FLr<62c*X(V#(hjAqYlxmQ zQVJ_$p~A6&1u6oLaTeo+h2f_)ZhT5s2TF$)isAXTj{?V1A_Djk-iDR6g9$;?@^%Ci zR-N4S@7V-ypTlqHC&Zii1NPg%lVOvr@fiXE=!GDy#p0e>^x}H;%a!Q&;Ho`KBoM{i z1YheH1dtdE4Bag*mB9k8ljid)E7^cXW1Tv3*ZoUza46r+cg%^Dt{NW%sqO1MR6l0@ z3Cx51g27OgVkx-QOV^c!xyLHR4wGwTOAk_&UO3(kSMtONvppI8I*usa68N35tu9qNv6e7Uu1xpW zRs7$-tb~CR14t^vVF#Gh%Xfz-QuDNuV~iiq2!F{LL;sQIPujrMp7VixAvh%c!&E%@ zOvxob;xKD`XJ0s|zKwVo$Ixdh{#$x$FDScsY|EsSh%<7BQG|{?^7hfs#wAAO-uPV< zgm)x`%OAm3S!q`_p>&6F%rg#TY~ws^O5=ZR@P`~|7iue&B-xry)id0Dvy-A&LMShs zshHV_B_b0i9CNlqDCB$@70xlkJ?$G8mkU(hh6Ct*5nOe=TAQ}IGM;$YI

=#Y-}v zzL7I!Ds5&2w`9Hw!C10-eM29TAd2lTm4)iLD9n<;ZA#EQKzv-`l?L*ddfPjq#n1|?T; z6-&`#ebJ^+Lx+JRW9tryut|^KNb)M%Ze$RF+?3T~NU~20w{Mw;*4J_i-dA{;Xuj$6 Rr{U6gt6ZlN;{UBc{|8&ug<}8! literal 0 HcmV?d00001 diff --git a/docs/docs/install/img/truenas/truenas10.webp b/docs/docs/install/img/truenas/truenas10.webp new file mode 100644 index 0000000000000000000000000000000000000000..21a8b2588a8c3e245493a02ccff54c13069242a4 GIT binary patch literal 17070 zcmZttV|*u1u)vM(#zCReA$e7K4(artd-_OW;@>4D6bUG1y+C>}J9-4E1;2xB*4f5SuD-9K z2(cy7CoD31l{)!Vj`aisenH@cVU|(4k_akh2S|v&$^e;`*(a;HPUeLkS@=c5j^BME zgnzH{^fSclHQN-peOGx(*?c4B_1a*Yz>F+<6UAALbv&kLJ2V@~IWj4$;|~om>I>l* zaxJWcCVX5m@wGrOkyQ-D!SY3F8W z`2ywMeg#}0v>jjYJs-VCpY)KgH6My6kXZ0vK}d=?ev=Cc{|Z})!XO^2v*Apb6OFn7 zJ`8L2BlSn!hefL$2-@-o*4z&qkNObolsW`No%CRfCg2p^;2FN~(sIv=p}4-ueV_7w zmV7_td}a7cd7euB?<3s%S(O^(Id1^o70yBcArb7>=gNG^$GaLv$y#N_!%5rz078e) zyAjS@03A#`p4*M~7xDn%>Ke*2pUI18^N4KUIZVq#k1d2sXr>T-I;rA35@@X2|FH1}Ty$JQff8ke?Et$Shbu=SIv^v!hqHD4Ax&MjKwbk)0j3 z+)&opcEKY|>V!xWGG-~}S@M*z+l-E3B0S=yWeSbe{A%AbaL2^#lT``(*jToXBtal& zN&{3Ca~DSQRhL@GtS^6ro=F#uWe($@Y*1K6rlptq2o!rbVz~n#{*8Uh*n-X@O4fu% z1PpUBWo9psl$_0b9BoDLVLP@8M#&@}%1)G$iL}uacfpPvL3o4M(EG$YA08>4P=e+y z*-ksZO!4x>q+L-7K>^5CVN9*VquWl`L_~|JR z0EpADTP1g?49v#eb{j5sa5(Pco^dDPYDl1s8VA7?DDBch7Ff!hcZLUQHtkhlx0+6w zfAsiT(3=;qamrdaj>k!JH7=Lf{166H_H+z%x)`|KWm32ecQ#NB%x2Wi_ehr;tmM8hC$$;B{^+` zZbii$VOKI1qjX(H@+1@dVJ=EtbqskiVup%!zb3+U zFme6-x6~AUqKsFItKiCgKfhq_S4P$Tuk+61GD+;LjJ4BEe`>ky#1S|z0{-lw7_Mkb z=Zp*NRmilfrfhY>W$F8N(^1Tj5x4RZh7Hkjf{^1)!tVP#`gwCd_^R2Djpclb(bzJa&ps5{sc`BMEkNMG7e&a^0Nt1_$Nu?{%=Ii zt@C`>y3ZQsi|#A`%J3<<%Lhq+X%TvHz`)Uj^7A-GT4~`SO+a}m1GSSziq2+S)4+b`JRyRt4Xt(8`B57yeAb_N@M0{=ThibxA7+1$A zGYlHxb&M^iK#nuve$H0D6O@_dP&EHvUsPEH^l8M_3q1K6)|vAdb{gkc>sJx5H0VK= z%$gPdsuIh-UQY1rdLASAh{|JMltLZDa?%T|OI8kP?;P_X*N~lQ%c3<0En%}wnp2W@ z?dSgW-2`I~0^z5pi;CpKdzVN)5JK%%RWCx`C19?;@iyr``w-5jR^e*4tg5PA7_$Gq z9^*=>rGFkChGhscN0bVDDQmKP?`qyD;jpftx~j@HiII+BTU)epSrvCS!Nu)7G?w75 za?g59+Qg^Jg~wg^@C0VLH;Xu)1#!pZwPA(-04CRfn3Zf@XrCy9`ol5LLKZSq5wYsg z&2>Eun?>>WSH>!6SdJz&?6QU2%m8QWTj&H6;vRDctNt zv!s=;=gV>SN|qvw-m|hB<)gDmtt|gmSeHaFqQml;%|$v_g(`v*3p(T1qCIaCn7-Ou zytY)w54D75(CJ#8U0v_r3#M^DLAJvLTN&#sq@Jin4?7kAgl{1c0G z-Ao*N(iJ;(9PNc`F)?CI+*PCqAawY87jpC!zdRCFS`NfI7w)1fR7irKrNonoOO0E! zA~d@{;>(Yq*Q#HB?*H`O+tyzxX{80HwH|1;WDepMZrZe5{`-tP_(T}%)rB{)G~I<} zRa5F~6=Dav(lqN{#z}D{6LM`<DXrWea6%l3^&efQQD_G98zIN{D^jLu!l z?RXq`oi{guFm^mbrPD^U=Ne877W{hlfxC}b-c{%Cje4y8O^e~ep$@8YVLnm&=&%Y)RMY@(v#5C? zys0#Q5(Gb+3Tc7v;tc*G_~q*A2WV4Qb=>uAo=U?0lo7G!e(z12e=lh?4a3(?+$40l z&na-;0Jl|roeDkaf~UQ91})&>AN{^MOZ^Tf+~dZ+IO;?@xLb-+s0 z4=``G3Gd=CB3ms3MTaR0AYpZ9&c{^p{(<5)CE-V>zye_l5D#*j)3 zlh8W2?Qk4HZz@LT+V*>e9s`K{OSK}p4{U*Ek)|`EVnOn++W8e-{Z9&_QcW2UJcV=U zr-%Gj%rIUaW^@#W#klspz&;*YZI%~s&z*}Q@H)L=6nDX0C0-BLwnHO$&77~Kny+zD z-z(DPVz+&2kK3U{co(K^k9qvw3(1|4!+NLbx9^2}SB%Q{B$5sglgc^$%kTuo&W^@jug@RN{vdEs7&s~E8! z{A`c671jG#u^!k(JYIJ`n7Z}7z}q0`4yHjmcwV>aW;2e5Vzxg^W-!~Whjm6aBlgt% zqf8-o{LeFGSHt!>dr5PV206;0h|7!@*Ehy#qQf@EtwMs0zGDJ_B-B9-7}D7A7q-lH z^Yk)f^|6XdwSsxqwf$MU2Bj|ebM-??2Ul9eCf~obh#G)8cc($8D4ed7RvchhX3*j_C%IMpE@~)=_=^z zAV{-u&p+NODn0X*4ndw|$?5Av)_>zJBcWj?4&4y=>aFlyBs(sGg~0RDm#=wMXs%Q* zo$g0U4xBFY{Y}63s1HzEf3>*y8jWdO|JMK`ce5a4(kiU*Id~HQZhEs2Qy&n+Kdq1% zfGU#NM@RXr<%j4)HnDVd5+QCb4K+~Ei965D{2}`rj#>J)SKJWt%za)N1TUdJaNHpU z*tFTSGN^0Fo_z!)gb=DR(4ZPTRb|Y>d*;!05M`% zIs`TQAGPM2X7uGEE7S;Zv+gQ@h(3VHsKJbVsV=@H^P=XHeZ4L_f_xa(<&L5Qdaztb zy%cF;4rShwbwGsJA|KQCY4XBo#$@~-AJk3Pc*~=3QT_nQ;;r4}N(0Yyq;q24L*G9* z&aZJzo}0;)wW)V8^U;WVvyysHsQkfhX=$zVE&;{@Q$}sDEJN|k@2{%8G=oP=6`(AQ zJE(hIvcUWA;9YQPEa}@q+yPtxlmUv``U7lH&MmHG29~Z@Bw=$FaaJ@h z!(s;E7pv6m%;=p5w~&RSxz;X#7w?lz#q&*Vka^@{oy=IhEY|C}9fB711N z?Ec4oY=6&}<`wrBD6l>#T9n?np>#=G^59$QlA(shfWeCzfbK~Z_Z2AkzWj`)9{vyZ zp_Bw`c8F<{jAEMC2q~z9d!1YE2`f2S3!v!&Cf^pQggZU1r@yP;7jK*oAxn07yQc3N z{qlpgE0aj6FH+86I(=rc3IuPvK{y%I+2V+FZDb~`d#))hs!--YrwKiqATld<^g5px z-|PHsbA{BJq!>r_72+OgjB%s*@llgl7Y638GJo`i0_PzSa1n*2U@4#?=b6^P$y%{Q z>ATDjdst51SZS&)bYKg%Gg@nj<`+Q!d{Yw&k254_&`G6!K*Mh6k&rBt7!fKj2pH4H zB*P`2QzsEMMm_mM%8$F9BF5)kvBb}QeRYM$K>;HF(V%`v33tSK4Ld#c>Vw0_dr7{% z_Xt%g?(uI^IV#DTpje9Pc*;r+u2QSWzEYBtNai6CTVs$^5RWw#h6PR5gAs{I)(0gy zf>L!M;ihU7a}$vu;UD&miLcV%B2sc%Cgzk3iuX|_!J`)CNKTe5?mYb_*ycq~OD|DK z!b46@8JQj?KF*Ge`fLlMW-lsz6V-0LQ1)hJWBT#MQP;~zi*yUkq>MyIlC<)kaOb~9 zUX!q{SZJ45_1qUNvmcW5c7bnEtbmgXdnMKrbByv8ul=v0mYm2Di$#Y`dFGW8yvdHv zh{4T0B{hX_r%Mc(%3nUcGuOA$1_uMq{rq(7q-k2J4eod@)wL?MwcH?A7VB{0tD zXJNZoEpn&E^~bS3Vg;Id0)3fRwofexgv)g&+QtWVy^of`-u@uhu=Vz|`}@GA011)8 z(A(vTuf%EUX6!yFNw$&^8tycB+0#drE>3ntVx{lKewNu!2NYKHtVZq?iFYV<9%Ozi z>&3^6!p5{aQD%v=ef;60DoWwXE|l!6zi*TgDsKP|Wg0)I$2biNaoJc-tns0IPPJXT zSTd*V_CIL%oRc65rJ~?y8CP%ui8&Gx$>fvA<+`)NJp_|{?uBq0)l|xIRUfxyn3JPa zTtN2JXbvLP?i0HoAIC1eDs;L^`&6hzp=UpFCPaOeQj@$4E{XZYeoJF7JATATpG8&A zFP9pl%!)u-y02yP?>iB!3dtFZ3LQV$F>5oe=5)%Mvc9v=XIhVHzb(A4tvUT;Uy*7D z->aZs0f}(P{+$~j;CuX6^DAS`5w`1Hy3A=buf1ZcFh>8hctakKzKA7mb9MJQtQToE z?*nVS>em~%{fT{i!9|o4$&Fb?7lnFzahj7E8%q!VyHXL6B5U%m-~q;Uyu2^qr(Z7& z_t!lu+E`wo`&gxWsXY^xTl!@pb~mqJMCEWvmaT9SY`h9*FzqMk_P#!3pc5Z`>Fy$V zD7H7Zn);ZN@vVXTVD`@Bzdm3^1n>tMXPqS8^nWsB>kZg8nj8poo!x_j{5ew&k8h@NXU0984_!9(J@RKZqx7{5w_z z$%PJ`IPUZk7)SBC9vwdCq=e#7E)Dhfkfx>3xcVNpmlBdMZlW*89Wrr&u5xw@q2-t4 zFXmpo*NQQp*~%M_n0Wg^{wo{+4pwgTY&zb4M6wTKy)2pPdKtSP69A-PA^D3gDgTA` zxH>KYcy>H%z9eA2PyhFEoXj=NM+{8Lneqs0-rcT@FVLtJ-LzZsw?+j?MX{rwule;K zHXaaBr0<3{lVJl2Lj*T#V27&U-}`efsX44BB1Vsg1}J zNM%1r{o&MUWa6v|C2BQh3#sG}U>~a;O4yW`FGb_!#85Y?_q0maZGhjEB&6&bHaSkD z&gr$8Tfh8-#QcZbrFZeL9&Z{r-5Eq`fYP!Rlf zsd|xz+Ce@H-K!)9jL&FX`k+1r)vVt(i7f8isn8Xy0yHNar<@_fnmgRBVruzWK+yvj z9aT&U0&rr^iMB%a+E^YYDi5S0oiYtBu$jNC_r`O5 zD%dR408T}HFWFsw$yvUBBEB)sxHVyd2G6pGjF<@IU}FJjgHTBaB$N@S`yLur%JLyC&Fs0^8;0}$AIRhv)-+^|1z1a~5J`W;ho zQpTP7D%o0%A-dIz28R3i81~cv-j5+p;)&6LEjIK^Gw6~tAf;4PyH0tyPI)*m@1ilw zxi`IMI|U!u1Y*p?91#fB1l{KlsdsJ>IV-y`yz(vqn-;!z(#)De4oQ}#`%j5;ylRCQ zj>yxNT2ls|{XNt2?aEH7<-acJtLHXehV)Hz>8$VLxpG@emq62|SSmbn{V!t^eu@rt_JZSeL4X}9Y z3q*sg8NfO*&B%c&+QY6S*-FRrYb@T9DUL6>7-SVg*Z5)g$5UE-;to6(KQ<4z{kgoq zSLDH{Fj|;`YIev>PS8gu^@9^q4DPo$#H0)r<|F>I=>I`(89l+aw5>d3)fO`44F(p~ zYYvccsjU9}5m~=RX-}PiLq4=7Sc6;Nu~X@EpL6MbJx9WmhC=qLwd+O2@FXe8(24wg zL=QUrF;JU8a4Ik9W$(t5M6r9TjJ^@a1k+Yz8V$wcicyQ7^0U$~a`VpRrz4TTaNgpi z>=PD!E4%uKf|27yLa~W4F3gjFf=n^A+GmRvnlSB07i2`1EzLu6DET`MfKO*_&rPTf z#fObggne#TgcVS~gN>Z)>(jT;zB1Lf~vv;V=#R z#GY)a>7(8NHQwtTauQUiL8{`a%$4Lu+LzOddr%w{YIvS$K~(7XuW(<{uc;3-wCK*a zd5k?DIMX#j2=MS77UZVQj5)E#=f+%QmFfU-0by zUO0leQYe3sX=|WgUgdv4L5Um$o9LLelVGP|og375%k~e^h5Qriihg(3iJRY7Wmp0- zwm5~Y)$cCqk$slgWwVDtZ;M4^_PHl`{&na0kmYmR5 zcIMPRqgy^NWmcpueAzTs-2_P;v2wZZ*y$SRjbL%b6ZI`IhAwx1mON+4V3#{5Eg(BXh1Y6t@i?$lPouwcwZXQWE}C*R z=S*V1nylu5ULIo4RmEaFT=(LhhE0&bte)WHnoBpE;m~qLgS)J)XCdc7aI+5cyFfT} z_eg%tK$(6yhtHVW-k_F#xNFeM5X;~!yvmadQB2y!JMp%9oyd7eD@4?ze@3VrCgB^I z!(arzAoS3PDL#cmpR0CeHNm)2$PiW7acIRv=bdp+CfMOD8iUKego9;u0BQ&Mt#UXq z=eK1z2j0t_dDk2QSxn7rInEm@#Iwl9(!)S2!=@P}t?14DJ}LfRZ!}c?Fl$zPNT&)&|21ioy7j*NT=r z?FsLoGu|e;Azebiq32s1M@hv2HAijRkIN?~M9se7O$d*#+{FBWQVUSTg`KOQ?Jq4gyx zKyEm4{wcKb%amLXU^?qMHAOrBXImf?(RGlrg6W4g5gV5N*A5ShZ{cN7M7lXt`PU!B z`d4YZfHZ^M$g|A?7s@bFDepg;4?@qJlV+x{hz>98X;4`HU-r|WJd}Gz4L`#R#?n)H z>u3@OJHk%I0eSIY4d$?N+H=Rgjq-U2^gkWMt6J{lq8$F`l{@I(tV`@ZRV%6V^Iz=J zHC$;9x7dckl|Rz))nm^0<=!)IyKhGG;*rFQ&R?49zViQg?f;KK`Cm-wx*{A~U2_lD zYWYog>s0ZKk2KvJ@J;A&qir*7PjQWl)Z?m&UQ_#jJaT@%-6s%*d7IDysn0>+kkngq6GysZqe?ow=9<1~iV0Ba-6~)L#bIvx7W*I@Y&Jfz4?U_WFr7k|0bp+Ks0=^fm)R7Q@!tLxC;W&PReItXMl7>8-}VcXvp2$n2dDF3aE9ACPKI^hnQeQ}dy zySi0-rw!YtK)d-KQf2iCL@oEy&febZ{FjL5=7z|+rw+98JJd$mJ_emKY_%A3u3DOR zmuvo~Wk!Xo#$ysyI2S0;C?iB;=f)od%%`q@yaIJ$)G!xWBsg!gts5$%|)J76ygkym4Od+*3+)N&}!-V?-MS3 zK;?e?!c{!8S~KpbTX@fFF@WU}7wOpQ@)z>YM`+6nv%YV3M60^wS znvjyUAFi8Xm>*yw_<`U#nTNbUdo=VF%|)uh_7@ET-Mty|HyIT2)15Z@aGEfADYx`x zEVeUsZnwt%$$Hhe3crse7%j@2Nxz&~8!PU*d9GnPt81`CeMK|$I;Y(!*A{Rz@te<12v^Fbh2?f)gA)xMcWEi{3| z-uI0X*s|Q2Q;Ckk$mEVqYA@6IbHWQy{7oKOjik%A1xsuh8TVZ{y%Fe$) z>R64yT&nD;u*u}n{au8dD?sF>tW4G*iAlyD+>k~^^JWxxJ2Rn#ZGP{8o=LLQ%~6W4 z0BsHl2A~E)MnQB9*-Mk3+q_ z@)^`_W|0VXOf_p(6Jr9i30VMA6Z_dp%=5m9)53s2?pz~QvS|~O^DKo zKMJW(K8&;aoUo2Cj374S!^_*DrA&yph-mx-kGYKV+a>xn)2#8Ge|Lz2Ui%!#I(ifo z167#x_eNtm08$Ty>gP=b1I~^4Q9oYqEm@pW>0e54r8Fsdts7v+P`Uv`!kwa@OC43dSx*P#PaeVKExCDBP~J^ zDoj)St16K<90bx&u*VF+gnMWNJ``%vOk0B5><*`KV&|;cg_nD=DrD2b3;3gPMvNk) z_4~bB4pjd0bv(rjXr4wD7ESUoD$oP7VFrU=W}P4P&ba=2MvnjsQ~6NUasEWZmR(C0 z8e63xSQ;Ryp&vivJ5&bEKIinqs6%X`r$<+zd5}cMWUiD-tFhHda@U|cMj#%Vv`!1w zc2I4~Et)Hl_I3lV7|oN)MRI}C!fa17!_Y-);SC@-95h#U5)cefH+aslJboWQ2AbID zTv3v=CwJreNA@rJr&n;)aLEEv^7@%MbbW2qOk7op{Ns}-e)+5tR?1(ZE%k-g?@VCr z!r1F#7>|M)Fx4*q$?Jvh2TIG{qL``dhjOB>>-WAU0sIx73b68+vv$wNT+e|)a9Nmm zaEr_Jm<8oAIrtcnFTK$yQmVfhgHAK=`2~Z#Q_a}|+HNw)>_3-P?Q@ZDo0^FfOg9zQ zCKRhBI}HZux*=s2W3Kuv5sSRk|0``Xh;LgKVYmhzs8_`$t?@0A&NG)=Cm3&@`EcAo zDAs*qP&Y$msGs2{dWmEvb>v}|EAM<#MGul&o7ol^R);RIBe%oIA7^k6$c1BqjeIO7^<27v*s-f(kJ|#gZ^^b-CdHi#-F-L5mh_Ah z!T2nG9qPae&3h>pK{kjI&b$lynVrC(P?y!m{(ye2Eh*&9s)S8>71UwZPEp(LB#3UA zrDlbG;#C_>cS21u{LzId2^3YfIzgW%&+)`V*_ zHMZov3&ZKXYq*zz|4c3bq;_Q$XES=4wa=Jmh%yIF!K~OS*1!Fb5wNPsmG>VbFTG*>%q$E?{&L_52R8+TW zqvX|p>CLjlU9VJ_)@(nUAk;qo*W4ZVczO0Z1L{1F*?fLA#IO>?U-az>MWjl*QS-0g zZC7gOWMRXe^YfeD`iK{-2YhzP59tbN{57xijWSwZV3= zEXxsuTl(-Fg~MZ*E)2zRL!(?%?3<{jM(S3)LJ#JFGWM_=wOn}{VW}-*fT*2nwfVRJ zv1`L-BnFYsgR&s@{=&%5yxAwT$t#-BZDBRb-(nbg33ckhO03DSkB$Ym5=wVDfN+oe z8C3)aK=))2#!)s4OO@S){q&2G-c}RnZ3rgj^ zB4_fH)S{)swzsHJ6RYkHP@m^it@SUPzP7#jk|Mo%ZT=wlJs*u}C6?iUqlSs=@Ca;E zm7Va34*SH!@MgwZ6q%gS@AzxIUb=HXT4c_!m@5(0=}+x0Y!wbzxKz4A_`&Fb0v}eeZ0ENFeC0F z+~-dN8&ez7cqSlH!*roH0ojm=JF~k9p$0E=j4O}Q#p#4ywgxBkU=06YW3aBAW*vuE zb%rY9GY_ji9iTtmR3y4+q5^vFAcnOMfBuW0PG&~YMZ2%+;^9bCmbRGs)6+y@5P2@7ElH9Yh9Zsn8uKfM) z4N^1w+8`SR#k57=j8+tr=)p)V=9?LPi200j_WK1e+@|PV6kGq%CX-ZO!WGRp0&@Bn z~p!%1-CgHcfsZ_q`tKdNuOjLP|wabxQ<#WEQ{$!tW&CydUlW9 zq=t0TjZiLW^EVtc@XzGcjfyJ|SU=A;XS+x7J}kFz*~;-h&#R#kmR*oSs|=;Jml>#B z(M}4&vA2(G3VaRXeS5lrRe@(Sn(e4s$ zFF#RP+MC9$o2VVA4=B3S*nSN9+ZYHi_t>kbD!$U3O_NdHsH*i!U>{I3Q}I7v+1iXW z0Ba`yoH{>-#6zh*6vW`MIX@75*j>n%{cq zWBI#P{XI1z`o`Pzv!%l4OR)>F8@}?}fr(aW2(eqPIf2*<-3U&8A zslq$2ib;(Z1^uMR{x+{zm-@_cdb81DU1O6a^u~KMIC2f^)^mhpTc*?}Z`hvx=|SautiX<98)sc(_5|M|6;+xLTam3DX^=kl9v z6w$DjOEYx}k^S5PXV=q~V_4cclU_3~XYevz7r(7gS8N|*n_Ty_nSs>g{-CHX+Flo{ z#r>|+2A*Te?#ttK)&n;do-VzUtv_!+|D;AFf=~cpzCE($TdlBbiNE_^J0G>?(49CE z(TeRQiyR7gLZ$petdS`Yb}!kcC>E}i@)8c6b8mxmpLE=<7b=Y z;pSfGHe%6Wr`@IKAcSKR7_Z8;{^?ZK8eU?4(sTIZMG(~fE=DPfy%+82fFjNhCWo;8 zRDTrk7@7 z9VmVJ>5=#6baKDfh_isv&Eq36#uRKw;309^j%yN74pOlDN+f)$uw-F!HV)7*?nl-| zLN9zrs|JH)dwd_$$O`}Aad21j4h(>v6bk*-a(%>Ky1+LH?P&!@rOp~biKGlXtA{#R}XiM?p8Mise) zR;BZp)`Praw`o>gq8JQNNt}K80>I3&$d4L)Y@&_?=xhZ%;-JQwt3)tIU+^7scmEdj zCe~H1wH%y5x;&a9mq-4|_%@Zs7Krf4GHQ9sj{Yx@Ebbe~R}W&xgu_p80z@3Qzy>TY z246eSh2Fyd%0&`e&CHfvxQPy`ury-Q*G%^O2nX`4x2WO zaDbZmPbDW`0l`i3IY!wW0DjZ$B-HMB2G3q zbcZ$EI2woc)<1&QJe;b7@e@j@RSXJ>jj{t)G#b2`BANF6dGk~6GQ?i+f;O?}Ee|P@ z=hsPjKD-_K_Pjnu4shc#?hxDH9cae?4Lg_U0}o=aoXaNQv!9Wv|9R}&@#5Xkx`Y#H zReSb-Cj$QeQmK;fuhYx>3Fj>fwH;;K{OZB74G^7ZrM9s)q7p~ou+;M!N8ps=*NP24 zW8WMsUe~ov#?Kl@WFLIE0H`%jEz+RCn>adlSwnu1OeC#`~ zM{?cCisiFovTqezb$j>kC*xKMZJH`x)zaL{2Xq>Sw>&s)03FrJzXm(2s7eA|=v%ac zM88nMb^6<ZU;T|AO8X zLHi8<` zh6k)B6+Wm`p;NeZWg%qVw_MpNQ(%lYgQsF}&ej}uYn|%F+F}1NuxY;?5MVBuL$KiO zcahGq+RJ7z(Ay=mEf`O26@-b97X8%d*5wR?_u4%|T{%=3?+jS>+-YT7^A*(!iWT(ltVpY z$STld$2Ru(FR@W);d}#uUTC@`Tvb~N@c6WYxtT6?Ib!a zzazT)8&TDZ!pEl#{*M*a%c^!Kuu|9WG#ZhQRHMd1J$tjB*XmTmW-uG*luL&UEL5t4 zn$YCDJT`;SzLzB?^_({529AXHb7hcL)TVglhZ?Rq4N@yhV z#nLLtM!{|(GRi(N$^8fCte}KCK^YzzO_C!Jf=o9SsgBjZweVIAJt2sPq?%&8(LENh zVFnQC9=L8>4;I7?NN0R+f+eF)2nYXSDQmS!tOTjoBud8i!GVFf!qSvV-yt8ZWF0|M zQy(0^2lctMWc=_YzpGIZmCfKHCPZ9Bv|qqdJNY-Zn0$1HB8Nw{*YCS+(jtF@avtg} zJ70u2Xn=zQawZ?EOzAxJV7K)~SusQ_3M=A3RMnhEkW^xUFnO3ZD{T~cLm~1slTmZWtDlKK9c6eLLj6b>VdZ3AiwH|p$Nn2)spF?XRZH5 zF=L3Hiy350K`lr3$`}+D3u3*MJz!ku>&KXm;2V@nH>#dI$r$HxPYD}D!Kf8s{KbgD z#K|o6GLhn&t(@95TO`Q8WyJ~%R_x{;-e9!Ov!9z8j@6pc&r=qrPZorZUIjyi_%tSZ zhyMi4j~7(2WiJViE}j!Tq{gR$S;xwWZ&)s!A226<5XT^8&$H?GDPV!MbezboNFUR> z4Bj*Xd21ivG)=Z&QeG?dx(!gLM^*@{9gG`t^r7Faby=HDG7JtD?F&zk5(-igXtx>` z5DQMuZ2limwGM-too3dFFcv-zsMAs>uQ0{@sMjeL$y-NV#M+fsqC)WC2g&$0i>96F zvqHE;P{nW*{0DR4EVGQ0{AoofU0lKr#Zoa0v}v6UF<^4q#=J85|~iBkq!PwEW4 z6ncrEQ%S{)kw>vVtK@l5HM5#G>4iNY#DV2@olmI6sg!$UEk$805%^F5X@3e!>n39uD*IF!=u zaPI0=-TM4Ufq?|$WMbo50i6*1#7!Gfs91nae6)aa7LJH=s-B_f5v)+-#)68HzH{qF z?iy2IcoqC5S!4Oz6|(Nn(IC~a16w7cm-a1+`TTT)B>nzs>me_K=L`{pqF1D#WSN6D zg7NC}7ypd`Ht6_jW|XFI5-4ivnQP#-@XhIJr>wkEnL>_^muIMkNZ!briMRj4p7Fmj zpp5F4`O!RgjO*C7&peBpJp?4b|L1@t+;3ayQvQ^v&ipb(^`U&SVE7JZs&{DgqtkjRS&qK?AgF?XBV9REfK`NfYCS*3-q}>Fm z(G5AYj54pd4!6hX!$7cXxD~kiNn|kiU8dteF&t*s6ID`^LF095M~x6X5qcEHYnCkb z<^h@32?oCy`x)12+IKFhFi5MC5Y9iXXaD$PG))JPsGBk+N8gvQ2S3K(fZ!|j+bxiP zf5lT!dzKq{<|E>>3=X>V!(W9lh~$$CAm1)|b$=v+As3yUMse8t8A{7Cw3?qSq6cCu$VS)G{iAt1T#rW(7*jAQd%!G%P09 zvAHR({IC%7dfZ8d1EYK#tGxR?Z!;RrH9fhH(3B~?Nj)y${`V0x-=#4jZ(FTv=X$4Q z7Sm^qmTEH238eh0nV}oyYJ#j|e!GSlV`?Io+4wPEHwzJv}2GhBN!Ag{dV zA?&d%3;kB@oEf`&g&kZEWEE0_DFb8i#!nN zKB{rwr~g(eQ-PcIpfS(iSha-ot>uniU5H(ygli(Fqb-9Vnjr<2}W9k z+eL801xcuDCilu;a(s#?c3D6*&bFv-}qTtt8?gt0Bx9fV8 ze|;94p_M!tGH2;c4c0!HF<=J}JNbNwi#vcaVg7#tV*#B0AK1ZlB_Hd>3@0Lb&>HJyqI#wdrU3i_F(BH7Rt|20OV+$560{Y>@M3kUEx3yphI+F59tgM} z{%|1PlncH&SX1QMB@@{X8oNlX_Dr+H{5?0!NOr{|0bM2WZ04oe=+Y$@vpYKe>;?R! zV-&mMi8+zVW8*1v>5`o+ve{U;%fVQe&hnHei@U3kE0?^>7MYHPC%WU}ZU!AWK2A>6 zR5EgOly&{U&YJ#(liwH+4L1FfzOPlA%CoZX9nnIw_kt~N{e^5I`pA1c#dGqTbRx;@ zy#t=HMa=Q_te>n)}0l`cMB&IN-6P5sSI>*MIs? e|LH&dr~mYy{+q92X?JXd;!gkRKmDiwoY4lw(<;#b literal 0 HcmV?d00001 diff --git a/docs/docs/install/img/truenas09.webp b/docs/docs/install/img/truenas/truenas11.webp similarity index 100% rename from docs/docs/install/img/truenas09.webp rename to docs/docs/install/img/truenas/truenas11.webp diff --git a/docs/docs/install/img/truenas04.webp b/docs/docs/install/img/truenas/truenas12.webp similarity index 100% rename from docs/docs/install/img/truenas04.webp rename to docs/docs/install/img/truenas/truenas12.webp diff --git a/docs/docs/install/img/truenas05.webp b/docs/docs/install/img/truenas05.webp deleted file mode 100644 index c5e451770f6066e5ebd463bccbfdc6f2d9312911..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9106 zcmYk9V{{#Cx3yz6HXA#Q8r!xT+h)TZ+fH_Dr?Ju4wv#s6vAy#>@AIBB&iC*Bv+gn1 zyyjYCEHzmvsc1?F2yIC*6)hDWvVecyiPI3dkPI!5(@;Y5*1uFMXsC*dZ%>jS2oY@@ z-|_MGIxVP1V&z$hjA;P3X78n7?bq_gnJZzmm!PBkP*Uxq@z~z77Sxy7eYAam&mHXt z_Y+_|U-XmyL;l0+$;sGA|Fd&Pa?!89C+gMdL-;!Ho6*T<+5_dC@TJl7XUjVTfDSzT zDZWU0*NZId{HgjzdyV+P|BeRkyZZ_mMaz$!vr1g~t`Vjf%Ew(rmWFfX_gkAMEF ziTuS;SVZYU$EvE^a8snd<6kU`U&We2r6NjTa|*vzTE(KhycQ$%Bf`>{x_)-$Mh;Ju z7I`t{v`hHY@4Cd)2Y3yGgQpxoc z5(Nhug%E6$)B8U(twmr~mNq%LLs-44M6vPdYIUCHE+cNW%zd=P8-&hb=14lz4kZZq zSBGxF`!QBNu{CT9_Pr(3twe_=%8iaP8G^k3vJi!NC-XyK_@E4XuMbt;e+VhvCdK6q zjQa2&x3fp&9NZioLTD=hflBb{&W$$yfs&DOgWPx;gE>0L616cvUuPe$H5-j2Do1aQ zA7QwHxeil)wz7AqnE9i?y!2;M)(g)W^*^w#UiUGHV?DQ1VXT_;aflYv3I1bfeczd< zUsKQ2eRosuf02t#z|IsA>`Hro^I`pyMEE0A`sJhl)(flS$Bpust!HZC3uO50G-4`w z34gGBKhknS+`sVyDxD{@d_`X-muh?daE^Whw@bRqL38f+uW)&x6Cn#`_1H5c#PW|6 z0LayJo?fi{ihSRjdRkucIR&UY3vo%vA+?5^C~Lj`x7Z*3KiNxF2BdpU(Dsc&W#!J* zHXtTgDT~D^(UUCpkf3<_-zg)~!tv|0_`1GZu+P(92`$XLNIV?HV9vU&=aB^Xgn+iK zHP_->cK`g_|LP{q2PG9p7BAS)px%iwP7JUWZ3UsF@;_pFRdg|}u(Ph<1_}7O9pKXE z|F3c%=V@sXE;HN5XYaS9?dV99viD=!$$~fskFg1TNz6B8Ztur`bx!jC^~#c#VTD~mdv)_q(bGE`gw_8!Mctk09SysB*IaJ!tKNgL&?dR@!%A*gzA-FQdV|HhBWeg|Nme$Ra4o^azV3 z^E{vz>IOf2-y z=(9+}Bff7Cj#>Htt)#2h?PNbq*pG4P3JtAYMYq70G(^qsMG;niP(!g(KWu@~mW1;h z!DcyX-+h`0bY)YI|7+z#^Nir21{G%SaqZ!$3X5=atxq)%OY1#X1*B4^$YWVdqq5iC z86v~Kl$TkW6^<3NtYhl`MeyPH69NKU#U)R8?e;n2hkCn)5MpHr24nAUH+hhT{i&3< zqy=Nakt>PRWjyiSw6(ntD?&;n{BRPQfpw?c+l=5wR?=?bLqeG5r9D_ZuG0-&5|pd7 zO3|vsWpzwWVM_|K0kE(_P}8GHh>-?2?yVPEyVmH;R0-4@2+*uRAd`mAA#$K%_n{f{K-|wg+jXhk6+s#C*n6 zp(jJj?ou{pjgHKbG4bbSwTPKrV#RGF`8hf5@!TWTSci>j7|3*%#u*-{47IXnP^*xZ z^}yT1wWA4BR@D~kS+I|}+A)+4K|g(bz!=`Mv44_Co$Mbh#$0?uCkSU5zy*Y9huLPN zQus|J*)}fhkyADUXJimV00SW13UUmF5h*-f93f6-?D%94$;8v@pZg#s%O~&gmT!x9 z>c8V6X=cr0DIVWecf~{T#WBMwFTh=We{4gi_96)uyKu8Xy*$T6UNh|=`(SV;1%L{3 zCd~R@IR4Jmnh86!as&(Ez%(2*^l%!{^2)qgnHuDk=32i?F>}kI6MGKV_m3L#st;M8 ze`qv4?99t@YSM@Q<@9l0aP|>0lL@Z_#2ORY1~$Rop0d&?^55Vqs9_8DW9*?#&qdal zQG`lbDB+HG%wJQE79&$5aVki7ghPpg z!T;HyMK&3n^1!Q^=|kXKf6_KU9m<=*@gU}D+-c@FUKRn}1)uk|1lqcM3_~Hh_1iR1I^l16wNnMXhg~|I&sKVwB9bs^!d8N%DYC8p zyWpM$I;~%0kz2qEh}yMM{-ku%U=L6zJGtZE5rr92K4-%Yzi&eR5$(qSFm4=|sd({fUK_M1K z@m!sV4yqi!upv36Y+A})j9=;7wE4;lQiNPJ6?VzI#B2wS2Ai6($LK)Rw?@b<|k zUgmjnUGv!U)+)qCks!Rmq)INvuxVi0j_KibF{XY8q3~X5=ff}M_GC$F`br@U4-e5p zCW|PtGReaIHTdS{uSfhFjz;7K72x4|p?C_2`mO{-^-#!L=B)nh_p9aRu(o;qYsz8w zD@+;u1LpjD3W4MK`>re&H&WaXFxf_l#R&5aGx9g04?6Xp zpQsu(FgDo!bgu+4rC5dzoX=}4WmMA-es|rNCpRli{wLElzR(eozs7-4^-c(t3s5y8{v@C>DdsgLd2*xrL&$=*H;7|7it0M4FkXw zfqdHMKq7T*q=ieF&Ng!}J>s>#*eohM+FkjbzT{+&zd{4sW?UO-Q$dDvQl~YiC&T{E z!GrS7+o^xj+m55TZHKM@gYN`f-Eu-7%PI_#zW4qsRuE90i%_hfJpzYn8LCTQJX82yENrq9{o zF3O~h$%s9D?;FlK5cGok^1M3pyvjR*VtebIAAa4 z%evQXa>reJ0)jLuT3St!-6ekpDU$e|@T&zEw%yt3FN6iriZzr9b@vzFQC@~bhebwT{J)u+69F zzCpQ`?sUD6Z55x#mUB4T$w3RWEKI$xuW}{>hVBop z9Pup}HMY9QeZUe2i0hc4u_r=cYNr{a)WK$HA?aOI(Z^ocK#s`cGh?&3<9U28 z^&4l{)%E-Ot-=b+^TfDa7|#JF1&^slf~FSh$e-os#`4Tc53j0|;S3h5JOpp;y5uq7 z-H!?`yh)eHHHRU+ZPyu>l*RxSYZG+d%yrl}E~dDLEc(!azIxJ=o}o08Gi?`7HIL!_ zmsZt>e2Uo`=fP+D>$tn~>tm@d6joZYuZbuA<`OZ+if?H!1fMMOh_~xl-|z+7&09MB zt7q$%_|$CWRBvT@eS_c`Rl1$oMa0|Z@$b51(nVi*K?G+`g*GV03XZMPc8=+4fMTBP z%r`_U)RNw)NgZW}V1>JCYC{E$SomKn=jC6O5-kU)M~U9`{bv)*(HWv-p)eFa>*=7f zOo6PrkVRhxw^4%3$}QiCZ2!oM37O$x(*5=Ka@EH#;4C-M^IZ6yHDvGsdeg86?@0kjOzp9 zS2EE~v0I@>YBo%YAUt2w1nL6Ig$c)rl*8>Q^1-+G`T=(VN3JdQ*U&xjbSy(|Kx z<$*aeir?Cs}ZoH;^8Q}HBhr)X6e0%^HdTZT*yHgF2OBe}k_ zxhoHRa-Hg>{qV`5qQDKN;pnUxU()2x!V!1(Jd@blSoDkd+T!Ad&r)R8r-C|9)+JEc z1a;Vwk(6&vB<5xe;7MWgn!!88VzuLRrKd3rG}MG6^bTyWU`to~lE8)N!A;{~?dLOp zcTt6Wld@&ft~&UaVxd5;sykm(3akB4D=1z2o)*N+QJ7@Q)XeLbYuH;pk@i)&BvVT# z^U>d!xEe{f%jQKo07nM`SJ?WK<>`vNCzBJ{8#`HnX*B0u6rv3w=ADEeHa+RjmLVDm z@#q5O0OhN#b@`TiTug>{8fs;}Vts{arDoaQXGD7KQJ)2phtH$%_0Q8PnhD!Axm&(- zZ5ij}OgP}}8L`jba^f7Yz>9eT_`*GBYYRZzyH(NRJpy3kzXutzatQOWKtUcz7{ii2neZovY_@t0*AQJj4c zq3yH0{e!NwH%5O>Jr?Y5P2U_tkuMjE`p&viV2=vRfEk?TRO7n9GV0a5ICaulabl}m zeqvV(K4sPH6#e@kn+S}|1o9gNUKS@nq`$6EY<7jW;!Cexz0~EvI2i(bLvzpP=Y^@2 zpM@eicx>Om1d5z%wreP9fOBgZ^ZzwljG)W2kWuu7;*_Kr@?a#SXBR-4wx5sOF zdAyimgs!B+0Rmgid~)*|4(u=%>7I$ycdYqrlcS`gh%(TVPKC#xblU1^*VHwg8|4F% zsHu1&J38n#`(AYi9d~TOT%Gfd$`W5gW!tvnr68G6!#Nv<>4Rkn^<}=F0xm+|&V~f1 zr@pfPj$TXp=g=THZm?s6nGM~hEb%uGLL}Hd=m_jQXO*uqv93;WgL@u9@B{m8`#uj)6_5OQx#vY){>ciqL+~G z`)rmcC8%(;v}O7_Ah#KFs8N%?v#tCnf+#XU*(zB6O8z_uFNHIt`+dv-azE;bNEKg>L}}WT#>gN?ukGpQ>AIBlJm6O zNAK2JDFIHsk5*G1(;rbsKO)*aJP8-z?ess#s5c``qL+3fY!Nb$xAQR&6ueZdDGH@v zP^B}QnSF2UwE$=jVL_KO=vza~=6dp+}5B)fN9%P8l_ow{o^h5eZ^9N2vw36+Ia{ zvoUJCf*Od?R^n}%Vg5Oj4yYHJ=mF_x=&$JUrhBKXXCx=_c zS*Y9U)LGX}SHJ@sIXECVn^d&J4n9{J#=enUy9uC$8g{lhi6HjJk2oIdB$m37%y=j* zY>ol5P;TT^N+z}f#;G_5!B4s0$ z{a)BC7+qe^<(lBm%k<>PoJLW)wzE6_iR=GEYsP?X?Sm~cyEFXOGOAZ(a?@oeXip*@tCo8Y_| z1RxvUK02bRB|DY;h&eJHqEghSzn^B`m+WJ8QZljD8h*<{22s*$JWY$3BmLGqEj0%x zwUJYfv`1sO<`Zo*)%>I3ij&dCM5uZjhLGV2RE((_@}F>I1kvyXuvjkJHsaQ-u+S^z z@J;(d;OUPBfSukkAU$)J)X9Oy>lJ|7J?MRgbx$1>%|D`pkF~6Hpj$o`0-w=)Q;uI(6#%$D(qI%~2?}&&Pl!yz;kCeGv&vPx>C+$Z!BlUdiXi zcYhGlO!0wt=(pp(XVn(4TWx`>*8d2|4e0qPE3^RXD_$UH@X|jT{_6D}%)%OLJR&F$vS+0gv4rJ})XG_+ETfFrhIW8RYPPP!*d$Z!#v%ISaodGhud>3n1kSTyS z*P7a7&BZH9l0`s=SpS&o#~`O)Mu=%Qe9ZIF-=*Lj`t80mUid8k0%*ifm>}(taaJRy z;s45<^(xn}IhiVF>76BKrK#=@E97!qN?d5scV}X+F1cW|X-tx^+UK@+gGZ6pRbym* zz4P-Cyja2djmX4umaUe%h@CI>xABFLyScKZNF$=S%6Ds+=Q!wovnrfBe$UbP>1!tj zh?}aMwof={4&s3oYYYfTT_6%7+8=BS$b}& z>0bF59M%{}7h$D6uu*o6h}+LK)_wYRS?)6rr@_38unT zjQD**ji2D{)F;8=nL!Tmtj5M2{DNp}_n4rHYN6zH^O;jhT25**-9Nxt^;fz`3Sjnp(fd2o`KG`+0iR^>;IGPyQZJ8;xP8-fuOFI`%S zWu9OfkO@|A@IWcaK6{L`sb4S5)?Ks^YoC=_5y9Ff>a=lzP_a(Z$#pLBCUC%@&jnf)fg-N`23z%OGW~zz>YFe^{WR3j+r*EHMVX7 zXM7!t*r>>$xXqX@;l>`iueyqgnKjw0<=Xf&*{68MT*mUm5oIs=JSsuap92v`9uppl zx&uw-W;{q>67^nYsV)^NN;Y3RN!MRirK1GC28EsTGC%P0r`8hXr$AUrG|EotgMGet zGAtdJ)hPOUh@zc|MMm{4pykAkAAps;kYXKEgEU>98)AO0B#*>Gbe$L2B+~1LNO3o2 zynSd7u=sPl=C;Y;TL(0~04<$1(S0xN^h#eWuJImefiO7Hc^)<3W1S3m*iSa0 zI4d`NqQ!*ri2p4`F8031G@4zWv(B&Tre?1i(f>+JCPuwnizn&+5EJ`Q5aE_A=f;v- zrv+530&X6x_AR=6;oZHnrLa|8#Qj-|zCtCuCd;xDSxTLF zNC@n^g+IY*cKG|3&!Ck3I*wq8SD~Lo4+li^XJsDX1k5>VPk@j{<^B4F7z}64c;%+CgT)HC(3*$z7r6l~HBqt@Dv?O=y&JcWSa zRcz>sS#d!LoIB*42Xl--GE2C<-16@*n&F~aal|$Q2kOPklubx$FeI&l10TE39{4|; z)5%6CJ)&_okpUfEJQ81O?MGWkDkU3k5?ewH3nHIdFaoDRV4DL?s1W&!msMX_s)&jM zQWTT0u~x9_8Hy2ku#^ zXD8K^^^_&KG3)C0L+7lb-+>vO{#Bp83%P(dfcWw&<87?Gn*EoGrMO)Q4A9!#!FRVN zry63iTJ_r>1hrG2N_z4Oy;J4jIsqbs_pO+R@^9;{gqE3+hpNf)ov&emJnPm)Lwf0x z&Hb(I^85WZ6n%0SBo!3M)!{LGm z=)%0&cjKw)&iGUsM~&effFuoWM*(_(1g{S|--Zh6&e@c3cHBzqNsFC5Vo{kRfuBlx zIzZRZ@NK+kl}H@D`!EYC8>b&?UgY+>_ct}^Du4UVH04$+r_DLUx9X_fN0u9U#k3p% zm@~HI+<$&hAVhpxpLu#@e8F18Uv_b`olL8nO4@1hu8el|a_Xi-RpEilI(W{LKG!(t^O1I aXvfk1iZ5e3qu=jgaMGs(9z_BnApRfS3DbQ5 diff --git a/docs/docs/install/img/truenas06.webp b/docs/docs/install/img/truenas06.webp deleted file mode 100644 index b7c7c51aa46024d232e11f410030a51c2d4dbc80..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1646 zcmV-!29fzvNk&Fy1^@t8MM6+kP&go31^@uiF94kZDzyQK06x)Zu~H}{r6-}%NZ9}y z32AQOa*-SJrSe~5f8=}*@a3tGRg7KqKdA4>KH2`$$zJmP=YM7D1?oN7@B#TR^p5h6 z?fn4yv#f7G4<&!^e0_ag>>2v{7LoJ$*mAs!5CEX7#=+1aiz9#_(x+k~Ra9R1N#Tp_ z6QFGr+eD%5y@h~Z>gfBTFL5S{aTbGJJfD;Deox8pd#EuLkZpa~b`c(HEvf&?AL9@I z2+);gc)@6LVzT zo0D>GP06`80qXme*-C^fi9=4*HDRRTLT!kT@*W8n65?D-iE%C^kn*Dm1j78Tpod*) zyH^jofs4sDV}BZ4_L2cZk*$K1`A5Sa416y-EkKDbzMcUt#2sD3fJ2?9Un5O6jP8yb zn5Bv|9(`u0HcZDnc0<}0c7O~I8 zABH=?SLVKQ#h75<*7C0nBFh=`=Xe|lNZ97TuUDIrEn!B#9-VH4qRx6s%q{)b-GmLq zZVyQ23wcX241LZPRB%aWe$wa3^2G#kWkB^6q8_oA+(?k-kYRD{M_+Y~RuS|A4AJY~YSh`{)xJ+@BJJkZ_R9NR zC0QF@NjwZJY1-vG=2^dSqksem=?4O)9~Go&-^4$_O} z+$w)GD??<)EW`^qjc75JxYgrZ3$NPoo^2)vRs(uTThM}V=Q91md0x(P#fZ;hTi)jf zsQck*_Icjwj6i~!F~jOn0~>gJ2J_qeJVeOLuA3f;e;ak#P!lWOnjN3jQ;w-J!3p6# zh40GjI!>$XrXTSu7olxZdEbgPVW=3_5s-Knr)v1;5|jHtnPdeC0iC*kve(wi8LcnC z5ByemlvEYhgRMmEQ} z)i~w)j$w$uX~##d61_WH7LB7atF*DYc=_jZvBcH(7++a>r6{hq1m$b@EedL z_+lbk2&7kf@c3MxkE!*)y?tBf+6&>YxEgEv*+bcinfzya_-o?MX##y?t+xJvZqv5Myn5*22HwNkbWzOp`YbDPH(I zLbA;QHNIP-!7=k|(=wws(99ww6REY&S6T_~cV^Z?=?zor0JdBMgHCWzFl60Up{gD0vW{hhzvQb6D%Lk zl4WrM<`z)D4;s%kgA?sPlON4CsoTueKR1EmC(*UqdXNk^{h_ReDe4|)4v_^e{!X1* zvpGs~>)d7RgV$)mgGU*YC+(n{bKZ21G{ujm9%EUJ>>uPG56f6u5kNiAUa^(hsN!{L s=}2AVC#u)rFAX%656SLgN24Br$#GB_IJVxvZ2&l6OA*G@THOEu01`Gk)c^nh diff --git a/docs/docs/install/img/truenas07.webp b/docs/docs/install/img/truenas07.webp deleted file mode 100644 index c08e2bdd9c59e832330d1d13f2e3ad6f07f42546..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19086 zcmXuKW3VVa)3&{A+qP}n)?T)4+qP}n)?T)4+s1p{`}w}(&rD6#q>?k8?oMY$Nm5KK zR0;q2)HfgB>M{^8kv&|A*Ul>JcfL|18<|thVnkt|-KV z>dpVyn_R_N5B+u6%eE7T?yX#-H~N5D*RjY@Sv> zvuGE1U^(NCM)2Al#rNto3I< zgiv&e{Jl`{^*g4WdIK)=inAY#+gkFChe{Uy2r66UcTxQhmD)+5*)|~47SsP>vQRO& zGc7HrZmHN1oNPFTGuSzjxxHRFm&A+p2PaAmo_n0u*Fp&sRHK?}kp6=WaYQ z_`a_aN-3qZ-``$sRw9?IqwOf%p=4UId4N2xV&a&;C|;>Be!%OweJ)|4T5cMlQ~zVK zQeNw|4bY#><4bB(_Wgf@P*nv2-&*{geT4cEdI!`ix;TqI;{`X=D)y0Ws zfnv=iORaAY@5n!qOiiXnbiHGl*|CdMHE$M%vQ+wY;w2|k~7~;sAv=H zn=f^(^jMd^G%ZmBi4eqd!tY)vvA)TJ?MDMcuY*=hwxMyNLcRl>NU>hCL1G=H_!Je# zUk#R65ycs3)({R%_N^;Kk$Z=Uv}fts_6_!IaNjz?z2gz$R$^I>uaB#uEGLO}r9dAdILuFaq-G7=X zyC}|-?;xZz40P`L-XpxAfg1N@vRwQdccsToWVn{(OX^02YtW@KKCL2UC%)@_ofxNb z2NY@>Hu@+e?(7v%%L8xh_pm|8FRO z@fffc?R4F#OGu(w)Yjk3m@p;cy|6?7Rx8s@XW?26aM6>4zqKyZKiioyfNSy&>+I%@ zN)CJaV^?Gfui2KUudKzq4sl52x}XSxc^Z}cPnSl(MBF1sq{Nph@}&P0m<5+mXH6D{_D-2FY7L0S_oKt^u=E+AP+P|h@M5uj7%ixP zfxWMrfO8t&Di~*mX@J;=18`YA)&_Zj_{&Uo+`b5P(oH{@;W=fWa?UtqpE+fMZp(5! zT$n^N3I!);v?RY)i_VOgM;jgy4(T@#xQ1IjvpxRnRYhKiSlW$jw{tn}_W$VJHUFz8 zMMMVj=*_lYDlG*iW-Pm9aAFS`Trye(TJ}#hmD1kihv?d+@Xg+{U)O$jb-tX;w2a z_YQlTR}veTCo(!jAM^Nu`xkd^nzE8^H)k^auSf$qCQkp^n}`TslS$pG*GzeGSijs0zDh`opA=GXDaFMa3lRY6gB!8n(UuFZj4>6;56Chbr=`BwbD> zRa17tys^z;x5@moqjKjSEVMto&zcxZKoTAkHz&r)tSLP2;JFh2{88jpaMeHC*xp_Q z0Qeai0Kk~Oh#(cm>jM%3kSUXPZD@1_jR3$Hk#S8c%h3gql0}a=1lsX=i9ueXdY%Ty4C9MuCWY%_7 zAP$MqS+mNb6ZzHK_b`mmQh411OzbM@(le-Z)` zl9g_0Ic8^H4|$AzdMFDfJsELV&~F4Lcm?cXcxaxqlQOvZ{p=(s0VV7kw@+F-4EY)4 zqu-AHghiA7u)ZrHnSA*4c!yNwYvC3nu&zHmwUOu_BDFQ)e#4f1vp}CcMUJS z`Be%lH$%mfPU;IsZvMC&rv~>CfYDG4x3DIk0~^$@h+HQ*Ap?+tNi=Zebd1tXBi!@8re1j(p?-jVKkOv2f)vJD z*|y(j$!pZY9^U=H?ow6sg4LH@irc0d*-R=1B^_NkMDEd3XFQGoh>th?#%iN22XN66 z&kiyJIT>wN(LsS2CXwmq1_}?Mj4P@Sa&-*_MobJv?0AV=wahnCU(f6~Ae))1txP6p zeZ|cyVUnLts-KCA04uyj)uz)tOg(oi09%2agiOlu*Bc<27$W7%2~jS3ZkHh*Nydaq?b%xMV0BY@1iUzDuET zS&W}j$ea39ZA8f!8%zdLd0f%Gu?=q}_jesoIi=1D;|kr9XiO6gL;WQOz%F$avvbl< z5Y3c^bk$zB@$A-sC?#X9vgo_D@#XnbLT^o&-2*YuJ^9u@@3|%n!n-VA<1=wp7S`|z zUXg5?yecqh1t{-lOlIT4G{wUz$-pBsO zrQ3nrJ`vpm8Z8mk%CCMu&wQIVphV!1$ zZY!9TG37;TC}tnuK+f0DLDP%;+bHh*>sF~-{%=V6QxeOz>F7AG`Bzv)F+Ty~iW8h1 z*JRC;&)z8%Z`a1Fbuo4G?tP`X;-I5v1rJ@}RP6Ezn9RA^8MVEI4F2iXa6f#6X5meH z!As<=Z&20GqPbdqwn9xUHm(6J+=5eFCFXVTs1h$>DL_{c@1R3P z{K+ol8XWWb8jNQXWLH@KnUu-xwv%2EWJicBP@z(ZxJ6L_t~lPGW-;OmuHuaLv*TkZ zX(lJ8_fs2#NOJ^cTMFI}<3S}di=BS)Awh8a$u4sO8lj4#0 zYQn4{|5DFEyweV*mzW4mT+YRN8WbB~YgI6%R(wFO?_Sl+Vb|5Y&{L4odQ1VMcM&vs z-nbukbvG`E?ch!gqie^AqeWLT7Kq3W377JPD!o*=ZQ7`$`-J`NcJ^m|U7iXohIc@O z;_WU+5KBw0(J`C5CS;0`ZB2a3QE;~EVc*|upN!r9<- zYWlnsw>-I2b$lm?z{mLxTtWo4jMmikSV$r0q)=JA{D$hqnPG;ly--96`XQw3=?6QX ze+ZHLu4FLk4iJI#Ar$2PY7+^l$gSCN8sV{y>~kXQ_oM=*Q*bumn7i-4n8w!*1;h6| z7)Se0Ud2xYiY(6v3y{P_F_5qmF$v!s6T3D9i#xIV5hM~C>vL5yyS91)G_bd@H-qvZg>u*!WMKbMA?p)~nxl3B*2cvmq*$EuL_ zK75mk=85Q3FF}4jJtU6)XR;u17|zL&{4HP+*;0Z%HXC_MXQb@0(@&^^kHD=F@Yo1_ z!`2BgznNv<)~=GFsv#)R@IFTYaE=7|xh^)oI22_b`xaEI*hP?S8){f>x-K3_f+Eea zyWM(fb2*lb^bfme(K<=)V8Z3+9Z=r*Em>Vz>9iCv%L=jR9xzXNc8O9(w=GyuizZDI z?XsE{jT7HJx#@Dnd71M?)sn3Dd!z=fXieT|lNJhX-kh`H2DLg(^hr2=1YDZ|;e0^R zyU~F<_4ECwpyUb5 z8O9FgE&=FLN^%m^bXEP73a{*d>H4(giGNr*p%P9Ur56ZDgJkG_)~I5E^mqag=dn0d z(v%|Piu~Q7+l4_Ad9E6$0{}o6nINcZU~ad2EdXQnN%w6ceiPPuXUzuE=S3wEoZ?b4 z%$QbYI*}FDXypW6oAzQDwC)$7@?J}b*GzXGL~14~A3++vt18X*Dm5yVzsebJWLY0Zr2P!8ZUX6JiBJXkkeXm* z1IR}CGmZEWBG`d9@rRpF zb(sYh#0*X14~5(nM8=TFmrd3nL2TYA3yI+H-sR@rnYw&7Yk_7BJfIC!j@n9qK`DO! zlXbhpBc|K+FBP`w<&abWrBP)600$g^0f?T{yDVby7H8E_80v`l$+RR~N;W<~44&ve z%UBqjkAnDl`7cXFl>e7IzUE1jabz6gq@iV#Ck`row3e9?WBaKHKa68jxpDzr)_dU) zAixX^VgSh6Eh?s&Rcf~eBT5u_E?#YEbz4;^vZZ$cO~OMge!>|l6m*<))nE{nqgmzh z2!PlzqZ{3;Swj`7@P+PK2|Iz1Fuz0O7XO7e#X$R$ zN(RqHqS}=$V5)^e4~L$O4|`jy8_1wuR94OOw^!GJ%KCnBTGw_i!7d+P6#O8V+6}rk zv(;u9!3|BHa5GUpYt^dK7o5UsoZO}g(QFkL+J&FN3ocxio@ie#4aL%gs>F~$#}Mt~ zZ#26`$;Xuh7cicKg%M-p)(RffV3MR-lH?5VEd)6ZED^{hGYMW=#5Sc=O+j z$b0UB-J80pziafH66~eBr6`xF9CA4ab#xWCg_+`oLl6&eiCw`nHQ`W-{z3=FGf!U_BGU!1}A{oB(y*$pg_x(XDO~M{WOg3!@GF`IaHtyOloiii)n{ z#XDo$XmVEoHv}K|MxcE21@C37*qLv~IIx1vGvIeYyI+fmeWb^zFcU}|mL*>HR(9`8zJ-}}U5QzJud zjnkAy?Eb#3U>3u5sJSNqX5s$SX(lC8919WC9Iwsd!y)I;zWOV?^mNI5sCTH#(+7BZ z6f=?IFZcQ$E>U|iO+6rj=7n->lUv3lS)wy|)h=?;+MZ^zog6Zzp%>fkuQM!>ix5fD zo4>2a&kp^r(wd{){);zl4LoT^@(G zBtd4`rLEbSm#4$;sPe&nYap23ykL2GmuO?yT#B3HJ&`ruyb#A&^9@Fhd%mj+CLFdu za(@iiV9O6|48mfG>puj%glS)$es39l%Ly4VF~1?^Egr5xt035fHy5SPvZ6mu5%h=M z&aQOhWVOB!o{QmQ7yKB+A+YuMF^_?q_l)NtfdI|WSYf&W*6CAK)a5hKW;ly)2tAQD zVYp@&_Uu+lF`rikoDMGoczI5ZewD^CaEkoYNHiL63`!H`@ZwgO2dwWxS%wMHkQ0Aq zmVAnEU>l)^Y)_nl$NXEuGe{gUc$3h@4-Xe@>LA)EBkoXM_A{xzH1m_SeTM2?iTMP7 zUNlW*bTb)M?&2&l5m1k|x|aG(eG%Etsk(_+OU`wF6`-O^L5Yc_&%F4RG6Ftv>{Z6u zhYG!!f%|I>3DtQl=x-U+ZHphp4R8aCg@7A+f~{^4&8dcjSmTP6`9AAiE z68npZOx`>z(6LXO0`h!}<_O-8zhgeZ>sgmsHo0)r&IEal4tbNg!`HdsmRv5i<23_S zcpFyQatM_~Yd#(vcww%h)Za<{pi4xk_dJ$sVS*EQi5vzK4J1}Tss~B(Oo-&_Wnn?> zymcps<;6EiatS|ZVN6Fak{B!D!EQ0o0tL1-pfHaeK1LNq!%YxY6@#?plP>}c6n?-8 zfXjYN`6$Y;RLp1hpU`q*0i zKRs_ZLLL2O_@v~&cXA02i-$C2_d!D+FcC;7x0`{wAYP3SZSJxMQMe?NcYU`t*v~QE zqTE2-dwHsqw%88(?F8rZBErZRmhxlHDW9p!LvQbxVXuJy|E^ z=KS(uXc>R4Q7hk42(#6+ z+npFJx+RKB@qo8kLQ!Z&=l{*wpNA!MigMhZGZt8 zgl^7d?vAGQpUAi?P@D3CTUNtuBYg`LH^M$_-j`ABK?bp1H||(RE!AcbxAD+})35FY z`KT&%;RCp5P82)*r*#y-ViK!0r{-yl}G0Hv{23=iVRAo4u>-!HEk(lo36DMo!lf{=x=Q ze^qcHvM}1Fk_D8$9V`M`-AwkFPx_RrzD7-)WfPBvo(>|Z#1hCSX|)KP>IansbQq7f zVn?KJ#EX_Qvq_1EKO|Bzj;qODQ&UaYVA}+L1`}iCtxPyx7>zGLY1}9fb2-p+*Bm@o zk*}E1L5SbI>at~3Dw-unmF+({AnVP-%xW0%!nnP4VUy@^3YI4a z(^EzPz&@YVtN}q6PDLY745aja)rZEJkNcx~NH-p*%wB9yVG-%eON-sgf8h8Wn2q%H z?Vxm)l8q@~C-H`{$+1x=*tm|EP$Z*oo@iWJp07oDAsyp%y+gj!U#RsoIO%PPOpxoXb#toN$$>7lJRtQ9=tGb-i9gNh@fmC7 zQa(FfnPA7bIfZ>A|8dPfPsS_0mBu?FKi!sRfRUF22f%8Z8XtW-&r@p+UV8|QZ4(Fs zVhFf60OJ6WTL^Z&(bJ|Kj`R5Gm;gYD>=Or4L0~V@IqJDq)N6pZf*IxBHKqc)G=dDN zgI)Up==<`oX-#m!Z8yuZk<_@HtSR_AS$Bjm=x&FvhNJ!n0zw=UlWC*x9>kt33;&)u zqn-Q_d{A;lpYO>Dq03r|1TD4j2c$@WZYSuSnD$Y@{=LR7YYuX^$z0UGtMl8-1z1OWkseK8pX(U= z@p?2M_Da67N=67lEs9ct2tdV!8xrmgcbeH0i9e4P0D)HX3COue6S$eI2H?@rYMG}&cuUT~^-ajv@9=Z2h-g;ecn zhhukMjSUM-Fs6N1euKPUi3i?2TZ%jMZVpktHxW#-oebkz8@~j+8uJ*wUEbr>4VyIL zD^um_^kr?#Y0BY4*ME*^Ge{&9g2y!3&wud!!p*5z1dS8TSI&BiTN+v;_acmp&z*rZ-;yNwBI#7(hD zcs8`>dO6^e;Xx`KgSe=CiGEa9Z8#$Vqqmb1yNf>`3$G6Lm4oR#7qIa=1TXIt<_!?1 zW5R+(EVZyG?>kQ7*RKtansG7-!k(aXNXYfdv)IMv(XW2#Ig6k7^x+Xl9a%Yv7+?tF z@sl<4F5au_tsbRSa%dSJTAt+x&liU!C;-jzQOfnRNFbhT%%5n1UAErLPeuqmlwl;d z{S4+&6+=sXbJ7|1jjRNL3(NXLcgToS(vW4xA};Uqm;RiMtO%*g8?|i7$K=NVO<-C1 z=PEOCh5fBKo~I{(VPVums1!?Sq1um#>0op>e5>T8iN9m-aT_`Z+PPrHawd(mJsw#zWD-Yn(9J%51q}ugVx$w|3@%yp zqhme2l&@MlJ9wvF(@d;1Oc~Y2Y|iMS3;eMH3p1EFrdzYtdVyEbeEaya5* z&=D7>c*Vv);nI$JVnleh*VMottl=W6fE&vn3KPVz+HsP}_8m3|SPjCK)+bF$+Sf^4 zjU`7jVfu_^_@U>3lo6Eiu@aH@6; zm;bCXL)tFnWNwZbDvFX>4Ze;_@9KJG)qPb(VLw`IJh_6hf0bh|5J$2hJNKzWe9aEc z%v}G1fjCi@SgVrhpvHxbeP>}c89^2$LL!AX1$cy-JQK@Cpv1w4Y93Ne@j*W$S?uf6 zIF4U*KWXgd*Y@Fyu^VNsPb;Ht8eYq@cq5Po;-zM)(uOz91FyQBaHzDjY=QkAY)-}X zX@Tdz(zXY1n|+848Nw#>he=|nb186k`16JXgiWF0y<#~r9v2fed5RCOnp6a8Dw#|c z>f*q&Ev`tbk4nGh5DN&Y{`;%lwF99Egu)G4{b6=$Fg5rsT6$Xbpi}q5zswjnh$&6$ zht|d6I`F6m+1&+zd;Iqf;=RjwW(o&Xc`@sO}nZ*%+?)*r6sPQ9cxYx zf*c0D^Kdj~hIqEpl!3$FrU->`1=FOzIAkNkdGo4I4pNW416Z^^mCcAGYKIE|7jCg4vqwJq_29nUzgf4*iiV~YGL-#|(s&6Ruu6I;E zHLWg~$WOp0Z#|YYhqCe!OgKs%!}$H1$%P%XtK3uNTkgr;hR~rZw1~cYW~1_9`Ji`p z#||q*#>Td=vz!I3i@`z@Y=oIG%&HZPZ06drvEHjogw7*UvB}JnuGbS89Qa&E=~L2t z954W=nA{Hi*x*nKfgv+A!u5<*e^hD5U$+coJTr2>NPggr?8v2!86O7p_k*;wjKG$K zrNOdvlTtIvc*UN_5))qOkG~_T=uN+%&q#1?y(8Mkjn+9s``-!6f>vDyNR8nt>^dF zY5~mcTkU~*X}SKS{kGmA>;HY#$Za&W=ynKJBPusFfV@?Dl*D=Di0red&XdW04cnG_*We zZ9Hs+N$XE6(GLI;#yYVi|J9Y)F0p98{Y^s~kRQp_UuWAWG&S{`2)S+ICf%-c_wm9W z)sRyViyv`T!$Y{C;WAuq@Q1j~jT4V{ns7Yi74dYlo>3xtd^nUmax$j5w5vB(qSX@D zH5U1Ay3h6j&mR9Z#eXN}25d&~19o!tK~k|GZIa<$D3L`4+9kgtZCH+}ajPDb+*kBDby_2jKz4wG+;? z5?f5D&eKw9BncpY8N0r_+J1ga^a$G1m9ghmpSURxQvvw@%kom;jI}OJ2vI%p&??jv zBZt?OX&m>rW`h`EXvpG)^kW1KT|&|4q066I=;MiD%vMi_r@U~b$Zdz4m2}pl} zHqDiLTx$EuC0eRxX8E6WRp#Zpq#Xs&O=JHnyF=dq`CX-!-TI)q-ms6as_T9yQQUH8 z>gU)TA2<@C7t4Pl&5ANs$-_yBHY{ef(CaLDxV;^p;g1J%9k!EhLga7+qd{bL@tE{b z?T$5aT7)&C6ZK0OooL9YHJJB>V5L_nO=?vWNtiADI+;=vTP^Bn2&Uoza2f?P8Xu-9 zwqb#^N^q~%U%_3!ib}84_K5yT5GTN}7o0_I$A)QGJJ2h@)7r@#TSb$J*m=@7Oq!e; z-|2~HqGufi03H*F5*hGS0D(!xIcTG`G>&NUvv=f?TFuii?lSe8sUI$<I=@Vs5=k&E&N0j70K8@L zSD<4!4Lwat^j8~|fKYSo5yaDW--PZ(tHhx9Q(R!(eq3HuHman&);w>s((9ShnceZc zmWhylUq8NOakoZ#t7dH09fbl|M%u&fZKb3b58%ADvqUpS+a$hu1yQ`8R%jVY>rXJjGak@o`Z=gzTP zpWt7b@yA7mI8t6m(pcrQD?9s_GiX2X+$OwMIk%VE-x0v)1pV}v-0Zi@(4_hBq;XM{ zk-R#mV#PV^5w%pSLXY zSjz1;WB5-bJN!QZ*3)Ws&oSxviEK~PP3amOaq=q1@VlrLCnBs2Kbo&D=DMDXURSDq z3i$GN>-GocYYxV%-=wjsVFhSwz7wD3`=ES=he7>u49nvA?YTZAHQ>MYleJWd?(NUl zKMZO~bo^I*|Qpi#_@))x`Vubw$ zjD0wjmz5EtM}^nwBNt(_I*2xm;N<7veyQ%$j7QyFvz|u13r%vS68P9D=JeMY6MhCh zF@^hI$&eD@r9IVviYkalV}Dombo#f14pIX@(DUdun$GP-RVW*Snm!lM77WO_>@

    *LOo5w5MM4>4^$YRlrRH|EhUr9{WxX;_g2J^@1bY!%7@&!Yta$ zvng!WgO*6_ThF+jvHzY=lK(g=aFXhRvgoTVa_7F*-=MQp!i6?)T$x63+=$)v_3qnV zZ`i(!(U1X%!I~-O(MctjM#IH@r%`Iqu#Nuexs8i`4zM?m@_^*4M1a~Xt2WzezLymU z+|H{EAcJ>d`U}G~LF4F7rqeEhE7gXeJ5F+{F#te89$Q(E*Os4CW^+Y2x@rc0Hy6HY zIq!We#GaoJ+balH^_GV5SgVlrW+?LCgg`-bM5U>HHdF~45`L)g@i}1>bvb*SE`bw3 zA+``<{PYS zQYH4*L{m8er*JuG$B6aCT1f@!i)PB62D2OXF|zE<*St;S8t7c zypwFHWMgw16yHCQf1K^Ru715-3w9^&WpK@3k`RhK{9HamdevJB{vH!!&ZvnDY|1+1 zU01D2uYI6D-5>T8LU2KyD*YhsXM<=SS|g(m&r6zo*M~$|&LqGAs{{ZmsfQ2roklPL zRP{aS^A84kH?ilbKU@qI_7J%MC)`h{-5`M+3O|VmZ(Ad#*RNpn@09%h{1Hq;1~3NA zp-#(+=xU(iK&ik_KH;%tj{hpW{Du)Rq$uO0g7dJjzo0>W%*Ofu>oSa45!?&^ybx+yGDITU^r5dxj^zl{wr z!p0)8re0?_iH4vQN+UO@f{4^R`x4)RbsHmrPTLe7I=8(%$zSa17?KM#PKV$RKGFRs8bO3q(>$N3Fmzaud3oM9yZ5{Nv`l?J1mGj!DC}$aOCeU; zlr1~`Y$<)fU{~wEGHG~gZAy!+i=-B2`gDCgq@rN?cw6c5@DrC`r8{w3ZpL*G?#+bL-(Al`E8=wRZfoLFa^y(8+ zi|gs(^@nly?yP|}~&Uz)_GvJLeIJEQOdQxShNHk?T=~*)uBA0+L1uF5*K(mH1j3{Z^ z&g7HzUGZ-|z+~PO8WaF!NW`HVE@akIc7O;DzRjs*fSqbI88%$_sBtQs47l@2E6k69 zYGNQ&v4xgWEk^og5_%|o1c5&ps`)Q6#u*?-drQBbNsTr4Mf34K91J!`OOuz0VOTV} zC~Y=3Z&U8bX3z6$(Tno5n4*3qr2Y}_@*_DdlgpPKBTiZ8H{t#Ibg;XWhtR-$KNnlD z|LE|SMg{s+uu*5kM@kK%#LKCy>LI?|#e6wvfDw&07Wpjx%X$b&5~Nr42yW12bHk8@ z#U+`R-uYi3ZJfofs8}tf@ZN~HCNUZW0%R|c@)r3?;1a^oBy`WPlok*k-azy#RR(1! zVMKJo9IB*9z_=T^gX`?11TKU?V>2xZi6L)8H6+5^a^Hk@e`g&#bO+#|%!YSR)9ERk z(E|9)p>hzOQkq|-t{X@ia7M9CVc`XS+zaqZC9zB?HAbkdC(?rOxC+z6hex=y^29;- zKb*nYywvO%I%P`8993gbxdAzzWR8WR9^WW>W`f?gitvN_uds|5fqp*%@tSoi!vF21 zR?)1`2;?s>!{kZU0O3q^ z3j)ZTZR*J8pg1H#JK2J|fW~d|QKY)JOfOf3YV+=Zzew`~gAEa`7Ea%rS;zwH`<_XF z(bl2wjRh!SuW08-fMgU5Sf+9l=uHpWoJ%imp*%DBuygpY^w)?KJa zKuWuq2H#8#5bjV^8x=oVql-`Xx;6m3V(;YAb7_!^Zc)b3|0`>l3w71-E>d3aJZQ`^ zx7ran&LJ7${rNfS4RmFYMYu-EJZU#36-s?t%73-8;YP#`dtQ@*gv< zFhaMQ*O;|{Za$R=)U%?8_3nZ-XAl~=>`Mq&j;9U|lPi!-#>y^C;lbL3(mfS9YU zypM)+I5L&6G~tD$w~|$fc=QfechD~KtpHad2cSQvs|e1cyvKR;AoxyioJqNFmus8! zEQ;k1EMPPTKRq$F-ezJ?wg^mirW!`_j~ zip9+50%WkW^GgZ(_SP+ZQo{I)P9z8;o^Mp_JM;h}gpXKh{U@oZl}-0aC8r0=Fvg|K za$kS1$L}OmBf_a=!w2i<$$fZW$6T~Ai;n+Eag z2#^ufutlg1$4z|KF=MAwG5|wA@8TuQBY$(LMsN}CH1l>%oR8>zaiL4`2j(}6TcqUe zdi$;*bT4@NV_go8(swqj#Jh)qp(;|~E%yKina>f@OM4*qLueYFm8MsjhHolz` zOBx4B6z5NH57&@Jt<~|{rVaI&yRh=ZNw)A|A90l=c3hn99IFrznu-F!zO#-pf zqJ%FXE3dq!o^r4gyt0Nx9Mb3{cvTE-#$Bm50@ZTH6X0$RaBcmsF{9jP8}yuqTi$x4 zR^1@`woBmfyFlA0KnVja%~i*y8MFzxY4%qcAr(I8X#mx`ebM(2$KkluW9(CHw6m8EZmOXf} z4<^@)^~_zYs5CnAK(S$AjJb65xg3B-e9E=+(+*miMx~Z5mBwd?+Q73u zy6}Y#nD6|>Hp$>iVrJ3knkk5Hyp zH!Wo-F#G6CMZQXoMXk_bGvL0@EkI@H*X|;BPDLnFT;~F=v4lksWO&_@lFB;j&JKTw zo=64HdmU|9(I)5w_wB-ulWU>x-=E}Tro56ov^J4sbrMu$Sun*jpmADg%*_I`pU5Lk zqtLWlLkYZlT6H4eQ9m|Oz{4w=D|WKO=FTpQ3kf*On&gj>oRXWotQ-B@q{8-c*da72 zYwer;;P^|bH)P^{EG&_WORVR;g6R@6Ya_rFj_!l{0Y7OccR}QAorQ>*V28WY7cI3n zI&C(1A0d7LT`Er&j_*5IZQJ7~R*nUlT@rHq6dkv&d|zcy{ffCuR-r#dy*B{0L+b5( zkI0obyofX`Uoj&5@?W;axX-o^w``R^P$lnyd*b1#F?tuB(qlji%AlQm6hm~PagxxZ zFZ|&G2=Z!tz*D*Za=Y=1^`kgEQP_fl;Q#mA%Vj;|Jl(N}{dz`Q6^@KlH2??R7(quD z^ZLa93%dzK_PfT{H%5FX+>YWUqaXozyFmGzm1W7W6a;P#jmh@_22MFJCQmDv*Xvtg zkEIO`u$mFq5DVf!1>hg!*{zBe&*(ZaZ56AQMsq|D#D`n3km$hC{IT&XgYe9L4;@dA z1HZ_mw*)9p&#q8Y=@3WWv$Rb@#kK9kJlR<1-5;sxXI?;E{qb*4+3vV)gl*|xda6G_ zio87PFXc+bdJ`yo*-5!j%Mch0vOfp2vUwnY7%$r8@g)y%xrAoPpnvLR4EUS06le6# zJERrNj?nf1MDlGrID|ix%m*YIr+E(~ zxI(A>-mpbuPO=JOeh^|5|DsSr66~qhH@^!x_HOcY-6oKoqyut#AEPGXtjfGiHxYhm zPv}R5X7@5mB6Apf>se{wOY%yOJfkh)XO1+09H#&oT1-@k)o2)b%k7awp#t|P&#G2{ ze_ugOvIF(ofc-hD^#FWH?;x*!^7}YoehWP09m5wT+D$S182S-pp@=x3v)zw>A5FQa zfl7KrmB6j^>Ea~tv1K!>{Uh}EL7wH)}rhew0Wj0;~|D2<3L!^(E$u?ekQ(q`?ioh zcW#&1T?4Z+^7CyuPvupdto&Mqx*DFk^d|L;OF1L|6E&mN+UX9_4vMRaf9AVbZx1C+bA70G<1|fNgLemrk^IGxT|jR5 zf2!4LD{C=lTwm;!kzgaoZQ6XGnr_$Z(Z8+_HF7P!(c8{ac0XrZ7*ggUl&9rFyJOG%=dQL`-*9ZkuTl2Lz!s}ngJK8 z%xhyVg$un7k;tQ^=u40xk)QaX^iTnzZt1MOw)3l|O2+4Co3=ldAo+tyS)~BbF^7tr z!(q!Wg9tmGI#4tp9~M0s1K0Qd3{%47dn`Y*;svkZ-nSU`Xn(LNX?lAlO6?ebsane~ zEq(XLV|eU_3vzl?4AXDzE26DZCX9$Fx9IZI01kCkXX!@i=nW+gtLI`U;M2^E2WXX~S*|i(*I-2gvkVmy^eTLFM1(=`xy^sEI52p`|rv-AmEtt%4> zX@J}!V#kc}a8ob%9>i_=!0II?qg3>pD`jfxTth947?`ucSn-38is|=c5HX3?%@nnG z^NbOi2qgbu1qI<(HOxjM9FaoUj4dR&C|RFb31!a;z9YsI3G`usfyvZI?hj8ApBuOy z2-e=8Zw!rgEwEnP-sEKa2t0J=?2`4zC&l^?mTrHD^k}_CMXG0 zXz%iX4AZM|8EyBYlz}DLzcO{>m6X~9|4uqRM#V)Ok^TGlghxeRNbh_R%rpK-VU?yo zmT4y$cCqB|Qy(Y)p@`NQS$PN%Hd@*ob!olEFQV6&WMB9X=ua$h`k19nHnGisZzI`! zN(x_Kp^~MqiJR!yHs00=zk%RO77qoR;ammGumb{7#>K)v#MTi)Lq+S^E$_5*8XkqR zOwU+aiKlh;s__VB91s0u+EJSA#z9^-2C(GT%IJlf3Ehn1ZlkDS>!|m~pMDfKeAhOr zu#grKDsMmEB^$Pm+OiJv!DjCE!1^+X#=c0;r$g-fzIhX;LdmOmh6$iySf?3jlb#12uiMcM1CM+F=v^`pm9U^3Sckk5@7x}wl zZVehc;NMD-J^i&SwZ#)T5G3Ys%EIPzQCzoObfh~sc0-4=8d&im#R8gM1NA#8;`DX6 z`qrFWT$`^bojJ_<(u0hN@hlwpnLbpc>F|YRxH}sK4!~SVa52#ER~AmVk*i9xP!YIOCYmHA{!lO9t>(&SNuul zy5>phSb^kAeu~GIjdch=8Pr$8g4!`b%qoj?MiuqQOiosg(lg>bLcwo?Ta0)vX~uM2 z%c z^ACK9VT%{Sk1SljJ3hRQW(~8#7ur9BoM%vGmc5}iOU_KZEzR-CTgSE3C5wRXz%cA3 zprddJ>gJ9gCv>Y3jIEFGAeb_?rK2gic|Hn zLK_xHfYhBmu)u+s5|5dHS2AcQ95;TwfL;1R_yL&Vv*IP)M#O8Dm{CA7bVDgN+IAzv zTBv@7jU-!)K^0>I3BO@vELuMS)nqLrS0WZ91)&ImdD}ZbuO(U=rgn!PWR;iSNob=C~mJ|R7+S2a*{d=Rw zku!ql*?~&XHedtTX?_AUH97FV&zX+?0v`y)PK{SVV?M}e&2mNzW}}z zK4-pGp8WNDMqWrh6F*xno_h%&dTtFq_>#ZNzqh{p_&e4`WW)ZMUHerGa&Bt4*P;v7)1niZ9w1t?c#dzHT<*K%6{)__yjH&<7=YPG|;%R9rzl^eKp? zsLbG07$^r3wE_xf7*R8pptOOOidstk(KP?bbO-+yB~bsL{IfEyCNJ~<$nmcm{+#vW zal2n8B0+)*ci9NgXyrNQ;m=kqeBs;kGW=iSJe-r|?)T2K0|Dl26{pIlknAUN&@vf6wd$`ordE|dg&@KH>D@RQ> zyG0?iHiyqF2$^J67Q%QWAVKZS9Hd772ZFx|zon3Z5dGXgzB>URR=o?G33CB*)om6u zSUN_(?iB(b)L`AJKO;~EO4A4?3~r15lU(|$M+zq>j$coZHn1XwB1ZTaxXAi0oxSNQ z@EY5qCPT`rr@N{eiN)DyQlJ}(Ii`TG(TGwh3o5Q%*VWHKY{XLH4h!eyT((!Ts&a{RknLed6|gFsBL~>r}KFf2Za5&-W0yB3Xn=iv4N^R zg_{gVJoMUuD9B;5XA58sEdcl;o zpy#&}{oKri5<4t7-TVltujgUDTcsN@W2VtR5M{>KW`-9pc6uSw*sESZ)F_qvsmJ`G z`Tn1$oRAWCXH@HT5P<7BT9tQFNC{(#YfcfEa{a|#iVqN0*2vk#TDQ_L|8uIgtXU?s zQrb4kg9B|sPLR%jzHns1$T1$WaJe4>JNsF{0d7jelMdhuUJtr(2P^Q=m5|4mSG_0) z=lx#`JE2YYuzf@P@8p<*5aX=DzVyF^##s3qX9JNE4wliJ?Dw!wuBtmlo4#>W|2I5X zgj;FHX_I`_hWK1IvU8_-qM#|s!Z-cXjCTLaPB;7*6;$PS6!@xG>G+^7+3rhjmB@6%LIt;=9N?RW{=78b7-sCl+$GI#j%ld3|V$ zpG3EhSx>zGpTY1S#Qqdv(C~+B3zJ?6#An(W;^uT!Ane`dtN9s)hy4SNaanvmGat^0 z2R^*#DOu|D#UF5x3Q%8h<)DAtn~G@4O*L~WBVG9aT!i8P0DKL|b}U^)GzBOyYOYOg z-eTmF_lWfSLTIZjut0{*rA2bNNjz6R1_`s~#?eQ#lnAY{Wzd`KtW=aycPfm{FISIh zv4oQ+(f=TugBGQh&{BopS9J@cTY!wJY#v3UIB z9i#eH8rB2RtSO}p8aN*Hp5tgu3A5bd;7LA7mnLa*@?)!$0 zM6zb^ax%yIIDgUCWrEcID(^vufw|+I-uZw5DKyc_vV^9NE_&j=xG#=(y*t*-u$qwUIRA!DmlQ z3k1nk>HGwgz_wO&03o^C#k$Fl{;p=Ec^rBxhIoar&QzSH-N^iX{8fjw9gOdPFZbfJ zCPmUl8<0jE6(0vdXjbeK9X>NF>DJ6rLU|=L%PtZxRUEqCvg>fjQqpcsuGYG2{8p_w zgw<8z$ZbK&PK!Jt%?l-V9lC14M4ClG$f(-n%Jj$+g6ZE% z7`lvuawz3(f#cQS`>n`gV=v68A`~O3n~4lKbnjD`!7S%^6Y+!N4|xsjE>KnIrxmc) z&o24m??xJW*Ib2#CrF26?aHadM&MWCPm)G!<*ckN1F8!$rOhE+BF`jc^-|X5MbT}^ zO-h}g>(#lo1d=jH8ty!)#r-=zJ7u&2xhCiUS@iN@_p7a=tVA+Y@_TDHDjB7CD7CCv z5+JTsk^)5P4ftnlk7MQT=RNHV%h}sSG;k-FH}GSk9MAkQ@b)U`mp!fx+OMK}6b?n1 zi2g$jClqz9g-NQh{hI9rYqW;aDC5^Loc=wWLmVSiBWX@8GVn5&F<&@M0geGWG) zFkLot!>@gAY!A0Z#A0Qlm41s@zKeO>e3w>hfF^3@O`zFcY!cSQUaUQzziQ`S3nGkJ zA>9T_nZ-$u5($OIEM4pK82ml$z?qS}tfd8X0PzJGn5ggcXhG#Wj3lzkruWUBOh5jT zV%7EVOLj4clb|N6gzKP%`IHQn?{Gg%Qb4#Bftkz9*n~unyvo6RUYj|Xv}#3ZH8Zr- z>(GUr^?RGv<-4M%7}WeBOVzKBinka)mPv8Lu1enUX2s65P2(F$J974CTdzLgH9MQEM_=YV0*a)|57oMWh4O zu%6wYU^o=d&#H~22X1dwKp&hp3@{bDG}jlnbTUI*mj|=LUyQ98_BrS5-eID(%-#-K zxt6ik@0Slwy8XYMHNfEPo~b>$=n@@Y3@TbeGj46fk#fr3D^#RkC$O^`=x~1&SG-Xp z$8^3IV+%^j_1k48R4n0Iz%u{fY)v*`1Vbt&&x=$1#f4u+*)saK0by|?UPSpinN}n(8ny8B_Fv#e!krgB^9I0%{KxJKRVEDGl#ZU>hj&qZZ@$&qsto>>Y zj^EfH%0!y1&lT9i7>SK79l5tD)`S5DTxgd)`>guKc<$su@D#U>4-Z4O&(j2vsC2Wf+OXckqhcBX?_`%%alo8fp25{KM%7 zboJ@Zkg=blyfv{(N$ZUw5;iO%TWW5(maUH_s%?yA^D$i)O!W}D2=z%XAk>8I4zuS; zfB~9(^*I0ENuL^`1eOvwCekxL` zoZMC!JfwU%9tR@k$$ZvL1b0 z0KLjCJj^YzV<(IH*r75P5-Ev2^m1iypI%Jf!nHaFf94rw9US|Antb?Tn8t%Q>%=(P zTG~61eg8@ElTNiSUO{;lc$s5;*iCWUjU5b}$ly@C8rFGqgnibKwDRm6!D7+Ek7Ybn z6z^~AR;h7~Hj|!l%bzw=YO|lpK2D1HgIJ6 zU`OE_IHGj9SYo1S%}D+GN~=74+?edJ)H~+)P+mY-;!$zyaBWFSU(&^-8W~rWr5R?< zY#((FRV>T*A*kcEx*e*VW`(b#2$mnB7k<&ByZ{~jkBb~O55(9llj4xMb}&z9eOdmp zlh=33;LWIzB21J>TOHOM$hWvC(~{$bq#HyHndBWDR1?KV=Lj#Ooq`Se2SaZq2m+Ip z)?JuVG_KtJU(GFB{K`xMx6}BoKN;z;Bz`YAC1}|FSzE|rMnf&_NU=^$pwXDYc+y@$ z=W5WER6mke-1NN2PL3+!)D;%f6JswY4!WXTk^os*8k=_sAFwdVK$q+{dwV1jh>2LO zoFxi8t46u}YbQ(U*ne&`$Df;iT1UTtE{i1UalZWpByOTe$$nWvSB&K{uj0*O#j1kW zxN9v?^@I-J(`5h9um5bG;mwO83Oj-NhLPMc*&6<9U4dY@Bi-2kdY+zqJ;d zM_Q7jaKPq86$8%vQlp!7rrNiqg2c;8_gVv3Tv(%%7MK=g268Visd1H<%2qI=@2+n+6bO3&_DTd>kcR>0@A z8Yy0J%m2CDs`f3z$>`#0_r2gHyN4MYHvyM5BwaLGGl6yTTs$k*#bUKvdi%Qf$`THl zirjeU&0)XcPphrB33K6Bz;|pMf6|&7~F*#Dpuf~U}A6)PY8;fO2ygvvbQjnQX%J{V*R>rK9p#l80+{%P| zuiPxZ-+@el;wVY6rjajg^qL{JC+IhjB>3Tl5vH9FrgG8skS#iB-oy+kRWd6-5LsxE z7UA&F)z;`Jp2cw~<10(J<6%@k5~blq1@kGZWoqqSw!N|#rL5%V>i*)m!J^!|OX7#IX-ACL>wOpv63e#UP!cWJ*)nxiXpZOjec{DGX&3(^Lf-vu6j9HMf~eI}s- z0!2eRvVIVhVa(@zK*51r>#D5GL@-8LaBvN%u8oCIpR8d}!ef5IYU>TQqJ(b^EZST! zmfNw%UL5VZ=$tm!@Q>r?Xq+1|-r>5iXWF!@T}#7|rB#Gux>aQ;XPt3H^&l0;$Q~;q zRHf4M7!RU0H!K8`==znrC&^UcSxq7}k}7=)=B^#^qwVt3m2%N9)^Y>%J350f2hk|R zcSk@%XRr6|7z>GxEH%1Fn~>J*Nzgc`?1nb1*0kUvkv1|x%dL%|`+!|@u1W5%(|l^U zNW+pb&oz94V$q!!9?0&vUW-0dZZ~Ok;4{Ac>tGLLzFt-ranf=55w%}vOBqORPHfYD00WW)L>N?J5)q6TvlUeV^Mp-y!9_D2WvSs z*v8;gD^bi|{W>C1>SR6=H+OlRS95EdLVO8CW!06%5`u9~>{GRBDdh`hCDYu&;F;&% zTPCf^W=;1^Bh-tbdk*ugOcZd(049EQf7ib0THUA})lVSsZ`T7!jETp#g4e&tbC>CX zAMdt~=MBfaamjYD1;x;iIDvv8fhQdv(rew?3Q4#TQA3;M{7%XN ztLN@=hE=WF*o@NPzpi?-Gzj*L8x&n6x`buTT5&j<)MLgh7?f0()?dKx9;jyqSEi5H zIGwBk1(!U{!R1@1#GUIhCLF)1)}6H*G}i0f?JmUweeJ2n^hp*x^%x{Cd5^rLv@8f?G*&K{KOi!6RPaT_aGS%)5^4lrhVYPsB@zg z+lCTymGP^Up8u%x_fag?eoN32bmQK)!&hDn#;J9BiKc6tu{~Wh0suN#J20R%WO4Pl zz2agE9`T~tMGXLK0xh1tt31p1cbbR|#(XKQAZX%9Yc|t9VcuDQ zinV9TNZPU8rMEb4k=}@zmjYRdXHhW?S&(wkfl&11nDatIJ4x!>)lCJZ{#*!4X+|Z} zMy;+%x!-EDVmmD4Pm*e7^IKZmdUA^)T zE2EM(nE>94twlwsr-1JxN$q@S%BsV$&=oj6IK4%m(9pi~M3e`*5%>*Q9FG`fbr7yn%pPKvG-YDQ!05)mec6d`>qu0+GP{Wjg zJ;i;oBt~E2!QI8aXJvgRvz7URCH@fe)$}uRppVtW#2m`$P|qL74+!51b~Bj$S;dN} zo(N4*>>>pH(<{{;<%l{8HWtF-GqyW@$4p?A2|cyq%)stKnYDme*?*qlP@t!m4Bv_p z3avU=B!Qm`x%O|?C)7aRGfy`A3wb4b;@gxhkQV?^u zu{H&tBGOd@iU0GRg{BO~?rw$WKCmg4TuWUnnp^k`%o0keD;JObPZ;>2%tQR%Sn+lb za%S}20`mkO{%k#AbkYH;*n!&8eGB~2wFz!83v=UWgh7F{i6$ZePrd@E(b67U|YojI5Z3nUudbPF2(9s=|t>XWjYO zaKf-48)bNoQF1B4@XQou7gEYQuXPsrIf`tD4^Rv@$e!>h=)tL4JhkzJMsvTVaL{1H z)}Cku7f}dO-%c0PFp7sm`XJVL0vObwj{d0=c`K(#X+a>bP|Y-j?$x|f_q zCSA>I6q3DhL@fQ?=@Dw^JhWSn@WE7gD!Vi2&|iRtr9f-ny(DbMoR`U`nIIsnN;Wou zOKkJWYAPwJ%eMX$+x%j071_e8$S36(Yt;XRl_6$O+}V*{%YJ+8rn7#W?A{uf`3v%# z4;eF%jIAG8pQxE}!wo7LZ~bj|gX`z^+p2V9VY>_)8-Cq1aVY(gJ;^L441`yq8#~3Y z>fK|t(dpl~;7G~}(iEBG)8{q>@a^ly%j9Cl17Y+5|3|m4Pvf&!bZAsU-cK|C(&+9_ zmO$;d5C=U4L63T8=ztlSGP2LTtr!6hC`pLIc45L1v4bzhs3(+RcX2}@9S3a`6PlapUv0RIq&K9<9$5AaCy+NO(2syzvMBSN<1;PRaR}70p12Kd zywj)o(qGXigWC5u2$_{Am48 zP{6i+4ok5W!0*}67rX54K!(FwN(}|syDabr>jb=FWn3{|sKh=Ec={0aU}HMB0&i3C z$B==wj`UkA^)<{`IobW+V3_h+FOZTjlie?U8(Z|YHikAchW?BrP!hrpL{~A;F)kOw zA%a9=sD`-Rdf4vuqN}0SAd8uXDv&gicDuKrX@z@Of~{UU=!G=_uR?kCU(Gtp+9tuG z&|BlNZD8|cX(kz#n7ROLiN1s=Zo@CaZ2Vee7u5a}fI(AsmY2R3O7DEUF?ChE)`oJ` zG6EF%;KYlQGTP5rUp_l_wBLU&FWv8GMnml&b8J?3VL*Kxu%f}s#1Z9B#FMv)g(wAWb`OHB-b#6n-z_}*a19UxRiMGz1 zu>P{rJz2}@o%VfvE}It^QUi=&016DIw_xQAT zFof>c%B9gQM_Q?}+fk|T z6gqAOKik-*Y530yjho#Rg5JTQpsHMnAlo~Nw+CSmjl>Pqu=07M_*$Vhwo-3I@bYw5 zxvT64*Dgf6;m7Htn>Y#aPg3z?%vj9N4Gu1qzLrL^`#sqx1f}4<5dx7*buKFy(xw1i zN~;nvVM=O0VPW4xJ1PcT2QRmabVx^GM+g#$P}KW=$(!S6#n0gR^|U?tl}fibpl227;xMt;}SI9mh>bjsVd_O)G81 znVD8=f4*T$TPXqB=+|k`$+Zsl|DGXReVT9ns3;9z056XOOK2N%2He@yiu?{qNJ$!< z$H0nYr-txs`c^VJsg7Nu?^^$anVB6nJYclQ5N`epWX((WswNk*oDb50d2kNb3)`5NgCt?jaEA%mJ`%n`B&9y)vnTNt zVA-Stzo(d`2YjJ|a5#ICC_wTv2i2edFb6j{I#~mCwt0Eq>X8@Eu%mgskR_Sd=rCC<`B84SufQ~>roh3?{r;XR$zrQUCnwRcdvy~1 zGKMJ8u2*Qd#vWJ;qTGr@b9f4>Ko|cPLW1+|0TXKq~5dDd* zG+RR=x`EP_AsMI3-uTinUS2JaNhRshFDWgF^ChJX%hOnuysJ&m31Y{nG$ChpIZSmJ zjPlE#GOd;q-R;_8R-h?Qfd?nWDOUB^o*GYJ+4ig__od9_4m;gY+TXkp*Uit6Xs`2_ z%)B}Lny*xTNYk4Tfy{|IioXmuWchXaDA)P5BEZ$@5ED)1G|(Z{wz8B8+Ye3l<#Aa< zZRjN%BDD%PzCu%5>(qsPv5dR`C>VZR{ErN%tij zP|CZZLT)7N!~cNOr`ILNJgIo;E=%m*e!ug@mLCh^-_+x zgk#?Rs}s2ZRsv3IFM^r%t~;6@Sirn?3sD?lh{&1`!xNWOSslsy1ga<&m(Z7!`^RH=I*s%5Y>FKF7wB2tYUFR z?l+RyLX?}r@i|yD7~!FuVRw58x$AqJ(WE(*V*!h!T~R_eW}Bxvl1$UoY~5a*A!Qs` z#$Tl2%$9yWlJVbA>(&o?!)wqeGCvg{&C_9jr7fx5(NS(1{idGJ4s^rUNnS*yT}^9n zI?;q;Y}9s$)>_Vh3dT2Y{0M`UZm_XBWbK?P4U)OkIV0WKI&R9WW?R`o7oLR*idmOq z>8h*ZO$1|=J{ctvW{+%#h<7a@(;Ju92jl;c-;MI*>zFrsqMu*lyc&#+)`6}adj@ny zg@0>8klRwReX+{MiA$t;cn3j8Ynkj=lE+D zEcX$8j7JtMM++hIaNkpJ1RK%B{YH73TCC%Mqsth-eporjafYN)@dxf?K1H{8QJHsw zngK1=m67(X<2p-RLBR$m&4zkY`}tVI^W)6(BVhQr>F%Okq+Z6RmJfrRlxn-3>CZ&5 zYE{MI3)IvZ&(xGH zGq>xct&3f~Rd>|`-2UGUdEUFEYyT56dxZSS;pr;x8qzS^fVh*08smXxDItHyIL<&P zfsP|(R6-v_WlAbj4T#7|d125_JtXe@e0yJ(Hd0%nUn1QwXpqbH#3aYOgtz|zJfR{h r$#wM?$xy0!%E<0(nN*MLCiv~ExM9@~r_Kn#senOjnmkMP0Ra9VN3kHy diff --git a/docs/docs/install/img/truenas10.webp b/docs/docs/install/img/truenas10.webp deleted file mode 100644 index 21668cfccd6cfbdd268500208ace4129440e15e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6824 zcmXYyWmFu>vV{ltpuycTKp5QJ2@)KF2Z9Zr;4Z-(g1fuRAi>>(yUXBC&;j1L=bipl zt5)0*Ns zr{it(I`;2uzn}N!x@Ul|F6_4p(bqS=L!^_7KW_(bzHcmhhOctDT`6z&Z+$Pnwh$f< z?s6VF)BPGRHJ;g?yO#g>vmJ@;y;;62ycN8iyoOG`&22Vq0^X*&T3-iTOJ0ud;s^j8 z+g`o*!50`CMD1+E6P{1e+c`XgAG>`1w@r!((-@$w_{a@eCvROy@9{w))T}N1*1s*Z z)Q5~f`DCG~g_(UjWN0j=?OXYx`v{}sFAE}0T!S+VawpgIef}?TluEMP{btQjPkZh- zZDESkNz$P$*jcpIK(C*+p)LH-Vrn8ULy9*E2KWeIv5^G%I*>xcmD4{AM)Euaez!0uNC5hxZyMw5@ds7KA^5>>S^C~ z7tP3h5yarSlHwK~u6W61aU~e7dVPwvzjnWIQ3?!vqbQ3b03Rk1(pS`9LeD z(OVNLN&al*Ox+!yi`2Z)@AbyHd8j^7Fc5gThA-?ALehW?xq951)LlK}2D6Tq4bQQ7 z8w!hMMLzPS*6pkIb)Gh*O!3`&+&GKYsX)x#{yuQ`IuHk-6pe(z-v?hAAN@Em%JLMh zA^i@x<+A!mdg<#Q-1kbB;U4Gfg7Y2UR5chEtN_K7q=cIdvA=uZ^%7}>Y_Y+$t0s^> zyO32CK99uDyMHZ@|IFu9h^00E%kBSrHE}iajrylX1L(9%^|`70Tyd3{I1u%xA7ex@ z=yzul!W7w_ecHcCupn7M5^e+O@md3q?qdl6H3LhWacpsySD9*5)1y(Kn&Tf4mPvIqzBXMw<9;>U-vzgu zek$*q|33kbvOw-H-O?PvU;pL(??Wd^Ij0EXIgx95)qowk86+Y8kwQgrReT+h<3u=} z&w*1xgD)<^@Z7@D@|i{0&Uq~ZDHs2^;p^V)t8P=xf7B9_w+2u?>9w_f;8KPM)lDFgnY_eM z?^5!is5P7pX&e%b`XJE>r!9(ar}{1qwiKHs-3nRichS{FywgF?T^u)-)C0|DgCPMF zW~jit=yKp_+1)symI}m3&$i+5BqRh8Lbfw#>#lS~S#HhC9*{C~2caT>rOy60fOhg} zDZfXX8y>ep7g?s3W)6LR{J!o~=83$d%lf;a#DefmG4Jo_EiZQqEIFr+|3&wy;Xfye zbOT;r4f7WDi+DRw*GABp^{qT8W&dswHB|j&9rVPCR#rNBf=0~aaum$WnI+WA(Ugx? zbm#!7v~f=O{zziBs$#=`3LPr$ne8DlpW2$sgL@pCiK2PP?|upnT)%y zySc!wk@J}6g%Y6SLa9l776m;^v_J*}ilH9)f6r5?ISEUOu^fL4wWvPfyif%d^}Wd;DGPYRTFh{FD#r-*La%NV=FF4PR&M z$l|(jx)ax!?U|r-bSU`m$9iZD@pL!e@#>PF8@$}CAcAN-HFk`Ku-x+q7H%Z`Dalp% ztbYJMd^`73t1US6eOd@NX+9E~4sTnc)OlON7(PKkYZ}svn-QN?n9Y2B9uRwGX{jdX z9GtHgSfqR1y$z42;lKd}53dc!I+wsU{=S^sVZ?H<4t53Ogb;Crnb^pjM~36Yu+a1F)Vuyr zM_7GmE207-G)@13AdZ~1Noo0LuoX(NR2-p4p?}R);kJU4@X#pKrV^ncY_q8f22t?Y z&Ra8pHRsrSEI?&;UJ&0iG$QS&UWOz#J7ACuW+IW7VHTVBoCQLqf9$=!>8KRt6bB=| zgBUFyDPl&Cm+x%mF-ba&sRy(~Kxf|*!qCKWVM`b&;e&#}?_evyc+?e&2+`FU2rrd_ zk$Smn<7-q@J=FXm!2aGf&*9dlQYf9Ue-zlqN0%qnO%HvOWW&{ZZ~1gU8aS3vqmdfn z{~?fcoj&A4f0^!z9!}`;^pIxY9l-|zPa%wIZ;v1 ze;-dP{QJR^(@6L#uf5wxfO-Ny375!F-f}InA*?X=I4?gRd8;+899<%nn_DhL?t5aC zMeRfGZs-zTsf@oc?c%1%ahF@`@SqEEJSToxq^2alwsI};$6YwsOn50->D)`{UW$rh zDKp8buUjh@?xhUkne8)zNS?0*?&zg`%J`j0(?^X60JrYFm@q@1oBn- zfl^?6mYPvy$p9}0+G4m#hr?A9I#qRW9@0s9c6-O^nc zYWbtnT4T#>Y&koAHM{)4%ypkG#bf2HY!Z6_X~)y2*u?wXjtS;lSLsfcNSE3XtxXrU zh-y(NKO0Zg;yHKkrLs_i4)R01-+`(H)RMU98`Jozu%TgKpd59NW9dj<%1(` zLNJ)sATk#Fl^!N&-%V;bn;s10Or@l$^R%IXte3g7X^lAHa^~5^U^8o7yv$t~?AJ4k zg`<7&C!w0@X5)}}T6~AV&XmU)z$+ea2x7NngXK3K-PY$17^tpZ`@bU`Ib4h!e}8D+ z>nHAjOBP$iQ?>Ez{o=W{$hue-*^Zr(XyxZKuMztZ0!#upehVBYI;_ayT);`4lO4c> z8M8D)D?h@0A9vP??igLn`U%Q!{wYPg_ftn|ah$7|+@xRoILzO0o`@`@#>2yA9XTSJ z$c{(6YsdYuLUNi$)zkDI7gM)DVDQ5Ktq0wFAVFsNlRA7ooV#lg@7M6bLyjwGEdy^( z%}QxbreSHC=hXV~jfyx$K-3pSeEO12Ki^`^*;=5|o2TJbOqf9iwRJ!}nPhn=i#cM( zRo{tZyEq)Ae>WOU@q@)`U30u6X1sz;Xf2x;qVUdC_h&2giS3Uq&owV+)2Jdr<(SZ( zqCO1AbneUW&O|Van2$`y;%AOEfKJ0(03i~6mIMi16;*bG!}BY*7&_j*?5goH_uWw& z&SfnmvSSYmuC9vNz9>7nN4EB%=0=(>^i%lvjxXO_69`Taf>`J6cUWNY=06O_RDxll znBv#!bPvDH^+Bo~!AL6T`&tl{+9Pg0o8!X8o@F29sWek%gKgY>`hgZc!w?Q2mq_?E zw3Yl)+q?TiPij7is%f-L?aMB^$rd1vQM#t~ty+H7vuHZ7FWk~)vrQH!#r}wtnyX6w z$98`+OufAow7f~xR3t8 zYMk_K?(=+mb$eRG&*miCRASJI-`jCV=ANxT+9kCI@U*9G*r6H zR}RwGHb+z?d-pwZ_^jJFX-#|5afYJy||!7B8*1Tt+*_@puL!5P{a2oB0r2A8+3htDlwOu#9ToKQj%O4&gTXg!a zSQ^bJ1H;oGf4|~QFI%QXUB`>b+SNgyxYXI9C$-M6sTEq!QS`}``%`pint|N;GVK}8 zHuHqcGRz^+5A{kuJgfv}FPPq@5Hk)*Dhsj^E^4=F_h){Tjh zW(S42*>y64B)vgocY$HCb}p`4OvK)f(taTgAR*e3taZ69^v+4!Uza4nA<=PhE~%Ix ztKkDhVfd{DB&6EW-hNrfH0QJ(r6|UkQRAP)fllZ{B4X%JI}+jMiGuj!A9l#}#mvNu zwvIdF^z~p6^e*HK216NGo7z@rO?1*8Fxxz03eoWJgQf@s!zY-UJt8zGc%`*{<+t&| z)3uIERq_nVOZWH|tis^?-u^Bmo2a%FHEEF>Upai8$L;8~BH}?sfw`C$r>XSBmKqna zF54?_{k?{-zEUr;qB=>H>kYp#_6*}~0uX5qUN<|<*70{wJ;(7^{RkaZIz`+%YUx0f zmwLg`Q>)xku8Lu;gK~5ESdIhKFTIy}5*Y7cYf6n?l`%8YhUXqtkaC8J)3b~ex0>IC2$Es@bsbsBm9^Y5K@#s?;n%O?)EmE zu=Uwd=Ioc)ceft&mmYODx;Po-VSL%}o5blj{B;fgv2?tTD)`}jM~yU^O?ldp4Wb zMqpd;h=9tKsd~hyZD1%#LuRXC-A;$^cD~UQP|1)rip|9q1gLdYmh?p1_TkbWdcpb3 zUqyP~G`a!B$ks;Ge9jQQV;ZAm0Df0;#&O$k$`>9yE9vz6GAY}|d622M-hnHVVSrM2 z0+T{D^(u#R*c{l7P`n5lv6c#JT8|o|=?EPyKfPO3{ybZXtB7c1Z!(?k5)zO!ePw8J zU)oGfhCTyS-T6hKXTYWOiMVkW(*`ymcMlp!be`W8c z0Q+S&m*h?M1oYH?bazrB;^Yf|>!Z`1JW2HKUs3d3n(V*w<=Yl6SyVK>wlf`Z zlsH7FFjFnzd^K64x5s}EFdBz&cqMyO9O;{obxfG(2p6wETR46Yqi}Oj!H%Piy0cG| z%CbX`Zi?6wu<~blwVK1bSBWbrAHl}}PcrZ_b&@{76r>4MJd!M&AwDsxTJ4T%uOTi) zzBf?<2iQ7aCb=t5%bVlq&NY;wpO^r3k=h<0{*LZ`LfM)KW#;s8qPMP{w4d^9>)KTf zi32o#zw6JnV4fAY_iVx7o{NPJY;HDuj5EH=Nj5LpR12( zY*RKNqG8{Cml-Z}FWC$;MnWvt-DX`p9PyAq=Kuzie}GwWdYy9OTgWUk`XfUKa;MZh z&skF##fU$1)KXmmWwf%#DBz;2hL83~_w#?c60rZmKI7{r@Y>dz3xLFZz%q23BVR0< z-?{-0gkUT9I{K_4#idVIu73tFUTi1~QcaEDCMT|_tMTcrQj0OFfmVPCgA;mVXAFEt z!Ah0Bp${bPywqv{gSvXgQHpvWFbA`(LK0m8;eS1?OH8%5`I> zjw}HrXV7*ml5-KYG{2hn4c_x`L)A7A#%H4sFe^IAW`>~J21pxpN2;ml?%w^+xS6jH zDp&6=3g~(-nk(SytImERdgIS!@a~wfqYk?%gFZq_vJ!Tq z41~ylxyUkh4v|pudmx>ztqT(=)jM#3si`%_y$S!N5ZzOC{ojL|LhACY?sPm^c2s4R zwbv<8Wt4ah-PIe((d6DP#2MvD|9AZQd2V?`EYqPGe%3r`eBBXh%BoZ{fVG-nMhiag zk$Q4dc#*ZxPWsmCXiFK;RCeaBIS+Iy=qWILHcMIRI!}~PVt%Wc#_}l64fqBsKrRI~ zWHI5pku(+2iXA$>n3V=$^zStBexBLIwDJiBo6EO_{HY|wLrez z+CU<2)64`4F0hK@A$*E`kG_&uH4co>hWKs>w412c3VkSA3RE@9H@2%$31MlH1ka> z;jrQ?!l4FX_|PU%jOk6EM?1#C*s7};XS7WVVUkQEBu# z)`lRO>;h3Iq=gV@Ppg+zb-j>CWB&ugfz$wPu0;{KHYUNwSbe>^-E`iao%DSN>pXO- z%MrK@w-QBbjH2?ia+F@Xm5XUii+P>*XwUqyNGd1QX-)sE#bO`zx#t{d|K62#<%6=$ zQ@R>yaDvZ|H$uzwgJ4YVqu!7UC3H@3SXd4ASiV5q{;k~)(~V^&!~;W36NvNkT~?47HA<=3tH)8==mA9gcaKjQe& znoD#$8#>dxyL#W!9b$u}+KcR8Q~^9WKPSfP4;78CUkGq749p%5LbZ3})x@tH(IoGG zbd$sWsODL%0&~OP9OkFrH7G21G)p)61Ar)!6$(kDV`%zwujYHr!$ubJMiXO^%}Pc;dOI83)2;sGwfmtTg*wJh3dR1UP5ZM_I4%-MXo!GKiYibf zRkVSkw(!Gddf}w~=ALoYb3rADo&y)+cuXd|q9R5}jc@=(lt!6iS zteKT62VS9-tt}8luJtpAX%n-TanAMUS+CR+yEii z!u0|{HqN2O{`^;XOP(%F^UGT?Ebp9fP}qnvj1{dTf-+~R*o^u`f!!qEK?hU&>Fxx$ SK{f`5jg<(5310#Ktp5WL^IzKl diff --git a/docs/docs/install/img/truenas12.webp b/docs/docs/install/img/truenas12.webp deleted file mode 100644 index 1ed1e54c46367a7aadd9e9ed13a6e4011c5f1df0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4930 zcmYLJbyO4l`yCCVLAq;9WQ0gaI2sj3NyliF5E1E`l!S=L2ni`c8YaSMM#Cs6r5gb$ zVKgJY`o8b^{XXa1b3T7P=RWt|=NdlH)Rbog08BMh^-c8Uc*XwwJ&=Hp1R`Yw(S-L# zo}vtkK!T;Y$U1)yb_!?rQ+UycJazpVcit6D6e)F`%YzSw9y!)ZQBU^*Tpk&O`Hk3C{CX{Z+0mID3|$Kd4HUl=juq=uCOBg` zSU3-1xZXaeJnFc5Qb(2=d;pEQ_T8qw4Bo$}`a#?)N2Rkl zOgNA9jeqwEKm4Vc%98lj+c`e&ywwmE!UZHBUt*6X@niZrTi&BP&()aQq#T)>g`cnk zGOD}&9rAZjsW%60(K)o4l4H0Fr87l>(isUx6W>@9(RbAC_Os+&i&&u}sI`F61{U7{ zE|8U#xw_ar$nfFLbR)5uk9@mfKMAt6;tQ_Q9Igm8o@JC#ar+B~ENo_aZ=7rdilp8$ zZG${-$2F3jFl?gpK_iFM^Iu{xl%cOM&c<1SEK<_`zb(yat(zCV>yhrI{M}n|?tcCr z--Z0&tp*eR!&@02uX-F+Q_V%|&U>)QH`yz-+|P3|$BI8a)Z@j1W>R_oQTDCMYvhV; z)^~B+I#$R}_a`|5VpD2tACQ-ed^UXlM_UPRA))hftA_QFtiUii$a~N|pEBCN))AcO zvE57JDTLUw{)zslUMJl|^+d0WoH6J6hKMmSsk z<2#xnefkXL2nl=K`v>^|Vn?mdwI$AAc%r&&;L{F2Z9o!-e`BQ5z4xW((Gp}0`)WA} zBA{#4C8oU9lubLAMfY;$zammh71MCcM?QL|tqgJtn*_OqPP-$euae=G2JZh@J4I4y z!di~b7@u+9&h!bjZ4b~~*A^-@G+1S3z0E2P&yQ1=VJ+{Q{bulxevo6h!Usf7Ig#>@ zrLN*O=eai^*uTjk`zL~Ea$Rw$>LRdmB9MS{uWwsdo2Ycs!+&G4YZxm1>7u#Xu1V;f zJ_Kucufv9zWal5@OqD1(hEhHLBZE>`*He~>i70x2)|M#4NPEBEh@Kbjb+WV4pU6sy zTy^`S&cM;*3AA5Jq@qPRPKXF91V~D7kw^S9O`>8aVx@kiuEt3Xy~ez@>};}6xJuH09xmwR;ibl~WF+dX#5kiOZOCAPrg zuLpvtd4^Wz835qQA4k)}F3?FVOa84v$VUu9Yt1eYjdN@LX}}v$m_ftG?Lt0i^>iNs#dqo9*JcDbU|;r4??XAIiV^I*bi_EQtc#z+`XM5Q9i>x$L^{xB_kdx#k&u!D8_vo!&Jjf>6OYWBtV#Vf-+8=DNVtB0Opp&ShZ& zKK{fK^jf01>@MuRq5QB*y#KVK@aDfgLbNt#@j zp5=n=>CPy-4UfU;Jeecm*-T85d7KNJ$iXyYUai)w0 zd)ZWn!XB3jHU0Scrt!4=>>Zo9*dutz;jcI*^-#~j!S-GrygIi$ zDmoRQCShiykh0b@{ZQuptj2(1fsgz$@Xk<&j^9>!^>v3&8`8urSRHsQb`B(o_nuss z)fOKq#^88ATO>&Y8gn&Y-|5Ao7wN(>OXCT-1zj?;Xz+@ZI@=A{Y_XfQ_oGM1b!Gb% zR-GaW_BPFaPTo=KvHRZBixPKBlv5>5pnld0K*v-DO0QYj&OgvFdAm@US$gF;u-lf8 zcYg88j3CHhQ5n){pC!3YQ^$6*hLGtC=PVG`v@Z3E;8&4$3XXR6QNt-#17-T~c&!oD zGrsXb1WnMkw9F8{Q3eL!e4WWgg#;0eR%~@fx+B>~>Pq%hd!?@(7%n>GpLnGyt9nmc z6sof%AjkPV5us|A$1@Ghni^3b=nc*@tY1$vPa*QXTb)a-nvRvQ6VpKbm}{qfA~AdI zbl(?1)c{HFcZHbn0=WU30**I={29>ZiOmZd-CmVX~}Q0l|-?ov*$`_9J=t#weo z!*tv&SBOq4x0+uMrnWMrR-TE@qw0!VQ-sN(E+=b|>9aDCQD76a^ezL5H|mxsg)XS2 zb4CglvC{4<_2})3@d^(2u8X$?IXVHr(k!8GCYsU#CyVf}5lK~FYi7bzulu>;J5e+u zQk`+&q#TmYyl4R3Ry53y7%6T2hh{l$(hSt2XzFf)%Vvd``S!_ydx(ygj&ktQX_}rQq z2|lb$Y8jG~K@s&&z`wTzms~*U(#0 zjY2y%(>`mM_=K(lSCj3gt}^e4QB~INhAVG+b(NhBfE99LRarg_&Pwxa+_$b99!QNI zh_K{n19~p|g|FU3P{19XmOrJ-f9fo^aTfgkm8!8NOI&-4Z8tC->hEm9Aq&`BO0*v0 z#(>Te=p3*7pNAZhrN-y9*W!~Qv4B61ih*<2>-F}@WEVv_Y_TL6@_Dk-SRTN5?|%Fb zVF_kJPM4t$EgbgKHpC&U(Yk1`fQPHFIsITrF$(aa(m4iGbz`N~X8eiKt$DMx$rv>6 z>4p;2oMM-7Xj(qIgp{X`BctQ8V)k`n&zM$eUd2bPCZD~vk>9N1644yNSU0u{GAiG@T10!QuE zw>3c$ar|L4N|RQwKB{#!D|E+wTG`el*M8Q=tmFcHXX0yih!imGJ%q8G71{T50EnS6 z``|rcG#rnPd;i;Oma_7;`XuXMV<=lUg|Dg30>XM)y_~jLqg;_+*f=Y;@QmW_`H62U ziOR$q*j$CPc{`EH>yyyTlxDC)K8jkOQB}-;0@XrHlGJ^0R)Y3f#dxmdgq2Zkfs>I=R|Ps6>}6h)28fnajS7FpXetmw zLMug#zfNu#0C?U7eBY+4lxNE7yM9`P&u7}ZIlI}BB)eN9$>{iKUK@E-FILPWkoO#D zC@+0HanG16pyI47;Hms&Z+D3jzySOj?50`~D zsK?-E5H2V~`s1~NMZ+V}d$)y;wi;BF*!dZl=ZRx~H0mXmn1=2;;PFS`wP#g*Z2oqc zLZ?mlx8}7BZDg2d99NOkzK`hxvHCO`p)TYwdh{!fa;iK_LUZH%M@iOfQksI;OY@~? zG^W9G;X1jrr2PO>wg(fC;*zHio{f*yoZRlb81grIMLzMWC5AP68ARg{ZAi+~oGa;P zuxF01{nBPnkpCc4kSQVLI7MMS1In4Y^koD6H7AwW{wgS)kcN#3W>3KvpV)zqKo60w z_cww1j4RgM4&MMhIK?tr0oU^zUYq>ZmZW9aIaY*b+p5Xxf#qGk9_!AYV7jeEy;Vk2 zYK@1b{oWjJBgO-)-3^|r$Iu%~B;wq0Jj{5X0=Q(${NcS&iI=_6^lf&e%xW1vyxOv+ zJ~62YR*X%`VY;N!;%2K^3hEss$BR1@96#i2vaKDPb-StAbfSs<#4UOp(^?A+2p2dR z@8{3rIUJISikZ7@WelrqJb|8_!yf5xw<2#q%eC@CI*)X0(|*py?WM?>i~v4>PR8gx zPD94$Pnhz~Rd zak^SNy5^6=#mCO(mJRM|F&UGPS9ud@O4Rm`Pd$6o=E(roJMV9%wBm}YevjvBEkc~0 zoUBYm^}J!4Z-bo_IP5)V-ITb?8GX%ZDZLz_+*44QxMQzeBA2|dG|8>PYMvz^IX(NZ z6n%q`6Kq{T@R@pS|Mz~BcvfRC4tfl0x!t`ZRNW9Pr&ERgAkWV3n&|ZziMfdBOf+zO zN=y>Z3uiGo>zt@o_L{%@BXo`f3E0F$bl4Mj+~P%xJpM%N7U}q8OP8W=vjUeuGhDnv zAz~RW^3f>T6S4tsipD97cfY*&_>v7F3GeiK{|xr7xf!_rZhFa*Kg{=3ZG=82m?4)` z+ERCpD!TVR?@Q-avH;w=hBrq4d&8`A$OBr{{lRpXr8qwBGFqguH@CTH>c?L_ss{wO zs^T0*E7LE{Wa3@)jl1Mrd<}AvF+YGh6Q~18sFd@n5Z4vUG*W>$zpFpYXwD3DE16c_ ztgQ;0y=pyq+&M(c@EMGK^ew9XZyE8xp*Qbav4VB7vnpDMGKF`&*g<(LOS$rqZ_b9s z8GCTumxEOD6UCnGn1_6MqSE#;n;Xa&EvR%smpl#B@{ov+g%+{AlbkBT#;;4_U?iQ^ z!bTg#XEHncyWsI8BlJyui7H#TO$SeqjdS#RY@zr1ByFa#gU0?ya5jcNnN%RJF^Oj{ zyc%2$+T4&j3zB5B1!2Qe4_go1&dDV*-oGiDgkN6ps&|l;(Y##y^7IveJmM}2=9Vuw z-BA!F;{OG|3c`Qpcr46$lTZYVChx-64*1CdDRw6$<={UXUYS0M3cT&AkJ(EGV^BM5 z5#(LdXNSHjznJFxGbuU4w+p!skEd4W+Wqj3mf1CKri6Pgw;>}H3}%u|(gZA-*#?kZ z2JU?#Zn7)Av1GGlt?DfF6+Y|Ehl*AuGHN|d=R3K&9PK{dl~a8}Dib$Xu2l|}%^!P> zvYB+ijkk2E`Rsa&5eJ)?T@luBP@daUdfkPjw_xTm*bo`NwU(yWP2IoHp` zp&_|zjBjHZ+)=UN5x<+iQlW&2av7~>Y|R?n)sQ%;+DS(7N#1h2N7yL8w8-kWaoO$L zI5h|Jo1S)HoIz9%LVNBWqS#tYn2pDBO&?R0R*-*s{hYq@x?8(f$#1EV;F5yickZSa zl>BCiS?k)PJ1xg=T=xo@Oeoyl3i%OTy+J>ma5Yp~&vaqMa6hX8+xPs9mw1?Kim+s} zVo&+lfcAyM;B$V7tG+=%ET`w(Q*wE~JGDVy+y!&@*D1Y@@t4D%!MB&r(`^srb2jZp zvCwZZU*+t|lOlyS0paF8g))iNeEMWS_N=&sajs6oDj$VK?ikqvzx>G-AmbQqin6P4 zZ&Z16D?s>mJ;3d1qCs}*SxP>!WC7<^inxTq=C4c+(Hk3meQOF~f$X~_0w9Did4(>3 z>MN_|&qnS0Yg~>BQsK3w_-S~R!eAkz83?og`*+V*+INV*EwU`VMRKQDi;O)M!B-GW zCt!ex9$rx(_->j{5KB|0elvY9@HtplM)jfBm-CZ3s-H0~O~+?wCuhCCh5V+Vxa3@3 zOblbHBQe`|c- zh(TPv5K2hb?^(!52#fZb-U2dfrFKmiI&r99ORC3{1=<=#Nq95Ww_pe!8|e67Z`9jO zD26jT{#aybogSnTFkA=Bm}RfSj<_n;uVDn(TUaicqh987)oxpcQ;L#2a%ESy0;zAv ibc>P_Nl0yx5@l}42L(&RER`lc8(sq6IKxd80PsJ;7-_Qr diff --git a/docs/docs/install/truenas.md b/docs/docs/install/truenas.md index de110e00a1..a13147a1be 100644 --- a/docs/docs/install/truenas.md +++ b/docs/docs/install/truenas.md @@ -2,6 +2,9 @@ sidebar_position: 80 --- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + # TrueNAS [Community] :::note @@ -9,211 +12,324 @@ This is a community contribution and not officially supported by the Immich team Community support can be found in the dedicated channel on the [Discord Server](https://discord.immich.app/). -**Please report app issues to the corresponding [Github Repository](https://github.com/truenas/apps/tree/master/trains/community/immich).** +**Please report app issues to the corresponding [GitHub Repository](https://github.com/truenas/apps/tree/master/trains/community/immich).** +::: + +:::warning +This guide covers the installation of Immich on TrueNAS Community Edition 24.10.2.2 (Electric Eel) and later. + +We recommend keeping TrueNAS Community Edition and Immich relatively up to date with the latest versions to avoid any issues. + +If you are using an older version of TrueNAS, we ask that you upgrade to the latest version before installing Immich. Check the [TrueNAS Community Edition Release Notes](https://www.truenas.com/docs/softwarereleases/) for more information on breaking changes, new features, and how to upgrade your system. ::: Immich can easily be installed on TrueNAS Community Edition via the **Community** train application. Consider reviewing the TrueNAS [Apps resources](https://apps.truenas.com/getting-started/) if you have not previously configured applications on your system. -TrueNAS Community Edition makes installing and updating Immich easy, but you must use the Immich web portal and mobile app to configure accounts and access libraries. - ## First Steps -The Immich app in TrueNAS Community Edition installs, completes the initial configuration, then starts the Immich web portal. -When updates become available, TrueNAS alerts and provides easy updates. - -Before installing the Immich app in TrueNAS, review the [Environment Variables](#environment-variables) documentation to see if you want to configure any during installation. -You may also configure environment variables at any time after deploying the application. - ### Setting up Storage Datasets Before beginning app installation, [create the datasets](https://www.truenas.com/docs/scale/scaletutorials/storage/datasets/datasetsscale/) to use in the **Storage Configuration** section during installation. -Immich requires seven datasets: `library`, `upload`, `thumbs`, `profile`, `video`, `backups`, and `pgData`. -You can organize these as one parent with seven child datasets, for example `/mnt/tank/immich/library`, `/mnt/tank/immich/upload`, and so on. + +In TrueNAS, Immich requires 2 datasets for the application to function correctly: `data` and `pgData`. You can set the datasets to any names to match your naming conventions or preferences. +You can organize these as one parent with two child datasets, for example `/mnt/tank/immich/data` and `/mnt/tank/immich/pgData`. Immich App Widget -:::info Permissions -The **pgData** dataset must be owned by the user `netdata` (UID 999) for postgres to start. The other datasets must be owned by the user `root` (UID 0) or a group that includes the user `root` (UID 0) for immich to have the necessary permissions. +:::info Datasets Permissions -If the **library** dataset uses ACL it must have [ACL mode](https://www.truenas.com/docs/core/coretutorials/storage/pools/permissions/#access-control-lists) set to `Passthrough` if you plan on using a [storage template](/docs/administration/storage-template.mdx) and the dataset is configured for network sharing (its ACL type is set to `SMB/NFSv4`). When the template is applied and files need to be moved from **upload** to **library**, Immich performs `chmod` internally and needs to be allowed to execute the command. [More info.](https://github.com/immich-app/immich/pull/13017) +The **pgData** dataset must be owned by the user `netdata` (UID 999) for Postgres to start. + +The `data` dataset must have given the **_modify_** permission to the user who will run Immich. + +Since TrueNAS Community Edition 24.10.2.2 and later, Immich can be run as any user and group, the default user being `apps` (UID 568) and the default group being `apps` (GID 568). This user, either `apps` or another user you choose, must have **_modify_** permissions on the **data** dataset. + +For an easy setup: + +- Create the parent dataset `immich` keeping the default **Generic** preset. +- Select `Dataset Preset` **Apps** instead of **Generic** when creating the `data` dataset. This will automatically give the correct permissions to the dataset. If you want to use another user for Immich, you can keep the **Generic** preset, but you will need to give the **_modify_** permission to that other user. +- For the `pgData` dataset, you can keep the default preset **Generic** as permissions can be set during the installation of the Immich app (See [Storage Configuration](#storage-configuration) section). + ::: + +:::tip +To improve performance, Immich recommends using SSDs for the database. If you have a pool made of SSDs, you can create the `pgData` dataset on that pool. + +Thumbnails can also be stored on the SSDs for faster access. This is an advanced option and not required for Immich to run. More information on how you can use multiple datasets to manage Immich storage in a finer-grained manner can be found in the [Advanced: Multiple Datasets for Immich Storage](#advanced-multiple-datasets-for-immich-storage) section below. +::: + +:::warning +If you just created the datasets using the **Apps** preset, you can skip this warning section. + +If the **data** dataset uses ACL it must have [ACL mode](https://www.truenas.com/docs/scale/scaletutorials/datasets/permissionsscale/) set to `Passthrough` if you plan on using a [storage template](/docs/administration/storage-template.mdx) and the dataset is configured for network sharing (its ACL type is set to `SMB/NFSv4`). When the template is applied and files need to be moved from **upload** to **library** (internal folder created by Immich within the **data** dataset), Immich performs `chmod` internally and must be allowed to execute the command. [More info.](https://github.com/immich-app/immich/pull/13017) + +To change or verify the ACL mode, go to the **Datasets** screen, select the **library** dataset, click on the **Edit** button next to **Dataset Details**, then click on the **Advanced Options** tab, scroll down to the **ACL Mode** section, and select `Passthrough` from the dropdown menu. Click **Save** to apply the changes. If the option is greyed out, set the **ACL Type** to `SMB/NFSv4` first, then you can change the **ACL Mode** to `Passthrough`. ::: ## Installing the Immich Application -To install the **Immich** application, go to **Apps**, click **Discover Apps**, either begin typing Immich into the search field or scroll down to locate the **Immich** application widget. +To install the **Immich** application, go to **Apps**, click **Discover Apps**, and either begin typing Immich into the search field or scroll down to locate the **Immich** application widget. +
    + +Click on the widget to open the **Immich** application details screen. Immich App Widget -Click on the widget to open the **Immich** application details screen. +
    -

    +
    +Click **Install** to open the Immich application configuration screen. Immich App Details Screen -Click **Install** to open the Immich application configuration screen. - -

    +
    Application configuration settings are presented in several sections, each explained below. -To find specific fields click in the **Search Input Fields** search field, scroll down to a particular section or click on the section heading on the navigation area in the upper-right corner. +To find specific fields, click in the **Search Input Fields** search field, scroll down to a particular section, or click on the section heading on the navigation area in the upper-right corner. ### Application Name and Version Install Immich Screen -Accept the default value or enter a name in **Application Name** field. -In most cases use the default name, but if adding a second deployment of the application you must change this name. +Keep the default value or enter a name in the **Application Name** field. +Change it if you’re deploying a second instance. -Accept the default version number in **Version**. -When a new version becomes available, the application has an update badge. -The **Installed Applications** screen shows the option to update applications. +Immich version within TrueNAS catalog (Different from Immich release version). ### Immich Configuration Configuration Settings + +The **Timezone** is set to the system default, which usually matches your local timezone. You can change it to another timezone if you prefer. + +**Enable Machine Learning** is enabled by default. It allows Immich to use machine learning features such as face recognition, image search, and smart duplicate detection. Untick this option if you do not want to use these features. + +Select the **Machine Learning Image Type** based on the hardware you have. More details here: [Hardware-Accelerated Machine Learning](/docs/features/ml-hardware-acceleration.md) + +**Database Password** should be set to a custom value using only the characters `A-Za-z0-9`. This password is used to secure the Postgres database. + +**Redis Password** should be set to a custom value using only the characters `A-Za-z0-9`. Preferably, use a different password from the database password. + +Keep the **Log Level** to the default `Log` value. + +Leave **Hugging Face Endpoint** blank. (This is used to download ML models from a different source.) + +Set **Database Storage Type** to the type of storage (**HDD** or **SSD**) that the pool where the **pgData** dataset is located uses. + +**Additional Environment Variables** can be left blank. + +
    +Advanced users: Adding Environment Variables + +Environment variables can be set by clicking the **Add** button and filling in the **Name** and **Value** fields. + + -Accept the default value in **Timezone** or change to match your local timezone. -**Timezone** is only used by the Immich `exiftool` microservice if it cannot be determined from the image metadata. +These are used to add custom configuration options or to enable specific features. +More information on available environment variables can be found in the **[environment variables documentation](/docs/install/environment-variables/)**. -Untick **Enable Machine Learning** if you will not use face recognition, image search, and smart duplicate detection. +:::info +Some environment variables are not available for the TrueNAS Community Edition app as they can be configured through GUI options in the [Edit Immich screen](#edit-app-settings). -Accept the default option or select the **Machine Learning Image Type** for your hardware based on the [Hardware-Accelerated Machine Learning Supported Backends](/docs/features/ml-hardware-acceleration.md#supported-backends). +Some examples are: `IMMICH_VERSION`, `UPLOAD_LOCATION`, `DB_DATA_LOCATION`, `TZ`, `IMMICH_LOG_LEVEL`, `DB_PASSWORD`, `REDIS_PASSWORD`. +::: -Immich's default is `postgres` but you should consider setting the **Database Password** to a custom value using only the characters `A-Za-z0-9`. +
    -The **Redis Password** should be set to a custom value using only the characters `A-Za-z0-9`. +### User and Group Configuration -Accept the **Log Level** default of **Log**. +Application in TrueNAS runs as a specific user and group. Immich uses the default user `apps` (UID 568) and the default group `apps` (GID 568). -Leave **Hugging Face Endpoint** blank. (This is for downloading ML models from a different source.) + -Leave **Additional Environment Variables** blank or see [Environment Variables](#environment-variables) to set before installing. +- **User ID**: Keep the default value `apps` (UID 568) or define a different one if needed. + +- **Group ID**: Keep the default value `apps` (GID 568) or define a different one if needed. + +:::warning +If you change the user or group, make sure that the datasets you created for Immich data storage have the correct permissions set for that user and group as specified in the [Setting up Storage Datasets](#setting-up-storage-datasets) section above. +::: ### Network Configuration Networking Settings -Accept the default port `30041` in **WebUI Port** or enter a custom port number. -:::info Allowed Port Numbers -Only numbers within the range 9000-65535 may be used on TrueNAS versions below TrueNAS Community Edition 24.10 Electric Eel. +- **Port Bind Mode**: This lets you expose the port to the host system, allowing you to access Immich from outside the TrueNAS system. Keep the default **_Publish port on the host for external access_** value unless you have a specific reason to change it. -Regardless of version, to avoid port conflicts, don't use [ports on this list](https://www.truenas.com/docs/solutions/optimizations/security/#truenas-default-ports). -::: +- **Port Number**: Keep the default port `30041` or enter a custom port number. + +- **Host IPs**: Leave the default blank value. ### Storage Configuration -Immich requires seven storage datasets. - - - -:::note Default Setting (Not recommended) -The default setting for datasets is **ixVolume (dataset created automatically by the system)** but this results in your data being harder to access manually and can result in data loss if you delete the immich app. (Not recommended) +:::danger Default Settings (Not recommended) +The default setting for datasets is **ixVolume (dataset created automatically by the system)**. This is not recommended as this results in your data being harder to access manually and can result in data loss if you delete the immich app. It is also harder to manage snapshots and replication tasks. It is recommended to use the **Host Path (Path that already exists on the system)** option instead. ::: -For each Storage option select **Host Path (Path that already exists on the system)** and then select the matching dataset [created before installing the app](#setting-up-storage-datasets): **Immich Library Storage**: `library`, **Immich Uploads Storage**: `upload`, **Immich Thumbs Storage**: `thumbs`, **Immich Profile Storage**: `profile`, **Immich Video Storage**: `video`, **Immich Backups Storage**: `backups`, **Postgres Data Storage**: `pgData`. +The storage configuration section allows you to set up the storage locations for Immich data. You can select the datasets created in the previous step. -The image above has example values. -
    +For the Data Storage, select **Host Path (Path that already exists on the system)** and then select the dataset you created for Immich data storage, for example, `data`. -### Additional Storage [(External Libraries)](/docs/features/libraries) +The Machine Learning cache can be left with default _Temporary_ + +For the Postgres Data Storage, select **Host Path (Path that already exists on the system)** and then select the dataset you created for Postgres data storage, for example, `pgData`. + +:::info +**Postgres Data Storage** +Once **Host Path** is selected, a checkbox appears with **_Automatic Permissions_**. If you have not set the ownership of the **pgData** dataset to `netdata` (UID 999), tick this box as it will set the user ownership to `netdata` (UID 999) and the group ownership to `docker` (GID 999) automatically. If you have set the ownership of the **pgData** dataset to `netdata` (UID 999), you can leave this box unticked. +::: + +### Additional Storage (Advanced Users) + +
    +External Libraries :::danger Advanced Users Only -This feature should only be used by advanced users. If this is your first time installing Immich, then DO NOT mount an external library until you have a working setup. Also, your mount path MUST be something unique and should NOT be your library or upload location or a Linux directory like `/lib`. The picture below shows a valid example. +This feature should only be used by advanced users. If this is your first time installing Immich, then DO NOT mount an external library until you have a working setup. ::: -You may configure [External Libraries](/docs/features/libraries) by mounting them using **Additional Storage**. -The **Mount Path** is the location you will need to copy and paste into the External Library settings within Immich. -The **Host Path** is the location on the TrueNAS Community Edition server where your external library is located. +You may configure [external libraries](/docs/features/libraries) by mounting them using **Additional Storage**. - +The dataset that contains your external library files must at least give **read** access to the user running Immich (Default: `apps` (UID 568), `apps` (GID 568)). +If you want to be able to delete files or edit metadata in the external library using Immich, you will need to give the **modify** permission to the user running Immich. + +- **Mount Path** is the location you will need to copy and paste into the external library settings within Immich. +- **Host Path** is the location on the TrueNAS Community Edition server where your external library is located. +- **Read Only** is a checkbox that you can tick if you want to prevent Immich from modifying the files in the external library. This is useful if you want to use Immich to view and search your external library without modifying it. + +:::warning +Each mount path MUST be something unique and should NOT be your library or upload location or a Linux directory like `/lib`. + +A general recommendation is to mount any external libraries to a path beginning with `/mnt` or `/media` followed by a unique name, such as `/mnt/external-libraries` or `/media/my-external-libraries`. If you plan to mount multiple external libraries, you can use paths like `/mnt/external-libraries/library1`, `/mnt/external-libraries/library2`, etc. +::: + +
    + +
    +Multiple Datasets for Immich Storage + +:::danger Advanced Users Only +This feature should only be used by advanced users. +::: + +Immich can use multiple datasets for its storage, allowing you to manage your data more granularly, similar to the old storage configuration. This is useful if you want to separate your data into different datasets for performance or organizational reasons. There is a general guide for this [here](/docs/guides/custom-locations), but read on for the TrueNAS guide. + +Each additional dataset has to give the permission **_modify_** to the user who will run Immich (Default: `apps` (UID 568), `apps` (GID 568)) +As described in the [Setting up Storage Datasets](#setting-up-storage-datasets) section above, you have to create the datasets with the **Apps** preset to ensure the correct permissions are set, or you can set the permissions manually after creating the datasets. + +Immich uses 6 folders for its storage: `library`, `upload`, `thumbs`, `profile`, `encoded-video`, and `backups`. You can create a dataset for each of these folders or only for some of them. + +To mount these datasets: + +1. Add an **Additional Storage** entry for each dataset you want to use. +2. Select **Type** as **Host Path (Path that already exists on the system)**. +3. Enter the **Mount Path** with `/data/`. The `` is the name of the folder you want to mount, for example, `library`, `upload`, `thumbs`, `profile`, `encoded-video`, or `backups`. + :::danger Important + You have to write the full path, including `/data/`, as Immich expects the data to be in that location. + If you do not include this path, Immich will not be able to find the data and will not write the data to the location you specified. + ::: +4. Select the **Host Path** as the dataset you created for that folder, for example, `/mnt/tank/immich/library`, `/mnt/tank/immich/upload`, etc. + + + +
    + + ### Resources Configuration -Accept the default **CPU** limit of `2` threads or specify the number of threads (CPUs with Multi-/Hyper-threading have 2 threads per core). +- **CPU**: Depending on your system resources, you can keep the default value of `2` threads or specify a different number. Immich recommends at least `8` threads. -Specify the **Memory** limit in MB of RAM. Immich recommends at least 6000 MB (6GB). If you selected **Enable Machine Learning** in **Immich Configuration**, you should probably set this above 8000 MB. +- **Memory**: Limit in MB of RAM. Immich recommends at least 6000 MB (6GB). If you selected **Enable Machine Learning** in **Immich Configuration**, you should probably set this above 8000 MB. -:::info Older TrueNAS Versions -Before TrueNAS Community Edition version 24.10 Electric Eel: +Both **CPU** and **Memory** are limits, not reservations. This means that Immich can use up to the specified amount of CPU threads and RAM, but it will not reserve that amount of resources at all times. The system will allocate resources as needed, and Immich will use less than the specified amount most of the time. -The **CPU** value was specified in a different format with a default of `4000m` which is 4 threads. +- Enable **GPU Configuration** options if you have a GPU or CPU with integrated graphics that you will use for [Hardware Transcoding](/docs/features/hardware-transcoding) and/or [Hardware-Accelerated Machine Learning](/docs/features/ml-hardware-acceleration.md). -The **Memory** value was specified in a different format with a default of `8Gi` which is 8 GiB of RAM. The value was specified in bytes or a number with a measurement suffix. Examples: `129M`, `123Mi`, `1000000000` -::: - -Enable **GPU Configuration** options if you have a GPU that you will use for [Hardware Transcoding](/docs/features/hardware-transcoding) and/or [Hardware-Accelerated Machine Learning](/docs/features/ml-hardware-acceleration.md). More info: [GPU Passthrough Docs for TrueNAS Apps](https://apps.truenas.com/managing-apps/installing-apps/#gpu-passthrough) +The process for NVIDIA GPU passthrough requires additional steps. +More details here: [GPU Passthrough Docs for TrueNAS Apps](https://apps.truenas.com/managing-apps/installing-apps/#gpu-passthrough) ### Install Finally, click **Install**. The system opens the **Installed Applications** screen with the Immich app in the **Deploying** state. -When the installation completes it changes to **Running**. +When the installation completes, it changes to **Running**. Immich Installed -Click **Web Portal** on the **Application Info** widget to open the Immich web interface to set up your account and begin uploading photos. +Click **Web Portal** on the **Application Info** widget, or go to the URL `http://:30041` in your web browser to open the Immich web interface. This will show you the onboarding process to set up your first user account, which will be an administrator account. + +After that, you can start using Immich to upload and manage your photos and videos. :::tip For more information on how to use the application once installed, please refer to the [Post Install](/docs/install/post-install.mdx) guide. @@ -228,23 +344,6 @@ For more information on how to use the application once installed, please refer - Click **Update** at the very bottom of the page to save changes. - TrueNAS automatically updates, recreates, and redeploys the Immich container with the updated settings. -## Environment Variables - -You can set [Environment Variables](/docs/install/environment-variables) by clicking **Add** on the **Additional Environment Variables** option and filling in the **Name** and **Value**. - - - -:::info -Some Environment Variables are not available for the TrueNAS Community Edition app. This is mainly because they can be configured through GUI options in the [Edit Immich screen](#edit-app-settings). - -Some examples are: `IMMICH_VERSION`, `UPLOAD_LOCATION`, `DB_DATA_LOCATION`, `TZ`, `IMMICH_LOG_LEVEL`, `DB_PASSWORD`, `REDIS_PASSWORD`. -::: - ## Updating the App :::danger @@ -261,3 +360,116 @@ To update the app to the latest version: - You may view the Changelog. - Click **Upgrade** to begin the process and open a counter dialog that shows the upgrade progress. - When complete, the update badge and buttons disappear and the application Update state on the Installed screen changes from Update Available to Up to date. + +## Migration + +:::danger +Perform a backup of your Immich data before proceeding with the migration steps below. This is crucial to prevent any data loss if something goes wrong during the migration process. + +The migration should also be performed when the Immich app is not running to ensure no data is being written while you are copying the data. +::: + +### Migration from Old Storage Configuration + +There are two ways to migrate from the old storage configuration to the new one, depending on whether you want to keep the old multiple datasets or if you want to move to a double dataset configuration with a single dataset for Immich data storage and a single dataset for Postgres data storage. + +:::note Old TrueNAS Versions Permissions +If you were using an older version of TrueNAS (before 24.10.2.2), the datasets, except the one for **pgData** had only to be owned by the `root` user (UID 0). You might have to add the **modify** permission to the `apps` user (UID 568) or the user you want to run Immich as, to all of them, except **pgData**. The steps to add or change ACL permissions are described in the [TrueNAS documentation](https://www.truenas.com/docs/scale/scaletutorials/datasets/permissionsscale/). +::: + + + + +To migrate from the old storage configuration to the new one, you will need to create a new dataset for the Immich data storage and copy the data from the old datasets to the new ones. The steps are as follows: + +1. **Stop the Immich app** from the TrueNAS web interface to ensure no data is being written while you are copying the data. +2. **Create a new dataset** for the Immich data storage, for example, `data`. As described in the [Setting up Storage Datasets](#setting-up-storage-datasets) section above, create the dataset with the **Apps** preset to ensure the correct permissions are set. +3. **Copy the data** from the old datasets to the new dataset. We advise using the `rsync` command to copy the data, as it will preserve the permissions and ownership of the files. The following commands are examples: + +```bash +rsync -av /mnt/tank/immich/library/ /mnt/tank/immich/data/library/ +rsync -av /mnt/tank/immich/upload/ /mnt/tank/immich/data/upload/ +rsync -av /mnt/tank/immich/thumbs/ /mnt/tank/immich/data/thumbs/ +rsync -av /mnt/tank/immich/profile/ /mnt/tank/immich/data/profile/ +rsync -av /mnt/tank/immich/video/ /mnt/tank/immich/data/encoded-video/ +rsync -av /mnt/tank/immich/backups/ /mnt/tank/immich/data/backups/ +``` + +Make sure to replace `/mnt/tank/immich/` with the correct path to your old datasets and `/mnt/tank/immich/data/` with the correct path to your new dataset. + +:::tip +If you were using **ixVolume (dataset created automatically by the system)** for Immich data storage, the path to the data should be `/mnt/.ix-apps/app_mounts/immich/`. You have to use this path instead of `/mnt/tank/immich/` in the `rsync` command above, for example: + +```bash +rsync -av /mnt/.ix-apps/app_mounts/immich/library/ /mnt/tank/immich/data/library/ +``` + +If you were also using an ixVolume for Postgres data storage, you also should, first create the pgData dataset, as described in the [Setting up Storage Datasets](#setting-up-storage-datasets) section above, and then you can use the following command to copy the Postgres data: + +```bash +rsync -av /mnt/.ix-apps/app_mounts/immich/pgData/ /mnt/tank/immich/pgData/ +``` + +::: + +:::warning +Make sure that for each folder, the `.immich` file is copied as well, as it contains important metadata for Immich. If for some reason the `.immich` file is not copied, you can copy it manually with the `rsync` command, for example: + +```bash +rsync -av /mnt/tank/immich/library/.immich /mnt/tank/immich/data/library/ +``` + +Replace `library` with the name of the folder where you are copying the file. +::: + +4. **Update the permissions** as the permissions of the data that have been copied has been preserved, to ensure that the `apps` user (UID 568) has the correct permissions on all the copied data. If you just created the dataset with the **Apps** preset, from the TrueNAS web interface, go to the **Datasets** screen, select the **data** dataset, click on the **Edit** button next to **Permissions**, tick the "Apply permissions recursively" checkbox, and click **Save**. This will apply the correct permissions to all the copied data. +5. **Update the Immich app** to use the new dataset: + - Go to the **Installed Applications** screen and select Immich from the list of installed applications. + - Click **Edit** on the **Application Info** widget. + - In the **Storage Configuration** section, untick the **Use Old Storage Configuration (Deprecated)** checkbox. + - For the **Data Storage**, select **Host Path (Path that already exists on the system)** and then select the new dataset you created for Immich data storage, for example, `data`. + - For the **Postgres Data Storage**, verify that it is still set to the dataset you created for Postgres data storage, for example, `pgData`. + - Click **Update** at the bottom of the page to save changes. + +6. **Start the Immich app** from the TrueNAS web interface. + +This will recreate the Immich container with the new storage configuration and start the app. + +If everything went well, you should now be able to access Immich with the new storage configuration. You can verify that the data has been copied correctly by checking the Immich web interface and ensuring that all your photos and videos are still available. You may delete the old datasets, if you no longer need them, using the TrueNAS web interface. + +If you were using **ixVolume (dataset created automatically by the system)** or folders for Immich data storage, you can delete the old datasets using the following commands: + +```bash +rm -r /mnt/.ix-apps/app_mounts/immich/library +rm -r /mnt/.ix-apps/app_mounts/immich/uploads +rm -r /mnt/.ix-apps/app_mounts/immich/thumbs +rm -r /mnt/.ix-apps/app_mounts/immich/profile +rm -r /mnt/.ix-apps/app_mounts/immich/video +rm -r /mnt/.ix-apps/app_mounts/immich/backups +``` + + + + +To migrate from the old storage configuration to the new one without creating new datasets. +1. **Stop the Immich app** from the TrueNAS web interface to ensure no data is being written while you are updating the app. +2. **Update the datasets permissions**: Ensure that the datasets used for Immich data storage (`library`, `upload`, `thumbs`, `profile`, `video`, `backups`) have the correct permissions set for the user who will run Immich. The user should have ***modify*** permissions on these datasets. The default user for Immich is `apps` (UID 568) and the default group is `apps` (GID 568). If you are using a different user, make sure to set the permissions accordingly. You can do this from the TrueNAS web interface by going to the **Datasets** screen, selecting each dataset, clicking on the **Edit** button next to **Permissions**, and adding the user with ***modify*** permissions. +3. **Update the Immich app** to use the existing datasets: + - Go to the **Installed Applications** screen and select Immich from the list of installed applications. + - Click **Edit** on the **Application Info** widget. + - In the **Storage Configuration** section, untick the **Use Old Storage Configuration (Deprecated)** checkbox. + - For the **Data Storage**, you can keep the **ixVolume (dataset created automatically by the system)** as no data will be directly written to it. We recommend selecting **Host Path (Path that already exists on the system)** and then select a **new** dataset you created for Immich data storage, for example, `data`. + - For the **Postgres Data Storage**, keep **Host Path (Path that already exists on the system)** and then select the existing dataset you used for Postgres data storage, for example, `pgData`. + - Following the instructions in the [Multiple Datasets for Immich Storage](#additional-storage-advanced-users) section, you can add, **for each old dataset**, a new Additional Storage with the following settings: + - **Type**: `Host Path (Path that already exists on the system)` + - **Mount Path**: `/data/` (e.g. `/data/library`) + - **Host Path**: `/mnt//` (e.g. `/mnt/tank/immich/library`) + :::danger Ensure using the correct paths names + Make sure to replace `` with the actual name of the folder used by Immich: `library`, `upload`, `thumbs`, `profile`, `encoded-video`, and `backups`. Also, replace `` and `` with the actual names of your pool and dataset. + ::: + - **Read Only**: Keep it unticked as Immich needs to write to these datasets. + - Click **Update** at the bottom of the page to save changes. +4. **Start the Immich app** from the TrueNAS web interface. This will recreate the Immich container with the new storage configuration and start the app. If everything went well, you should now be able to access Immich with the new storage configuration. You can verify that the data is still available by checking the Immich web interface and ensuring that all your photos and videos are still accessible. + + + From 1193a23282a2ad3f733d12e5e15e2a130a8af320 Mon Sep 17 00:00:00 2001 From: Thomas <9749173+uhthomas@users.noreply.github.com> Date: Wed, 6 Aug 2025 22:31:37 +0100 Subject: [PATCH 195/748] feat(web): don't scroll to visible assets (#20729) The timeline has been quite aggressive with scrolling to assets, even if they were right in the middle of the page. If the asset is visible, then we shouldn't scroll to it. It's really confusing when assets jump around after being viewed. --- .../lib/components/photos-page/asset-grid.svelte | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/web/src/lib/components/photos-page/asset-grid.svelte b/web/src/lib/components/photos-page/asset-grid.svelte index 808a4cf54c..69faf8e1e5 100644 --- a/web/src/lib/components/photos-page/asset-grid.svelte +++ b/web/src/lib/components/photos-page/asset-grid.svelte @@ -149,12 +149,28 @@ return height; }; + const assetIsVisible = (assetTop: number): boolean => { + if (!element) { + return false; + } + + const { clientHeight, scrollTop } = element; + return assetTop >= scrollTop && assetTop < scrollTop + clientHeight; + }; + const scrollToAssetId = async (assetId: string) => { const monthGroup = await timelineManager.findMonthGroupForAsset(assetId); if (!monthGroup) { return false; } + const height = getAssetHeight(assetId, monthGroup); + + // If the asset is already visible, then don't scroll. + if (assetIsVisible(height)) { + return true; + } + scrollTo(height); updateSlidingWindow(); return true; From 89292fecb4d1319658486d1065f3dd6084f45dda Mon Sep 17 00:00:00 2001 From: Thomas <9749173+uhthomas@users.noreply.github.com> Date: Wed, 6 Aug 2025 22:34:24 +0100 Subject: [PATCH 196/748] fix(web): use correct sliding window offset for search results (#20726) The contents of search results are slightly offset by the search bar, search terms and spacing (margins/padding), and needs to be factored in when calculating whether an asset is visible or not. The offset was 0, which meant that assets were removed from view too early. --- .../search/[[photos=photos]]/[[assetId=id]]/+page.svelte | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte index e7798b1ac7..b3177fc39e 100644 --- a/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -52,6 +52,7 @@ let { isViewing: showAssetViewer } = assetViewingStore; const viewport: Viewport = $state({ width: 0, height: 0 }); + let searchResultsElement: HTMLElement | undefined = $state(); // The GalleryViewer pushes it's own history state, which causes weird // behavior for history.back(). To prevent that we store the previous page @@ -362,6 +363,7 @@ class="mb-12 bg-immich-bg dark:bg-immich-dark-bg m-4" bind:clientHeight={viewport.height} bind:clientWidth={viewport.width} + bind:this={searchResultsElement} > {#if searchResultAlbums.length > 0}
    @@ -381,8 +383,8 @@ onIntersected={loadNextPage} showArchiveIcon={true} {viewport} - pageHeaderOffset={54} onReload={onSearchQueryUpdate} + slidingWindowOffset={searchResultsElement.offsetTop} /> {:else if !isLoading}
    From 68b617130abf3a109fa356024b159f65aea0c027 Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Thu, 7 Aug 2025 03:22:42 +0530 Subject: [PATCH 197/748] chore: disable android auto backup (#20734) Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- mobile/android/app/src/main/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/android/app/src/main/AndroidManifest.xml b/mobile/android/app/src/main/AndroidManifest.xml index 09276f6d4a..eee4ff2ddc 100644 --- a/mobile/android/app/src/main/AndroidManifest.xml +++ b/mobile/android/app/src/main/AndroidManifest.xml @@ -27,7 +27,7 @@ + android:largeHeap="true" android:enableOnBackInvokedCallback="false" android:allowBackup="false"> Date: Thu, 7 Aug 2025 03:27:17 +0530 Subject: [PATCH 198/748] fix: cleanup logger DB in isolates (#20730) Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- .../test_utils/general_helper.dart | 29 +++++++------------ mobile/lib/main.dart | 4 ++- mobile/lib/services/background.service.dart | 4 ++- .../services/backup_verification.service.dart | 4 ++- mobile/lib/utils/bootstrap.dart | 7 ++--- mobile/lib/utils/isolate.dart | 5 +++- 6 files changed, 25 insertions(+), 28 deletions(-) diff --git a/mobile/integration_test/test_utils/general_helper.dart b/mobile/integration_test/test_utils/general_helper.dart index 8e17bae9d3..550f44b501 100644 --- a/mobile/integration_test/test_utils/general_helper.dart +++ b/mobile/integration_test/test_utils/general_helper.dart @@ -4,6 +4,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart'; import 'package:immich_mobile/main.dart' as app; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; @@ -40,16 +41,14 @@ class ImmichTestHelper { await EasyLocalization.ensureInitialized(); // Clear all data from Isar (reuse existing instance if available) final db = await Bootstrap.initIsar(); - await Bootstrap.initDomain(db); + final logDb = DriftLogger(); + await Bootstrap.initDomain(db, logDb); await Store.clear(); await db.writeTxn(() => db.clear()); // Load main Widget await tester.pumpWidget( ProviderScope( - overrides: [ - dbProvider.overrideWithValue(db), - isarProvider.overrideWithValue(db), - ], + overrides: [dbProvider.overrideWithValue(db), isarProvider.overrideWithValue(db)], child: const app.MainWidget(), ), ); @@ -59,18 +58,11 @@ class ImmichTestHelper { } @isTest -void immichWidgetTest( - String description, - Future Function(WidgetTester, ImmichTestHelper) test, -) { - testWidgets( - description, - (widgetTester) async { - await ImmichTestHelper.loadApp(widgetTester); - await test(widgetTester, ImmichTestHelper(widgetTester)); - }, - semanticsEnabled: false, - ); +void immichWidgetTest(String description, Future Function(WidgetTester, ImmichTestHelper) test) { + testWidgets(description, (widgetTester) async { + await ImmichTestHelper.loadApp(widgetTester); + await test(widgetTester, ImmichTestHelper(widgetTester)); + }, semanticsEnabled: false); } Future pumpUntilFound( @@ -79,8 +71,7 @@ Future pumpUntilFound( Duration timeout = const Duration(seconds: 120), }) async { bool found = false; - final timer = - Timer(timeout, () => throw TimeoutException("Pump until has timed out")); + final timer = Timer(timeout, () => throw TimeoutException("Pump until has timed out")); while (found != true) { await tester.pump(); found = tester.any(finder); diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 0bac282694..19ed746833 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -14,6 +14,7 @@ import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/constants/locales.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/generated/codegen_loader.g.dart'; +import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart'; import 'package:immich_mobile/providers/app_life_cycle.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/share_intent_upload.provider.dart'; import 'package:immich_mobile/providers/db.provider.dart'; @@ -41,7 +42,8 @@ import 'package:worker_manager/worker_manager.dart'; void main() async { ImmichWidgetsBinding(); final db = await Bootstrap.initIsar(); - await Bootstrap.initDomain(db); + final logDb = DriftLogger(); + await Bootstrap.initDomain(db, logDb); await initApp(); // Warm-up isolate pool for worker manager await workerManager.init(dynamicSpawning: true); diff --git a/mobile/lib/services/background.service.dart b/mobile/lib/services/background.service.dart index dea20b6d98..3bcc93f19f 100644 --- a/mobile/lib/services/background.service.dart +++ b/mobile/lib/services/background.service.dart @@ -14,6 +14,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/backup_album.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart'; import 'package:immich_mobile/models/backup/backup_candidate.model.dart'; import 'package:immich_mobile/models/backup/current_upload_asset.model.dart'; import 'package:immich_mobile/models/backup/error_upload_asset.model.dart'; @@ -331,7 +332,8 @@ class BackgroundService { Future _onAssetsChanged() async { final db = await Bootstrap.initIsar(); - await Bootstrap.initDomain(db); + final logDb = DriftLogger(); + await Bootstrap.initDomain(db, logDb); final ref = ProviderContainer(overrides: [dbProvider.overrideWithValue(db), isarProvider.overrideWithValue(db)]); diff --git a/mobile/lib/services/backup_verification.service.dart b/mobile/lib/services/backup_verification.service.dart index c0f3c0205e..8f39fd17e5 100644 --- a/mobile/lib/services/backup_verification.service.dart +++ b/mobile/lib/services/backup_verification.service.dart @@ -11,6 +11,7 @@ import 'package:immich_mobile/domain/services/user.service.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/exif.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart'; import 'package:immich_mobile/infrastructure/utils/exif.converter.dart'; import 'package:immich_mobile/providers/infrastructure/exif.provider.dart'; import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; @@ -116,7 +117,8 @@ class BackupVerificationService { final List result = []; BackgroundIsolateBinaryMessenger.ensureInitialized(tuple.rootIsolateToken); final db = await Bootstrap.initIsar(); - await Bootstrap.initDomain(db); + final logDb = DriftLogger(); + await Bootstrap.initDomain(db, logDb); await tuple.fileMediaRepository.enableBackgroundAccess(); final ApiService apiService = ApiService(); apiService.setEndpoint(tuple.endpoint); diff --git a/mobile/lib/utils/bootstrap.dart b/mobile/lib/utils/bootstrap.dart index 9cab9caf9e..d2ad5ea16f 100644 --- a/mobile/lib/utils/bootstrap.dart +++ b/mobile/lib/utils/bootstrap.dart @@ -47,14 +47,11 @@ abstract final class Bootstrap { ); } - static Future initDomain(Isar db, {bool shouldBufferLogs = true}) async { - // load drift dbs - final loggerDb = DriftLogger(); - + static Future initDomain(Isar db, DriftLogger logDb, {bool shouldBufferLogs = true}) async { await StoreService.init(storeRepository: IsarStoreRepository(db)); await LogService.init( - logRepository: LogRepository(loggerDb), + logRepository: LogRepository(logDb), storeRepository: IsarStoreRepository(db), shouldBuffer: shouldBufferLogs, ); diff --git a/mobile/lib/utils/isolate.dart b/mobile/lib/utils/isolate.dart index 01903cfc74..2dfd9d4f5f 100644 --- a/mobile/lib/utils/isolate.dart +++ b/mobile/lib/utils/isolate.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/services/log.service.dart'; +import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart'; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/providers/infrastructure/cancel.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; @@ -35,7 +36,8 @@ Cancelable runInIsolateGentle({ DartPluginRegistrant.ensureInitialized(); final db = await Bootstrap.initIsar(); - await Bootstrap.initDomain(db, shouldBufferLogs: false); + final logDb = DriftLogger(); + await Bootstrap.initDomain(db, logDb, shouldBufferLogs: false); final ref = ProviderContainer( overrides: [ // TODO: Remove once isar is removed @@ -57,6 +59,7 @@ Cancelable runInIsolateGentle({ } finally { try { await LogService.I.flush(); + await logDb.close(); await ref.read(driftProvider).close(); // Close Isar safely From f1c494ef973f668bae6e9260a537e9baeb448f5d Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Thu, 7 Aug 2025 03:27:33 +0530 Subject: [PATCH 199/748] fix: use create if not exists clause for indexes (#20728) Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- mobile/drift_schemas/main/drift_schema_v7.json | 2 +- mobile/lib/infrastructure/entities/exif.entity.dart | 2 +- mobile/lib/infrastructure/entities/exif.entity.drift.dart | 2 +- .../lib/infrastructure/entities/local_asset.entity.dart | 2 +- .../infrastructure/entities/local_asset.entity.drift.dart | 2 +- .../lib/infrastructure/entities/remote_asset.entity.dart | 6 ++++-- .../entities/remote_asset.entity.drift.dart | 4 ++-- mobile/lib/infrastructure/repositories/db.repository.dart | 3 +++ .../infrastructure/repositories/db.repository.steps.dart | 8 ++++---- mobile/test/drift/main/generated/schema_v7.dart | 8 ++++---- 10 files changed, 22 insertions(+), 17 deletions(-) diff --git a/mobile/drift_schemas/main/drift_schema_v7.json b/mobile/drift_schemas/main/drift_schema_v7.json index 77f57c34d9..bcd502bdc0 100644 --- a/mobile/drift_schemas/main/drift_schema_v7.json +++ b/mobile/drift_schemas/main/drift_schema_v7.json @@ -1 +1 @@ -{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"has_profile_image","getter_name":"hasProfileImage","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"has_profile_image\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"has_profile_image\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"profile_changed_at","getter_name":"profileChangedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"library_id","getter_name":"libraryId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":5,"references":[3,4],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":6,"references":[3],"type":"index","data":{"on":3,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_owner_checksum","sql":null,"unique":false,"columns":["owner_id","checksum"]}},{"id":8,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum\nON remote_asset_entity (owner_id, checksum)\nWHERE (library_id IS NULL);\n","unique":true,"columns":[]}},{"id":9,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_library_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum\nON remote_asset_entity (owner_id, library_id, checksum)\nWHERE (library_id IS NOT NULL);\n","unique":true,"columns":[]}},{"id":10,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":11,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":12,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":13,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":14,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":15,"references":[1,14],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":16,"references":[14,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":18,"references":[1,17],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":19,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":20,"references":[1,19],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":21,"references":[13],"type":"index","data":{"on":13,"name":"idx_lat_lng","sql":null,"unique":false,"columns":["latitude","longitude"]}}]} \ No newline at end of file +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"has_profile_image","getter_name":"hasProfileImage","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"has_profile_image\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"has_profile_image\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"profile_changed_at","getter_name":"profileChangedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"library_id","getter_name":"libraryId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":5,"references":[3,4],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":6,"references":[3],"type":"index","data":{"on":3,"name":"idx_local_asset_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)","unique":false,"columns":[]}},{"id":7,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_owner_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)","unique":false,"columns":[]}},{"id":8,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum\nON remote_asset_entity (owner_id, checksum)\nWHERE (library_id IS NULL);\n","unique":true,"columns":[]}},{"id":9,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_library_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum\nON remote_asset_entity (owner_id, library_id, checksum)\nWHERE (library_id IS NOT NULL);\n","unique":true,"columns":[]}},{"id":10,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)","unique":false,"columns":[]}},{"id":11,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":12,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":13,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":14,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":15,"references":[1,14],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":16,"references":[14,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":18,"references":[1,17],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":19,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":20,"references":[1,19],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":21,"references":[13],"type":"index","data":{"on":13,"name":"idx_lat_lng","sql":"CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)","unique":false,"columns":[]}}]} \ No newline at end of file diff --git a/mobile/lib/infrastructure/entities/exif.entity.dart b/mobile/lib/infrastructure/entities/exif.entity.dart index 87c32461da..9c7f9e9975 100644 --- a/mobile/lib/infrastructure/entities/exif.entity.dart +++ b/mobile/lib/infrastructure/entities/exif.entity.dart @@ -95,7 +95,7 @@ class ExifInfo { ); } -@TableIndex(name: 'idx_lat_lng', columns: {#latitude, #longitude}) +@TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)') class RemoteExifEntity extends Table with DriftDefaultsMixin { const RemoteExifEntity(); diff --git a/mobile/lib/infrastructure/entities/exif.entity.drift.dart b/mobile/lib/infrastructure/entities/exif.entity.drift.dart index c31050c321..8695e2004b 100644 --- a/mobile/lib/infrastructure/entities/exif.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/exif.entity.drift.dart @@ -701,7 +701,7 @@ typedef $$RemoteExifEntityTableProcessedTableManager = >; i0.Index get idxLatLng => i0.Index( 'idx_lat_lng', - 'CREATE INDEX idx_lat_lng ON remote_exif_entity (latitude, longitude)', + 'CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)', ); class $RemoteExifEntityTable extends i2.RemoteExifEntity diff --git a/mobile/lib/infrastructure/entities/local_asset.entity.dart b/mobile/lib/infrastructure/entities/local_asset.entity.dart index e5519cfacf..3130e41dbb 100644 --- a/mobile/lib/infrastructure/entities/local_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/local_asset.entity.dart @@ -4,7 +4,7 @@ import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.d import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart'; import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; -@TableIndex(name: 'idx_local_asset_checksum', columns: {#checksum}) +@TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)') class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin { const LocalAssetEntity(); diff --git a/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart index 329401d5db..d0fe742463 100644 --- a/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart @@ -338,7 +338,7 @@ typedef $$LocalAssetEntityTableProcessedTableManager = >; i0.Index get idxLocalAssetChecksum => i0.Index( 'idx_local_asset_checksum', - 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + 'CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)', ); class $LocalAssetEntityTable extends i3.LocalAssetEntity diff --git a/mobile/lib/infrastructure/entities/remote_asset.entity.dart b/mobile/lib/infrastructure/entities/remote_asset.entity.dart index ecc0aa3d76..4426974413 100644 --- a/mobile/lib/infrastructure/entities/remote_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/remote_asset.entity.dart @@ -5,7 +5,9 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart'; import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; -@TableIndex(name: 'idx_remote_asset_owner_checksum', columns: {#ownerId, #checksum}) +@TableIndex.sql( + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', +) @TableIndex.sql(''' CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) @@ -16,7 +18,7 @@ CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE (library_id IS NOT NULL); ''') -@TableIndex(name: 'idx_remote_asset_checksum', columns: {#checksum}) +@TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)') class RemoteAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin { const RemoteAssetEntity(); diff --git a/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart index 9681d1e75d..eab7f95f64 100644 --- a/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart @@ -628,7 +628,7 @@ typedef $$RemoteAssetEntityTableProcessedTableManager = >; i0.Index get idxRemoteAssetOwnerChecksum => i0.Index( 'idx_remote_asset_owner_checksum', - 'CREATE INDEX idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', ); class $RemoteAssetEntityTable extends i3.RemoteAssetEntity @@ -1641,5 +1641,5 @@ i0.Index get uQRemoteAssetsOwnerLibraryChecksum => i0.Index( ); i0.Index get idxRemoteAssetChecksum => i0.Index( 'idx_remote_asset_checksum', - 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)', ); diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index 17cd590d79..c829b7c588 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -106,10 +106,13 @@ class Drift extends $Drift implements IDatabaseRepository { from5To6: (m, v6) async { // Drops the (checksum, ownerId) and adds it back as (ownerId, checksum) await customStatement('DROP INDEX IF EXISTS UQ_remote_asset_owner_checksum'); + await m.drop(v6.idxRemoteAssetOwnerChecksum); await m.create(v6.idxRemoteAssetOwnerChecksum); // Adds libraryId to remote_asset_entity await m.addColumn(v6.remoteAssetEntity, v6.remoteAssetEntity.libraryId); + await m.drop(v6.uQRemoteAssetsOwnerChecksum); await m.create(v6.uQRemoteAssetsOwnerChecksum); + await m.drop(v6.uQRemoteAssetsOwnerLibraryChecksum); await m.create(v6.uQRemoteAssetsOwnerLibraryChecksum); }, from6To7: (m, v7) async { diff --git a/mobile/lib/infrastructure/repositories/db.repository.steps.dart b/mobile/lib/infrastructure/repositories/db.repository.steps.dart index 9d6b02ab32..a5e8c010c2 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.steps.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.steps.dart @@ -2847,11 +2847,11 @@ final class Schema7 extends i0.VersionedSchema { ); final i1.Index idxLocalAssetChecksum = i1.Index( 'idx_local_asset_checksum', - 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + 'CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)', ); final i1.Index idxRemoteAssetOwnerChecksum = i1.Index( 'idx_remote_asset_owner_checksum', - 'CREATE INDEX idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', ); final i1.Index uQRemoteAssetsOwnerChecksum = i1.Index( 'UQ_remote_assets_owner_checksum', @@ -2863,7 +2863,7 @@ final class Schema7 extends i0.VersionedSchema { ); final i1.Index idxRemoteAssetChecksum = i1.Index( 'idx_remote_asset_checksum', - 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)', ); late final Shape4 userMetadataEntity = Shape4( source: i0.VersionedTable( @@ -3045,7 +3045,7 @@ final class Schema7 extends i0.VersionedSchema { ); final i1.Index idxLatLng = i1.Index( 'idx_lat_lng', - 'CREATE INDEX idx_lat_lng ON remote_exif_entity (latitude, longitude)', + 'CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)', ); } diff --git a/mobile/test/drift/main/generated/schema_v7.dart b/mobile/test/drift/main/generated/schema_v7.dart index c91e1ac53a..276a0cdc74 100644 --- a/mobile/test/drift/main/generated/schema_v7.dart +++ b/mobile/test/drift/main/generated/schema_v7.dart @@ -6383,11 +6383,11 @@ class DatabaseAtV7 extends GeneratedDatabase { LocalAlbumAssetEntity(this); late final Index idxLocalAssetChecksum = Index( 'idx_local_asset_checksum', - 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + 'CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)', ); late final Index idxRemoteAssetOwnerChecksum = Index( 'idx_remote_asset_owner_checksum', - 'CREATE INDEX idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', ); late final Index uQRemoteAssetsOwnerChecksum = Index( 'UQ_remote_assets_owner_checksum', @@ -6399,7 +6399,7 @@ class DatabaseAtV7 extends GeneratedDatabase { ); late final Index idxRemoteAssetChecksum = Index( 'idx_remote_asset_checksum', - 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)', ); late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); late final PartnerEntity partnerEntity = PartnerEntity(this); @@ -6415,7 +6415,7 @@ class DatabaseAtV7 extends GeneratedDatabase { late final AssetFaceEntity assetFaceEntity = AssetFaceEntity(this); late final Index idxLatLng = Index( 'idx_lat_lng', - 'CREATE INDEX idx_lat_lng ON remote_exif_entity (latitude, longitude)', + 'CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)', ); @override Iterable> get allTables => From f36efd128b63df4031d2161377a421eb3506f3fd Mon Sep 17 00:00:00 2001 From: Thomas <9749173+uhthomas@users.noreply.github.com> Date: Wed, 6 Aug 2025 22:57:51 +0100 Subject: [PATCH 200/748] fix(web): prevent thumbhashes from covering search bar (#20720) The thumbhash had a z-index setting which meant it would cover the search bar, and would always cause weird animations when scrolling up in search results. This is fixable by removing the z-index and moving it in front the other elements to get a naturally higher higher z-index preference. --- .../__test__/image-thumbnail.spec.ts | 22 ---------------- .../thumbnail/__test__/thumbnail.spec.ts | 11 ++++++++ .../assets/thumbnail/image-thumbnail.svelte | 26 ------------------- .../assets/thumbnail/thumbnail.svelte | 23 +++++++++------- 4 files changed, 25 insertions(+), 57 deletions(-) delete mode 100644 web/src/lib/components/assets/thumbnail/__test__/image-thumbnail.spec.ts diff --git a/web/src/lib/components/assets/thumbnail/__test__/image-thumbnail.spec.ts b/web/src/lib/components/assets/thumbnail/__test__/image-thumbnail.spec.ts deleted file mode 100644 index e14628a42f..0000000000 --- a/web/src/lib/components/assets/thumbnail/__test__/image-thumbnail.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import ImageThumbnail from '$lib/components/assets/thumbnail/image-thumbnail.svelte'; -import { render } from '@testing-library/svelte'; - -describe('ImageThumbnail component', () => { - beforeAll(() => { - Element.prototype.animate = vi.fn().mockImplementation(() => ({ - cancel: () => {}, - })); - }); - - it('shows thumbhash while image is loading', () => { - const sut = render(ImageThumbnail, { - url: 'http://localhost/img.png', - altText: 'test', - base64ThumbHash: '1QcSHQRnh493V4dIh4eXh1h4kJUI', - widthStyle: '250px', - }); - - const thumbhash = sut.getByTestId('thumbhash'); - expect(thumbhash).not.toBeFalsy(); - }); -}); diff --git a/web/src/lib/components/assets/thumbnail/__test__/thumbnail.spec.ts b/web/src/lib/components/assets/thumbnail/__test__/thumbnail.spec.ts index 21466780e8..f8e5fe0efa 100644 --- a/web/src/lib/components/assets/thumbnail/__test__/thumbnail.spec.ts +++ b/web/src/lib/components/assets/thumbnail/__test__/thumbnail.spec.ts @@ -45,4 +45,15 @@ describe('Thumbnail component', () => { const tabbables = getTabbable(container!); expect(tabbables.length).toBe(0); }); + + it('shows thumbhash while image is loading', () => { + const asset = assetFactory.build({ originalPath: 'image.jpg', originalMimeType: 'image/jpeg' }); + const sut = render(Thumbnail, { + asset, + selected: true, + }); + + const thumbhash = sut.getByTestId('thumbhash'); + expect(thumbhash).not.toBeFalsy(); + }); }); diff --git a/web/src/lib/components/assets/thumbnail/image-thumbnail.svelte b/web/src/lib/components/assets/thumbnail/image-thumbnail.svelte index cda474b1fb..41c52823b7 100644 --- a/web/src/lib/components/assets/thumbnail/image-thumbnail.svelte +++ b/web/src/lib/components/assets/thumbnail/image-thumbnail.svelte @@ -1,13 +1,10 @@ @@ -55,7 +52,7 @@ {/if} + import { resolve } from '$app/paths'; import BottomInfo from '$lib/components/shared-components/side-bar/bottom-info.svelte'; import RecentAlbums from '$lib/components/shared-components/side-bar/recent-albums.svelte'; import Sidebar from '$lib/components/sidebar/sidebar.svelte'; @@ -48,19 +49,19 @@ {#if $featureFlags.search} - + {/if} {#if $featureFlags.map} @@ -69,19 +70,19 @@ {#if $preferences.people.enabled && $preferences.people.sidebarWeb} {/if} {#if $preferences.sharedLinks.enabled && $preferences.sharedLinks.sidebarWeb} - + {/if} @@ -90,14 +91,14 @@ {#if $preferences.tags.enabled && $preferences.tags.sidebarWeb} - + {/if} {#if $preferences.folders.enabled && $preferences.folders.sidebarWeb} - + {/if} @@ -141,7 +142,7 @@ {#if $featureFlags.trash} From 01a9f735c8be380438833e6bdc6a8610ecbda6ab Mon Sep 17 00:00:00 2001 From: Arpit Singh <3810664+ufizo@users.noreply.github.com> Date: Thu, 7 Aug 2025 06:43:23 -0600 Subject: [PATCH 206/748] fix: avoid unnecessary writes to system metadata repository (#20538) Co-authored-by: Zack Pollard --- server/src/services/storage.service.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/server/src/services/storage.service.ts b/server/src/services/storage.service.ts index 5b08204bf9..b983c34f62 100644 --- a/server/src/services/storage.service.ts +++ b/server/src/services/storage.service.ts @@ -96,9 +96,10 @@ export class StorageService extends BaseService { await this.databaseRepository.withLock(DatabaseLock.MediaLocation, async () => { const current = StorageCore.getMediaLocation(); const samples = await this.assetRepository.getFileSamples(); + const savedValue = await this.systemMetadataRepository.get(SystemMetadataKey.MediaLocation); if (samples.length > 0) { const path = samples[0].path; - const savedValue = await this.systemMetadataRepository.get(SystemMetadataKey.MediaLocation); + let previous = savedValue?.location || ''; if (!previous && this.configRepository.getEnv().storage.mediaLocation) { @@ -125,7 +126,10 @@ export class StorageService extends BaseService { } } - await this.systemMetadataRepository.set(SystemMetadataKey.MediaLocation, { location: current }); + // Only set MediaLocation in systemMetadataRepository if needed + if (savedValue?.location !== current) { + await this.systemMetadataRepository.set(SystemMetadataKey.MediaLocation, { location: current }); + } }); } From df2525ee085d77c6e0104346211c9abd6df594ba Mon Sep 17 00:00:00 2001 From: Nicholas <30300649+NicholasFlamy@users.noreply.github.com> Date: Thu, 7 Aug 2025 09:02:13 -0400 Subject: [PATCH 207/748] feat(docs): add `make dev-docs` (#20572) --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 815d1a153b..f599dd9738 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,9 @@ dev-update: dev-scale: @trap 'make dev-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.dev.yml up --build -V --scale immich-server=3 --remove-orphans +dev-docs: + npm --prefix docs run start + .PHONY: e2e e2e: @trap 'make e2e-down' EXIT; COMPOSE_BAKE=true docker compose -f ./e2e/docker-compose.yml up --build -V --remove-orphans From 011a66731448ef67355743a878ae79cc0c786fd8 Mon Sep 17 00:00:00 2001 From: mkuehne707 <59650037+mkuehne707@users.noreply.github.com> Date: Thu, 7 Aug 2025 15:42:33 +0200 Subject: [PATCH 208/748] feat: batch change date and time relatively (#17717) Co-authored-by: marcel.kuehne <> Co-authored-by: Zack Pollard --- e2e/src/api/specs/asset.e2e-spec.ts | 24 ++++ i18n/en.json | 6 + .../lib/model/asset_bulk_update_dto.dart | 36 ++++- open-api/immich-openapi-specs.json | 6 + open-api/typescript-sdk/src/fetch-client.ts | 2 + server/src/dtos/asset.dto.ts | 13 +- server/src/queries/asset.repository.sql | 12 ++ server/src/repositories/asset.repository.ts | 15 ++ server/src/services/asset.service.spec.ts | 27 ++++ server/src/services/asset.service.ts | 52 +++++-- server/src/validation.spec.ts | 37 ++++- server/src/validation.ts | 23 +++ .../repositories/asset.repository.mock.ts | 1 + .../asset-viewer/detail-panel.svelte | 12 +- .../components/elements/duration-input.svelte | 52 +++++++ .../actions/change-date-action.svelte | 56 +++++++- .../shared-components/change-date.spec.ts | 115 ++++++++++++++- .../shared-components/change-date.svelte | 131 ++++++++++++++---- web/src/lib/utils/timeline-util.ts | 6 + 19 files changed, 574 insertions(+), 52 deletions(-) create mode 100644 web/src/lib/components/elements/duration-input.svelte diff --git a/e2e/src/api/specs/asset.e2e-spec.ts b/e2e/src/api/specs/asset.e2e-spec.ts index c1e9f9dfb8..5e9d90ddc6 100644 --- a/e2e/src/api/specs/asset.e2e-spec.ts +++ b/e2e/src/api/specs/asset.e2e-spec.ts @@ -854,6 +854,30 @@ describe('/asset', () => { }); }); + describe('PUT /assets', () => { + it('should update date time original relatively', async () => { + const { status, body } = await request(app) + .put(`/assets/`) + .set('Authorization', `Bearer ${user1.accessToken}`) + .send({ ids: [user1Assets[0].id], dateTimeRelative: -1441 }); + + expect(body).toEqual({}); + expect(status).toEqual(204); + + const result = await request(app) + .get(`/assets/${user1Assets[0].id}`) + .set('Authorization', `Bearer ${user1.accessToken}`) + .send(); + + expect(result.body).toMatchObject({ + id: user1Assets[0].id, + exifInfo: expect.objectContaining({ + dateTimeOriginal: '2023-11-19T01:10:00+00:00', + }), + }); + }); + }); + describe('POST /assets', () => { beforeAll(setupTests, 30_000); diff --git a/i18n/en.json b/i18n/en.json index d953f25c8a..4ddadf7348 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -749,6 +749,7 @@ "date_of_birth_saved": "Date of birth saved successfully", "date_range": "Date range", "day": "Day", + "days": "Days", "deduplicate_all": "Deduplicate All", "deduplication_criteria_1": "Image size in bytes", "deduplication_criteria_2": "Count of EXIF data", @@ -837,6 +838,8 @@ "edit_date": "Edit date", "edit_date_and_time": "Edit date and time", "edit_date_and_time_action_prompt": "{count} date and time edited", + "edit_date_and_time_by_offset": "Change date by offset", + "edit_date_and_time_by_offset_interval": "New date range: {from} - {to}", "edit_description": "Edit description", "edit_description_prompt": "Please select a new description:", "edit_exclusion_pattern": "Edit exclusion pattern", @@ -1107,6 +1110,7 @@ "home_page_upload_err_limit": "Can only upload a maximum of 30 assets at a time, skipping", "host": "Host", "hour": "Hour", + "hours": "Hours", "id": "ID", "idle": "Idle", "ignore_icloud_photos": "Ignore iCloud photos", @@ -1295,6 +1299,7 @@ "merged_people_count": "Merged {count, plural, one {# person} other {# people}}", "minimize": "Minimize", "minute": "Minute", + "minutes": "Minutes", "missing": "Missing", "model": "Model", "month": "Month", @@ -1368,6 +1373,7 @@ "oauth": "OAuth", "official_immich_resources": "Official Immich Resources", "offline": "Offline", + "offset": "Offset", "ok": "Ok", "oldest_first": "Oldest first", "on_this_device": "On this device", diff --git a/mobile/openapi/lib/model/asset_bulk_update_dto.dart b/mobile/openapi/lib/model/asset_bulk_update_dto.dart index 571badf029..d7e75ae365 100644 --- a/mobile/openapi/lib/model/asset_bulk_update_dto.dart +++ b/mobile/openapi/lib/model/asset_bulk_update_dto.dart @@ -14,6 +14,7 @@ class AssetBulkUpdateDto { /// Returns a new [AssetBulkUpdateDto] instance. AssetBulkUpdateDto({ this.dateTimeOriginal, + this.dateTimeRelative, this.description, this.duplicateId, this.ids = const [], @@ -21,6 +22,7 @@ class AssetBulkUpdateDto { this.latitude, this.longitude, this.rating, + this.timeZone, this.visibility, }); @@ -32,6 +34,14 @@ class AssetBulkUpdateDto { /// String? dateTimeOriginal; + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + num? dateTimeRelative; + /// /// Please note: This property should have been non-nullable! Since the specification file /// does not include a default value (using the "default:" property), however, the generated @@ -78,6 +88,14 @@ class AssetBulkUpdateDto { /// num? rating; + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + String? timeZone; + /// /// Please note: This property should have been non-nullable! Since the specification file /// does not include a default value (using the "default:" property), however, the generated @@ -89,6 +107,7 @@ class AssetBulkUpdateDto { @override bool operator ==(Object other) => identical(this, other) || other is AssetBulkUpdateDto && other.dateTimeOriginal == dateTimeOriginal && + other.dateTimeRelative == dateTimeRelative && other.description == description && other.duplicateId == duplicateId && _deepEquality.equals(other.ids, ids) && @@ -96,12 +115,14 @@ class AssetBulkUpdateDto { other.latitude == latitude && other.longitude == longitude && other.rating == rating && + other.timeZone == timeZone && other.visibility == visibility; @override int get hashCode => // ignore: unnecessary_parenthesis (dateTimeOriginal == null ? 0 : dateTimeOriginal!.hashCode) + + (dateTimeRelative == null ? 0 : dateTimeRelative!.hashCode) + (description == null ? 0 : description!.hashCode) + (duplicateId == null ? 0 : duplicateId!.hashCode) + (ids.hashCode) + @@ -109,10 +130,11 @@ class AssetBulkUpdateDto { (latitude == null ? 0 : latitude!.hashCode) + (longitude == null ? 0 : longitude!.hashCode) + (rating == null ? 0 : rating!.hashCode) + + (timeZone == null ? 0 : timeZone!.hashCode) + (visibility == null ? 0 : visibility!.hashCode); @override - String toString() => 'AssetBulkUpdateDto[dateTimeOriginal=$dateTimeOriginal, description=$description, duplicateId=$duplicateId, ids=$ids, isFavorite=$isFavorite, latitude=$latitude, longitude=$longitude, rating=$rating, visibility=$visibility]'; + String toString() => 'AssetBulkUpdateDto[dateTimeOriginal=$dateTimeOriginal, dateTimeRelative=$dateTimeRelative, description=$description, duplicateId=$duplicateId, ids=$ids, isFavorite=$isFavorite, latitude=$latitude, longitude=$longitude, rating=$rating, timeZone=$timeZone, visibility=$visibility]'; Map toJson() { final json = {}; @@ -121,6 +143,11 @@ class AssetBulkUpdateDto { } else { // json[r'dateTimeOriginal'] = null; } + if (this.dateTimeRelative != null) { + json[r'dateTimeRelative'] = this.dateTimeRelative; + } else { + // json[r'dateTimeRelative'] = null; + } if (this.description != null) { json[r'description'] = this.description; } else { @@ -152,6 +179,11 @@ class AssetBulkUpdateDto { } else { // json[r'rating'] = null; } + if (this.timeZone != null) { + json[r'timeZone'] = this.timeZone; + } else { + // json[r'timeZone'] = null; + } if (this.visibility != null) { json[r'visibility'] = this.visibility; } else { @@ -170,6 +202,7 @@ class AssetBulkUpdateDto { return AssetBulkUpdateDto( dateTimeOriginal: mapValueOfType(json, r'dateTimeOriginal'), + dateTimeRelative: num.parse('${json[r'dateTimeRelative']}'), description: mapValueOfType(json, r'description'), duplicateId: mapValueOfType(json, r'duplicateId'), ids: json[r'ids'] is Iterable @@ -179,6 +212,7 @@ class AssetBulkUpdateDto { latitude: num.parse('${json[r'latitude']}'), longitude: num.parse('${json[r'longitude']}'), rating: num.parse('${json[r'rating']}'), + timeZone: mapValueOfType(json, r'timeZone'), visibility: AssetVisibility.fromJson(json[r'visibility']), ); } diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 74a1407096..e8b5df9dc1 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -9979,6 +9979,9 @@ "dateTimeOriginal": { "type": "string" }, + "dateTimeRelative": { + "type": "number" + }, "description": { "type": "string" }, @@ -10007,6 +10010,9 @@ "minimum": -1, "type": "number" }, + "timeZone": { + "type": "string" + }, "visibility": { "allOf": [ { diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index a1a04ea566..5011e065eb 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -456,6 +456,7 @@ export type AssetMediaResponseDto = { }; export type AssetBulkUpdateDto = { dateTimeOriginal?: string; + dateTimeRelative?: number; description?: string; duplicateId?: string | null; ids: string[]; @@ -463,6 +464,7 @@ export type AssetBulkUpdateDto = { latitude?: number; longitude?: number; rating?: number; + timeZone?: string; visibility?: AssetVisibility; }; export type AssetBulkUploadCheckItem = { diff --git a/server/src/dtos/asset.dto.ts b/server/src/dtos/asset.dto.ts index 5728d21646..31e5679e76 100644 --- a/server/src/dtos/asset.dto.ts +++ b/server/src/dtos/asset.dto.ts @@ -8,6 +8,7 @@ import { IsNotEmpty, IsPositive, IsString, + IsTimeZone, Max, Min, ValidateIf, @@ -15,7 +16,7 @@ import { import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; import { AssetType, AssetVisibility } from 'src/enum'; import { AssetStats } from 'src/repositories/asset.repository'; -import { Optional, ValidateBoolean, ValidateEnum, ValidateUUID } from 'src/validation'; +import { IsNotSiblingOf, Optional, ValidateBoolean, ValidateEnum, ValidateUUID } from 'src/validation'; export class DeviceIdDto { @IsNotEmpty() @@ -65,6 +66,16 @@ export class AssetBulkUpdateDto extends UpdateAssetBase { @Optional() duplicateId?: string | null; + + @IsNotSiblingOf(['dateTimeOriginal']) + @Optional() + @IsInt() + dateTimeRelative?: number; + + @IsNotSiblingOf(['dateTimeOriginal']) + @IsTimeZone() + @Optional() + timeZone?: string; } export class UpdateAssetDto extends UpdateAssetBase { diff --git a/server/src/queries/asset.repository.sql b/server/src/queries/asset.repository.sql index f7a4d1402d..712fb08a50 100644 --- a/server/src/queries/asset.repository.sql +++ b/server/src/queries/asset.repository.sql @@ -7,6 +7,18 @@ set where "assetId" in ($2) +-- AssetRepository.updateDateTimeOriginal +update "asset_exif" +set + "dateTimeOriginal" = "dateTimeOriginal" + $1::interval, + "timeZone" = $2 +where + "assetId" in ($3) +returning + "assetId", + "dateTimeOriginal", + "timeZone" + -- AssetRepository.getByDayOfYear with "res" as ( diff --git a/server/src/repositories/asset.repository.ts b/server/src/repositories/asset.repository.ts index 8aa25c4a6a..61ccbf6541 100644 --- a/server/src/repositories/asset.repository.ts +++ b/server/src/repositories/asset.repository.ts @@ -169,6 +169,21 @@ export class AssetRepository { await this.db.updateTable('asset_exif').set(options).where('assetId', 'in', ids).execute(); } + @GenerateSql({ params: [[DummyValue.UUID], DummyValue.NUMBER, DummyValue.STRING] }) + @Chunked() + async updateDateTimeOriginal( + ids: string[], + delta?: number, + timeZone?: string, + ): Promise<{ assetId: string; dateTimeOriginal: Date | null; timeZone: string | null }[]> { + return await this.db + .updateTable('asset_exif') + .set({ dateTimeOriginal: sql`"dateTimeOriginal" + ${(delta ?? 0) + ' minute'}::interval`, timeZone }) + .where('assetId', 'in', ids) + .returning(['assetId', 'dateTimeOriginal', 'timeZone']) + .execute(); + } + async upsertJobStatus(...jobStatus: Insertable[]): Promise { if (jobStatus.length === 0) { return; diff --git a/server/src/services/asset.service.spec.ts b/server/src/services/asset.service.spec.ts index 6461735976..7b29b8ab96 100755 --- a/server/src/services/asset.service.spec.ts +++ b/server/src/services/asset.service.spec.ts @@ -468,6 +468,33 @@ describe(AssetService.name, () => { }); expect(mocks.asset.updateAll).toHaveBeenCalled(); }); + + it('should update exif table if dateTimeRelative and timeZone field is provided', async () => { + mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1'])); + const dateTimeRelative = 35; + const timeZone = 'UTC+2'; + mocks.asset.updateDateTimeOriginal.mockResolvedValue([ + { assetId: 'asset-1', dateTimeOriginal: new Date('2020-02-25T04:41:00'), timeZone }, + ]); + await sut.updateAll(authStub.admin, { + ids: ['asset-1'], + dateTimeRelative, + timeZone, + }); + expect(mocks.asset.updateDateTimeOriginal).toHaveBeenCalledWith(['asset-1'], dateTimeRelative, timeZone); + expect(mocks.job.queueAll).toHaveBeenCalledWith([ + { + name: JobName.SidecarWrite, + data: { + id: 'asset-1', + dateTimeOriginal: '2020-02-25T06:41:00.000+02:00', + description: undefined, + latitude: undefined, + longitude: undefined, + }, + }, + ]); + }); }); describe('deleteAll', () => { diff --git a/server/src/services/asset.service.ts b/server/src/services/asset.service.ts index 864a9cc512..9a2c580707 100644 --- a/server/src/services/asset.service.ts +++ b/server/src/services/asset.service.ts @@ -113,22 +113,48 @@ export class AssetService extends BaseService { } async updateAll(auth: AuthDto, dto: AssetBulkUpdateDto): Promise { - const { ids, description, dateTimeOriginal, latitude, longitude, ...options } = dto; + const { ids, description, dateTimeOriginal, dateTimeRelative, timeZone, latitude, longitude, ...options } = dto; await this.requireAccess({ auth, permission: Permission.AssetUpdate, ids }); - if ( - description !== undefined || - dateTimeOriginal !== undefined || - latitude !== undefined || - longitude !== undefined - ) { + const staticValuesChanged = + description !== undefined || dateTimeOriginal !== undefined || latitude !== undefined || longitude !== undefined; + + if (staticValuesChanged) { await this.assetRepository.updateAllExif(ids, { description, dateTimeOriginal, latitude, longitude }); - await this.jobRepository.queueAll( - ids.map((id) => ({ - name: JobName.SidecarWrite, - data: { id, description, dateTimeOriginal, latitude, longitude }, - })), - ); + } + + const assets = + (dateTimeRelative !== undefined && dateTimeRelative !== 0) || timeZone !== undefined + ? await this.assetRepository.updateDateTimeOriginal(ids, dateTimeRelative, timeZone) + : null; + + const dateTimesWithTimezone = + assets?.map((asset) => { + const isoString = asset.dateTimeOriginal?.toISOString(); + let dateTime = isoString ? DateTime.fromISO(isoString) : null; + + if (dateTime && asset.timeZone) { + dateTime = dateTime.setZone(asset.timeZone); + } + + return { + assetId: asset.assetId, + dateTimeOriginal: dateTime?.toISO() ?? null, + }; + }) ?? null; + + if (staticValuesChanged || dateTimesWithTimezone) { + const entries: JobItem[] = (dateTimesWithTimezone ?? ids).map((entry: any) => ({ + name: JobName.SidecarWrite, + data: { + id: entry.assetId ?? entry, + description, + dateTimeOriginal: entry.dateTimeOriginal ?? dateTimeOriginal, + latitude, + longitude, + }, + })); + await this.jobRepository.queueAll(entries); } if ( diff --git a/server/src/validation.spec.ts b/server/src/validation.spec.ts index 7cd7826223..631ba60a60 100644 --- a/server/src/validation.spec.ts +++ b/server/src/validation.spec.ts @@ -1,7 +1,8 @@ import { plainToInstance } from 'class-transformer'; import { validate } from 'class-validator'; import { DateTime } from 'luxon'; -import { IsDateStringFormat, MaxDateString } from 'src/validation'; +import { IsDateStringFormat, IsNotSiblingOf, MaxDateString, Optional } from 'src/validation'; +import { describe } from 'vitest'; describe('Validation', () => { describe('MaxDateString', () => { @@ -54,4 +55,38 @@ describe('Validation', () => { await expect(validate(dto)).resolves.toHaveLength(1); }); }); + + describe('IsNotSiblingOf', () => { + class MyDto { + @IsNotSiblingOf(['attribute2']) + @Optional() + attribute1?: string; + + @IsNotSiblingOf(['attribute1', 'attribute3']) + @Optional() + attribute2?: string; + + @IsNotSiblingOf(['attribute2']) + @Optional() + attribute3?: string; + + @Optional() + unrelatedAttribute?: string; + } + + it('passes when only one attribute is present', async () => { + const dto = plainToInstance(MyDto, { attribute1: 'value1', unrelatedAttribute: 'value2' }); + await expect(validate(dto)).resolves.toHaveLength(0); + }); + + it('fails when colliding attributes are present', async () => { + const dto = plainToInstance(MyDto, { attribute1: 'value1', attribute2: 'value2' }); + await expect(validate(dto)).resolves.toHaveLength(2); + }); + + it('passes when no colliding attributes are present', async () => { + const dto = plainToInstance(MyDto, { attribute1: 'value1', attribute3: 'value2' }); + await expect(validate(dto)).resolves.toHaveLength(0); + }); + }); }); diff --git a/server/src/validation.ts b/server/src/validation.ts index 3f7e1c6f3b..e583f6a44e 100644 --- a/server/src/validation.ts +++ b/server/src/validation.ts @@ -22,11 +22,13 @@ import { Validate, ValidateBy, ValidateIf, + ValidationArguments, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface, buildMessage, isDateString, + isDefined, } from 'class-validator'; import { CronJob } from 'cron'; import { DateTime } from 'luxon'; @@ -146,6 +148,27 @@ export function Optional({ nullable, emptyToNull, ...validationOptions }: Option return applyDecorators(...decorators); } +export function IsNotSiblingOf(siblings: string[], validationOptions?: ValidationOptions) { + return ValidateBy( + { + name: 'isNotSiblingOf', + constraints: siblings, + validator: { + validate(value: any, args: ValidationArguments) { + if (!isDefined(value)) { + return true; + } + return args.constraints.filter((prop) => isDefined((args.object as any)[prop])).length === 0; + }, + defaultMessage: (args: ValidationArguments) => { + return `${args.property} cannot exist alongside any of the following properties: ${args.constraints.join(', ')}`; + }, + }, + }, + validationOptions, + ); +} + export const ValidateHexColor = () => { const decorators = [ IsHexColor(), diff --git a/server/test/repositories/asset.repository.mock.ts b/server/test/repositories/asset.repository.mock.ts index 6fca29d98e..79e3d506f3 100644 --- a/server/test/repositories/asset.repository.mock.ts +++ b/server/test/repositories/asset.repository.mock.ts @@ -8,6 +8,7 @@ export const newAssetRepositoryMock = (): Mocked (isShowChangeDate = false)} /> diff --git a/web/src/lib/components/elements/duration-input.svelte b/web/src/lib/components/elements/duration-input.svelte new file mode 100644 index 0000000000..1aebe17640 --- /dev/null +++ b/web/src/lib/components/elements/duration-input.svelte @@ -0,0 +1,52 @@ + + +
    + + + + +
    diff --git a/web/src/lib/components/photos-page/actions/change-date-action.svelte b/web/src/lib/components/photos-page/actions/change-date-action.svelte index 5f65fdd744..3007798719 100644 --- a/web/src/lib/components/photos-page/actions/change-date-action.svelte +++ b/web/src/lib/components/photos-page/actions/change-date-action.svelte @@ -1,14 +1,18 @@ (confirmed ? handleConfirm() : onCancel())} > {#snippet promptSnippet()} -
    -
    - - + {#if withDuration} +
    + + +
    - {#if timezoneInput} -
    - handleOnSelect(option)} - /> + {/if} +
    +
    +
    + +
    - {/if} +
    +
    + + +
    +
    + {#if timezoneInput} +
    + handleOnSelect(option)} + /> +
    + {/if} +
    + {$t('edit_date_and_time_by_offset_interval', { values: { from: intervalFrom, to: intervalTo } })} +
    +
    {/snippet} diff --git a/web/src/lib/utils/timeline-util.ts b/web/src/lib/utils/timeline-util.ts index c160c65922..a1147b708f 100644 --- a/web/src/lib/utils/timeline-util.ts +++ b/web/src/lib/utils/timeline-util.ts @@ -151,6 +151,12 @@ export function formatGroupTitle(_date: DateTime): string { export const getDateLocaleString = (date: DateTime, opts?: LocaleOptions): string => date.toLocaleString(DateTime.DATE_MED_WITH_WEEKDAY, opts); +export const getDateTimeOffsetLocaleString = (date: DateTime, opts?: LocaleOptions): string => + date.toLocaleString( + { year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit', timeZoneName: 'longOffset' }, + opts, + ); + export const toTimelineAsset = (unknownAsset: AssetResponseDto | TimelineAsset): TimelineAsset => { if (isTimelineAsset(unknownAsset)) { return unknownAsset; From 89522daaac628e0afb1ca289cd31f04320ae9953 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 7 Aug 2025 09:19:44 -0500 Subject: [PATCH 209/748] fix: invalidate album api on log out (#20756) --- mobile/lib/utils/provider_utils.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mobile/lib/utils/provider_utils.dart b/mobile/lib/utils/provider_utils.dart index 7af0e61d3c..6c2d6e0f11 100644 --- a/mobile/lib/utils/provider_utils.dart +++ b/mobile/lib/utils/provider_utils.dart @@ -4,6 +4,7 @@ import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; import 'package:immich_mobile/repositories/activity_api.repository.dart'; import 'package:immich_mobile/repositories/album_api.repository.dart'; import 'package:immich_mobile/repositories/asset_api.repository.dart'; +import 'package:immich_mobile/repositories/drift_album_api_repository.dart'; import 'package:immich_mobile/repositories/partner_api.repository.dart'; import 'package:immich_mobile/repositories/person_api.repository.dart'; import 'package:immich_mobile/repositories/timeline.repository.dart'; @@ -17,4 +18,7 @@ void invalidateAllApiRepositoryProviders(WidgetRef ref) { ref.invalidate(assetApiRepositoryProvider); ref.invalidate(timelineRepositoryProvider); ref.invalidate(searchApiRepositoryProvider); + + // Drift + ref.invalidate(driftAlbumApiRepositoryProvider); } From 1283491cc2899755d54596f4d108c4877dff8683 Mon Sep 17 00:00:00 2001 From: bo0tzz Date: Thu, 7 Aug 2025 18:14:33 +0200 Subject: [PATCH 210/748] chore: fork PRs can't have previews (#20464) * chore: fork PRs can't have previews * chore: fix formatting * chore: different close message for fork PRs --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- .github/workflows/preview-label.yaml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/preview-label.yaml b/.github/workflows/preview-label.yaml index edd9dfdae9..3ab9fd267f 100644 --- a/.github/workflows/preview-label.yaml +++ b/.github/workflows/preview-label.yaml @@ -20,7 +20,7 @@ jobs: remove-label: runs-on: ubuntu-latest - if: ${{ github.event.action == 'closed' && contains(github.event.pull_request.labels.*.name, 'preview') }} + if: ${{ (github.event.action == 'closed' || github.event.pull_request.head.repo.fork) && contains(github.event.pull_request.labels.*.name, 'preview') }} permissions: pull-requests: write steps: @@ -33,3 +33,15 @@ jobs: repo: context.repo.repo, name: 'preview' }) + + - uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2.8.2 + if: ${{ github.event.pull_request.head.repo.fork }} + with: + message-id: 'preview-status' + message: 'PRs from forks cannot have preview environments.' + + - uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2.8.2 + if: ${{ !github.event.pull_request.head.repo.fork }} + with: + message-id: 'preview-status' + message: 'Preview environment has been removed.' From c74989d3045eb7396c64189d7e8c822f0f991d8e Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Thu, 7 Aug 2025 13:00:50 -0400 Subject: [PATCH 211/748] docs: include openapi.json (#20760) --- docs/.gitignore | 4 +++- docs/package.json | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/.gitignore b/docs/.gitignore index 502ac97045..fbb000246e 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -18,4 +18,6 @@ npm-debug.log* yarn-debug.log* yarn-error.log* -yarn.lock \ No newline at end of file +yarn.lock + +/static/openapi.json diff --git a/docs/package.json b/docs/package.json index 9e4ab5504f..7fbcbada5a 100644 --- a/docs/package.json +++ b/docs/package.json @@ -7,7 +7,8 @@ "format": "prettier --check .", "format:fix": "prettier --write .", "start": "docusaurus start --port 3005", - "build": "docusaurus build", + "copy:openapi": "jq -c < ../open-api/immich-openapi-specs.json > ./static/openapi.json || exit 0", + "build": "npm run copy:openapi && docusaurus build", "swizzle": "docusaurus swizzle", "deploy": "docusaurus deploy", "clear": "docusaurus clear", From a896c5a4ddbbab880584522bfdfd972d80bebca0 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Thu, 7 Aug 2025 13:01:05 -0400 Subject: [PATCH 212/748] fix(web): shared-link autocomplete (#20761) --- web/src/lib/modals/SharedLinkCreateModal.svelte | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/src/lib/modals/SharedLinkCreateModal.svelte b/web/src/lib/modals/SharedLinkCreateModal.svelte index 6d46405ad7..8f77ff6415 100644 --- a/web/src/lib/modals/SharedLinkCreateModal.svelte +++ b/web/src/lib/modals/SharedLinkCreateModal.svelte @@ -165,7 +165,7 @@
    - + {#if slug} /s/{encodeURIComponent(slug)} @@ -173,11 +173,11 @@
    - + - +
    From 7b83b7b2d5783623663a5602397d022eaabe1802 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 7 Aug 2025 13:06:16 -0500 Subject: [PATCH 213/748] fix: don't show remove from album action from the main timeline (#20757) * fix: don't show remove from album action from the main timeline * pr feedback --- .../pages/drift_remote_album.page.dart | 43 ++++-- .../current_album.provider.dart | 1 + .../common/remote_album_sliver_app_bar.dart | 132 +++++++++--------- 3 files changed, 94 insertions(+), 82 deletions(-) diff --git a/mobile/lib/presentation/pages/drift_remote_album.page.dart b/mobile/lib/presentation/pages/drift_remote_album.page.dart index 9d060e51d0..eecc4b1e1e 100644 --- a/mobile/lib/presentation/pages/drift_remote_album.page.dart +++ b/mobile/lib/presentation/pages/drift_remote_album.page.dart @@ -10,6 +10,7 @@ import 'package:immich_mobile/presentation/widgets/bottom_sheet/remote_album_bot import 'package:immich_mobile/presentation/widgets/remote_album/drift_album_option.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/remote_album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; @@ -215,22 +216,34 @@ class _RemoteAlbumPageState extends ConsumerState { @override Widget build(BuildContext context) { - return ProviderScope( - overrides: [ - timelineServiceProvider.overrideWith((ref) { - final timelineService = ref.watch(timelineFactoryProvider).remoteAlbum(albumId: _album.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }), - ], - child: Timeline( - appBar: RemoteAlbumSliverAppBar( - icon: Icons.photo_album_outlined, - onShowOptions: () => showOptionSheet(context), - onToggleAlbumOrder: () => toggleAlbumOrder(), - onEditTitle: () => showEditTitleAndDescription(context), + return PopScope( + onPopInvokedWithResult: (didPop, _) { + if (didPop) { + Future.microtask(() { + if (mounted) { + ref.read(currentRemoteAlbumProvider.notifier).dispose(); + ref.read(remoteAlbumProvider.notifier).refresh(); + } + }); + } + }, + child: ProviderScope( + overrides: [ + timelineServiceProvider.overrideWith((ref) { + final timelineService = ref.watch(timelineFactoryProvider).remoteAlbum(albumId: _album.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), + ], + child: Timeline( + appBar: RemoteAlbumSliverAppBar( + icon: Icons.photo_album_outlined, + onShowOptions: () => showOptionSheet(context), + onToggleAlbumOrder: () => toggleAlbumOrder(), + onEditTitle: () => showEditTitleAndDescription(context), + ), + bottomSheet: RemoteAlbumBottomSheet(album: _album), ), - bottomSheet: RemoteAlbumBottomSheet(album: _album), ), ); } diff --git a/mobile/lib/providers/infrastructure/current_album.provider.dart b/mobile/lib/providers/infrastructure/current_album.provider.dart index 5d6e0414b0..0d95674ec7 100644 --- a/mobile/lib/providers/infrastructure/current_album.provider.dart +++ b/mobile/lib/providers/infrastructure/current_album.provider.dart @@ -31,5 +31,6 @@ class CurrentAlbumNotifier extends AutoDisposeNotifier { void dispose() { _keepAliveLink?.close(); _assetSubscription?.cancel(); + state = null; } } diff --git a/mobile/lib/widgets/common/remote_album_sliver_app_bar.dart b/mobile/lib/widgets/common/remote_album_sliver_app_bar.dart index e1332165bd..fb7acc8d0f 100644 --- a/mobile/lib/widgets/common/remote_album_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/remote_album_sliver_app_bar.dart @@ -14,7 +14,6 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/datetime_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/images/image_provider.dart'; -import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/remote_album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; @@ -74,76 +73,75 @@ class _MesmerizingSliverAppBarState extends ConsumerState const SizedBox(height: 120), - _ => const SizedBox(height: 452), - }, - ) - : SliverAppBar( - expandedHeight: 400.0, - floating: false, - pinned: true, - snap: false, - elevation: 0, - leading: IconButton( - icon: Icon( - Platform.isIOS ? Icons.arrow_back_ios_new_rounded : Icons.arrow_back, - color: actionIconColor, - shadows: actionIconShadows, - ), - onPressed: () { - ref.read(remoteAlbumProvider.notifier).refresh(); - context.navigateTo(const TabShellRoute(children: [DriftAlbumsRoute()])); - }, + if (isMultiSelectEnabled) { + return SliverToBoxAdapter( + child: switch (_scrollProgress) { + < 0.8 => const SizedBox(height: 120), + _ => const SizedBox(height: 452), + }, + ); + } else { + return SliverAppBar( + expandedHeight: 400.0, + floating: false, + pinned: true, + snap: false, + elevation: 0, + leading: IconButton( + icon: Icon( + Platform.isIOS ? Icons.arrow_back_ios_new_rounded : Icons.arrow_back, + color: actionIconColor, + shadows: actionIconShadows, + ), + onPressed: () => context.navigateTo(const TabShellRoute(children: [DriftAlbumsRoute()])), + ), + actions: [ + if (widget.onToggleAlbumOrder != null) + IconButton( + icon: Icon(Icons.swap_vert_rounded, color: actionIconColor, shadows: actionIconShadows), + onPressed: widget.onToggleAlbumOrder, ), - actions: [ - if (widget.onToggleAlbumOrder != null) - IconButton( - icon: Icon(Icons.swap_vert_rounded, color: actionIconColor, shadows: actionIconShadows), - onPressed: widget.onToggleAlbumOrder, - ), - if (widget.onShowOptions != null) - IconButton( - icon: Icon(Icons.more_vert, color: actionIconColor, shadows: actionIconShadows), - onPressed: widget.onShowOptions, - ), - ], - flexibleSpace: Builder( - builder: (context) { - final settings = context.dependOnInheritedWidgetOfExactType(); - final scrollProgress = _calculateScrollProgress(settings); + if (widget.onShowOptions != null) + IconButton( + icon: Icon(Icons.more_vert, color: actionIconColor, shadows: actionIconShadows), + onPressed: widget.onShowOptions, + ), + ], + flexibleSpace: Builder( + builder: (context) { + final settings = context.dependOnInheritedWidgetOfExactType(); + final scrollProgress = _calculateScrollProgress(settings); - // Update scroll progress for the leading button - WidgetsBinding.instance.addPostFrameCallback((_) { - if (mounted && _scrollProgress != scrollProgress) { - setState(() { - _scrollProgress = scrollProgress; - }); - } + // Update scroll progress for the leading button + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted && _scrollProgress != scrollProgress) { + setState(() { + _scrollProgress = scrollProgress; }); + } + }); - return FlexibleSpaceBar( - centerTitle: true, - title: AnimatedSwitcher( - duration: const Duration(milliseconds: 200), - child: scrollProgress > 0.95 - ? Text( - currentAlbum.name, - style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.w600, fontSize: 18), - ) - : null, - ), - background: _ExpandedBackground( - scrollProgress: scrollProgress, - icon: widget.icon, - onEditTitle: widget.onEditTitle, - ), - ); - }, - ), - ); + return FlexibleSpaceBar( + centerTitle: true, + title: AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + child: scrollProgress > 0.95 + ? Text( + currentAlbum.name, + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.w600, fontSize: 18), + ) + : null, + ), + background: _ExpandedBackground( + scrollProgress: scrollProgress, + icon: widget.icon, + onEditTitle: widget.onEditTitle, + ), + ); + }, + ), + ); + } } } From 1d4d8e7a9ad6750e32b664bf94fbe3c3b1938258 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 7 Aug 2025 13:43:56 -0500 Subject: [PATCH 214/748] chore: bump @immich/ui to 24 (#20767) chore: bump @ui 24 --- web/package-lock.json | 35 ++++++++++++++--------------------- web/package.json | 2 +- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index 516babb3a9..da4b01e440 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", "@immich/sdk": "file:../open-api/typescript-sdk", - "@immich/ui": "^0.23.6", + "@immich/ui": "^0.24.0", "@mapbox/mapbox-gl-rtl-text": "0.2.3", "@mdi/js": "^7.4.47", "@photo-sphere-viewer/core": "^5.11.5", @@ -1370,15 +1370,15 @@ "link": true }, "node_modules/@immich/ui": { - "version": "0.23.6", - "resolved": "https://registry.npmjs.org/@immich/ui/-/ui-0.23.6.tgz", - "integrity": "sha512-HYIguDx/nCXcvqLKhY1R/+Aks6mn8B9jIiNVQH6WODDPbvGFrvQT5uINhXHrjsdyuzKBVS6dps+lx9+9Z6z4rA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@immich/ui/-/ui-0.24.0.tgz", + "integrity": "sha512-qqgKYz6g5HPJFtKO12CPzFA8E+dJXZZFoM8WLUUsGjILXHeIX6DeywjZuRC2Ojd3mo9I5KlEDUvHPfv9eh+NXw==", "license": "GNU Affero General Public License version 3", "dependencies": { "@mdi/js": "^7.4.47", "bits-ui": "^2.0.0", "tailwind-merge": "^3.0.0", - "tailwind-variants": "^1.0.0" + "tailwind-variants": "^2.0.0" }, "peerDependencies": { "svelte": "^5.0.0" @@ -9085,29 +9085,22 @@ } }, "node_modules/tailwind-variants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-1.0.0.tgz", - "integrity": "sha512-2WSbv4ulEEyuBKomOunut65D8UZwxrHoRfYnxGcQNnHqlSCp2+B7Yz2W+yrNDrxRodOXtGD/1oCcKGNBnUqMqA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-2.1.0.tgz", + "integrity": "sha512-82m0eRex0z6A3GpvfoTCpHr+wWJmbecfVZfP3mqLoDxeya5tN4mYJQZwa5Aw1hRZTedwpu1D2JizYenoEdyD8w==", "license": "MIT", - "dependencies": { - "tailwind-merge": "3.0.2" - }, "engines": { "node": ">=16.x", "pnpm": ">=7.x" }, "peerDependencies": { + "tailwind-merge": ">=3.0.0", "tailwindcss": "*" - } - }, - "node_modules/tailwind-variants/node_modules/tailwind-merge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.0.2.tgz", - "integrity": "sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/dcastil" + }, + "peerDependenciesMeta": { + "tailwind-merge": { + "optional": true + } } }, "node_modules/tailwindcss": { diff --git a/web/package.json b/web/package.json index c14ff6f04c..d3ef82a50d 100644 --- a/web/package.json +++ b/web/package.json @@ -28,7 +28,7 @@ "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", "@immich/sdk": "file:../open-api/typescript-sdk", - "@immich/ui": "^0.23.6", + "@immich/ui": "^0.24.0", "@mapbox/mapbox-gl-rtl-text": "0.2.3", "@mdi/js": "^7.4.47", "@photo-sphere-viewer/core": "^5.11.5", From cfbc24579def79f030c4991322ef92ebfd297298 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Thu, 7 Aug 2025 15:07:31 -0400 Subject: [PATCH 215/748] feat(web): reset pin code (#20766) --- i18n/en.json | 5 ++ .../PinCodeChangeForm.svelte | 19 +++-- .../PinCodeCreateForm.svelte | 5 +- .../user-settings-page/PinCodeInput.svelte | 3 +- .../user-settings-page/PinCodeSettings.svelte | 17 +++- web/src/lib/modals/PinCodeResetModal.svelte | 81 +++++++++++++++++++ 6 files changed, 112 insertions(+), 18 deletions(-) create mode 100644 web/src/lib/modals/PinCodeResetModal.svelte diff --git a/i18n/en.json b/i18n/en.json index 4ddadf7348..bc52dbd85c 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -912,6 +912,7 @@ "failed_to_load_notifications": "Failed to load notifications", "failed_to_load_people": "Failed to load people", "failed_to_remove_product_key": "Failed to remove product key", + "failed_to_reset_pin_code": "Failed to reset PIN code", "failed_to_stack_assets": "Failed to stack assets", "failed_to_unstack_assets": "Failed to un-stack assets", "failed_to_update_notification_status": "Failed to update notification status", @@ -1056,6 +1057,7 @@ "folder_not_found": "Folder not found", "folders": "Folders", "folders_feature_description": "Browsing the folder view for the photos and videos on the file system", + "forgot_pin_code_question": "Forgot your PIN?", "forward": "Forward", "gcast_enabled": "Google Cast", "gcast_enabled_description": "This feature loads external resources from Google in order to work.", @@ -1599,6 +1601,9 @@ "reset_password": "Reset password", "reset_people_visibility": "Reset people visibility", "reset_pin_code": "Reset PIN code", + "reset_pin_code_description": "If you forgot your PIN code, you can contact the server administrator to reset it", + "reset_pin_code_success": "Successfully reset PIN code", + "reset_pin_code_with_password": "You can always reset your PIN code with your password", "reset_sqlite": "Reset SQLite Database", "reset_sqlite_confirmation": "Are you sure you want to reset the SQLite database? You will need to log out and log in again to resync the data", "reset_sqlite_success": "Successfully reset the SQLite database", diff --git a/web/src/lib/components/user-settings-page/PinCodeChangeForm.svelte b/web/src/lib/components/user-settings-page/PinCodeChangeForm.svelte index 54bcaca38f..8e79a41353 100644 --- a/web/src/lib/components/user-settings-page/PinCodeChangeForm.svelte +++ b/web/src/lib/components/user-settings-page/PinCodeChangeForm.svelte @@ -6,7 +6,7 @@ import PinCodeInput from '$lib/components/user-settings-page/PinCodeInput.svelte'; import { handleError } from '$lib/utils/handle-error'; import { changePinCode } from '@immich/sdk'; - import { Button } from '@immich/ui'; + import { Button, Heading, Text } from '@immich/ui'; import { t } from 'svelte-i18n'; import { fade } from 'svelte/transition'; @@ -16,11 +16,11 @@ let isLoading = $state(false); let canSubmit = $derived(currentPinCode.length === 6 && confirmPinCode.length === 6 && newPinCode === confirmPinCode); - interface Props { - onChanged?: () => void; - } + type Props = { + onForgot: () => void; + }; - let { onChanged }: Props = $props(); + let { onForgot }: Props = $props(); const handleSubmit = async (event: Event) => { event.preventDefault(); @@ -38,8 +38,6 @@ message: $t('pin_code_changed_successfully'), type: NotificationType.Info, }); - - onChanged?.(); } catch (error) { handleError(error, $t('unable_to_change_pin_code')); } finally { @@ -58,12 +56,13 @@
    -

    {$t('change_pin_code')}

    + {$t('change_pin_code')} - - +
    diff --git a/web/src/lib/components/user-settings-page/PinCodeCreateForm.svelte b/web/src/lib/components/user-settings-page/PinCodeCreateForm.svelte index ae07e976b7..16f6d94eb4 100644 --- a/web/src/lib/components/user-settings-page/PinCodeCreateForm.svelte +++ b/web/src/lib/components/user-settings-page/PinCodeCreateForm.svelte @@ -6,7 +6,7 @@ import PinCodeInput from '$lib/components/user-settings-page/PinCodeInput.svelte'; import { handleError } from '$lib/utils/handle-error'; import { setupPinCode } from '@immich/sdk'; - import { Button } from '@immich/ui'; + import { Button, Heading } from '@immich/ui'; import { t } from 'svelte-i18n'; interface Props { @@ -54,10 +54,9 @@
    {#if showLabel} -

    {$t('setup_pin_code')}

    + {$t('setup_pin_code')} {/if} -
    diff --git a/web/src/lib/components/user-settings-page/PinCodeInput.svelte b/web/src/lib/components/user-settings-page/PinCodeInput.svelte index 0a078f9870..c438d8f0b1 100644 --- a/web/src/lib/components/user-settings-page/PinCodeInput.svelte +++ b/web/src/lib/components/user-settings-page/PinCodeInput.svelte @@ -1,4 +1,5 @@ -
    +
    {#if hasPinCode} -
    - +
    +
    {:else} -
    +
    (hasPinCode = true)} />
    {/if} diff --git a/web/src/lib/modals/PinCodeResetModal.svelte b/web/src/lib/modals/PinCodeResetModal.svelte new file mode 100644 index 0000000000..a776ff7a94 --- /dev/null +++ b/web/src/lib/modals/PinCodeResetModal.svelte @@ -0,0 +1,81 @@ + + + + + + +
    {$t('reset_pin_code_description')}
    + {#if passwordLoginEnabled} +
    +
    + + + + {$t('reset_pin_code_with_password')} + + +
    + {/if} +
    + +
    + + + {#if passwordLoginEnabled} + + + + + {:else} + + {/if} + +
    From b1aacfdbd9aca59232f33249335502e47e98ed61 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 7 Aug 2025 15:44:49 -0500 Subject: [PATCH 216/748] chore: log resume backup process (#20768) --- mobile/lib/providers/backup/drift_backup.provider.dart | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/mobile/lib/providers/backup/drift_backup.provider.dart b/mobile/lib/providers/backup/drift_backup.provider.dart index 39949fd526..3966f5c0e6 100644 --- a/mobile/lib/providers/backup/drift_backup.provider.dart +++ b/mobile/lib/providers/backup/drift_backup.provider.dart @@ -9,6 +9,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/services/upload.service.dart'; +import 'package:logging/logging.dart'; class EnqueueStatus { final int enqueueCount; @@ -213,6 +214,7 @@ class DriftBackupNotifier extends StateNotifier { final UploadService _uploadService; StreamSubscription? _statusSubscription; StreamSubscription? _progressSubscription; + final _logger = Logger("DriftBackupNotifier"); /// Remove upload item from state void _removeUploadItem(String taskId) { @@ -333,17 +335,17 @@ class DriftBackupNotifier extends StateNotifier { } Future handleBackupResume(String userId) async { - debugPrint("handleBackupResume"); + _logger.info("Resuming backup tasks..."); final tasks = await _uploadService.getActiveTasks(kBackupGroup); - debugPrint("Found ${tasks.length} tasks"); + _logger.info("Found ${tasks.length} tasks"); if (tasks.isEmpty) { // Start a new backup queue - debugPrint("Start a new backup queue"); + _logger.info("Start a new backup queue"); await startBackup(userId); } - debugPrint("Tasks to resume: ${tasks.length}"); + _logger.info("Tasks to resume: ${tasks.length}"); await _uploadService.resumeBackup(); } From 9ecaa3fa9d1b5ecd42dbbc1cf6d09ddf8212d8a7 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Fri, 8 Aug 2025 10:05:59 -0400 Subject: [PATCH 217/748] feat: more cursed knowledge (#20794) --- docs/src/pages/cursed-knowledge.tsx | 39 +++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/docs/src/pages/cursed-knowledge.tsx b/docs/src/pages/cursed-knowledge.tsx index 0db89fbfe7..f3dacc2ce6 100644 --- a/docs/src/pages/cursed-knowledge.tsx +++ b/docs/src/pages/cursed-knowledge.tsx @@ -16,6 +16,9 @@ import { mdiCloudKeyOutline, mdiRegex, mdiCodeJson, + mdiClockOutline, + mdiAccountOutline, + mdiRestart, } from '@mdi/js'; import Layout from '@theme/Layout'; import React from 'react'; @@ -26,6 +29,42 @@ const withLanguage = (date: Date) => (language: string) => date.toLocaleDateStri type Item = Omit & { date: Date }; const items: Item[] = [ + { + icon: mdiClockOutline, + iconColor: 'gray', + title: 'setTimeout is cursed', + description: + 'The setTimeout method in JavaScript is cursed when used with small values because the implementation may or may not actually wait the specified time.', + link: { + url: 'https://github.com/immich-app/immich/pull/20655', + text: '#20655', + }, + date: new Date(2025, 7, 4), + }, + { + icon: mdiAccountOutline, + iconColor: '#DAB1DA', + title: 'PostgreSQL USER is cursed', + description: + 'The USER keyword in PostgreSQL is cursed because you can select from it like a table, which leads to confusion if you have a table name user as well.', + link: { + url: 'https://github.com/immich-app/immich/pull/19891', + text: '#19891', + }, + date: new Date(2025, 7, 4), + }, + { + icon: mdiRestart, + iconColor: '#8395e3', + title: 'PostgreSQL RESET is cursed', + description: + 'PostgreSQL RESET is cursed because it is impossible to RESET a PostgreSQL extension parameter if the extension has been uninstalled.', + link: { + url: 'https://github.com/immich-app/immich/pull/19363', + text: '#19363', + }, + date: new Date(2025, 5, 20), + }, { icon: mdiRegex, iconColor: 'purple', From 538d5c81ea0f4bc1c58eaa6e1c1bd30893a1e45c Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Fri, 8 Aug 2025 15:42:38 -0400 Subject: [PATCH 218/748] feat: reset oauth ids (#20798) --- i18n/en.json | 4 ++ mobile/openapi/README.md | 1 + mobile/openapi/lib/api.dart | 1 + mobile/openapi/lib/api/auth_admin_api.dart | 54 +++++++++++++++ mobile/openapi/lib/model/permission.dart | 3 + open-api/immich-openapi-specs.json | 31 ++++++++- open-api/typescript-sdk/src/fetch-client.ts | 12 +++- .../src/controllers/auth-admin.controller.ts | 18 +++++ server/src/controllers/index.ts | 2 + server/src/enum.ts | 2 + server/src/repositories/user.repository.ts | 4 ++ server/src/services/auth-admin.service.ts | 11 ++++ server/src/services/index.ts | 2 + .../specs/services/auth-admin.service.spec.ts | 66 +++++++++++++++++++ .../settings/auth/auth-settings.svelte | 42 ++++++++++-- 15 files changed, 247 insertions(+), 6 deletions(-) create mode 100644 mobile/openapi/lib/api/auth_admin_api.dart create mode 100644 server/src/controllers/auth-admin.controller.ts create mode 100644 server/src/services/auth-admin.service.ts create mode 100644 server/test/medium/specs/services/auth-admin.service.spec.ts diff --git a/i18n/en.json b/i18n/en.json index bc52dbd85c..2554bb5783 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -355,6 +355,9 @@ "trash_number_of_days_description": "Number of days to keep the assets in trash before permanently removing them", "trash_settings": "Trash Settings", "trash_settings_description": "Manage trash settings", + "unlink_all_oauth_accounts": "Unlink all OAuth accounts", + "unlink_all_oauth_accounts_description": "Remember to unlink all OAuth accounts before migrating to a new provider.", + "unlink_all_oauth_accounts_prompt": "Are you sure you want to unlink all OAuth accounts? This will reset the OAuth ID for each user and cannot be undone.", "user_cleanup_job": "User cleanup", "user_delete_delay": "{user}'s account and assets will be scheduled for permanent deletion in {delay, plural, one {# day} other {# days}}.", "user_delete_delay_settings": "Delete delay", @@ -921,6 +924,7 @@ "paths_validation_failed": "{paths, plural, one {# path} other {# paths}} failed validation", "profile_picture_transparent_pixels": "Profile pictures cannot have transparent pixels. Please zoom in and/or move the image.", "quota_higher_than_disk_size": "You set a quota higher than the disk size", + "something_went_wrong": "Something went wrong", "unable_to_add_album_users": "Unable to add users to album", "unable_to_add_assets_to_shared_link": "Unable to add assets to shared link", "unable_to_add_comment": "Unable to add comment", diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index c4349ff657..2397d55c78 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -108,6 +108,7 @@ Class | Method | HTTP request | Description *AssetsApi* | [**updateAssets**](doc//AssetsApi.md#updateassets) | **PUT** /assets | *AssetsApi* | [**uploadAsset**](doc//AssetsApi.md#uploadasset) | **POST** /assets | *AssetsApi* | [**viewAsset**](doc//AssetsApi.md#viewasset) | **GET** /assets/{id}/thumbnail | +*AuthAdminApi* | [**unlinkAllOAuthAccountsAdmin**](doc//AuthAdminApi.md#unlinkalloauthaccountsadmin) | **POST** /admin/auth/unlink-all | *AuthenticationApi* | [**changePassword**](doc//AuthenticationApi.md#changepassword) | **POST** /auth/change-password | *AuthenticationApi* | [**changePinCode**](doc//AuthenticationApi.md#changepincode) | **PUT** /auth/pin-code | *AuthenticationApi* | [**getAuthStatus**](doc//AuthenticationApi.md#getauthstatus) | **GET** /auth/status | diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart index 8c1fa1a80a..8ecb9cd5f5 100644 --- a/mobile/openapi/lib/api.dart +++ b/mobile/openapi/lib/api.dart @@ -34,6 +34,7 @@ part 'api/api_keys_api.dart'; part 'api/activities_api.dart'; part 'api/albums_api.dart'; part 'api/assets_api.dart'; +part 'api/auth_admin_api.dart'; part 'api/authentication_api.dart'; part 'api/deprecated_api.dart'; part 'api/download_api.dart'; diff --git a/mobile/openapi/lib/api/auth_admin_api.dart b/mobile/openapi/lib/api/auth_admin_api.dart new file mode 100644 index 0000000000..d22b449aab --- /dev/null +++ b/mobile/openapi/lib/api/auth_admin_api.dart @@ -0,0 +1,54 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + + +class AuthAdminApi { + AuthAdminApi([ApiClient? apiClient]) : apiClient = apiClient ?? defaultApiClient; + + final ApiClient apiClient; + + /// This endpoint is an admin-only route, and requires the `adminAuth.unlinkAll` permission. + /// + /// Note: This method returns the HTTP [Response]. + Future unlinkAllOAuthAccountsAdminWithHttpInfo() async { + // ignore: prefer_const_declarations + final apiPath = r'/admin/auth/unlink-all'; + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'POST', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// This endpoint is an admin-only route, and requires the `adminAuth.unlinkAll` permission. + Future unlinkAllOAuthAccountsAdmin() async { + final response = await unlinkAllOAuthAccountsAdminWithHttpInfo(); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + } +} diff --git a/mobile/openapi/lib/model/permission.dart b/mobile/openapi/lib/model/permission.dart index b0903e8f19..95b9a55fba 100644 --- a/mobile/openapi/lib/model/permission.dart +++ b/mobile/openapi/lib/model/permission.dart @@ -150,6 +150,7 @@ class Permission { static const adminUserPeriodRead = Permission._(r'adminUser.read'); static const adminUserPeriodUpdate = Permission._(r'adminUser.update'); static const adminUserPeriodDelete = Permission._(r'adminUser.delete'); + static const adminAuthPeriodUnlinkAll = Permission._(r'adminAuth.unlinkAll'); /// List of all possible values in this [enum][Permission]. static const values = [ @@ -280,6 +281,7 @@ class Permission { adminUserPeriodRead, adminUserPeriodUpdate, adminUserPeriodDelete, + adminAuthPeriodUnlinkAll, ]; static Permission? fromJson(dynamic value) => PermissionTypeTransformer().decode(value); @@ -445,6 +447,7 @@ class PermissionTypeTransformer { case r'adminUser.read': return Permission.adminUserPeriodRead; case r'adminUser.update': return Permission.adminUserPeriodUpdate; case r'adminUser.delete': return Permission.adminUserPeriodDelete; + case r'adminAuth.unlinkAll': return Permission.adminAuthPeriodUnlinkAll; default: if (!allowNull) { throw ArgumentError('Unknown enum value to decode: $data'); diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index e8b5df9dc1..ad22aa09c8 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -214,6 +214,34 @@ "description": "This endpoint requires the `activity.delete` permission." } }, + "/admin/auth/unlink-all": { + "post": { + "operationId": "unlinkAllOAuthAccountsAdmin", + "parameters": [], + "responses": { + "204": { + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Auth (admin)" + ], + "x-immich-admin-only": true, + "x-immich-permission": "adminAuth.unlinkAll", + "description": "This endpoint is an admin-only route, and requires the `adminAuth.unlinkAll` permission." + } + }, "/admin/notifications": { "post": { "operationId": "createNotification", @@ -12687,7 +12715,8 @@ "adminUser.create", "adminUser.read", "adminUser.update", - "adminUser.delete" + "adminUser.delete", + "adminAuth.unlinkAll" ], "type": "string" }, diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 5011e065eb..ee5e2a769d 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -1646,6 +1646,15 @@ export function deleteActivity({ id }: { method: "DELETE" })); } +/** + * This endpoint is an admin-only route, and requires the `adminAuth.unlinkAll` permission. + */ +export function unlinkAllOAuthAccountsAdmin(opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchText("/admin/auth/unlink-all", { + ...opts, + method: "POST" + })); +} export function createNotification({ notificationCreateDto }: { notificationCreateDto: NotificationCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -4669,7 +4678,8 @@ export enum Permission { AdminUserCreate = "adminUser.create", AdminUserRead = "adminUser.read", AdminUserUpdate = "adminUser.update", - AdminUserDelete = "adminUser.delete" + AdminUserDelete = "adminUser.delete", + AdminAuthUnlinkAll = "adminAuth.unlinkAll" } export enum AssetMediaStatus { Created = "created", diff --git a/server/src/controllers/auth-admin.controller.ts b/server/src/controllers/auth-admin.controller.ts new file mode 100644 index 0000000000..dba352783e --- /dev/null +++ b/server/src/controllers/auth-admin.controller.ts @@ -0,0 +1,18 @@ +import { Controller, HttpCode, HttpStatus, Post } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { Permission } from 'src/enum'; +import { Auth, Authenticated } from 'src/middleware/auth.guard'; +import { AuthAdminService } from 'src/services/auth-admin.service'; + +@ApiTags('Auth (admin)') +@Controller('admin/auth') +export class AuthAdminController { + constructor(private service: AuthAdminService) {} + @Post('unlink-all') + @Authenticated({ permission: Permission.AdminAuthUnlinkAll, admin: true }) + @HttpCode(HttpStatus.NO_CONTENT) + unlinkAllOAuthAccountsAdmin(@Auth() auth: AuthDto): Promise { + return this.service.unlinkAll(auth); + } +} diff --git a/server/src/controllers/index.ts b/server/src/controllers/index.ts index 9c39e580b6..137abf103c 100644 --- a/server/src/controllers/index.ts +++ b/server/src/controllers/index.ts @@ -4,6 +4,7 @@ import { APIKeyController } from 'src/controllers/api-key.controller'; import { AppController } from 'src/controllers/app.controller'; import { AssetMediaController } from 'src/controllers/asset-media.controller'; import { AssetController } from 'src/controllers/asset.controller'; +import { AuthAdminController } from 'src/controllers/auth-admin.controller'; import { AuthController } from 'src/controllers/auth.controller'; import { DownloadController } from 'src/controllers/download.controller'; import { DuplicateController } from 'src/controllers/duplicate.controller'; @@ -40,6 +41,7 @@ export const controllers = [ AssetController, AssetMediaController, AuthController, + AuthAdminController, DownloadController, DuplicateController, FaceController, diff --git a/server/src/enum.ts b/server/src/enum.ts index 8c7ee85a32..02ef222883 100644 --- a/server/src/enum.ts +++ b/server/src/enum.ts @@ -235,6 +235,8 @@ export enum Permission { AdminUserRead = 'adminUser.read', AdminUserUpdate = 'adminUser.update', AdminUserDelete = 'adminUser.delete', + + AdminAuthUnlinkAll = 'adminAuth.unlinkAll', } export enum SharedLinkType { diff --git a/server/src/repositories/user.repository.ts b/server/src/repositories/user.repository.ts index 9d5f19b26a..a63a4cc553 100644 --- a/server/src/repositories/user.repository.ts +++ b/server/src/repositories/user.repository.ts @@ -194,6 +194,10 @@ export class UserRepository { .executeTakeFirstOrThrow(); } + async updateAll(dto: Updateable) { + await this.db.updateTable('user').set(dto).execute(); + } + restore(id: string) { return this.db .updateTable('user') diff --git a/server/src/services/auth-admin.service.ts b/server/src/services/auth-admin.service.ts new file mode 100644 index 0000000000..3648a19957 --- /dev/null +++ b/server/src/services/auth-admin.service.ts @@ -0,0 +1,11 @@ +import { Injectable } from '@nestjs/common'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { BaseService } from 'src/services/base.service'; + +@Injectable() +export class AuthAdminService extends BaseService { + async unlinkAll(_auth: AuthDto) { + // TODO replace '' with null + await this.userRepository.updateAll({ oauthId: '' }); + } +} diff --git a/server/src/services/index.ts b/server/src/services/index.ts index 88b68d2c13..cad38ca1f4 100644 --- a/server/src/services/index.ts +++ b/server/src/services/index.ts @@ -5,6 +5,7 @@ import { ApiService } from 'src/services/api.service'; import { AssetMediaService } from 'src/services/asset-media.service'; import { AssetService } from 'src/services/asset.service'; import { AuditService } from 'src/services/audit.service'; +import { AuthAdminService } from 'src/services/auth-admin.service'; import { AuthService } from 'src/services/auth.service'; import { BackupService } from 'src/services/backup.service'; import { CliService } from 'src/services/cli.service'; @@ -49,6 +50,7 @@ export const services = [ AssetService, AuditService, AuthService, + AuthAdminService, BackupService, CliService, DatabaseService, diff --git a/server/test/medium/specs/services/auth-admin.service.spec.ts b/server/test/medium/specs/services/auth-admin.service.spec.ts new file mode 100644 index 0000000000..fa2a69f665 --- /dev/null +++ b/server/test/medium/specs/services/auth-admin.service.spec.ts @@ -0,0 +1,66 @@ +import { Kysely } from 'kysely'; +import { LoggingRepository } from 'src/repositories/logging.repository'; +import { UserRepository } from 'src/repositories/user.repository'; +import { DB } from 'src/schema'; +import { AuthAdminService } from 'src/services/auth-admin.service'; +import { newMediumService } from 'test/medium.factory'; +import { factory } from 'test/small.factory'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = (db?: Kysely) => { + return newMediumService(AuthAdminService, { + database: db || defaultDatabase, + real: [UserRepository], + mock: [LoggingRepository], + }); +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe(AuthAdminService.name, () => { + describe('unlinkAll', () => { + it('should reset user.oauthId', async () => { + const { sut, ctx } = setup(); + const userRepo = ctx.get(UserRepository); + const { user } = await ctx.newUser({ oauthId: 'test-oauth-id' }); + const auth = factory.auth(); + + await expect(sut.unlinkAll(auth)).resolves.toBeUndefined(); + await expect(userRepo.get(user.id, { withDeleted: true })).resolves.toEqual( + expect.objectContaining({ oauthId: '' }), + ); + }); + + it('should reset a deleted user', async () => { + const { sut, ctx } = setup(); + const userRepo = ctx.get(UserRepository); + const { user } = await ctx.newUser({ oauthId: 'test-oauth-id', deletedAt: new Date() }); + const auth = factory.auth(); + + await expect(sut.unlinkAll(auth)).resolves.toBeUndefined(); + await expect(userRepo.get(user.id, { withDeleted: true })).resolves.toEqual( + expect.objectContaining({ oauthId: '' }), + ); + }); + + it('should reset multiple users', async () => { + const { sut, ctx } = setup(); + const userRepo = ctx.get(UserRepository); + const { user: user1 } = await ctx.newUser({ oauthId: '1' }); + const { user: user2 } = await ctx.newUser({ oauthId: '2', deletedAt: new Date() }); + const auth = factory.auth(); + + await expect(sut.unlinkAll(auth)).resolves.toBeUndefined(); + await expect(userRepo.get(user1.id, { withDeleted: true })).resolves.toEqual( + expect.objectContaining({ oauthId: '' }), + ); + await expect(userRepo.get(user2.id, { withDeleted: true })).resolves.toEqual( + expect.objectContaining({ oauthId: '' }), + ); + }); + }); +}); diff --git a/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte b/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte index ce6dc26171..ef371910c5 100644 --- a/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte +++ b/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte @@ -1,5 +1,9 @@
    @@ -56,7 +82,7 @@ subtitle={$t('admin.oauth_settings_description')} >
    -

    + {#snippet children({ message })} {/snippet} -

    + + +
    + {$t('admin.unlink_all_oauth_accounts_description')} + +
    + Date: Fri, 8 Aug 2025 15:44:39 -0400 Subject: [PATCH 219/748] fix(sql-tools): null default (#20796) --- .../sql-tools/comparers/column.comparer.spec.ts | 17 +++++++++++++++++ .../src/sql-tools/comparers/column.comparer.ts | 4 ++-- server/src/sql-tools/helpers.ts | 2 +- .../transformers/column.transformer.spec.ts | 14 ++++++++++++++ 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/server/src/sql-tools/comparers/column.comparer.spec.ts b/server/src/sql-tools/comparers/column.comparer.spec.ts index fde237ad7b..0fd4ed74b5 100644 --- a/server/src/sql-tools/comparers/column.comparer.spec.ts +++ b/server/src/sql-tools/comparers/column.comparer.spec.ts @@ -62,6 +62,23 @@ describe('compareColumns', () => { ]); }); + it('should detect a change in default', () => { + const source: DatabaseColumn = { ...testColumn, nullable: true }; + const target: DatabaseColumn = { ...testColumn, nullable: true, default: "''" }; + const reason = `default is different (null vs '')`; + expect(compareColumns.onCompare(source, target)).toEqual([ + { + columnName: 'test', + tableName: 'table1', + type: 'ColumnAlter', + changes: { + default: 'NULL', + }, + reason, + }, + ]); + }); + it('should detect a comment change', () => { const source: DatabaseColumn = { ...testColumn, comment: 'new comment' }; const target: DatabaseColumn = { ...testColumn, comment: 'old comment' }; diff --git a/server/src/sql-tools/comparers/column.comparer.ts b/server/src/sql-tools/comparers/column.comparer.ts index 035fd6fc98..d3033430ef 100644 --- a/server/src/sql-tools/comparers/column.comparer.ts +++ b/server/src/sql-tools/comparers/column.comparer.ts @@ -72,9 +72,9 @@ export const compareColumns = { tableName: source.tableName, columnName: source.name, changes: { - default: String(source.default), + default: String(source.default ?? 'NULL'), }, - reason: `default is different (${source.default} vs ${target.default})`, + reason: `default is different (${source.default ?? 'null'} vs ${target.default})`, }); } diff --git a/server/src/sql-tools/helpers.ts b/server/src/sql-tools/helpers.ts index 8131f0350b..12586f27b2 100644 --- a/server/src/sql-tools/helpers.ts +++ b/server/src/sql-tools/helpers.ts @@ -175,7 +175,7 @@ export const isDefaultEqual = (source: DatabaseColumn, target: DatabaseColumn) = if ( withTypeCast(source.default, getColumnType(source)) === target.default || - source.default === withTypeCast(target.default, getColumnType(target)) + withTypeCast(target.default, getColumnType(target)) === source.default ) { return true; } diff --git a/server/src/sql-tools/transformers/column.transformer.spec.ts b/server/src/sql-tools/transformers/column.transformer.spec.ts index 1e29d4bff6..6828e2a72d 100644 --- a/server/src/sql-tools/transformers/column.transformer.spec.ts +++ b/server/src/sql-tools/transformers/column.transformer.spec.ts @@ -116,6 +116,20 @@ describe(transformColumns.name, () => { }), ).toEqual([`ALTER TABLE "table1" ALTER COLUMN "column1" SET DEFAULT uuid_generate_v4();`]); }); + + it('should update the default value to NULL', () => { + expect( + transformColumns(ctx, { + type: 'ColumnAlter', + tableName: 'table1', + columnName: 'column1', + changes: { + default: 'NULL', + }, + reason: 'unknown', + }), + ).toEqual([`ALTER TABLE "table1" ALTER COLUMN "column1" SET DEFAULT NULL;`]); + }); }); describe('ColumnDrop', () => { From 13563fc5079358a710901bca73eba1b3687ae77b Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Fri, 8 Aug 2025 15:56:37 -0400 Subject: [PATCH 220/748] chore: update response codes (#20770) * chore: update response codes * chore: skip problematic test --- e2e/src/api/specs/album.e2e-spec.ts | 2 +- e2e/src/api/specs/asset.e2e-spec.ts | 2 +- e2e/src/api/specs/partner.e2e-spec.ts | 2 +- e2e/src/api/specs/shared-link.e2e-spec.ts | 2 +- e2e/src/api/specs/user.e2e-spec.ts | 2 +- open-api/immich-openapi-specs.json | 44 +++++++++---------- open-api/typescript-sdk/src/fetch-client.ts | 4 +- server/src/controllers/activity.controller.ts | 2 +- server/src/controllers/album.controller.ts | 7 ++- server/src/controllers/api-key.controller.ts | 2 +- .../src/controllers/asset-media.controller.ts | 8 ++-- server/src/controllers/asset.controller.ts | 4 +- server/src/controllers/auth.controller.ts | 13 +++--- server/src/controllers/download.controller.ts | 4 +- .../src/controllers/duplicate.controller.ts | 4 +- server/src/controllers/face.controller.ts | 5 ++- server/src/controllers/job.controller.ts | 3 +- server/src/controllers/library.controller.ts | 8 ++-- server/src/controllers/memory.controller.ts | 4 +- .../notification-admin.controller.ts | 4 +- .../controllers/notification.controller.ts | 5 ++- server/src/controllers/oauth.controller.ts | 3 +- server/src/controllers/partner.controller.ts | 3 +- server/src/controllers/person.controller.ts | 5 ++- server/src/controllers/search.controller.ts | 10 ++--- server/src/controllers/server.controller.ts | 3 +- .../src/controllers/shared-link.controller.ts | 17 ++++++- server/src/controllers/stack.controller.ts | 2 +- server/src/controllers/sync.controller.ts | 12 ++--- .../controllers/system-metadata.controller.ts | 2 +- server/src/controllers/tag.controller.ts | 2 +- server/src/controllers/trash.controller.ts | 6 +-- server/src/controllers/user.controller.ts | 8 ++-- 33 files changed, 119 insertions(+), 85 deletions(-) diff --git a/e2e/src/api/specs/album.e2e-spec.ts b/e2e/src/api/specs/album.e2e-spec.ts index af9b17cc13..5615a312f2 100644 --- a/e2e/src/api/specs/album.e2e-spec.ts +++ b/e2e/src/api/specs/album.e2e-spec.ts @@ -683,7 +683,7 @@ describe('/albums', () => { .set('Authorization', `Bearer ${user1.accessToken}`) .send({ role: AlbumUserRole.Editor }); - expect(status).toBe(200); + expect(status).toBe(204); // Get album to verify the role change const { body } = await request(app) diff --git a/e2e/src/api/specs/asset.e2e-spec.ts b/e2e/src/api/specs/asset.e2e-spec.ts index 5e9d90ddc6..9c8b893075 100644 --- a/e2e/src/api/specs/asset.e2e-spec.ts +++ b/e2e/src/api/specs/asset.e2e-spec.ts @@ -555,7 +555,7 @@ describe('/asset', () => { expect(body).toMatchObject({ id: user1Assets[0].id, livePhotoVideoId: null }); }); - it('should update date time original when sidecar file contains DateTimeOriginal', async () => { + it.skip('should update date time original when sidecar file contains DateTimeOriginal', async () => { const sidecarData = ` diff --git a/e2e/src/api/specs/partner.e2e-spec.ts b/e2e/src/api/specs/partner.e2e-spec.ts index 1654f04e18..db37791bac 100644 --- a/e2e/src/api/specs/partner.e2e-spec.ts +++ b/e2e/src/api/specs/partner.e2e-spec.ts @@ -116,7 +116,7 @@ describe('/partners', () => { .delete(`/partners/${user3.userId}`) .set('Authorization', `Bearer ${user1.accessToken}`); - expect(status).toBe(200); + expect(status).toBe(204); }); it('should throw a bad request if partner not found', async () => { diff --git a/e2e/src/api/specs/shared-link.e2e-spec.ts b/e2e/src/api/specs/shared-link.e2e-spec.ts index f56a058529..f25a54786a 100644 --- a/e2e/src/api/specs/shared-link.e2e-spec.ts +++ b/e2e/src/api/specs/shared-link.e2e-spec.ts @@ -485,7 +485,7 @@ describe('/shared-links', () => { .delete(`/shared-links/${linkWithAlbum.id}`) .set('Authorization', `Bearer ${user1.accessToken}`); - expect(status).toBe(200); + expect(status).toBe(204); }); }); }); diff --git a/e2e/src/api/specs/user.e2e-spec.ts b/e2e/src/api/specs/user.e2e-spec.ts index b9eb140c56..3f280dddf5 100644 --- a/e2e/src/api/specs/user.e2e-spec.ts +++ b/e2e/src/api/specs/user.e2e-spec.ts @@ -304,7 +304,7 @@ describe('/users', () => { const { status } = await request(app) .delete(`/users/me/license`) .set('Authorization', `Bearer ${nonAdmin.accessToken}`); - expect(status).toBe(200); + expect(status).toBe(204); }); }); }); diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index ad22aa09c8..c80f3f8340 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -989,7 +989,7 @@ } ], "responses": { - "200": { + "204": { "description": "" } }, @@ -1280,7 +1280,7 @@ } ], "responses": { - "200": { + "204": { "description": "" } }, @@ -1333,7 +1333,7 @@ "required": true }, "responses": { - "200": { + "204": { "description": "" } }, @@ -2568,7 +2568,7 @@ "required": true }, "responses": { - "200": { + "204": { "description": "" } }, @@ -2603,7 +2603,7 @@ "required": true }, "responses": { - "201": { + "204": { "description": "" } }, @@ -2638,7 +2638,7 @@ "required": true }, "responses": { - "200": { + "204": { "description": "" } }, @@ -2665,7 +2665,7 @@ "operationId": "lockAuthSession", "parameters": [], "responses": { - "200": { + "204": { "description": "" } }, @@ -2700,7 +2700,7 @@ "required": true }, "responses": { - "200": { + "204": { "description": "" } }, @@ -2922,7 +2922,7 @@ "required": true }, "responses": { - "200": { + "204": { "description": "" } }, @@ -2994,7 +2994,7 @@ } ], "responses": { - "200": { + "204": { "description": "" } }, @@ -3123,7 +3123,7 @@ "required": true }, "responses": { - "200": { + "204": { "description": "" } }, @@ -3245,7 +3245,7 @@ "required": true }, "responses": { - "201": { + "204": { "description": "" } }, @@ -4252,7 +4252,7 @@ "required": true }, "responses": { - "200": { + "204": { "description": "" } }, @@ -4356,7 +4356,7 @@ "required": true }, "responses": { - "200": { + "204": { "description": "" } }, @@ -4393,7 +4393,7 @@ } ], "responses": { - "200": { + "204": { "description": "" } }, @@ -4586,7 +4586,7 @@ "required": true }, "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { @@ -4720,7 +4720,7 @@ } ], "responses": { - "200": { + "204": { "description": "" } }, @@ -5198,7 +5198,7 @@ "required": true }, "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { @@ -6250,7 +6250,7 @@ "operationId": "deleteServerLicense", "parameters": [], "responses": { - "200": { + "204": { "description": "" } }, @@ -6963,7 +6963,7 @@ } ], "responses": { - "200": { + "204": { "description": "" } }, @@ -8984,7 +8984,7 @@ "operationId": "deleteUserLicense", "parameters": [], "responses": { - "200": { + "204": { "description": "" } }, @@ -9085,7 +9085,7 @@ "operationId": "deleteUserOnboarding", "parameters": [], "responses": { - "200": { + "204": { "description": "" } }, diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index ee5e2a769d..892e869579 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -2978,7 +2978,7 @@ export function linkOAuthAccount({ oAuthCallbackDto }: { oAuthCallbackDto: OAuthCallbackDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ - status: 201; + status: 200; data: UserAdminResponseDto; }>("/oauth/link", oazapfts.json({ ...opts, @@ -3169,7 +3169,7 @@ export function mergePerson({ id, mergePersonDto }: { mergePersonDto: MergePersonDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ - status: 201; + status: 200; data: BulkIdResponseDto[]; }>(`/people/${encodeURIComponent(id)}/merge`, oazapfts.json({ ...opts, diff --git a/server/src/controllers/activity.controller.ts b/server/src/controllers/activity.controller.ts index d2d34da102..75b2e2f8a3 100644 --- a/server/src/controllers/activity.controller.ts +++ b/server/src/controllers/activity.controller.ts @@ -46,8 +46,8 @@ export class ActivityController { } @Delete(':id') - @HttpCode(HttpStatus.NO_CONTENT) @Authenticated({ permission: Permission.ActivityDelete }) + @HttpCode(HttpStatus.NO_CONTENT) deleteActivity(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.delete(auth, id); } diff --git a/server/src/controllers/album.controller.ts b/server/src/controllers/album.controller.ts index 36c5e0b13b..a331fc04f1 100644 --- a/server/src/controllers/album.controller.ts +++ b/server/src/controllers/album.controller.ts @@ -1,4 +1,4 @@ -import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query } from '@nestjs/common'; +import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Patch, Post, Put, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { AddUsersDto, @@ -62,6 +62,7 @@ export class AlbumController { @Delete(':id') @Authenticated({ permission: Permission.AlbumDelete }) + @HttpCode(HttpStatus.NO_CONTENT) deleteAlbum(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto) { return this.service.delete(auth, id); } @@ -98,6 +99,7 @@ export class AlbumController { @Put(':id/user/:userId') @Authenticated({ permission: Permission.AlbumUserUpdate }) + @HttpCode(HttpStatus.NO_CONTENT) updateAlbumUser( @Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, @@ -109,11 +111,12 @@ export class AlbumController { @Delete(':id/user/:userId') @Authenticated({ permission: Permission.AlbumUserDelete }) + @HttpCode(HttpStatus.NO_CONTENT) removeUserFromAlbum( @Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, @Param('userId', new ParseMeUUIDPipe({ version: '4' })) userId: string, - ) { + ): Promise { return this.service.removeUser(auth, id, userId); } } diff --git a/server/src/controllers/api-key.controller.ts b/server/src/controllers/api-key.controller.ts index 6347a1274a..dc9e85f33a 100644 --- a/server/src/controllers/api-key.controller.ts +++ b/server/src/controllers/api-key.controller.ts @@ -41,8 +41,8 @@ export class APIKeyController { } @Delete(':id') - @HttpCode(HttpStatus.NO_CONTENT) @Authenticated({ permission: Permission.ApiKeyDelete }) + @HttpCode(HttpStatus.NO_CONTENT) deleteApiKey(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.delete(auth, id); } diff --git a/server/src/controllers/asset-media.controller.ts b/server/src/controllers/asset-media.controller.ts index 8e83b77fb0..3b216aca0c 100644 --- a/server/src/controllers/asset-media.controller.ts +++ b/server/src/controllers/asset-media.controller.ts @@ -171,12 +171,12 @@ export class AssetMediaController { * Checks if multiple assets exist on the server and returns all existing - used by background backup */ @Post('exist') - @HttpCode(HttpStatus.OK) + @Authenticated() @ApiOperation({ summary: 'checkExistingAssets', description: 'Checks if multiple assets exist on the server and returns all existing - used by background backup', }) - @Authenticated() + @HttpCode(HttpStatus.OK) checkExistingAssets( @Auth() auth: AuthDto, @Body() dto: CheckExistingAssetsDto, @@ -188,12 +188,12 @@ export class AssetMediaController { * Checks if assets exist by checksums */ @Post('bulk-upload-check') - @HttpCode(HttpStatus.OK) + @Authenticated() @ApiOperation({ summary: 'checkBulkUpload', description: 'Checks if assets exist by checksums', }) - @Authenticated() + @HttpCode(HttpStatus.OK) checkBulkUpload( @Auth() auth: AuthDto, @Body() dto: AssetBulkUploadCheckDto, diff --git a/server/src/controllers/asset.controller.ts b/server/src/controllers/asset.controller.ts index d23785a5ff..edb5aab602 100644 --- a/server/src/controllers/asset.controller.ts +++ b/server/src/controllers/asset.controller.ts @@ -57,15 +57,15 @@ export class AssetController { } @Put() - @HttpCode(HttpStatus.NO_CONTENT) @Authenticated({ permission: Permission.AssetUpdate }) + @HttpCode(HttpStatus.NO_CONTENT) updateAssets(@Auth() auth: AuthDto, @Body() dto: AssetBulkUpdateDto): Promise { return this.service.updateAll(auth, dto); } @Delete() - @HttpCode(HttpStatus.NO_CONTENT) @Authenticated({ permission: Permission.AssetDelete }) + @HttpCode(HttpStatus.NO_CONTENT) deleteAssets(@Auth() auth: AuthDto, @Body() dto: AssetBulkDeleteDto): Promise { return this.service.deleteAll(auth, dto); } diff --git a/server/src/controllers/auth.controller.ts b/server/src/controllers/auth.controller.ts index 30b0d662f2..e865d18f59 100644 --- a/server/src/controllers/auth.controller.ts +++ b/server/src/controllers/auth.controller.ts @@ -49,22 +49,22 @@ export class AuthController { } @Post('validateToken') - @HttpCode(HttpStatus.OK) @Authenticated() + @HttpCode(HttpStatus.OK) validateAccessToken(): ValidateAccessTokenResponseDto { return { authStatus: true }; } @Post('change-password') - @HttpCode(HttpStatus.OK) @Authenticated({ permission: Permission.AuthChangePassword }) + @HttpCode(HttpStatus.OK) changePassword(@Auth() auth: AuthDto, @Body() dto: ChangePasswordDto): Promise { return this.service.changePassword(auth, dto); } @Post('logout') - @HttpCode(HttpStatus.OK) @Authenticated() + @HttpCode(HttpStatus.OK) async logout( @Req() request: Request, @Res({ passthrough: true }) res: Response, @@ -88,32 +88,35 @@ export class AuthController { @Post('pin-code') @Authenticated({ permission: Permission.PinCodeCreate }) + @HttpCode(HttpStatus.NO_CONTENT) setupPinCode(@Auth() auth: AuthDto, @Body() dto: PinCodeSetupDto): Promise { return this.service.setupPinCode(auth, dto); } @Put('pin-code') @Authenticated({ permission: Permission.PinCodeUpdate }) + @HttpCode(HttpStatus.NO_CONTENT) async changePinCode(@Auth() auth: AuthDto, @Body() dto: PinCodeChangeDto): Promise { return this.service.changePinCode(auth, dto); } @Delete('pin-code') @Authenticated({ permission: Permission.PinCodeDelete }) + @HttpCode(HttpStatus.NO_CONTENT) async resetPinCode(@Auth() auth: AuthDto, @Body() dto: PinCodeResetDto): Promise { return this.service.resetPinCode(auth, dto); } @Post('session/unlock') - @HttpCode(HttpStatus.OK) @Authenticated() + @HttpCode(HttpStatus.NO_CONTENT) async unlockAuthSession(@Auth() auth: AuthDto, @Body() dto: SessionUnlockDto): Promise { return this.service.unlockSession(auth, dto); } @Post('session/lock') - @HttpCode(HttpStatus.OK) @Authenticated() + @HttpCode(HttpStatus.NO_CONTENT) async lockAuthSession(@Auth() auth: AuthDto): Promise { return this.service.lockSession(auth); } diff --git a/server/src/controllers/download.controller.ts b/server/src/controllers/download.controller.ts index 4f5b18e585..a7c2af78ed 100644 --- a/server/src/controllers/download.controller.ts +++ b/server/src/controllers/download.controller.ts @@ -20,9 +20,9 @@ export class DownloadController { } @Post('archive') - @HttpCode(HttpStatus.OK) - @FileResponse() @Authenticated({ permission: Permission.AssetDownload, sharedLink: true }) + @FileResponse() + @HttpCode(HttpStatus.OK) downloadArchive(@Auth() auth: AuthDto, @Body() dto: AssetIdsDto): Promise { return this.service.downloadArchive(auth, dto).then(asStreamableFile); } diff --git a/server/src/controllers/duplicate.controller.ts b/server/src/controllers/duplicate.controller.ts index da6fe4042d..9cf5ae97a6 100644 --- a/server/src/controllers/duplicate.controller.ts +++ b/server/src/controllers/duplicate.controller.ts @@ -1,4 +1,4 @@ -import { Body, Controller, Delete, Get, Param } from '@nestjs/common'; +import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; @@ -21,12 +21,14 @@ export class DuplicateController { @Delete() @Authenticated({ permission: Permission.DuplicateDelete }) + @HttpCode(HttpStatus.NO_CONTENT) deleteDuplicates(@Auth() auth: AuthDto, @Body() dto: BulkIdsDto): Promise { return this.service.deleteAll(auth, dto); } @Delete(':id') @Authenticated({ permission: Permission.DuplicateDelete }) + @HttpCode(HttpStatus.NO_CONTENT) deleteDuplicate(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.delete(auth, id); } diff --git a/server/src/controllers/face.controller.ts b/server/src/controllers/face.controller.ts index 20b6db6039..564b217c16 100644 --- a/server/src/controllers/face.controller.ts +++ b/server/src/controllers/face.controller.ts @@ -1,4 +1,4 @@ -import { Body, Controller, Delete, Get, Param, Post, Put, Query } from '@nestjs/common'; +import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Put, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { AuthDto } from 'src/dtos/auth.dto'; import { @@ -42,7 +42,8 @@ export class FaceController { @Delete(':id') @Authenticated({ permission: Permission.FaceDelete }) - deleteFace(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, @Body() dto: AssetFaceDeleteDto) { + @HttpCode(HttpStatus.NO_CONTENT) + deleteFace(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, @Body() dto: AssetFaceDeleteDto): Promise { return this.service.deleteFace(auth, id, dto); } } diff --git a/server/src/controllers/job.controller.ts b/server/src/controllers/job.controller.ts index e6b40e6810..9c4e819649 100644 --- a/server/src/controllers/job.controller.ts +++ b/server/src/controllers/job.controller.ts @@ -1,4 +1,4 @@ -import { Body, Controller, Get, Param, Post, Put } from '@nestjs/common'; +import { Body, Controller, Get, HttpCode, HttpStatus, Param, Post, Put } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { AllJobStatusResponseDto, JobCommandDto, JobCreateDto, JobIdParamDto, JobStatusDto } from 'src/dtos/job.dto'; import { Permission } from 'src/enum'; @@ -18,6 +18,7 @@ export class JobController { @Post() @Authenticated({ permission: Permission.JobCreate, admin: true }) + @HttpCode(HttpStatus.NO_CONTENT) createJob(@Body() dto: JobCreateDto): Promise { return this.service.create(dto); } diff --git a/server/src/controllers/library.controller.ts b/server/src/controllers/library.controller.ts index e090586f57..b37bc40ce7 100644 --- a/server/src/controllers/library.controller.ts +++ b/server/src/controllers/library.controller.ts @@ -43,15 +43,15 @@ export class LibraryController { } @Delete(':id') - @HttpCode(HttpStatus.NO_CONTENT) @Authenticated({ permission: Permission.LibraryDelete, admin: true }) + @HttpCode(HttpStatus.NO_CONTENT) deleteLibrary(@Param() { id }: UUIDParamDto): Promise { return this.service.delete(id); } @Post(':id/validate') - @HttpCode(200) @Authenticated({ admin: true }) + @HttpCode(HttpStatus.OK) // TODO: change endpoint to validate current settings instead validate(@Param() { id }: UUIDParamDto, @Body() dto: ValidateLibraryDto): Promise { return this.service.validate(id, dto); @@ -64,9 +64,9 @@ export class LibraryController { } @Post(':id/scan') - @HttpCode(HttpStatus.NO_CONTENT) @Authenticated({ permission: Permission.LibraryUpdate, admin: true }) - scanLibrary(@Param() { id }: UUIDParamDto) { + @HttpCode(HttpStatus.NO_CONTENT) + scanLibrary(@Param() { id }: UUIDParamDto): Promise { return this.service.queueScan(id); } } diff --git a/server/src/controllers/memory.controller.ts b/server/src/controllers/memory.controller.ts index 786f2af8a4..3b5ad2bb4e 100644 --- a/server/src/controllers/memory.controller.ts +++ b/server/src/controllers/memory.controller.ts @@ -54,8 +54,8 @@ export class MemoryController { } @Delete(':id') - @HttpCode(HttpStatus.NO_CONTENT) @Authenticated({ permission: Permission.MemoryDelete }) + @HttpCode(HttpStatus.NO_CONTENT) deleteMemory(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.remove(auth, id); } @@ -71,8 +71,8 @@ export class MemoryController { } @Delete(':id/assets') - @HttpCode(HttpStatus.OK) @Authenticated({ permission: Permission.MemoryAssetDelete }) + @HttpCode(HttpStatus.OK) removeMemoryAssets( @Auth() auth: AuthDto, @Body() dto: BulkIdsDto, diff --git a/server/src/controllers/notification-admin.controller.ts b/server/src/controllers/notification-admin.controller.ts index 9bac865bdf..28ca7bfd30 100644 --- a/server/src/controllers/notification-admin.controller.ts +++ b/server/src/controllers/notification-admin.controller.ts @@ -25,15 +25,15 @@ export class NotificationAdminController { } @Post('test-email') - @HttpCode(HttpStatus.OK) @Authenticated({ admin: true }) + @HttpCode(HttpStatus.OK) sendTestEmailAdmin(@Auth() auth: AuthDto, @Body() dto: SystemConfigSmtpDto): Promise { return this.service.sendTestEmail(auth.user.id, dto); } @Post('templates/:name') - @HttpCode(HttpStatus.OK) @Authenticated({ admin: true }) + @HttpCode(HttpStatus.OK) getNotificationTemplateAdmin( @Auth() auth: AuthDto, @Param('name') name: EmailTemplate, diff --git a/server/src/controllers/notification.controller.ts b/server/src/controllers/notification.controller.ts index af4eb198b6..8ce183c5d0 100644 --- a/server/src/controllers/notification.controller.ts +++ b/server/src/controllers/notification.controller.ts @@ -1,4 +1,4 @@ -import { Body, Controller, Delete, Get, Param, Put, Query } from '@nestjs/common'; +import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Put, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { AuthDto } from 'src/dtos/auth.dto'; import { @@ -26,12 +26,14 @@ export class NotificationController { @Put() @Authenticated({ permission: Permission.NotificationUpdate }) + @HttpCode(HttpStatus.NO_CONTENT) updateNotifications(@Auth() auth: AuthDto, @Body() dto: NotificationUpdateAllDto): Promise { return this.service.updateAll(auth, dto); } @Delete() @Authenticated({ permission: Permission.NotificationDelete }) + @HttpCode(HttpStatus.NO_CONTENT) deleteNotifications(@Auth() auth: AuthDto, @Body() dto: NotificationDeleteAllDto): Promise { return this.service.deleteAll(auth, dto); } @@ -54,6 +56,7 @@ export class NotificationController { @Delete(':id') @Authenticated({ permission: Permission.NotificationDelete }) + @HttpCode(HttpStatus.NO_CONTENT) deleteNotification(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.delete(auth, id); } diff --git a/server/src/controllers/oauth.controller.ts b/server/src/controllers/oauth.controller.ts index 7da75f573a..f81a184557 100644 --- a/server/src/controllers/oauth.controller.ts +++ b/server/src/controllers/oauth.controller.ts @@ -70,6 +70,7 @@ export class OAuthController { @Post('link') @Authenticated() + @HttpCode(HttpStatus.OK) linkOAuthAccount( @Req() request: Request, @Auth() auth: AuthDto, @@ -79,8 +80,8 @@ export class OAuthController { } @Post('unlink') - @HttpCode(HttpStatus.OK) @Authenticated() + @HttpCode(HttpStatus.OK) unlinkOAuthAccount(@Auth() auth: AuthDto): Promise { return this.service.unlink(auth); } diff --git a/server/src/controllers/partner.controller.ts b/server/src/controllers/partner.controller.ts index 6b6efaa570..f2f4e3d7d6 100644 --- a/server/src/controllers/partner.controller.ts +++ b/server/src/controllers/partner.controller.ts @@ -1,4 +1,4 @@ -import { Body, Controller, Delete, Get, Param, Post, Put, Query } from '@nestjs/common'; +import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Put, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { AuthDto } from 'src/dtos/auth.dto'; import { PartnerResponseDto, PartnerSearchDto, UpdatePartnerDto } from 'src/dtos/partner.dto'; @@ -36,6 +36,7 @@ export class PartnerController { @Delete(':id') @Authenticated({ permission: Permission.PartnerDelete }) + @HttpCode(HttpStatus.NO_CONTENT) removePartner(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.remove(auth, id); } diff --git a/server/src/controllers/person.controller.ts b/server/src/controllers/person.controller.ts index ec66f7a9ca..84bb864cd3 100644 --- a/server/src/controllers/person.controller.ts +++ b/server/src/controllers/person.controller.ts @@ -63,8 +63,8 @@ export class PersonController { } @Delete() - @HttpCode(HttpStatus.NO_CONTENT) @Authenticated({ permission: Permission.PersonDelete }) + @HttpCode(HttpStatus.NO_CONTENT) deletePeople(@Auth() auth: AuthDto, @Body() dto: BulkIdsDto): Promise { return this.service.deleteAll(auth, dto); } @@ -86,8 +86,8 @@ export class PersonController { } @Delete(':id') - @HttpCode(HttpStatus.NO_CONTENT) @Authenticated({ permission: Permission.PersonDelete }) + @HttpCode(HttpStatus.NO_CONTENT) deletePerson(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.delete(auth, id); } @@ -122,6 +122,7 @@ export class PersonController { @Post(':id/merge') @Authenticated({ permission: Permission.PersonMerge }) + @HttpCode(HttpStatus.OK) mergePerson( @Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, diff --git a/server/src/controllers/search.controller.ts b/server/src/controllers/search.controller.ts index 15f8bc3a5a..f9aa6bce81 100644 --- a/server/src/controllers/search.controller.ts +++ b/server/src/controllers/search.controller.ts @@ -27,36 +27,36 @@ export class SearchController { constructor(private service: SearchService) {} @Post('metadata') - @HttpCode(HttpStatus.OK) @Authenticated({ permission: Permission.AssetRead }) + @HttpCode(HttpStatus.OK) searchAssets(@Auth() auth: AuthDto, @Body() dto: MetadataSearchDto): Promise { return this.service.searchMetadata(auth, dto); } @Post('statistics') - @HttpCode(HttpStatus.OK) @Authenticated({ permission: Permission.AssetStatistics }) + @HttpCode(HttpStatus.OK) searchAssetStatistics(@Auth() auth: AuthDto, @Body() dto: StatisticsSearchDto): Promise { return this.service.searchStatistics(auth, dto); } @Post('random') - @HttpCode(HttpStatus.OK) @Authenticated({ permission: Permission.AssetRead }) + @HttpCode(HttpStatus.OK) searchRandom(@Auth() auth: AuthDto, @Body() dto: RandomSearchDto): Promise { return this.service.searchRandom(auth, dto); } @Post('large-assets') - @HttpCode(HttpStatus.OK) @Authenticated({ permission: Permission.AssetRead }) + @HttpCode(HttpStatus.OK) searchLargeAssets(@Auth() auth: AuthDto, @Query() dto: LargeAssetSearchDto): Promise { return this.service.searchLargeAssets(auth, dto); } @Post('smart') - @HttpCode(HttpStatus.OK) @Authenticated({ permission: Permission.AssetRead }) + @HttpCode(HttpStatus.OK) searchSmart(@Auth() auth: AuthDto, @Body() dto: SmartSearchDto): Promise { return this.service.searchSmart(auth, dto); } diff --git a/server/src/controllers/server.controller.ts b/server/src/controllers/server.controller.ts index 0c184ba302..f9a340eb31 100644 --- a/server/src/controllers/server.controller.ts +++ b/server/src/controllers/server.controller.ts @@ -1,4 +1,4 @@ -import { Body, Controller, Delete, Get, Put } from '@nestjs/common'; +import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Put } from '@nestjs/common'; import { ApiNotFoundResponse, ApiTags } from '@nestjs/swagger'; import { LicenseKeyDto, LicenseResponseDto } from 'src/dtos/license.dto'; import { @@ -104,6 +104,7 @@ export class ServerController { @Delete('license') @Authenticated({ permission: Permission.ServerLicenseDelete, admin: true }) + @HttpCode(HttpStatus.NO_CONTENT) deleteServerLicense(): Promise { return this.service.deleteLicense(); } diff --git a/server/src/controllers/shared-link.controller.ts b/server/src/controllers/shared-link.controller.ts index 273d625ca7..ef0a93e012 100644 --- a/server/src/controllers/shared-link.controller.ts +++ b/server/src/controllers/shared-link.controller.ts @@ -1,4 +1,18 @@ -import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query, Req, Res } from '@nestjs/common'; +import { + Body, + Controller, + Delete, + Get, + HttpCode, + HttpStatus, + Param, + Patch, + Post, + Put, + Query, + Req, + Res, +} from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { Request, Response } from 'express'; import { AssetIdsResponseDto } from 'src/dtos/asset-ids.response.dto'; @@ -73,6 +87,7 @@ export class SharedLinkController { @Delete(':id') @Authenticated({ permission: Permission.SharedLinkDelete }) + @HttpCode(HttpStatus.NO_CONTENT) removeSharedLink(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.remove(auth, id); } diff --git a/server/src/controllers/stack.controller.ts b/server/src/controllers/stack.controller.ts index 5b153a163b..6acd4abc24 100644 --- a/server/src/controllers/stack.controller.ts +++ b/server/src/controllers/stack.controller.ts @@ -49,8 +49,8 @@ export class StackController { } @Delete(':id') - @HttpCode(HttpStatus.NO_CONTENT) @Authenticated({ permission: Permission.StackDelete }) + @HttpCode(HttpStatus.NO_CONTENT) deleteStack(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.delete(auth, id); } diff --git a/server/src/controllers/sync.controller.ts b/server/src/controllers/sync.controller.ts index a7b2b21a54..61432e43e3 100644 --- a/server/src/controllers/sync.controller.ts +++ b/server/src/controllers/sync.controller.ts @@ -26,23 +26,23 @@ export class SyncController { ) {} @Post('full-sync') - @HttpCode(HttpStatus.OK) @Authenticated() + @HttpCode(HttpStatus.OK) getFullSyncForUser(@Auth() auth: AuthDto, @Body() dto: AssetFullSyncDto): Promise { return this.service.getFullSync(auth, dto); } @Post('delta-sync') - @HttpCode(HttpStatus.OK) @Authenticated() + @HttpCode(HttpStatus.OK) getDeltaSync(@Auth() auth: AuthDto, @Body() dto: AssetDeltaSyncDto): Promise { return this.service.getDeltaSync(auth, dto); } @Post('stream') + @Authenticated({ permission: Permission.SyncStream }) @Header('Content-Type', 'application/jsonlines+json') @HttpCode(HttpStatus.OK) - @Authenticated({ permission: Permission.SyncStream }) async getSyncStream(@Auth() auth: AuthDto, @Res() res: Response, @Body() dto: SyncStreamDto) { try { await this.service.stream(auth, res, dto); @@ -59,16 +59,16 @@ export class SyncController { } @Post('ack') - @HttpCode(HttpStatus.NO_CONTENT) @Authenticated({ permission: Permission.SyncCheckpointUpdate }) + @HttpCode(HttpStatus.NO_CONTENT) sendSyncAck(@Auth() auth: AuthDto, @Body() dto: SyncAckSetDto) { return this.service.setAcks(auth, dto); } @Delete('ack') - @HttpCode(HttpStatus.NO_CONTENT) @Authenticated({ permission: Permission.SyncCheckpointDelete }) - deleteSyncAck(@Auth() auth: AuthDto, @Body() dto: SyncAckDeleteDto) { + @HttpCode(HttpStatus.NO_CONTENT) + deleteSyncAck(@Auth() auth: AuthDto, @Body() dto: SyncAckDeleteDto): Promise { return this.service.deleteAcks(auth, dto); } } diff --git a/server/src/controllers/system-metadata.controller.ts b/server/src/controllers/system-metadata.controller.ts index ad2245a391..d6634e9444 100644 --- a/server/src/controllers/system-metadata.controller.ts +++ b/server/src/controllers/system-metadata.controller.ts @@ -21,8 +21,8 @@ export class SystemMetadataController { } @Post('admin-onboarding') - @HttpCode(HttpStatus.NO_CONTENT) @Authenticated({ permission: Permission.SystemMetadataUpdate, admin: true }) + @HttpCode(HttpStatus.NO_CONTENT) updateAdminOnboarding(@Body() dto: AdminOnboardingUpdateDto): Promise { return this.service.updateAdminOnboarding(dto); } diff --git a/server/src/controllers/tag.controller.ts b/server/src/controllers/tag.controller.ts index 4906bc0c6e..59915ef2a4 100644 --- a/server/src/controllers/tag.controller.ts +++ b/server/src/controllers/tag.controller.ts @@ -57,8 +57,8 @@ export class TagController { } @Delete(':id') - @HttpCode(HttpStatus.NO_CONTENT) @Authenticated({ permission: Permission.TagDelete }) + @HttpCode(HttpStatus.NO_CONTENT) deleteTag(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.remove(auth, id); } diff --git a/server/src/controllers/trash.controller.ts b/server/src/controllers/trash.controller.ts index 1bb46e4f98..eaf489f104 100644 --- a/server/src/controllers/trash.controller.ts +++ b/server/src/controllers/trash.controller.ts @@ -13,22 +13,22 @@ export class TrashController { constructor(private service: TrashService) {} @Post('empty') - @HttpCode(HttpStatus.OK) @Authenticated({ permission: Permission.AssetDelete }) + @HttpCode(HttpStatus.OK) emptyTrash(@Auth() auth: AuthDto): Promise { return this.service.empty(auth); } @Post('restore') - @HttpCode(HttpStatus.OK) @Authenticated({ permission: Permission.AssetDelete }) + @HttpCode(HttpStatus.OK) restoreTrash(@Auth() auth: AuthDto): Promise { return this.service.restore(auth); } @Post('restore/assets') - @HttpCode(HttpStatus.OK) @Authenticated({ permission: Permission.AssetDelete }) + @HttpCode(HttpStatus.OK) restoreAssets(@Auth() auth: AuthDto, @Body() dto: BulkIdsDto): Promise { return this.service.restoreAssets(auth, dto); } diff --git a/server/src/controllers/user.controller.ts b/server/src/controllers/user.controller.ts index 1b91e1a848..d72b088c54 100644 --- a/server/src/controllers/user.controller.ts +++ b/server/src/controllers/user.controller.ts @@ -84,6 +84,7 @@ export class UserController { @Delete('me/license') @Authenticated({ permission: Permission.UserLicenseDelete }) + @HttpCode(HttpStatus.NO_CONTENT) async deleteUserLicense(@Auth() auth: AuthDto): Promise { await this.service.deleteLicense(auth); } @@ -102,6 +103,7 @@ export class UserController { @Delete('me/onboarding') @Authenticated({ permission: Permission.UserOnboardingDelete }) + @HttpCode(HttpStatus.NO_CONTENT) async deleteUserOnboarding(@Auth() auth: AuthDto): Promise { await this.service.deleteOnboarding(auth); } @@ -112,11 +114,11 @@ export class UserController { return this.service.get(id); } + @Post('profile-image') + @Authenticated({ permission: Permission.UserProfileImageUpdate }) @UseInterceptors(FileUploadInterceptor) @ApiConsumes('multipart/form-data') @ApiBody({ description: 'A new avatar for the user', type: CreateProfileImageDto }) - @Post('profile-image') - @Authenticated({ permission: Permission.UserProfileImageUpdate }) createProfileImage( @Auth() auth: AuthDto, @UploadedFile() fileInfo: Express.Multer.File, @@ -125,8 +127,8 @@ export class UserController { } @Delete('profile-image') - @HttpCode(HttpStatus.NO_CONTENT) @Authenticated({ permission: Permission.UserProfileImageDelete }) + @HttpCode(HttpStatus.NO_CONTENT) deleteProfileImage(@Auth() auth: AuthDto): Promise { return this.service.deleteProfileImage(auth); } From 4b9019e7627dfc3ddfbd9f9f54c95395ebc0b5a0 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 9 Aug 2025 23:01:47 -0500 Subject: [PATCH 221/748] fix: return method correctly (#20831) --- mobile/lib/providers/backup/drift_backup.provider.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/lib/providers/backup/drift_backup.provider.dart b/mobile/lib/providers/backup/drift_backup.provider.dart index 3966f5c0e6..748a099049 100644 --- a/mobile/lib/providers/backup/drift_backup.provider.dart +++ b/mobile/lib/providers/backup/drift_backup.provider.dart @@ -342,11 +342,11 @@ class DriftBackupNotifier extends StateNotifier { if (tasks.isEmpty) { // Start a new backup queue _logger.info("Start a new backup queue"); - await startBackup(userId); + return startBackup(userId); } _logger.info("Tasks to resume: ${tasks.length}"); - await _uploadService.resumeBackup(); + return _uploadService.resumeBackup(); } @override From d6d31c6695d9fd08c230c9486514bcc6d37eda16 Mon Sep 17 00:00:00 2001 From: Nicholas <30300649+NicholasFlamy@users.noreply.github.com> Date: Sun, 10 Aug 2025 22:23:21 -0400 Subject: [PATCH 222/748] fix: change all download icons to `mdiDownload` for clarity and consistency (#20821) change all download icons to `mdiDownload` for clarity and consistency --- web/src/lib/components/album-page/album-viewer.svelte | 4 ++-- web/src/lib/components/album-page/albums-list.svelte | 4 ++-- .../asset-viewer/actions/download-action.svelte | 6 +++--- .../components/photos-page/actions/download-action.svelte | 8 +++----- .../components/share-page/individual-shared-viewer.svelte | 4 ++-- .../[[photos=photos]]/[[assetId=id]]/+page.svelte | 4 ++-- 6 files changed, 14 insertions(+), 16 deletions(-) diff --git a/web/src/lib/components/album-page/album-viewer.svelte b/web/src/lib/components/album-page/album-viewer.svelte index 0866d38557..8c91d29a34 100644 --- a/web/src/lib/components/album-page/album-viewer.svelte +++ b/web/src/lib/components/album-page/album-viewer.svelte @@ -14,7 +14,7 @@ import { fileUploadHandler, openFileUploadDialog } from '$lib/utils/file-uploader'; import type { AlbumResponseDto, SharedLinkResponseDto, UserResponseDto } from '@immich/sdk'; import { IconButton } from '@immich/ui'; - import { mdiFileImagePlusOutline, mdiFolderDownloadOutline } from '@mdi/js'; + import { mdiDownload, mdiFileImagePlusOutline } from '@mdi/js'; import { onDestroy } from 'svelte'; import { t } from 'svelte-i18n'; import DownloadAction from '../photos-page/actions/download-action.svelte'; @@ -125,7 +125,7 @@ variant="ghost" aria-label={$t('download')} onclick={() => downloadAlbum(album)} - icon={mdiFolderDownloadOutline} + icon={mdiDownload} /> {/if} {#if sharedLink.showMetadata && $featureFlags.loaded && $featureFlags.map} diff --git a/web/src/lib/components/album-page/albums-list.svelte b/web/src/lib/components/album-page/albums-list.svelte index 668f624af5..a2ac1231ed 100644 --- a/web/src/lib/components/album-page/albums-list.svelte +++ b/web/src/lib/components/album-page/albums-list.svelte @@ -38,7 +38,7 @@ import { normalizeSearchString } from '$lib/utils/string-utils'; import { addUsersToAlbum, deleteAlbum, isHttpError, type AlbumResponseDto, type AlbumUserAddDto } from '@immich/sdk'; import { modalManager } from '@immich/ui'; - import { mdiDeleteOutline, mdiFolderDownloadOutline, mdiRenameOutline, mdiShareVariantOutline } from '@mdi/js'; + import { mdiDeleteOutline, mdiDownload, mdiRenameOutline, mdiShareVariantOutline } from '@mdi/js'; import { groupBy } from 'lodash-es'; import { onMount, type Snippet } from 'svelte'; import { t } from 'svelte-i18n'; @@ -419,7 +419,7 @@ /> openShareModal()} /> {/if} - handleDownloadAlbum()} /> + handleDownloadAlbum()} /> {#if showFullContextMenu} setAlbumToDelete()} /> {/if} diff --git a/web/src/lib/components/asset-viewer/actions/download-action.svelte b/web/src/lib/components/asset-viewer/actions/download-action.svelte index 677550e2da..f790569703 100644 --- a/web/src/lib/components/asset-viewer/actions/download-action.svelte +++ b/web/src/lib/components/asset-viewer/actions/download-action.svelte @@ -6,7 +6,7 @@ import { downloadFile } from '$lib/utils/asset-utils'; import { getAssetInfo } from '@immich/sdk'; import { IconButton } from '@immich/ui'; - import { mdiFolderDownloadOutline } from '@mdi/js'; + import { mdiDownload } from '@mdi/js'; import { t } from 'svelte-i18n'; interface Props { @@ -26,10 +26,10 @@ color="secondary" shape="round" variant="ghost" - icon={mdiFolderDownloadOutline} + icon={mdiDownload} aria-label={$t('download')} onclick={onDownloadFile} /> {:else} - + {/if} diff --git a/web/src/lib/components/photos-page/actions/download-action.svelte b/web/src/lib/components/photos-page/actions/download-action.svelte index 0a1376374c..6f2a7771e9 100644 --- a/web/src/lib/components/photos-page/actions/download-action.svelte +++ b/web/src/lib/components/photos-page/actions/download-action.svelte @@ -5,7 +5,7 @@ import { downloadArchive, downloadFile } from '$lib/utils/asset-utils'; import { getAssetInfo } from '@immich/sdk'; import { IconButton } from '@immich/ui'; - import { mdiCloudDownloadOutline, mdiFileDownloadOutline, mdiFolderDownloadOutline } from '@mdi/js'; + import { mdiDownload } from '@mdi/js'; import { t } from 'svelte-i18n'; import MenuOption from '../../shared-components/context-menu/menu-option.svelte'; import { getAssetControlContext } from '../asset-select-control-bar.svelte'; @@ -31,21 +31,19 @@ clearSelect(); await downloadArchive(filename, { assetIds: assets.map((asset) => asset.id) }); }; - - let menuItemIcon = $derived(getAssets().length === 1 ? mdiFileDownloadOutline : mdiFolderDownloadOutline); {#if menuItem} - + {:else} {/if} diff --git a/web/src/lib/components/share-page/individual-shared-viewer.svelte b/web/src/lib/components/share-page/individual-shared-viewer.svelte index 1c4bf1e4b6..c677922379 100644 --- a/web/src/lib/components/share-page/individual-shared-viewer.svelte +++ b/web/src/lib/components/share-page/individual-shared-viewer.svelte @@ -14,7 +14,7 @@ import { toTimelineAsset } from '$lib/utils/timeline-util'; import { addSharedLinkAssets, getAssetInfo, type SharedLinkResponseDto } from '@immich/sdk'; import { IconButton } from '@immich/ui'; - import { mdiArrowLeft, mdiFileImagePlusOutline, mdiFolderDownloadOutline, mdiSelectAll } from '@mdi/js'; + import { mdiArrowLeft, mdiDownload, mdiFileImagePlusOutline, mdiSelectAll } from '@mdi/js'; import { t } from 'svelte-i18n'; import AssetViewer from '../asset-viewer/asset-viewer.svelte'; import DownloadAction from '../photos-page/actions/download-action.svelte'; @@ -135,7 +135,7 @@ variant="ghost" aria-label={$t('download')} onclick={downloadAssets} - icon={mdiFolderDownloadOutline} + icon={mdiDownload} /> {/if} {/snippet} diff --git a/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte index 0d430d918f..4370d0ec7c 100644 --- a/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -75,7 +75,7 @@ mdiCogOutline, mdiDeleteOutline, mdiDotsVertical, - mdiFolderDownloadOutline, + mdiDownload, mdiImageOutline, mdiImagePlusOutline, mdiLink, @@ -664,7 +664,7 @@ color="secondary" aria-label={$t('download')} onclick={handleDownloadAlbum} - icon={mdiFolderDownloadOutline} + icon={mdiDownload} /> {/if} From f317cbe221016f9c0261c08e3343be061a359b0d Mon Sep 17 00:00:00 2001 From: Min Idzelis Date: Sun, 10 Aug 2025 22:24:00 -0400 Subject: [PATCH 223/748] fix: devcontainer broken by debian Trixie going stable (#20843) --- server/Dockerfile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/server/Dockerfile b/server/Dockerfile index 3d8f3eea74..d2dc203eb3 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -18,7 +18,7 @@ ENTRYPOINT ["tini", "--", "/bin/bash", "-c"] FROM dev AS dev-container-server RUN rm -rf /usr/src/app -RUN apt-get update && \ +RUN apt-get update --allow-releaseinfo-change && \ apt-get install sudo inetutils-ping openjdk-11-jre-headless \ vim nano \ -y --no-install-recommends --fix-missing @@ -69,8 +69,6 @@ RUN sudo apt-get update \ && sudo apt-get update \ && sudo apt-get install dcm -y -COPY --chmod=777 ../.devcontainer/mobile/container-mobile-post-create.sh /immich-devcontainer/container-mobile-post-create.sh - RUN dart --disable-analytics FROM dev AS prod From 03a8b6cb38ab2fe1bb0e129ad73784f843d1cb44 Mon Sep 17 00:00:00 2001 From: Nicholas <30300649+NicholasFlamy@users.noreply.github.com> Date: Sun, 10 Aug 2025 22:26:23 -0400 Subject: [PATCH 224/748] feat: add i18n formatting to `make translation` in mobile makefile (#20807) add i18n formatting to `make translation` in mobile makefile --- mobile/makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/mobile/makefile b/mobile/makefile index 356649d5dd..cfe864a7ee 100644 --- a/mobile/makefile +++ b/mobile/makefile @@ -25,6 +25,7 @@ migration: dart run drift_dev make-migrations translation: + npm --prefix ../web run format:i18n dart run easy_localization:generate -S ../i18n dart run bin/generate_keys.dart dart format lib/generated/codegen_loader.g.dart From e7060dc292d82e64f1c48ae466fbe8186ea98283 Mon Sep 17 00:00:00 2001 From: Gabriel Soldani Date: Sun, 10 Aug 2025 23:31:26 -0300 Subject: [PATCH 225/748] fix(web): fix layout loop with single row grids in explore page (#20833) --- web/src/routes/(user)/explore/+page.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/routes/(user)/explore/+page.svelte b/web/src/routes/(user)/explore/+page.svelte index 49d38d0815..cc69e52597 100644 --- a/web/src/routes/(user)/explore/+page.svelte +++ b/web/src/routes/(user)/explore/+page.svelte @@ -52,7 +52,7 @@ draggable="false">{$t('view_all')}
    - + {#snippet children({ itemCount })} {#each people.slice(0, itemCount) as person (person.id)}
    @@ -86,7 +86,7 @@ draggable="false">{$t('view_all')}
    - + {#snippet children({ itemCount })} {#each places.slice(0, itemCount) as item (item.data.id)} Date: Mon, 11 Aug 2025 15:01:31 -0500 Subject: [PATCH 226/748] feat: edit image in beta timeline (#20709) * feat: edit image in beta timeline * delete album notifier pull * feat: sync local after saving image * feat: queue asset for manual upload after saving * chore: clarify PlatformException catch --- .../pages/editing/drift_crop.page.dart | 174 ++++++++++++++++++ .../pages/editing/drift_edit.page.dart | 165 +++++++++++++++++ .../pages/editing/drift_filter.page.dart | 159 ++++++++++++++++ .../edit_image_action_button.widget.dart | 37 ++++ .../asset_viewer/bottom_bar.widget.dart | 2 + .../repositories/file_media.repository.dart | 15 +- mobile/lib/routing/router.dart | 6 + mobile/lib/routing/router.gr.dart | 154 ++++++++++++++++ 8 files changed, 711 insertions(+), 1 deletion(-) create mode 100644 mobile/lib/presentation/pages/editing/drift_crop.page.dart create mode 100644 mobile/lib/presentation/pages/editing/drift_edit.page.dart create mode 100644 mobile/lib/presentation/pages/editing/drift_filter.page.dart create mode 100644 mobile/lib/presentation/widgets/action_buttons/edit_image_action_button.widget.dart diff --git a/mobile/lib/presentation/pages/editing/drift_crop.page.dart b/mobile/lib/presentation/pages/editing/drift_crop.page.dart new file mode 100644 index 0000000000..5b14292aa2 --- /dev/null +++ b/mobile/lib/presentation/pages/editing/drift_crop.page.dart @@ -0,0 +1,174 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:crop_image/crop_image.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/utils/hooks/crop_controller_hook.dart'; + +/// A widget for cropping an image. +/// This widget uses [HookWidget] to manage its lifecycle and state. It allows +/// users to crop an image and then navigate to the [EditImagePage] with the +/// cropped image. + +@RoutePage() +class DriftCropImagePage extends HookWidget { + final Image image; + final BaseAsset asset; + const DriftCropImagePage({super.key, required this.image, required this.asset}); + + @override + Widget build(BuildContext context) { + final cropController = useCropController(); + final aspectRatio = useState(null); + + return Scaffold( + appBar: AppBar( + backgroundColor: context.scaffoldBackgroundColor, + title: Text("crop".tr()), + leading: CloseButton(color: context.primaryColor), + actions: [ + IconButton( + icon: Icon(Icons.done_rounded, color: context.primaryColor, size: 24), + onPressed: () async { + final croppedImage = await cropController.croppedImage(); + context.pushRoute(DriftEditImageRoute(asset: asset, image: croppedImage, isEdited: true)); + }, + ), + ], + ), + backgroundColor: context.scaffoldBackgroundColor, + body: SafeArea( + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Column( + children: [ + Container( + padding: const EdgeInsets.only(top: 20), + width: constraints.maxWidth * 0.9, + height: constraints.maxHeight * 0.6, + child: CropImage(controller: cropController, image: image, gridColor: Colors.white), + ), + Expanded( + child: Container( + width: double.infinity, + decoration: BoxDecoration( + color: context.scaffoldBackgroundColor, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + ), + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.only(left: 20, right: 20, bottom: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + IconButton( + icon: Icon(Icons.rotate_left, color: context.themeData.iconTheme.color), + onPressed: () { + cropController.rotateLeft(); + }, + ), + IconButton( + icon: Icon(Icons.rotate_right, color: context.themeData.iconTheme.color), + onPressed: () { + cropController.rotateRight(); + }, + ), + ], + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _AspectRatioButton( + cropController: cropController, + aspectRatio: aspectRatio, + ratio: null, + label: 'Free', + ), + _AspectRatioButton( + cropController: cropController, + aspectRatio: aspectRatio, + ratio: 1.0, + label: '1:1', + ), + _AspectRatioButton( + cropController: cropController, + aspectRatio: aspectRatio, + ratio: 16.0 / 9.0, + label: '16:9', + ), + _AspectRatioButton( + cropController: cropController, + aspectRatio: aspectRatio, + ratio: 3.0 / 2.0, + label: '3:2', + ), + _AspectRatioButton( + cropController: cropController, + aspectRatio: aspectRatio, + ratio: 7.0 / 5.0, + label: '7:5', + ), + ], + ), + ], + ), + ), + ), + ), + ], + ); + }, + ), + ), + ); + } +} + +class _AspectRatioButton extends StatelessWidget { + final CropController cropController; + final ValueNotifier aspectRatio; + final double? ratio; + final String label; + + const _AspectRatioButton({ + required this.cropController, + required this.aspectRatio, + required this.ratio, + required this.label, + }); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: Icon(switch (label) { + 'Free' => Icons.crop_free_rounded, + '1:1' => Icons.crop_square_rounded, + '16:9' => Icons.crop_16_9_rounded, + '3:2' => Icons.crop_3_2_rounded, + '7:5' => Icons.crop_7_5_rounded, + _ => Icons.crop_free_rounded, + }, color: aspectRatio.value == ratio ? context.primaryColor : context.themeData.iconTheme.color), + onPressed: () { + cropController.crop = const Rect.fromLTRB(0.1, 0.1, 0.9, 0.9); + aspectRatio.value = ratio; + cropController.aspectRatio = ratio; + }, + ), + Text(label, style: context.textTheme.displayMedium), + ], + ); + } +} diff --git a/mobile/lib/presentation/pages/editing/drift_edit.page.dart b/mobile/lib/presentation/pages/editing/drift_edit.page.dart new file mode 100644 index 0000000000..da62d49b49 --- /dev/null +++ b/mobile/lib/presentation/pages/editing/drift_edit.page.dart @@ -0,0 +1,165 @@ +import 'dart:async'; +import 'dart:ui'; + +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/entities/asset.entity.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/providers/background_sync.provider.dart'; +import 'package:immich_mobile/repositories/file_media.repository.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/services/upload.service.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as p; + +/// A stateless widget that provides functionality for editing an image. +/// +/// This widget allows users to edit an image provided either as an [Asset] or +/// directly as an [Image]. It ensures that exactly one of these is provided. +/// +/// It also includes a conversion method to convert an [Image] to a [Uint8List] to save the image on the user's phone +/// They automatically navigate to the [HomePage] with the edited image saved and they eventually get backed up to the server. +@immutable +@RoutePage() +class DriftEditImagePage extends ConsumerWidget { + final BaseAsset asset; + final Image image; + final bool isEdited; + + const DriftEditImagePage({super.key, required this.asset, required this.image, required this.isEdited}); + Future _imageToUint8List(Image image) async { + final Completer completer = Completer(); + image.image + .resolve(const ImageConfiguration()) + .addListener( + ImageStreamListener((ImageInfo info, bool _) { + info.image.toByteData(format: ImageByteFormat.png).then((byteData) { + if (byteData != null) { + completer.complete(byteData.buffer.asUint8List()); + } else { + completer.completeError('Failed to convert image to bytes'); + } + }); + }, onError: (exception, stackTrace) => completer.completeError(exception)), + ); + return completer.future; + } + + Future _saveEditedImage(BuildContext context, BaseAsset asset, Image image, WidgetRef ref) async { + try { + final Uint8List imageData = await _imageToUint8List(image); + LocalAsset? localAsset; + + try { + localAsset = await ref + .read(fileMediaRepositoryProvider) + .saveLocalAsset(imageData, title: "${p.withoutExtension(asset.name)}_edited.jpg"); + } on PlatformException catch (e) { + // OS might not return the saved image back, so we handle that gracefully + // This can happen if app does not have full library access + Logger("SaveEditedImage").warning("Failed to retrieve the saved image back from OS", e); + } + + ref.read(backgroundSyncProvider).syncLocal(full: true); + context.navigator.popUntil((route) => route.isFirst); + ImmichToast.show(durationInSecond: 3, context: context, msg: 'Image Saved!'); + + if (localAsset == null) { + return; + } + + await ref.read(uploadServiceProvider).manualBackup([localAsset]); + } catch (e) { + ImmichToast.show( + durationInSecond: 6, + context: context, + msg: "error_saving_image".tr(namedArgs: {'error': e.toString()}), + ); + } + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Scaffold( + appBar: AppBar( + title: Text("edit".tr()), + backgroundColor: context.scaffoldBackgroundColor, + leading: IconButton( + icon: Icon(Icons.close_rounded, color: context.primaryColor, size: 24), + onPressed: () => context.navigator.popUntil((route) => route.isFirst), + ), + actions: [ + TextButton( + onPressed: isEdited ? () => _saveEditedImage(context, asset, image, ref) : null, + child: Text("save_to_gallery".tr(), style: TextStyle(color: isEdited ? context.primaryColor : Colors.grey)), + ), + ], + ), + backgroundColor: context.scaffoldBackgroundColor, + body: Center( + child: ConstrainedBox( + constraints: BoxConstraints(maxHeight: context.height * 0.7, maxWidth: context.width * 0.9), + child: Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(7)), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.2), + spreadRadius: 2, + blurRadius: 10, + offset: const Offset(0, 3), + ), + ], + ), + child: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(7)), + child: Image(image: image.image, fit: BoxFit.contain), + ), + ), + ), + ), + bottomNavigationBar: Container( + height: 70, + margin: const EdgeInsets.only(bottom: 60, right: 10, left: 10, top: 10), + decoration: BoxDecoration( + color: context.scaffoldBackgroundColor, + borderRadius: const BorderRadius.all(Radius.circular(30)), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IconButton( + icon: Icon(Icons.crop_rotate_rounded, color: context.themeData.iconTheme.color, size: 25), + onPressed: () { + context.pushRoute(DriftCropImageRoute(asset: asset, image: image)); + }, + ), + Text("crop".tr(), style: context.textTheme.displayMedium), + ], + ), + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IconButton( + icon: Icon(Icons.filter, color: context.themeData.iconTheme.color, size: 25), + onPressed: () { + context.pushRoute(DriftFilterImageRoute(asset: asset, image: image)); + }, + ), + Text("filter".tr(), style: context.textTheme.displayMedium), + ], + ), + ], + ), + ), + ); + } +} diff --git a/mobile/lib/presentation/pages/editing/drift_filter.page.dart b/mobile/lib/presentation/pages/editing/drift_filter.page.dart new file mode 100644 index 0000000000..75c3f81de2 --- /dev/null +++ b/mobile/lib/presentation/pages/editing/drift_filter.page.dart @@ -0,0 +1,159 @@ +import 'dart:async'; +import 'dart:ui' as ui; + +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:immich_mobile/constants/filters.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/routing/router.dart'; + +/// A widget for filtering an image. +/// This widget uses [HookWidget] to manage its lifecycle and state. It allows +/// users to add filters to an image and then navigate to the [EditImagePage] with the +/// final composition.' +@RoutePage() +class DriftFilterImagePage extends HookWidget { + final Image image; + final BaseAsset asset; + + const DriftFilterImagePage({super.key, required this.image, required this.asset}); + + @override + Widget build(BuildContext context) { + final colorFilter = useState(filters[0]); + final selectedFilterIndex = useState(0); + + Future createFilteredImage(ui.Image inputImage, ColorFilter filter) { + final completer = Completer(); + final size = Size(inputImage.width.toDouble(), inputImage.height.toDouble()); + final recorder = ui.PictureRecorder(); + final canvas = Canvas(recorder); + + final paint = Paint()..colorFilter = filter; + canvas.drawImage(inputImage, Offset.zero, paint); + + recorder.endRecording().toImage(size.width.round(), size.height.round()).then((image) { + completer.complete(image); + }); + + return completer.future; + } + + void applyFilter(ColorFilter filter, int index) { + colorFilter.value = filter; + selectedFilterIndex.value = index; + } + + Future applyFilterAndConvert(ColorFilter filter) async { + final completer = Completer(); + image.image + .resolve(ImageConfiguration.empty) + .addListener( + ImageStreamListener((ImageInfo info, bool _) { + completer.complete(info.image); + }), + ); + final uiImage = await completer.future; + + final filteredUiImage = await createFilteredImage(uiImage, filter); + final byteData = await filteredUiImage.toByteData(format: ui.ImageByteFormat.png); + final pngBytes = byteData!.buffer.asUint8List(); + + return Image.memory(pngBytes, fit: BoxFit.contain); + } + + return Scaffold( + appBar: AppBar( + backgroundColor: context.scaffoldBackgroundColor, + title: Text("filter".tr()), + leading: CloseButton(color: context.primaryColor), + actions: [ + IconButton( + icon: Icon(Icons.done_rounded, color: context.primaryColor, size: 24), + onPressed: () async { + final filteredImage = await applyFilterAndConvert(colorFilter.value); + context.pushRoute(DriftEditImageRoute(asset: asset, image: filteredImage, isEdited: true)); + }, + ), + ], + ), + backgroundColor: context.scaffoldBackgroundColor, + body: Column( + children: [ + SizedBox( + height: context.height * 0.7, + child: Center( + child: ColorFiltered(colorFilter: colorFilter.value, child: image), + ), + ), + SizedBox( + height: 120, + child: ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: filters.length, + itemBuilder: (context, index) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: _FilterButton( + image: image, + label: filterNames[index], + filter: filters[index], + isSelected: selectedFilterIndex.value == index, + onTap: () => applyFilter(filters[index], index), + ), + ); + }, + ), + ), + ], + ), + ); + } +} + +class _FilterButton extends StatelessWidget { + final Image image; + final String label; + final ColorFilter filter; + final bool isSelected; + final VoidCallback onTap; + + const _FilterButton({ + required this.image, + required this.label, + required this.filter, + required this.isSelected, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + GestureDetector( + onTap: onTap, + child: Container( + width: 80, + height: 80, + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(10)), + border: isSelected ? Border.all(color: context.primaryColor, width: 3) : null, + ), + child: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(10)), + child: ColorFiltered( + colorFilter: filter, + child: FittedBox(fit: BoxFit.cover, child: image), + ), + ), + ), + ), + const SizedBox(height: 10), + Text(label, style: context.themeData.textTheme.bodyMedium), + ], + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/edit_image_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/edit_image_action_button.widget.dart new file mode 100644 index 0000000000..cde0db8e18 --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/edit_image_action_button.widget.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/pages/editing/drift_edit.page.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/images/image_provider.dart'; +import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; + +class EditImageActionButton extends ConsumerWidget { + const EditImageActionButton({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final currentAsset = ref.watch(currentAssetNotifier); + + onPress() { + if (currentAsset == null) { + return; + } + + final image = Image(image: getFullImageProvider(currentAsset)); + + context.navigator.push( + MaterialPageRoute( + builder: (context) => DriftEditImagePage(asset: currentAsset, image: image, isEdited: false), + ), + ); + } + + return BaseActionButton( + iconData: Icons.tune, + label: "edit".t(context: context), + onPressed: onPress, + ); + } +} diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart index b220d4e6a5..bb7b06113c 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart @@ -6,6 +6,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/edit_image_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; @@ -38,6 +39,7 @@ class ViewerBottomBar extends ConsumerWidget { final actions = [ const ShareActionButton(source: ActionSource.viewer), if (asset.isLocalOnly) const UploadActionButton(source: ActionSource.viewer), + if (asset.type == AssetType.image) const EditImageActionButton(), if (asset.hasRemote && isOwner) const ArchiveActionButton(source: ActionSource.viewer), asset.isLocalOnly ? const DeleteLocalActionButton(source: ActionSource.viewer) diff --git a/mobile/lib/repositories/file_media.repository.dart b/mobile/lib/repositories/file_media.repository.dart index 6d429e4777..654be78fb4 100644 --- a/mobile/lib/repositories/file_media.repository.dart +++ b/mobile/lib/repositories/file_media.repository.dart @@ -2,7 +2,8 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/entities/asset.entity.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/entities/asset.entity.dart' hide AssetType; import 'package:immich_mobile/repositories/asset_media.repository.dart'; import 'package:photo_manager/photo_manager.dart' hide AssetType; @@ -15,6 +16,18 @@ class FileMediaRepository { return AssetMediaRepository.toAsset(entity); } + Future saveLocalAsset(Uint8List data, {required String title, String? relativePath}) async { + final entity = await PhotoManager.editor.saveImage(data, filename: title, title: title, relativePath: relativePath); + + return LocalAsset( + id: entity.id, + name: title, + type: AssetType.image, + createdAt: entity.createDateTime, + updatedAt: entity.modifiedDateTime, + ); + } + Future saveImageWithFile(String filePath, {String? title, String? relativePath}) async { final entity = await PhotoManager.editor.saveImageWithPath(filePath, title: title, relativePath: relativePath); return AssetMediaRepository.toAsset(entity); diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index da24617824..5f8e4fe53c 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -101,6 +101,9 @@ import 'package:immich_mobile/presentation/pages/drift_remote_album.page.dart'; import 'package:immich_mobile/presentation/pages/drift_trash.page.dart'; import 'package:immich_mobile/presentation/pages/drift_user_selection.page.dart'; import 'package:immich_mobile/presentation/pages/drift_video.page.dart'; +import 'package:immich_mobile/presentation/pages/editing/drift_crop.page.dart'; +import 'package:immich_mobile/presentation/pages/editing/drift_edit.page.dart'; +import 'package:immich_mobile/presentation/pages/editing/drift_filter.page.dart'; import 'package:immich_mobile/presentation/pages/local_timeline.page.dart'; import 'package:immich_mobile/presentation/pages/search/drift_search.page.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.page.dart'; @@ -333,6 +336,9 @@ class AppRouter extends RootStackRouter { AutoRoute(page: DriftBackupOptionsRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: DriftAlbumOptionsRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: DriftMapRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftEditImageRoute.page), + AutoRoute(page: DriftCropImageRoute.page), + AutoRoute(page: DriftFilterImageRoute.page), // required to handle all deeplinks in deep_link.service.dart // auto_route_library#1722 RedirectRoute(path: '*', redirectTo: '/'), diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index 8c5064e752..1abe49b14f 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -828,6 +828,112 @@ class DriftCreateAlbumRoute extends PageRouteInfo { ); } +/// generated route for +/// [DriftCropImagePage] +class DriftCropImageRoute extends PageRouteInfo { + DriftCropImageRoute({ + Key? key, + required Image image, + required BaseAsset asset, + List? children, + }) : super( + DriftCropImageRoute.name, + args: DriftCropImageRouteArgs(key: key, image: image, asset: asset), + initialChildren: children, + ); + + static const String name = 'DriftCropImageRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + final args = data.argsAs(); + return DriftCropImagePage( + key: args.key, + image: args.image, + asset: args.asset, + ); + }, + ); +} + +class DriftCropImageRouteArgs { + const DriftCropImageRouteArgs({ + this.key, + required this.image, + required this.asset, + }); + + final Key? key; + + final Image image; + + final BaseAsset asset; + + @override + String toString() { + return 'DriftCropImageRouteArgs{key: $key, image: $image, asset: $asset}'; + } +} + +/// generated route for +/// [DriftEditImagePage] +class DriftEditImageRoute extends PageRouteInfo { + DriftEditImageRoute({ + Key? key, + required BaseAsset asset, + required Image image, + required bool isEdited, + List? children, + }) : super( + DriftEditImageRoute.name, + args: DriftEditImageRouteArgs( + key: key, + asset: asset, + image: image, + isEdited: isEdited, + ), + initialChildren: children, + ); + + static const String name = 'DriftEditImageRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + final args = data.argsAs(); + return DriftEditImagePage( + key: args.key, + asset: args.asset, + image: args.image, + isEdited: args.isEdited, + ); + }, + ); +} + +class DriftEditImageRouteArgs { + const DriftEditImageRouteArgs({ + this.key, + required this.asset, + required this.image, + required this.isEdited, + }); + + final Key? key; + + final BaseAsset asset; + + final Image image; + + final bool isEdited; + + @override + String toString() { + return 'DriftEditImageRouteArgs{key: $key, asset: $asset, image: $image, isEdited: $isEdited}'; + } +} + /// generated route for /// [DriftFavoritePage] class DriftFavoriteRoute extends PageRouteInfo { @@ -844,6 +950,54 @@ class DriftFavoriteRoute extends PageRouteInfo { ); } +/// generated route for +/// [DriftFilterImagePage] +class DriftFilterImageRoute extends PageRouteInfo { + DriftFilterImageRoute({ + Key? key, + required Image image, + required BaseAsset asset, + List? children, + }) : super( + DriftFilterImageRoute.name, + args: DriftFilterImageRouteArgs(key: key, image: image, asset: asset), + initialChildren: children, + ); + + static const String name = 'DriftFilterImageRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + final args = data.argsAs(); + return DriftFilterImagePage( + key: args.key, + image: args.image, + asset: args.asset, + ); + }, + ); +} + +class DriftFilterImageRouteArgs { + const DriftFilterImageRouteArgs({ + this.key, + required this.image, + required this.asset, + }); + + final Key? key; + + final Image image; + + final BaseAsset asset; + + @override + String toString() { + return 'DriftFilterImageRouteArgs{key: $key, image: $image, asset: $asset}'; + } +} + /// generated route for /// [DriftLibraryPage] class DriftLibraryRoute extends PageRouteInfo { From e29cc66361dd66c6c152e469ecfb4d2c5ba07ddd Mon Sep 17 00:00:00 2001 From: Mert <101130780+mertalev@users.noreply.github.com> Date: Mon, 11 Aug 2025 16:50:48 -0400 Subject: [PATCH 227/748] docs: vectorchord migration instructions, deprecation log on startup (#20867) * deprecation log, migration docs * update tests * fix info boxes --- docs/docs/install/upgrading.md | 99 ++++++++++++++++++++ server/src/services/database.service.spec.ts | 30 +++++- server/src/services/database.service.ts | 6 ++ 3 files changed, 131 insertions(+), 4 deletions(-) diff --git a/docs/docs/install/upgrading.md b/docs/docs/install/upgrading.md index 4425e23d68..d638a6f7d1 100644 --- a/docs/docs/install/upgrading.md +++ b/docs/docs/install/upgrading.md @@ -27,3 +27,102 @@ docker image prune [watchtower]: https://containrrr.dev/watchtower/ [breaking]: https://github.com/immich-app/immich/discussions?discussions_q=label%3Achangelog%3Abreaking-change+sort%3Adate_created [releases]: https://github.com/immich-app/immich/releases + +## Migrating to VectorChord + +:::info +If you deploy Immich using Docker Compose, see `ghcr.io/immich-app/postgres` in the `docker-compose.yml` file and have not explicitly set the `DB_VECTOR_EXTENSION` environmental variable, your Immich database is already using VectorChord and this section does not apply to you. +::: + +:::important +If you do not deploy Immich using Docker Compose and see a deprecation warning for pgvecto.rs on server startup, you should refer to the maintainers of the Immich distribution for guidance (if using a turnkey solution) or adapt the instructions for your specific setup. +::: + +Immich has migrated off of the deprecated pgvecto.rs database extension to its successor, [VectorChord](https://github.com/tensorchord/VectorChord), which comes with performance improvements in almost every aspect. This section will guide you on how to make this change in a Docker Compose setup. + +Before making any changes, please [back up your database](/docs/administration/backup-and-restore). While every effort has been made to make this migration as smooth as possible, there’s always a chance that something can go wrong. + +After making a backup, please modify your `docker-compose.yml` file with the following information. + +```diff + [...] + + database: + container_name: immich_postgres +- image: docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52 ++ image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0 + environment: + POSTGRES_PASSWORD: ${DB_PASSWORD} + POSTGRES_USER: ${DB_USERNAME} + POSTGRES_DB: ${DB_DATABASE_NAME} + POSTGRES_INITDB_ARGS: '--data-checksums' ++ # Uncomment the DB_STORAGE_TYPE: 'HDD' var if your database isn't stored on SSDs ++ # DB_STORAGE_TYPE: 'HDD' + volumes: + # Do not edit the next line. If you want to change the database storage location on your system, edit the value of DB_DATA_LOCATION in the .env file + - ${DB_DATA_LOCATION}:/var/lib/postgresql/data +- healthcheck: +- test: >- +- pg_isready --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" || exit 1; +- Chksum="$$(psql --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" --tuples-only --no-align +- --command='SELECT COALESCE(SUM(checksum_failures), 0) FROM pg_stat_database')"; +- echo "checksum failure count is $$Chksum"; +- [ "$$Chksum" = '0' ] || exit 1 +- interval: 5m +- start_interval: 30s +- start_period: 5m +- command: >- +- postgres +- -c shared_preload_libraries=vectors.so +- -c 'search_path="$$user", public, vectors' +- -c logging_collector=on +- -c max_wal_size=2GB +- -c shared_buffers=512MB +- -c wal_compression=on ++ shm_size: 128mb + restart: always + + [...] +``` + +:::important +If you deviated from the defaults of pg14 or pgvectors0.2.0, you must adjust the pg major version and pgvecto.rs version. If you are still using the default `docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0` image, you can just follow the changes above. For example, if the previous image is `docker.io/tensorchord/pgvecto-rs:pg16-v0.3.0`, the new image should be `ghcr.io/immich-app/postgres:16-vectorchord0.3.0-pgvectors0.3.0` instead of the image specified in the diff. +::: + +After making these changes, you can start Immich as normal. Immich will make some changes to the DB during startup, which can take seconds to minutes to finish, depending on hardware and library size. In particular, it’s normal for the server logs to be seemingly stuck at `Reindexing clip_index` and `Reindexing face_index`for some time if you have over 100k assets in Immich and/or Immich is on a relatively weak server. If you see these logs and there are no errors, just give it time. + +:::danger +After switching to VectorChord, you should not downgrade Immich below 1.133.0. +::: + +Please don’t hesitate to contact us on [GitHub](https://github.com/immich-app/immich/discussions) or [Discord](https://discord.immich.app/) if you encounter migration issues. + +### VectorChord FAQ + +#### I have a separate PostgreSQL instance shared with multiple services. How can I switch to VectorChord? + +Please see the [standalone PostgreSQL documentation](/docs/administration/postgres-standalone#migrating-to-vectorchord) for migration instructions. The migration path will be different depending on whether you’re currently using pgvecto.rs or pgvector, as well as whether Immich has superuser DB permissions. + +#### Why are so many lines removed from the `docker-compose.yml` file? Does this mean the health check is removed? + +These lines are now incorporated into the image itself along with some additional tuning. + +#### What does this change mean for my existing DB backups? + +The new DB image includes pgvector and pgvecto.rs in addition to VectorChord, so you can use this image to restore from existing backups that used either of these extensions. However, backups made after switching to VectorChord require an image containing VectorChord to restore successfully. + +#### Do I still need pgvecto.rs installed after migrating to VectorChord? + +pgvecto.rs only needs to be available during the migration, or if you need to restore from a backup that used pgvecto.rs. For a leaner DB and a smaller image, you can optionally switch to an image variant that doesn’t have pgvecto.rs installed after you’ve performed the migration and started Immich: `ghcr.io/immich-app/postgres:14-vectorchord0.4.3`, changing the PostgreSQL version as appropriate. + +#### Why does it matter whether my database is on an SSD or an HDD? + +These storage mediums have different performance characteristics. As a result, the optimal settings for an SSD are not the same as those for an HDD. Either configuration is compatible with SSD and HDD, but using the right configuration will make Immich snappier. As a general tip, we recommend users store the database on an SSD whenever possible. + +#### Can I use the new database image as a general PostgreSQL image outside of Immich? + +It’s a standard PostgreSQL container image that additionally contains the VectorChord, pgvector, and (optionally) pgvecto.rs extensions. If you were using the previous pgvecto.rs image for other purposes, you can similarly do so with this image. + +#### If pgvecto.rs and pgvector still work, why should I switch to VectorChord? + +VectorChord is faster, more stable, uses less RAM, and (with the settings Immich uses) offers higher-quality results than pgvector and pgvecto.rs. This translates to better search and facial recognition experiences. In addition, pgvecto.rs support will be dropped in the future, so changing it sooner will avoid disruption. diff --git a/server/src/services/database.service.spec.ts b/server/src/services/database.service.spec.ts index b4022ee864..e30722d3d7 100644 --- a/server/src/services/database.service.spec.ts +++ b/server/src/services/database.service.spec.ts @@ -261,8 +261,12 @@ describe(DatabaseService.name, () => { await expect(sut.onBootstrap()).rejects.toThrow('Failed to update extension'); - expect(mocks.logger.warn.mock.calls[0][0]).toContain( - `The ${extensionName} extension can be updated to ${updateInRange}.`, + expect(mocks.logger.warn.mock.calls).toEqual( + expect.arrayContaining([ + expect.arrayContaining([ + expect.stringContaining(`The ${extensionName} extension can be updated to ${updateInRange}.`), + ]), + ]), ); expect(mocks.logger.fatal).not.toHaveBeenCalled(); expect(mocks.database.updateVectorExtension).toHaveBeenCalledWith(extension, updateInRange); @@ -281,8 +285,10 @@ describe(DatabaseService.name, () => { await expect(sut.onBootstrap()).resolves.toBeUndefined(); - expect(mocks.logger.warn).toHaveBeenCalledTimes(1); - expect(mocks.logger.warn.mock.calls[0][0]).toContain(extensionName); + expect(mocks.logger.warn.mock.calls).toEqual( + expect.arrayContaining([expect.arrayContaining([expect.stringContaining(extensionName)])]), + ); + expect(mocks.database.updateVectorExtension).toHaveBeenCalledWith(extension, updateInRange); expect(mocks.database.runMigrations).toHaveBeenCalledTimes(1); expect(mocks.logger.fatal).not.toHaveBeenCalled(); @@ -415,5 +421,21 @@ describe(DatabaseService.name, () => { expect(mocks.database.dropExtension).not.toHaveBeenCalled(); }); + + it(`should warn if using pgvecto.rs`, async () => { + mocks.database.getExtensionVersions.mockResolvedValue([ + { + name: DatabaseExtension.Vectors, + installedVersion: minVersionInRange, + availableVersion: minVersionInRange, + }, + ]); + mocks.database.getVectorExtension.mockResolvedValue(DatabaseExtension.Vectors); + + await expect(sut.onBootstrap()).resolves.toBeUndefined(); + + expect(mocks.logger.warn).toHaveBeenCalledTimes(1); + expect(mocks.logger.warn.mock.calls[0][0]).toContain('DEPRECATION WARNING'); + }); }); }); diff --git a/server/src/services/database.service.ts b/server/src/services/database.service.ts index e54be28fc2..758198a197 100644 --- a/server/src/services/database.service.ts +++ b/server/src/services/database.service.ts @@ -53,6 +53,9 @@ const messages = { `The database currently has ${name} ${installedVersion} activated, but the Postgres instance only has ${availableVersion} available. This most likely means the extension was downgraded. If ${name} ${installedVersion} is compatible with Immich, please ensure the Postgres instance has this available.`, + deprecatedExtension: (name: string) => + `DEPRECATION WARNING: The ${name} extension is deprecated and support for it will be removed very soon. + See https://immich.app/docs/install/upgrading#migrating-to-vectorchord in order to switch to the VectorChord extension instead.`, }; @Injectable() @@ -71,6 +74,9 @@ export class DatabaseService extends BaseService { await this.databaseRepository.withLock(DatabaseLock.Migrations, async () => { const extension = await this.databaseRepository.getVectorExtension(); const name = EXTENSION_NAMES[extension]; + if (extension === DatabaseExtension.Vectors) { + this.logger.warn(messages.deprecatedExtension(name)); + } const extensionRange = this.databaseRepository.getExtensionVersionRange(extension); const extensionVersions = await this.databaseRepository.getExtensionVersions(VECTOR_EXTENSIONS); From f09bed9ad2b8883564a66ba2ee049e8ad4ba0169 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 11 Aug 2025 16:42:16 -0500 Subject: [PATCH 228/748] fix: age info cut off (#20872) --- .../asset_viewer/bottom_sheet/sheet_people_details.widget.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart index 5adaa7cc72..fee34bca1b 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart @@ -61,7 +61,7 @@ class _SheetPeopleDetailsState extends ConsumerState { ), ), SizedBox( - height: 150, + height: 160, child: ListView( padding: const EdgeInsets.only(left: 16.0), scrollDirection: Axis.horizontal, From 24db881c1449eb7a6e8a6903264f9b9db5dc4ff9 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 11 Aug 2025 16:49:53 -0500 Subject: [PATCH 229/748] feat: swipe to delete album (#20765) --- .../widgets/album/album_selector.widget.dart | 99 ++++++++++++------- 1 file changed, 65 insertions(+), 34 deletions(-) diff --git a/mobile/lib/presentation/widgets/album/album_selector.widget.dart b/mobile/lib/presentation/widgets/album/album_selector.widget.dart index a97ca736d1..53871b48ae 100644 --- a/mobile/lib/presentation/widgets/album/album_selector.widget.dart +++ b/mobile/lib/presentation/widgets/album/album_selector.widget.dart @@ -19,6 +19,7 @@ import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/utils/remote_album.utils.dart'; +import 'package:immich_mobile/widgets/common/confirm_dialog.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:immich_mobile/widgets/common/search_field.dart'; import 'package:sliver_tools/sliver_tools.dart'; @@ -423,42 +424,72 @@ class _AlbumList extends ConsumerWidget { sliver: SliverList.builder( itemBuilder: (_, index) { final album = albums[index]; - - return Padding( - padding: const EdgeInsets.only(bottom: 8.0), - child: LargeLeadingTile( - title: Text( - album.name, - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), - ), - subtitle: Text( - '${'items_count'.t(context: context, args: {'count': album.assetCount})} • ${album.ownerId != userId ? 'shared_by_user'.t(context: context, args: {'user': album.ownerName}) : 'owned'.t(context: context)}', - overflow: TextOverflow.ellipsis, - style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), - ), - onTap: () => onAlbumSelected(album), - leadingPadding: const EdgeInsets.only(right: 16), - leading: album.thumbnailAssetId != null - ? ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(15)), - child: SizedBox(width: 80, height: 80, child: Thumbnail(remoteId: album.thumbnailAssetId)), - ) - : SizedBox( - width: 80, - height: 80, - child: Container( - decoration: BoxDecoration( - color: context.colorScheme.surfaceContainer, - borderRadius: const BorderRadius.all(Radius.circular(16)), - border: Border.all(color: context.colorScheme.outline.withAlpha(50), width: 1), - ), - child: const Icon(Icons.photo_album_rounded, size: 24, color: Colors.grey), - ), - ), + final albumTile = LargeLeadingTile( + title: Text( + album.name, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), ), + subtitle: Text( + '${'items_count'.t(context: context, args: {'count': album.assetCount})} • ${album.ownerId != userId ? 'shared_by_user'.t(context: context, args: {'user': album.ownerName}) : 'owned'.t(context: context)}', + overflow: TextOverflow.ellipsis, + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), + ), + onTap: () => onAlbumSelected(album), + leadingPadding: const EdgeInsets.only(right: 16), + leading: album.thumbnailAssetId != null + ? ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(15)), + child: SizedBox(width: 80, height: 80, child: Thumbnail(remoteId: album.thumbnailAssetId)), + ) + : SizedBox( + width: 80, + height: 80, + child: Container( + decoration: BoxDecoration( + color: context.colorScheme.surfaceContainer, + borderRadius: const BorderRadius.all(Radius.circular(16)), + border: Border.all(color: context.colorScheme.outline.withAlpha(50), width: 1), + ), + child: const Icon(Icons.photo_album_rounded, size: 24, color: Colors.grey), + ), + ), ); + final isOwner = album.ownerId == userId; + + if (isOwner) { + return Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: Dismissible( + key: ValueKey(album.id), + background: Container( + color: context.colorScheme.error, + alignment: Alignment.centerRight, + padding: const EdgeInsets.only(right: 16), + child: Icon(Icons.delete, color: context.colorScheme.onError), + ), + direction: DismissDirection.endToStart, + confirmDismiss: (direction) { + return showDialog( + context: context, + builder: (context) => ConfirmDialog( + onOk: () => true, + title: "delete_album".t(context: context), + content: "album_delete_confirmation".t(context: context, args: {'album': album.name}), + ok: "delete".t(context: context), + ), + ); + }, + onDismissed: (direction) async { + await ref.read(remoteAlbumProvider.notifier).deleteAlbum(album.id); + }, + child: albumTile, + ), + ); + } else { + return Padding(padding: const EdgeInsets.only(bottom: 8.0), child: albumTile); + } }, itemCount: albums.length, ), From 5d2777a5c622da2ef31be303d881374c4816dfea Mon Sep 17 00:00:00 2001 From: Mirek Date: Mon, 11 Aug 2025 23:50:34 +0200 Subject: [PATCH 230/748] feat: format date and time in /admin/users/ -> Profile section (#20811) Matches the format used in the user settings page. Added a formatting function in utils. --- .../user-settings-page/app-settings.svelte | 18 +-------- web/src/lib/utils.ts | 38 +++++++++++++++++++ web/src/routes/admin/users/[id]/+page.svelte | 11 +++++- 3 files changed, 49 insertions(+), 18 deletions(-) diff --git a/web/src/lib/components/user-settings-page/app-settings.svelte b/web/src/lib/components/user-settings-page/app-settings.svelte index d248038a24..ffb0263c94 100644 --- a/web/src/lib/components/user-settings-page/app-settings.svelte +++ b/web/src/lib/components/user-settings-page/app-settings.svelte @@ -12,7 +12,7 @@ playVideoThumbnailOnHover, showDeleteModal, } from '$lib/stores/preferences.store'; - import { findLocale } from '$lib/utils'; + import { createDateFormatter, findLocale } from '$lib/utils'; import { onMount } from 'svelte'; import { t } from 'svelte-i18n'; import { fade } from 'svelte/transition'; @@ -48,21 +48,7 @@ } }; let editedLocale = $derived(findLocale($locale).code); - let formattedDate = $derived( - time.toLocaleString(editedLocale, { - year: 'numeric', - month: '2-digit', - day: '2-digit', - }), - ); - let timePortion = $derived( - time.toLocaleString(editedLocale, { - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - }), - ); - let selectedDate = $derived(`${formattedDate} ${timePortion}`); + let selectedDate: string = $derived(createDateFormatter(editedLocale).formatDateTime(time)); let selectedOption = $derived({ value: findLocale(editedLocale).code || fallbackLocale.code, label: findLocale(editedLocale).name || fallbackLocale.name, diff --git a/web/src/lib/utils.ts b/web/src/lib/utils.ts index 46e6720799..8ced866f6d 100644 --- a/web/src/lib/utils.ts +++ b/web/src/lib/utils.ts @@ -36,6 +36,12 @@ interface DownloadRequestOptions { onDownloadProgress?: (event: ProgressEvent) => void; } +interface DateFormatter { + formatDate: (date: Date) => string; + formatTime: (date: Date) => string; + formatDateTime: (date: Date) => string; +} + export const initLanguage = async () => { const preferenceLang = get(lang); for (const { code, loader } of langs) { @@ -343,3 +349,35 @@ export const withError = async (fn: () => Promise): Promise<[undefined, T] // eslint-disable-next-line unicorn/prefer-code-point export const decodeBase64 = (data: string) => Uint8Array.from(atob(data), (c) => c.charCodeAt(0)); + +export function createDateFormatter(localeCode: string | undefined): DateFormatter { + return { + formatDate: (date: Date): string => + date.toLocaleString(localeCode, { + year: 'numeric', + month: '2-digit', + day: '2-digit', + }), + + formatTime: (date: Date): string => + date.toLocaleString(localeCode, { + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + }), + + formatDateTime: (date: Date): string => { + const formattedDate = date.toLocaleString(localeCode, { + year: 'numeric', + month: '2-digit', + day: '2-digit', + }); + const formattedTime = date.toLocaleString(localeCode, { + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + }); + return `${formattedDate} ${formattedTime}`; + }, + }; +} diff --git a/web/src/routes/admin/users/[id]/+page.svelte b/web/src/routes/admin/users/[id]/+page.svelte index 960b006d4b..27e0b6cfd2 100644 --- a/web/src/routes/admin/users/[id]/+page.svelte +++ b/web/src/routes/admin/users/[id]/+page.svelte @@ -13,6 +13,7 @@ import UserRestoreConfirmModal from '$lib/modals/UserRestoreConfirmModal.svelte'; import { locale } from '$lib/stores/preferences.store'; import { user as authUser } from '$lib/stores/user.store'; + import { createDateFormatter, findLocale } from '$lib/utils'; import { getBytesWithUnit } from '$lib/utils/byte-units'; import { handleError } from '$lib/utils/handle-error'; import { updateUserAdmin } from '@immich/sdk'; @@ -70,6 +71,12 @@ let canResetPassword = $derived($authUser.id !== user.id); let newPassword = $state(''); + let editedLocale = $derived(findLocale($locale).code); + let createAtDate: Date = $derived(new Date(user.createdAt)); + let updatedAtDate: Date = $derived(new Date(user.updatedAt)); + let userCreatedAtDateAndTime: string = $derived(createDateFormatter(editedLocale).formatDateTime(createAtDate)); + let userUpdatedAtDateAndTime: string = $derived(createDateFormatter(editedLocale).formatDateTime(updatedAtDate)); + const handleEdit = async () => { const result = await modalManager.show(UserEditModal, { user: { ...user } }); if (result) { @@ -266,11 +273,11 @@
    {$t('created_at')} - {user.createdAt} + {userCreatedAtDateAndTime}
    {$t('updated_at')} - {user.updatedAt} + {userUpdatedAtDateAndTime}
    {$t('id')} From adb55f37262bf5116cf86cda9953dcefb28428d0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Aug 2025 18:07:49 -0400 Subject: [PATCH 231/748] fix(deps): update machine-learning (#19803) * fix(deps): update machine-learning * typing fixes --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: mertalev <101130780+mertalev@users.noreply.github.com> --- machine-learning/Dockerfile | 8 +- .../immich_ml/models/transforms.py | 14 +- machine-learning/immich_ml/schemas.py | 4 - machine-learning/pyproject.toml | 1 + machine-learning/uv.lock | 3247 +++++++++-------- 5 files changed, 1821 insertions(+), 1453 deletions(-) diff --git a/machine-learning/Dockerfile b/machine-learning/Dockerfile index 372982af67..58ab57010f 100644 --- a/machine-learning/Dockerfile +++ b/machine-learning/Dockerfile @@ -1,6 +1,6 @@ ARG DEVICE=cpu -FROM python:3.11-bookworm@sha256:ce3b954c9285a7a145cba620bae03db836ab890b6b9e0d05a3ca522ea00dfbc9 AS builder-cpu +FROM python:3.11-bookworm@sha256:c1239cb82bf08176c4c90421ab425a1696257b098d9ce21e68de9319c255a47d AS builder-cpu FROM builder-cpu AS builder-openvino @@ -59,7 +59,7 @@ ENV PYTHONDONTWRITEBYTECODE=1 \ RUN apt-get update && apt-get install -y --no-install-recommends g++ -COPY --from=ghcr.io/astral-sh/uv:latest@sha256:9653efd4380d5a0e5511e337dcfc3b8ba5bc4e6ea7fa3be7716598261d5503fa /uv /uvx /bin/ +COPY --from=ghcr.io/astral-sh/uv:latest@sha256:67b2bcccdc103d608727d1b577e58008ef810f751ed324715eb60b3f0c040d30 /uv /uvx /bin/ RUN --mount=type=cache,target=/root/.cache/uv \ --mount=type=bind,source=uv.lock,target=uv.lock \ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ @@ -68,11 +68,11 @@ RUN if [ "$DEVICE" = "rocm" ]; then \ uv pip install /opt/onnxruntime_rocm-*.whl; \ fi -FROM python:3.11-slim-bookworm@sha256:9e1912aab0a30bbd9488eb79063f68f42a68ab0946cbe98fecf197fe5b085506 AS prod-cpu +FROM python:3.11-slim-bookworm@sha256:0ce77749ac83174a31d5e107ce0cfa6b28a2fd6b0615e029d9d84b39c48976ee AS prod-cpu ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2 -FROM python:3.11-slim-bookworm@sha256:9e1912aab0a30bbd9488eb79063f68f42a68ab0946cbe98fecf197fe5b085506 AS prod-openvino +FROM python:3.11-slim-bookworm@sha256:0ce77749ac83174a31d5e107ce0cfa6b28a2fd6b0615e029d9d84b39c48976ee AS prod-openvino RUN apt-get update && \ apt-get install --no-install-recommends -yqq ocl-icd-libopencl1 wget && \ diff --git a/machine-learning/immich_ml/models/transforms.py b/machine-learning/immich_ml/models/transforms.py index e70763a07f..3f9d93bed3 100644 --- a/machine-learning/immich_ml/models/transforms.py +++ b/machine-learning/immich_ml/models/transforms.py @@ -36,7 +36,7 @@ def to_numpy(img: Image.Image) -> NDArray[np.float32]: def normalize( img: NDArray[np.float32], mean: float | NDArray[np.float32], std: float | NDArray[np.float32] ) -> NDArray[np.float32]: - return np.divide(img - mean, std, dtype=np.float32) + return (img - mean) / std def get_pil_resampling(resample: str) -> Image.Resampling: @@ -58,11 +58,13 @@ def decode_pil(image_bytes: bytes | IO[bytes] | Image.Image) -> Image.Image: def decode_cv2(image_bytes: NDArray[np.uint8] | bytes | Image.Image) -> NDArray[np.uint8]: - if isinstance(image_bytes, bytes): - image_bytes = decode_pil(image_bytes) # pillow is much faster than cv2 - if isinstance(image_bytes, Image.Image): - return pil_to_cv2(image_bytes) - return image_bytes + match image_bytes: + case bytes() | memoryview() | bytearray(): + return pil_to_cv2(decode_pil(image_bytes)) # pillow is much faster than cv2 + case Image.Image(): + return pil_to_cv2(image_bytes) + case _: + return image_bytes def clean_text(text: str, canonicalize: bool = False) -> str: diff --git a/machine-learning/immich_ml/schemas.py b/machine-learning/immich_ml/schemas.py index 6f9076807d..7ad1e215d6 100644 --- a/machine-learning/immich_ml/schemas.py +++ b/machine-learning/immich_ml/schemas.py @@ -112,8 +112,4 @@ def has_profiling(obj: Any) -> TypeGuard[HasProfiling]: return hasattr(obj, "profiling") and isinstance(obj.profiling, dict) -def is_ndarray(obj: Any, dtype: "type[np._DTypeScalar_co]") -> "TypeGuard[npt.NDArray[np._DTypeScalar_co]]": - return isinstance(obj, np.ndarray) and obj.dtype == dtype - - T = TypeVar("T") diff --git a/machine-learning/pyproject.toml b/machine-learning/pyproject.toml index ca8f432ae2..f0f08b20b6 100644 --- a/machine-learning/pyproject.toml +++ b/machine-learning/pyproject.toml @@ -12,6 +12,7 @@ dependencies = [ "gunicorn>=21.1.0", "huggingface-hub>=0.20.1,<1.0", "insightface>=0.7.3,<1.0", + "numpy<2", "opencv-python-headless>=4.7.0.72,<5.0", "orjson>=3.9.5", "pillow>=9.5.0,<11.0", diff --git a/machine-learning/uv.lock b/machine-learning/uv.lock index 7da2fd3920..3c26676108 100644 --- a/machine-learning/uv.lock +++ b/machine-learning/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 1 requires-python = ">=3.10, <4.0" resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'darwin'", @@ -23,9 +23,9 @@ resolution-markers = [ name = "aiocache" version = "0.12.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7a/64/b945b8025a9d1e6e2138845f4022165d3b337f55f50984fbc6a4c0a1e355/aiocache-0.12.3.tar.gz", hash = "sha256:f528b27bf4d436b497a1d0d1a8f59a542c153ab1e37c3621713cb376d44c4713", size = 132196, upload-time = "2024-09-25T13:20:23.823Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7a/64/b945b8025a9d1e6e2138845f4022165d3b337f55f50984fbc6a4c0a1e355/aiocache-0.12.3.tar.gz", hash = "sha256:f528b27bf4d436b497a1d0d1a8f59a542c153ab1e37c3621713cb376d44c4713", size = 132196 } wheels = [ - { url = "https://files.pythonhosted.org/packages/37/d7/15d67e05b235d1ed8c3ce61688fe4d84130e72af1657acadfaac3479f4cf/aiocache-0.12.3-py2.py3-none-any.whl", hash = "sha256:889086fc24710f431937b87ad3720a289f7fc31c4fd8b68e9f918b9bacd8270d", size = 28199, upload-time = "2024-09-25T13:20:22.688Z" }, + { url = "https://files.pythonhosted.org/packages/37/d7/15d67e05b235d1ed8c3ce61688fe4d84130e72af1657acadfaac3479f4cf/aiocache-0.12.3-py2.py3-none-any.whl", hash = "sha256:889086fc24710f431937b87ad3720a289f7fc31c4fd8b68e9f918b9bacd8270d", size = 28199 }, ] [[package]] @@ -38,20 +38,21 @@ dependencies = [ { name = "pyyaml" }, { name = "qudida" }, { name = "scikit-image" }, - { name = "scipy" }, + { name = "scipy", version = "1.11.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/14/d6/8dd5b690d28a332a0b2c3179a345808b5d4c7ad5ddc079b7e116098dff35/albumentations-1.3.1.tar.gz", hash = "sha256:a6a38388fe546c568071e8c82f414498e86c9ed03c08b58e7a88b31cf7a244c6", size = 176371, upload-time = "2023-06-10T07:44:32.36Z" } +sdist = { url = "https://files.pythonhosted.org/packages/14/d6/8dd5b690d28a332a0b2c3179a345808b5d4c7ad5ddc079b7e116098dff35/albumentations-1.3.1.tar.gz", hash = "sha256:a6a38388fe546c568071e8c82f414498e86c9ed03c08b58e7a88b31cf7a244c6", size = 176371 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/f6/c486cedb4f75147232f32ec4c97026714cfef7c7e247a1f0427bc5489f66/albumentations-1.3.1-py3-none-any.whl", hash = "sha256:6b641d13733181d9ecdc29550e6ad580d1bfa9d25e2213a66940062f25e291bd", size = 125706, upload-time = "2023-06-10T07:44:30.373Z" }, + { url = "https://files.pythonhosted.org/packages/9b/f6/c486cedb4f75147232f32ec4c97026714cfef7c7e247a1f0427bc5489f66/albumentations-1.3.1-py3-none-any.whl", hash = "sha256:6b641d13733181d9ecdc29550e6ad580d1bfa9d25e2213a66940062f25e291bd", size = 125706 }, ] [[package]] name = "annotated-types" version = "0.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, ] [[package]] @@ -64,18 +65,27 @@ dependencies = [ { name = "sniffio" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2d/b8/7333d87d5f03247215d86a86362fd3e324111788c6cdd8d2e6196a6ba833/anyio-4.2.0.tar.gz", hash = "sha256:e1875bb4b4e2de1669f4bc7869b6d3f54231cdced71605e6e64c9be77e3be50f", size = 158770, upload-time = "2023-12-16T17:06:57.709Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/b8/7333d87d5f03247215d86a86362fd3e324111788c6cdd8d2e6196a6ba833/anyio-4.2.0.tar.gz", hash = "sha256:e1875bb4b4e2de1669f4bc7869b6d3f54231cdced71605e6e64c9be77e3be50f", size = 158770 } wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/cd/d6d9bb1dadf73e7af02d18225cbd2c93f8552e13130484f1c8dcfece292b/anyio-4.2.0-py3-none-any.whl", hash = "sha256:745843b39e829e108e518c489b31dc757de7d2131d53fac32bd8df268227bfee", size = 85481, upload-time = "2023-12-16T17:06:55.989Z" }, + { url = "https://files.pythonhosted.org/packages/bf/cd/d6d9bb1dadf73e7af02d18225cbd2c93f8552e13130484f1c8dcfece292b/anyio-4.2.0-py3-none-any.whl", hash = "sha256:745843b39e829e108e518c489b31dc757de7d2131d53fac32bd8df268227bfee", size = 85481 }, +] + +[[package]] +name = "backports-asyncio-runner" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313 }, ] [[package]] name = "bidict" version = "0.23.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9a/6e/026678aa5a830e07cd9498a05d3e7e650a4f56a42f267a53d22bcda1bdc9/bidict-0.23.1.tar.gz", hash = "sha256:03069d763bc387bbd20e7d49914e75fc4132a41937fa3405417e1a5a2d006d71", size = 29093, upload-time = "2024-02-18T19:09:05.748Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/6e/026678aa5a830e07cd9498a05d3e7e650a4f56a42f267a53d22bcda1bdc9/bidict-0.23.1.tar.gz", hash = "sha256:03069d763bc387bbd20e7d49914e75fc4132a41937fa3405417e1a5a2d006d71", size = 29093 } wheels = [ - { url = "https://files.pythonhosted.org/packages/99/37/e8730c3587a65eb5645d4aba2d27aae48e8003614d6aaf15dda67f702f1f/bidict-0.23.1-py3-none-any.whl", hash = "sha256:5dae8d4d79b552a71cbabc7deb25dfe8ce710b17ff41711e13010ead2abfc3e5", size = 32764, upload-time = "2024-02-18T19:09:04.156Z" }, + { url = "https://files.pythonhosted.org/packages/99/37/e8730c3587a65eb5645d4aba2d27aae48e8003614d6aaf15dda67f702f1f/bidict-0.23.1-py3-none-any.whl", hash = "sha256:5dae8d4d79b552a71cbabc7deb25dfe8ce710b17ff41711e13010ead2abfc3e5", size = 32764 }, ] [[package]] @@ -91,113 +101,113 @@ dependencies = [ { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/94/49/26a7b0f3f35da4b5a65f081943b7bcd22d7002f5f0fb8098ec1ff21cb6ef/black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", size = 649449, upload-time = "2025-01-29T04:15:40.373Z" } +sdist = { url = "https://files.pythonhosted.org/packages/94/49/26a7b0f3f35da4b5a65f081943b7bcd22d7002f5f0fb8098ec1ff21cb6ef/black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", size = 649449 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/3b/4ba3f93ac8d90410423fdd31d7541ada9bcee1df32fb90d26de41ed40e1d/black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32", size = 1629419, upload-time = "2025-01-29T05:37:06.642Z" }, - { url = "https://files.pythonhosted.org/packages/b4/02/0bde0485146a8a5e694daed47561785e8b77a0466ccc1f3e485d5ef2925e/black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da", size = 1461080, upload-time = "2025-01-29T05:37:09.321Z" }, - { url = "https://files.pythonhosted.org/packages/52/0e/abdf75183c830eaca7589144ff96d49bce73d7ec6ad12ef62185cc0f79a2/black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7", size = 1766886, upload-time = "2025-01-29T04:18:24.432Z" }, - { url = "https://files.pythonhosted.org/packages/dc/a6/97d8bb65b1d8a41f8a6736222ba0a334db7b7b77b8023ab4568288f23973/black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9", size = 1419404, upload-time = "2025-01-29T04:19:04.296Z" }, - { url = "https://files.pythonhosted.org/packages/7e/4f/87f596aca05c3ce5b94b8663dbfe242a12843caaa82dd3f85f1ffdc3f177/black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0", size = 1614372, upload-time = "2025-01-29T05:37:11.71Z" }, - { url = "https://files.pythonhosted.org/packages/e7/d0/2c34c36190b741c59c901e56ab7f6e54dad8df05a6272a9747ecef7c6036/black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299", size = 1442865, upload-time = "2025-01-29T05:37:14.309Z" }, - { url = "https://files.pythonhosted.org/packages/21/d4/7518c72262468430ead45cf22bd86c883a6448b9eb43672765d69a8f1248/black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096", size = 1749699, upload-time = "2025-01-29T04:18:17.688Z" }, - { url = "https://files.pythonhosted.org/packages/58/db/4f5beb989b547f79096e035c4981ceb36ac2b552d0ac5f2620e941501c99/black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2", size = 1428028, upload-time = "2025-01-29T04:18:51.711Z" }, - { url = "https://files.pythonhosted.org/packages/83/71/3fe4741df7adf015ad8dfa082dd36c94ca86bb21f25608eb247b4afb15b2/black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b", size = 1650988, upload-time = "2025-01-29T05:37:16.707Z" }, - { url = "https://files.pythonhosted.org/packages/13/f3/89aac8a83d73937ccd39bbe8fc6ac8860c11cfa0af5b1c96d081facac844/black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc", size = 1453985, upload-time = "2025-01-29T05:37:18.273Z" }, - { url = "https://files.pythonhosted.org/packages/6f/22/b99efca33f1f3a1d2552c714b1e1b5ae92efac6c43e790ad539a163d1754/black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f", size = 1783816, upload-time = "2025-01-29T04:18:33.823Z" }, - { url = "https://files.pythonhosted.org/packages/18/7e/a27c3ad3822b6f2e0e00d63d58ff6299a99a5b3aee69fa77cd4b0076b261/black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba", size = 1440860, upload-time = "2025-01-29T04:19:12.944Z" }, - { url = "https://files.pythonhosted.org/packages/98/87/0edf98916640efa5d0696e1abb0a8357b52e69e82322628f25bf14d263d1/black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", size = 1650673, upload-time = "2025-01-29T05:37:20.574Z" }, - { url = "https://files.pythonhosted.org/packages/52/e5/f7bf17207cf87fa6e9b676576749c6b6ed0d70f179a3d812c997870291c3/black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", size = 1453190, upload-time = "2025-01-29T05:37:22.106Z" }, - { url = "https://files.pythonhosted.org/packages/e3/ee/adda3d46d4a9120772fae6de454c8495603c37c4c3b9c60f25b1ab6401fe/black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", size = 1782926, upload-time = "2025-01-29T04:18:58.564Z" }, - { url = "https://files.pythonhosted.org/packages/cc/64/94eb5f45dcb997d2082f097a3944cfc7fe87e071907f677e80788a2d7b7a/black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", size = 1442613, upload-time = "2025-01-29T04:19:27.63Z" }, - { url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646, upload-time = "2025-01-29T04:15:38.082Z" }, + { url = "https://files.pythonhosted.org/packages/4d/3b/4ba3f93ac8d90410423fdd31d7541ada9bcee1df32fb90d26de41ed40e1d/black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32", size = 1629419 }, + { url = "https://files.pythonhosted.org/packages/b4/02/0bde0485146a8a5e694daed47561785e8b77a0466ccc1f3e485d5ef2925e/black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da", size = 1461080 }, + { url = "https://files.pythonhosted.org/packages/52/0e/abdf75183c830eaca7589144ff96d49bce73d7ec6ad12ef62185cc0f79a2/black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7", size = 1766886 }, + { url = "https://files.pythonhosted.org/packages/dc/a6/97d8bb65b1d8a41f8a6736222ba0a334db7b7b77b8023ab4568288f23973/black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9", size = 1419404 }, + { url = "https://files.pythonhosted.org/packages/7e/4f/87f596aca05c3ce5b94b8663dbfe242a12843caaa82dd3f85f1ffdc3f177/black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0", size = 1614372 }, + { url = "https://files.pythonhosted.org/packages/e7/d0/2c34c36190b741c59c901e56ab7f6e54dad8df05a6272a9747ecef7c6036/black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299", size = 1442865 }, + { url = "https://files.pythonhosted.org/packages/21/d4/7518c72262468430ead45cf22bd86c883a6448b9eb43672765d69a8f1248/black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096", size = 1749699 }, + { url = "https://files.pythonhosted.org/packages/58/db/4f5beb989b547f79096e035c4981ceb36ac2b552d0ac5f2620e941501c99/black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2", size = 1428028 }, + { url = "https://files.pythonhosted.org/packages/83/71/3fe4741df7adf015ad8dfa082dd36c94ca86bb21f25608eb247b4afb15b2/black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b", size = 1650988 }, + { url = "https://files.pythonhosted.org/packages/13/f3/89aac8a83d73937ccd39bbe8fc6ac8860c11cfa0af5b1c96d081facac844/black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc", size = 1453985 }, + { url = "https://files.pythonhosted.org/packages/6f/22/b99efca33f1f3a1d2552c714b1e1b5ae92efac6c43e790ad539a163d1754/black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f", size = 1783816 }, + { url = "https://files.pythonhosted.org/packages/18/7e/a27c3ad3822b6f2e0e00d63d58ff6299a99a5b3aee69fa77cd4b0076b261/black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba", size = 1440860 }, + { url = "https://files.pythonhosted.org/packages/98/87/0edf98916640efa5d0696e1abb0a8357b52e69e82322628f25bf14d263d1/black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", size = 1650673 }, + { url = "https://files.pythonhosted.org/packages/52/e5/f7bf17207cf87fa6e9b676576749c6b6ed0d70f179a3d812c997870291c3/black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", size = 1453190 }, + { url = "https://files.pythonhosted.org/packages/e3/ee/adda3d46d4a9120772fae6de454c8495603c37c4c3b9c60f25b1ab6401fe/black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", size = 1782926 }, + { url = "https://files.pythonhosted.org/packages/cc/64/94eb5f45dcb997d2082f097a3944cfc7fe87e071907f677e80788a2d7b7a/black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", size = 1442613 }, + { url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646 }, ] [[package]] name = "blinker" version = "1.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/13/6df5fc090ff4e5d246baf1f45fe9e5623aa8565757dfa5bd243f6a545f9e/blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182", size = 28134, upload-time = "2023-11-01T22:06:01.588Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/13/6df5fc090ff4e5d246baf1f45fe9e5623aa8565757dfa5bd243f6a545f9e/blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182", size = 28134 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/2a/7f3714cbc6356a0efec525ce7a0613d581072ed6eb53eb7b9754f33db807/blinker-1.7.0-py3-none-any.whl", hash = "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9", size = 13068, upload-time = "2023-11-01T22:06:00.162Z" }, + { url = "https://files.pythonhosted.org/packages/fa/2a/7f3714cbc6356a0efec525ce7a0613d581072ed6eb53eb7b9754f33db807/blinker-1.7.0-py3-none-any.whl", hash = "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9", size = 13068 }, ] [[package]] name = "brotli" version = "1.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2f/c2/f9e977608bdf958650638c3f1e28f85a1b075f075ebbe77db8555463787b/Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724", size = 7372270, upload-time = "2023-09-07T14:05:41.643Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2f/c2/f9e977608bdf958650638c3f1e28f85a1b075f075ebbe77db8555463787b/Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724", size = 7372270 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/3a/dbf4fb970c1019a57b5e492e1e0eae745d32e59ba4d6161ab5422b08eefe/Brotli-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752", size = 873045, upload-time = "2023-09-07T14:03:16.894Z" }, - { url = "https://files.pythonhosted.org/packages/dd/11/afc14026ea7f44bd6eb9316d800d439d092c8d508752055ce8d03086079a/Brotli-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9", size = 446218, upload-time = "2023-09-07T14:03:18.917Z" }, - { url = "https://files.pythonhosted.org/packages/36/83/7545a6e7729db43cb36c4287ae388d6885c85a86dd251768a47015dfde32/Brotli-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ae56aca0402a0f9a3431cddda62ad71666ca9d4dc3a10a142b9dce2e3c0cda3", size = 2903872, upload-time = "2023-09-07T14:03:20.398Z" }, - { url = "https://files.pythonhosted.org/packages/32/23/35331c4d9391fcc0f29fd9bec2c76e4b4eeab769afbc4b11dd2e1098fb13/Brotli-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43ce1b9935bfa1ede40028054d7f48b5469cd02733a365eec8a329ffd342915d", size = 2941254, upload-time = "2023-09-07T14:03:21.914Z" }, - { url = "https://files.pythonhosted.org/packages/3b/24/1671acb450c902edb64bd765d73603797c6c7280a9ada85a195f6b78c6e5/Brotli-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7c4855522edb2e6ae7fdb58e07c3ba9111e7621a8956f481c68d5d979c93032e", size = 2857293, upload-time = "2023-09-07T14:03:24Z" }, - { url = "https://files.pythonhosted.org/packages/d5/00/40f760cc27007912b327fe15bf6bfd8eaecbe451687f72a8abc587d503b3/Brotli-1.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:38025d9f30cf4634f8309c6874ef871b841eb3c347e90b0851f63d1ded5212da", size = 3002385, upload-time = "2023-09-07T14:03:26.248Z" }, - { url = "https://files.pythonhosted.org/packages/b8/cb/8aaa83f7a4caa131757668c0fb0c4b6384b09ffa77f2fba9570d87ab587d/Brotli-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e6a904cb26bfefc2f0a6f240bdf5233be78cd2488900a2f846f3c3ac8489ab80", size = 2911104, upload-time = "2023-09-07T14:03:27.849Z" }, - { url = "https://files.pythonhosted.org/packages/bc/c4/65456561d89d3c49f46b7fbeb8fe6e449f13bdc8ea7791832c5d476b2faf/Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d", size = 2809981, upload-time = "2023-09-07T14:03:29.92Z" }, - { url = "https://files.pythonhosted.org/packages/05/1b/cf49528437bae28abce5f6e059f0d0be6fecdcc1d3e33e7c54b3ca498425/Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0", size = 2935297, upload-time = "2023-09-07T14:03:32.035Z" }, - { url = "https://files.pythonhosted.org/packages/81/ff/190d4af610680bf0c5a09eb5d1eac6e99c7c8e216440f9c7cfd42b7adab5/Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e", size = 2930735, upload-time = "2023-09-07T14:03:33.801Z" }, - { url = "https://files.pythonhosted.org/packages/80/7d/f1abbc0c98f6e09abd3cad63ec34af17abc4c44f308a7a539010f79aae7a/Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c", size = 2933107, upload-time = "2024-10-18T12:32:09.016Z" }, - { url = "https://files.pythonhosted.org/packages/34/ce/5a5020ba48f2b5a4ad1c0522d095ad5847a0be508e7d7569c8630ce25062/Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1", size = 2845400, upload-time = "2024-10-18T12:32:11.134Z" }, - { url = "https://files.pythonhosted.org/packages/44/89/fa2c4355ab1eecf3994e5a0a7f5492c6ff81dfcb5f9ba7859bd534bb5c1a/Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2", size = 3031985, upload-time = "2024-10-18T12:32:12.813Z" }, - { url = "https://files.pythonhosted.org/packages/af/a4/79196b4a1674143d19dca400866b1a4d1a089040df7b93b88ebae81f3447/Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec", size = 2927099, upload-time = "2024-10-18T12:32:14.733Z" }, - { url = "https://files.pythonhosted.org/packages/e9/54/1c0278556a097f9651e657b873ab08f01b9a9ae4cac128ceb66427d7cd20/Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2", size = 333172, upload-time = "2023-09-07T14:03:35.212Z" }, - { url = "https://files.pythonhosted.org/packages/f7/65/b785722e941193fd8b571afd9edbec2a9b838ddec4375d8af33a50b8dab9/Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128", size = 357255, upload-time = "2023-09-07T14:03:36.447Z" }, - { url = "https://files.pythonhosted.org/packages/96/12/ad41e7fadd5db55459c4c401842b47f7fee51068f86dd2894dd0dcfc2d2a/Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc", size = 873068, upload-time = "2023-09-07T14:03:37.779Z" }, - { url = "https://files.pythonhosted.org/packages/95/4e/5afab7b2b4b61a84e9c75b17814198ce515343a44e2ed4488fac314cd0a9/Brotli-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6", size = 446244, upload-time = "2023-09-07T14:03:39.223Z" }, - { url = "https://files.pythonhosted.org/packages/9d/e6/f305eb61fb9a8580c525478a4a34c5ae1a9bcb12c3aee619114940bc513d/Brotli-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd", size = 2906500, upload-time = "2023-09-07T14:03:40.858Z" }, - { url = "https://files.pythonhosted.org/packages/3e/4f/af6846cfbc1550a3024e5d3775ede1e00474c40882c7bf5b37a43ca35e91/Brotli-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf", size = 2943950, upload-time = "2023-09-07T14:03:42.896Z" }, - { url = "https://files.pythonhosted.org/packages/b3/e7/ca2993c7682d8629b62630ebf0d1f3bb3d579e667ce8e7ca03a0a0576a2d/Brotli-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61", size = 2918527, upload-time = "2023-09-07T14:03:44.552Z" }, - { url = "https://files.pythonhosted.org/packages/b3/96/da98e7bedc4c51104d29cc61e5f449a502dd3dbc211944546a4cc65500d3/Brotli-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327", size = 2845489, upload-time = "2023-09-07T14:03:46.594Z" }, - { url = "https://files.pythonhosted.org/packages/e8/ef/ccbc16947d6ce943a7f57e1a40596c75859eeb6d279c6994eddd69615265/Brotli-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd", size = 2914080, upload-time = "2023-09-07T14:03:48.204Z" }, - { url = "https://files.pythonhosted.org/packages/80/d6/0bd38d758d1afa62a5524172f0b18626bb2392d717ff94806f741fcd5ee9/Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9", size = 2813051, upload-time = "2023-09-07T14:03:50.348Z" }, - { url = "https://files.pythonhosted.org/packages/14/56/48859dd5d129d7519e001f06dcfbb6e2cf6db92b2702c0c2ce7d97e086c1/Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265", size = 2938172, upload-time = "2023-09-07T14:03:52.395Z" }, - { url = "https://files.pythonhosted.org/packages/3d/77/a236d5f8cd9e9f4348da5acc75ab032ab1ab2c03cc8f430d24eea2672888/Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8", size = 2933023, upload-time = "2023-09-07T14:03:53.96Z" }, - { url = "https://files.pythonhosted.org/packages/f1/87/3b283efc0f5cb35f7f84c0c240b1e1a1003a5e47141a4881bf87c86d0ce2/Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f", size = 2935871, upload-time = "2024-10-18T12:32:16.688Z" }, - { url = "https://files.pythonhosted.org/packages/f3/eb/2be4cc3e2141dc1a43ad4ca1875a72088229de38c68e842746b342667b2a/Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757", size = 2847784, upload-time = "2024-10-18T12:32:18.459Z" }, - { url = "https://files.pythonhosted.org/packages/66/13/b58ddebfd35edde572ccefe6890cf7c493f0c319aad2a5badee134b4d8ec/Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0", size = 3034905, upload-time = "2024-10-18T12:32:20.192Z" }, - { url = "https://files.pythonhosted.org/packages/84/9c/bc96b6c7db824998a49ed3b38e441a2cae9234da6fa11f6ed17e8cf4f147/Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b", size = 2929467, upload-time = "2024-10-18T12:32:21.774Z" }, - { url = "https://files.pythonhosted.org/packages/e7/71/8f161dee223c7ff7fea9d44893fba953ce97cf2c3c33f78ba260a91bcff5/Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50", size = 333169, upload-time = "2023-09-07T14:03:55.404Z" }, - { url = "https://files.pythonhosted.org/packages/02/8a/fece0ee1057643cb2a5bbf59682de13f1725f8482b2c057d4e799d7ade75/Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1", size = 357253, upload-time = "2023-09-07T14:03:56.643Z" }, - { url = "https://files.pythonhosted.org/packages/5c/d0/5373ae13b93fe00095a58efcbce837fd470ca39f703a235d2a999baadfbc/Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28", size = 815693, upload-time = "2024-10-18T12:32:23.824Z" }, - { url = "https://files.pythonhosted.org/packages/8e/48/f6e1cdf86751300c288c1459724bfa6917a80e30dbfc326f92cea5d3683a/Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f", size = 422489, upload-time = "2024-10-18T12:32:25.641Z" }, - { url = "https://files.pythonhosted.org/packages/06/88/564958cedce636d0f1bed313381dfc4b4e3d3f6015a63dae6146e1b8c65c/Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409", size = 873081, upload-time = "2023-09-07T14:03:57.967Z" }, - { url = "https://files.pythonhosted.org/packages/58/79/b7026a8bb65da9a6bb7d14329fd2bd48d2b7f86d7329d5cc8ddc6a90526f/Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2", size = 446244, upload-time = "2023-09-07T14:03:59.319Z" }, - { url = "https://files.pythonhosted.org/packages/e5/18/c18c32ecea41b6c0004e15606e274006366fe19436b6adccc1ae7b2e50c2/Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451", size = 2906505, upload-time = "2023-09-07T14:04:01.327Z" }, - { url = "https://files.pythonhosted.org/packages/08/c8/69ec0496b1ada7569b62d85893d928e865df29b90736558d6c98c2031208/Brotli-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f4bf76817c14aa98cc6697ac02f3972cb8c3da93e9ef16b9c66573a68014f91", size = 2944152, upload-time = "2023-09-07T14:04:03.033Z" }, - { url = "https://files.pythonhosted.org/packages/ab/fb/0517cea182219d6768113a38167ef6d4eb157a033178cc938033a552ed6d/Brotli-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0c5516f0aed654134a2fc936325cc2e642f8a0e096d075209672eb321cff408", size = 2919252, upload-time = "2023-09-07T14:04:04.675Z" }, - { url = "https://files.pythonhosted.org/packages/c7/53/73a3431662e33ae61a5c80b1b9d2d18f58dfa910ae8dd696e57d39f1a2f5/Brotli-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c3020404e0b5eefd7c9485ccf8393cfb75ec38ce75586e046573c9dc29967a0", size = 2845955, upload-time = "2023-09-07T14:04:06.585Z" }, - { url = "https://files.pythonhosted.org/packages/55/ac/bd280708d9c5ebdbf9de01459e625a3e3803cce0784f47d633562cf40e83/Brotli-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ed11165dd45ce798d99a136808a794a748d5dc38511303239d4e2363c0695dc", size = 2914304, upload-time = "2023-09-07T14:04:08.668Z" }, - { url = "https://files.pythonhosted.org/packages/76/58/5c391b41ecfc4527d2cc3350719b02e87cb424ef8ba2023fb662f9bf743c/Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180", size = 2814452, upload-time = "2023-09-07T14:04:10.736Z" }, - { url = "https://files.pythonhosted.org/packages/c7/4e/91b8256dfe99c407f174924b65a01f5305e303f486cc7a2e8a5d43c8bec3/Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248", size = 2938751, upload-time = "2023-09-07T14:04:12.875Z" }, - { url = "https://files.pythonhosted.org/packages/5a/a6/e2a39a5d3b412938362bbbeba5af904092bf3f95b867b4a3eb856104074e/Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966", size = 2933757, upload-time = "2023-09-07T14:04:14.551Z" }, - { url = "https://files.pythonhosted.org/packages/13/f0/358354786280a509482e0e77c1a5459e439766597d280f28cb097642fc26/Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9", size = 2936146, upload-time = "2024-10-18T12:32:27.257Z" }, - { url = "https://files.pythonhosted.org/packages/80/f7/daf538c1060d3a88266b80ecc1d1c98b79553b3f117a485653f17070ea2a/Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb", size = 2848055, upload-time = "2024-10-18T12:32:29.376Z" }, - { url = "https://files.pythonhosted.org/packages/ad/cf/0eaa0585c4077d3c2d1edf322d8e97aabf317941d3a72d7b3ad8bce004b0/Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111", size = 3035102, upload-time = "2024-10-18T12:32:31.371Z" }, - { url = "https://files.pythonhosted.org/packages/d8/63/1c1585b2aa554fe6dbce30f0c18bdbc877fa9a1bf5ff17677d9cca0ac122/Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839", size = 2930029, upload-time = "2024-10-18T12:32:33.293Z" }, - { url = "https://files.pythonhosted.org/packages/5f/3b/4e3fd1893eb3bbfef8e5a80d4508bec17a57bb92d586c85c12d28666bb13/Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0", size = 333276, upload-time = "2023-09-07T14:04:16.49Z" }, - { url = "https://files.pythonhosted.org/packages/3d/d5/942051b45a9e883b5b6e98c041698b1eb2012d25e5948c58d6bf85b1bb43/Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951", size = 357255, upload-time = "2023-09-07T14:04:17.83Z" }, - { url = "https://files.pythonhosted.org/packages/0a/9f/fb37bb8ffc52a8da37b1c03c459a8cd55df7a57bdccd8831d500e994a0ca/Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5", size = 815681, upload-time = "2024-10-18T12:32:34.942Z" }, - { url = "https://files.pythonhosted.org/packages/06/b3/dbd332a988586fefb0aa49c779f59f47cae76855c2d00f450364bb574cac/Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8", size = 422475, upload-time = "2024-10-18T12:32:36.485Z" }, - { url = "https://files.pythonhosted.org/packages/bb/80/6aaddc2f63dbcf2d93c2d204e49c11a9ec93a8c7c63261e2b4bd35198283/Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f", size = 2906173, upload-time = "2024-10-18T12:32:37.978Z" }, - { url = "https://files.pythonhosted.org/packages/ea/1d/e6ca79c96ff5b641df6097d299347507d39a9604bde8915e76bf026d6c77/Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648", size = 2943803, upload-time = "2024-10-18T12:32:39.606Z" }, - { url = "https://files.pythonhosted.org/packages/ac/a3/d98d2472e0130b7dd3acdbb7f390d478123dbf62b7d32bda5c830a96116d/Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0", size = 2918946, upload-time = "2024-10-18T12:32:41.679Z" }, - { url = "https://files.pythonhosted.org/packages/c4/a5/c69e6d272aee3e1423ed005d8915a7eaa0384c7de503da987f2d224d0721/Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089", size = 2845707, upload-time = "2024-10-18T12:32:43.478Z" }, - { url = "https://files.pythonhosted.org/packages/58/9f/4149d38b52725afa39067350696c09526de0125ebfbaab5acc5af28b42ea/Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368", size = 2936231, upload-time = "2024-10-18T12:32:45.224Z" }, - { url = "https://files.pythonhosted.org/packages/5a/5a/145de884285611838a16bebfdb060c231c52b8f84dfbe52b852a15780386/Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c", size = 2848157, upload-time = "2024-10-18T12:32:46.894Z" }, - { url = "https://files.pythonhosted.org/packages/50/ae/408b6bfb8525dadebd3b3dd5b19d631da4f7d46420321db44cd99dcf2f2c/Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284", size = 3035122, upload-time = "2024-10-18T12:32:48.844Z" }, - { url = "https://files.pythonhosted.org/packages/af/85/a94e5cfaa0ca449d8f91c3d6f78313ebf919a0dbd55a100c711c6e9655bc/Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7", size = 2930206, upload-time = "2024-10-18T12:32:51.198Z" }, - { url = "https://files.pythonhosted.org/packages/c2/f0/a61d9262cd01351df22e57ad7c34f66794709acab13f34be2675f45bf89d/Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0", size = 333804, upload-time = "2024-10-18T12:32:52.661Z" }, - { url = "https://files.pythonhosted.org/packages/7e/c1/ec214e9c94000d1c1974ec67ced1c970c148aa6b8d8373066123fc3dbf06/Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b", size = 358517, upload-time = "2024-10-18T12:32:54.066Z" }, + { url = "https://files.pythonhosted.org/packages/6d/3a/dbf4fb970c1019a57b5e492e1e0eae745d32e59ba4d6161ab5422b08eefe/Brotli-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752", size = 873045 }, + { url = "https://files.pythonhosted.org/packages/dd/11/afc14026ea7f44bd6eb9316d800d439d092c8d508752055ce8d03086079a/Brotli-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9", size = 446218 }, + { url = "https://files.pythonhosted.org/packages/36/83/7545a6e7729db43cb36c4287ae388d6885c85a86dd251768a47015dfde32/Brotli-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ae56aca0402a0f9a3431cddda62ad71666ca9d4dc3a10a142b9dce2e3c0cda3", size = 2903872 }, + { url = "https://files.pythonhosted.org/packages/32/23/35331c4d9391fcc0f29fd9bec2c76e4b4eeab769afbc4b11dd2e1098fb13/Brotli-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43ce1b9935bfa1ede40028054d7f48b5469cd02733a365eec8a329ffd342915d", size = 2941254 }, + { url = "https://files.pythonhosted.org/packages/3b/24/1671acb450c902edb64bd765d73603797c6c7280a9ada85a195f6b78c6e5/Brotli-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7c4855522edb2e6ae7fdb58e07c3ba9111e7621a8956f481c68d5d979c93032e", size = 2857293 }, + { url = "https://files.pythonhosted.org/packages/d5/00/40f760cc27007912b327fe15bf6bfd8eaecbe451687f72a8abc587d503b3/Brotli-1.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:38025d9f30cf4634f8309c6874ef871b841eb3c347e90b0851f63d1ded5212da", size = 3002385 }, + { url = "https://files.pythonhosted.org/packages/b8/cb/8aaa83f7a4caa131757668c0fb0c4b6384b09ffa77f2fba9570d87ab587d/Brotli-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e6a904cb26bfefc2f0a6f240bdf5233be78cd2488900a2f846f3c3ac8489ab80", size = 2911104 }, + { url = "https://files.pythonhosted.org/packages/bc/c4/65456561d89d3c49f46b7fbeb8fe6e449f13bdc8ea7791832c5d476b2faf/Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d", size = 2809981 }, + { url = "https://files.pythonhosted.org/packages/05/1b/cf49528437bae28abce5f6e059f0d0be6fecdcc1d3e33e7c54b3ca498425/Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0", size = 2935297 }, + { url = "https://files.pythonhosted.org/packages/81/ff/190d4af610680bf0c5a09eb5d1eac6e99c7c8e216440f9c7cfd42b7adab5/Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e", size = 2930735 }, + { url = "https://files.pythonhosted.org/packages/80/7d/f1abbc0c98f6e09abd3cad63ec34af17abc4c44f308a7a539010f79aae7a/Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c", size = 2933107 }, + { url = "https://files.pythonhosted.org/packages/34/ce/5a5020ba48f2b5a4ad1c0522d095ad5847a0be508e7d7569c8630ce25062/Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1", size = 2845400 }, + { url = "https://files.pythonhosted.org/packages/44/89/fa2c4355ab1eecf3994e5a0a7f5492c6ff81dfcb5f9ba7859bd534bb5c1a/Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2", size = 3031985 }, + { url = "https://files.pythonhosted.org/packages/af/a4/79196b4a1674143d19dca400866b1a4d1a089040df7b93b88ebae81f3447/Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec", size = 2927099 }, + { url = "https://files.pythonhosted.org/packages/e9/54/1c0278556a097f9651e657b873ab08f01b9a9ae4cac128ceb66427d7cd20/Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2", size = 333172 }, + { url = "https://files.pythonhosted.org/packages/f7/65/b785722e941193fd8b571afd9edbec2a9b838ddec4375d8af33a50b8dab9/Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128", size = 357255 }, + { url = "https://files.pythonhosted.org/packages/96/12/ad41e7fadd5db55459c4c401842b47f7fee51068f86dd2894dd0dcfc2d2a/Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc", size = 873068 }, + { url = "https://files.pythonhosted.org/packages/95/4e/5afab7b2b4b61a84e9c75b17814198ce515343a44e2ed4488fac314cd0a9/Brotli-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6", size = 446244 }, + { url = "https://files.pythonhosted.org/packages/9d/e6/f305eb61fb9a8580c525478a4a34c5ae1a9bcb12c3aee619114940bc513d/Brotli-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd", size = 2906500 }, + { url = "https://files.pythonhosted.org/packages/3e/4f/af6846cfbc1550a3024e5d3775ede1e00474c40882c7bf5b37a43ca35e91/Brotli-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf", size = 2943950 }, + { url = "https://files.pythonhosted.org/packages/b3/e7/ca2993c7682d8629b62630ebf0d1f3bb3d579e667ce8e7ca03a0a0576a2d/Brotli-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61", size = 2918527 }, + { url = "https://files.pythonhosted.org/packages/b3/96/da98e7bedc4c51104d29cc61e5f449a502dd3dbc211944546a4cc65500d3/Brotli-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327", size = 2845489 }, + { url = "https://files.pythonhosted.org/packages/e8/ef/ccbc16947d6ce943a7f57e1a40596c75859eeb6d279c6994eddd69615265/Brotli-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd", size = 2914080 }, + { url = "https://files.pythonhosted.org/packages/80/d6/0bd38d758d1afa62a5524172f0b18626bb2392d717ff94806f741fcd5ee9/Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9", size = 2813051 }, + { url = "https://files.pythonhosted.org/packages/14/56/48859dd5d129d7519e001f06dcfbb6e2cf6db92b2702c0c2ce7d97e086c1/Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265", size = 2938172 }, + { url = "https://files.pythonhosted.org/packages/3d/77/a236d5f8cd9e9f4348da5acc75ab032ab1ab2c03cc8f430d24eea2672888/Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8", size = 2933023 }, + { url = "https://files.pythonhosted.org/packages/f1/87/3b283efc0f5cb35f7f84c0c240b1e1a1003a5e47141a4881bf87c86d0ce2/Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f", size = 2935871 }, + { url = "https://files.pythonhosted.org/packages/f3/eb/2be4cc3e2141dc1a43ad4ca1875a72088229de38c68e842746b342667b2a/Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757", size = 2847784 }, + { url = "https://files.pythonhosted.org/packages/66/13/b58ddebfd35edde572ccefe6890cf7c493f0c319aad2a5badee134b4d8ec/Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0", size = 3034905 }, + { url = "https://files.pythonhosted.org/packages/84/9c/bc96b6c7db824998a49ed3b38e441a2cae9234da6fa11f6ed17e8cf4f147/Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b", size = 2929467 }, + { url = "https://files.pythonhosted.org/packages/e7/71/8f161dee223c7ff7fea9d44893fba953ce97cf2c3c33f78ba260a91bcff5/Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50", size = 333169 }, + { url = "https://files.pythonhosted.org/packages/02/8a/fece0ee1057643cb2a5bbf59682de13f1725f8482b2c057d4e799d7ade75/Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1", size = 357253 }, + { url = "https://files.pythonhosted.org/packages/5c/d0/5373ae13b93fe00095a58efcbce837fd470ca39f703a235d2a999baadfbc/Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28", size = 815693 }, + { url = "https://files.pythonhosted.org/packages/8e/48/f6e1cdf86751300c288c1459724bfa6917a80e30dbfc326f92cea5d3683a/Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f", size = 422489 }, + { url = "https://files.pythonhosted.org/packages/06/88/564958cedce636d0f1bed313381dfc4b4e3d3f6015a63dae6146e1b8c65c/Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409", size = 873081 }, + { url = "https://files.pythonhosted.org/packages/58/79/b7026a8bb65da9a6bb7d14329fd2bd48d2b7f86d7329d5cc8ddc6a90526f/Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2", size = 446244 }, + { url = "https://files.pythonhosted.org/packages/e5/18/c18c32ecea41b6c0004e15606e274006366fe19436b6adccc1ae7b2e50c2/Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451", size = 2906505 }, + { url = "https://files.pythonhosted.org/packages/08/c8/69ec0496b1ada7569b62d85893d928e865df29b90736558d6c98c2031208/Brotli-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f4bf76817c14aa98cc6697ac02f3972cb8c3da93e9ef16b9c66573a68014f91", size = 2944152 }, + { url = "https://files.pythonhosted.org/packages/ab/fb/0517cea182219d6768113a38167ef6d4eb157a033178cc938033a552ed6d/Brotli-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0c5516f0aed654134a2fc936325cc2e642f8a0e096d075209672eb321cff408", size = 2919252 }, + { url = "https://files.pythonhosted.org/packages/c7/53/73a3431662e33ae61a5c80b1b9d2d18f58dfa910ae8dd696e57d39f1a2f5/Brotli-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c3020404e0b5eefd7c9485ccf8393cfb75ec38ce75586e046573c9dc29967a0", size = 2845955 }, + { url = "https://files.pythonhosted.org/packages/55/ac/bd280708d9c5ebdbf9de01459e625a3e3803cce0784f47d633562cf40e83/Brotli-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ed11165dd45ce798d99a136808a794a748d5dc38511303239d4e2363c0695dc", size = 2914304 }, + { url = "https://files.pythonhosted.org/packages/76/58/5c391b41ecfc4527d2cc3350719b02e87cb424ef8ba2023fb662f9bf743c/Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180", size = 2814452 }, + { url = "https://files.pythonhosted.org/packages/c7/4e/91b8256dfe99c407f174924b65a01f5305e303f486cc7a2e8a5d43c8bec3/Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248", size = 2938751 }, + { url = "https://files.pythonhosted.org/packages/5a/a6/e2a39a5d3b412938362bbbeba5af904092bf3f95b867b4a3eb856104074e/Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966", size = 2933757 }, + { url = "https://files.pythonhosted.org/packages/13/f0/358354786280a509482e0e77c1a5459e439766597d280f28cb097642fc26/Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9", size = 2936146 }, + { url = "https://files.pythonhosted.org/packages/80/f7/daf538c1060d3a88266b80ecc1d1c98b79553b3f117a485653f17070ea2a/Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb", size = 2848055 }, + { url = "https://files.pythonhosted.org/packages/ad/cf/0eaa0585c4077d3c2d1edf322d8e97aabf317941d3a72d7b3ad8bce004b0/Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111", size = 3035102 }, + { url = "https://files.pythonhosted.org/packages/d8/63/1c1585b2aa554fe6dbce30f0c18bdbc877fa9a1bf5ff17677d9cca0ac122/Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839", size = 2930029 }, + { url = "https://files.pythonhosted.org/packages/5f/3b/4e3fd1893eb3bbfef8e5a80d4508bec17a57bb92d586c85c12d28666bb13/Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0", size = 333276 }, + { url = "https://files.pythonhosted.org/packages/3d/d5/942051b45a9e883b5b6e98c041698b1eb2012d25e5948c58d6bf85b1bb43/Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951", size = 357255 }, + { url = "https://files.pythonhosted.org/packages/0a/9f/fb37bb8ffc52a8da37b1c03c459a8cd55df7a57bdccd8831d500e994a0ca/Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5", size = 815681 }, + { url = "https://files.pythonhosted.org/packages/06/b3/dbd332a988586fefb0aa49c779f59f47cae76855c2d00f450364bb574cac/Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8", size = 422475 }, + { url = "https://files.pythonhosted.org/packages/bb/80/6aaddc2f63dbcf2d93c2d204e49c11a9ec93a8c7c63261e2b4bd35198283/Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f", size = 2906173 }, + { url = "https://files.pythonhosted.org/packages/ea/1d/e6ca79c96ff5b641df6097d299347507d39a9604bde8915e76bf026d6c77/Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648", size = 2943803 }, + { url = "https://files.pythonhosted.org/packages/ac/a3/d98d2472e0130b7dd3acdbb7f390d478123dbf62b7d32bda5c830a96116d/Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0", size = 2918946 }, + { url = "https://files.pythonhosted.org/packages/c4/a5/c69e6d272aee3e1423ed005d8915a7eaa0384c7de503da987f2d224d0721/Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089", size = 2845707 }, + { url = "https://files.pythonhosted.org/packages/58/9f/4149d38b52725afa39067350696c09526de0125ebfbaab5acc5af28b42ea/Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368", size = 2936231 }, + { url = "https://files.pythonhosted.org/packages/5a/5a/145de884285611838a16bebfdb060c231c52b8f84dfbe52b852a15780386/Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c", size = 2848157 }, + { url = "https://files.pythonhosted.org/packages/50/ae/408b6bfb8525dadebd3b3dd5b19d631da4f7d46420321db44cd99dcf2f2c/Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284", size = 3035122 }, + { url = "https://files.pythonhosted.org/packages/af/85/a94e5cfaa0ca449d8f91c3d6f78313ebf919a0dbd55a100c711c6e9655bc/Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7", size = 2930206 }, + { url = "https://files.pythonhosted.org/packages/c2/f0/a61d9262cd01351df22e57ad7c34f66794709acab13f34be2675f45bf89d/Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0", size = 333804 }, + { url = "https://files.pythonhosted.org/packages/7e/c1/ec214e9c94000d1c1974ec67ced1c970c148aa6b8d8373066123fc3dbf06/Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b", size = 358517 }, ] [[package]] name = "certifi" version = "2023.11.17" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d4/91/c89518dd4fe1f3a4e3f6ab7ff23cb00ef2e8c9adf99dacc618ad5e068e28/certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1", size = 163637, upload-time = "2023-11-18T02:54:02.397Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/91/c89518dd4fe1f3a4e3f6ab7ff23cb00ef2e8c9adf99dacc618ad5e068e28/certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1", size = 163637 } wheels = [ - { url = "https://files.pythonhosted.org/packages/64/62/428ef076be88fa93716b576e4a01f919d25968913e817077a386fcbe4f42/certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474", size = 162530, upload-time = "2023-11-18T02:54:00.083Z" }, + { url = "https://files.pythonhosted.org/packages/64/62/428ef076be88fa93716b576e4a01f919d25968913e817077a386fcbe4f42/certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474", size = 162530 }, ] [[package]] @@ -207,108 +217,108 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pycparser" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621, upload-time = "2024-09-04T20:45:21.852Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 } wheels = [ - { url = "https://files.pythonhosted.org/packages/90/07/f44ca684db4e4f08a3fdc6eeb9a0d15dc6883efc7b8c90357fdbf74e186c/cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14", size = 182191, upload-time = "2024-09-04T20:43:30.027Z" }, - { url = "https://files.pythonhosted.org/packages/08/fd/cc2fedbd887223f9f5d170c96e57cbf655df9831a6546c1727ae13fa977a/cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67", size = 178592, upload-time = "2024-09-04T20:43:32.108Z" }, - { url = "https://files.pythonhosted.org/packages/de/cc/4635c320081c78d6ffc2cab0a76025b691a91204f4aa317d568ff9280a2d/cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382", size = 426024, upload-time = "2024-09-04T20:43:34.186Z" }, - { url = "https://files.pythonhosted.org/packages/b6/7b/3b2b250f3aab91abe5f8a51ada1b717935fdaec53f790ad4100fe2ec64d1/cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702", size = 448188, upload-time = "2024-09-04T20:43:36.286Z" }, - { url = "https://files.pythonhosted.org/packages/d3/48/1b9283ebbf0ec065148d8de05d647a986c5f22586b18120020452fff8f5d/cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3", size = 455571, upload-time = "2024-09-04T20:43:38.586Z" }, - { url = "https://files.pythonhosted.org/packages/40/87/3b8452525437b40f39ca7ff70276679772ee7e8b394934ff60e63b7b090c/cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6", size = 436687, upload-time = "2024-09-04T20:43:40.084Z" }, - { url = "https://files.pythonhosted.org/packages/8d/fb/4da72871d177d63649ac449aec2e8a29efe0274035880c7af59101ca2232/cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17", size = 446211, upload-time = "2024-09-04T20:43:41.526Z" }, - { url = "https://files.pythonhosted.org/packages/ab/a0/62f00bcb411332106c02b663b26f3545a9ef136f80d5df746c05878f8c4b/cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8", size = 461325, upload-time = "2024-09-04T20:43:43.117Z" }, - { url = "https://files.pythonhosted.org/packages/36/83/76127035ed2e7e27b0787604d99da630ac3123bfb02d8e80c633f218a11d/cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e", size = 438784, upload-time = "2024-09-04T20:43:45.256Z" }, - { url = "https://files.pythonhosted.org/packages/21/81/a6cd025db2f08ac88b901b745c163d884641909641f9b826e8cb87645942/cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be", size = 461564, upload-time = "2024-09-04T20:43:46.779Z" }, - { url = "https://files.pythonhosted.org/packages/f8/fe/4d41c2f200c4a457933dbd98d3cf4e911870877bd94d9656cc0fcb390681/cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c", size = 171804, upload-time = "2024-09-04T20:43:48.186Z" }, - { url = "https://files.pythonhosted.org/packages/d1/b6/0b0f5ab93b0df4acc49cae758c81fe4e5ef26c3ae2e10cc69249dfd8b3ab/cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15", size = 181299, upload-time = "2024-09-04T20:43:49.812Z" }, - { url = "https://files.pythonhosted.org/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", size = 182264, upload-time = "2024-09-04T20:43:51.124Z" }, - { url = "https://files.pythonhosted.org/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", size = 178651, upload-time = "2024-09-04T20:43:52.872Z" }, - { url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259, upload-time = "2024-09-04T20:43:56.123Z" }, - { url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200, upload-time = "2024-09-04T20:43:57.891Z" }, - { url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235, upload-time = "2024-09-04T20:44:00.18Z" }, - { url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721, upload-time = "2024-09-04T20:44:01.585Z" }, - { url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242, upload-time = "2024-09-04T20:44:03.467Z" }, - { url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999, upload-time = "2024-09-04T20:44:05.023Z" }, - { url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242, upload-time = "2024-09-04T20:44:06.444Z" }, - { url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604, upload-time = "2024-09-04T20:44:08.206Z" }, - { url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727, upload-time = "2024-09-04T20:44:09.481Z" }, - { url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400, upload-time = "2024-09-04T20:44:10.873Z" }, - { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178, upload-time = "2024-09-04T20:44:12.232Z" }, - { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840, upload-time = "2024-09-04T20:44:13.739Z" }, - { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803, upload-time = "2024-09-04T20:44:15.231Z" }, - { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850, upload-time = "2024-09-04T20:44:17.188Z" }, - { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729, upload-time = "2024-09-04T20:44:18.688Z" }, - { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256, upload-time = "2024-09-04T20:44:20.248Z" }, - { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424, upload-time = "2024-09-04T20:44:21.673Z" }, - { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568, upload-time = "2024-09-04T20:44:23.245Z" }, - { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736, upload-time = "2024-09-04T20:44:24.757Z" }, - { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448, upload-time = "2024-09-04T20:44:26.208Z" }, - { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976, upload-time = "2024-09-04T20:44:27.578Z" }, - { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989, upload-time = "2024-09-04T20:44:28.956Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802, upload-time = "2024-09-04T20:44:30.289Z" }, - { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792, upload-time = "2024-09-04T20:44:32.01Z" }, - { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893, upload-time = "2024-09-04T20:44:33.606Z" }, - { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810, upload-time = "2024-09-04T20:44:35.191Z" }, - { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200, upload-time = "2024-09-04T20:44:36.743Z" }, - { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447, upload-time = "2024-09-04T20:44:38.492Z" }, - { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358, upload-time = "2024-09-04T20:44:40.046Z" }, - { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469, upload-time = "2024-09-04T20:44:41.616Z" }, - { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475, upload-time = "2024-09-04T20:44:43.733Z" }, - { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009, upload-time = "2024-09-04T20:44:45.309Z" }, + { url = "https://files.pythonhosted.org/packages/90/07/f44ca684db4e4f08a3fdc6eeb9a0d15dc6883efc7b8c90357fdbf74e186c/cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14", size = 182191 }, + { url = "https://files.pythonhosted.org/packages/08/fd/cc2fedbd887223f9f5d170c96e57cbf655df9831a6546c1727ae13fa977a/cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67", size = 178592 }, + { url = "https://files.pythonhosted.org/packages/de/cc/4635c320081c78d6ffc2cab0a76025b691a91204f4aa317d568ff9280a2d/cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382", size = 426024 }, + { url = "https://files.pythonhosted.org/packages/b6/7b/3b2b250f3aab91abe5f8a51ada1b717935fdaec53f790ad4100fe2ec64d1/cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702", size = 448188 }, + { url = "https://files.pythonhosted.org/packages/d3/48/1b9283ebbf0ec065148d8de05d647a986c5f22586b18120020452fff8f5d/cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3", size = 455571 }, + { url = "https://files.pythonhosted.org/packages/40/87/3b8452525437b40f39ca7ff70276679772ee7e8b394934ff60e63b7b090c/cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6", size = 436687 }, + { url = "https://files.pythonhosted.org/packages/8d/fb/4da72871d177d63649ac449aec2e8a29efe0274035880c7af59101ca2232/cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17", size = 446211 }, + { url = "https://files.pythonhosted.org/packages/ab/a0/62f00bcb411332106c02b663b26f3545a9ef136f80d5df746c05878f8c4b/cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8", size = 461325 }, + { url = "https://files.pythonhosted.org/packages/36/83/76127035ed2e7e27b0787604d99da630ac3123bfb02d8e80c633f218a11d/cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e", size = 438784 }, + { url = "https://files.pythonhosted.org/packages/21/81/a6cd025db2f08ac88b901b745c163d884641909641f9b826e8cb87645942/cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be", size = 461564 }, + { url = "https://files.pythonhosted.org/packages/f8/fe/4d41c2f200c4a457933dbd98d3cf4e911870877bd94d9656cc0fcb390681/cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c", size = 171804 }, + { url = "https://files.pythonhosted.org/packages/d1/b6/0b0f5ab93b0df4acc49cae758c81fe4e5ef26c3ae2e10cc69249dfd8b3ab/cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15", size = 181299 }, + { url = "https://files.pythonhosted.org/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", size = 182264 }, + { url = "https://files.pythonhosted.org/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", size = 178651 }, + { url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259 }, + { url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200 }, + { url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235 }, + { url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721 }, + { url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242 }, + { url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999 }, + { url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242 }, + { url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604 }, + { url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727 }, + { url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400 }, + { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178 }, + { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840 }, + { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803 }, + { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850 }, + { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729 }, + { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256 }, + { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424 }, + { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568 }, + { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736 }, + { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448 }, + { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976 }, + { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989 }, + { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802 }, + { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792 }, + { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893 }, + { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810 }, + { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200 }, + { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447 }, + { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358 }, + { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469 }, + { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475 }, + { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 }, ] [[package]] name = "charset-normalizer" version = "3.3.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809, upload-time = "2023-11-01T04:04:59.997Z" } +sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809 } wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/61/095a0aa1a84d1481998b534177c8566fdc50bb1233ea9a0478cd3cc075bd/charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", size = 194219, upload-time = "2023-11-01T04:02:29.048Z" }, - { url = "https://files.pythonhosted.org/packages/cc/94/f7cf5e5134175de79ad2059edf2adce18e0685ebdb9227ff0139975d0e93/charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", size = 122521, upload-time = "2023-11-01T04:02:32.452Z" }, - { url = "https://files.pythonhosted.org/packages/46/6a/d5c26c41c49b546860cc1acabdddf48b0b3fb2685f4f5617ac59261b44ae/charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", size = 120383, upload-time = "2023-11-01T04:02:34.11Z" }, - { url = "https://files.pythonhosted.org/packages/b8/60/e2f67915a51be59d4539ed189eb0a2b0d292bf79270410746becb32bc2c3/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", size = 138223, upload-time = "2023-11-01T04:02:36.213Z" }, - { url = "https://files.pythonhosted.org/packages/05/8c/eb854996d5fef5e4f33ad56927ad053d04dc820e4a3d39023f35cad72617/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", size = 148101, upload-time = "2023-11-01T04:02:38.067Z" }, - { url = "https://files.pythonhosted.org/packages/f6/93/bb6cbeec3bf9da9b2eba458c15966658d1daa8b982c642f81c93ad9b40e1/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", size = 140699, upload-time = "2023-11-01T04:02:39.436Z" }, - { url = "https://files.pythonhosted.org/packages/da/f1/3702ba2a7470666a62fd81c58a4c40be00670e5006a67f4d626e57f013ae/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", size = 142065, upload-time = "2023-11-01T04:02:41.357Z" }, - { url = "https://files.pythonhosted.org/packages/3f/ba/3f5e7be00b215fa10e13d64b1f6237eb6ebea66676a41b2bcdd09fe74323/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", size = 144505, upload-time = "2023-11-01T04:02:43.108Z" }, - { url = "https://files.pythonhosted.org/packages/33/c3/3b96a435c5109dd5b6adc8a59ba1d678b302a97938f032e3770cc84cd354/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", size = 139425, upload-time = "2023-11-01T04:02:45.427Z" }, - { url = "https://files.pythonhosted.org/packages/43/05/3bf613e719efe68fb3a77f9c536a389f35b95d75424b96b426a47a45ef1d/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", size = 145287, upload-time = "2023-11-01T04:02:46.705Z" }, - { url = "https://files.pythonhosted.org/packages/58/78/a0bc646900994df12e07b4ae5c713f2b3e5998f58b9d3720cce2aa45652f/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", size = 149929, upload-time = "2023-11-01T04:02:48.098Z" }, - { url = "https://files.pythonhosted.org/packages/eb/5c/97d97248af4920bc68687d9c3b3c0f47c910e21a8ff80af4565a576bd2f0/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", size = 141605, upload-time = "2023-11-01T04:02:49.605Z" }, - { url = "https://files.pythonhosted.org/packages/a8/31/47d018ef89f95b8aded95c589a77c072c55e94b50a41aa99c0a2008a45a4/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", size = 142646, upload-time = "2023-11-01T04:02:51.35Z" }, - { url = "https://files.pythonhosted.org/packages/ae/d5/4fecf1d58bedb1340a50f165ba1c7ddc0400252d6832ff619c4568b36cc0/charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", size = 92846, upload-time = "2023-11-01T04:02:52.679Z" }, - { url = "https://files.pythonhosted.org/packages/a2/a0/4af29e22cb5942488cf45630cbdd7cefd908768e69bdd90280842e4e8529/charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", size = 100343, upload-time = "2023-11-01T04:02:53.915Z" }, - { url = "https://files.pythonhosted.org/packages/68/77/02839016f6fbbf808e8b38601df6e0e66c17bbab76dff4613f7511413597/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", size = 191647, upload-time = "2023-11-01T04:02:55.329Z" }, - { url = "https://files.pythonhosted.org/packages/3e/33/21a875a61057165e92227466e54ee076b73af1e21fe1b31f1e292251aa1e/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", size = 121434, upload-time = "2023-11-01T04:02:57.173Z" }, - { url = "https://files.pythonhosted.org/packages/dd/51/68b61b90b24ca35495956b718f35a9756ef7d3dd4b3c1508056fa98d1a1b/charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", size = 118979, upload-time = "2023-11-01T04:02:58.442Z" }, - { url = "https://files.pythonhosted.org/packages/e4/a6/7ee57823d46331ddc37dd00749c95b0edec2c79b15fc0d6e6efb532e89ac/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", size = 136582, upload-time = "2023-11-01T04:02:59.776Z" }, - { url = "https://files.pythonhosted.org/packages/74/f1/0d9fe69ac441467b737ba7f48c68241487df2f4522dd7246d9426e7c690e/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", size = 146645, upload-time = "2023-11-01T04:03:02.186Z" }, - { url = "https://files.pythonhosted.org/packages/05/31/e1f51c76db7be1d4aef220d29fbfa5dbb4a99165d9833dcbf166753b6dc0/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", size = 139398, upload-time = "2023-11-01T04:03:04.255Z" }, - { url = "https://files.pythonhosted.org/packages/40/26/f35951c45070edc957ba40a5b1db3cf60a9dbb1b350c2d5bef03e01e61de/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", size = 140273, upload-time = "2023-11-01T04:03:05.983Z" }, - { url = "https://files.pythonhosted.org/packages/07/07/7e554f2bbce3295e191f7e653ff15d55309a9ca40d0362fcdab36f01063c/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", size = 142577, upload-time = "2023-11-01T04:03:07.567Z" }, - { url = "https://files.pythonhosted.org/packages/d8/b5/eb705c313100defa57da79277d9207dc8d8e45931035862fa64b625bfead/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", size = 137747, upload-time = "2023-11-01T04:03:08.886Z" }, - { url = "https://files.pythonhosted.org/packages/19/28/573147271fd041d351b438a5665be8223f1dd92f273713cb882ddafe214c/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", size = 143375, upload-time = "2023-11-01T04:03:10.613Z" }, - { url = "https://files.pythonhosted.org/packages/cf/7c/f3b682fa053cc21373c9a839e6beba7705857075686a05c72e0f8c4980ca/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", size = 148474, upload-time = "2023-11-01T04:03:11.973Z" }, - { url = "https://files.pythonhosted.org/packages/1e/49/7ab74d4ac537ece3bc3334ee08645e231f39f7d6df6347b29a74b0537103/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", size = 140232, upload-time = "2023-11-01T04:03:13.505Z" }, - { url = "https://files.pythonhosted.org/packages/2d/dc/9dacba68c9ac0ae781d40e1a0c0058e26302ea0660e574ddf6797a0347f7/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", size = 140859, upload-time = "2023-11-01T04:03:17.362Z" }, - { url = "https://files.pythonhosted.org/packages/6c/c2/4a583f800c0708dd22096298e49f887b49d9746d0e78bfc1d7e29816614c/charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", size = 92509, upload-time = "2023-11-01T04:03:21.453Z" }, - { url = "https://files.pythonhosted.org/packages/57/ec/80c8d48ac8b1741d5b963797b7c0c869335619e13d4744ca2f67fc11c6fc/charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", size = 99870, upload-time = "2023-11-01T04:03:22.723Z" }, - { url = "https://files.pythonhosted.org/packages/d1/b2/fcedc8255ec42afee97f9e6f0145c734bbe104aac28300214593eb326f1d/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", size = 192892, upload-time = "2023-11-01T04:03:24.135Z" }, - { url = "https://files.pythonhosted.org/packages/2e/7d/2259318c202f3d17f3fe6438149b3b9e706d1070fe3fcbb28049730bb25c/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", size = 122213, upload-time = "2023-11-01T04:03:25.66Z" }, - { url = "https://files.pythonhosted.org/packages/3a/52/9f9d17c3b54dc238de384c4cb5a2ef0e27985b42a0e5cc8e8a31d918d48d/charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", size = 119404, upload-time = "2023-11-01T04:03:27.04Z" }, - { url = "https://files.pythonhosted.org/packages/99/b0/9c365f6d79a9f0f3c379ddb40a256a67aa69c59609608fe7feb6235896e1/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", size = 137275, upload-time = "2023-11-01T04:03:28.466Z" }, - { url = "https://files.pythonhosted.org/packages/91/33/749df346e93d7a30cdcb90cbfdd41a06026317bfbfb62cd68307c1a3c543/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", size = 147518, upload-time = "2023-11-01T04:03:29.82Z" }, - { url = "https://files.pythonhosted.org/packages/72/1a/641d5c9f59e6af4c7b53da463d07600a695b9824e20849cb6eea8a627761/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", size = 140182, upload-time = "2023-11-01T04:03:31.511Z" }, - { url = "https://files.pythonhosted.org/packages/ee/fb/14d30eb4956408ee3ae09ad34299131fb383c47df355ddb428a7331cfa1e/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", size = 141869, upload-time = "2023-11-01T04:03:32.887Z" }, - { url = "https://files.pythonhosted.org/packages/df/3e/a06b18788ca2eb6695c9b22325b6fde7dde0f1d1838b1792a0076f58fe9d/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", size = 144042, upload-time = "2023-11-01T04:03:34.412Z" }, - { url = "https://files.pythonhosted.org/packages/45/59/3d27019d3b447a88fe7e7d004a1e04be220227760264cc41b405e863891b/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", size = 138275, upload-time = "2023-11-01T04:03:35.759Z" }, - { url = "https://files.pythonhosted.org/packages/7b/ef/5eb105530b4da8ae37d506ccfa25057961b7b63d581def6f99165ea89c7e/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", size = 144819, upload-time = "2023-11-01T04:03:37.216Z" }, - { url = "https://files.pythonhosted.org/packages/a2/51/e5023f937d7f307c948ed3e5c29c4b7a3e42ed2ee0b8cdf8f3a706089bf0/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", size = 149415, upload-time = "2023-11-01T04:03:38.694Z" }, - { url = "https://files.pythonhosted.org/packages/24/9d/2e3ef673dfd5be0154b20363c5cdcc5606f35666544381bee15af3778239/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", size = 141212, upload-time = "2023-11-01T04:03:40.07Z" }, - { url = "https://files.pythonhosted.org/packages/5b/ae/ce2c12fcac59cb3860b2e2d76dc405253a4475436b1861d95fe75bdea520/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", size = 142167, upload-time = "2023-11-01T04:03:41.491Z" }, - { url = "https://files.pythonhosted.org/packages/ed/3a/a448bf035dce5da359daf9ae8a16b8a39623cc395a2ffb1620aa1bce62b0/charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", size = 93041, upload-time = "2023-11-01T04:03:42.836Z" }, - { url = "https://files.pythonhosted.org/packages/b6/7c/8debebb4f90174074b827c63242c23851bdf00a532489fba57fef3416e40/charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", size = 100397, upload-time = "2023-11-01T04:03:44.467Z" }, - { url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543, upload-time = "2023-11-01T04:04:58.622Z" }, + { url = "https://files.pythonhosted.org/packages/2b/61/095a0aa1a84d1481998b534177c8566fdc50bb1233ea9a0478cd3cc075bd/charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", size = 194219 }, + { url = "https://files.pythonhosted.org/packages/cc/94/f7cf5e5134175de79ad2059edf2adce18e0685ebdb9227ff0139975d0e93/charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", size = 122521 }, + { url = "https://files.pythonhosted.org/packages/46/6a/d5c26c41c49b546860cc1acabdddf48b0b3fb2685f4f5617ac59261b44ae/charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", size = 120383 }, + { url = "https://files.pythonhosted.org/packages/b8/60/e2f67915a51be59d4539ed189eb0a2b0d292bf79270410746becb32bc2c3/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", size = 138223 }, + { url = "https://files.pythonhosted.org/packages/05/8c/eb854996d5fef5e4f33ad56927ad053d04dc820e4a3d39023f35cad72617/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", size = 148101 }, + { url = "https://files.pythonhosted.org/packages/f6/93/bb6cbeec3bf9da9b2eba458c15966658d1daa8b982c642f81c93ad9b40e1/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", size = 140699 }, + { url = "https://files.pythonhosted.org/packages/da/f1/3702ba2a7470666a62fd81c58a4c40be00670e5006a67f4d626e57f013ae/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", size = 142065 }, + { url = "https://files.pythonhosted.org/packages/3f/ba/3f5e7be00b215fa10e13d64b1f6237eb6ebea66676a41b2bcdd09fe74323/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", size = 144505 }, + { url = "https://files.pythonhosted.org/packages/33/c3/3b96a435c5109dd5b6adc8a59ba1d678b302a97938f032e3770cc84cd354/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", size = 139425 }, + { url = "https://files.pythonhosted.org/packages/43/05/3bf613e719efe68fb3a77f9c536a389f35b95d75424b96b426a47a45ef1d/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", size = 145287 }, + { url = "https://files.pythonhosted.org/packages/58/78/a0bc646900994df12e07b4ae5c713f2b3e5998f58b9d3720cce2aa45652f/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", size = 149929 }, + { url = "https://files.pythonhosted.org/packages/eb/5c/97d97248af4920bc68687d9c3b3c0f47c910e21a8ff80af4565a576bd2f0/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", size = 141605 }, + { url = "https://files.pythonhosted.org/packages/a8/31/47d018ef89f95b8aded95c589a77c072c55e94b50a41aa99c0a2008a45a4/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", size = 142646 }, + { url = "https://files.pythonhosted.org/packages/ae/d5/4fecf1d58bedb1340a50f165ba1c7ddc0400252d6832ff619c4568b36cc0/charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", size = 92846 }, + { url = "https://files.pythonhosted.org/packages/a2/a0/4af29e22cb5942488cf45630cbdd7cefd908768e69bdd90280842e4e8529/charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", size = 100343 }, + { url = "https://files.pythonhosted.org/packages/68/77/02839016f6fbbf808e8b38601df6e0e66c17bbab76dff4613f7511413597/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", size = 191647 }, + { url = "https://files.pythonhosted.org/packages/3e/33/21a875a61057165e92227466e54ee076b73af1e21fe1b31f1e292251aa1e/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", size = 121434 }, + { url = "https://files.pythonhosted.org/packages/dd/51/68b61b90b24ca35495956b718f35a9756ef7d3dd4b3c1508056fa98d1a1b/charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", size = 118979 }, + { url = "https://files.pythonhosted.org/packages/e4/a6/7ee57823d46331ddc37dd00749c95b0edec2c79b15fc0d6e6efb532e89ac/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", size = 136582 }, + { url = "https://files.pythonhosted.org/packages/74/f1/0d9fe69ac441467b737ba7f48c68241487df2f4522dd7246d9426e7c690e/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", size = 146645 }, + { url = "https://files.pythonhosted.org/packages/05/31/e1f51c76db7be1d4aef220d29fbfa5dbb4a99165d9833dcbf166753b6dc0/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", size = 139398 }, + { url = "https://files.pythonhosted.org/packages/40/26/f35951c45070edc957ba40a5b1db3cf60a9dbb1b350c2d5bef03e01e61de/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", size = 140273 }, + { url = "https://files.pythonhosted.org/packages/07/07/7e554f2bbce3295e191f7e653ff15d55309a9ca40d0362fcdab36f01063c/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", size = 142577 }, + { url = "https://files.pythonhosted.org/packages/d8/b5/eb705c313100defa57da79277d9207dc8d8e45931035862fa64b625bfead/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", size = 137747 }, + { url = "https://files.pythonhosted.org/packages/19/28/573147271fd041d351b438a5665be8223f1dd92f273713cb882ddafe214c/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", size = 143375 }, + { url = "https://files.pythonhosted.org/packages/cf/7c/f3b682fa053cc21373c9a839e6beba7705857075686a05c72e0f8c4980ca/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", size = 148474 }, + { url = "https://files.pythonhosted.org/packages/1e/49/7ab74d4ac537ece3bc3334ee08645e231f39f7d6df6347b29a74b0537103/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", size = 140232 }, + { url = "https://files.pythonhosted.org/packages/2d/dc/9dacba68c9ac0ae781d40e1a0c0058e26302ea0660e574ddf6797a0347f7/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", size = 140859 }, + { url = "https://files.pythonhosted.org/packages/6c/c2/4a583f800c0708dd22096298e49f887b49d9746d0e78bfc1d7e29816614c/charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", size = 92509 }, + { url = "https://files.pythonhosted.org/packages/57/ec/80c8d48ac8b1741d5b963797b7c0c869335619e13d4744ca2f67fc11c6fc/charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", size = 99870 }, + { url = "https://files.pythonhosted.org/packages/d1/b2/fcedc8255ec42afee97f9e6f0145c734bbe104aac28300214593eb326f1d/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", size = 192892 }, + { url = "https://files.pythonhosted.org/packages/2e/7d/2259318c202f3d17f3fe6438149b3b9e706d1070fe3fcbb28049730bb25c/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", size = 122213 }, + { url = "https://files.pythonhosted.org/packages/3a/52/9f9d17c3b54dc238de384c4cb5a2ef0e27985b42a0e5cc8e8a31d918d48d/charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", size = 119404 }, + { url = "https://files.pythonhosted.org/packages/99/b0/9c365f6d79a9f0f3c379ddb40a256a67aa69c59609608fe7feb6235896e1/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", size = 137275 }, + { url = "https://files.pythonhosted.org/packages/91/33/749df346e93d7a30cdcb90cbfdd41a06026317bfbfb62cd68307c1a3c543/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", size = 147518 }, + { url = "https://files.pythonhosted.org/packages/72/1a/641d5c9f59e6af4c7b53da463d07600a695b9824e20849cb6eea8a627761/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", size = 140182 }, + { url = "https://files.pythonhosted.org/packages/ee/fb/14d30eb4956408ee3ae09ad34299131fb383c47df355ddb428a7331cfa1e/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", size = 141869 }, + { url = "https://files.pythonhosted.org/packages/df/3e/a06b18788ca2eb6695c9b22325b6fde7dde0f1d1838b1792a0076f58fe9d/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", size = 144042 }, + { url = "https://files.pythonhosted.org/packages/45/59/3d27019d3b447a88fe7e7d004a1e04be220227760264cc41b405e863891b/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", size = 138275 }, + { url = "https://files.pythonhosted.org/packages/7b/ef/5eb105530b4da8ae37d506ccfa25057961b7b63d581def6f99165ea89c7e/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", size = 144819 }, + { url = "https://files.pythonhosted.org/packages/a2/51/e5023f937d7f307c948ed3e5c29c4b7a3e42ed2ee0b8cdf8f3a706089bf0/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", size = 149415 }, + { url = "https://files.pythonhosted.org/packages/24/9d/2e3ef673dfd5be0154b20363c5cdcc5606f35666544381bee15af3778239/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", size = 141212 }, + { url = "https://files.pythonhosted.org/packages/5b/ae/ce2c12fcac59cb3860b2e2d76dc405253a4475436b1861d95fe75bdea520/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", size = 142167 }, + { url = "https://files.pythonhosted.org/packages/ed/3a/a448bf035dce5da359daf9ae8a16b8a39623cc395a2ffb1620aa1bce62b0/charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", size = 93041 }, + { url = "https://files.pythonhosted.org/packages/b6/7c/8debebb4f90174074b827c63242c23851bdf00a532489fba57fef3416e40/charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", size = 100397 }, + { url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543 }, ] [[package]] @@ -318,18 +328,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121, upload-time = "2023-08-17T17:29:11.868Z" } +sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941, upload-time = "2023-08-17T17:29:10.08Z" }, + { url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941 }, ] [[package]] name = "colorama" version = "0.4.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, ] [[package]] @@ -339,118 +349,219 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "humanfriendly" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0", size = 278520, upload-time = "2021-06-11T10:22:45.202Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0", size = 278520 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", size = 46018, upload-time = "2021-06-11T10:22:42.561Z" }, + { url = "https://files.pythonhosted.org/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", size = 46018 }, ] [[package]] name = "configargparse" version = "1.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/4d/6c9ef746dfcc2a32e26f3860bb4a011c008c392b83eabdfb598d1a8bbe5d/configargparse-1.7.1.tar.gz", hash = "sha256:79c2ddae836a1e5914b71d58e4b9adbd9f7779d4e6351a637b7d2d9b6c46d3d9", size = 43958, upload-time = "2025-05-23T14:26:17.369Z" } +sdist = { url = "https://files.pythonhosted.org/packages/85/4d/6c9ef746dfcc2a32e26f3860bb4a011c008c392b83eabdfb598d1a8bbe5d/configargparse-1.7.1.tar.gz", hash = "sha256:79c2ddae836a1e5914b71d58e4b9adbd9f7779d4e6351a637b7d2d9b6c46d3d9", size = 43958 } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/28/d28211d29bcc3620b1fece85a65ce5bb22f18670a03cd28ea4b75ede270c/configargparse-1.7.1-py3-none-any.whl", hash = "sha256:8b586a31f9d873abd1ca527ffbe58863c99f36d896e2829779803125e83be4b6", size = 25607, upload-time = "2025-05-23T14:26:15.923Z" }, + { url = "https://files.pythonhosted.org/packages/31/28/d28211d29bcc3620b1fece85a65ce5bb22f18670a03cd28ea4b75ede270c/configargparse-1.7.1-py3-none-any.whl", hash = "sha256:8b586a31f9d873abd1ca527ffbe58863c99f36d896e2829779803125e83be4b6", size = 25607 }, ] [[package]] name = "contourpy" version = "1.2.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, +resolution-markers = [ + "python_full_version < '3.11' and sys_platform == 'darwin'", + "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')", ] -sdist = { url = "https://files.pythonhosted.org/packages/11/a3/48ddc7ae832b000952cf4be64452381d150a41a2299c2eb19237168528d1/contourpy-1.2.0.tar.gz", hash = "sha256:171f311cb758de7da13fc53af221ae47a5877be5a0843a9fe150818c51ed276a", size = 13455881, upload-time = "2023-11-03T17:01:03.144Z" } +dependencies = [ + { name = "numpy", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/11/a3/48ddc7ae832b000952cf4be64452381d150a41a2299c2eb19237168528d1/contourpy-1.2.0.tar.gz", hash = "sha256:171f311cb758de7da13fc53af221ae47a5877be5a0843a9fe150818c51ed276a", size = 13455881 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/ea/f6e90933d82cc5aacf52f886a1c01f47f96eba99108ca2929c7b3ef45f82/contourpy-1.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0274c1cb63625972c0c007ab14dd9ba9e199c36ae1a231ce45d725cbcbfd10a8", size = 256873, upload-time = "2023-11-03T16:56:34.548Z" }, - { url = "https://files.pythonhosted.org/packages/fe/26/43821d61b7ee62c1809ec852bc572aaf4c27f101ebcebbbcce29a5ee0445/contourpy-1.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab459a1cbbf18e8698399c595a01f6dcc5c138220ca3ea9e7e6126232d102bb4", size = 242211, upload-time = "2023-11-03T16:56:38.028Z" }, - { url = "https://files.pythonhosted.org/packages/9b/99/c8fb63072a7573fe7682e1786a021f29f9c5f660a3aafcdce80b9ee8348d/contourpy-1.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fdd887f17c2f4572ce548461e4f96396681212d858cae7bd52ba3310bc6f00f", size = 293195, upload-time = "2023-11-03T16:56:41.598Z" }, - { url = "https://files.pythonhosted.org/packages/c7/a7/ae0b4bb8e0c865270d02ee619981413996dc10ddf1fd2689c938173ff62f/contourpy-1.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d16edfc3fc09968e09ddffada434b3bf989bf4911535e04eada58469873e28e", size = 332279, upload-time = "2023-11-03T16:56:46.08Z" }, - { url = "https://files.pythonhosted.org/packages/94/7c/682228b9085ff323fb7e946fe139072e5f21b71360cf91f36ea079d4ea95/contourpy-1.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c203f617abc0dde5792beb586f827021069fb6d403d7f4d5c2b543d87edceb9", size = 305326, upload-time = "2023-11-03T16:56:49.647Z" }, - { url = "https://files.pythonhosted.org/packages/58/56/e2c43dcfa1f9c7db4d5e3d6f5134b24ed953f4e2133a4b12f0062148db58/contourpy-1.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b69303ceb2e4d4f146bf82fda78891ef7bcd80c41bf16bfca3d0d7eb545448aa", size = 310732, upload-time = "2023-11-03T16:56:53.773Z" }, - { url = "https://files.pythonhosted.org/packages/94/0b/8495c4582057abc8377f945f6e11a86f1c07ad7b32fd4fdc968478cd0324/contourpy-1.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:884c3f9d42d7218304bc74a8a7693d172685c84bd7ab2bab1ee567b769696df9", size = 803420, upload-time = "2023-11-03T16:57:00.669Z" }, - { url = "https://files.pythonhosted.org/packages/d5/1f/40399c7da649297147d404aedaa675cc60018f48ad284630c0d1406133e3/contourpy-1.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4a1b1208102be6e851f20066bf0e7a96b7d48a07c9b0cfe6d0d4545c2f6cadab", size = 829204, upload-time = "2023-11-03T16:57:07.813Z" }, - { url = "https://files.pythonhosted.org/packages/8b/01/4be433b60dce7cbce8315cbcdfc016e7d25430a8b94e272355dff79cc3a8/contourpy-1.2.0-cp310-cp310-win32.whl", hash = "sha256:34b9071c040d6fe45d9826cbbe3727d20d83f1b6110d219b83eb0e2a01d79488", size = 165434, upload-time = "2023-11-03T16:57:10.601Z" }, - { url = "https://files.pythonhosted.org/packages/fd/7c/168f8343f33d861305e18c56901ef1bb675d3c7f977f435ec72751a71a54/contourpy-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:bd2f1ae63998da104f16a8b788f685e55d65760cd1929518fd94cd682bf03e41", size = 186652, upload-time = "2023-11-03T16:57:13.57Z" }, - { url = "https://files.pythonhosted.org/packages/9b/54/1dafec3c84df1d29119037330f7289db84a679cb2d5283af4ef24d89f532/contourpy-1.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dd10c26b4eadae44783c45ad6655220426f971c61d9b239e6f7b16d5cdaaa727", size = 258243, upload-time = "2023-11-03T16:57:16.604Z" }, - { url = "https://files.pythonhosted.org/packages/5b/ac/26fa1057f62beaa2af4c55c6ac733b114a403b746cfe0ce3dc6e4aec921a/contourpy-1.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5c6b28956b7b232ae801406e529ad7b350d3f09a4fde958dfdf3c0520cdde0dd", size = 243408, upload-time = "2023-11-03T16:57:20.021Z" }, - { url = "https://files.pythonhosted.org/packages/b7/33/cd0ecc80123f499d76d2fe2807cb4d5638ef8730735c580c8a8a03e1928e/contourpy-1.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebeac59e9e1eb4b84940d076d9f9a6cec0064e241818bcb6e32124cc5c3e377a", size = 294142, upload-time = "2023-11-03T16:57:23.48Z" }, - { url = "https://files.pythonhosted.org/packages/6d/75/1b7bf20bf6394e01df2c4b4b3d44d3dc280c16ddaff72724639100bd4314/contourpy-1.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:139d8d2e1c1dd52d78682f505e980f592ba53c9f73bd6be102233e358b401063", size = 333129, upload-time = "2023-11-03T16:57:27.141Z" }, - { url = "https://files.pythonhosted.org/packages/22/5b/fedd961dff1877e5d3b83c5201295cfdcdc2438884c2851aa7ecf6cec045/contourpy-1.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e9dc350fb4c58adc64df3e0703ab076f60aac06e67d48b3848c23647ae4310e", size = 307461, upload-time = "2023-11-03T16:57:30.537Z" }, - { url = "https://files.pythonhosted.org/packages/e2/83/29a63bbc72839cc6b24b5a0e3d004d4ed4e8439f26460ad9a34e39251904/contourpy-1.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18fc2b4ed8e4a8fe849d18dce4bd3c7ea637758c6343a1f2bae1e9bd4c9f4686", size = 313352, upload-time = "2023-11-03T16:57:34.937Z" }, - { url = "https://files.pythonhosted.org/packages/4b/c7/4bac0fc4f1e802ab47e75076d83d2e1448e0668ba6cc9000cf4e9d5bd94a/contourpy-1.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:16a7380e943a6d52472096cb7ad5264ecee36ed60888e2a3d3814991a0107286", size = 804127, upload-time = "2023-11-03T16:57:42.201Z" }, - { url = "https://files.pythonhosted.org/packages/e3/47/b3fd5bdc2f6ec13502d57a5bc390ffe62648605ed1689c93b0015150a784/contourpy-1.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8d8faf05be5ec8e02a4d86f616fc2a0322ff4a4ce26c0f09d9f7fb5330a35c95", size = 829561, upload-time = "2023-11-03T16:57:49.667Z" }, - { url = "https://files.pythonhosted.org/packages/5c/04/be16038e754169caea4d02d82f8e5cd97dece593e5ac9e05735da0afd0c5/contourpy-1.2.0-cp311-cp311-win32.whl", hash = "sha256:67b7f17679fa62ec82b7e3e611c43a016b887bd64fb933b3ae8638583006c6d6", size = 166197, upload-time = "2023-11-03T16:57:52.682Z" }, - { url = "https://files.pythonhosted.org/packages/ca/2a/d197a412ec474391ee878b1218cf2fe9c6e963903755887fc5654c06636a/contourpy-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:99ad97258985328b4f207a5e777c1b44a83bfe7cf1f87b99f9c11d4ee477c4de", size = 187556, upload-time = "2023-11-03T16:57:55.286Z" }, - { url = "https://files.pythonhosted.org/packages/4f/03/839da46999173226bead08794cbd7b4d37c9e6b02686ca74c93556b43258/contourpy-1.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:575bcaf957a25d1194903a10bc9f316c136c19f24e0985a2b9b5608bdf5dbfe0", size = 259253, upload-time = "2023-11-03T16:57:58.572Z" }, - { url = "https://files.pythonhosted.org/packages/f3/9e/8fb3f53144269d3fecdd8786d3a4686eeff55b9b35a3c0772a3f62f71e36/contourpy-1.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9e6c93b5b2dbcedad20a2f18ec22cae47da0d705d454308063421a3b290d9ea4", size = 242555, upload-time = "2023-11-03T16:58:01.48Z" }, - { url = "https://files.pythonhosted.org/packages/a6/85/9815ccb5a18ee8c9a46bd5ef20d02b292cd4a99c62553f38c87015f16d59/contourpy-1.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:464b423bc2a009088f19bdf1f232299e8b6917963e2b7e1d277da5041f33a779", size = 288108, upload-time = "2023-11-03T16:58:05.546Z" }, - { url = "https://files.pythonhosted.org/packages/5a/d9/4df5c26bd0f496c8cd7940fd53db95d07deeb98518f02f805ce570590da8/contourpy-1.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:68ce4788b7d93e47f84edd3f1f95acdcd142ae60bc0e5493bfd120683d2d4316", size = 330810, upload-time = "2023-11-03T16:58:09.568Z" }, - { url = "https://files.pythonhosted.org/packages/67/d4/8aae9793a0cfde72959312521ebd3aa635c260c3d580448e8db6bdcdd1aa/contourpy-1.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d7d1f8871998cdff5d2ff6a087e5e1780139abe2838e85b0b46b7ae6cc25399", size = 305290, upload-time = "2023-11-03T16:58:13.017Z" }, - { url = "https://files.pythonhosted.org/packages/20/84/ffddcdcc579cbf7213fd92a3578ca08a931a3bf879a22deb5a83ffc5002c/contourpy-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e739530c662a8d6d42c37c2ed52a6f0932c2d4a3e8c1f90692ad0ce1274abe0", size = 303937, upload-time = "2023-11-03T16:58:16.426Z" }, - { url = "https://files.pythonhosted.org/packages/d8/ad/6e570cf525f909da94559ed716189f92f529bc7b5f78645733c44619a0e2/contourpy-1.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:247b9d16535acaa766d03037d8e8fb20866d054d3c7fbf6fd1f993f11fc60ca0", size = 801977, upload-time = "2023-11-03T16:58:23.539Z" }, - { url = "https://files.pythonhosted.org/packages/36/b4/55f23482c596eca36d16fc668b147865c56fcf90353f4c57f073d8d5e532/contourpy-1.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:461e3ae84cd90b30f8d533f07d87c00379644205b1d33a5ea03381edc4b69431", size = 827442, upload-time = "2023-11-03T16:58:30.724Z" }, - { url = "https://files.pythonhosted.org/packages/e9/47/9c081b1f11d6053cb0aa4c46b7de2ea2849a4a8d40de81c7bc3f99773b02/contourpy-1.2.0-cp312-cp312-win32.whl", hash = "sha256:1c2559d6cffc94890b0529ea7eeecc20d6fadc1539273aa27faf503eb4656d8f", size = 165363, upload-time = "2023-11-03T16:58:33.54Z" }, - { url = "https://files.pythonhosted.org/packages/8e/ae/a6353db548bff1a592b85ae6bb80275f0a51dc25a0410d059e5b33183e36/contourpy-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:491b1917afdd8638a05b611a56d46587d5a632cabead889a5440f7c638bc6ed9", size = 187731, upload-time = "2023-11-03T16:58:36.585Z" }, + { url = "https://files.pythonhosted.org/packages/e8/ea/f6e90933d82cc5aacf52f886a1c01f47f96eba99108ca2929c7b3ef45f82/contourpy-1.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0274c1cb63625972c0c007ab14dd9ba9e199c36ae1a231ce45d725cbcbfd10a8", size = 256873 }, + { url = "https://files.pythonhosted.org/packages/fe/26/43821d61b7ee62c1809ec852bc572aaf4c27f101ebcebbbcce29a5ee0445/contourpy-1.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab459a1cbbf18e8698399c595a01f6dcc5c138220ca3ea9e7e6126232d102bb4", size = 242211 }, + { url = "https://files.pythonhosted.org/packages/9b/99/c8fb63072a7573fe7682e1786a021f29f9c5f660a3aafcdce80b9ee8348d/contourpy-1.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fdd887f17c2f4572ce548461e4f96396681212d858cae7bd52ba3310bc6f00f", size = 293195 }, + { url = "https://files.pythonhosted.org/packages/c7/a7/ae0b4bb8e0c865270d02ee619981413996dc10ddf1fd2689c938173ff62f/contourpy-1.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d16edfc3fc09968e09ddffada434b3bf989bf4911535e04eada58469873e28e", size = 332279 }, + { url = "https://files.pythonhosted.org/packages/94/7c/682228b9085ff323fb7e946fe139072e5f21b71360cf91f36ea079d4ea95/contourpy-1.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c203f617abc0dde5792beb586f827021069fb6d403d7f4d5c2b543d87edceb9", size = 305326 }, + { url = "https://files.pythonhosted.org/packages/58/56/e2c43dcfa1f9c7db4d5e3d6f5134b24ed953f4e2133a4b12f0062148db58/contourpy-1.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b69303ceb2e4d4f146bf82fda78891ef7bcd80c41bf16bfca3d0d7eb545448aa", size = 310732 }, + { url = "https://files.pythonhosted.org/packages/94/0b/8495c4582057abc8377f945f6e11a86f1c07ad7b32fd4fdc968478cd0324/contourpy-1.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:884c3f9d42d7218304bc74a8a7693d172685c84bd7ab2bab1ee567b769696df9", size = 803420 }, + { url = "https://files.pythonhosted.org/packages/d5/1f/40399c7da649297147d404aedaa675cc60018f48ad284630c0d1406133e3/contourpy-1.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4a1b1208102be6e851f20066bf0e7a96b7d48a07c9b0cfe6d0d4545c2f6cadab", size = 829204 }, + { url = "https://files.pythonhosted.org/packages/8b/01/4be433b60dce7cbce8315cbcdfc016e7d25430a8b94e272355dff79cc3a8/contourpy-1.2.0-cp310-cp310-win32.whl", hash = "sha256:34b9071c040d6fe45d9826cbbe3727d20d83f1b6110d219b83eb0e2a01d79488", size = 165434 }, + { url = "https://files.pythonhosted.org/packages/fd/7c/168f8343f33d861305e18c56901ef1bb675d3c7f977f435ec72751a71a54/contourpy-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:bd2f1ae63998da104f16a8b788f685e55d65760cd1929518fd94cd682bf03e41", size = 186652 }, + { url = "https://files.pythonhosted.org/packages/9b/54/1dafec3c84df1d29119037330f7289db84a679cb2d5283af4ef24d89f532/contourpy-1.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dd10c26b4eadae44783c45ad6655220426f971c61d9b239e6f7b16d5cdaaa727", size = 258243 }, + { url = "https://files.pythonhosted.org/packages/5b/ac/26fa1057f62beaa2af4c55c6ac733b114a403b746cfe0ce3dc6e4aec921a/contourpy-1.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5c6b28956b7b232ae801406e529ad7b350d3f09a4fde958dfdf3c0520cdde0dd", size = 243408 }, + { url = "https://files.pythonhosted.org/packages/b7/33/cd0ecc80123f499d76d2fe2807cb4d5638ef8730735c580c8a8a03e1928e/contourpy-1.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebeac59e9e1eb4b84940d076d9f9a6cec0064e241818bcb6e32124cc5c3e377a", size = 294142 }, + { url = "https://files.pythonhosted.org/packages/6d/75/1b7bf20bf6394e01df2c4b4b3d44d3dc280c16ddaff72724639100bd4314/contourpy-1.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:139d8d2e1c1dd52d78682f505e980f592ba53c9f73bd6be102233e358b401063", size = 333129 }, + { url = "https://files.pythonhosted.org/packages/22/5b/fedd961dff1877e5d3b83c5201295cfdcdc2438884c2851aa7ecf6cec045/contourpy-1.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e9dc350fb4c58adc64df3e0703ab076f60aac06e67d48b3848c23647ae4310e", size = 307461 }, + { url = "https://files.pythonhosted.org/packages/e2/83/29a63bbc72839cc6b24b5a0e3d004d4ed4e8439f26460ad9a34e39251904/contourpy-1.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18fc2b4ed8e4a8fe849d18dce4bd3c7ea637758c6343a1f2bae1e9bd4c9f4686", size = 313352 }, + { url = "https://files.pythonhosted.org/packages/4b/c7/4bac0fc4f1e802ab47e75076d83d2e1448e0668ba6cc9000cf4e9d5bd94a/contourpy-1.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:16a7380e943a6d52472096cb7ad5264ecee36ed60888e2a3d3814991a0107286", size = 804127 }, + { url = "https://files.pythonhosted.org/packages/e3/47/b3fd5bdc2f6ec13502d57a5bc390ffe62648605ed1689c93b0015150a784/contourpy-1.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8d8faf05be5ec8e02a4d86f616fc2a0322ff4a4ce26c0f09d9f7fb5330a35c95", size = 829561 }, + { url = "https://files.pythonhosted.org/packages/5c/04/be16038e754169caea4d02d82f8e5cd97dece593e5ac9e05735da0afd0c5/contourpy-1.2.0-cp311-cp311-win32.whl", hash = "sha256:67b7f17679fa62ec82b7e3e611c43a016b887bd64fb933b3ae8638583006c6d6", size = 166197 }, + { url = "https://files.pythonhosted.org/packages/ca/2a/d197a412ec474391ee878b1218cf2fe9c6e963903755887fc5654c06636a/contourpy-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:99ad97258985328b4f207a5e777c1b44a83bfe7cf1f87b99f9c11d4ee477c4de", size = 187556 }, + { url = "https://files.pythonhosted.org/packages/4f/03/839da46999173226bead08794cbd7b4d37c9e6b02686ca74c93556b43258/contourpy-1.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:575bcaf957a25d1194903a10bc9f316c136c19f24e0985a2b9b5608bdf5dbfe0", size = 259253 }, + { url = "https://files.pythonhosted.org/packages/f3/9e/8fb3f53144269d3fecdd8786d3a4686eeff55b9b35a3c0772a3f62f71e36/contourpy-1.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9e6c93b5b2dbcedad20a2f18ec22cae47da0d705d454308063421a3b290d9ea4", size = 242555 }, + { url = "https://files.pythonhosted.org/packages/a6/85/9815ccb5a18ee8c9a46bd5ef20d02b292cd4a99c62553f38c87015f16d59/contourpy-1.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:464b423bc2a009088f19bdf1f232299e8b6917963e2b7e1d277da5041f33a779", size = 288108 }, + { url = "https://files.pythonhosted.org/packages/5a/d9/4df5c26bd0f496c8cd7940fd53db95d07deeb98518f02f805ce570590da8/contourpy-1.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:68ce4788b7d93e47f84edd3f1f95acdcd142ae60bc0e5493bfd120683d2d4316", size = 330810 }, + { url = "https://files.pythonhosted.org/packages/67/d4/8aae9793a0cfde72959312521ebd3aa635c260c3d580448e8db6bdcdd1aa/contourpy-1.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d7d1f8871998cdff5d2ff6a087e5e1780139abe2838e85b0b46b7ae6cc25399", size = 305290 }, + { url = "https://files.pythonhosted.org/packages/20/84/ffddcdcc579cbf7213fd92a3578ca08a931a3bf879a22deb5a83ffc5002c/contourpy-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e739530c662a8d6d42c37c2ed52a6f0932c2d4a3e8c1f90692ad0ce1274abe0", size = 303937 }, + { url = "https://files.pythonhosted.org/packages/d8/ad/6e570cf525f909da94559ed716189f92f529bc7b5f78645733c44619a0e2/contourpy-1.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:247b9d16535acaa766d03037d8e8fb20866d054d3c7fbf6fd1f993f11fc60ca0", size = 801977 }, + { url = "https://files.pythonhosted.org/packages/36/b4/55f23482c596eca36d16fc668b147865c56fcf90353f4c57f073d8d5e532/contourpy-1.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:461e3ae84cd90b30f8d533f07d87c00379644205b1d33a5ea03381edc4b69431", size = 827442 }, + { url = "https://files.pythonhosted.org/packages/e9/47/9c081b1f11d6053cb0aa4c46b7de2ea2849a4a8d40de81c7bc3f99773b02/contourpy-1.2.0-cp312-cp312-win32.whl", hash = "sha256:1c2559d6cffc94890b0529ea7eeecc20d6fadc1539273aa27faf503eb4656d8f", size = 165363 }, + { url = "https://files.pythonhosted.org/packages/8e/ae/a6353db548bff1a592b85ae6bb80275f0a51dc25a0410d059e5b33183e36/contourpy-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:491b1917afdd8638a05b611a56d46587d5a632cabead889a5440f7c638bc6ed9", size = 187731 }, +] + +[[package]] +name = "contourpy" +version = "1.3.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and sys_platform == 'darwin'", + "python_full_version == '3.12.*' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version == '3.13.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version >= '3.14' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform != 'darwin' and sys_platform != 'linux')", + "(python_full_version == '3.13.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform != 'darwin' and sys_platform != 'linux')", + "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version == '3.11.*' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')", +] +dependencies = [ + { name = "numpy", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/2e/c4390a31919d8a78b90e8ecf87cd4b4c4f05a5b48d05ec17db8e5404c6f4/contourpy-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:709a48ef9a690e1343202916450bc48b9e51c049b089c7f79a267b46cffcdaa1", size = 288773 }, + { url = "https://files.pythonhosted.org/packages/0d/44/c4b0b6095fef4dc9c420e041799591e3b63e9619e3044f7f4f6c21c0ab24/contourpy-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:23416f38bfd74d5d28ab8429cc4d63fa67d5068bd711a85edb1c3fb0c3e2f381", size = 270149 }, + { url = "https://files.pythonhosted.org/packages/30/2e/dd4ced42fefac8470661d7cb7e264808425e6c5d56d175291e93890cce09/contourpy-1.3.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:929ddf8c4c7f348e4c0a5a3a714b5c8542ffaa8c22954862a46ca1813b667ee7", size = 329222 }, + { url = "https://files.pythonhosted.org/packages/f2/74/cc6ec2548e3d276c71389ea4802a774b7aa3558223b7bade3f25787fafc2/contourpy-1.3.3-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9e999574eddae35f1312c2b4b717b7885d4edd6cb46700e04f7f02db454e67c1", size = 377234 }, + { url = "https://files.pythonhosted.org/packages/03/b3/64ef723029f917410f75c09da54254c5f9ea90ef89b143ccadb09df14c15/contourpy-1.3.3-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf67e0e3f482cb69779dd3061b534eb35ac9b17f163d851e2a547d56dba0a3a", size = 380555 }, + { url = "https://files.pythonhosted.org/packages/5f/4b/6157f24ca425b89fe2eb7e7be642375711ab671135be21e6faa100f7448c/contourpy-1.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51e79c1f7470158e838808d4a996fa9bac72c498e93d8ebe5119bc1e6becb0db", size = 355238 }, + { url = "https://files.pythonhosted.org/packages/98/56/f914f0dd678480708a04cfd2206e7c382533249bc5001eb9f58aa693e200/contourpy-1.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:598c3aaece21c503615fd59c92a3598b428b2f01bfb4b8ca9c4edeecc2438620", size = 1326218 }, + { url = "https://files.pythonhosted.org/packages/fb/d7/4a972334a0c971acd5172389671113ae82aa7527073980c38d5868ff1161/contourpy-1.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:322ab1c99b008dad206d406bb61d014cf0174df491ae9d9d0fac6a6fda4f977f", size = 1392867 }, + { url = "https://files.pythonhosted.org/packages/75/3e/f2cc6cd56dc8cff46b1a56232eabc6feea52720083ea71ab15523daab796/contourpy-1.3.3-cp311-cp311-win32.whl", hash = "sha256:fd907ae12cd483cd83e414b12941c632a969171bf90fc937d0c9f268a31cafff", size = 183677 }, + { url = "https://files.pythonhosted.org/packages/98/4b/9bd370b004b5c9d8045c6c33cf65bae018b27aca550a3f657cdc99acdbd8/contourpy-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:3519428f6be58431c56581f1694ba8e50626f2dd550af225f82fb5f5814d2a42", size = 225234 }, + { url = "https://files.pythonhosted.org/packages/d9/b6/71771e02c2e004450c12b1120a5f488cad2e4d5b590b1af8bad060360fe4/contourpy-1.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:15ff10bfada4bf92ec8b31c62bf7c1834c244019b4a33095a68000d7075df470", size = 193123 }, + { url = "https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", size = 293419 }, + { url = "https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", size = 273979 }, + { url = "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653 }, + { url = "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536 }, + { url = "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397 }, + { url = "https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601 }, + { url = "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288 }, + { url = "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386 }, + { url = "https://files.pythonhosted.org/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69", size = 185018 }, + { url = "https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b", size = 226567 }, + { url = "https://files.pythonhosted.org/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc", size = 193655 }, + { url = "https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5", size = 293257 }, + { url = "https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1", size = 274034 }, + { url = "https://files.pythonhosted.org/packages/73/23/90e31ceeed1de63058a02cb04b12f2de4b40e3bef5e082a7c18d9c8ae281/contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286", size = 334672 }, + { url = "https://files.pythonhosted.org/packages/ed/93/b43d8acbe67392e659e1d984700e79eb67e2acb2bd7f62012b583a7f1b55/contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5", size = 381234 }, + { url = "https://files.pythonhosted.org/packages/46/3b/bec82a3ea06f66711520f75a40c8fc0b113b2a75edb36aa633eb11c4f50f/contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67", size = 385169 }, + { url = "https://files.pythonhosted.org/packages/4b/32/e0f13a1c5b0f8572d0ec6ae2f6c677b7991fafd95da523159c19eff0696a/contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9", size = 362859 }, + { url = "https://files.pythonhosted.org/packages/33/71/e2a7945b7de4e58af42d708a219f3b2f4cff7386e6b6ab0a0fa0033c49a9/contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659", size = 1332062 }, + { url = "https://files.pythonhosted.org/packages/12/fc/4e87ac754220ccc0e807284f88e943d6d43b43843614f0a8afa469801db0/contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7", size = 1403932 }, + { url = "https://files.pythonhosted.org/packages/a6/2e/adc197a37443f934594112222ac1aa7dc9a98faf9c3842884df9a9d8751d/contourpy-1.3.3-cp313-cp313-win32.whl", hash = "sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d", size = 185024 }, + { url = "https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263", size = 226578 }, + { url = "https://files.pythonhosted.org/packages/8a/9a/2f6024a0c5995243cd63afdeb3651c984f0d2bc727fd98066d40e141ad73/contourpy-1.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9", size = 193524 }, + { url = "https://files.pythonhosted.org/packages/c0/b3/f8a1a86bd3298513f500e5b1f5fd92b69896449f6cab6a146a5d52715479/contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d", size = 306730 }, + { url = "https://files.pythonhosted.org/packages/3f/11/4780db94ae62fc0c2053909b65dc3246bd7cecfc4f8a20d957ad43aa4ad8/contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216", size = 287897 }, + { url = "https://files.pythonhosted.org/packages/ae/15/e59f5f3ffdd6f3d4daa3e47114c53daabcb18574a26c21f03dc9e4e42ff0/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae", size = 326751 }, + { url = "https://files.pythonhosted.org/packages/0f/81/03b45cfad088e4770b1dcf72ea78d3802d04200009fb364d18a493857210/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20", size = 375486 }, + { url = "https://files.pythonhosted.org/packages/0c/ba/49923366492ffbdd4486e970d421b289a670ae8cf539c1ea9a09822b371a/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99", size = 388106 }, + { url = "https://files.pythonhosted.org/packages/9f/52/5b00ea89525f8f143651f9f03a0df371d3cbd2fccd21ca9b768c7a6500c2/contourpy-1.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b", size = 352548 }, + { url = "https://files.pythonhosted.org/packages/32/1d/a209ec1a3a3452d490f6b14dd92e72280c99ae3d1e73da74f8277d4ee08f/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a", size = 1322297 }, + { url = "https://files.pythonhosted.org/packages/bc/9e/46f0e8ebdd884ca0e8877e46a3f4e633f6c9c8c4f3f6e72be3fe075994aa/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e", size = 1391023 }, + { url = "https://files.pythonhosted.org/packages/b9/70/f308384a3ae9cd2209e0849f33c913f658d3326900d0ff5d378d6a1422d2/contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3", size = 196157 }, + { url = "https://files.pythonhosted.org/packages/b2/dd/880f890a6663b84d9e34a6f88cded89d78f0091e0045a284427cb6b18521/contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8", size = 240570 }, + { url = "https://files.pythonhosted.org/packages/80/99/2adc7d8ffead633234817ef8e9a87115c8a11927a94478f6bb3d3f4d4f7d/contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301", size = 199713 }, + { url = "https://files.pythonhosted.org/packages/72/8b/4546f3ab60f78c514ffb7d01a0bd743f90de36f0019d1be84d0a708a580a/contourpy-1.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a", size = 292189 }, + { url = "https://files.pythonhosted.org/packages/fd/e1/3542a9cb596cadd76fcef413f19c79216e002623158befe6daa03dbfa88c/contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cbedb772ed74ff5be440fa8eee9bd49f64f6e3fc09436d9c7d8f1c287b121d77", size = 273251 }, + { url = "https://files.pythonhosted.org/packages/b1/71/f93e1e9471d189f79d0ce2497007731c1e6bf9ef6d1d61b911430c3db4e5/contourpy-1.3.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22e9b1bd7a9b1d652cd77388465dc358dafcd2e217d35552424aa4f996f524f5", size = 335810 }, + { url = "https://files.pythonhosted.org/packages/91/f9/e35f4c1c93f9275d4e38681a80506b5510e9327350c51f8d4a5a724d178c/contourpy-1.3.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a22738912262aa3e254e4f3cb079a95a67132fc5a063890e224393596902f5a4", size = 382871 }, + { url = "https://files.pythonhosted.org/packages/b5/71/47b512f936f66a0a900d81c396a7e60d73419868fba959c61efed7a8ab46/contourpy-1.3.3-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:afe5a512f31ee6bd7d0dda52ec9864c984ca3d66664444f2d72e0dc4eb832e36", size = 386264 }, + { url = "https://files.pythonhosted.org/packages/04/5f/9ff93450ba96b09c7c2b3f81c94de31c89f92292f1380261bd7195bea4ea/contourpy-1.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f64836de09927cba6f79dcd00fdd7d5329f3fccc633468507079c829ca4db4e3", size = 363819 }, + { url = "https://files.pythonhosted.org/packages/3e/a6/0b185d4cc480ee494945cde102cb0149ae830b5fa17bf855b95f2e70ad13/contourpy-1.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1fd43c3be4c8e5fd6e4f2baeae35ae18176cf2e5cced681cca908addf1cdd53b", size = 1333650 }, + { url = "https://files.pythonhosted.org/packages/43/d7/afdc95580ca56f30fbcd3060250f66cedbde69b4547028863abd8aa3b47e/contourpy-1.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6afc576f7b33cf00996e5c1102dc2a8f7cc89e39c0b55df93a0b78c1bd992b36", size = 1404833 }, + { url = "https://files.pythonhosted.org/packages/e2/e2/366af18a6d386f41132a48f033cbd2102e9b0cf6345d35ff0826cd984566/contourpy-1.3.3-cp314-cp314-win32.whl", hash = "sha256:66c8a43a4f7b8df8b71ee1840e4211a3c8d93b214b213f590e18a1beca458f7d", size = 189692 }, + { url = "https://files.pythonhosted.org/packages/7d/c2/57f54b03d0f22d4044b8afb9ca0e184f8b1afd57b4f735c2fa70883dc601/contourpy-1.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:cf9022ef053f2694e31d630feaacb21ea24224be1c3ad0520b13d844274614fd", size = 232424 }, + { url = "https://files.pythonhosted.org/packages/18/79/a9416650df9b525737ab521aa181ccc42d56016d2123ddcb7b58e926a42c/contourpy-1.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:95b181891b4c71de4bb404c6621e7e2390745f887f2a026b2d99e92c17892339", size = 198300 }, + { url = "https://files.pythonhosted.org/packages/1f/42/38c159a7d0f2b7b9c04c64ab317042bb6952b713ba875c1681529a2932fe/contourpy-1.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:33c82d0138c0a062380332c861387650c82e4cf1747aaa6938b9b6516762e772", size = 306769 }, + { url = "https://files.pythonhosted.org/packages/c3/6c/26a8205f24bca10974e77460de68d3d7c63e282e23782f1239f226fcae6f/contourpy-1.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ea37e7b45949df430fe649e5de8351c423430046a2af20b1c1961cae3afcda77", size = 287892 }, + { url = "https://files.pythonhosted.org/packages/66/06/8a475c8ab718ebfd7925661747dbb3c3ee9c82ac834ccb3570be49d129f4/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d304906ecc71672e9c89e87c4675dc5c2645e1f4269a5063b99b0bb29f232d13", size = 326748 }, + { url = "https://files.pythonhosted.org/packages/b4/a3/c5ca9f010a44c223f098fccd8b158bb1cb287378a31ac141f04730dc49be/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca658cd1a680a5c9ea96dc61cdbae1e85c8f25849843aa799dfd3cb370ad4fbe", size = 375554 }, + { url = "https://files.pythonhosted.org/packages/80/5b/68bd33ae63fac658a4145088c1e894405e07584a316738710b636c6d0333/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ab2fd90904c503739a75b7c8c5c01160130ba67944a7b77bbf36ef8054576e7f", size = 388118 }, + { url = "https://files.pythonhosted.org/packages/40/52/4c285a6435940ae25d7410a6c36bda5145839bc3f0beb20c707cda18b9d2/contourpy-1.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7301b89040075c30e5768810bc96a8e8d78085b47d8be6e4c3f5a0b4ed478a0", size = 352555 }, + { url = "https://files.pythonhosted.org/packages/24/ee/3e81e1dd174f5c7fefe50e85d0892de05ca4e26ef1c9a59c2a57e43b865a/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2a2a8b627d5cc6b7c41a4beff6c5ad5eb848c88255fda4a8745f7e901b32d8e4", size = 1322295 }, + { url = "https://files.pythonhosted.org/packages/3c/b2/6d913d4d04e14379de429057cd169e5e00f6c2af3bb13e1710bcbdb5da12/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fd6ec6be509c787f1caf6b247f0b1ca598bef13f4ddeaa126b7658215529ba0f", size = 1391027 }, + { url = "https://files.pythonhosted.org/packages/93/8a/68a4ec5c55a2971213d29a9374913f7e9f18581945a7a31d1a39b5d2dfe5/contourpy-1.3.3-cp314-cp314t-win32.whl", hash = "sha256:e74a9a0f5e3fff48fb5a7f2fd2b9b70a3fe014a67522f79b7cca4c0c7e43c9ae", size = 202428 }, + { url = "https://files.pythonhosted.org/packages/fa/96/fd9f641ffedc4fa3ace923af73b9d07e869496c9cc7a459103e6e978992f/contourpy-1.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:13b68d6a62db8eafaebb8039218921399baf6e47bf85006fd8529f2a08ef33fc", size = 250331 }, + { url = "https://files.pythonhosted.org/packages/ae/8c/469afb6465b853afff216f9528ffda78a915ff880ed58813ba4faf4ba0b6/contourpy-1.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b7448cb5a725bb1e35ce88771b86fba35ef418952474492cf7c764059933ff8b", size = 203831 }, + { url = "https://files.pythonhosted.org/packages/a5/29/8dcfe16f0107943fa92388c23f6e05cff0ba58058c4c95b00280d4c75a14/contourpy-1.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cd5dfcaeb10f7b7f9dc8941717c6c2ade08f587be2226222c12b25f0483ed497", size = 278809 }, + { url = "https://files.pythonhosted.org/packages/85/a9/8b37ef4f7dafeb335daee3c8254645ef5725be4d9c6aa70b50ec46ef2f7e/contourpy-1.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:0c1fc238306b35f246d61a1d416a627348b5cf0648648a031e14bb8705fcdfe8", size = 261593 }, + { url = "https://files.pythonhosted.org/packages/0a/59/ebfb8c677c75605cc27f7122c90313fd2f375ff3c8d19a1694bda74aaa63/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70f9aad7de812d6541d29d2bbf8feb22ff7e1c299523db288004e3157ff4674e", size = 302202 }, + { url = "https://files.pythonhosted.org/packages/3c/37/21972a15834d90bfbfb009b9d004779bd5a07a0ec0234e5ba8f64d5736f4/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ed3657edf08512fc3fe81b510e35c2012fbd3081d2e26160f27ca28affec989", size = 329207 }, + { url = "https://files.pythonhosted.org/packages/0c/58/bd257695f39d05594ca4ad60df5bcb7e32247f9951fd09a9b8edb82d1daa/contourpy-1.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77", size = 225315 }, ] [[package]] name = "coverage" version = "7.6.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/12/3669b6382792783e92046730ad3327f53b2726f0603f4c311c4da4824222/coverage-7.6.4.tar.gz", hash = "sha256:29fc0f17b1d3fea332f8001d4558f8214af7f1d87a345f3a133c901d60347c73", size = 798716, upload-time = "2024-10-20T22:57:39.682Z" } +sdist = { url = "https://files.pythonhosted.org/packages/52/12/3669b6382792783e92046730ad3327f53b2726f0603f4c311c4da4824222/coverage-7.6.4.tar.gz", hash = "sha256:29fc0f17b1d3fea332f8001d4558f8214af7f1d87a345f3a133c901d60347c73", size = 798716 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/93/4ad92f71e28ece5c0326e5f4a6630aa4928a8846654a65cfff69b49b95b9/coverage-7.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f8ae553cba74085db385d489c7a792ad66f7f9ba2ee85bfa508aeb84cf0ba07", size = 206713, upload-time = "2024-10-20T22:56:03.877Z" }, - { url = "https://files.pythonhosted.org/packages/01/ae/747a580b1eda3f2e431d87de48f0604bd7bc92e52a1a95185a4aa585bc47/coverage-7.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8165b796df0bd42e10527a3f493c592ba494f16ef3c8b531288e3d0d72c1f6f0", size = 207149, upload-time = "2024-10-20T22:56:06.511Z" }, - { url = "https://files.pythonhosted.org/packages/07/1a/1f573f8a6145f6d4c9130bbc120e0024daf1b24cf2a78d7393fa6eb6aba7/coverage-7.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7c8b95bf47db6d19096a5e052ffca0a05f335bc63cef281a6e8fe864d450a72", size = 235584, upload-time = "2024-10-20T22:56:07.678Z" }, - { url = "https://files.pythonhosted.org/packages/40/42/c8523f2e4db34aa9389caee0d3688b6ada7a84fcc782e943a868a7f302bd/coverage-7.6.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ed9281d1b52628e81393f5eaee24a45cbd64965f41857559c2b7ff19385df51", size = 233486, upload-time = "2024-10-20T22:56:09.496Z" }, - { url = "https://files.pythonhosted.org/packages/8d/95/565c310fffa16ede1a042e9ea1ca3962af0d8eb5543bc72df6b91dc0c3d5/coverage-7.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0809082ee480bb8f7416507538243c8863ac74fd8a5d2485c46f0f7499f2b491", size = 234649, upload-time = "2024-10-20T22:56:11.326Z" }, - { url = "https://files.pythonhosted.org/packages/d5/81/3b550674d98968ec29c92e3e8650682be6c8b1fa7581a059e7e12e74c431/coverage-7.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d541423cdd416b78626b55f123412fcf979d22a2c39fce251b350de38c15c15b", size = 233744, upload-time = "2024-10-20T22:56:12.481Z" }, - { url = "https://files.pythonhosted.org/packages/0d/70/d66c7f51b3e33aabc5ea9f9624c1c9d9655472962270eb5e7b0d32707224/coverage-7.6.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58809e238a8a12a625c70450b48e8767cff9eb67c62e6154a642b21ddf79baea", size = 232204, upload-time = "2024-10-20T22:56:14.236Z" }, - { url = "https://files.pythonhosted.org/packages/23/2d/2b3a2dbed7a5f40693404c8a09e779d7c1a5fbed089d3e7224c002129ec8/coverage-7.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c9b8e184898ed014884ca84c70562b4a82cbc63b044d366fedc68bc2b2f3394a", size = 233335, upload-time = "2024-10-20T22:56:15.521Z" }, - { url = "https://files.pythonhosted.org/packages/5a/4f/92d1d2ad720d698a4e71c176eacf531bfb8e0721d5ad560556f2c484a513/coverage-7.6.4-cp310-cp310-win32.whl", hash = "sha256:6bd818b7ea14bc6e1f06e241e8234508b21edf1b242d49831831a9450e2f35fa", size = 209435, upload-time = "2024-10-20T22:56:17.309Z" }, - { url = "https://files.pythonhosted.org/packages/c7/b9/cdf158e7991e2287bcf9082670928badb73d310047facac203ff8dcd5ff3/coverage-7.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:06babbb8f4e74b063dbaeb74ad68dfce9186c595a15f11f5d5683f748fa1d172", size = 210243, upload-time = "2024-10-20T22:56:18.366Z" }, - { url = "https://files.pythonhosted.org/packages/87/31/9c0cf84f0dfcbe4215b7eb95c31777cdc0483c13390e69584c8150c85175/coverage-7.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:73d2b73584446e66ee633eaad1a56aad577c077f46c35ca3283cd687b7715b0b", size = 206819, upload-time = "2024-10-20T22:56:20.132Z" }, - { url = "https://files.pythonhosted.org/packages/53/ed/a38401079ad320ad6e054a01ec2b61d270511aeb3c201c80e99c841229d5/coverage-7.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51b44306032045b383a7a8a2c13878de375117946d68dcb54308111f39775a25", size = 207263, upload-time = "2024-10-20T22:56:21.88Z" }, - { url = "https://files.pythonhosted.org/packages/20/e7/c3ad33b179ab4213f0d70da25a9c214d52464efa11caeab438592eb1d837/coverage-7.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3fb02fe73bed561fa12d279a417b432e5b50fe03e8d663d61b3d5990f29546", size = 239205, upload-time = "2024-10-20T22:56:23.03Z" }, - { url = "https://files.pythonhosted.org/packages/36/91/fc02e8d8e694f557752120487fd982f654ba1421bbaa5560debf96ddceda/coverage-7.6.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed8fe9189d2beb6edc14d3ad19800626e1d9f2d975e436f84e19efb7fa19469b", size = 236612, upload-time = "2024-10-20T22:56:24.882Z" }, - { url = "https://files.pythonhosted.org/packages/cc/57/cb08f0eda0389a9a8aaa4fc1f9fec7ac361c3e2d68efd5890d7042c18aa3/coverage-7.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b369ead6527d025a0fe7bd3864e46dbee3aa8f652d48df6174f8d0bac9e26e0e", size = 238479, upload-time = "2024-10-20T22:56:26.749Z" }, - { url = "https://files.pythonhosted.org/packages/d5/c9/2c7681a9b3ca6e6f43d489c2e6653a53278ed857fd6e7010490c307b0a47/coverage-7.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ade3ca1e5f0ff46b678b66201f7ff477e8fa11fb537f3b55c3f0568fbfe6e718", size = 237405, upload-time = "2024-10-20T22:56:27.958Z" }, - { url = "https://files.pythonhosted.org/packages/b5/4e/ebfc6944b96317df8b537ae875d2e57c27b84eb98820bc0a1055f358f056/coverage-7.6.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:27fb4a050aaf18772db513091c9c13f6cb94ed40eacdef8dad8411d92d9992db", size = 236038, upload-time = "2024-10-20T22:56:29.816Z" }, - { url = "https://files.pythonhosted.org/packages/13/f2/3a0bf1841a97c0654905e2ef531170f02c89fad2555879db8fe41a097871/coverage-7.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4f704f0998911abf728a7783799444fcbbe8261c4a6c166f667937ae6a8aa522", size = 236812, upload-time = "2024-10-20T22:56:31.654Z" }, - { url = "https://files.pythonhosted.org/packages/b9/9c/66bf59226b52ce6ed9541b02d33e80a6e816a832558fbdc1111a7bd3abd4/coverage-7.6.4-cp311-cp311-win32.whl", hash = "sha256:29155cd511ee058e260db648b6182c419422a0d2e9a4fa44501898cf918866cf", size = 209400, upload-time = "2024-10-20T22:56:33.569Z" }, - { url = "https://files.pythonhosted.org/packages/2a/a0/b0790934c04dfc8d658d4a62acb8f7ca0efdf3818456fcad757b11c6479d/coverage-7.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:8902dd6a30173d4ef09954bfcb24b5d7b5190cf14a43170e386979651e09ba19", size = 210243, upload-time = "2024-10-20T22:56:34.863Z" }, - { url = "https://files.pythonhosted.org/packages/7d/e7/9291de916d084f41adddfd4b82246e68d61d6a75747f075f7e64628998d2/coverage-7.6.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12394842a3a8affa3ba62b0d4ab7e9e210c5e366fbac3e8b2a68636fb19892c2", size = 207013, upload-time = "2024-10-20T22:56:36.034Z" }, - { url = "https://files.pythonhosted.org/packages/27/03/932c2c5717a7fa80cd43c6a07d3177076d97b79f12f40f882f9916db0063/coverage-7.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b6b4c83d8e8ea79f27ab80778c19bc037759aea298da4b56621f4474ffeb117", size = 207251, upload-time = "2024-10-20T22:56:38.054Z" }, - { url = "https://files.pythonhosted.org/packages/d5/3f/0af47dcb9327f65a45455fbca846fe96eb57c153af46c4754a3ba678938a/coverage-7.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d5b8007f81b88696d06f7df0cb9af0d3b835fe0c8dbf489bad70b45f0e45613", size = 240268, upload-time = "2024-10-20T22:56:40.051Z" }, - { url = "https://files.pythonhosted.org/packages/8a/3c/37a9d81bbd4b23bc7d46ca820e16174c613579c66342faa390a271d2e18b/coverage-7.6.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b57b768feb866f44eeed9f46975f3d6406380275c5ddfe22f531a2bf187eda27", size = 237298, upload-time = "2024-10-20T22:56:41.929Z" }, - { url = "https://files.pythonhosted.org/packages/c0/70/6b0627e5bd68204ee580126ed3513140b2298995c1233bd67404b4e44d0e/coverage-7.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5915fcdec0e54ee229926868e9b08586376cae1f5faa9bbaf8faf3561b393d52", size = 239367, upload-time = "2024-10-20T22:56:43.141Z" }, - { url = "https://files.pythonhosted.org/packages/3c/eb/634d7dfab24ac3b790bebaf9da0f4a5352cbc125ce6a9d5c6cf4c6cae3c7/coverage-7.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b58c672d14f16ed92a48db984612f5ce3836ae7d72cdd161001cc54512571f2", size = 238853, upload-time = "2024-10-20T22:56:44.33Z" }, - { url = "https://files.pythonhosted.org/packages/d9/0d/8e3ed00f1266ef7472a4e33458f42e39492e01a64281084fb3043553d3f1/coverage-7.6.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2fdef0d83a2d08d69b1f2210a93c416d54e14d9eb398f6ab2f0a209433db19e1", size = 237160, upload-time = "2024-10-20T22:56:46.258Z" }, - { url = "https://files.pythonhosted.org/packages/ce/9c/4337f468ef0ab7a2e0887a9c9da0e58e2eada6fc6cbee637a4acd5dfd8a9/coverage-7.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cf717ee42012be8c0cb205dbbf18ffa9003c4cbf4ad078db47b95e10748eec5", size = 238824, upload-time = "2024-10-20T22:56:48.666Z" }, - { url = "https://files.pythonhosted.org/packages/5e/09/3e94912b8dd37251377bb02727a33a67ee96b84bbbe092f132b401ca5dd9/coverage-7.6.4-cp312-cp312-win32.whl", hash = "sha256:7bb92c539a624cf86296dd0c68cd5cc286c9eef2d0c3b8b192b604ce9de20a17", size = 209639, upload-time = "2024-10-20T22:56:50.664Z" }, - { url = "https://files.pythonhosted.org/packages/01/69/d4f3a4101171f32bc5b3caec8ff94c2c60f700107a6aaef7244b2c166793/coverage-7.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:1032e178b76a4e2b5b32e19d0fd0abbce4b58e77a1ca695820d10e491fa32b08", size = 210428, upload-time = "2024-10-20T22:56:52.468Z" }, - { url = "https://files.pythonhosted.org/packages/c2/4d/2dede4f7cb5a70fb0bb40a57627fddf1dbdc6b9c1db81f7c4dcdcb19e2f4/coverage-7.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:023bf8ee3ec6d35af9c1c6ccc1d18fa69afa1cb29eaac57cb064dbb262a517f9", size = 207039, upload-time = "2024-10-20T22:56:53.656Z" }, - { url = "https://files.pythonhosted.org/packages/3f/f9/d86368ae8c79e28f1fb458ebc76ae9ff3e8bd8069adc24e8f2fed03c58b7/coverage-7.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0ac3d42cb51c4b12df9c5f0dd2f13a4f24f01943627120ec4d293c9181219ba", size = 207298, upload-time = "2024-10-20T22:56:54.979Z" }, - { url = "https://files.pythonhosted.org/packages/64/c5/b4cc3c3f64622c58fbfd4d8b9a7a8ce9d355f172f91fcabbba1f026852f6/coverage-7.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8fe4984b431f8621ca53d9380901f62bfb54ff759a1348cd140490ada7b693c", size = 239813, upload-time = "2024-10-20T22:56:56.209Z" }, - { url = "https://files.pythonhosted.org/packages/8a/86/14c42e60b70a79b26099e4d289ccdfefbc68624d096f4481163085aa614c/coverage-7.6.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fbd612f8a091954a0c8dd4c0b571b973487277d26476f8480bfa4b2a65b5d06", size = 236959, upload-time = "2024-10-20T22:56:58.06Z" }, - { url = "https://files.pythonhosted.org/packages/7f/f8/4436a643631a2fbab4b44d54f515028f6099bfb1cd95b13cfbf701e7f2f2/coverage-7.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dacbc52de979f2823a819571f2e3a350a7e36b8cb7484cdb1e289bceaf35305f", size = 238950, upload-time = "2024-10-20T22:56:59.329Z" }, - { url = "https://files.pythonhosted.org/packages/49/50/1571810ddd01f99a0a8be464a4ac8b147f322cd1e8e296a1528984fc560b/coverage-7.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dab4d16dfef34b185032580e2f2f89253d302facba093d5fa9dbe04f569c4f4b", size = 238610, upload-time = "2024-10-20T22:57:00.645Z" }, - { url = "https://files.pythonhosted.org/packages/f3/8c/6312d241fe7cbd1f0cade34a62fea6f333d1a261255d76b9a87074d8703c/coverage-7.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:862264b12ebb65ad8d863d51f17758b1684560b66ab02770d4f0baf2ff75da21", size = 236697, upload-time = "2024-10-20T22:57:01.944Z" }, - { url = "https://files.pythonhosted.org/packages/ce/5f/fef33dfd05d87ee9030f614c857deb6df6556b8f6a1c51bbbb41e24ee5ac/coverage-7.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5beb1ee382ad32afe424097de57134175fea3faf847b9af002cc7895be4e2a5a", size = 238541, upload-time = "2024-10-20T22:57:03.848Z" }, - { url = "https://files.pythonhosted.org/packages/a9/64/6a984b6e92e1ea1353b7ffa08e27f707a5e29b044622445859200f541e8c/coverage-7.6.4-cp313-cp313-win32.whl", hash = "sha256:bf20494da9653f6410213424f5f8ad0ed885e01f7e8e59811f572bdb20b8972e", size = 209707, upload-time = "2024-10-20T22:57:05.123Z" }, - { url = "https://files.pythonhosted.org/packages/5c/60/ce5a9e942e9543783b3db5d942e0578b391c25cdd5e7f342d854ea83d6b7/coverage-7.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:182e6cd5c040cec0a1c8d415a87b67ed01193ed9ad458ee427741c7d8513d963", size = 210439, upload-time = "2024-10-20T22:57:06.35Z" }, - { url = "https://files.pythonhosted.org/packages/78/53/6719677e92c308207e7f10561a1b16ab8b5c00e9328efc9af7cfd6fb703e/coverage-7.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a181e99301a0ae128493a24cfe5cfb5b488c4e0bf2f8702091473d033494d04f", size = 207784, upload-time = "2024-10-20T22:57:07.857Z" }, - { url = "https://files.pythonhosted.org/packages/fa/dd/7054928930671fcb39ae6a83bb71d9ab5f0afb733172543ced4b09a115ca/coverage-7.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:df57bdbeffe694e7842092c5e2e0bc80fff7f43379d465f932ef36f027179806", size = 208058, upload-time = "2024-10-20T22:57:09.845Z" }, - { url = "https://files.pythonhosted.org/packages/b5/7d/fd656ddc2b38301927b9eb3aae3fe827e7aa82e691923ed43721fd9423c9/coverage-7.6.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bcd1069e710600e8e4cf27f65c90c7843fa8edfb4520fb0ccb88894cad08b11", size = 250772, upload-time = "2024-10-20T22:57:11.147Z" }, - { url = "https://files.pythonhosted.org/packages/90/d0/eb9a3cc2100b83064bb086f18aedde3afffd7de6ead28f69736c00b7f302/coverage-7.6.4-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99b41d18e6b2a48ba949418db48159d7a2e81c5cc290fc934b7d2380515bd0e3", size = 246490, upload-time = "2024-10-20T22:57:13.02Z" }, - { url = "https://files.pythonhosted.org/packages/45/44/3f64f38f6faab8a0cfd2c6bc6eb4c6daead246b97cf5f8fc23bf3788f841/coverage-7.6.4-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1e54712ba3474f34b7ef7a41e65bd9037ad47916ccb1cc78769bae324c01a", size = 248848, upload-time = "2024-10-20T22:57:14.927Z" }, - { url = "https://files.pythonhosted.org/packages/5d/11/4c465a5f98656821e499f4b4619929bd5a34639c466021740ecdca42aa30/coverage-7.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53d202fd109416ce011578f321460795abfe10bb901b883cafd9b3ef851bacfc", size = 248340, upload-time = "2024-10-20T22:57:16.246Z" }, - { url = "https://files.pythonhosted.org/packages/f1/96/ebecda2d016cce9da812f404f720ca5df83c6b29f65dc80d2000d0078741/coverage-7.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:c48167910a8f644671de9f2083a23630fbf7a1cb70ce939440cd3328e0919f70", size = 246229, upload-time = "2024-10-20T22:57:17.546Z" }, - { url = "https://files.pythonhosted.org/packages/16/d9/3d820c00066ae55d69e6d0eae11d6149a5ca7546de469ba9d597f01bf2d7/coverage-7.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cc8ff50b50ce532de2fa7a7daae9dd12f0a699bfcd47f20945364e5c31799fef", size = 247510, upload-time = "2024-10-20T22:57:18.925Z" }, - { url = "https://files.pythonhosted.org/packages/8f/c3/4fa1eb412bb288ff6bfcc163c11700ff06e02c5fad8513817186e460ed43/coverage-7.6.4-cp313-cp313t-win32.whl", hash = "sha256:b8d3a03d9bfcaf5b0141d07a88456bb6a4c3ce55c080712fec8418ef3610230e", size = 210353, upload-time = "2024-10-20T22:57:20.891Z" }, - { url = "https://files.pythonhosted.org/packages/7e/77/03fc2979d1538884d921c2013075917fc927f41cd8526909852fe4494112/coverage-7.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:f3ddf056d3ebcf6ce47bdaf56142af51bb7fad09e4af310241e9db7a3a8022e1", size = 211502, upload-time = "2024-10-20T22:57:22.21Z" }, - { url = "https://files.pythonhosted.org/packages/cc/56/e1d75e8981a2a92c2a777e67c26efa96c66da59d645423146eb9ff3a851b/coverage-7.6.4-pp39.pp310-none-any.whl", hash = "sha256:3c65d37f3a9ebb703e710befdc489a38683a5b152242664b973a7b7b22348a4e", size = 198954, upload-time = "2024-10-20T22:57:38.28Z" }, + { url = "https://files.pythonhosted.org/packages/a5/93/4ad92f71e28ece5c0326e5f4a6630aa4928a8846654a65cfff69b49b95b9/coverage-7.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f8ae553cba74085db385d489c7a792ad66f7f9ba2ee85bfa508aeb84cf0ba07", size = 206713 }, + { url = "https://files.pythonhosted.org/packages/01/ae/747a580b1eda3f2e431d87de48f0604bd7bc92e52a1a95185a4aa585bc47/coverage-7.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8165b796df0bd42e10527a3f493c592ba494f16ef3c8b531288e3d0d72c1f6f0", size = 207149 }, + { url = "https://files.pythonhosted.org/packages/07/1a/1f573f8a6145f6d4c9130bbc120e0024daf1b24cf2a78d7393fa6eb6aba7/coverage-7.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7c8b95bf47db6d19096a5e052ffca0a05f335bc63cef281a6e8fe864d450a72", size = 235584 }, + { url = "https://files.pythonhosted.org/packages/40/42/c8523f2e4db34aa9389caee0d3688b6ada7a84fcc782e943a868a7f302bd/coverage-7.6.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ed9281d1b52628e81393f5eaee24a45cbd64965f41857559c2b7ff19385df51", size = 233486 }, + { url = "https://files.pythonhosted.org/packages/8d/95/565c310fffa16ede1a042e9ea1ca3962af0d8eb5543bc72df6b91dc0c3d5/coverage-7.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0809082ee480bb8f7416507538243c8863ac74fd8a5d2485c46f0f7499f2b491", size = 234649 }, + { url = "https://files.pythonhosted.org/packages/d5/81/3b550674d98968ec29c92e3e8650682be6c8b1fa7581a059e7e12e74c431/coverage-7.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d541423cdd416b78626b55f123412fcf979d22a2c39fce251b350de38c15c15b", size = 233744 }, + { url = "https://files.pythonhosted.org/packages/0d/70/d66c7f51b3e33aabc5ea9f9624c1c9d9655472962270eb5e7b0d32707224/coverage-7.6.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58809e238a8a12a625c70450b48e8767cff9eb67c62e6154a642b21ddf79baea", size = 232204 }, + { url = "https://files.pythonhosted.org/packages/23/2d/2b3a2dbed7a5f40693404c8a09e779d7c1a5fbed089d3e7224c002129ec8/coverage-7.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c9b8e184898ed014884ca84c70562b4a82cbc63b044d366fedc68bc2b2f3394a", size = 233335 }, + { url = "https://files.pythonhosted.org/packages/5a/4f/92d1d2ad720d698a4e71c176eacf531bfb8e0721d5ad560556f2c484a513/coverage-7.6.4-cp310-cp310-win32.whl", hash = "sha256:6bd818b7ea14bc6e1f06e241e8234508b21edf1b242d49831831a9450e2f35fa", size = 209435 }, + { url = "https://files.pythonhosted.org/packages/c7/b9/cdf158e7991e2287bcf9082670928badb73d310047facac203ff8dcd5ff3/coverage-7.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:06babbb8f4e74b063dbaeb74ad68dfce9186c595a15f11f5d5683f748fa1d172", size = 210243 }, + { url = "https://files.pythonhosted.org/packages/87/31/9c0cf84f0dfcbe4215b7eb95c31777cdc0483c13390e69584c8150c85175/coverage-7.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:73d2b73584446e66ee633eaad1a56aad577c077f46c35ca3283cd687b7715b0b", size = 206819 }, + { url = "https://files.pythonhosted.org/packages/53/ed/a38401079ad320ad6e054a01ec2b61d270511aeb3c201c80e99c841229d5/coverage-7.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51b44306032045b383a7a8a2c13878de375117946d68dcb54308111f39775a25", size = 207263 }, + { url = "https://files.pythonhosted.org/packages/20/e7/c3ad33b179ab4213f0d70da25a9c214d52464efa11caeab438592eb1d837/coverage-7.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3fb02fe73bed561fa12d279a417b432e5b50fe03e8d663d61b3d5990f29546", size = 239205 }, + { url = "https://files.pythonhosted.org/packages/36/91/fc02e8d8e694f557752120487fd982f654ba1421bbaa5560debf96ddceda/coverage-7.6.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed8fe9189d2beb6edc14d3ad19800626e1d9f2d975e436f84e19efb7fa19469b", size = 236612 }, + { url = "https://files.pythonhosted.org/packages/cc/57/cb08f0eda0389a9a8aaa4fc1f9fec7ac361c3e2d68efd5890d7042c18aa3/coverage-7.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b369ead6527d025a0fe7bd3864e46dbee3aa8f652d48df6174f8d0bac9e26e0e", size = 238479 }, + { url = "https://files.pythonhosted.org/packages/d5/c9/2c7681a9b3ca6e6f43d489c2e6653a53278ed857fd6e7010490c307b0a47/coverage-7.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ade3ca1e5f0ff46b678b66201f7ff477e8fa11fb537f3b55c3f0568fbfe6e718", size = 237405 }, + { url = "https://files.pythonhosted.org/packages/b5/4e/ebfc6944b96317df8b537ae875d2e57c27b84eb98820bc0a1055f358f056/coverage-7.6.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:27fb4a050aaf18772db513091c9c13f6cb94ed40eacdef8dad8411d92d9992db", size = 236038 }, + { url = "https://files.pythonhosted.org/packages/13/f2/3a0bf1841a97c0654905e2ef531170f02c89fad2555879db8fe41a097871/coverage-7.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4f704f0998911abf728a7783799444fcbbe8261c4a6c166f667937ae6a8aa522", size = 236812 }, + { url = "https://files.pythonhosted.org/packages/b9/9c/66bf59226b52ce6ed9541b02d33e80a6e816a832558fbdc1111a7bd3abd4/coverage-7.6.4-cp311-cp311-win32.whl", hash = "sha256:29155cd511ee058e260db648b6182c419422a0d2e9a4fa44501898cf918866cf", size = 209400 }, + { url = "https://files.pythonhosted.org/packages/2a/a0/b0790934c04dfc8d658d4a62acb8f7ca0efdf3818456fcad757b11c6479d/coverage-7.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:8902dd6a30173d4ef09954bfcb24b5d7b5190cf14a43170e386979651e09ba19", size = 210243 }, + { url = "https://files.pythonhosted.org/packages/7d/e7/9291de916d084f41adddfd4b82246e68d61d6a75747f075f7e64628998d2/coverage-7.6.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12394842a3a8affa3ba62b0d4ab7e9e210c5e366fbac3e8b2a68636fb19892c2", size = 207013 }, + { url = "https://files.pythonhosted.org/packages/27/03/932c2c5717a7fa80cd43c6a07d3177076d97b79f12f40f882f9916db0063/coverage-7.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b6b4c83d8e8ea79f27ab80778c19bc037759aea298da4b56621f4474ffeb117", size = 207251 }, + { url = "https://files.pythonhosted.org/packages/d5/3f/0af47dcb9327f65a45455fbca846fe96eb57c153af46c4754a3ba678938a/coverage-7.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d5b8007f81b88696d06f7df0cb9af0d3b835fe0c8dbf489bad70b45f0e45613", size = 240268 }, + { url = "https://files.pythonhosted.org/packages/8a/3c/37a9d81bbd4b23bc7d46ca820e16174c613579c66342faa390a271d2e18b/coverage-7.6.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b57b768feb866f44eeed9f46975f3d6406380275c5ddfe22f531a2bf187eda27", size = 237298 }, + { url = "https://files.pythonhosted.org/packages/c0/70/6b0627e5bd68204ee580126ed3513140b2298995c1233bd67404b4e44d0e/coverage-7.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5915fcdec0e54ee229926868e9b08586376cae1f5faa9bbaf8faf3561b393d52", size = 239367 }, + { url = "https://files.pythonhosted.org/packages/3c/eb/634d7dfab24ac3b790bebaf9da0f4a5352cbc125ce6a9d5c6cf4c6cae3c7/coverage-7.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b58c672d14f16ed92a48db984612f5ce3836ae7d72cdd161001cc54512571f2", size = 238853 }, + { url = "https://files.pythonhosted.org/packages/d9/0d/8e3ed00f1266ef7472a4e33458f42e39492e01a64281084fb3043553d3f1/coverage-7.6.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2fdef0d83a2d08d69b1f2210a93c416d54e14d9eb398f6ab2f0a209433db19e1", size = 237160 }, + { url = "https://files.pythonhosted.org/packages/ce/9c/4337f468ef0ab7a2e0887a9c9da0e58e2eada6fc6cbee637a4acd5dfd8a9/coverage-7.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cf717ee42012be8c0cb205dbbf18ffa9003c4cbf4ad078db47b95e10748eec5", size = 238824 }, + { url = "https://files.pythonhosted.org/packages/5e/09/3e94912b8dd37251377bb02727a33a67ee96b84bbbe092f132b401ca5dd9/coverage-7.6.4-cp312-cp312-win32.whl", hash = "sha256:7bb92c539a624cf86296dd0c68cd5cc286c9eef2d0c3b8b192b604ce9de20a17", size = 209639 }, + { url = "https://files.pythonhosted.org/packages/01/69/d4f3a4101171f32bc5b3caec8ff94c2c60f700107a6aaef7244b2c166793/coverage-7.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:1032e178b76a4e2b5b32e19d0fd0abbce4b58e77a1ca695820d10e491fa32b08", size = 210428 }, + { url = "https://files.pythonhosted.org/packages/c2/4d/2dede4f7cb5a70fb0bb40a57627fddf1dbdc6b9c1db81f7c4dcdcb19e2f4/coverage-7.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:023bf8ee3ec6d35af9c1c6ccc1d18fa69afa1cb29eaac57cb064dbb262a517f9", size = 207039 }, + { url = "https://files.pythonhosted.org/packages/3f/f9/d86368ae8c79e28f1fb458ebc76ae9ff3e8bd8069adc24e8f2fed03c58b7/coverage-7.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0ac3d42cb51c4b12df9c5f0dd2f13a4f24f01943627120ec4d293c9181219ba", size = 207298 }, + { url = "https://files.pythonhosted.org/packages/64/c5/b4cc3c3f64622c58fbfd4d8b9a7a8ce9d355f172f91fcabbba1f026852f6/coverage-7.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8fe4984b431f8621ca53d9380901f62bfb54ff759a1348cd140490ada7b693c", size = 239813 }, + { url = "https://files.pythonhosted.org/packages/8a/86/14c42e60b70a79b26099e4d289ccdfefbc68624d096f4481163085aa614c/coverage-7.6.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fbd612f8a091954a0c8dd4c0b571b973487277d26476f8480bfa4b2a65b5d06", size = 236959 }, + { url = "https://files.pythonhosted.org/packages/7f/f8/4436a643631a2fbab4b44d54f515028f6099bfb1cd95b13cfbf701e7f2f2/coverage-7.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dacbc52de979f2823a819571f2e3a350a7e36b8cb7484cdb1e289bceaf35305f", size = 238950 }, + { url = "https://files.pythonhosted.org/packages/49/50/1571810ddd01f99a0a8be464a4ac8b147f322cd1e8e296a1528984fc560b/coverage-7.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dab4d16dfef34b185032580e2f2f89253d302facba093d5fa9dbe04f569c4f4b", size = 238610 }, + { url = "https://files.pythonhosted.org/packages/f3/8c/6312d241fe7cbd1f0cade34a62fea6f333d1a261255d76b9a87074d8703c/coverage-7.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:862264b12ebb65ad8d863d51f17758b1684560b66ab02770d4f0baf2ff75da21", size = 236697 }, + { url = "https://files.pythonhosted.org/packages/ce/5f/fef33dfd05d87ee9030f614c857deb6df6556b8f6a1c51bbbb41e24ee5ac/coverage-7.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5beb1ee382ad32afe424097de57134175fea3faf847b9af002cc7895be4e2a5a", size = 238541 }, + { url = "https://files.pythonhosted.org/packages/a9/64/6a984b6e92e1ea1353b7ffa08e27f707a5e29b044622445859200f541e8c/coverage-7.6.4-cp313-cp313-win32.whl", hash = "sha256:bf20494da9653f6410213424f5f8ad0ed885e01f7e8e59811f572bdb20b8972e", size = 209707 }, + { url = "https://files.pythonhosted.org/packages/5c/60/ce5a9e942e9543783b3db5d942e0578b391c25cdd5e7f342d854ea83d6b7/coverage-7.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:182e6cd5c040cec0a1c8d415a87b67ed01193ed9ad458ee427741c7d8513d963", size = 210439 }, + { url = "https://files.pythonhosted.org/packages/78/53/6719677e92c308207e7f10561a1b16ab8b5c00e9328efc9af7cfd6fb703e/coverage-7.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a181e99301a0ae128493a24cfe5cfb5b488c4e0bf2f8702091473d033494d04f", size = 207784 }, + { url = "https://files.pythonhosted.org/packages/fa/dd/7054928930671fcb39ae6a83bb71d9ab5f0afb733172543ced4b09a115ca/coverage-7.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:df57bdbeffe694e7842092c5e2e0bc80fff7f43379d465f932ef36f027179806", size = 208058 }, + { url = "https://files.pythonhosted.org/packages/b5/7d/fd656ddc2b38301927b9eb3aae3fe827e7aa82e691923ed43721fd9423c9/coverage-7.6.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bcd1069e710600e8e4cf27f65c90c7843fa8edfb4520fb0ccb88894cad08b11", size = 250772 }, + { url = "https://files.pythonhosted.org/packages/90/d0/eb9a3cc2100b83064bb086f18aedde3afffd7de6ead28f69736c00b7f302/coverage-7.6.4-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99b41d18e6b2a48ba949418db48159d7a2e81c5cc290fc934b7d2380515bd0e3", size = 246490 }, + { url = "https://files.pythonhosted.org/packages/45/44/3f64f38f6faab8a0cfd2c6bc6eb4c6daead246b97cf5f8fc23bf3788f841/coverage-7.6.4-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1e54712ba3474f34b7ef7a41e65bd9037ad47916ccb1cc78769bae324c01a", size = 248848 }, + { url = "https://files.pythonhosted.org/packages/5d/11/4c465a5f98656821e499f4b4619929bd5a34639c466021740ecdca42aa30/coverage-7.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53d202fd109416ce011578f321460795abfe10bb901b883cafd9b3ef851bacfc", size = 248340 }, + { url = "https://files.pythonhosted.org/packages/f1/96/ebecda2d016cce9da812f404f720ca5df83c6b29f65dc80d2000d0078741/coverage-7.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:c48167910a8f644671de9f2083a23630fbf7a1cb70ce939440cd3328e0919f70", size = 246229 }, + { url = "https://files.pythonhosted.org/packages/16/d9/3d820c00066ae55d69e6d0eae11d6149a5ca7546de469ba9d597f01bf2d7/coverage-7.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cc8ff50b50ce532de2fa7a7daae9dd12f0a699bfcd47f20945364e5c31799fef", size = 247510 }, + { url = "https://files.pythonhosted.org/packages/8f/c3/4fa1eb412bb288ff6bfcc163c11700ff06e02c5fad8513817186e460ed43/coverage-7.6.4-cp313-cp313t-win32.whl", hash = "sha256:b8d3a03d9bfcaf5b0141d07a88456bb6a4c3ce55c080712fec8418ef3610230e", size = 210353 }, + { url = "https://files.pythonhosted.org/packages/7e/77/03fc2979d1538884d921c2013075917fc927f41cd8526909852fe4494112/coverage-7.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:f3ddf056d3ebcf6ce47bdaf56142af51bb7fad09e4af310241e9db7a3a8022e1", size = 211502 }, + { url = "https://files.pythonhosted.org/packages/cc/56/e1d75e8981a2a92c2a777e67c26efa96c66da59d645423146eb9ff3a851b/coverage-7.6.4-pp39.pp310-none-any.whl", hash = "sha256:3c65d37f3a9ebb703e710befdc489a38683a5b152242664b973a7b7b22348a4e", size = 198954 }, ] [package.optional-dependencies] @@ -462,80 +573,80 @@ toml = [ name = "cycler" version = "0.12.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321 }, ] [[package]] name = "cython" version = "3.0.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/68/09/ffb61f29b8e3d207c444032b21328327d753e274ea081bc74e009827cc81/Cython-3.0.8.tar.gz", hash = "sha256:8333423d8fd5765e7cceea3a9985dd1e0a5dfeb2734629e1a2ed2d6233d39de6", size = 2744096, upload-time = "2024-01-10T11:01:02.155Z" } +sdist = { url = "https://files.pythonhosted.org/packages/68/09/ffb61f29b8e3d207c444032b21328327d753e274ea081bc74e009827cc81/Cython-3.0.8.tar.gz", hash = "sha256:8333423d8fd5765e7cceea3a9985dd1e0a5dfeb2734629e1a2ed2d6233d39de6", size = 2744096 } wheels = [ - { url = "https://files.pythonhosted.org/packages/63/f4/d2542e186fe33ec1cc542770fb17466421ed54f4ffe04d00fe9549d0a467/Cython-3.0.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a846e0a38e2b24e9a5c5dc74b0e54c6e29420d88d1dafabc99e0fc0f3e338636", size = 3100459, upload-time = "2024-01-10T11:33:49.545Z" }, - { url = "https://files.pythonhosted.org/packages/fc/27/2652f395aa708fb3081148e0df3ab700bd7288636c65332ef7febad6a380/Cython-3.0.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45523fdc2b78d79b32834cc1cc12dc2ca8967af87e22a3ee1bff20e77c7f5520", size = 3456626, upload-time = "2024-01-10T11:01:44.897Z" }, - { url = "https://files.pythonhosted.org/packages/f9/bd/e8a1d26d04c08a67bcc383f2ea5493a4e77f37a8770ead00a238b08ad729/Cython-3.0.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa0b7f3f841fe087410cab66778e2d3fb20ae2d2078a2be3dffe66c6574be39", size = 3621379, upload-time = "2024-01-10T11:01:48.777Z" }, - { url = "https://files.pythonhosted.org/packages/03/ae/ead7ec03d0062d439879d41b7830e4f2480213f7beabf2f7052a191cc6f7/Cython-3.0.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e87294e33e40c289c77a135f491cd721bd089f193f956f7b8ed5aa2d0b8c558f", size = 3671873, upload-time = "2024-01-10T11:01:51.858Z" }, - { url = "https://files.pythonhosted.org/packages/63/b0/81dad725604d7b529c492f873a7fa1b5800704a9f26e100ed25e9fd8d057/Cython-3.0.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a1df7a129344b1215c20096d33c00193437df1a8fcca25b71f17c23b1a44f782", size = 3463832, upload-time = "2024-01-10T11:01:55.364Z" }, - { url = "https://files.pythonhosted.org/packages/13/cd/72b8e0af597ac1b376421847acf6d6fa252e60059a2a00dcf05ceb16d28f/Cython-3.0.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:13c2a5e57a0358da467d97667297bf820b62a1a87ae47c5f87938b9bb593acbd", size = 3618325, upload-time = "2024-01-10T11:01:59.03Z" }, - { url = "https://files.pythonhosted.org/packages/ef/73/11a4355d8b8966504c751e5bcb25916c4140de27bb2ba1b54ff21994d7fe/Cython-3.0.8-cp310-cp310-win32.whl", hash = "sha256:96b028f044f5880e3cb18ecdcfc6c8d3ce9d0af28418d5ab464509f26d8adf12", size = 2571305, upload-time = "2024-01-10T11:02:02.589Z" }, - { url = "https://files.pythonhosted.org/packages/18/15/fdc0c3552d20f9337b134a36d786da24e47998fc39f62cb61c1534f26123/Cython-3.0.8-cp310-cp310-win_amd64.whl", hash = "sha256:8140597a8b5cc4f119a1190f5a2228a84f5ca6d8d9ec386cfce24663f48b2539", size = 2776113, upload-time = "2024-01-10T11:02:05.581Z" }, - { url = "https://files.pythonhosted.org/packages/db/a7/f4a0bc9a80e23b380daa2ebb4879bf434aaa0b3b91f7ad8a7f9762b4bd1b/Cython-3.0.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aae26f9663e50caf9657148403d9874eea41770ecdd6caf381d177c2b1bb82ba", size = 3113615, upload-time = "2024-01-10T11:34:05.899Z" }, - { url = "https://files.pythonhosted.org/packages/e9/e9/e9295df74246c165b91253a473bfa179debf739c9bee961cbb3ae56c2b79/Cython-3.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:547eb3cdb2f8c6f48e6865d5a741d9dd051c25b3ce076fbca571727977b28ac3", size = 3436320, upload-time = "2024-01-10T11:02:08.689Z" }, - { url = "https://files.pythonhosted.org/packages/26/2c/6a887c957aa53e44f928119dea628a5dfacc8e875424034f5fecac9daba4/Cython-3.0.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a567d4b9ba70b26db89d75b243529de9e649a2f56384287533cf91512705bee", size = 3591755, upload-time = "2024-01-10T11:02:11.773Z" }, - { url = "https://files.pythonhosted.org/packages/ba/b8/f9c97bae6281da50b3ecb1f7fef0f7f7851eae084609b364717a2b366bf1/Cython-3.0.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51d1426263b0e82fb22bda8ea60dc77a428581cc19e97741011b938445d383f1", size = 3636099, upload-time = "2024-01-10T11:02:15.191Z" }, - { url = "https://files.pythonhosted.org/packages/17/ae/cd055c2c081c67a6fcad1d8d17d82bd6395b14c6741e3a938f40318c8bc5/Cython-3.0.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c26daaeccda072459b48d211415fd1e5507c06bcd976fa0d5b8b9f1063467d7b", size = 3458119, upload-time = "2024-01-10T11:02:19.103Z" }, - { url = "https://files.pythonhosted.org/packages/72/ab/ac6f5548d6194f4bb2fc8c6c996aa7369f0fa1403e4d4de787d9e9309b27/Cython-3.0.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:289ce7838208211cd166e975865fd73b0649bf118170b6cebaedfbdaf4a37795", size = 3614418, upload-time = "2024-01-10T11:02:22.732Z" }, - { url = "https://files.pythonhosted.org/packages/70/e2/3e3e448b7a94887bec3235bcb71957b6681dc42b4536459f8f54d46fa936/Cython-3.0.8-cp311-cp311-win32.whl", hash = "sha256:c8aa05f5e17f8042a3be052c24f2edc013fb8af874b0bf76907d16c51b4e7871", size = 2572819, upload-time = "2024-01-10T11:02:25.976Z" }, - { url = "https://files.pythonhosted.org/packages/85/7d/58635941dfbb5b4e197adb88080b9cbfb230dc3b75683698a530a1989bdb/Cython-3.0.8-cp311-cp311-win_amd64.whl", hash = "sha256:000dc9e135d0eec6ecb2b40a5b02d0868a2f8d2e027a41b0fe16a908a9e6de02", size = 2784167, upload-time = "2024-01-10T11:02:28.808Z" }, - { url = "https://files.pythonhosted.org/packages/3d/8e/28f8c6109990eef7317ab7e43644092b49a88a39f9373dcd19318946df09/Cython-3.0.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:90d3fe31db55685d8cb97d43b0ec39ef614fcf660f83c77ed06aa670cb0e164f", size = 3135638, upload-time = "2024-01-10T11:34:22.889Z" }, - { url = "https://files.pythonhosted.org/packages/83/1f/4720cb682b8ed1ab9749dea35351a66dd29b6a022628cce038415660c384/Cython-3.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e24791ddae2324e88e3c902a765595c738f19ae34ee66bfb1a6dac54b1833419", size = 3340052, upload-time = "2024-01-10T11:02:32.471Z" }, - { url = "https://files.pythonhosted.org/packages/8a/47/ec3fceb9e8f7d6fa130216b8740038e1df7c8e5f215bba363fcf1272a6c1/Cython-3.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f020fa1c0552052e0660790b8153b79e3fc9a15dbd8f1d0b841fe5d204a6ae6", size = 3510079, upload-time = "2024-01-10T11:02:35.312Z" }, - { url = "https://files.pythonhosted.org/packages/71/31/b458127851e248effb909e2791b55870914863cde7c60b94db5ee65d7867/Cython-3.0.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18bfa387d7a7f77d7b2526af69a65dbd0b731b8d941aaff5becff8e21f6d7717", size = 3573972, upload-time = "2024-01-10T11:02:39.044Z" }, - { url = "https://files.pythonhosted.org/packages/6b/d5/ca6513844d0634abd05ba12304053a454bb70441a9520afa9897d4300156/Cython-3.0.8-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fe81b339cffd87c0069c6049b4d33e28bdd1874625ee515785bf42c9fdff3658", size = 3356158, upload-time = "2024-01-10T11:02:42.125Z" }, - { url = "https://files.pythonhosted.org/packages/33/59/98a87b6264f4ad45c820db13c4ec657567476efde020c49443cc842a86af/Cython-3.0.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:80fd94c076e1e1b1ee40a309be03080b75f413e8997cddcf401a118879863388", size = 3522312, upload-time = "2024-01-10T11:02:45.056Z" }, - { url = "https://files.pythonhosted.org/packages/2b/cb/132115d07a0b9d4f075e0741db70a5416b424dcd875b2bb0dd805e818222/Cython-3.0.8-cp312-cp312-win32.whl", hash = "sha256:85077915a93e359a9b920280d214dc0cf8a62773e1f3d7d30fab8ea4daed670c", size = 2602579, upload-time = "2024-01-10T11:02:48.368Z" }, - { url = "https://files.pythonhosted.org/packages/b4/69/cb4620287cd9ef461103e122c0a2ae7f7ecf183e02510676fb5a15c95b05/Cython-3.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:0cb2dcc565c7851f75d496f724a384a790fab12d1b82461b663e66605bec429a", size = 2791268, upload-time = "2024-01-10T11:02:51.483Z" }, - { url = "https://files.pythonhosted.org/packages/e3/7f/f584f5d15323feb897d42ef0e9d910649e2150d7a30cf7e7a8cc1d236e6f/Cython-3.0.8-py2.py3-none-any.whl", hash = "sha256:171b27051253d3f9108e9759e504ba59ff06e7f7ba944457f94deaf9c21bf0b6", size = 1168213, upload-time = "2024-01-10T11:00:56.857Z" }, + { url = "https://files.pythonhosted.org/packages/63/f4/d2542e186fe33ec1cc542770fb17466421ed54f4ffe04d00fe9549d0a467/Cython-3.0.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a846e0a38e2b24e9a5c5dc74b0e54c6e29420d88d1dafabc99e0fc0f3e338636", size = 3100459 }, + { url = "https://files.pythonhosted.org/packages/fc/27/2652f395aa708fb3081148e0df3ab700bd7288636c65332ef7febad6a380/Cython-3.0.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45523fdc2b78d79b32834cc1cc12dc2ca8967af87e22a3ee1bff20e77c7f5520", size = 3456626 }, + { url = "https://files.pythonhosted.org/packages/f9/bd/e8a1d26d04c08a67bcc383f2ea5493a4e77f37a8770ead00a238b08ad729/Cython-3.0.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa0b7f3f841fe087410cab66778e2d3fb20ae2d2078a2be3dffe66c6574be39", size = 3621379 }, + { url = "https://files.pythonhosted.org/packages/03/ae/ead7ec03d0062d439879d41b7830e4f2480213f7beabf2f7052a191cc6f7/Cython-3.0.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e87294e33e40c289c77a135f491cd721bd089f193f956f7b8ed5aa2d0b8c558f", size = 3671873 }, + { url = "https://files.pythonhosted.org/packages/63/b0/81dad725604d7b529c492f873a7fa1b5800704a9f26e100ed25e9fd8d057/Cython-3.0.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a1df7a129344b1215c20096d33c00193437df1a8fcca25b71f17c23b1a44f782", size = 3463832 }, + { url = "https://files.pythonhosted.org/packages/13/cd/72b8e0af597ac1b376421847acf6d6fa252e60059a2a00dcf05ceb16d28f/Cython-3.0.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:13c2a5e57a0358da467d97667297bf820b62a1a87ae47c5f87938b9bb593acbd", size = 3618325 }, + { url = "https://files.pythonhosted.org/packages/ef/73/11a4355d8b8966504c751e5bcb25916c4140de27bb2ba1b54ff21994d7fe/Cython-3.0.8-cp310-cp310-win32.whl", hash = "sha256:96b028f044f5880e3cb18ecdcfc6c8d3ce9d0af28418d5ab464509f26d8adf12", size = 2571305 }, + { url = "https://files.pythonhosted.org/packages/18/15/fdc0c3552d20f9337b134a36d786da24e47998fc39f62cb61c1534f26123/Cython-3.0.8-cp310-cp310-win_amd64.whl", hash = "sha256:8140597a8b5cc4f119a1190f5a2228a84f5ca6d8d9ec386cfce24663f48b2539", size = 2776113 }, + { url = "https://files.pythonhosted.org/packages/db/a7/f4a0bc9a80e23b380daa2ebb4879bf434aaa0b3b91f7ad8a7f9762b4bd1b/Cython-3.0.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aae26f9663e50caf9657148403d9874eea41770ecdd6caf381d177c2b1bb82ba", size = 3113615 }, + { url = "https://files.pythonhosted.org/packages/e9/e9/e9295df74246c165b91253a473bfa179debf739c9bee961cbb3ae56c2b79/Cython-3.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:547eb3cdb2f8c6f48e6865d5a741d9dd051c25b3ce076fbca571727977b28ac3", size = 3436320 }, + { url = "https://files.pythonhosted.org/packages/26/2c/6a887c957aa53e44f928119dea628a5dfacc8e875424034f5fecac9daba4/Cython-3.0.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a567d4b9ba70b26db89d75b243529de9e649a2f56384287533cf91512705bee", size = 3591755 }, + { url = "https://files.pythonhosted.org/packages/ba/b8/f9c97bae6281da50b3ecb1f7fef0f7f7851eae084609b364717a2b366bf1/Cython-3.0.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51d1426263b0e82fb22bda8ea60dc77a428581cc19e97741011b938445d383f1", size = 3636099 }, + { url = "https://files.pythonhosted.org/packages/17/ae/cd055c2c081c67a6fcad1d8d17d82bd6395b14c6741e3a938f40318c8bc5/Cython-3.0.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c26daaeccda072459b48d211415fd1e5507c06bcd976fa0d5b8b9f1063467d7b", size = 3458119 }, + { url = "https://files.pythonhosted.org/packages/72/ab/ac6f5548d6194f4bb2fc8c6c996aa7369f0fa1403e4d4de787d9e9309b27/Cython-3.0.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:289ce7838208211cd166e975865fd73b0649bf118170b6cebaedfbdaf4a37795", size = 3614418 }, + { url = "https://files.pythonhosted.org/packages/70/e2/3e3e448b7a94887bec3235bcb71957b6681dc42b4536459f8f54d46fa936/Cython-3.0.8-cp311-cp311-win32.whl", hash = "sha256:c8aa05f5e17f8042a3be052c24f2edc013fb8af874b0bf76907d16c51b4e7871", size = 2572819 }, + { url = "https://files.pythonhosted.org/packages/85/7d/58635941dfbb5b4e197adb88080b9cbfb230dc3b75683698a530a1989bdb/Cython-3.0.8-cp311-cp311-win_amd64.whl", hash = "sha256:000dc9e135d0eec6ecb2b40a5b02d0868a2f8d2e027a41b0fe16a908a9e6de02", size = 2784167 }, + { url = "https://files.pythonhosted.org/packages/3d/8e/28f8c6109990eef7317ab7e43644092b49a88a39f9373dcd19318946df09/Cython-3.0.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:90d3fe31db55685d8cb97d43b0ec39ef614fcf660f83c77ed06aa670cb0e164f", size = 3135638 }, + { url = "https://files.pythonhosted.org/packages/83/1f/4720cb682b8ed1ab9749dea35351a66dd29b6a022628cce038415660c384/Cython-3.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e24791ddae2324e88e3c902a765595c738f19ae34ee66bfb1a6dac54b1833419", size = 3340052 }, + { url = "https://files.pythonhosted.org/packages/8a/47/ec3fceb9e8f7d6fa130216b8740038e1df7c8e5f215bba363fcf1272a6c1/Cython-3.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f020fa1c0552052e0660790b8153b79e3fc9a15dbd8f1d0b841fe5d204a6ae6", size = 3510079 }, + { url = "https://files.pythonhosted.org/packages/71/31/b458127851e248effb909e2791b55870914863cde7c60b94db5ee65d7867/Cython-3.0.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18bfa387d7a7f77d7b2526af69a65dbd0b731b8d941aaff5becff8e21f6d7717", size = 3573972 }, + { url = "https://files.pythonhosted.org/packages/6b/d5/ca6513844d0634abd05ba12304053a454bb70441a9520afa9897d4300156/Cython-3.0.8-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fe81b339cffd87c0069c6049b4d33e28bdd1874625ee515785bf42c9fdff3658", size = 3356158 }, + { url = "https://files.pythonhosted.org/packages/33/59/98a87b6264f4ad45c820db13c4ec657567476efde020c49443cc842a86af/Cython-3.0.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:80fd94c076e1e1b1ee40a309be03080b75f413e8997cddcf401a118879863388", size = 3522312 }, + { url = "https://files.pythonhosted.org/packages/2b/cb/132115d07a0b9d4f075e0741db70a5416b424dcd875b2bb0dd805e818222/Cython-3.0.8-cp312-cp312-win32.whl", hash = "sha256:85077915a93e359a9b920280d214dc0cf8a62773e1f3d7d30fab8ea4daed670c", size = 2602579 }, + { url = "https://files.pythonhosted.org/packages/b4/69/cb4620287cd9ef461103e122c0a2ae7f7ecf183e02510676fb5a15c95b05/Cython-3.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:0cb2dcc565c7851f75d496f724a384a790fab12d1b82461b663e66605bec429a", size = 2791268 }, + { url = "https://files.pythonhosted.org/packages/e3/7f/f584f5d15323feb897d42ef0e9d910649e2150d7a30cf7e7a8cc1d236e6f/Cython-3.0.8-py2.py3-none-any.whl", hash = "sha256:171b27051253d3f9108e9759e504ba59ff06e7f7ba944457f94deaf9c21bf0b6", size = 1168213 }, ] [[package]] name = "easydict" version = "1.11" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0a/d2/deb3296d08097fedd622d423c0ec8b68b78c1704b3f1545326f6ce05c75c/easydict-1.11.tar.gz", hash = "sha256:dcb1d2ed28eb300c8e46cd371340373abc62f7c14d6dea74fdfc6f1069061c78", size = 6644, upload-time = "2023-10-23T23:01:37.686Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/d2/deb3296d08097fedd622d423c0ec8b68b78c1704b3f1545326f6ce05c75c/easydict-1.11.tar.gz", hash = "sha256:dcb1d2ed28eb300c8e46cd371340373abc62f7c14d6dea74fdfc6f1069061c78", size = 6644 } [[package]] name = "exceptiongroup" version = "1.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/1c/beef724eaf5b01bb44b6338c8c3494eff7cab376fab4904cfbbc3585dc79/exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68", size = 26264, upload-time = "2023-11-21T08:42:17.407Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/1c/beef724eaf5b01bb44b6338c8c3494eff7cab376fab4904cfbbc3585dc79/exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68", size = 26264 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/9a/5028fd52db10e600f1c4674441b968cf2ea4959085bfb5b99fb1250e5f68/exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14", size = 16210, upload-time = "2023-11-21T08:42:15.525Z" }, + { url = "https://files.pythonhosted.org/packages/b8/9a/5028fd52db10e600f1c4674441b968cf2ea4959085bfb5b99fb1250e5f68/exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14", size = 16210 }, ] [[package]] name = "fastapi" -version = "0.115.14" +version = "0.116.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "starlette" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ca/53/8c38a874844a8b0fa10dd8adf3836ac154082cf88d3f22b544e9ceea0a15/fastapi-0.115.14.tar.gz", hash = "sha256:b1de15cdc1c499a4da47914db35d0e4ef8f1ce62b624e94e0e5824421df99739", size = 296263, upload-time = "2025-06-26T15:29:08.21Z" } +sdist = { url = "https://files.pythonhosted.org/packages/78/d7/6c8b3bfe33eeffa208183ec037fee0cce9f7f024089ab1c5d12ef04bd27c/fastapi-0.116.1.tar.gz", hash = "sha256:ed52cbf946abfd70c5a0dccb24673f0670deeb517a88b3544d03c2a6bf283143", size = 296485 } wheels = [ - { url = "https://files.pythonhosted.org/packages/53/50/b1222562c6d270fea83e9c9075b8e8600b8479150a18e4516a6138b980d1/fastapi-0.115.14-py3-none-any.whl", hash = "sha256:6c0c8bf9420bd58f565e585036d971872472b4f7d3f6c73b698e10cffdefb3ca", size = 95514, upload-time = "2025-06-26T15:29:06.49Z" }, + { url = "https://files.pythonhosted.org/packages/e5/47/d63c60f59a59467fda0f93f46335c9d18526d7071f025cb5b89d5353ea42/fastapi-0.116.1-py3-none-any.whl", hash = "sha256:c46ac7c312df840f0c9e220f7964bada936781bc4e2e6eb71f1c4d7553786565", size = 95631 }, ] [[package]] name = "filelock" version = "3.13.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/70/70/41905c80dcfe71b22fb06827b8eae65781783d4a14194bce79d16a013263/filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e", size = 14553, upload-time = "2023-10-30T18:29:39.035Z" } +sdist = { url = "https://files.pythonhosted.org/packages/70/70/41905c80dcfe71b22fb06827b8eae65781783d4a14194bce79d16a013263/filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e", size = 14553 } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/54/84d42a0bee35edba99dee7b59a8d4970eccdd44b99fe728ed912106fc781/filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c", size = 11740, upload-time = "2023-10-30T18:29:37.267Z" }, + { url = "https://files.pythonhosted.org/packages/81/54/84d42a0bee35edba99dee7b59a8d4970eccdd44b99fe728ed912106fc781/filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c", size = 11740 }, ] [[package]] @@ -549,9 +660,9 @@ dependencies = [ { name = "jinja2" }, { name = "werkzeug" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d8/09/c1a7354d3925a3c6c8cfdebf4245bae67d633ffda1ba415add06ffc839c5/flask-3.0.0.tar.gz", hash = "sha256:cfadcdb638b609361d29ec22360d6070a77d7463dcb3ab08d2c2f2f168845f58", size = 674171, upload-time = "2023-09-30T14:36:12.918Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/09/c1a7354d3925a3c6c8cfdebf4245bae67d633ffda1ba415add06ffc839c5/flask-3.0.0.tar.gz", hash = "sha256:cfadcdb638b609361d29ec22360d6070a77d7463dcb3ab08d2c2f2f168845f58", size = 674171 } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/42/015c23096649b908c809c69388a805a571a3bea44362fe87e33fc3afa01f/flask-3.0.0-py3-none-any.whl", hash = "sha256:21128f47e4e3b9d597a3e8521a329bf56909b690fcc3fa3e477725aa81367638", size = 99724, upload-time = "2023-09-30T14:36:10.961Z" }, + { url = "https://files.pythonhosted.org/packages/36/42/015c23096649b908c809c69388a805a571a3bea44362fe87e33fc3afa01f/flask-3.0.0-py3-none-any.whl", hash = "sha256:21128f47e4e3b9d597a3e8521a329bf56909b690fcc3fa3e477725aa81367638", size = 99724 }, ] [[package]] @@ -561,9 +672,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "flask" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/40/6a/a8d56d60bcfa1ec3e4fdad81b45aafd508c3bd5c244a16526fa29139d7d4/flask_cors-4.0.1.tar.gz", hash = "sha256:eeb69b342142fdbf4766ad99357a7f3876a2ceb77689dc10ff912aac06c389e4", size = 30306, upload-time = "2024-05-04T19:49:43.538Z" } +sdist = { url = "https://files.pythonhosted.org/packages/40/6a/a8d56d60bcfa1ec3e4fdad81b45aafd508c3bd5c244a16526fa29139d7d4/flask_cors-4.0.1.tar.gz", hash = "sha256:eeb69b342142fdbf4766ad99357a7f3876a2ceb77689dc10ff912aac06c389e4", size = 30306 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/52/2aa6285f104616f73ee1ad7905a16b2b35af0143034ad0cf7b64bcba715c/Flask_Cors-4.0.1-py2.py3-none-any.whl", hash = "sha256:f2a704e4458665580c074b714c4627dd5a306b333deb9074d0b1794dfa2fb677", size = 14290, upload-time = "2024-05-04T19:49:41.721Z" }, + { url = "https://files.pythonhosted.org/packages/8b/52/2aa6285f104616f73ee1ad7905a16b2b35af0143034ad0cf7b64bcba715c/Flask_Cors-4.0.1-py2.py3-none-any.whl", hash = "sha256:f2a704e4458665580c074b714c4627dd5a306b333deb9074d0b1794dfa2fb677", size = 14290 }, ] [[package]] @@ -574,60 +685,60 @@ dependencies = [ { name = "flask" }, { name = "werkzeug" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c3/6e/2f4e13e373bb49e68c02c51ceadd22d172715a06716f9299d9df01b6ddb2/Flask-Login-0.6.3.tar.gz", hash = "sha256:5e23d14a607ef12806c699590b89d0f0e0d67baeec599d75947bf9c147330333", size = 48834, upload-time = "2023-10-30T14:53:21.151Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/6e/2f4e13e373bb49e68c02c51ceadd22d172715a06716f9299d9df01b6ddb2/Flask-Login-0.6.3.tar.gz", hash = "sha256:5e23d14a607ef12806c699590b89d0f0e0d67baeec599d75947bf9c147330333", size = 48834 } wheels = [ - { url = "https://files.pythonhosted.org/packages/59/f5/67e9cc5c2036f58115f9fe0f00d203cf6780c3ff8ae0e705e7a9d9e8ff9e/Flask_Login-0.6.3-py3-none-any.whl", hash = "sha256:849b25b82a436bf830a054e74214074af59097171562ab10bfa999e6b78aae5d", size = 17303, upload-time = "2023-10-30T14:53:19.636Z" }, + { url = "https://files.pythonhosted.org/packages/59/f5/67e9cc5c2036f58115f9fe0f00d203cf6780c3ff8ae0e705e7a9d9e8ff9e/Flask_Login-0.6.3-py3-none-any.whl", hash = "sha256:849b25b82a436bf830a054e74214074af59097171562ab10bfa999e6b78aae5d", size = 17303 }, ] [[package]] name = "flatbuffers" version = "23.5.26" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0c/6e/3e52cd294d8e7a61e010973cce076a0cb2c6c0dfd4d0b7a13648c1b98329/flatbuffers-23.5.26.tar.gz", hash = "sha256:9ea1144cac05ce5d86e2859f431c6cd5e66cd9c78c558317c7955fb8d4c78d89", size = 22114, upload-time = "2023-05-26T17:35:16.034Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/6e/3e52cd294d8e7a61e010973cce076a0cb2c6c0dfd4d0b7a13648c1b98329/flatbuffers-23.5.26.tar.gz", hash = "sha256:9ea1144cac05ce5d86e2859f431c6cd5e66cd9c78c558317c7955fb8d4c78d89", size = 22114 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/12/d5c79ee252793ffe845d58a913197bfa02ae9a0b5c9bc3dc4b58d477b9e7/flatbuffers-23.5.26-py2.py3-none-any.whl", hash = "sha256:c0ff356da363087b915fde4b8b45bdda73432fc17cddb3c8157472eab1422ad1", size = 26744, upload-time = "2023-05-26T17:35:14.269Z" }, + { url = "https://files.pythonhosted.org/packages/6f/12/d5c79ee252793ffe845d58a913197bfa02ae9a0b5c9bc3dc4b58d477b9e7/flatbuffers-23.5.26-py2.py3-none-any.whl", hash = "sha256:c0ff356da363087b915fde4b8b45bdda73432fc17cddb3c8157472eab1422ad1", size = 26744 }, ] [[package]] name = "fonttools" version = "4.47.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e5/cd/75d24afa673edf92fd04657fad7d3b5e20c4abc3cad5bc14e5e30051c1f0/fonttools-4.47.2.tar.gz", hash = "sha256:7df26dd3650e98ca45f1e29883c96a0b9f5bb6af8d632a6a108bc744fa0bd9b3", size = 3410067, upload-time = "2024-01-11T11:22:45.293Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/cd/75d24afa673edf92fd04657fad7d3b5e20c4abc3cad5bc14e5e30051c1f0/fonttools-4.47.2.tar.gz", hash = "sha256:7df26dd3650e98ca45f1e29883c96a0b9f5bb6af8d632a6a108bc744fa0bd9b3", size = 3410067 } wheels = [ - { url = "https://files.pythonhosted.org/packages/19/30/02de0b7f3d72f2c4fce3e512b166c1bdbe5a687408474b61eb0114be921c/fonttools-4.47.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3b629108351d25512d4ea1a8393a2dba325b7b7d7308116b605ea3f8e1be88df", size = 2779949, upload-time = "2024-01-11T11:19:56.276Z" }, - { url = "https://files.pythonhosted.org/packages/9a/52/1a5e1373afb78a040ea0c371ab8a79da121060a8e518968bb8f41457ca90/fonttools-4.47.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c19044256c44fe299d9a73456aabee4b4d06c6b930287be93b533b4737d70aa1", size = 2281336, upload-time = "2024-01-11T11:20:08.835Z" }, - { url = "https://files.pythonhosted.org/packages/c5/ce/9d3b5bf51aafee024566ebb374f5b040381d92660cb04647af3c5860c611/fonttools-4.47.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8be28c036b9f186e8c7eaf8a11b42373e7e4949f9e9f370202b9da4c4c3f56c", size = 4541692, upload-time = "2024-01-11T11:20:13.378Z" }, - { url = "https://files.pythonhosted.org/packages/e8/68/af41b7cfd35c7418e17b6a43bb106be4b0f0e5feb405a88dee29b186f2a7/fonttools-4.47.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f83a4daef6d2a202acb9bf572958f91cfde5b10c8ee7fb1d09a4c81e5d851fd8", size = 4600529, upload-time = "2024-01-11T11:20:17.27Z" }, - { url = "https://files.pythonhosted.org/packages/ab/7e/428dbb4cfc342b7a05cbc9d349e134e7fad6588f4ce2a7128e8e3e58ad3b/fonttools-4.47.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5a5318ba5365d992666ac4fe35365f93004109d18858a3e18ae46f67907670", size = 4524215, upload-time = "2024-01-11T11:20:21.061Z" }, - { url = "https://files.pythonhosted.org/packages/a6/61/762fad1cc1debc4626f2eb373fa999591c63c231fce53d5073574a639531/fonttools-4.47.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8f57ecd742545362a0f7186774b2d1c53423ed9ece67689c93a1055b236f638c", size = 4584778, upload-time = "2024-01-11T11:20:25.815Z" }, - { url = "https://files.pythonhosted.org/packages/04/30/170ca22284c1d825470e8b5871d6b25d3a70e2f5b185ffb1647d5e11ee4d/fonttools-4.47.2-cp310-cp310-win32.whl", hash = "sha256:a1c154bb85dc9a4cf145250c88d112d88eb414bad81d4cb524d06258dea1bdc0", size = 2131876, upload-time = "2024-01-11T11:20:30.261Z" }, - { url = "https://files.pythonhosted.org/packages/df/07/4a30437bed355b838b8ce31d14c5983334c31adc97e70c6ecff90c60d6d2/fonttools-4.47.2-cp310-cp310-win_amd64.whl", hash = "sha256:3e2b95dce2ead58fb12524d0ca7d63a63459dd489e7e5838c3cd53557f8933e1", size = 2177937, upload-time = "2024-01-11T11:20:33.814Z" }, - { url = "https://files.pythonhosted.org/packages/dd/1d/670372323642eada0f7743cfcdd156de6a28d37769c916421fec2f32c814/fonttools-4.47.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:29495d6d109cdbabe73cfb6f419ce67080c3ef9ea1e08d5750240fd4b0c4763b", size = 2782908, upload-time = "2024-01-11T11:20:37.495Z" }, - { url = "https://files.pythonhosted.org/packages/c1/36/5f0bb863a6575db4c4b67fa9be7f98e4c551dd87638ef327bc180b988998/fonttools-4.47.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0a1d313a415eaaba2b35d6cd33536560deeebd2ed758b9bfb89ab5d97dc5deac", size = 2283501, upload-time = "2024-01-11T11:20:42.027Z" }, - { url = "https://files.pythonhosted.org/packages/bd/1e/95de682a86567426bcc40a56c9b118ffa97de6cbfcc293addf20994e329d/fonttools-4.47.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90f898cdd67f52f18049250a6474185ef6544c91f27a7bee70d87d77a8daf89c", size = 4848039, upload-time = "2024-01-11T11:20:47.038Z" }, - { url = "https://files.pythonhosted.org/packages/ef/95/92a0b5fc844c1db734752f8a51431de519cd6b02e7e561efa9e9fd415544/fonttools-4.47.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3480eeb52770ff75140fe7d9a2ec33fb67b07efea0ab5129c7e0c6a639c40c70", size = 4893166, upload-time = "2024-01-11T11:20:50.855Z" }, - { url = "https://files.pythonhosted.org/packages/ff/e6/ed9dd7ee1afd6cd70eb7237688118fe489dbde962e3765c91c86c095f84b/fonttools-4.47.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0255dbc128fee75fb9be364806b940ed450dd6838672a150d501ee86523ac61e", size = 4815529, upload-time = "2024-01-11T11:20:54.696Z" }, - { url = "https://files.pythonhosted.org/packages/6b/67/cdffa0b3cd8f863b45125c335bbd3d9dc16ec42f5a8d5b64dd1244c5ce6b/fonttools-4.47.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f791446ff297fd5f1e2247c188de53c1bfb9dd7f0549eba55b73a3c2087a2703", size = 4875414, upload-time = "2024-01-11T11:20:58.435Z" }, - { url = "https://files.pythonhosted.org/packages/b8/fb/41638e748c8f20f5483987afcf9be746d3ccb9e9600ca62128a27c791a82/fonttools-4.47.2-cp311-cp311-win32.whl", hash = "sha256:740947906590a878a4bde7dd748e85fefa4d470a268b964748403b3ab2aeed6c", size = 2130073, upload-time = "2024-01-11T11:21:02.056Z" }, - { url = "https://files.pythonhosted.org/packages/a0/ef/93321cf55180a778b4d97919b28739874c0afab90e7b9f5b232db70f47c2/fonttools-4.47.2-cp311-cp311-win_amd64.whl", hash = "sha256:63fbed184979f09a65aa9c88b395ca539c94287ba3a364517698462e13e457c9", size = 2178744, upload-time = "2024-01-11T11:21:05.88Z" }, - { url = "https://files.pythonhosted.org/packages/c0/bd/4dd1e8a9e632f325d9203ce543402f912f26efd213c8d9efec0180fbac64/fonttools-4.47.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4ec558c543609e71b2275c4894e93493f65d2f41c15fe1d089080c1d0bb4d635", size = 2754076, upload-time = "2024-01-11T11:21:09.745Z" }, - { url = "https://files.pythonhosted.org/packages/e6/4d/c2ebaac81dadbc3fc3c3c2fa5fe7b16429dc713b1b8ace49e11e92904d78/fonttools-4.47.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e040f905d542362e07e72e03612a6270c33d38281fd573160e1003e43718d68d", size = 2263784, upload-time = "2024-01-11T11:21:13.367Z" }, - { url = "https://files.pythonhosted.org/packages/d3/f6/9d484cd275845c7e503a8669a5952a7fa089c7a881babb4dce5ebe6fc5d1/fonttools-4.47.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6dd58cc03016b281bd2c74c84cdaa6bd3ce54c5a7f47478b7657b930ac3ed8eb", size = 4769142, upload-time = "2024-01-11T11:21:17.615Z" }, - { url = "https://files.pythonhosted.org/packages/7a/bf/c6ae0768a531b38245aac0bb8d30bc05d53d499e09fccdc5d72e7c8d28b6/fonttools-4.47.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32ab2e9702dff0dd4510c7bb958f265a8d3dd5c0e2547e7b5f7a3df4979abb07", size = 4853241, upload-time = "2024-01-11T11:21:21.16Z" }, - { url = "https://files.pythonhosted.org/packages/2b/f0/c06709666cb7722447efb70ea456c302bd6eb3b997d30076401fb32bca4b/fonttools-4.47.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3a808f3c1d1df1f5bf39be869b6e0c263570cdafb5bdb2df66087733f566ea71", size = 4730447, upload-time = "2024-01-11T11:21:24.755Z" }, - { url = "https://files.pythonhosted.org/packages/3e/71/4c758ae5f4f8047904fc1c6bbbb828248c94cc7aa6406af3a62ede766f25/fonttools-4.47.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ac71e2e201df041a2891067dc36256755b1229ae167edbdc419b16da78732c2f", size = 4809265, upload-time = "2024-01-11T11:21:28.586Z" }, - { url = "https://files.pythonhosted.org/packages/81/f6/a6912c11280607d48947341e2167502605a3917925c835afcd7dfcabc289/fonttools-4.47.2-cp312-cp312-win32.whl", hash = "sha256:69731e8bea0578b3c28fdb43dbf95b9386e2d49a399e9a4ad736b8e479b08085", size = 2118363, upload-time = "2024-01-11T11:21:33.245Z" }, - { url = "https://files.pythonhosted.org/packages/81/4b/42d0488765ea5aa308b4e8197cb75366b2124240a73e86f98b6107ccf282/fonttools-4.47.2-cp312-cp312-win_amd64.whl", hash = "sha256:b3e1304e5f19ca861d86a72218ecce68f391646d85c851742d265787f55457a4", size = 2165866, upload-time = "2024-01-11T11:21:37.23Z" }, - { url = "https://files.pythonhosted.org/packages/af/2f/c34b0f99d46766cf49566d1ee2ee3606e4c9880b5a7d734257dc61c804e9/fonttools-4.47.2-py3-none-any.whl", hash = "sha256:7eb7ad665258fba68fd22228a09f347469d95a97fb88198e133595947a20a184", size = 1063011, upload-time = "2024-01-11T11:22:41.676Z" }, + { url = "https://files.pythonhosted.org/packages/19/30/02de0b7f3d72f2c4fce3e512b166c1bdbe5a687408474b61eb0114be921c/fonttools-4.47.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3b629108351d25512d4ea1a8393a2dba325b7b7d7308116b605ea3f8e1be88df", size = 2779949 }, + { url = "https://files.pythonhosted.org/packages/9a/52/1a5e1373afb78a040ea0c371ab8a79da121060a8e518968bb8f41457ca90/fonttools-4.47.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c19044256c44fe299d9a73456aabee4b4d06c6b930287be93b533b4737d70aa1", size = 2281336 }, + { url = "https://files.pythonhosted.org/packages/c5/ce/9d3b5bf51aafee024566ebb374f5b040381d92660cb04647af3c5860c611/fonttools-4.47.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8be28c036b9f186e8c7eaf8a11b42373e7e4949f9e9f370202b9da4c4c3f56c", size = 4541692 }, + { url = "https://files.pythonhosted.org/packages/e8/68/af41b7cfd35c7418e17b6a43bb106be4b0f0e5feb405a88dee29b186f2a7/fonttools-4.47.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f83a4daef6d2a202acb9bf572958f91cfde5b10c8ee7fb1d09a4c81e5d851fd8", size = 4600529 }, + { url = "https://files.pythonhosted.org/packages/ab/7e/428dbb4cfc342b7a05cbc9d349e134e7fad6588f4ce2a7128e8e3e58ad3b/fonttools-4.47.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5a5318ba5365d992666ac4fe35365f93004109d18858a3e18ae46f67907670", size = 4524215 }, + { url = "https://files.pythonhosted.org/packages/a6/61/762fad1cc1debc4626f2eb373fa999591c63c231fce53d5073574a639531/fonttools-4.47.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8f57ecd742545362a0f7186774b2d1c53423ed9ece67689c93a1055b236f638c", size = 4584778 }, + { url = "https://files.pythonhosted.org/packages/04/30/170ca22284c1d825470e8b5871d6b25d3a70e2f5b185ffb1647d5e11ee4d/fonttools-4.47.2-cp310-cp310-win32.whl", hash = "sha256:a1c154bb85dc9a4cf145250c88d112d88eb414bad81d4cb524d06258dea1bdc0", size = 2131876 }, + { url = "https://files.pythonhosted.org/packages/df/07/4a30437bed355b838b8ce31d14c5983334c31adc97e70c6ecff90c60d6d2/fonttools-4.47.2-cp310-cp310-win_amd64.whl", hash = "sha256:3e2b95dce2ead58fb12524d0ca7d63a63459dd489e7e5838c3cd53557f8933e1", size = 2177937 }, + { url = "https://files.pythonhosted.org/packages/dd/1d/670372323642eada0f7743cfcdd156de6a28d37769c916421fec2f32c814/fonttools-4.47.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:29495d6d109cdbabe73cfb6f419ce67080c3ef9ea1e08d5750240fd4b0c4763b", size = 2782908 }, + { url = "https://files.pythonhosted.org/packages/c1/36/5f0bb863a6575db4c4b67fa9be7f98e4c551dd87638ef327bc180b988998/fonttools-4.47.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0a1d313a415eaaba2b35d6cd33536560deeebd2ed758b9bfb89ab5d97dc5deac", size = 2283501 }, + { url = "https://files.pythonhosted.org/packages/bd/1e/95de682a86567426bcc40a56c9b118ffa97de6cbfcc293addf20994e329d/fonttools-4.47.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90f898cdd67f52f18049250a6474185ef6544c91f27a7bee70d87d77a8daf89c", size = 4848039 }, + { url = "https://files.pythonhosted.org/packages/ef/95/92a0b5fc844c1db734752f8a51431de519cd6b02e7e561efa9e9fd415544/fonttools-4.47.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3480eeb52770ff75140fe7d9a2ec33fb67b07efea0ab5129c7e0c6a639c40c70", size = 4893166 }, + { url = "https://files.pythonhosted.org/packages/ff/e6/ed9dd7ee1afd6cd70eb7237688118fe489dbde962e3765c91c86c095f84b/fonttools-4.47.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0255dbc128fee75fb9be364806b940ed450dd6838672a150d501ee86523ac61e", size = 4815529 }, + { url = "https://files.pythonhosted.org/packages/6b/67/cdffa0b3cd8f863b45125c335bbd3d9dc16ec42f5a8d5b64dd1244c5ce6b/fonttools-4.47.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f791446ff297fd5f1e2247c188de53c1bfb9dd7f0549eba55b73a3c2087a2703", size = 4875414 }, + { url = "https://files.pythonhosted.org/packages/b8/fb/41638e748c8f20f5483987afcf9be746d3ccb9e9600ca62128a27c791a82/fonttools-4.47.2-cp311-cp311-win32.whl", hash = "sha256:740947906590a878a4bde7dd748e85fefa4d470a268b964748403b3ab2aeed6c", size = 2130073 }, + { url = "https://files.pythonhosted.org/packages/a0/ef/93321cf55180a778b4d97919b28739874c0afab90e7b9f5b232db70f47c2/fonttools-4.47.2-cp311-cp311-win_amd64.whl", hash = "sha256:63fbed184979f09a65aa9c88b395ca539c94287ba3a364517698462e13e457c9", size = 2178744 }, + { url = "https://files.pythonhosted.org/packages/c0/bd/4dd1e8a9e632f325d9203ce543402f912f26efd213c8d9efec0180fbac64/fonttools-4.47.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4ec558c543609e71b2275c4894e93493f65d2f41c15fe1d089080c1d0bb4d635", size = 2754076 }, + { url = "https://files.pythonhosted.org/packages/e6/4d/c2ebaac81dadbc3fc3c3c2fa5fe7b16429dc713b1b8ace49e11e92904d78/fonttools-4.47.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e040f905d542362e07e72e03612a6270c33d38281fd573160e1003e43718d68d", size = 2263784 }, + { url = "https://files.pythonhosted.org/packages/d3/f6/9d484cd275845c7e503a8669a5952a7fa089c7a881babb4dce5ebe6fc5d1/fonttools-4.47.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6dd58cc03016b281bd2c74c84cdaa6bd3ce54c5a7f47478b7657b930ac3ed8eb", size = 4769142 }, + { url = "https://files.pythonhosted.org/packages/7a/bf/c6ae0768a531b38245aac0bb8d30bc05d53d499e09fccdc5d72e7c8d28b6/fonttools-4.47.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32ab2e9702dff0dd4510c7bb958f265a8d3dd5c0e2547e7b5f7a3df4979abb07", size = 4853241 }, + { url = "https://files.pythonhosted.org/packages/2b/f0/c06709666cb7722447efb70ea456c302bd6eb3b997d30076401fb32bca4b/fonttools-4.47.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3a808f3c1d1df1f5bf39be869b6e0c263570cdafb5bdb2df66087733f566ea71", size = 4730447 }, + { url = "https://files.pythonhosted.org/packages/3e/71/4c758ae5f4f8047904fc1c6bbbb828248c94cc7aa6406af3a62ede766f25/fonttools-4.47.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ac71e2e201df041a2891067dc36256755b1229ae167edbdc419b16da78732c2f", size = 4809265 }, + { url = "https://files.pythonhosted.org/packages/81/f6/a6912c11280607d48947341e2167502605a3917925c835afcd7dfcabc289/fonttools-4.47.2-cp312-cp312-win32.whl", hash = "sha256:69731e8bea0578b3c28fdb43dbf95b9386e2d49a399e9a4ad736b8e479b08085", size = 2118363 }, + { url = "https://files.pythonhosted.org/packages/81/4b/42d0488765ea5aa308b4e8197cb75366b2124240a73e86f98b6107ccf282/fonttools-4.47.2-cp312-cp312-win_amd64.whl", hash = "sha256:b3e1304e5f19ca861d86a72218ecce68f391646d85c851742d265787f55457a4", size = 2165866 }, + { url = "https://files.pythonhosted.org/packages/af/2f/c34b0f99d46766cf49566d1ee2ee3606e4c9880b5a7d734257dc61c804e9/fonttools-4.47.2-py3-none-any.whl", hash = "sha256:7eb7ad665258fba68fd22228a09f347469d95a97fb88198e133595947a20a184", size = 1063011 }, ] [[package]] name = "fsspec" version = "2023.12.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/08/cac914ff6ff46c4500fc4323a939dbe7a0f528cca04e7fd3e859611dea41/fsspec-2023.12.2.tar.gz", hash = "sha256:8548d39e8810b59c38014934f6b31e57f40c1b20f911f4cc2b85389c7e9bf0cb", size = 167507, upload-time = "2023-12-11T21:19:54.832Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/08/cac914ff6ff46c4500fc4323a939dbe7a0f528cca04e7fd3e859611dea41/fsspec-2023.12.2.tar.gz", hash = "sha256:8548d39e8810b59c38014934f6b31e57f40c1b20f911f4cc2b85389c7e9bf0cb", size = 167507 } wheels = [ - { url = "https://files.pythonhosted.org/packages/70/25/fab23259a52ece5670dcb8452e1af34b89e6135ecc17cd4b54b4b479eac6/fsspec-2023.12.2-py3-none-any.whl", hash = "sha256:d800d87f72189a745fa3d6b033b9dc4a34ad069f60ca60b943a63599f5501960", size = 168979, upload-time = "2023-12-11T21:19:52.446Z" }, + { url = "https://files.pythonhosted.org/packages/70/25/fab23259a52ece5670dcb8452e1af34b89e6135ecc17cd4b54b4b479eac6/fsspec-2023.12.2-py3-none-any.whl", hash = "sha256:d800d87f72189a745fa3d6b033b9dc4a34ad069f60ca60b943a63599f5501960", size = 168979 }, ] [[package]] @@ -637,9 +748,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wcwidth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a5/d3/8650919bc3c7c6e90ee3fa7fd618bf373cbbe55dff043bd67353dbb20cd8/ftfy-6.3.1.tar.gz", hash = "sha256:9b3c3d90f84fb267fe64d375a07b7f8912d817cf86009ae134aa03e1819506ec", size = 308927, upload-time = "2024-10-26T00:50:35.149Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/d3/8650919bc3c7c6e90ee3fa7fd618bf373cbbe55dff043bd67353dbb20cd8/ftfy-6.3.1.tar.gz", hash = "sha256:9b3c3d90f84fb267fe64d375a07b7f8912d817cf86009ae134aa03e1819506ec", size = 308927 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/6e/81d47999aebc1b155f81eca4477a616a70f238a2549848c38983f3c22a82/ftfy-6.3.1-py3-none-any.whl", hash = "sha256:7c70eb532015cd2f9adb53f101fb6c7945988d023a085d127d1573dc49dd0083", size = 44821, upload-time = "2024-10-26T00:50:33.425Z" }, + { url = "https://files.pythonhosted.org/packages/ab/6e/81d47999aebc1b155f81eca4477a616a70f238a2549848c38983f3c22a82/ftfy-6.3.1-py3-none-any.whl", hash = "sha256:7c70eb532015cd2f9adb53f101fb6c7945988d023a085d127d1573dc49dd0083", size = 44821 }, ] [[package]] @@ -652,41 +763,41 @@ dependencies = [ { name = "zope-event" }, { name = "zope-interface" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/70/f0/be10ed5d7721ed2317d7feb59e167603217156c2a6d57f128523e24e673d/gevent-24.10.3.tar.gz", hash = "sha256:aa7ee1bd5cabb2b7ef35105f863b386c8d5e332f754b60cfc354148bd70d35d1", size = 6108837, upload-time = "2024-10-18T16:06:25.867Z" } +sdist = { url = "https://files.pythonhosted.org/packages/70/f0/be10ed5d7721ed2317d7feb59e167603217156c2a6d57f128523e24e673d/gevent-24.10.3.tar.gz", hash = "sha256:aa7ee1bd5cabb2b7ef35105f863b386c8d5e332f754b60cfc354148bd70d35d1", size = 6108837 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/6f/a2100e7883c7bdfc2b45cb60b310ca748762a21596258b9dd01c5c093dbc/gevent-24.10.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:d7a1ad0f2da582f5bd238bca067e1c6c482c30c15a6e4d14aaa3215cbb2232f3", size = 3014382, upload-time = "2024-10-18T15:37:34.041Z" }, - { url = "https://files.pythonhosted.org/packages/7a/b1/460e4884ed6185d9eb9c4c2e9639d2b254197e46513301c0f63dec22dc90/gevent-24.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4e526fdc279c655c1e809b0c34b45844182c2a6b219802da5e411bd2cf5a8ad", size = 4853460, upload-time = "2024-10-18T16:19:39.515Z" }, - { url = "https://files.pythonhosted.org/packages/ca/f6/7ded98760d381229183ecce8db2edcce96f13e23807d31a90c66dae85304/gevent-24.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57a5c4e0bdac482c5f02f240d0354e61362df73501ef6ebafce8ef635cad7527", size = 4977636, upload-time = "2024-10-18T16:18:45.464Z" }, - { url = "https://files.pythonhosted.org/packages/7d/21/7b928e6029eedb93ef94fc0aee701f497af2e601f0ec00aac0e72e3f450e/gevent-24.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d67daed8383326dc8b5e58d88e148d29b6b52274a489e383530b0969ae7b9cb9", size = 5058031, upload-time = "2024-10-18T16:23:10.719Z" }, - { url = "https://files.pythonhosted.org/packages/00/98/12c03fd004fbeeca01276ffc589f5a368fd741d02582ab7006d1bdef57e7/gevent-24.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e24ffea72e27987979c009536fd0868e52239b44afe6cf7135ce8aafd0f108e", size = 6683694, upload-time = "2024-10-18T15:59:35.475Z" }, - { url = "https://files.pythonhosted.org/packages/64/4c/ea14d971452d3da09e49267e052d8312f112c7835120aed78d22ef14efee/gevent-24.10.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c1d80090485da1ea3d99205fe97908b31188c1f4857f08b333ffaf2de2e89d18", size = 5286063, upload-time = "2024-10-18T16:38:24.113Z" }, - { url = "https://files.pythonhosted.org/packages/39/3f/397efff27e637d7306caa00d1560512c44028c25c70be1e72c46b79b1b66/gevent-24.10.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f0c129f81d60cda614acb4b0c5731997ca05b031fb406fcb58ad53a7ade53b13", size = 6817462, upload-time = "2024-10-18T16:02:48.427Z" }, - { url = "https://files.pythonhosted.org/packages/aa/5d/19939eaa7c5b7c0f37e0a0665a911ddfe1e35c25c512446fc356a065c16e/gevent-24.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:26ca7a6b42d35129617025ac801135118333cad75856ffc3217b38e707383eba", size = 1566631, upload-time = "2024-10-18T16:08:38.489Z" }, - { url = "https://files.pythonhosted.org/packages/6e/01/1be5cf013826d8baae235976d6a94f3628014fd2db7c071aeec13f82b4d1/gevent-24.10.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:68c3a0d8402755eba7f69022e42e8021192a721ca8341908acc222ea597029b6", size = 2966909, upload-time = "2024-10-18T15:37:31.43Z" }, - { url = "https://files.pythonhosted.org/packages/fe/3e/7fa9ab023f24d8689e2c77951981f8ea1f25089e0349a0bf8b35ee9b9277/gevent-24.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d850a453d66336272be4f1d3a8126777f3efdaea62d053b4829857f91e09755", size = 4913247, upload-time = "2024-10-18T16:19:41.792Z" }, - { url = "https://files.pythonhosted.org/packages/db/63/6e40eaaa3c2abd1561faff11dc3e6781f8c25e975354b8835762834415af/gevent-24.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8e58ee3723f1fbe07d66892f1caa7481c306f653a6829b6fd16cb23d618a5915", size = 5049036, upload-time = "2024-10-18T16:18:47.419Z" }, - { url = "https://files.pythonhosted.org/packages/94/89/158bc32cdc898dda0481040ac18650022e73133d93460c5af56ca622fe9a/gevent-24.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b52382124eca13135a3abe4f65c6bd428656975980a48e51b17aeab68bdb14db", size = 5107299, upload-time = "2024-10-18T16:23:12.296Z" }, - { url = "https://files.pythonhosted.org/packages/64/91/1abe62ee350fdfac186d33f615d0d3a0b3b140e7ccf23c73547aa0deec44/gevent-24.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ca2266e08f43c0e22c028801dff7d92a0b102ef20e4caeb6a46abfb95f6a328", size = 6819625, upload-time = "2024-10-18T15:59:38.226Z" }, - { url = "https://files.pythonhosted.org/packages/92/8b/0b2fe0d36b7c4d463e46cc68eaf6c14488bd7d86cc37e995c64a0ff7d02f/gevent-24.10.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d758f0d4dbf32502ec87bb9b536ca8055090a16f8305f0ada3ce6f34e70f2fd7", size = 5474079, upload-time = "2024-10-18T16:38:26.866Z" }, - { url = "https://files.pythonhosted.org/packages/12/7b/9f5abbf0021a50321314f850697e0f46d2e5081168223af2d8544af9d19f/gevent-24.10.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0de6eb3d55c03138fda567d9bfed28487ce5d0928c5107549767a93efdf2be26", size = 6901323, upload-time = "2024-10-18T16:02:50.066Z" }, - { url = "https://files.pythonhosted.org/packages/8a/63/607715c621ae78ed581b7ba36d076df63feeb352993d521327f865056771/gevent-24.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:385710355eadecdb70428a5ae3e7e5a45dcf888baa1426884588be9d25ac4290", size = 1549468, upload-time = "2024-10-18T16:01:30.331Z" }, - { url = "https://files.pythonhosted.org/packages/d9/e4/4edbe17001bb3e6fade4ad2d85ca8f9e4eabcbde4aa29aa6889281616e3e/gevent-24.10.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3ad8fb70aa0ebc935729c9699ac31b210a49b689a7b27b7ac9f91676475f3f53", size = 2970952, upload-time = "2024-10-18T15:37:31.389Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a6/ce0824fe9398ba6b00028a74840f12be1165d5feaacdc028ea953db3d6c3/gevent-24.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f18689f7a70d2ed0e75bad5036ec3c89690a493d4cfac8d7cdb258ac04b132bd", size = 5172230, upload-time = "2024-10-18T16:19:43.661Z" }, - { url = "https://files.pythonhosted.org/packages/25/d4/9002cfb585bfa52c860ed4b1349d1a6400bdf2df9f1bd21df5ff33eea33c/gevent-24.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f4f171d4d2018170454d84c934842e1b5f6ce7468ba298f6e7f7cff15000a3", size = 5338394, upload-time = "2024-10-18T16:18:49.371Z" }, - { url = "https://files.pythonhosted.org/packages/0c/98/222f1a14f22ad2d1cbcc37edb74095264c1f9c7ab49e6423693383462b8a/gevent-24.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7021e26d70189b33c27173d4173f27bf4685d6b6f1c0ea50e5335f8491cb110c", size = 5437989, upload-time = "2024-10-18T16:23:13.851Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e8/cbb46afea3c7ecdc7289e15cb4a6f89903f4f9754a27ca320d3e465abc78/gevent-24.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34aea15f9c79f27a8faeaa361bc1e72c773a9b54a1996a2ec4eefc8bcd59a824", size = 6838539, upload-time = "2024-10-18T15:59:40.489Z" }, - { url = "https://files.pythonhosted.org/packages/69/c3/e43e348f23da404a6d4368a14453ed097cdfca97d5212eaceb987d04a0e1/gevent-24.10.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8af65a4d4feaec6042c666d22c322a310fba3b47e841ad52f724b9c3ce5da48e", size = 5513842, upload-time = "2024-10-18T16:38:29.538Z" }, - { url = "https://files.pythonhosted.org/packages/c2/76/84b7c19c072a80900118717a85236859127d630cdf8b079fe42f19649f12/gevent-24.10.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:89c4115e3f5ada55f92b61701a46043fe42f702b5af863b029e4c1a76f6cc2d4", size = 6927374, upload-time = "2024-10-18T16:02:51.669Z" }, - { url = "https://files.pythonhosted.org/packages/5e/69/0ab1b04c363547058fb5035275c144957b80b36cb6aee715fe6181b0cee9/gevent-24.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:1ce6dab94c0b0d24425ba55712de2f8c9cb21267150ca63f5bb3a0e1f165da99", size = 1546701, upload-time = "2024-10-18T15:54:53.562Z" }, - { url = "https://files.pythonhosted.org/packages/f7/2d/c783583d7999cd2f2e7aa2d6a1c333d663003ca61255a89ff6a891be95f4/gevent-24.10.3-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:f147e38423fbe96e8731f60a63475b3d2cab2f3d10578d8ee9d10c507c58a2ff", size = 2962857, upload-time = "2024-10-18T15:37:33.098Z" }, - { url = "https://files.pythonhosted.org/packages/f3/77/d3ce96fd49406f61976e9a3b6c742b97bb274d3b30c68ff190c5b5f81afd/gevent-24.10.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18e6984ec96fc95fd67488555c38ece3015be1f38b1bcceb27b7d6c36b343008", size = 5141676, upload-time = "2024-10-18T16:19:45.484Z" }, - { url = "https://files.pythonhosted.org/packages/49/f4/f99f893770c316b9d2f03bd684947126cbed0321b89fe5423838974c2025/gevent-24.10.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:051b22e2758accfddb0457728bfc9abf8c3f2ce6bca43f1ff6e07b5ed9e49bf4", size = 5310248, upload-time = "2024-10-18T16:18:51.175Z" }, - { url = "https://files.pythonhosted.org/packages/e3/0c/67257ba906f76ed82e8f0bd8c00c2a0687b360a1050b70db7e58dff749ab/gevent-24.10.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb5edb6433764119a664bbb148d2aea9990950aa89cc3498f475c2408d523ea3", size = 5407304, upload-time = "2024-10-18T16:23:15.348Z" }, - { url = "https://files.pythonhosted.org/packages/35/6c/3a72da7c224b0111728130c0f1abc3ee07feff91b37e0ea83db98f4a3eaf/gevent-24.10.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce417bcaaab496bc9c77f75566531e9d93816262037b8b2dbb88b0fdcd66587c", size = 6818624, upload-time = "2024-10-18T15:59:42.068Z" }, - { url = "https://files.pythonhosted.org/packages/a3/96/cc5f6ecba032a45fc312fe0db2908a893057fd81361eea93845d6c325556/gevent-24.10.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:1c3a828b033fb02b7c31da4d75014a1f82e6c072fc0523456569a57f8b025861", size = 5484356, upload-time = "2024-10-18T16:38:31.709Z" }, - { url = "https://files.pythonhosted.org/packages/7c/97/e680b2b2f0c291ae4db9813ffbf02c22c2a0f14c8f1a613971385e29ef67/gevent-24.10.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f2ae3efbbd120cdf4a68b7abc27a37e61e6f443c5a06ec2c6ad94c37cd8471ec", size = 6903191, upload-time = "2024-10-18T16:02:53.888Z" }, - { url = "https://files.pythonhosted.org/packages/1b/1c/b4181957da062d1c060974ec6cb798cc24aeeb28e8cd2ece84eb4b4991f7/gevent-24.10.3-cp313-cp313-win_amd64.whl", hash = "sha256:9e1210334a9bc9f76c3d008e0785ca62214f8a54e1325f6c2ecab3b6a572a015", size = 1545117, upload-time = "2024-10-18T15:45:47.375Z" }, - { url = "https://files.pythonhosted.org/packages/89/2b/bf4af9950b8f9abd5b4025858f6311930de550e3498bbfeb47c914701a1d/gevent-24.10.3-pp310-pypy310_pp73-macosx_11_0_universal2.whl", hash = "sha256:e534e6a968d74463b11de6c9c67f4b4bf61775fb00f2e6e0f7fcdd412ceade18", size = 1271541, upload-time = "2024-10-18T15:37:53.146Z" }, + { url = "https://files.pythonhosted.org/packages/6b/6f/a2100e7883c7bdfc2b45cb60b310ca748762a21596258b9dd01c5c093dbc/gevent-24.10.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:d7a1ad0f2da582f5bd238bca067e1c6c482c30c15a6e4d14aaa3215cbb2232f3", size = 3014382 }, + { url = "https://files.pythonhosted.org/packages/7a/b1/460e4884ed6185d9eb9c4c2e9639d2b254197e46513301c0f63dec22dc90/gevent-24.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4e526fdc279c655c1e809b0c34b45844182c2a6b219802da5e411bd2cf5a8ad", size = 4853460 }, + { url = "https://files.pythonhosted.org/packages/ca/f6/7ded98760d381229183ecce8db2edcce96f13e23807d31a90c66dae85304/gevent-24.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57a5c4e0bdac482c5f02f240d0354e61362df73501ef6ebafce8ef635cad7527", size = 4977636 }, + { url = "https://files.pythonhosted.org/packages/7d/21/7b928e6029eedb93ef94fc0aee701f497af2e601f0ec00aac0e72e3f450e/gevent-24.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d67daed8383326dc8b5e58d88e148d29b6b52274a489e383530b0969ae7b9cb9", size = 5058031 }, + { url = "https://files.pythonhosted.org/packages/00/98/12c03fd004fbeeca01276ffc589f5a368fd741d02582ab7006d1bdef57e7/gevent-24.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e24ffea72e27987979c009536fd0868e52239b44afe6cf7135ce8aafd0f108e", size = 6683694 }, + { url = "https://files.pythonhosted.org/packages/64/4c/ea14d971452d3da09e49267e052d8312f112c7835120aed78d22ef14efee/gevent-24.10.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c1d80090485da1ea3d99205fe97908b31188c1f4857f08b333ffaf2de2e89d18", size = 5286063 }, + { url = "https://files.pythonhosted.org/packages/39/3f/397efff27e637d7306caa00d1560512c44028c25c70be1e72c46b79b1b66/gevent-24.10.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f0c129f81d60cda614acb4b0c5731997ca05b031fb406fcb58ad53a7ade53b13", size = 6817462 }, + { url = "https://files.pythonhosted.org/packages/aa/5d/19939eaa7c5b7c0f37e0a0665a911ddfe1e35c25c512446fc356a065c16e/gevent-24.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:26ca7a6b42d35129617025ac801135118333cad75856ffc3217b38e707383eba", size = 1566631 }, + { url = "https://files.pythonhosted.org/packages/6e/01/1be5cf013826d8baae235976d6a94f3628014fd2db7c071aeec13f82b4d1/gevent-24.10.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:68c3a0d8402755eba7f69022e42e8021192a721ca8341908acc222ea597029b6", size = 2966909 }, + { url = "https://files.pythonhosted.org/packages/fe/3e/7fa9ab023f24d8689e2c77951981f8ea1f25089e0349a0bf8b35ee9b9277/gevent-24.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d850a453d66336272be4f1d3a8126777f3efdaea62d053b4829857f91e09755", size = 4913247 }, + { url = "https://files.pythonhosted.org/packages/db/63/6e40eaaa3c2abd1561faff11dc3e6781f8c25e975354b8835762834415af/gevent-24.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8e58ee3723f1fbe07d66892f1caa7481c306f653a6829b6fd16cb23d618a5915", size = 5049036 }, + { url = "https://files.pythonhosted.org/packages/94/89/158bc32cdc898dda0481040ac18650022e73133d93460c5af56ca622fe9a/gevent-24.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b52382124eca13135a3abe4f65c6bd428656975980a48e51b17aeab68bdb14db", size = 5107299 }, + { url = "https://files.pythonhosted.org/packages/64/91/1abe62ee350fdfac186d33f615d0d3a0b3b140e7ccf23c73547aa0deec44/gevent-24.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ca2266e08f43c0e22c028801dff7d92a0b102ef20e4caeb6a46abfb95f6a328", size = 6819625 }, + { url = "https://files.pythonhosted.org/packages/92/8b/0b2fe0d36b7c4d463e46cc68eaf6c14488bd7d86cc37e995c64a0ff7d02f/gevent-24.10.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d758f0d4dbf32502ec87bb9b536ca8055090a16f8305f0ada3ce6f34e70f2fd7", size = 5474079 }, + { url = "https://files.pythonhosted.org/packages/12/7b/9f5abbf0021a50321314f850697e0f46d2e5081168223af2d8544af9d19f/gevent-24.10.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0de6eb3d55c03138fda567d9bfed28487ce5d0928c5107549767a93efdf2be26", size = 6901323 }, + { url = "https://files.pythonhosted.org/packages/8a/63/607715c621ae78ed581b7ba36d076df63feeb352993d521327f865056771/gevent-24.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:385710355eadecdb70428a5ae3e7e5a45dcf888baa1426884588be9d25ac4290", size = 1549468 }, + { url = "https://files.pythonhosted.org/packages/d9/e4/4edbe17001bb3e6fade4ad2d85ca8f9e4eabcbde4aa29aa6889281616e3e/gevent-24.10.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3ad8fb70aa0ebc935729c9699ac31b210a49b689a7b27b7ac9f91676475f3f53", size = 2970952 }, + { url = "https://files.pythonhosted.org/packages/3c/a6/ce0824fe9398ba6b00028a74840f12be1165d5feaacdc028ea953db3d6c3/gevent-24.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f18689f7a70d2ed0e75bad5036ec3c89690a493d4cfac8d7cdb258ac04b132bd", size = 5172230 }, + { url = "https://files.pythonhosted.org/packages/25/d4/9002cfb585bfa52c860ed4b1349d1a6400bdf2df9f1bd21df5ff33eea33c/gevent-24.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f4f171d4d2018170454d84c934842e1b5f6ce7468ba298f6e7f7cff15000a3", size = 5338394 }, + { url = "https://files.pythonhosted.org/packages/0c/98/222f1a14f22ad2d1cbcc37edb74095264c1f9c7ab49e6423693383462b8a/gevent-24.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7021e26d70189b33c27173d4173f27bf4685d6b6f1c0ea50e5335f8491cb110c", size = 5437989 }, + { url = "https://files.pythonhosted.org/packages/bf/e8/cbb46afea3c7ecdc7289e15cb4a6f89903f4f9754a27ca320d3e465abc78/gevent-24.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34aea15f9c79f27a8faeaa361bc1e72c773a9b54a1996a2ec4eefc8bcd59a824", size = 6838539 }, + { url = "https://files.pythonhosted.org/packages/69/c3/e43e348f23da404a6d4368a14453ed097cdfca97d5212eaceb987d04a0e1/gevent-24.10.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8af65a4d4feaec6042c666d22c322a310fba3b47e841ad52f724b9c3ce5da48e", size = 5513842 }, + { url = "https://files.pythonhosted.org/packages/c2/76/84b7c19c072a80900118717a85236859127d630cdf8b079fe42f19649f12/gevent-24.10.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:89c4115e3f5ada55f92b61701a46043fe42f702b5af863b029e4c1a76f6cc2d4", size = 6927374 }, + { url = "https://files.pythonhosted.org/packages/5e/69/0ab1b04c363547058fb5035275c144957b80b36cb6aee715fe6181b0cee9/gevent-24.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:1ce6dab94c0b0d24425ba55712de2f8c9cb21267150ca63f5bb3a0e1f165da99", size = 1546701 }, + { url = "https://files.pythonhosted.org/packages/f7/2d/c783583d7999cd2f2e7aa2d6a1c333d663003ca61255a89ff6a891be95f4/gevent-24.10.3-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:f147e38423fbe96e8731f60a63475b3d2cab2f3d10578d8ee9d10c507c58a2ff", size = 2962857 }, + { url = "https://files.pythonhosted.org/packages/f3/77/d3ce96fd49406f61976e9a3b6c742b97bb274d3b30c68ff190c5b5f81afd/gevent-24.10.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18e6984ec96fc95fd67488555c38ece3015be1f38b1bcceb27b7d6c36b343008", size = 5141676 }, + { url = "https://files.pythonhosted.org/packages/49/f4/f99f893770c316b9d2f03bd684947126cbed0321b89fe5423838974c2025/gevent-24.10.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:051b22e2758accfddb0457728bfc9abf8c3f2ce6bca43f1ff6e07b5ed9e49bf4", size = 5310248 }, + { url = "https://files.pythonhosted.org/packages/e3/0c/67257ba906f76ed82e8f0bd8c00c2a0687b360a1050b70db7e58dff749ab/gevent-24.10.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb5edb6433764119a664bbb148d2aea9990950aa89cc3498f475c2408d523ea3", size = 5407304 }, + { url = "https://files.pythonhosted.org/packages/35/6c/3a72da7c224b0111728130c0f1abc3ee07feff91b37e0ea83db98f4a3eaf/gevent-24.10.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce417bcaaab496bc9c77f75566531e9d93816262037b8b2dbb88b0fdcd66587c", size = 6818624 }, + { url = "https://files.pythonhosted.org/packages/a3/96/cc5f6ecba032a45fc312fe0db2908a893057fd81361eea93845d6c325556/gevent-24.10.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:1c3a828b033fb02b7c31da4d75014a1f82e6c072fc0523456569a57f8b025861", size = 5484356 }, + { url = "https://files.pythonhosted.org/packages/7c/97/e680b2b2f0c291ae4db9813ffbf02c22c2a0f14c8f1a613971385e29ef67/gevent-24.10.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f2ae3efbbd120cdf4a68b7abc27a37e61e6f443c5a06ec2c6ad94c37cd8471ec", size = 6903191 }, + { url = "https://files.pythonhosted.org/packages/1b/1c/b4181957da062d1c060974ec6cb798cc24aeeb28e8cd2ece84eb4b4991f7/gevent-24.10.3-cp313-cp313-win_amd64.whl", hash = "sha256:9e1210334a9bc9f76c3d008e0785ca62214f8a54e1325f6c2ecab3b6a572a015", size = 1545117 }, + { url = "https://files.pythonhosted.org/packages/89/2b/bf4af9950b8f9abd5b4025858f6311930de550e3498bbfeb47c914701a1d/gevent-24.10.3-pp310-pypy310_pp73-macosx_11_0_universal2.whl", hash = "sha256:e534e6a968d74463b11de6c9c67f4b4bf61775fb00f2e6e0f7fcdd412ceade18", size = 1271541 }, ] [[package]] @@ -699,103 +810,103 @@ dependencies = [ { name = "gevent" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8c/14/d4eddae757de44985718a9e38d9e6f2a923d764ed97d0f1cbc1a8aa2b0ef/geventhttpclient-2.3.1.tar.gz", hash = "sha256:b40ddac8517c456818942c7812f555f84702105c82783238c9fcb8dc12675185", size = 69345, upload-time = "2024-04-18T21:39:50.83Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/14/d4eddae757de44985718a9e38d9e6f2a923d764ed97d0f1cbc1a8aa2b0ef/geventhttpclient-2.3.1.tar.gz", hash = "sha256:b40ddac8517c456818942c7812f555f84702105c82783238c9fcb8dc12675185", size = 69345 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/a5/5e49d6a581b3f1399425e22961c6e341e90c12fa2193ed0adee9afbd864c/geventhttpclient-2.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:da22ab7bf5af4ba3d07cffee6de448b42696e53e7ac1fe97ed289037733bf1c2", size = 71729, upload-time = "2024-04-18T21:38:06.866Z" }, - { url = "https://files.pythonhosted.org/packages/eb/23/4ff584e5f344dae64b5bc588b65c4ea81083f9d662b9f64cf5f28e5ae9cc/geventhttpclient-2.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2399e3d4e2fae8bbd91756189da6e9d84adf8f3eaace5eef0667874a705a29f8", size = 52062, upload-time = "2024-04-18T21:38:08.433Z" }, - { url = "https://files.pythonhosted.org/packages/bb/60/6bd8badb97b31a49f4c2b79466abce208a97dad95d447893c7546063fc8a/geventhttpclient-2.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3e33e87d0d5b9f5782c4e6d3cb7e3592fea41af52713137d04776df7646d71b", size = 51645, upload-time = "2024-04-18T21:38:10.139Z" }, - { url = "https://files.pythonhosted.org/packages/e1/62/47d431bf05f74aa683d63163a11432bda8f576c86dec8c3bc9d6a156ee03/geventhttpclient-2.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c071db313866c3d0510feb6c0f40ec086ccf7e4a845701b6316c82c06e8b9b29", size = 117838, upload-time = "2024-04-18T21:38:12.036Z" }, - { url = "https://files.pythonhosted.org/packages/6c/8b/e7c9ae813bb41883a96ad9afcf86465219c3bb682daa8b09448481edef8a/geventhttpclient-2.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f36f0c6ef88a27e60af8369d9c2189fe372c6f2943182a7568e0f2ad33bb69f1", size = 123272, upload-time = "2024-04-18T21:38:13.704Z" }, - { url = "https://files.pythonhosted.org/packages/4d/26/71e9b2526009faadda9f588dac04f8bf837a5b97628ab44145efc3fa796e/geventhttpclient-2.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4624843c03a5337282a42247d987c2531193e57255ee307b36eeb4f243a0c21", size = 114319, upload-time = "2024-04-18T21:38:15.097Z" }, - { url = "https://files.pythonhosted.org/packages/34/8c/1da2960293c42b7a6b01dbe3204b569e4cdb55b8289cb1c7154826500f19/geventhttpclient-2.3.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d614573621ba827c417786057e1e20e9f96c4f6b3878c55b1b7b54e1026693bc", size = 112705, upload-time = "2024-04-18T21:38:17.005Z" }, - { url = "https://files.pythonhosted.org/packages/a7/a1/4d08ecf0f213fdc63f78a217f87c07c1cb9891e68cdf74c8cbca76298bdb/geventhttpclient-2.3.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5d51330a40ac9762879d0e296c279c1beae8cfa6484bb196ac829242c416b709", size = 121236, upload-time = "2024-04-18T21:38:18.831Z" }, - { url = "https://files.pythonhosted.org/packages/4f/f7/42ece3e1f54602c518d74364a214da3b35b6be267b335564b7e9f0d37705/geventhttpclient-2.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bc9f2162d4e8cb86bb5322d99bfd552088a3eacd540a841298f06bb8bc1f1f03", size = 117859, upload-time = "2024-04-18T21:38:20.917Z" }, - { url = "https://files.pythonhosted.org/packages/1f/8e/de026b3697bffe5fa1a4938a3882107e378eea826905acf8e46c69b71ffd/geventhttpclient-2.3.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:06e59d3397e63c65ecc7a7561a5289f0cf2e2c2252e29632741e792f57f5d124", size = 127268, upload-time = "2024-04-18T21:38:22.676Z" }, - { url = "https://files.pythonhosted.org/packages/54/bf/1ee99a322467e6825a24612d306a46ca94b51088170d1b5de0df1c82ab2a/geventhttpclient-2.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4436eef515b3e0c1d4a453ae32e047290e780a623c1eddb11026ae9d5fb03d42", size = 116426, upload-time = "2024-04-18T21:38:24.228Z" }, - { url = "https://files.pythonhosted.org/packages/72/54/10c8ec745b3dcbfd52af62977fec85829749c0325e1a5429d050a4b45e75/geventhttpclient-2.3.1-cp310-cp310-win32.whl", hash = "sha256:5d1cf7d8a4f8e15cc8fd7d88ac4cdb058d6274203a42587e594cc9f0850ac862", size = 47599, upload-time = "2024-04-18T21:38:26.385Z" }, - { url = "https://files.pythonhosted.org/packages/da/0d/36a47cdeaa83c3b4efdbd18d77720fa27dc40600998f4dedd7c4a1259862/geventhttpclient-2.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:4deaebc121036f7ea95430c2d0f80ab085b15280e6ab677a6360b70e57020e7f", size = 48302, upload-time = "2024-04-18T21:38:28.297Z" }, - { url = "https://files.pythonhosted.org/packages/56/ad/1fcbbea0465f04d4425960e3737d4d8ae6407043cfc88688fb17b9064160/geventhttpclient-2.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f0ae055b9ce1704f2ce72c0847df28f4e14dbb3eea79256cda6c909d82688ea3", size = 71733, upload-time = "2024-04-18T21:38:30.357Z" }, - { url = "https://files.pythonhosted.org/packages/06/1a/10e547adb675beea407ff7117ecb4e5063534569ac14bb4360279d2888dd/geventhttpclient-2.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f087af2ac439495b5388841d6f3c4de8d2573ca9870593d78f7b554aa5cfa7f5", size = 52060, upload-time = "2024-04-18T21:38:32.561Z" }, - { url = "https://files.pythonhosted.org/packages/e0/c0/9960ac6e8818a00702743cd2a9637d6f26909ac7ac59ca231f446e367b20/geventhttpclient-2.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:76c367d175810facfe56281e516c9a5a4a191eff76641faaa30aa33882ed4b2f", size = 51649, upload-time = "2024-04-18T21:38:34.265Z" }, - { url = "https://files.pythonhosted.org/packages/58/3a/b032cd8f885dafdfa002a8a0e4e21b633713798ec08e19010b815fbfead6/geventhttpclient-2.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a58376d0d461fe0322ff2ad362553b437daee1eeb92b4c0e3b1ffef9e77defbe", size = 117987, upload-time = "2024-04-18T21:38:37.661Z" }, - { url = "https://files.pythonhosted.org/packages/94/36/6493a5cbc20c269a51186946947f3ca2eae687e05831289891027bd038c3/geventhttpclient-2.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f440cc704f8a9869848a109b2c401805c17c070539b2014e7b884ecfc8591e33", size = 123356, upload-time = "2024-04-18T21:38:39.705Z" }, - { url = "https://files.pythonhosted.org/packages/2f/07/b66d9a13b97a7e59d84b4faf704113aa963aaf3a0f71c9138c8740d57d5c/geventhttpclient-2.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f10c62994f9052f23948c19de930b2d1f063240462c8bd7077c2b3290e61f4fa", size = 114460, upload-time = "2024-04-18T21:38:40.947Z" }, - { url = "https://files.pythonhosted.org/packages/4e/72/1467b9e1ef63aecfe3b42333fb7607f66129dffaeca231f97e4be6f71803/geventhttpclient-2.3.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52c45d9f3dd9627844c12e9ca347258c7be585bed54046336220e25ea6eac155", size = 112808, upload-time = "2024-04-18T21:38:42.684Z" }, - { url = "https://files.pythonhosted.org/packages/ce/ef/64894efd67cb3459074c734736ecacff398cd841a5538dc70e3e77d35500/geventhttpclient-2.3.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:77c1a2c6e3854bf87cd5588b95174640c8a881716bd07fa0d131d082270a6795", size = 122049, upload-time = "2024-04-18T21:38:44.184Z" }, - { url = "https://files.pythonhosted.org/packages/c5/c8/1b13b4ea4bb88d7c2db56d070a52daf4757b3139afd83885e81455cb422f/geventhttpclient-2.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ce649d4e25c2d56023471df0bf1e8e2ab67dfe4ff12ce3e8fe7e6fae30cd672a", size = 118755, upload-time = "2024-04-18T21:38:45.654Z" }, - { url = "https://files.pythonhosted.org/packages/d1/06/95ac63fa1ee118a4d5824aa0a6b0dc3a2934a2f4ce695bf6747e1744d813/geventhttpclient-2.3.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:265d9f31b4ac8f688eebef0bd4c814ffb37a16f769ad0c8c8b8c24a84db8eab5", size = 128053, upload-time = "2024-04-18T21:38:47.247Z" }, - { url = "https://files.pythonhosted.org/packages/8a/27/3d6dbbd128e1b965bae198bffa4b5552cd635397e3d2bbcc7d9592890ca9/geventhttpclient-2.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2de436a9d61dae877e4e811fb3e2594e2a1df1b18f4280878f318aef48a562b9", size = 117316, upload-time = "2024-04-18T21:38:49.086Z" }, - { url = "https://files.pythonhosted.org/packages/ed/9a/8b65daf417ff982fa1928ebc6ebdfb081750d426f877f0056288aaa689e8/geventhttpclient-2.3.1-cp311-cp311-win32.whl", hash = "sha256:83e22178b9480b0a95edf0053d4f30b717d0b696b3c262beabe6964d9c5224b1", size = 47598, upload-time = "2024-04-18T21:38:50.919Z" }, - { url = "https://files.pythonhosted.org/packages/ab/83/ed0d14787861cf30beddd3aadc29ad07d75555de43c629ba514ddd2978d0/geventhttpclient-2.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:97b072a282233384c1302a7dee88ad8bfedc916f06b1bc1da54f84980f1406a9", size = 48301, upload-time = "2024-04-18T21:38:52.14Z" }, - { url = "https://files.pythonhosted.org/packages/82/ee/bf3d26170a518d2b1254f44202f2fa4490496b476ee24046ff6c34e79c08/geventhttpclient-2.3.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e1c90abcc2735cd8dd2d2572a13da32f6625392dc04862decb5c6476a3ddee22", size = 71742, upload-time = "2024-04-18T21:38:54.167Z" }, - { url = "https://files.pythonhosted.org/packages/77/72/bd64b2a491094a3fbf7f3c314bb3c3918afb652783a8a9db07b86072da35/geventhttpclient-2.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5deb41c2f51247b4e568c14964f59d7b8e537eff51900564c88af3200004e678", size = 52070, upload-time = "2024-04-18T21:38:55.484Z" }, - { url = "https://files.pythonhosted.org/packages/85/96/e25becfde16c5551ba04ed2beac1f018e2efc70275ec19ae3765ff634ff2/geventhttpclient-2.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c6f1a56a66a90c4beae2f009b5e9d42db9a58ced165aa35441ace04d69cb7b37", size = 51650, upload-time = "2024-04-18T21:38:57.022Z" }, - { url = "https://files.pythonhosted.org/packages/5d/b8/fe6e938a369b3742103d04e5771e1ec7b18c047ac30b06a8e9704e2d34fc/geventhttpclient-2.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ee6e741849c29e3129b1ec3828ac3a5e5dcb043402f852ea92c52334fb8cabf", size = 118507, upload-time = "2024-04-18T21:38:58.936Z" }, - { url = "https://files.pythonhosted.org/packages/68/0b/381d01de049b02dc70addbcc1c8e24d15500bff6a9e89103c4aa8eb352c3/geventhttpclient-2.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0d0972096a63b1ddaa73fa3dab2c7a136e3ab8bf7999a2f85a5dee851fa77cdd", size = 124061, upload-time = "2024-04-18T21:39:00.473Z" }, - { url = "https://files.pythonhosted.org/packages/c6/e6/7c97b5bf41cc403b2936a0887a85550b3153aa4b60c0c5062c49cd6286f2/geventhttpclient-2.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00675ba682fb7d19d659c14686fa8a52a65e3f301b56c2a4ee6333b380dd9467", size = 115060, upload-time = "2024-04-18T21:39:02.323Z" }, - { url = "https://files.pythonhosted.org/packages/45/1f/3e02464449c74a8146f27218471578c1dfabf18731cf047520b76e1b6331/geventhttpclient-2.3.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea77b67c186df90473416f4403839728f70ef6cf1689cec97b4f6bbde392a8a8", size = 113762, upload-time = "2024-04-18T21:39:03.769Z" }, - { url = "https://files.pythonhosted.org/packages/4f/a4/08551776f7d6b219d6f73ca25be88806007b16af51a1dbfed7192528e1c3/geventhttpclient-2.3.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ddcc3f0fdffd9a3801e1005b73026202cffed8199863fdef9315bea9a860a032", size = 122018, upload-time = "2024-04-18T21:39:05.781Z" }, - { url = "https://files.pythonhosted.org/packages/70/14/ba91417ac7cbce8d553f72c885a19c6b9d7f9dc7de81b7814551cf020a57/geventhttpclient-2.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:c9f1ef4ec048563cc621a47ff01a4f10048ff8b676d7a4d75e5433ed8e703e56", size = 118884, upload-time = "2024-04-18T21:39:08.001Z" }, - { url = "https://files.pythonhosted.org/packages/7c/78/e1f2c30e11bda8347a74b3a7254f727ff53ea260244da77d76b96779a006/geventhttpclient-2.3.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:a364b30bec7a0a00dbe256e2b6807e4dc866bead7ac84aaa51ca5e2c3d15c258", size = 128224, upload-time = "2024-04-18T21:39:09.31Z" }, - { url = "https://files.pythonhosted.org/packages/ac/2f/b7fd96e9cfa9d9719b0c9feb50b4cbb341d1940e34fd3305006efa8c3e33/geventhttpclient-2.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:25d255383d3d6a6fbd643bb51ae1a7e4f6f7b0dbd5f3225b537d0bd0432eaf39", size = 117758, upload-time = "2024-04-18T21:39:11.287Z" }, - { url = "https://files.pythonhosted.org/packages/fb/e0/1384c9a76379ab257b75df92283797861dcae592dd98e471df254f87c635/geventhttpclient-2.3.1-cp312-cp312-win32.whl", hash = "sha256:ad0b507e354d2f398186dcb12fe526d0594e7c9387b514fb843f7a14fdf1729a", size = 47595, upload-time = "2024-04-18T21:39:12.535Z" }, - { url = "https://files.pythonhosted.org/packages/54/e3/6b8dbb24e3941e20abbe7736e59290c5d4182057ea1d984d46c853208bcd/geventhttpclient-2.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:7924e0883bc2b177cfe27aa65af6bb9dd57f3e26905c7675a2d1f3ef69df7cca", size = 48271, upload-time = "2024-04-18T21:39:14.479Z" }, - { url = "https://files.pythonhosted.org/packages/ee/9f/251b1b7e665523137a8711f0f0029196cf18b57741135f01aea80a56f16c/geventhttpclient-2.3.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c31431e38df45b3c79bf3c9427c796adb8263d622bc6fa25e2f6ba916c2aad93", size = 49827, upload-time = "2024-04-18T21:39:36.14Z" }, - { url = "https://files.pythonhosted.org/packages/74/c7/ad4c23de669191e1c83cfa28c51d3b50fc246d72e1ee40d4d5b330532492/geventhttpclient-2.3.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:855ab1e145575769b180b57accb0573a77cd6a7392f40a6ef7bc9a4926ebd77b", size = 54017, upload-time = "2024-04-18T21:39:37.577Z" }, - { url = "https://files.pythonhosted.org/packages/04/7b/59fc8c8fbd10596abfc46dc103654e3d9676de64229d8eee4b0a4ac2e890/geventhttpclient-2.3.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a374aad77c01539e786d0c7829bec2eba034ccd45733c1bf9811ad18d2a8ecd", size = 58359, upload-time = "2024-04-18T21:39:39.437Z" }, - { url = "https://files.pythonhosted.org/packages/94/b7/743552b0ecda75458c83d55d62937e29c9ee9a42598f57d4025d5de70004/geventhttpclient-2.3.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66c1e97460608304f400485ac099736fff3566d3d8db2038533d466f8cf5de5a", size = 54262, upload-time = "2024-04-18T21:39:40.866Z" }, - { url = "https://files.pythonhosted.org/packages/18/60/10f6215b6cc76b5845a7f4b9c3d1f47d7ecd84ce8769b1e27e0482d605d7/geventhttpclient-2.3.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4f843f81ee44ba4c553a1b3f73115e0ad8f00044023c24db29f5b1df3da08465", size = 48343, upload-time = "2024-04-18T21:39:42.173Z" }, + { url = "https://files.pythonhosted.org/packages/a2/a5/5e49d6a581b3f1399425e22961c6e341e90c12fa2193ed0adee9afbd864c/geventhttpclient-2.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:da22ab7bf5af4ba3d07cffee6de448b42696e53e7ac1fe97ed289037733bf1c2", size = 71729 }, + { url = "https://files.pythonhosted.org/packages/eb/23/4ff584e5f344dae64b5bc588b65c4ea81083f9d662b9f64cf5f28e5ae9cc/geventhttpclient-2.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2399e3d4e2fae8bbd91756189da6e9d84adf8f3eaace5eef0667874a705a29f8", size = 52062 }, + { url = "https://files.pythonhosted.org/packages/bb/60/6bd8badb97b31a49f4c2b79466abce208a97dad95d447893c7546063fc8a/geventhttpclient-2.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3e33e87d0d5b9f5782c4e6d3cb7e3592fea41af52713137d04776df7646d71b", size = 51645 }, + { url = "https://files.pythonhosted.org/packages/e1/62/47d431bf05f74aa683d63163a11432bda8f576c86dec8c3bc9d6a156ee03/geventhttpclient-2.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c071db313866c3d0510feb6c0f40ec086ccf7e4a845701b6316c82c06e8b9b29", size = 117838 }, + { url = "https://files.pythonhosted.org/packages/6c/8b/e7c9ae813bb41883a96ad9afcf86465219c3bb682daa8b09448481edef8a/geventhttpclient-2.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f36f0c6ef88a27e60af8369d9c2189fe372c6f2943182a7568e0f2ad33bb69f1", size = 123272 }, + { url = "https://files.pythonhosted.org/packages/4d/26/71e9b2526009faadda9f588dac04f8bf837a5b97628ab44145efc3fa796e/geventhttpclient-2.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4624843c03a5337282a42247d987c2531193e57255ee307b36eeb4f243a0c21", size = 114319 }, + { url = "https://files.pythonhosted.org/packages/34/8c/1da2960293c42b7a6b01dbe3204b569e4cdb55b8289cb1c7154826500f19/geventhttpclient-2.3.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d614573621ba827c417786057e1e20e9f96c4f6b3878c55b1b7b54e1026693bc", size = 112705 }, + { url = "https://files.pythonhosted.org/packages/a7/a1/4d08ecf0f213fdc63f78a217f87c07c1cb9891e68cdf74c8cbca76298bdb/geventhttpclient-2.3.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5d51330a40ac9762879d0e296c279c1beae8cfa6484bb196ac829242c416b709", size = 121236 }, + { url = "https://files.pythonhosted.org/packages/4f/f7/42ece3e1f54602c518d74364a214da3b35b6be267b335564b7e9f0d37705/geventhttpclient-2.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bc9f2162d4e8cb86bb5322d99bfd552088a3eacd540a841298f06bb8bc1f1f03", size = 117859 }, + { url = "https://files.pythonhosted.org/packages/1f/8e/de026b3697bffe5fa1a4938a3882107e378eea826905acf8e46c69b71ffd/geventhttpclient-2.3.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:06e59d3397e63c65ecc7a7561a5289f0cf2e2c2252e29632741e792f57f5d124", size = 127268 }, + { url = "https://files.pythonhosted.org/packages/54/bf/1ee99a322467e6825a24612d306a46ca94b51088170d1b5de0df1c82ab2a/geventhttpclient-2.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4436eef515b3e0c1d4a453ae32e047290e780a623c1eddb11026ae9d5fb03d42", size = 116426 }, + { url = "https://files.pythonhosted.org/packages/72/54/10c8ec745b3dcbfd52af62977fec85829749c0325e1a5429d050a4b45e75/geventhttpclient-2.3.1-cp310-cp310-win32.whl", hash = "sha256:5d1cf7d8a4f8e15cc8fd7d88ac4cdb058d6274203a42587e594cc9f0850ac862", size = 47599 }, + { url = "https://files.pythonhosted.org/packages/da/0d/36a47cdeaa83c3b4efdbd18d77720fa27dc40600998f4dedd7c4a1259862/geventhttpclient-2.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:4deaebc121036f7ea95430c2d0f80ab085b15280e6ab677a6360b70e57020e7f", size = 48302 }, + { url = "https://files.pythonhosted.org/packages/56/ad/1fcbbea0465f04d4425960e3737d4d8ae6407043cfc88688fb17b9064160/geventhttpclient-2.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f0ae055b9ce1704f2ce72c0847df28f4e14dbb3eea79256cda6c909d82688ea3", size = 71733 }, + { url = "https://files.pythonhosted.org/packages/06/1a/10e547adb675beea407ff7117ecb4e5063534569ac14bb4360279d2888dd/geventhttpclient-2.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f087af2ac439495b5388841d6f3c4de8d2573ca9870593d78f7b554aa5cfa7f5", size = 52060 }, + { url = "https://files.pythonhosted.org/packages/e0/c0/9960ac6e8818a00702743cd2a9637d6f26909ac7ac59ca231f446e367b20/geventhttpclient-2.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:76c367d175810facfe56281e516c9a5a4a191eff76641faaa30aa33882ed4b2f", size = 51649 }, + { url = "https://files.pythonhosted.org/packages/58/3a/b032cd8f885dafdfa002a8a0e4e21b633713798ec08e19010b815fbfead6/geventhttpclient-2.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a58376d0d461fe0322ff2ad362553b437daee1eeb92b4c0e3b1ffef9e77defbe", size = 117987 }, + { url = "https://files.pythonhosted.org/packages/94/36/6493a5cbc20c269a51186946947f3ca2eae687e05831289891027bd038c3/geventhttpclient-2.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f440cc704f8a9869848a109b2c401805c17c070539b2014e7b884ecfc8591e33", size = 123356 }, + { url = "https://files.pythonhosted.org/packages/2f/07/b66d9a13b97a7e59d84b4faf704113aa963aaf3a0f71c9138c8740d57d5c/geventhttpclient-2.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f10c62994f9052f23948c19de930b2d1f063240462c8bd7077c2b3290e61f4fa", size = 114460 }, + { url = "https://files.pythonhosted.org/packages/4e/72/1467b9e1ef63aecfe3b42333fb7607f66129dffaeca231f97e4be6f71803/geventhttpclient-2.3.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52c45d9f3dd9627844c12e9ca347258c7be585bed54046336220e25ea6eac155", size = 112808 }, + { url = "https://files.pythonhosted.org/packages/ce/ef/64894efd67cb3459074c734736ecacff398cd841a5538dc70e3e77d35500/geventhttpclient-2.3.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:77c1a2c6e3854bf87cd5588b95174640c8a881716bd07fa0d131d082270a6795", size = 122049 }, + { url = "https://files.pythonhosted.org/packages/c5/c8/1b13b4ea4bb88d7c2db56d070a52daf4757b3139afd83885e81455cb422f/geventhttpclient-2.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ce649d4e25c2d56023471df0bf1e8e2ab67dfe4ff12ce3e8fe7e6fae30cd672a", size = 118755 }, + { url = "https://files.pythonhosted.org/packages/d1/06/95ac63fa1ee118a4d5824aa0a6b0dc3a2934a2f4ce695bf6747e1744d813/geventhttpclient-2.3.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:265d9f31b4ac8f688eebef0bd4c814ffb37a16f769ad0c8c8b8c24a84db8eab5", size = 128053 }, + { url = "https://files.pythonhosted.org/packages/8a/27/3d6dbbd128e1b965bae198bffa4b5552cd635397e3d2bbcc7d9592890ca9/geventhttpclient-2.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2de436a9d61dae877e4e811fb3e2594e2a1df1b18f4280878f318aef48a562b9", size = 117316 }, + { url = "https://files.pythonhosted.org/packages/ed/9a/8b65daf417ff982fa1928ebc6ebdfb081750d426f877f0056288aaa689e8/geventhttpclient-2.3.1-cp311-cp311-win32.whl", hash = "sha256:83e22178b9480b0a95edf0053d4f30b717d0b696b3c262beabe6964d9c5224b1", size = 47598 }, + { url = "https://files.pythonhosted.org/packages/ab/83/ed0d14787861cf30beddd3aadc29ad07d75555de43c629ba514ddd2978d0/geventhttpclient-2.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:97b072a282233384c1302a7dee88ad8bfedc916f06b1bc1da54f84980f1406a9", size = 48301 }, + { url = "https://files.pythonhosted.org/packages/82/ee/bf3d26170a518d2b1254f44202f2fa4490496b476ee24046ff6c34e79c08/geventhttpclient-2.3.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e1c90abcc2735cd8dd2d2572a13da32f6625392dc04862decb5c6476a3ddee22", size = 71742 }, + { url = "https://files.pythonhosted.org/packages/77/72/bd64b2a491094a3fbf7f3c314bb3c3918afb652783a8a9db07b86072da35/geventhttpclient-2.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5deb41c2f51247b4e568c14964f59d7b8e537eff51900564c88af3200004e678", size = 52070 }, + { url = "https://files.pythonhosted.org/packages/85/96/e25becfde16c5551ba04ed2beac1f018e2efc70275ec19ae3765ff634ff2/geventhttpclient-2.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c6f1a56a66a90c4beae2f009b5e9d42db9a58ced165aa35441ace04d69cb7b37", size = 51650 }, + { url = "https://files.pythonhosted.org/packages/5d/b8/fe6e938a369b3742103d04e5771e1ec7b18c047ac30b06a8e9704e2d34fc/geventhttpclient-2.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ee6e741849c29e3129b1ec3828ac3a5e5dcb043402f852ea92c52334fb8cabf", size = 118507 }, + { url = "https://files.pythonhosted.org/packages/68/0b/381d01de049b02dc70addbcc1c8e24d15500bff6a9e89103c4aa8eb352c3/geventhttpclient-2.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0d0972096a63b1ddaa73fa3dab2c7a136e3ab8bf7999a2f85a5dee851fa77cdd", size = 124061 }, + { url = "https://files.pythonhosted.org/packages/c6/e6/7c97b5bf41cc403b2936a0887a85550b3153aa4b60c0c5062c49cd6286f2/geventhttpclient-2.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00675ba682fb7d19d659c14686fa8a52a65e3f301b56c2a4ee6333b380dd9467", size = 115060 }, + { url = "https://files.pythonhosted.org/packages/45/1f/3e02464449c74a8146f27218471578c1dfabf18731cf047520b76e1b6331/geventhttpclient-2.3.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea77b67c186df90473416f4403839728f70ef6cf1689cec97b4f6bbde392a8a8", size = 113762 }, + { url = "https://files.pythonhosted.org/packages/4f/a4/08551776f7d6b219d6f73ca25be88806007b16af51a1dbfed7192528e1c3/geventhttpclient-2.3.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ddcc3f0fdffd9a3801e1005b73026202cffed8199863fdef9315bea9a860a032", size = 122018 }, + { url = "https://files.pythonhosted.org/packages/70/14/ba91417ac7cbce8d553f72c885a19c6b9d7f9dc7de81b7814551cf020a57/geventhttpclient-2.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:c9f1ef4ec048563cc621a47ff01a4f10048ff8b676d7a4d75e5433ed8e703e56", size = 118884 }, + { url = "https://files.pythonhosted.org/packages/7c/78/e1f2c30e11bda8347a74b3a7254f727ff53ea260244da77d76b96779a006/geventhttpclient-2.3.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:a364b30bec7a0a00dbe256e2b6807e4dc866bead7ac84aaa51ca5e2c3d15c258", size = 128224 }, + { url = "https://files.pythonhosted.org/packages/ac/2f/b7fd96e9cfa9d9719b0c9feb50b4cbb341d1940e34fd3305006efa8c3e33/geventhttpclient-2.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:25d255383d3d6a6fbd643bb51ae1a7e4f6f7b0dbd5f3225b537d0bd0432eaf39", size = 117758 }, + { url = "https://files.pythonhosted.org/packages/fb/e0/1384c9a76379ab257b75df92283797861dcae592dd98e471df254f87c635/geventhttpclient-2.3.1-cp312-cp312-win32.whl", hash = "sha256:ad0b507e354d2f398186dcb12fe526d0594e7c9387b514fb843f7a14fdf1729a", size = 47595 }, + { url = "https://files.pythonhosted.org/packages/54/e3/6b8dbb24e3941e20abbe7736e59290c5d4182057ea1d984d46c853208bcd/geventhttpclient-2.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:7924e0883bc2b177cfe27aa65af6bb9dd57f3e26905c7675a2d1f3ef69df7cca", size = 48271 }, + { url = "https://files.pythonhosted.org/packages/ee/9f/251b1b7e665523137a8711f0f0029196cf18b57741135f01aea80a56f16c/geventhttpclient-2.3.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c31431e38df45b3c79bf3c9427c796adb8263d622bc6fa25e2f6ba916c2aad93", size = 49827 }, + { url = "https://files.pythonhosted.org/packages/74/c7/ad4c23de669191e1c83cfa28c51d3b50fc246d72e1ee40d4d5b330532492/geventhttpclient-2.3.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:855ab1e145575769b180b57accb0573a77cd6a7392f40a6ef7bc9a4926ebd77b", size = 54017 }, + { url = "https://files.pythonhosted.org/packages/04/7b/59fc8c8fbd10596abfc46dc103654e3d9676de64229d8eee4b0a4ac2e890/geventhttpclient-2.3.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a374aad77c01539e786d0c7829bec2eba034ccd45733c1bf9811ad18d2a8ecd", size = 58359 }, + { url = "https://files.pythonhosted.org/packages/94/b7/743552b0ecda75458c83d55d62937e29c9ee9a42598f57d4025d5de70004/geventhttpclient-2.3.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66c1e97460608304f400485ac099736fff3566d3d8db2038533d466f8cf5de5a", size = 54262 }, + { url = "https://files.pythonhosted.org/packages/18/60/10f6215b6cc76b5845a7f4b9c3d1f47d7ecd84ce8769b1e27e0482d605d7/geventhttpclient-2.3.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4f843f81ee44ba4c553a1b3f73115e0ad8f00044023c24db29f5b1df3da08465", size = 48343 }, ] [[package]] name = "greenlet" version = "3.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2f/ff/df5fede753cc10f6a5be0931204ea30c35fa2f2ea7a35b25bdaf4fe40e46/greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467", size = 186022, upload-time = "2024-09-20T18:21:04.506Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2f/ff/df5fede753cc10f6a5be0931204ea30c35fa2f2ea7a35b25bdaf4fe40e46/greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467", size = 186022 } wheels = [ - { url = "https://files.pythonhosted.org/packages/25/90/5234a78dc0ef6496a6eb97b67a42a8e96742a56f7dc808cb954a85390448/greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563", size = 271235, upload-time = "2024-09-20T17:07:18.761Z" }, - { url = "https://files.pythonhosted.org/packages/7c/16/cd631fa0ab7d06ef06387135b7549fdcc77d8d859ed770a0d28e47b20972/greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83", size = 637168, upload-time = "2024-09-20T17:36:43.774Z" }, - { url = "https://files.pythonhosted.org/packages/2f/b1/aed39043a6fec33c284a2c9abd63ce191f4f1a07319340ffc04d2ed3256f/greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0", size = 648826, upload-time = "2024-09-20T17:39:16.921Z" }, - { url = "https://files.pythonhosted.org/packages/76/25/40e0112f7f3ebe54e8e8ed91b2b9f970805143efef16d043dfc15e70f44b/greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120", size = 644443, upload-time = "2024-09-20T17:44:21.896Z" }, - { url = "https://files.pythonhosted.org/packages/fb/2f/3850b867a9af519794784a7eeed1dd5bc68ffbcc5b28cef703711025fd0a/greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc", size = 643295, upload-time = "2024-09-20T17:08:37.951Z" }, - { url = "https://files.pythonhosted.org/packages/cf/69/79e4d63b9387b48939096e25115b8af7cd8a90397a304f92436bcb21f5b2/greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617", size = 599544, upload-time = "2024-09-20T17:08:27.894Z" }, - { url = "https://files.pythonhosted.org/packages/46/1d/44dbcb0e6c323bd6f71b8c2f4233766a5faf4b8948873225d34a0b7efa71/greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7", size = 1125456, upload-time = "2024-09-20T17:44:11.755Z" }, - { url = "https://files.pythonhosted.org/packages/e0/1d/a305dce121838d0278cee39d5bb268c657f10a5363ae4b726848f833f1bb/greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6", size = 1149111, upload-time = "2024-09-20T17:09:22.104Z" }, - { url = "https://files.pythonhosted.org/packages/96/28/d62835fb33fb5652f2e98d34c44ad1a0feacc8b1d3f1aecab035f51f267d/greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80", size = 298392, upload-time = "2024-09-20T17:28:51.988Z" }, - { url = "https://files.pythonhosted.org/packages/28/62/1c2665558618553c42922ed47a4e6d6527e2fa3516a8256c2f431c5d0441/greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70", size = 272479, upload-time = "2024-09-20T17:07:22.332Z" }, - { url = "https://files.pythonhosted.org/packages/76/9d/421e2d5f07285b6e4e3a676b016ca781f63cfe4a0cd8eaecf3fd6f7a71ae/greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159", size = 640404, upload-time = "2024-09-20T17:36:45.588Z" }, - { url = "https://files.pythonhosted.org/packages/e5/de/6e05f5c59262a584e502dd3d261bbdd2c97ab5416cc9c0b91ea38932a901/greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e", size = 652813, upload-time = "2024-09-20T17:39:19.052Z" }, - { url = "https://files.pythonhosted.org/packages/49/93/d5f93c84241acdea15a8fd329362c2c71c79e1a507c3f142a5d67ea435ae/greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1", size = 648517, upload-time = "2024-09-20T17:44:24.101Z" }, - { url = "https://files.pythonhosted.org/packages/15/85/72f77fc02d00470c86a5c982b8daafdf65d38aefbbe441cebff3bf7037fc/greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383", size = 647831, upload-time = "2024-09-20T17:08:40.577Z" }, - { url = "https://files.pythonhosted.org/packages/f7/4b/1c9695aa24f808e156c8f4813f685d975ca73c000c2a5056c514c64980f6/greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a", size = 602413, upload-time = "2024-09-20T17:08:31.728Z" }, - { url = "https://files.pythonhosted.org/packages/76/70/ad6e5b31ef330f03b12559d19fda2606a522d3849cde46b24f223d6d1619/greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511", size = 1129619, upload-time = "2024-09-20T17:44:14.222Z" }, - { url = "https://files.pythonhosted.org/packages/f4/fb/201e1b932e584066e0f0658b538e73c459b34d44b4bd4034f682423bc801/greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395", size = 1155198, upload-time = "2024-09-20T17:09:23.903Z" }, - { url = "https://files.pythonhosted.org/packages/12/da/b9ed5e310bb8b89661b80cbcd4db5a067903bbcd7fc854923f5ebb4144f0/greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39", size = 298930, upload-time = "2024-09-20T17:25:18.656Z" }, - { url = "https://files.pythonhosted.org/packages/7d/ec/bad1ac26764d26aa1353216fcbfa4670050f66d445448aafa227f8b16e80/greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d", size = 274260, upload-time = "2024-09-20T17:08:07.301Z" }, - { url = "https://files.pythonhosted.org/packages/66/d4/c8c04958870f482459ab5956c2942c4ec35cac7fe245527f1039837c17a9/greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79", size = 649064, upload-time = "2024-09-20T17:36:47.628Z" }, - { url = "https://files.pythonhosted.org/packages/51/41/467b12a8c7c1303d20abcca145db2be4e6cd50a951fa30af48b6ec607581/greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa", size = 663420, upload-time = "2024-09-20T17:39:21.258Z" }, - { url = "https://files.pythonhosted.org/packages/27/8f/2a93cd9b1e7107d5c7b3b7816eeadcac2ebcaf6d6513df9abaf0334777f6/greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441", size = 658035, upload-time = "2024-09-20T17:44:26.501Z" }, - { url = "https://files.pythonhosted.org/packages/57/5c/7c6f50cb12be092e1dccb2599be5a942c3416dbcfb76efcf54b3f8be4d8d/greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36", size = 660105, upload-time = "2024-09-20T17:08:42.048Z" }, - { url = "https://files.pythonhosted.org/packages/f1/66/033e58a50fd9ec9df00a8671c74f1f3a320564c6415a4ed82a1c651654ba/greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9", size = 613077, upload-time = "2024-09-20T17:08:33.707Z" }, - { url = "https://files.pythonhosted.org/packages/19/c5/36384a06f748044d06bdd8776e231fadf92fc896bd12cb1c9f5a1bda9578/greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0", size = 1135975, upload-time = "2024-09-20T17:44:15.989Z" }, - { url = "https://files.pythonhosted.org/packages/38/f9/c0a0eb61bdf808d23266ecf1d63309f0e1471f284300ce6dac0ae1231881/greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942", size = 1163955, upload-time = "2024-09-20T17:09:25.539Z" }, - { url = "https://files.pythonhosted.org/packages/43/21/a5d9df1d21514883333fc86584c07c2b49ba7c602e670b174bd73cfc9c7f/greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01", size = 299655, upload-time = "2024-09-20T17:21:22.427Z" }, - { url = "https://files.pythonhosted.org/packages/f3/57/0db4940cd7bb461365ca8d6fd53e68254c9dbbcc2b452e69d0d41f10a85e/greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1", size = 272990, upload-time = "2024-09-20T17:08:26.312Z" }, - { url = "https://files.pythonhosted.org/packages/1c/ec/423d113c9f74e5e402e175b157203e9102feeb7088cee844d735b28ef963/greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff", size = 649175, upload-time = "2024-09-20T17:36:48.983Z" }, - { url = "https://files.pythonhosted.org/packages/a9/46/ddbd2db9ff209186b7b7c621d1432e2f21714adc988703dbdd0e65155c77/greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a", size = 663425, upload-time = "2024-09-20T17:39:22.705Z" }, - { url = "https://files.pythonhosted.org/packages/bc/f9/9c82d6b2b04aa37e38e74f0c429aece5eeb02bab6e3b98e7db89b23d94c6/greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e", size = 657736, upload-time = "2024-09-20T17:44:28.544Z" }, - { url = "https://files.pythonhosted.org/packages/d9/42/b87bc2a81e3a62c3de2b0d550bf91a86939442b7ff85abb94eec3fc0e6aa/greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4", size = 660347, upload-time = "2024-09-20T17:08:45.56Z" }, - { url = "https://files.pythonhosted.org/packages/37/fa/71599c3fd06336cdc3eac52e6871cfebab4d9d70674a9a9e7a482c318e99/greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e", size = 615583, upload-time = "2024-09-20T17:08:36.85Z" }, - { url = "https://files.pythonhosted.org/packages/4e/96/e9ef85de031703ee7a4483489b40cf307f93c1824a02e903106f2ea315fe/greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1", size = 1133039, upload-time = "2024-09-20T17:44:18.287Z" }, - { url = "https://files.pythonhosted.org/packages/87/76/b2b6362accd69f2d1889db61a18c94bc743e961e3cab344c2effaa4b4a25/greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c", size = 1160716, upload-time = "2024-09-20T17:09:27.112Z" }, - { url = "https://files.pythonhosted.org/packages/1f/1b/54336d876186920e185066d8c3024ad55f21d7cc3683c856127ddb7b13ce/greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761", size = 299490, upload-time = "2024-09-20T17:17:09.501Z" }, - { url = "https://files.pythonhosted.org/packages/5f/17/bea55bf36990e1638a2af5ba10c1640273ef20f627962cf97107f1e5d637/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011", size = 643731, upload-time = "2024-09-20T17:36:50.376Z" }, - { url = "https://files.pythonhosted.org/packages/78/d2/aa3d2157f9ab742a08e0fd8f77d4699f37c22adfbfeb0c610a186b5f75e0/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13", size = 649304, upload-time = "2024-09-20T17:39:24.55Z" }, - { url = "https://files.pythonhosted.org/packages/f1/8e/d0aeffe69e53ccff5a28fa86f07ad1d2d2d6537a9506229431a2a02e2f15/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475", size = 646537, upload-time = "2024-09-20T17:44:31.102Z" }, - { url = "https://files.pythonhosted.org/packages/05/79/e15408220bbb989469c8871062c97c6c9136770657ba779711b90870d867/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b", size = 642506, upload-time = "2024-09-20T17:08:47.852Z" }, - { url = "https://files.pythonhosted.org/packages/18/87/470e01a940307796f1d25f8167b551a968540fbe0551c0ebb853cb527dd6/greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822", size = 602753, upload-time = "2024-09-20T17:08:38.079Z" }, - { url = "https://files.pythonhosted.org/packages/e2/72/576815ba674eddc3c25028238f74d7b8068902b3968cbe456771b166455e/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01", size = 1122731, upload-time = "2024-09-20T17:44:20.556Z" }, - { url = "https://files.pythonhosted.org/packages/ac/38/08cc303ddddc4b3d7c628c3039a61a3aae36c241ed01393d00c2fd663473/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6", size = 1142112, upload-time = "2024-09-20T17:09:28.753Z" }, + { url = "https://files.pythonhosted.org/packages/25/90/5234a78dc0ef6496a6eb97b67a42a8e96742a56f7dc808cb954a85390448/greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563", size = 271235 }, + { url = "https://files.pythonhosted.org/packages/7c/16/cd631fa0ab7d06ef06387135b7549fdcc77d8d859ed770a0d28e47b20972/greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83", size = 637168 }, + { url = "https://files.pythonhosted.org/packages/2f/b1/aed39043a6fec33c284a2c9abd63ce191f4f1a07319340ffc04d2ed3256f/greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0", size = 648826 }, + { url = "https://files.pythonhosted.org/packages/76/25/40e0112f7f3ebe54e8e8ed91b2b9f970805143efef16d043dfc15e70f44b/greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120", size = 644443 }, + { url = "https://files.pythonhosted.org/packages/fb/2f/3850b867a9af519794784a7eeed1dd5bc68ffbcc5b28cef703711025fd0a/greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc", size = 643295 }, + { url = "https://files.pythonhosted.org/packages/cf/69/79e4d63b9387b48939096e25115b8af7cd8a90397a304f92436bcb21f5b2/greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617", size = 599544 }, + { url = "https://files.pythonhosted.org/packages/46/1d/44dbcb0e6c323bd6f71b8c2f4233766a5faf4b8948873225d34a0b7efa71/greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7", size = 1125456 }, + { url = "https://files.pythonhosted.org/packages/e0/1d/a305dce121838d0278cee39d5bb268c657f10a5363ae4b726848f833f1bb/greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6", size = 1149111 }, + { url = "https://files.pythonhosted.org/packages/96/28/d62835fb33fb5652f2e98d34c44ad1a0feacc8b1d3f1aecab035f51f267d/greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80", size = 298392 }, + { url = "https://files.pythonhosted.org/packages/28/62/1c2665558618553c42922ed47a4e6d6527e2fa3516a8256c2f431c5d0441/greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70", size = 272479 }, + { url = "https://files.pythonhosted.org/packages/76/9d/421e2d5f07285b6e4e3a676b016ca781f63cfe4a0cd8eaecf3fd6f7a71ae/greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159", size = 640404 }, + { url = "https://files.pythonhosted.org/packages/e5/de/6e05f5c59262a584e502dd3d261bbdd2c97ab5416cc9c0b91ea38932a901/greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e", size = 652813 }, + { url = "https://files.pythonhosted.org/packages/49/93/d5f93c84241acdea15a8fd329362c2c71c79e1a507c3f142a5d67ea435ae/greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1", size = 648517 }, + { url = "https://files.pythonhosted.org/packages/15/85/72f77fc02d00470c86a5c982b8daafdf65d38aefbbe441cebff3bf7037fc/greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383", size = 647831 }, + { url = "https://files.pythonhosted.org/packages/f7/4b/1c9695aa24f808e156c8f4813f685d975ca73c000c2a5056c514c64980f6/greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a", size = 602413 }, + { url = "https://files.pythonhosted.org/packages/76/70/ad6e5b31ef330f03b12559d19fda2606a522d3849cde46b24f223d6d1619/greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511", size = 1129619 }, + { url = "https://files.pythonhosted.org/packages/f4/fb/201e1b932e584066e0f0658b538e73c459b34d44b4bd4034f682423bc801/greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395", size = 1155198 }, + { url = "https://files.pythonhosted.org/packages/12/da/b9ed5e310bb8b89661b80cbcd4db5a067903bbcd7fc854923f5ebb4144f0/greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39", size = 298930 }, + { url = "https://files.pythonhosted.org/packages/7d/ec/bad1ac26764d26aa1353216fcbfa4670050f66d445448aafa227f8b16e80/greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d", size = 274260 }, + { url = "https://files.pythonhosted.org/packages/66/d4/c8c04958870f482459ab5956c2942c4ec35cac7fe245527f1039837c17a9/greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79", size = 649064 }, + { url = "https://files.pythonhosted.org/packages/51/41/467b12a8c7c1303d20abcca145db2be4e6cd50a951fa30af48b6ec607581/greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa", size = 663420 }, + { url = "https://files.pythonhosted.org/packages/27/8f/2a93cd9b1e7107d5c7b3b7816eeadcac2ebcaf6d6513df9abaf0334777f6/greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441", size = 658035 }, + { url = "https://files.pythonhosted.org/packages/57/5c/7c6f50cb12be092e1dccb2599be5a942c3416dbcfb76efcf54b3f8be4d8d/greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36", size = 660105 }, + { url = "https://files.pythonhosted.org/packages/f1/66/033e58a50fd9ec9df00a8671c74f1f3a320564c6415a4ed82a1c651654ba/greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9", size = 613077 }, + { url = "https://files.pythonhosted.org/packages/19/c5/36384a06f748044d06bdd8776e231fadf92fc896bd12cb1c9f5a1bda9578/greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0", size = 1135975 }, + { url = "https://files.pythonhosted.org/packages/38/f9/c0a0eb61bdf808d23266ecf1d63309f0e1471f284300ce6dac0ae1231881/greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942", size = 1163955 }, + { url = "https://files.pythonhosted.org/packages/43/21/a5d9df1d21514883333fc86584c07c2b49ba7c602e670b174bd73cfc9c7f/greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01", size = 299655 }, + { url = "https://files.pythonhosted.org/packages/f3/57/0db4940cd7bb461365ca8d6fd53e68254c9dbbcc2b452e69d0d41f10a85e/greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1", size = 272990 }, + { url = "https://files.pythonhosted.org/packages/1c/ec/423d113c9f74e5e402e175b157203e9102feeb7088cee844d735b28ef963/greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff", size = 649175 }, + { url = "https://files.pythonhosted.org/packages/a9/46/ddbd2db9ff209186b7b7c621d1432e2f21714adc988703dbdd0e65155c77/greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a", size = 663425 }, + { url = "https://files.pythonhosted.org/packages/bc/f9/9c82d6b2b04aa37e38e74f0c429aece5eeb02bab6e3b98e7db89b23d94c6/greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e", size = 657736 }, + { url = "https://files.pythonhosted.org/packages/d9/42/b87bc2a81e3a62c3de2b0d550bf91a86939442b7ff85abb94eec3fc0e6aa/greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4", size = 660347 }, + { url = "https://files.pythonhosted.org/packages/37/fa/71599c3fd06336cdc3eac52e6871cfebab4d9d70674a9a9e7a482c318e99/greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e", size = 615583 }, + { url = "https://files.pythonhosted.org/packages/4e/96/e9ef85de031703ee7a4483489b40cf307f93c1824a02e903106f2ea315fe/greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1", size = 1133039 }, + { url = "https://files.pythonhosted.org/packages/87/76/b2b6362accd69f2d1889db61a18c94bc743e961e3cab344c2effaa4b4a25/greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c", size = 1160716 }, + { url = "https://files.pythonhosted.org/packages/1f/1b/54336d876186920e185066d8c3024ad55f21d7cc3683c856127ddb7b13ce/greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761", size = 299490 }, + { url = "https://files.pythonhosted.org/packages/5f/17/bea55bf36990e1638a2af5ba10c1640273ef20f627962cf97107f1e5d637/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011", size = 643731 }, + { url = "https://files.pythonhosted.org/packages/78/d2/aa3d2157f9ab742a08e0fd8f77d4699f37c22adfbfeb0c610a186b5f75e0/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13", size = 649304 }, + { url = "https://files.pythonhosted.org/packages/f1/8e/d0aeffe69e53ccff5a28fa86f07ad1d2d2d6537a9506229431a2a02e2f15/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475", size = 646537 }, + { url = "https://files.pythonhosted.org/packages/05/79/e15408220bbb989469c8871062c97c6c9136770657ba779711b90870d867/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b", size = 642506 }, + { url = "https://files.pythonhosted.org/packages/18/87/470e01a940307796f1d25f8167b551a968540fbe0551c0ebb853cb527dd6/greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822", size = 602753 }, + { url = "https://files.pythonhosted.org/packages/e2/72/576815ba674eddc3c25028238f74d7b8068902b3968cbe456771b166455e/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01", size = 1122731 }, + { url = "https://files.pythonhosted.org/packages/ac/38/08cc303ddddc4b3d7c628c3039a61a3aae36c241ed01393d00c2fd663473/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6", size = 1142112 }, ] [[package]] @@ -805,33 +916,33 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "packaging" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/34/72/9614c465dc206155d93eff0ca20d42e1e35afc533971379482de953521a4/gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec", size = 375031, upload-time = "2024-08-10T20:25:27.378Z" } +sdist = { url = "https://files.pythonhosted.org/packages/34/72/9614c465dc206155d93eff0ca20d42e1e35afc533971379482de953521a4/gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec", size = 375031 } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/7d/6dac2a6e1eba33ee43f318edbed4ff29151a49b5d37f080aad1e6469bca4/gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", size = 85029, upload-time = "2024-08-10T20:25:24.996Z" }, + { url = "https://files.pythonhosted.org/packages/cb/7d/6dac2a6e1eba33ee43f318edbed4ff29151a49b5d37f080aad1e6469bca4/gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", size = 85029 }, ] [[package]] name = "h11" version = "0.14.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418, upload-time = "2022-09-25T15:40:01.519Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259, upload-time = "2022-09-25T15:39:59.68Z" }, + { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, ] [[package]] name = "hf-xet" -version = "1.1.2" +version = "1.1.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/be/58f20728a5b445f8b064e74f0618897b3439f5ef90934da1916b9dfac76f/hf_xet-1.1.2.tar.gz", hash = "sha256:3712d6d4819d3976a1c18e36db9f503e296283f9363af818f50703506ed63da3", size = 467009, upload-time = "2025-05-16T20:44:34.944Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/0a/a0f56735940fde6dd627602fec9ab3bad23f66a272397560abd65aba416e/hf_xet-1.1.7.tar.gz", hash = "sha256:20cec8db4561338824a3b5f8c19774055b04a8df7fff0cb1ff2cb1a0c1607b80", size = 477719 } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/ae/f1a63f75d9886f18a80220ba31a1c7b9c4752f03aae452f358f538c6a991/hf_xet-1.1.2-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:dfd1873fd648488c70735cb60f7728512bca0e459e61fcd107069143cd798469", size = 2642559, upload-time = "2025-05-16T20:44:30.217Z" }, - { url = "https://files.pythonhosted.org/packages/50/ab/d2c83ae18f1015d926defd5bfbe94c62d15e93f900e6a192e318ee947105/hf_xet-1.1.2-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:29b584983b2d977c44157d9241dcf0fd50acde0b7bff8897fe4386912330090d", size = 2541360, upload-time = "2025-05-16T20:44:29.056Z" }, - { url = "https://files.pythonhosted.org/packages/9f/a7/693dc9f34f979e30a378125e2150a0b2d8d166e6d83ce3950eeb81e560aa/hf_xet-1.1.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b29ac84298147fe9164cc55ad994ba47399f90b5d045b0b803b99cf5f06d8ec", size = 5183081, upload-time = "2025-05-16T20:44:27.505Z" }, - { url = "https://files.pythonhosted.org/packages/3d/23/c48607883f692a36c0a7735f47f98bad32dbe459a32d1568c0f21576985d/hf_xet-1.1.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d921ba32615676e436a0d15e162331abc9ed43d440916b1d836dc27ce1546173", size = 5356100, upload-time = "2025-05-16T20:44:25.681Z" }, - { url = "https://files.pythonhosted.org/packages/eb/5b/b2316c7f1076da0582b52ea228f68bea95e243c388440d1dc80297c9d813/hf_xet-1.1.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d9b03c34e13c44893ab6e8fea18ee8d2a6878c15328dd3aabedbdd83ee9f2ed3", size = 5647688, upload-time = "2025-05-16T20:44:31.867Z" }, - { url = "https://files.pythonhosted.org/packages/2c/98/e6995f0fa579929da7795c961f403f4ee84af36c625963f52741d56f242c/hf_xet-1.1.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:01b18608955b3d826307d37da8bd38b28a46cd2d9908b3a3655d1363274f941a", size = 5322627, upload-time = "2025-05-16T20:44:33.677Z" }, - { url = "https://files.pythonhosted.org/packages/59/40/8f1d5a44a64d8bf9e3c19576e789f716af54875b46daae65426714e75db1/hf_xet-1.1.2-cp37-abi3-win_amd64.whl", hash = "sha256:3562902c81299b09f3582ddfb324400c6a901a2f3bc854f83556495755f4954c", size = 2739542, upload-time = "2025-05-16T20:44:36.287Z" }, + { url = "https://files.pythonhosted.org/packages/b1/7c/8d7803995caf14e7d19a392a486a040f923e2cfeff824e9b800b92072f76/hf_xet-1.1.7-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:60dae4b44d520819e54e216a2505685248ec0adbdb2dd4848b17aa85a0375cde", size = 2761743 }, + { url = "https://files.pythonhosted.org/packages/51/a3/fa5897099454aa287022a34a30e68dbff0e617760f774f8bd1db17f06bd4/hf_xet-1.1.7-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:b109f4c11e01c057fc82004c9e51e6cdfe2cb230637644ade40c599739067b2e", size = 2624331 }, + { url = "https://files.pythonhosted.org/packages/86/50/2446a132267e60b8a48b2e5835d6e24fd988000d0f5b9b15ebd6d64ef769/hf_xet-1.1.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6efaaf1a5a9fc3a501d3e71e88a6bfebc69ee3a716d0e713a931c8b8d920038f", size = 3183844 }, + { url = "https://files.pythonhosted.org/packages/20/8f/ccc670616bb9beee867c6bb7139f7eab2b1370fe426503c25f5cbb27b148/hf_xet-1.1.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:751571540f9c1fbad9afcf222a5fb96daf2384bf821317b8bfb0c59d86078513", size = 3074209 }, + { url = "https://files.pythonhosted.org/packages/21/0a/4c30e1eb77205565b854f5e4a82cf1f056214e4dc87f2918ebf83d47ae14/hf_xet-1.1.7-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:18b61bbae92d56ae731b92087c44efcac216071182c603fc535f8e29ec4b09b8", size = 3239602 }, + { url = "https://files.pythonhosted.org/packages/f5/1e/fc7e9baf14152662ef0b35fa52a6e889f770a7ed14ac239de3c829ecb47e/hf_xet-1.1.7-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:713f2bff61b252f8523739969f247aa354ad8e6d869b8281e174e2ea1bb8d604", size = 3348184 }, + { url = "https://files.pythonhosted.org/packages/a3/73/e354eae84ceff117ec3560141224724794828927fcc013c5b449bf0b8745/hf_xet-1.1.7-cp37-abi3-win_amd64.whl", hash = "sha256:2e356da7d284479ae0f1dea3cf5a2f74fdf925d6dca84ac4341930d892c7cb34", size = 2820008 }, ] [[package]] @@ -842,45 +953,45 @@ dependencies = [ { name = "certifi" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/18/56/78a38490b834fa0942cbe6d39bd8a7fd76316e8940319305a98d2b320366/httpcore-1.0.2.tar.gz", hash = "sha256:9fc092e4799b26174648e54b74ed5f683132a464e95643b226e00c2ed2fa6535", size = 81036, upload-time = "2023-11-10T13:37:42.496Z" } +sdist = { url = "https://files.pythonhosted.org/packages/18/56/78a38490b834fa0942cbe6d39bd8a7fd76316e8940319305a98d2b320366/httpcore-1.0.2.tar.gz", hash = "sha256:9fc092e4799b26174648e54b74ed5f683132a464e95643b226e00c2ed2fa6535", size = 81036 } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/ba/78b0a99c4da0ff8b0f59defa2f13ca4668189b134bd9840b6202a93d9a0f/httpcore-1.0.2-py3-none-any.whl", hash = "sha256:096cc05bca73b8e459a1fc3dcf585148f63e534eae4339559c9b8a8d6399acc7", size = 76943, upload-time = "2023-11-10T13:37:40.937Z" }, + { url = "https://files.pythonhosted.org/packages/56/ba/78b0a99c4da0ff8b0f59defa2f13ca4668189b134bd9840b6202a93d9a0f/httpcore-1.0.2-py3-none-any.whl", hash = "sha256:096cc05bca73b8e459a1fc3dcf585148f63e534eae4339559c9b8a8d6399acc7", size = 76943 }, ] [[package]] name = "httptools" version = "0.6.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a7/9a/ce5e1f7e131522e6d3426e8e7a490b3a01f39a6696602e1c4f33f9e94277/httptools-0.6.4.tar.gz", hash = "sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c", size = 240639, upload-time = "2024-10-16T19:45:08.902Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/9a/ce5e1f7e131522e6d3426e8e7a490b3a01f39a6696602e1c4f33f9e94277/httptools-0.6.4.tar.gz", hash = "sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c", size = 240639 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/6f/972f8eb0ea7d98a1c6be436e2142d51ad2a64ee18e02b0e7ff1f62171ab1/httptools-0.6.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3c73ce323711a6ffb0d247dcd5a550b8babf0f757e86a52558fe5b86d6fefcc0", size = 198780, upload-time = "2024-10-16T19:44:06.882Z" }, - { url = "https://files.pythonhosted.org/packages/6a/b0/17c672b4bc5c7ba7f201eada4e96c71d0a59fbc185e60e42580093a86f21/httptools-0.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:345c288418f0944a6fe67be8e6afa9262b18c7626c3ef3c28adc5eabc06a68da", size = 103297, upload-time = "2024-10-16T19:44:08.129Z" }, - { url = "https://files.pythonhosted.org/packages/92/5e/b4a826fe91971a0b68e8c2bd4e7db3e7519882f5a8ccdb1194be2b3ab98f/httptools-0.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deee0e3343f98ee8047e9f4c5bc7cedbf69f5734454a94c38ee829fb2d5fa3c1", size = 443130, upload-time = "2024-10-16T19:44:09.45Z" }, - { url = "https://files.pythonhosted.org/packages/b0/51/ce61e531e40289a681a463e1258fa1e05e0be54540e40d91d065a264cd8f/httptools-0.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca80b7485c76f768a3bc83ea58373f8db7b015551117375e4918e2aa77ea9b50", size = 442148, upload-time = "2024-10-16T19:44:11.539Z" }, - { url = "https://files.pythonhosted.org/packages/ea/9e/270b7d767849b0c96f275c695d27ca76c30671f8eb8cc1bab6ced5c5e1d0/httptools-0.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:90d96a385fa941283ebd231464045187a31ad932ebfa541be8edf5b3c2328959", size = 415949, upload-time = "2024-10-16T19:44:13.388Z" }, - { url = "https://files.pythonhosted.org/packages/81/86/ced96e3179c48c6f656354e106934e65c8963d48b69be78f355797f0e1b3/httptools-0.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:59e724f8b332319e2875efd360e61ac07f33b492889284a3e05e6d13746876f4", size = 417591, upload-time = "2024-10-16T19:44:15.258Z" }, - { url = "https://files.pythonhosted.org/packages/75/73/187a3f620ed3175364ddb56847d7a608a6fc42d551e133197098c0143eca/httptools-0.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:c26f313951f6e26147833fc923f78f95604bbec812a43e5ee37f26dc9e5a686c", size = 88344, upload-time = "2024-10-16T19:44:16.54Z" }, - { url = "https://files.pythonhosted.org/packages/7b/26/bb526d4d14c2774fe07113ca1db7255737ffbb119315839af2065abfdac3/httptools-0.6.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f47f8ed67cc0ff862b84a1189831d1d33c963fb3ce1ee0c65d3b0cbe7b711069", size = 199029, upload-time = "2024-10-16T19:44:18.427Z" }, - { url = "https://files.pythonhosted.org/packages/a6/17/3e0d3e9b901c732987a45f4f94d4e2c62b89a041d93db89eafb262afd8d5/httptools-0.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0614154d5454c21b6410fdf5262b4a3ddb0f53f1e1721cfd59d55f32138c578a", size = 103492, upload-time = "2024-10-16T19:44:19.515Z" }, - { url = "https://files.pythonhosted.org/packages/b7/24/0fe235d7b69c42423c7698d086d4db96475f9b50b6ad26a718ef27a0bce6/httptools-0.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8787367fbdfccae38e35abf7641dafc5310310a5987b689f4c32cc8cc3ee975", size = 462891, upload-time = "2024-10-16T19:44:21.067Z" }, - { url = "https://files.pythonhosted.org/packages/b1/2f/205d1f2a190b72da6ffb5f41a3736c26d6fa7871101212b15e9b5cd8f61d/httptools-0.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40b0f7fe4fd38e6a507bdb751db0379df1e99120c65fbdc8ee6c1d044897a636", size = 459788, upload-time = "2024-10-16T19:44:22.958Z" }, - { url = "https://files.pythonhosted.org/packages/6e/4c/d09ce0eff09057a206a74575ae8f1e1e2f0364d20e2442224f9e6612c8b9/httptools-0.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40a5ec98d3f49904b9fe36827dcf1aadfef3b89e2bd05b0e35e94f97c2b14721", size = 433214, upload-time = "2024-10-16T19:44:24.513Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d2/84c9e23edbccc4a4c6f96a1b8d99dfd2350289e94f00e9ccc7aadde26fb5/httptools-0.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dacdd3d10ea1b4ca9df97a0a303cbacafc04b5cd375fa98732678151643d4988", size = 434120, upload-time = "2024-10-16T19:44:26.295Z" }, - { url = "https://files.pythonhosted.org/packages/d0/46/4d8e7ba9581416de1c425b8264e2cadd201eb709ec1584c381f3e98f51c1/httptools-0.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:288cd628406cc53f9a541cfaf06041b4c71d751856bab45e3702191f931ccd17", size = 88565, upload-time = "2024-10-16T19:44:29.188Z" }, - { url = "https://files.pythonhosted.org/packages/bb/0e/d0b71465c66b9185f90a091ab36389a7352985fe857e352801c39d6127c8/httptools-0.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:df017d6c780287d5c80601dafa31f17bddb170232d85c066604d8558683711a2", size = 200683, upload-time = "2024-10-16T19:44:30.175Z" }, - { url = "https://files.pythonhosted.org/packages/e2/b8/412a9bb28d0a8988de3296e01efa0bd62068b33856cdda47fe1b5e890954/httptools-0.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:85071a1e8c2d051b507161f6c3e26155b5c790e4e28d7f236422dbacc2a9cc44", size = 104337, upload-time = "2024-10-16T19:44:31.786Z" }, - { url = "https://files.pythonhosted.org/packages/9b/01/6fb20be3196ffdc8eeec4e653bc2a275eca7f36634c86302242c4fbb2760/httptools-0.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69422b7f458c5af875922cdb5bd586cc1f1033295aa9ff63ee196a87519ac8e1", size = 508796, upload-time = "2024-10-16T19:44:32.825Z" }, - { url = "https://files.pythonhosted.org/packages/f7/d8/b644c44acc1368938317d76ac991c9bba1166311880bcc0ac297cb9d6bd7/httptools-0.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e603a3bff50db08cd578d54f07032ca1631450ceb972c2f834c2b860c28ea2", size = 510837, upload-time = "2024-10-16T19:44:33.974Z" }, - { url = "https://files.pythonhosted.org/packages/52/d8/254d16a31d543073a0e57f1c329ca7378d8924e7e292eda72d0064987486/httptools-0.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec4f178901fa1834d4a060320d2f3abc5c9e39766953d038f1458cb885f47e81", size = 485289, upload-time = "2024-10-16T19:44:35.111Z" }, - { url = "https://files.pythonhosted.org/packages/5f/3c/4aee161b4b7a971660b8be71a92c24d6c64372c1ab3ae7f366b3680df20f/httptools-0.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9eb89ecf8b290f2e293325c646a211ff1c2493222798bb80a530c5e7502494f", size = 489779, upload-time = "2024-10-16T19:44:36.253Z" }, - { url = "https://files.pythonhosted.org/packages/12/b7/5cae71a8868e555f3f67a50ee7f673ce36eac970f029c0c5e9d584352961/httptools-0.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:db78cb9ca56b59b016e64b6031eda5653be0589dba2b1b43453f6e8b405a0970", size = 88634, upload-time = "2024-10-16T19:44:37.357Z" }, - { url = "https://files.pythonhosted.org/packages/94/a3/9fe9ad23fd35f7de6b91eeb60848986058bd8b5a5c1e256f5860a160cc3e/httptools-0.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ade273d7e767d5fae13fa637f4d53b6e961fb7fd93c7797562663f0171c26660", size = 197214, upload-time = "2024-10-16T19:44:38.738Z" }, - { url = "https://files.pythonhosted.org/packages/ea/d9/82d5e68bab783b632023f2fa31db20bebb4e89dfc4d2293945fd68484ee4/httptools-0.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:856f4bc0478ae143bad54a4242fccb1f3f86a6e1be5548fecfd4102061b3a083", size = 102431, upload-time = "2024-10-16T19:44:39.818Z" }, - { url = "https://files.pythonhosted.org/packages/96/c1/cb499655cbdbfb57b577734fde02f6fa0bbc3fe9fb4d87b742b512908dff/httptools-0.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:322d20ea9cdd1fa98bd6a74b77e2ec5b818abdc3d36695ab402a0de8ef2865a3", size = 473121, upload-time = "2024-10-16T19:44:41.189Z" }, - { url = "https://files.pythonhosted.org/packages/af/71/ee32fd358f8a3bb199b03261f10921716990808a675d8160b5383487a317/httptools-0.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d87b29bd4486c0093fc64dea80231f7c7f7eb4dc70ae394d70a495ab8436071", size = 473805, upload-time = "2024-10-16T19:44:42.384Z" }, - { url = "https://files.pythonhosted.org/packages/8a/0a/0d4df132bfca1507114198b766f1737d57580c9ad1cf93c1ff673e3387be/httptools-0.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:342dd6946aa6bda4b8f18c734576106b8a31f2fe31492881a9a160ec84ff4bd5", size = 448858, upload-time = "2024-10-16T19:44:43.959Z" }, - { url = "https://files.pythonhosted.org/packages/1e/6a/787004fdef2cabea27bad1073bf6a33f2437b4dbd3b6fb4a9d71172b1c7c/httptools-0.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b36913ba52008249223042dca46e69967985fb4051951f94357ea681e1f5dc0", size = 452042, upload-time = "2024-10-16T19:44:45.071Z" }, - { url = "https://files.pythonhosted.org/packages/4d/dc/7decab5c404d1d2cdc1bb330b1bf70e83d6af0396fd4fc76fc60c0d522bf/httptools-0.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:28908df1b9bb8187393d5b5db91435ccc9c8e891657f9cbb42a2541b44c82fc8", size = 87682, upload-time = "2024-10-16T19:44:46.46Z" }, + { url = "https://files.pythonhosted.org/packages/3b/6f/972f8eb0ea7d98a1c6be436e2142d51ad2a64ee18e02b0e7ff1f62171ab1/httptools-0.6.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3c73ce323711a6ffb0d247dcd5a550b8babf0f757e86a52558fe5b86d6fefcc0", size = 198780 }, + { url = "https://files.pythonhosted.org/packages/6a/b0/17c672b4bc5c7ba7f201eada4e96c71d0a59fbc185e60e42580093a86f21/httptools-0.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:345c288418f0944a6fe67be8e6afa9262b18c7626c3ef3c28adc5eabc06a68da", size = 103297 }, + { url = "https://files.pythonhosted.org/packages/92/5e/b4a826fe91971a0b68e8c2bd4e7db3e7519882f5a8ccdb1194be2b3ab98f/httptools-0.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deee0e3343f98ee8047e9f4c5bc7cedbf69f5734454a94c38ee829fb2d5fa3c1", size = 443130 }, + { url = "https://files.pythonhosted.org/packages/b0/51/ce61e531e40289a681a463e1258fa1e05e0be54540e40d91d065a264cd8f/httptools-0.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca80b7485c76f768a3bc83ea58373f8db7b015551117375e4918e2aa77ea9b50", size = 442148 }, + { url = "https://files.pythonhosted.org/packages/ea/9e/270b7d767849b0c96f275c695d27ca76c30671f8eb8cc1bab6ced5c5e1d0/httptools-0.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:90d96a385fa941283ebd231464045187a31ad932ebfa541be8edf5b3c2328959", size = 415949 }, + { url = "https://files.pythonhosted.org/packages/81/86/ced96e3179c48c6f656354e106934e65c8963d48b69be78f355797f0e1b3/httptools-0.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:59e724f8b332319e2875efd360e61ac07f33b492889284a3e05e6d13746876f4", size = 417591 }, + { url = "https://files.pythonhosted.org/packages/75/73/187a3f620ed3175364ddb56847d7a608a6fc42d551e133197098c0143eca/httptools-0.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:c26f313951f6e26147833fc923f78f95604bbec812a43e5ee37f26dc9e5a686c", size = 88344 }, + { url = "https://files.pythonhosted.org/packages/7b/26/bb526d4d14c2774fe07113ca1db7255737ffbb119315839af2065abfdac3/httptools-0.6.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f47f8ed67cc0ff862b84a1189831d1d33c963fb3ce1ee0c65d3b0cbe7b711069", size = 199029 }, + { url = "https://files.pythonhosted.org/packages/a6/17/3e0d3e9b901c732987a45f4f94d4e2c62b89a041d93db89eafb262afd8d5/httptools-0.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0614154d5454c21b6410fdf5262b4a3ddb0f53f1e1721cfd59d55f32138c578a", size = 103492 }, + { url = "https://files.pythonhosted.org/packages/b7/24/0fe235d7b69c42423c7698d086d4db96475f9b50b6ad26a718ef27a0bce6/httptools-0.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8787367fbdfccae38e35abf7641dafc5310310a5987b689f4c32cc8cc3ee975", size = 462891 }, + { url = "https://files.pythonhosted.org/packages/b1/2f/205d1f2a190b72da6ffb5f41a3736c26d6fa7871101212b15e9b5cd8f61d/httptools-0.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40b0f7fe4fd38e6a507bdb751db0379df1e99120c65fbdc8ee6c1d044897a636", size = 459788 }, + { url = "https://files.pythonhosted.org/packages/6e/4c/d09ce0eff09057a206a74575ae8f1e1e2f0364d20e2442224f9e6612c8b9/httptools-0.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40a5ec98d3f49904b9fe36827dcf1aadfef3b89e2bd05b0e35e94f97c2b14721", size = 433214 }, + { url = "https://files.pythonhosted.org/packages/3e/d2/84c9e23edbccc4a4c6f96a1b8d99dfd2350289e94f00e9ccc7aadde26fb5/httptools-0.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dacdd3d10ea1b4ca9df97a0a303cbacafc04b5cd375fa98732678151643d4988", size = 434120 }, + { url = "https://files.pythonhosted.org/packages/d0/46/4d8e7ba9581416de1c425b8264e2cadd201eb709ec1584c381f3e98f51c1/httptools-0.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:288cd628406cc53f9a541cfaf06041b4c71d751856bab45e3702191f931ccd17", size = 88565 }, + { url = "https://files.pythonhosted.org/packages/bb/0e/d0b71465c66b9185f90a091ab36389a7352985fe857e352801c39d6127c8/httptools-0.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:df017d6c780287d5c80601dafa31f17bddb170232d85c066604d8558683711a2", size = 200683 }, + { url = "https://files.pythonhosted.org/packages/e2/b8/412a9bb28d0a8988de3296e01efa0bd62068b33856cdda47fe1b5e890954/httptools-0.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:85071a1e8c2d051b507161f6c3e26155b5c790e4e28d7f236422dbacc2a9cc44", size = 104337 }, + { url = "https://files.pythonhosted.org/packages/9b/01/6fb20be3196ffdc8eeec4e653bc2a275eca7f36634c86302242c4fbb2760/httptools-0.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69422b7f458c5af875922cdb5bd586cc1f1033295aa9ff63ee196a87519ac8e1", size = 508796 }, + { url = "https://files.pythonhosted.org/packages/f7/d8/b644c44acc1368938317d76ac991c9bba1166311880bcc0ac297cb9d6bd7/httptools-0.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e603a3bff50db08cd578d54f07032ca1631450ceb972c2f834c2b860c28ea2", size = 510837 }, + { url = "https://files.pythonhosted.org/packages/52/d8/254d16a31d543073a0e57f1c329ca7378d8924e7e292eda72d0064987486/httptools-0.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec4f178901fa1834d4a060320d2f3abc5c9e39766953d038f1458cb885f47e81", size = 485289 }, + { url = "https://files.pythonhosted.org/packages/5f/3c/4aee161b4b7a971660b8be71a92c24d6c64372c1ab3ae7f366b3680df20f/httptools-0.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9eb89ecf8b290f2e293325c646a211ff1c2493222798bb80a530c5e7502494f", size = 489779 }, + { url = "https://files.pythonhosted.org/packages/12/b7/5cae71a8868e555f3f67a50ee7f673ce36eac970f029c0c5e9d584352961/httptools-0.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:db78cb9ca56b59b016e64b6031eda5653be0589dba2b1b43453f6e8b405a0970", size = 88634 }, + { url = "https://files.pythonhosted.org/packages/94/a3/9fe9ad23fd35f7de6b91eeb60848986058bd8b5a5c1e256f5860a160cc3e/httptools-0.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ade273d7e767d5fae13fa637f4d53b6e961fb7fd93c7797562663f0171c26660", size = 197214 }, + { url = "https://files.pythonhosted.org/packages/ea/d9/82d5e68bab783b632023f2fa31db20bebb4e89dfc4d2293945fd68484ee4/httptools-0.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:856f4bc0478ae143bad54a4242fccb1f3f86a6e1be5548fecfd4102061b3a083", size = 102431 }, + { url = "https://files.pythonhosted.org/packages/96/c1/cb499655cbdbfb57b577734fde02f6fa0bbc3fe9fb4d87b742b512908dff/httptools-0.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:322d20ea9cdd1fa98bd6a74b77e2ec5b818abdc3d36695ab402a0de8ef2865a3", size = 473121 }, + { url = "https://files.pythonhosted.org/packages/af/71/ee32fd358f8a3bb199b03261f10921716990808a675d8160b5383487a317/httptools-0.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d87b29bd4486c0093fc64dea80231f7c7f7eb4dc70ae394d70a495ab8436071", size = 473805 }, + { url = "https://files.pythonhosted.org/packages/8a/0a/0d4df132bfca1507114198b766f1737d57580c9ad1cf93c1ff673e3387be/httptools-0.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:342dd6946aa6bda4b8f18c734576106b8a31f2fe31492881a9a160ec84ff4bd5", size = 448858 }, + { url = "https://files.pythonhosted.org/packages/1e/6a/787004fdef2cabea27bad1073bf6a33f2437b4dbd3b6fb4a9d71172b1c7c/httptools-0.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b36913ba52008249223042dca46e69967985fb4051951f94357ea681e1f5dc0", size = 452042 }, + { url = "https://files.pythonhosted.org/packages/4d/dc/7decab5c404d1d2cdc1bb330b1bf70e83d6af0396fd4fc76fc60c0d522bf/httptools-0.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:28908df1b9bb8187393d5b5db91435ccc9c8e891657f9cbb42a2541b44c82fc8", size = 87682 }, ] [[package]] @@ -893,14 +1004,14 @@ dependencies = [ { name = "httpcore" }, { name = "idna" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, ] [[package]] name = "huggingface-hub" -version = "0.33.2" +version = "0.34.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, @@ -912,9 +1023,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fa/42/8a95c5632080ae312c0498744b2b852195e10b05a20b1be11c5141092f4c/huggingface_hub-0.33.2.tar.gz", hash = "sha256:84221defaec8fa09c090390cd68c78b88e3c4c2b7befba68d3dc5aacbc3c2c5f", size = 426637, upload-time = "2025-07-02T06:26:05.156Z" } +sdist = { url = "https://files.pythonhosted.org/packages/45/c9/bdbe19339f76d12985bc03572f330a01a93c04dffecaaea3061bdd7fb892/huggingface_hub-0.34.4.tar.gz", hash = "sha256:a4228daa6fb001be3f4f4bdaf9a0db00e1739235702848df00885c9b5742c85c", size = 459768 } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/f4/5f3f22e762ad1965f01122b42dae5bf0e009286e2dba601ce1d0dba72424/huggingface_hub-0.33.2-py3-none-any.whl", hash = "sha256:3749498bfa91e8cde2ddc2c1db92c79981f40e66434c20133b39e5928ac9bcc5", size = 515373, upload-time = "2025-07-02T06:26:03.072Z" }, + { url = "https://files.pythonhosted.org/packages/39/7b/bb06b061991107cd8783f300adff3e7b7f284e330fd82f507f2a1417b11d/huggingface_hub-0.34.4-py3-none-any.whl", hash = "sha256:9b365d781739c93ff90c359844221beef048403f1bc1f1c123c191257c3c890a", size = 561452 }, ] [[package]] @@ -924,18 +1035,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyreadline3", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702, upload-time = "2021-09-17T21:40:43.31Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794, upload-time = "2021-09-17T21:40:39.897Z" }, + { url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794 }, ] [[package]] name = "idna" version = "3.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", size = 175426, upload-time = "2023-11-25T15:40:54.902Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", size = 175426 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f", size = 61567, upload-time = "2023-11-25T15:40:52.604Z" }, + { url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f", size = 61567 }, ] [[package]] @@ -946,9 +1057,9 @@ dependencies = [ { name = "numpy" }, { name = "pillow" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/25/38/f4c568318c656352d211eec6954460dc3af0b7583a6682308f8a66e4c19b/imageio-2.33.1.tar.gz", hash = "sha256:78722d40b137bd98f5ec7312119f8aea9ad2049f76f434748eb306b6937cc1ce", size = 387374, upload-time = "2023-12-11T02:26:44.715Z" } +sdist = { url = "https://files.pythonhosted.org/packages/25/38/f4c568318c656352d211eec6954460dc3af0b7583a6682308f8a66e4c19b/imageio-2.33.1.tar.gz", hash = "sha256:78722d40b137bd98f5ec7312119f8aea9ad2049f76f434748eb306b6937cc1ce", size = 387374 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/69/3aaa69cb0748e33e644fda114c9abd3186ce369edd4fca11107e9f39c6a7/imageio-2.33.1-py3-none-any.whl", hash = "sha256:c5094c48ccf6b2e6da8b4061cd95e1209380afafcbeae4a4e280938cce227e1d", size = 313345, upload-time = "2023-12-11T02:26:42.724Z" }, + { url = "https://files.pythonhosted.org/packages/c0/69/3aaa69cb0748e33e644fda114c9abd3186ce369edd4fca11107e9f39c6a7/imageio-2.33.1-py3-none-any.whl", hash = "sha256:c5094c48ccf6b2e6da8b4061cd95e1209380afafcbeae4a4e280938cce227e1d", size = 313345 }, ] [[package]] @@ -962,6 +1073,7 @@ dependencies = [ { name = "gunicorn" }, { name = "huggingface-hub" }, { name = "insightface" }, + { name = "numpy" }, { name = "opencv-python-headless" }, { name = "orjson" }, { name = "pillow" }, @@ -1041,10 +1153,11 @@ requires-dist = [ { name = "gunicorn", specifier = ">=21.1.0" }, { name = "huggingface-hub", specifier = ">=0.20.1,<1.0" }, { name = "insightface", specifier = ">=0.7.3,<1.0" }, + { name = "numpy", specifier = "<2" }, { name = "onnxruntime", marker = "extra == 'armnn'", specifier = ">=1.15.0,<2" }, { name = "onnxruntime", marker = "extra == 'cpu'", specifier = ">=1.15.0,<2" }, { name = "onnxruntime", marker = "extra == 'rknn'", specifier = ">=1.15.0,<2" }, - { name = "onnxruntime-gpu", marker = "extra == 'cuda'", specifier = ">=1.17.0,<2", index = "https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/onnxruntime-cuda-12/pypi/simple" }, + { name = "onnxruntime-gpu", marker = "extra == 'cuda'", specifier = ">=1.17.0,<2", index = "https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/onnxruntime-cuda-12/pypi/simple/" }, { name = "onnxruntime-openvino", marker = "extra == 'openvino'", specifier = ">=1.17.1,<1.19.0" }, { name = "opencv-python-headless", specifier = ">=4.7.0.72,<5.0" }, { name = "orjson", specifier = ">=3.9.5" }, @@ -1105,9 +1218,9 @@ types = [ name = "iniconfig" version = "2.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646, upload-time = "2023-01-07T11:08:11.254Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892, upload-time = "2023-01-07T11:08:09.864Z" }, + { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, ] [[package]] @@ -1118,26 +1231,29 @@ dependencies = [ { name = "albumentations" }, { name = "cython" }, { name = "easydict" }, - { name = "matplotlib" }, + { name = "matplotlib", version = "3.8.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "matplotlib", version = "3.10.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "numpy" }, { name = "onnx" }, { name = "pillow" }, { name = "prettytable" }, { name = "requests" }, { name = "scikit-image" }, - { name = "scikit-learn" }, - { name = "scipy" }, + { name = "scikit-learn", version = "1.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scikit-learn", version = "1.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "scipy", version = "1.11.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0b/8d/0f4af90999ca96cf8cb846eb5ae27c5ef5b390f9c090dd19e4fa76364c13/insightface-0.7.3.tar.gz", hash = "sha256:f191f719612ebb37018f41936814500544cd0f86e6fcd676c023f354c668ddf7", size = 439490, upload-time = "2023-04-02T08:01:54.541Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/8d/0f4af90999ca96cf8cb846eb5ae27c5ef5b390f9c090dd19e4fa76364c13/insightface-0.7.3.tar.gz", hash = "sha256:f191f719612ebb37018f41936814500544cd0f86e6fcd676c023f354c668ddf7", size = 439490 } [[package]] name = "itsdangerous" version = "2.1.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7f/a1/d3fb83e7a61fa0c0d3d08ad0a94ddbeff3731c05212617dff3a94e097f08/itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a", size = 56143, upload-time = "2022-03-24T15:12:15.102Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7f/a1/d3fb83e7a61fa0c0d3d08ad0a94ddbeff3731c05212617dff3a94e097f08/itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a", size = 56143 } wheels = [ - { url = "https://files.pythonhosted.org/packages/68/5f/447e04e828f47465eeab35b5d408b7ebaaaee207f48b7136c5a7267a30ae/itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", size = 15749, upload-time = "2022-03-24T15:12:13.2Z" }, + { url = "https://files.pythonhosted.org/packages/68/5f/447e04e828f47465eeab35b5d408b7ebaaaee207f48b7136c5a7267a30ae/itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", size = 15749 }, ] [[package]] @@ -1147,85 +1263,85 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245, upload-time = "2024-05-05T23:42:02.455Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245 } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271, upload-time = "2024-05-05T23:41:59.928Z" }, + { url = "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271 }, ] [[package]] name = "joblib" version = "1.3.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/0f/d3b33b9f106dddef461f6df1872b7881321b247f3d255b87f61a7636f7fe/joblib-1.3.2.tar.gz", hash = "sha256:92f865e621e17784e7955080b6d042489e3b8e294949cc44c6eac304f59772b1", size = 1987720, upload-time = "2023-08-09T09:23:40.503Z" } +sdist = { url = "https://files.pythonhosted.org/packages/15/0f/d3b33b9f106dddef461f6df1872b7881321b247f3d255b87f61a7636f7fe/joblib-1.3.2.tar.gz", hash = "sha256:92f865e621e17784e7955080b6d042489e3b8e294949cc44c6eac304f59772b1", size = 1987720 } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/40/d551139c85db202f1f384ba8bcf96aca2f329440a844f924c8a0040b6d02/joblib-1.3.2-py3-none-any.whl", hash = "sha256:ef4331c65f239985f3f2220ecc87db222f08fd22097a3dd5698f693875f8cbb9", size = 302207, upload-time = "2023-08-09T09:23:34.583Z" }, + { url = "https://files.pythonhosted.org/packages/10/40/d551139c85db202f1f384ba8bcf96aca2f329440a844f924c8a0040b6d02/joblib-1.3.2-py3-none-any.whl", hash = "sha256:ef4331c65f239985f3f2220ecc87db222f08fd22097a3dd5698f693875f8cbb9", size = 302207 }, ] [[package]] name = "kiwisolver" version = "1.4.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b9/2d/226779e405724344fc678fcc025b812587617ea1a48b9442628b688e85ea/kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec", size = 97552, upload-time = "2023-08-24T09:30:39.861Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/2d/226779e405724344fc678fcc025b812587617ea1a48b9442628b688e85ea/kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec", size = 97552 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/56/cb02dcefdaab40df636b91e703b172966b444605a0ea313549f3ffc05bd3/kiwisolver-1.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af", size = 127397, upload-time = "2023-08-24T09:28:18.105Z" }, - { url = "https://files.pythonhosted.org/packages/0e/c1/d084f8edb26533a191415d5173157080837341f9a06af9dd1a75f727abb4/kiwisolver-1.4.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3", size = 68125, upload-time = "2023-08-24T09:28:19.218Z" }, - { url = "https://files.pythonhosted.org/packages/23/11/6fb190bae4b279d712a834e7b1da89f6dcff6791132f7399aa28a57c3565/kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4", size = 66211, upload-time = "2023-08-24T09:28:20.241Z" }, - { url = "https://files.pythonhosted.org/packages/b3/13/5e9e52feb33e9e063f76b2c5eb09cb977f5bba622df3210081bfb26ec9a3/kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1", size = 1637145, upload-time = "2023-08-24T09:28:21.439Z" }, - { url = "https://files.pythonhosted.org/packages/6f/40/4ab1fdb57fced80ce5903f04ae1aed7c1d5939dda4fd0c0aa526c12fe28a/kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff", size = 1617849, upload-time = "2023-08-24T09:28:23.004Z" }, - { url = "https://files.pythonhosted.org/packages/49/ca/61ef43bd0832c7253b370735b0c38972c140c8774889b884372a629a8189/kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a", size = 1400921, upload-time = "2023-08-24T09:28:24.331Z" }, - { url = "https://files.pythonhosted.org/packages/68/6f/854f6a845c00b4257482468e08d8bc386f4929ee499206142378ba234419/kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa", size = 1513009, upload-time = "2023-08-24T09:28:25.636Z" }, - { url = "https://files.pythonhosted.org/packages/50/65/76f303377167d12eb7a9b423d6771b39fe5c4373e4a42f075805b1f581ae/kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c", size = 1444819, upload-time = "2023-08-24T09:28:27.547Z" }, - { url = "https://files.pythonhosted.org/packages/7e/ee/98cdf9dde129551467138b6e18cc1cc901e75ecc7ffb898c6f49609f33b1/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b", size = 1817054, upload-time = "2023-08-24T09:28:28.839Z" }, - { url = "https://files.pythonhosted.org/packages/e6/5b/ab569016ec4abc7b496f6cb8a3ab511372c99feb6a23d948cda97e0db6da/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770", size = 1918613, upload-time = "2023-08-24T09:28:30.351Z" }, - { url = "https://files.pythonhosted.org/packages/93/ac/39b9f99d2474b1ac7af1ddfe5756ddf9b6a8f24c5f3a32cd4c010317fc6b/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0", size = 1872650, upload-time = "2023-08-24T09:28:32.303Z" }, - { url = "https://files.pythonhosted.org/packages/40/5b/be568548266516b114d1776120281ea9236c732fb6032a1f8f3b1e5e921c/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525", size = 1827415, upload-time = "2023-08-24T09:28:34.141Z" }, - { url = "https://files.pythonhosted.org/packages/d4/80/c0c13d2a17a12937a19ef378bf35e94399fd171ed6ec05bcee0f038e1eaf/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b", size = 1838094, upload-time = "2023-08-24T09:28:35.97Z" }, - { url = "https://files.pythonhosted.org/packages/70/d1/5ab93ee00ca5af708929cc12fbe665b6f1ed4ad58088e70dc00e87e0d107/kiwisolver-1.4.5-cp310-cp310-win32.whl", hash = "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238", size = 46585, upload-time = "2023-08-24T09:28:37.326Z" }, - { url = "https://files.pythonhosted.org/packages/4a/a1/8a9c9be45c642fa12954855d8b3a02d9fd8551165a558835a19508fec2e6/kiwisolver-1.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276", size = 56095, upload-time = "2023-08-24T09:28:38.325Z" }, - { url = "https://files.pythonhosted.org/packages/2a/eb/9e099ad7c47c279995d2d20474e1821100a5f10f847739bd65b1c1f02442/kiwisolver-1.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5", size = 127403, upload-time = "2023-08-24T09:28:39.3Z" }, - { url = "https://files.pythonhosted.org/packages/a6/94/695922e71288855fc7cace3bdb52edda9d7e50edba77abb0c9d7abb51e96/kiwisolver-1.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90", size = 68156, upload-time = "2023-08-24T09:28:40.301Z" }, - { url = "https://files.pythonhosted.org/packages/4a/fe/23d7fa78f7c66086d196406beb1fb2eaf629dd7adc01c3453033303d17fa/kiwisolver-1.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797", size = 66166, upload-time = "2023-08-24T09:28:41.235Z" }, - { url = "https://files.pythonhosted.org/packages/f1/68/f472bf16c9141bb1bea5c0b8c66c68fc1ccb048efdbd8f0872b92125724e/kiwisolver-1.4.5-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9", size = 1334300, upload-time = "2023-08-24T09:28:42.409Z" }, - { url = "https://files.pythonhosted.org/packages/8d/26/b4569d1f29751fca22ee915b4ebfef5974f4ef239b3335fc072882bd62d9/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437", size = 1426579, upload-time = "2023-08-24T09:28:43.677Z" }, - { url = "https://files.pythonhosted.org/packages/f3/a3/804fc7c8bf233806ec0321c9da35971578620f2ab4fafe67d76100b3ce52/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9", size = 1541360, upload-time = "2023-08-24T09:28:45.939Z" }, - { url = "https://files.pythonhosted.org/packages/07/ef/286e1d26524854f6fbd6540e8364d67a8857d61038ac743e11edc42fe217/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da", size = 1470091, upload-time = "2023-08-24T09:28:47.959Z" }, - { url = "https://files.pythonhosted.org/packages/17/ba/17a706b232308e65f57deeccae503c268292e6a091313f6ce833a23093ea/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e", size = 1426259, upload-time = "2023-08-24T09:28:49.224Z" }, - { url = "https://files.pythonhosted.org/packages/d0/f3/a0925611c9d6c2f37c5935a39203cadec6883aa914e013b46c84c4c2e641/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8", size = 1847516, upload-time = "2023-08-24T09:28:50.979Z" }, - { url = "https://files.pythonhosted.org/packages/da/85/82d59bb8f7c4c9bb2785138b72462cb1b161668f8230c58bbb28c0403cd5/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d", size = 1946228, upload-time = "2023-08-24T09:28:52.812Z" }, - { url = "https://files.pythonhosted.org/packages/34/3c/6a37f444c0233993881e5db3a6a1775925d4d9d2f2609bb325bb1348ed94/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0", size = 1901716, upload-time = "2023-08-24T09:28:54.115Z" }, - { url = "https://files.pythonhosted.org/packages/cd/7e/180425790efc00adfd47db14e1e341cb4826516982334129012b971121a6/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f", size = 1852871, upload-time = "2023-08-24T09:28:55.433Z" }, - { url = "https://files.pythonhosted.org/packages/1b/9a/13c68b2edb1fa74321e60893a9a5829788e135138e68060cf44e2d92d2c3/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f", size = 1870265, upload-time = "2023-08-24T09:28:56.855Z" }, - { url = "https://files.pythonhosted.org/packages/9f/0a/fa56a0fdee5da2b4c79899c0f6bd1aefb29d9438c2d66430e78793571c6b/kiwisolver-1.4.5-cp311-cp311-win32.whl", hash = "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac", size = 46649, upload-time = "2023-08-24T09:28:58.021Z" }, - { url = "https://files.pythonhosted.org/packages/1e/37/d3c2d4ba2719059a0f12730947bbe1ad5ee8bff89e8c35319dcb2c9ddb4c/kiwisolver-1.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355", size = 56116, upload-time = "2023-08-24T09:28:58.994Z" }, - { url = "https://files.pythonhosted.org/packages/f3/7a/debbce859be1a2711eb8437818107137192007b88d17b5cfdb556f457b42/kiwisolver-1.4.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a", size = 125484, upload-time = "2023-08-24T09:28:59.975Z" }, - { url = "https://files.pythonhosted.org/packages/2d/e0/bf8df75ba93b9e035cc6757dd5dcaf63084fdc1c846ae134e818bd7e0f03/kiwisolver-1.4.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192", size = 67332, upload-time = "2023-08-24T09:29:01.733Z" }, - { url = "https://files.pythonhosted.org/packages/26/61/58bb691f6880588be3a4801d199bd776032ece07203faf3e4a8b377f7d9b/kiwisolver-1.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45", size = 64987, upload-time = "2023-08-24T09:29:02.789Z" }, - { url = "https://files.pythonhosted.org/packages/8e/a3/96ac5413068b237c006f54dd8d70114e8756d70e3da7613c5aef20627e22/kiwisolver-1.4.5-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7", size = 1370613, upload-time = "2023-08-24T09:29:03.912Z" }, - { url = "https://files.pythonhosted.org/packages/4d/12/f48539e6e17068b59c7f12f4d6214b973431b8e3ac83af525cafd27cebec/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db", size = 1463183, upload-time = "2023-08-24T09:29:05.244Z" }, - { url = "https://files.pythonhosted.org/packages/f3/70/26c99be8eb034cc8e3f62e0760af1fbdc97a842a7cbc252f7978507d41c2/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff", size = 1581248, upload-time = "2023-08-24T09:29:06.531Z" }, - { url = "https://files.pythonhosted.org/packages/17/f6/f75f20e543639b09b2de7fc864274a5a9b96cda167a6210a1d9d19306b9d/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228", size = 1508815, upload-time = "2023-08-24T09:29:07.867Z" }, - { url = "https://files.pythonhosted.org/packages/e3/d5/bc0f22ac108743062ab703f8d6d71c9c7b077b8839fa358700bfb81770b8/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16", size = 1466042, upload-time = "2023-08-24T09:29:09.403Z" }, - { url = "https://files.pythonhosted.org/packages/75/18/98142500f21d6838bcab49ec919414a1f0c6d049d21ddadf139124db6a70/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9", size = 1885159, upload-time = "2023-08-24T09:29:10.66Z" }, - { url = "https://files.pythonhosted.org/packages/21/49/a241eff9e0ee013368c1d17957f9d345b0957493c3a43d82ebb558c90b0a/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162", size = 1981694, upload-time = "2023-08-24T09:29:12.469Z" }, - { url = "https://files.pythonhosted.org/packages/90/90/9490c3de4788123041b1d600d64434f1eed809a2ce9f688075a22166b289/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4", size = 1941579, upload-time = "2023-08-24T09:29:13.743Z" }, - { url = "https://files.pythonhosted.org/packages/b7/bb/a0cc488ef2aa92d7d304318c8549d3ec8dfe6dd3c2c67a44e3922b77bc4f/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3", size = 1888168, upload-time = "2023-08-24T09:29:15.097Z" }, - { url = "https://files.pythonhosted.org/packages/4f/e9/9c0de8e45fef3d63f85eed3b1757f9aa511065942866331ef8b99421f433/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a", size = 1908464, upload-time = "2023-08-24T09:29:16.539Z" }, - { url = "https://files.pythonhosted.org/packages/a3/60/4f0fd50b08f5be536ea0cef518ac7255d9dab43ca40f3b93b60e3ddf80dd/kiwisolver-1.4.5-cp312-cp312-win32.whl", hash = "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20", size = 46473, upload-time = "2023-08-24T09:29:17.956Z" }, - { url = "https://files.pythonhosted.org/packages/63/50/2746566bdf4a6a842d117367d05c90cfb87ac04e9e2845aa1fa21f071362/kiwisolver-1.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9", size = 56004, upload-time = "2023-08-24T09:29:19.329Z" }, + { url = "https://files.pythonhosted.org/packages/f1/56/cb02dcefdaab40df636b91e703b172966b444605a0ea313549f3ffc05bd3/kiwisolver-1.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af", size = 127397 }, + { url = "https://files.pythonhosted.org/packages/0e/c1/d084f8edb26533a191415d5173157080837341f9a06af9dd1a75f727abb4/kiwisolver-1.4.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3", size = 68125 }, + { url = "https://files.pythonhosted.org/packages/23/11/6fb190bae4b279d712a834e7b1da89f6dcff6791132f7399aa28a57c3565/kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4", size = 66211 }, + { url = "https://files.pythonhosted.org/packages/b3/13/5e9e52feb33e9e063f76b2c5eb09cb977f5bba622df3210081bfb26ec9a3/kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1", size = 1637145 }, + { url = "https://files.pythonhosted.org/packages/6f/40/4ab1fdb57fced80ce5903f04ae1aed7c1d5939dda4fd0c0aa526c12fe28a/kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff", size = 1617849 }, + { url = "https://files.pythonhosted.org/packages/49/ca/61ef43bd0832c7253b370735b0c38972c140c8774889b884372a629a8189/kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a", size = 1400921 }, + { url = "https://files.pythonhosted.org/packages/68/6f/854f6a845c00b4257482468e08d8bc386f4929ee499206142378ba234419/kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa", size = 1513009 }, + { url = "https://files.pythonhosted.org/packages/50/65/76f303377167d12eb7a9b423d6771b39fe5c4373e4a42f075805b1f581ae/kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c", size = 1444819 }, + { url = "https://files.pythonhosted.org/packages/7e/ee/98cdf9dde129551467138b6e18cc1cc901e75ecc7ffb898c6f49609f33b1/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b", size = 1817054 }, + { url = "https://files.pythonhosted.org/packages/e6/5b/ab569016ec4abc7b496f6cb8a3ab511372c99feb6a23d948cda97e0db6da/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770", size = 1918613 }, + { url = "https://files.pythonhosted.org/packages/93/ac/39b9f99d2474b1ac7af1ddfe5756ddf9b6a8f24c5f3a32cd4c010317fc6b/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0", size = 1872650 }, + { url = "https://files.pythonhosted.org/packages/40/5b/be568548266516b114d1776120281ea9236c732fb6032a1f8f3b1e5e921c/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525", size = 1827415 }, + { url = "https://files.pythonhosted.org/packages/d4/80/c0c13d2a17a12937a19ef378bf35e94399fd171ed6ec05bcee0f038e1eaf/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b", size = 1838094 }, + { url = "https://files.pythonhosted.org/packages/70/d1/5ab93ee00ca5af708929cc12fbe665b6f1ed4ad58088e70dc00e87e0d107/kiwisolver-1.4.5-cp310-cp310-win32.whl", hash = "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238", size = 46585 }, + { url = "https://files.pythonhosted.org/packages/4a/a1/8a9c9be45c642fa12954855d8b3a02d9fd8551165a558835a19508fec2e6/kiwisolver-1.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276", size = 56095 }, + { url = "https://files.pythonhosted.org/packages/2a/eb/9e099ad7c47c279995d2d20474e1821100a5f10f847739bd65b1c1f02442/kiwisolver-1.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5", size = 127403 }, + { url = "https://files.pythonhosted.org/packages/a6/94/695922e71288855fc7cace3bdb52edda9d7e50edba77abb0c9d7abb51e96/kiwisolver-1.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90", size = 68156 }, + { url = "https://files.pythonhosted.org/packages/4a/fe/23d7fa78f7c66086d196406beb1fb2eaf629dd7adc01c3453033303d17fa/kiwisolver-1.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797", size = 66166 }, + { url = "https://files.pythonhosted.org/packages/f1/68/f472bf16c9141bb1bea5c0b8c66c68fc1ccb048efdbd8f0872b92125724e/kiwisolver-1.4.5-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9", size = 1334300 }, + { url = "https://files.pythonhosted.org/packages/8d/26/b4569d1f29751fca22ee915b4ebfef5974f4ef239b3335fc072882bd62d9/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437", size = 1426579 }, + { url = "https://files.pythonhosted.org/packages/f3/a3/804fc7c8bf233806ec0321c9da35971578620f2ab4fafe67d76100b3ce52/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9", size = 1541360 }, + { url = "https://files.pythonhosted.org/packages/07/ef/286e1d26524854f6fbd6540e8364d67a8857d61038ac743e11edc42fe217/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da", size = 1470091 }, + { url = "https://files.pythonhosted.org/packages/17/ba/17a706b232308e65f57deeccae503c268292e6a091313f6ce833a23093ea/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e", size = 1426259 }, + { url = "https://files.pythonhosted.org/packages/d0/f3/a0925611c9d6c2f37c5935a39203cadec6883aa914e013b46c84c4c2e641/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8", size = 1847516 }, + { url = "https://files.pythonhosted.org/packages/da/85/82d59bb8f7c4c9bb2785138b72462cb1b161668f8230c58bbb28c0403cd5/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d", size = 1946228 }, + { url = "https://files.pythonhosted.org/packages/34/3c/6a37f444c0233993881e5db3a6a1775925d4d9d2f2609bb325bb1348ed94/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0", size = 1901716 }, + { url = "https://files.pythonhosted.org/packages/cd/7e/180425790efc00adfd47db14e1e341cb4826516982334129012b971121a6/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f", size = 1852871 }, + { url = "https://files.pythonhosted.org/packages/1b/9a/13c68b2edb1fa74321e60893a9a5829788e135138e68060cf44e2d92d2c3/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f", size = 1870265 }, + { url = "https://files.pythonhosted.org/packages/9f/0a/fa56a0fdee5da2b4c79899c0f6bd1aefb29d9438c2d66430e78793571c6b/kiwisolver-1.4.5-cp311-cp311-win32.whl", hash = "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac", size = 46649 }, + { url = "https://files.pythonhosted.org/packages/1e/37/d3c2d4ba2719059a0f12730947bbe1ad5ee8bff89e8c35319dcb2c9ddb4c/kiwisolver-1.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355", size = 56116 }, + { url = "https://files.pythonhosted.org/packages/f3/7a/debbce859be1a2711eb8437818107137192007b88d17b5cfdb556f457b42/kiwisolver-1.4.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a", size = 125484 }, + { url = "https://files.pythonhosted.org/packages/2d/e0/bf8df75ba93b9e035cc6757dd5dcaf63084fdc1c846ae134e818bd7e0f03/kiwisolver-1.4.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192", size = 67332 }, + { url = "https://files.pythonhosted.org/packages/26/61/58bb691f6880588be3a4801d199bd776032ece07203faf3e4a8b377f7d9b/kiwisolver-1.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45", size = 64987 }, + { url = "https://files.pythonhosted.org/packages/8e/a3/96ac5413068b237c006f54dd8d70114e8756d70e3da7613c5aef20627e22/kiwisolver-1.4.5-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7", size = 1370613 }, + { url = "https://files.pythonhosted.org/packages/4d/12/f48539e6e17068b59c7f12f4d6214b973431b8e3ac83af525cafd27cebec/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db", size = 1463183 }, + { url = "https://files.pythonhosted.org/packages/f3/70/26c99be8eb034cc8e3f62e0760af1fbdc97a842a7cbc252f7978507d41c2/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff", size = 1581248 }, + { url = "https://files.pythonhosted.org/packages/17/f6/f75f20e543639b09b2de7fc864274a5a9b96cda167a6210a1d9d19306b9d/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228", size = 1508815 }, + { url = "https://files.pythonhosted.org/packages/e3/d5/bc0f22ac108743062ab703f8d6d71c9c7b077b8839fa358700bfb81770b8/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16", size = 1466042 }, + { url = "https://files.pythonhosted.org/packages/75/18/98142500f21d6838bcab49ec919414a1f0c6d049d21ddadf139124db6a70/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9", size = 1885159 }, + { url = "https://files.pythonhosted.org/packages/21/49/a241eff9e0ee013368c1d17957f9d345b0957493c3a43d82ebb558c90b0a/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162", size = 1981694 }, + { url = "https://files.pythonhosted.org/packages/90/90/9490c3de4788123041b1d600d64434f1eed809a2ce9f688075a22166b289/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4", size = 1941579 }, + { url = "https://files.pythonhosted.org/packages/b7/bb/a0cc488ef2aa92d7d304318c8549d3ec8dfe6dd3c2c67a44e3922b77bc4f/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3", size = 1888168 }, + { url = "https://files.pythonhosted.org/packages/4f/e9/9c0de8e45fef3d63f85eed3b1757f9aa511065942866331ef8b99421f433/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a", size = 1908464 }, + { url = "https://files.pythonhosted.org/packages/a3/60/4f0fd50b08f5be536ea0cef518ac7255d9dab43ca40f3b93b60e3ddf80dd/kiwisolver-1.4.5-cp312-cp312-win32.whl", hash = "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20", size = 46473 }, + { url = "https://files.pythonhosted.org/packages/63/50/2746566bdf4a6a842d117367d05c90cfb87ac04e9e2845aa1fa21f071362/kiwisolver-1.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9", size = 56004 }, ] [[package]] name = "lazy-loader" version = "0.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0e/3a/1630a735bfdf9eb857a3b9a53317a1e1658ea97a1b4b39dcb0f71dae81f8/lazy_loader-0.3.tar.gz", hash = "sha256:3b68898e34f5b2a29daaaac172c6555512d0f32074f147e2254e4a6d9d838f37", size = 12268, upload-time = "2023-06-30T21:12:55.362Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0e/3a/1630a735bfdf9eb857a3b9a53317a1e1658ea97a1b4b39dcb0f71dae81f8/lazy_loader-0.3.tar.gz", hash = "sha256:3b68898e34f5b2a29daaaac172c6555512d0f32074f147e2254e4a6d9d838f37", size = 12268 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/c3/65b3814e155836acacf720e5be3b5757130346670ac454fee29d3eda1381/lazy_loader-0.3-py3-none-any.whl", hash = "sha256:1e9e76ee8631e264c62ce10006718e80b2cfc74340d17d1031e0f84af7478554", size = 9087, upload-time = "2023-06-30T21:12:51.09Z" }, + { url = "https://files.pythonhosted.org/packages/a1/c3/65b3814e155836acacf720e5be3b5757130346670ac454fee29d3eda1381/lazy_loader-0.3-py3-none-any.whl", hash = "sha256:1e9e76ee8631e264c62ce10006718e80b2cfc74340d17d1031e0f84af7478554", size = 9087 }, ] [[package]] name = "locust" -version = "2.37.11" +version = "2.38.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "configargparse" }, @@ -1245,14 +1361,14 @@ dependencies = [ { name = "typing-extensions", marker = "python_full_version < '3.11'" }, { name = "werkzeug" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4f/19/66cdab585f7d4385be615d3792402fc75a1bed7519e5283adbe7133dbc78/locust-2.37.11.tar.gz", hash = "sha256:89c79bc599aa57160bd41dd3876e35d8b9dee5abded78e35008d01fd8f1640ed", size = 2252602, upload-time = "2025-06-23T08:22:23.922Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fb/93/ecd79dde28e24bdc99488d4e2c0ad4117252257d5cbdd61e3b14d1f03786/locust-2.38.0.tar.gz", hash = "sha256:5bd6c29d8423733cb5d9a265548c9fef7b731f2254aa91885d6c98d0d39f90f0", size = 1406518 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/2d/e5ae05911521bf84113be349d51b16d54589e986837d2d518f63434ea3ec/locust-2.37.11-py3-none-any.whl", hash = "sha256:b826f95fbfd5d9a32df6ab1b74672b88e65bbc33ec99fdc10af98079952ad517", size = 2269179, upload-time = "2025-06-23T08:22:21.067Z" }, + { url = "https://files.pythonhosted.org/packages/ae/be/57ca67b95c45e69c173e86fe5c934d789effc2ec203d3e3ec2a0b32aa707/locust-2.38.0-py3-none-any.whl", hash = "sha256:b92c937e8659e9ffd6d6d1cab2f63f70aa98c87975911938d1f473534f46fd78", size = 1424083 }, ] [[package]] name = "locust-cloud" -version = "1.24.2" +version = "1.26.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "configargparse" }, @@ -1262,9 +1378,9 @@ dependencies = [ { name = "python-socketio", extra = ["client"] }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d9/77/bda24167a2b763ba5d3cad1f3fa2a938f5273e51a61bffdbc8dc2e3ba24d/locust_cloud-1.24.2.tar.gz", hash = "sha256:a2656537ff367e6d4d4673477ba9e81ed73a8423a71573cd2512248740eded77", size = 451122, upload-time = "2025-06-23T11:08:00.558Z" } +sdist = { url = "https://files.pythonhosted.org/packages/84/ad/10b299b134068a4250a9156e6832a717406abe1dfea2482a07ae7bdca8f3/locust_cloud-1.26.3.tar.gz", hash = "sha256:587acfd4d2dee715fb5f0c3c2d922770babf0b7cff7b2927afbb693a9cd193cc", size = 456042 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/38/8cda8aa1c6dfe5c5abbf69a9b10c03585c37eff64ca92733a291806052ac/locust_cloud-1.24.2-py3-none-any.whl", hash = "sha256:64a5e6f2bf0a1a012d9805291d44fb57e57535c2b5c0fa5bc87ba0d7cce9ef9c", size = 408594, upload-time = "2025-06-23T11:07:59.092Z" }, + { url = "https://files.pythonhosted.org/packages/50/6a/276fc50a9d170e7cbb6715735480cb037abb526639bca85491576e6eee4a/locust_cloud-1.26.3-py3-none-any.whl", hash = "sha256:8cb4b8bb9adcd5b99327bc8ed1d98cf67a29d9d29512651e6e94869de6f1faa8", size = 410023 }, ] [[package]] @@ -1274,148 +1390,240 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mdurl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } wheels = [ - { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" }, + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, ] [[package]] name = "markupsafe" version = "2.1.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6d/7c/59a3248f411813f8ccba92a55feaac4bf360d29e2ff05ee7d8e1ef2d7dbf/MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad", size = 19132, upload-time = "2023-06-02T21:43:45.578Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/7c/59a3248f411813f8ccba92a55feaac4bf360d29e2ff05ee7d8e1ef2d7dbf/MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad", size = 19132 } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/1d/713d443799d935f4d26a4f1510c9e61b1d288592fb869845e5cc92a1e055/MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa", size = 17846, upload-time = "2023-06-02T21:42:33.954Z" }, - { url = "https://files.pythonhosted.org/packages/f7/9c/86cbd8e0e1d81f0ba420f20539dd459c50537c7751e28102dbfee2b6f28c/MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", size = 13720, upload-time = "2023-06-02T21:42:35.102Z" }, - { url = "https://files.pythonhosted.org/packages/a6/56/f1d4ee39e898a9e63470cbb7fae1c58cce6874f25f54220b89213a47f273/MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", size = 26498, upload-time = "2023-06-02T21:42:36.608Z" }, - { url = "https://files.pythonhosted.org/packages/12/b3/d9ed2c0971e1435b8a62354b18d3060b66c8cb1d368399ec0b9baa7c0ee5/MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52", size = 25691, upload-time = "2023-06-02T21:42:37.778Z" }, - { url = "https://files.pythonhosted.org/packages/bf/b7/c5ba9b7ad9ad21fc4a60df226615cf43ead185d328b77b0327d603d00cc5/MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", size = 25366, upload-time = "2023-06-02T21:42:39.441Z" }, - { url = "https://files.pythonhosted.org/packages/71/61/f5673d7aac2cf7f203859008bb3fc2b25187aa330067c5e9955e5c5ebbab/MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", size = 30505, upload-time = "2023-06-02T21:42:41.088Z" }, - { url = "https://files.pythonhosted.org/packages/47/26/932140621773bfd4df3223fbdd9e78de3477f424f0d2987c313b1cb655ff/MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", size = 29616, upload-time = "2023-06-02T21:42:42.273Z" }, - { url = "https://files.pythonhosted.org/packages/3c/c8/74d13c999cbb49e3460bf769025659a37ef4a8e884de629720ab4e42dcdb/MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7", size = 29891, upload-time = "2023-06-02T21:42:43.635Z" }, - { url = "https://files.pythonhosted.org/packages/96/e4/4db3b1abc5a1fe7295aa0683eafd13832084509c3b8236f3faf8dd4eff75/MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", size = 16525, upload-time = "2023-06-02T21:42:45.271Z" }, - { url = "https://files.pythonhosted.org/packages/84/a8/c4aebb8a14a1d39d5135eb8233a0b95831cdc42c4088358449c3ed657044/MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", size = 17083, upload-time = "2023-06-02T21:42:46.948Z" }, - { url = "https://files.pythonhosted.org/packages/fe/09/c31503cb8150cf688c1534a7135cc39bb9092f8e0e6369ec73494d16ee0e/MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", size = 17862, upload-time = "2023-06-02T21:42:48.569Z" }, - { url = "https://files.pythonhosted.org/packages/c0/c7/171f5ac6b065e1425e8fabf4a4dfbeca76fd8070072c6a41bd5c07d90d8b/MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", size = 13738, upload-time = "2023-06-02T21:42:49.727Z" }, - { url = "https://files.pythonhosted.org/packages/a2/f7/9175ad1b8152092f7c3b78c513c1bdfe9287e0564447d1c2d3d1a2471540/MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee", size = 28891, upload-time = "2023-06-02T21:42:51.33Z" }, - { url = "https://files.pythonhosted.org/packages/fe/21/2eff1de472ca6c99ec3993eab11308787b9879af9ca8bbceb4868cf4f2ca/MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2", size = 28096, upload-time = "2023-06-02T21:42:52.966Z" }, - { url = "https://files.pythonhosted.org/packages/f4/a0/103f94793c3bf829a18d2415117334ece115aeca56f2df1c47fa02c6dbd6/MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9", size = 27631, upload-time = "2023-06-02T21:42:54.518Z" }, - { url = "https://files.pythonhosted.org/packages/43/70/f24470f33b2035b035ef0c0ffebf57006beb2272cf3df068fc5154e04ead/MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", size = 33863, upload-time = "2023-06-02T21:42:55.777Z" }, - { url = "https://files.pythonhosted.org/packages/32/d4/ce98c4ca713d91c4a17c1a184785cc00b9e9c25699d618956c2b9999500a/MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", size = 32591, upload-time = "2023-06-02T21:42:57.415Z" }, - { url = "https://files.pythonhosted.org/packages/bb/82/f88ccb3ca6204a4536cf7af5abdad7c3657adac06ab33699aa67279e0744/MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac", size = 33186, upload-time = "2023-06-02T21:42:59.107Z" }, - { url = "https://files.pythonhosted.org/packages/44/53/93405d37bb04a10c43b1bdd6f548097478d494d7eadb4b364e3e1337f0cc/MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb", size = 16537, upload-time = "2023-06-02T21:43:00.927Z" }, - { url = "https://files.pythonhosted.org/packages/be/bb/08b85bc194034efbf572e70c3951549c8eca0ada25363afc154386b5390a/MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", size = 17089, upload-time = "2023-06-02T21:43:02.355Z" }, - { url = "https://files.pythonhosted.org/packages/89/5a/ee546f2aa73a1d6fcfa24272f356fe06d29acca81e76b8d32ca53e429a2e/MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc", size = 17849, upload-time = "2023-09-07T16:00:43.795Z" }, - { url = "https://files.pythonhosted.org/packages/3a/72/9f683a059bde096776e8acf9aa34cbbba21ddc399861fe3953790d4f2cde/MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823", size = 13700, upload-time = "2023-09-07T16:00:45.384Z" }, - { url = "https://files.pythonhosted.org/packages/9d/78/92f15eb9b1e8f1668a9787ba103cf6f8d19a9efed8150245404836145c24/MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11", size = 29319, upload-time = "2023-09-07T16:00:46.48Z" }, - { url = "https://files.pythonhosted.org/packages/51/94/9a04085114ff2c24f7424dbc890a281d73c5a74ea935dc2e69c66a3bd558/MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd", size = 28314, upload-time = "2023-09-07T16:00:47.64Z" }, - { url = "https://files.pythonhosted.org/packages/ec/53/fcb3214bd370185e223b209ce6bb010fb887ea57173ca4f75bd211b24e10/MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939", size = 27696, upload-time = "2023-09-07T16:00:48.92Z" }, - { url = "https://files.pythonhosted.org/packages/e7/33/54d29854716725d7826079b8984dd235fac76dab1c32321e555d493e61f5/MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c", size = 33746, upload-time = "2023-09-07T16:00:50.081Z" }, - { url = "https://files.pythonhosted.org/packages/11/40/ea7f85e2681d29bc9301c757257de561923924f24de1802d9c3baa396bb4/MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c", size = 32131, upload-time = "2023-09-07T16:00:51.822Z" }, - { url = "https://files.pythonhosted.org/packages/41/f1/bc770c37ecd58638c18f8ec85df205dacb818ccf933692082fd93010a4bc/MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1", size = 32878, upload-time = "2023-09-07T16:00:53.575Z" }, - { url = "https://files.pythonhosted.org/packages/49/74/bf95630aab0a9ed6a67556cd4e54f6aeb0e74f4cb0fd2f229154873a4be4/MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007", size = 16426, upload-time = "2023-09-07T16:00:55.987Z" }, - { url = "https://files.pythonhosted.org/packages/44/44/dbaf65876e258facd65f586dde158387ab89963e7f2235551afc9c2e24c2/MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb", size = 16979, upload-time = "2023-09-07T16:00:57.77Z" }, + { url = "https://files.pythonhosted.org/packages/20/1d/713d443799d935f4d26a4f1510c9e61b1d288592fb869845e5cc92a1e055/MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa", size = 17846 }, + { url = "https://files.pythonhosted.org/packages/f7/9c/86cbd8e0e1d81f0ba420f20539dd459c50537c7751e28102dbfee2b6f28c/MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", size = 13720 }, + { url = "https://files.pythonhosted.org/packages/a6/56/f1d4ee39e898a9e63470cbb7fae1c58cce6874f25f54220b89213a47f273/MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", size = 26498 }, + { url = "https://files.pythonhosted.org/packages/12/b3/d9ed2c0971e1435b8a62354b18d3060b66c8cb1d368399ec0b9baa7c0ee5/MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52", size = 25691 }, + { url = "https://files.pythonhosted.org/packages/bf/b7/c5ba9b7ad9ad21fc4a60df226615cf43ead185d328b77b0327d603d00cc5/MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", size = 25366 }, + { url = "https://files.pythonhosted.org/packages/71/61/f5673d7aac2cf7f203859008bb3fc2b25187aa330067c5e9955e5c5ebbab/MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", size = 30505 }, + { url = "https://files.pythonhosted.org/packages/47/26/932140621773bfd4df3223fbdd9e78de3477f424f0d2987c313b1cb655ff/MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", size = 29616 }, + { url = "https://files.pythonhosted.org/packages/3c/c8/74d13c999cbb49e3460bf769025659a37ef4a8e884de629720ab4e42dcdb/MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7", size = 29891 }, + { url = "https://files.pythonhosted.org/packages/96/e4/4db3b1abc5a1fe7295aa0683eafd13832084509c3b8236f3faf8dd4eff75/MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", size = 16525 }, + { url = "https://files.pythonhosted.org/packages/84/a8/c4aebb8a14a1d39d5135eb8233a0b95831cdc42c4088358449c3ed657044/MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", size = 17083 }, + { url = "https://files.pythonhosted.org/packages/fe/09/c31503cb8150cf688c1534a7135cc39bb9092f8e0e6369ec73494d16ee0e/MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", size = 17862 }, + { url = "https://files.pythonhosted.org/packages/c0/c7/171f5ac6b065e1425e8fabf4a4dfbeca76fd8070072c6a41bd5c07d90d8b/MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", size = 13738 }, + { url = "https://files.pythonhosted.org/packages/a2/f7/9175ad1b8152092f7c3b78c513c1bdfe9287e0564447d1c2d3d1a2471540/MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee", size = 28891 }, + { url = "https://files.pythonhosted.org/packages/fe/21/2eff1de472ca6c99ec3993eab11308787b9879af9ca8bbceb4868cf4f2ca/MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2", size = 28096 }, + { url = "https://files.pythonhosted.org/packages/f4/a0/103f94793c3bf829a18d2415117334ece115aeca56f2df1c47fa02c6dbd6/MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9", size = 27631 }, + { url = "https://files.pythonhosted.org/packages/43/70/f24470f33b2035b035ef0c0ffebf57006beb2272cf3df068fc5154e04ead/MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", size = 33863 }, + { url = "https://files.pythonhosted.org/packages/32/d4/ce98c4ca713d91c4a17c1a184785cc00b9e9c25699d618956c2b9999500a/MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", size = 32591 }, + { url = "https://files.pythonhosted.org/packages/bb/82/f88ccb3ca6204a4536cf7af5abdad7c3657adac06ab33699aa67279e0744/MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac", size = 33186 }, + { url = "https://files.pythonhosted.org/packages/44/53/93405d37bb04a10c43b1bdd6f548097478d494d7eadb4b364e3e1337f0cc/MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb", size = 16537 }, + { url = "https://files.pythonhosted.org/packages/be/bb/08b85bc194034efbf572e70c3951549c8eca0ada25363afc154386b5390a/MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", size = 17089 }, + { url = "https://files.pythonhosted.org/packages/89/5a/ee546f2aa73a1d6fcfa24272f356fe06d29acca81e76b8d32ca53e429a2e/MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc", size = 17849 }, + { url = "https://files.pythonhosted.org/packages/3a/72/9f683a059bde096776e8acf9aa34cbbba21ddc399861fe3953790d4f2cde/MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823", size = 13700 }, + { url = "https://files.pythonhosted.org/packages/9d/78/92f15eb9b1e8f1668a9787ba103cf6f8d19a9efed8150245404836145c24/MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11", size = 29319 }, + { url = "https://files.pythonhosted.org/packages/51/94/9a04085114ff2c24f7424dbc890a281d73c5a74ea935dc2e69c66a3bd558/MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd", size = 28314 }, + { url = "https://files.pythonhosted.org/packages/ec/53/fcb3214bd370185e223b209ce6bb010fb887ea57173ca4f75bd211b24e10/MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939", size = 27696 }, + { url = "https://files.pythonhosted.org/packages/e7/33/54d29854716725d7826079b8984dd235fac76dab1c32321e555d493e61f5/MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c", size = 33746 }, + { url = "https://files.pythonhosted.org/packages/11/40/ea7f85e2681d29bc9301c757257de561923924f24de1802d9c3baa396bb4/MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c", size = 32131 }, + { url = "https://files.pythonhosted.org/packages/41/f1/bc770c37ecd58638c18f8ec85df205dacb818ccf933692082fd93010a4bc/MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1", size = 32878 }, + { url = "https://files.pythonhosted.org/packages/49/74/bf95630aab0a9ed6a67556cd4e54f6aeb0e74f4cb0fd2f229154873a4be4/MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007", size = 16426 }, + { url = "https://files.pythonhosted.org/packages/44/44/dbaf65876e258facd65f586dde158387ab89963e7f2235551afc9c2e24c2/MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb", size = 16979 }, ] [[package]] name = "matplotlib" version = "3.8.2" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "contourpy" }, - { name = "cycler" }, - { name = "fonttools" }, - { name = "kiwisolver" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "pillow" }, - { name = "pyparsing" }, - { name = "python-dateutil" }, +resolution-markers = [ + "python_full_version < '3.11' and sys_platform == 'darwin'", + "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')", ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/ab/38a0e94cb01dacb50f06957c2bed1c83b8f9dac6618988a37b2487862944/matplotlib-3.8.2.tar.gz", hash = "sha256:01a978b871b881ee76017152f1f1a0cbf6bd5f7b8ff8c96df0df1bd57d8755a1", size = 35866957, upload-time = "2023-11-17T21:16:40.15Z" } +dependencies = [ + { name = "contourpy", version = "1.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "cycler", marker = "python_full_version < '3.11'" }, + { name = "fonttools", marker = "python_full_version < '3.11'" }, + { name = "kiwisolver", marker = "python_full_version < '3.11'" }, + { name = "numpy", marker = "python_full_version < '3.11'" }, + { name = "packaging", marker = "python_full_version < '3.11'" }, + { name = "pillow", marker = "python_full_version < '3.11'" }, + { name = "pyparsing", marker = "python_full_version < '3.11'" }, + { name = "python-dateutil", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fb/ab/38a0e94cb01dacb50f06957c2bed1c83b8f9dac6618988a37b2487862944/matplotlib-3.8.2.tar.gz", hash = "sha256:01a978b871b881ee76017152f1f1a0cbf6bd5f7b8ff8c96df0df1bd57d8755a1", size = 35866957 } wheels = [ - { url = "https://files.pythonhosted.org/packages/92/d0/fc5f6796a1956f5b9a33555611d01a3cec038f000c3d70ecb051b1631ac4/matplotlib-3.8.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:09796f89fb71a0c0e1e2f4bdaf63fb2cefc84446bb963ecdeb40dfee7dfa98c7", size = 7590640, upload-time = "2023-11-17T21:17:02.834Z" }, - { url = "https://files.pythonhosted.org/packages/57/44/007b592809f50883c910db9ec4b81b16dfa0136407250fb581824daabf03/matplotlib-3.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6f9c6976748a25e8b9be51ea028df49b8e561eed7809146da7a47dbecebab367", size = 7484350, upload-time = "2023-11-17T21:17:12.281Z" }, - { url = "https://files.pythonhosted.org/packages/01/87/c7b24f3048234fe10184560263be2173311376dc3d1fa329de7f012d6ce5/matplotlib-3.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b78e4f2cedf303869b782071b55fdde5987fda3038e9d09e58c91cc261b5ad18", size = 11382388, upload-time = "2023-11-17T21:17:26.461Z" }, - { url = "https://files.pythonhosted.org/packages/19/e5/a4ea514515f270224435c69359abb7a3d152ed31b9ee3ba5e63017461945/matplotlib-3.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e208f46cf6576a7624195aa047cb344a7f802e113bb1a06cfd4bee431de5e31", size = 11611959, upload-time = "2023-11-17T21:17:40.541Z" }, - { url = "https://files.pythonhosted.org/packages/09/23/ab5a562c9acb81e351b084bea39f65b153918417fb434619cf5a19f44a55/matplotlib-3.8.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:46a569130ff53798ea5f50afce7406e91fdc471ca1e0e26ba976a8c734c9427a", size = 9536938, upload-time = "2023-11-17T21:17:49.925Z" }, - { url = "https://files.pythonhosted.org/packages/46/37/b5e27ab30ecc0a3694c8a78287b5ef35dad0c3095c144fcc43081170bfd6/matplotlib-3.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:830f00640c965c5b7f6bc32f0d4ce0c36dfe0379f7dd65b07a00c801713ec40a", size = 7643836, upload-time = "2023-11-17T21:17:58.379Z" }, - { url = "https://files.pythonhosted.org/packages/a9/0d/53afb186adafc7326d093b8333e8a79974c495095771659f4304626c4bc7/matplotlib-3.8.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d86593ccf546223eb75a39b44c32788e6f6440d13cfc4750c1c15d0fcb850b63", size = 7593458, upload-time = "2023-11-17T21:18:06.141Z" }, - { url = "https://files.pythonhosted.org/packages/ce/25/a557ee10ac9dce1300850024707ce1850a6958f1673a9194be878b99d631/matplotlib-3.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9a5430836811b7652991939012f43d2808a2db9b64ee240387e8c43e2e5578c8", size = 7486840, upload-time = "2023-11-17T21:18:13.706Z" }, - { url = "https://files.pythonhosted.org/packages/e7/3d/72712b3895ee180f6e342638a8591c31912fbcc09ce9084cc256da16d0a0/matplotlib-3.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9576723858a78751d5aacd2497b8aef29ffea6d1c95981505877f7ac28215c6", size = 11387332, upload-time = "2023-11-17T21:18:23.699Z" }, - { url = "https://files.pythonhosted.org/packages/92/1a/cd3e0c90d1a763ad90073e13b189b4702f11becf4e71dbbad70a7a149811/matplotlib-3.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ba9cbd8ac6cf422f3102622b20f8552d601bf8837e49a3afed188d560152788", size = 11616911, upload-time = "2023-11-17T21:18:35.27Z" }, - { url = "https://files.pythonhosted.org/packages/78/4a/bad239071477305a3758eb4810615e310a113399cddd7682998be9f01e97/matplotlib-3.8.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:03f9d160a29e0b65c0790bb07f4f45d6a181b1ac33eb1bb0dd225986450148f0", size = 9549260, upload-time = "2023-11-17T21:18:44.836Z" }, - { url = "https://files.pythonhosted.org/packages/26/5a/27fd341e4510257789f19a4b4be8bb90d1113b8f176c3dab562b4f21466e/matplotlib-3.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:3773002da767f0a9323ba1a9b9b5d00d6257dbd2a93107233167cfb581f64717", size = 7645742, upload-time = "2023-11-17T21:18:53.448Z" }, - { url = "https://files.pythonhosted.org/packages/e4/1b/864d28d5a72d586ac137f4ca54d5afc8b869720e30d508dbd9adcce4d231/matplotlib-3.8.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:4c318c1e95e2f5926fba326f68177dee364aa791d6df022ceb91b8221bd0a627", size = 7590988, upload-time = "2023-11-17T21:19:01.119Z" }, - { url = "https://files.pythonhosted.org/packages/9a/b0/dd2b60f2dd90fbc21d1d3129c36a453c322d7995d5e3589f5b3c59ee528d/matplotlib-3.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:091275d18d942cf1ee9609c830a1bc36610607d8223b1b981c37d5c9fc3e46a4", size = 7483594, upload-time = "2023-11-17T21:19:09.865Z" }, - { url = "https://files.pythonhosted.org/packages/33/da/9942533ad9f96753bde0e5a5d48eacd6c21de8ea1ad16570e31bda8a017f/matplotlib-3.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b0f3b8ea0e99e233a4bcc44590f01604840d833c280ebb8fe5554fd3e6cfe8d", size = 11380843, upload-time = "2023-11-17T21:19:20.46Z" }, - { url = "https://files.pythonhosted.org/packages/fc/52/bfd36eb4745a3b21b3946c2c3a15679b620e14574fe2b98e9451b65ef578/matplotlib-3.8.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7b1704a530395aaf73912be741c04d181f82ca78084fbd80bc737be04848331", size = 11604608, upload-time = "2023-11-17T21:19:31.363Z" }, - { url = "https://files.pythonhosted.org/packages/6d/8c/0cdfbf604d4ea3dfa77435176c51e233cc408ad8f3efbf8d2c9f57cbdafb/matplotlib-3.8.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:533b0e3b0c6768eef8cbe4b583731ce25a91ab54a22f830db2b031e83cca9213", size = 9545252, upload-time = "2023-11-17T21:19:42.271Z" }, - { url = "https://files.pythonhosted.org/packages/2e/51/c77a14869b7eb9d6fb440e811b754fc3950d6868c38ace57d0632b674415/matplotlib-3.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:0f4fc5d72b75e2c18e55eb32292659cf731d9d5b312a6eb036506304f4675630", size = 7645067, upload-time = "2023-11-17T21:19:50.091Z" }, + { url = "https://files.pythonhosted.org/packages/92/d0/fc5f6796a1956f5b9a33555611d01a3cec038f000c3d70ecb051b1631ac4/matplotlib-3.8.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:09796f89fb71a0c0e1e2f4bdaf63fb2cefc84446bb963ecdeb40dfee7dfa98c7", size = 7590640 }, + { url = "https://files.pythonhosted.org/packages/57/44/007b592809f50883c910db9ec4b81b16dfa0136407250fb581824daabf03/matplotlib-3.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6f9c6976748a25e8b9be51ea028df49b8e561eed7809146da7a47dbecebab367", size = 7484350 }, + { url = "https://files.pythonhosted.org/packages/01/87/c7b24f3048234fe10184560263be2173311376dc3d1fa329de7f012d6ce5/matplotlib-3.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b78e4f2cedf303869b782071b55fdde5987fda3038e9d09e58c91cc261b5ad18", size = 11382388 }, + { url = "https://files.pythonhosted.org/packages/19/e5/a4ea514515f270224435c69359abb7a3d152ed31b9ee3ba5e63017461945/matplotlib-3.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e208f46cf6576a7624195aa047cb344a7f802e113bb1a06cfd4bee431de5e31", size = 11611959 }, + { url = "https://files.pythonhosted.org/packages/09/23/ab5a562c9acb81e351b084bea39f65b153918417fb434619cf5a19f44a55/matplotlib-3.8.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:46a569130ff53798ea5f50afce7406e91fdc471ca1e0e26ba976a8c734c9427a", size = 9536938 }, + { url = "https://files.pythonhosted.org/packages/46/37/b5e27ab30ecc0a3694c8a78287b5ef35dad0c3095c144fcc43081170bfd6/matplotlib-3.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:830f00640c965c5b7f6bc32f0d4ce0c36dfe0379f7dd65b07a00c801713ec40a", size = 7643836 }, + { url = "https://files.pythonhosted.org/packages/a9/0d/53afb186adafc7326d093b8333e8a79974c495095771659f4304626c4bc7/matplotlib-3.8.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d86593ccf546223eb75a39b44c32788e6f6440d13cfc4750c1c15d0fcb850b63", size = 7593458 }, + { url = "https://files.pythonhosted.org/packages/ce/25/a557ee10ac9dce1300850024707ce1850a6958f1673a9194be878b99d631/matplotlib-3.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9a5430836811b7652991939012f43d2808a2db9b64ee240387e8c43e2e5578c8", size = 7486840 }, + { url = "https://files.pythonhosted.org/packages/e7/3d/72712b3895ee180f6e342638a8591c31912fbcc09ce9084cc256da16d0a0/matplotlib-3.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9576723858a78751d5aacd2497b8aef29ffea6d1c95981505877f7ac28215c6", size = 11387332 }, + { url = "https://files.pythonhosted.org/packages/92/1a/cd3e0c90d1a763ad90073e13b189b4702f11becf4e71dbbad70a7a149811/matplotlib-3.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ba9cbd8ac6cf422f3102622b20f8552d601bf8837e49a3afed188d560152788", size = 11616911 }, + { url = "https://files.pythonhosted.org/packages/78/4a/bad239071477305a3758eb4810615e310a113399cddd7682998be9f01e97/matplotlib-3.8.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:03f9d160a29e0b65c0790bb07f4f45d6a181b1ac33eb1bb0dd225986450148f0", size = 9549260 }, + { url = "https://files.pythonhosted.org/packages/26/5a/27fd341e4510257789f19a4b4be8bb90d1113b8f176c3dab562b4f21466e/matplotlib-3.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:3773002da767f0a9323ba1a9b9b5d00d6257dbd2a93107233167cfb581f64717", size = 7645742 }, + { url = "https://files.pythonhosted.org/packages/e4/1b/864d28d5a72d586ac137f4ca54d5afc8b869720e30d508dbd9adcce4d231/matplotlib-3.8.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:4c318c1e95e2f5926fba326f68177dee364aa791d6df022ceb91b8221bd0a627", size = 7590988 }, + { url = "https://files.pythonhosted.org/packages/9a/b0/dd2b60f2dd90fbc21d1d3129c36a453c322d7995d5e3589f5b3c59ee528d/matplotlib-3.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:091275d18d942cf1ee9609c830a1bc36610607d8223b1b981c37d5c9fc3e46a4", size = 7483594 }, + { url = "https://files.pythonhosted.org/packages/33/da/9942533ad9f96753bde0e5a5d48eacd6c21de8ea1ad16570e31bda8a017f/matplotlib-3.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b0f3b8ea0e99e233a4bcc44590f01604840d833c280ebb8fe5554fd3e6cfe8d", size = 11380843 }, + { url = "https://files.pythonhosted.org/packages/fc/52/bfd36eb4745a3b21b3946c2c3a15679b620e14574fe2b98e9451b65ef578/matplotlib-3.8.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7b1704a530395aaf73912be741c04d181f82ca78084fbd80bc737be04848331", size = 11604608 }, + { url = "https://files.pythonhosted.org/packages/6d/8c/0cdfbf604d4ea3dfa77435176c51e233cc408ad8f3efbf8d2c9f57cbdafb/matplotlib-3.8.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:533b0e3b0c6768eef8cbe4b583731ce25a91ab54a22f830db2b031e83cca9213", size = 9545252 }, + { url = "https://files.pythonhosted.org/packages/2e/51/c77a14869b7eb9d6fb440e811b754fc3950d6868c38ace57d0632b674415/matplotlib-3.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:0f4fc5d72b75e2c18e55eb32292659cf731d9d5b312a6eb036506304f4675630", size = 7645067 }, +] + +[[package]] +name = "matplotlib" +version = "3.10.5" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and sys_platform == 'darwin'", + "python_full_version == '3.12.*' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version == '3.13.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version >= '3.14' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform != 'darwin' and sys_platform != 'linux')", + "(python_full_version == '3.13.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform != 'darwin' and sys_platform != 'linux')", + "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version == '3.11.*' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')", +] +dependencies = [ + { name = "contourpy", version = "1.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "cycler", marker = "python_full_version >= '3.11'" }, + { name = "fonttools", marker = "python_full_version >= '3.11'" }, + { name = "kiwisolver", marker = "python_full_version >= '3.11'" }, + { name = "numpy", marker = "python_full_version >= '3.11'" }, + { name = "packaging", marker = "python_full_version >= '3.11'" }, + { name = "pillow", marker = "python_full_version >= '3.11'" }, + { name = "pyparsing", marker = "python_full_version >= '3.11'" }, + { name = "python-dateutil", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/91/f2939bb60b7ebf12478b030e0d7f340247390f402b3b189616aad790c366/matplotlib-3.10.5.tar.gz", hash = "sha256:352ed6ccfb7998a00881692f38b4ca083c691d3e275b4145423704c34c909076", size = 34804044 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/89/5355cdfe43242cb4d1a64a67cb6831398b665ad90e9702c16247cbd8d5ab/matplotlib-3.10.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:5d4773a6d1c106ca05cb5a5515d277a6bb96ed09e5c8fab6b7741b8fcaa62c8f", size = 8229094 }, + { url = "https://files.pythonhosted.org/packages/34/bc/ba802650e1c69650faed261a9df004af4c6f21759d7a1ec67fe972f093b3/matplotlib-3.10.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc88af74e7ba27de6cbe6faee916024ea35d895ed3d61ef6f58c4ce97da7185a", size = 8091464 }, + { url = "https://files.pythonhosted.org/packages/ac/64/8d0c8937dee86c286625bddb1902efacc3e22f2b619f5b5a8df29fe5217b/matplotlib-3.10.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:64c4535419d5617f7363dad171a5a59963308e0f3f813c4bed6c9e6e2c131512", size = 8653163 }, + { url = "https://files.pythonhosted.org/packages/11/dc/8dfc0acfbdc2fc2336c72561b7935cfa73db9ca70b875d8d3e1b3a6f371a/matplotlib-3.10.5-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a277033048ab22d34f88a3c5243938cef776493f6201a8742ed5f8b553201343", size = 9490635 }, + { url = "https://files.pythonhosted.org/packages/54/02/e3fdfe0f2e9fb05f3a691d63876639dbf684170fdcf93231e973104153b4/matplotlib-3.10.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e4a6470a118a2e93022ecc7d3bd16b3114b2004ea2bf014fff875b3bc99b70c6", size = 9539036 }, + { url = "https://files.pythonhosted.org/packages/c1/29/82bf486ff7f4dbedfb11ccc207d0575cbe3be6ea26f75be514252bde3d70/matplotlib-3.10.5-cp310-cp310-win_amd64.whl", hash = "sha256:7e44cada61bec8833c106547786814dd4a266c1b2964fd25daa3804f1b8d4467", size = 8093529 }, + { url = "https://files.pythonhosted.org/packages/aa/c7/1f2db90a1d43710478bb1e9b57b162852f79234d28e4f48a28cc415aa583/matplotlib-3.10.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:dcfc39c452c6a9f9028d3e44d2d721484f665304857188124b505b2c95e1eecf", size = 8239216 }, + { url = "https://files.pythonhosted.org/packages/82/6d/ca6844c77a4f89b1c9e4d481c412e1d1dbabf2aae2cbc5aa2da4a1d6683e/matplotlib-3.10.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:903352681b59f3efbf4546985142a9686ea1d616bb054b09a537a06e4b892ccf", size = 8102130 }, + { url = "https://files.pythonhosted.org/packages/1d/1e/5e187a30cc673a3e384f3723e5f3c416033c1d8d5da414f82e4e731128ea/matplotlib-3.10.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:080c3676a56b8ee1c762bcf8fca3fe709daa1ee23e6ef06ad9f3fc17332f2d2a", size = 8666471 }, + { url = "https://files.pythonhosted.org/packages/03/c0/95540d584d7d645324db99a845ac194e915ef75011a0d5e19e1b5cee7e69/matplotlib-3.10.5-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4b4984d5064a35b6f66d2c11d668565f4389b1119cc64db7a4c1725bc11adffc", size = 9500518 }, + { url = "https://files.pythonhosted.org/packages/ba/2e/e019352099ea58b4169adb9c6e1a2ad0c568c6377c2b677ee1f06de2adc7/matplotlib-3.10.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3967424121d3a46705c9fa9bdb0931de3228f13f73d7bb03c999c88343a89d89", size = 9552372 }, + { url = "https://files.pythonhosted.org/packages/b7/81/3200b792a5e8b354f31f4101ad7834743ad07b6d620259f2059317b25e4d/matplotlib-3.10.5-cp311-cp311-win_amd64.whl", hash = "sha256:33775bbeb75528555a15ac29396940128ef5613cf9a2d31fb1bfd18b3c0c0903", size = 8100634 }, + { url = "https://files.pythonhosted.org/packages/52/46/a944f6f0c1f5476a0adfa501969d229ce5ae60cf9a663be0e70361381f89/matplotlib-3.10.5-cp311-cp311-win_arm64.whl", hash = "sha256:c61333a8e5e6240e73769d5826b9a31d8b22df76c0778f8480baf1b4b01c9420", size = 7978880 }, + { url = "https://files.pythonhosted.org/packages/66/1e/c6f6bcd882d589410b475ca1fc22e34e34c82adff519caf18f3e6dd9d682/matplotlib-3.10.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:00b6feadc28a08bd3c65b2894f56cf3c94fc8f7adcbc6ab4516ae1e8ed8f62e2", size = 8253056 }, + { url = "https://files.pythonhosted.org/packages/53/e6/d6f7d1b59413f233793dda14419776f5f443bcccb2dfc84b09f09fe05dbe/matplotlib-3.10.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee98a5c5344dc7f48dc261b6ba5d9900c008fc12beb3fa6ebda81273602cc389", size = 8110131 }, + { url = "https://files.pythonhosted.org/packages/66/2b/bed8a45e74957549197a2ac2e1259671cd80b55ed9e1fe2b5c94d88a9202/matplotlib-3.10.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a17e57e33de901d221a07af32c08870ed4528db0b6059dce7d7e65c1122d4bea", size = 8669603 }, + { url = "https://files.pythonhosted.org/packages/7e/a7/315e9435b10d057f5e52dfc603cd353167ae28bb1a4e033d41540c0067a4/matplotlib-3.10.5-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97b9d6443419085950ee4a5b1ee08c363e5c43d7176e55513479e53669e88468", size = 9508127 }, + { url = "https://files.pythonhosted.org/packages/7f/d9/edcbb1f02ca99165365d2768d517898c22c6040187e2ae2ce7294437c413/matplotlib-3.10.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ceefe5d40807d29a66ae916c6a3915d60ef9f028ce1927b84e727be91d884369", size = 9566926 }, + { url = "https://files.pythonhosted.org/packages/3b/d9/6dd924ad5616c97b7308e6320cf392c466237a82a2040381163b7500510a/matplotlib-3.10.5-cp312-cp312-win_amd64.whl", hash = "sha256:c04cba0f93d40e45b3c187c6c52c17f24535b27d545f757a2fffebc06c12b98b", size = 8107599 }, + { url = "https://files.pythonhosted.org/packages/0e/f3/522dc319a50f7b0279fbe74f86f7a3506ce414bc23172098e8d2bdf21894/matplotlib-3.10.5-cp312-cp312-win_arm64.whl", hash = "sha256:a41bcb6e2c8e79dc99c5511ae6f7787d2fb52efd3d805fff06d5d4f667db16b2", size = 7978173 }, + { url = "https://files.pythonhosted.org/packages/8d/05/4f3c1f396075f108515e45cb8d334aff011a922350e502a7472e24c52d77/matplotlib-3.10.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:354204db3f7d5caaa10e5de74549ef6a05a4550fdd1c8f831ab9bca81efd39ed", size = 8253586 }, + { url = "https://files.pythonhosted.org/packages/2f/2c/e084415775aac7016c3719fe7006cdb462582c6c99ac142f27303c56e243/matplotlib-3.10.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b072aac0c3ad563a2b3318124756cb6112157017f7431626600ecbe890df57a1", size = 8110715 }, + { url = "https://files.pythonhosted.org/packages/52/1b/233e3094b749df16e3e6cd5a44849fd33852e692ad009cf7de00cf58ddf6/matplotlib-3.10.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d52fd5b684d541b5a51fb276b2b97b010c75bee9aa392f96b4a07aeb491e33c7", size = 8669397 }, + { url = "https://files.pythonhosted.org/packages/e8/ec/03f9e003a798f907d9f772eed9b7c6a9775d5bd00648b643ebfb88e25414/matplotlib-3.10.5-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee7a09ae2f4676276f5a65bd9f2bd91b4f9fbaedf49f40267ce3f9b448de501f", size = 9508646 }, + { url = "https://files.pythonhosted.org/packages/91/e7/c051a7a386680c28487bca27d23b02d84f63e3d2a9b4d2fc478e6a42e37e/matplotlib-3.10.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ba6c3c9c067b83481d647af88b4e441d532acdb5ef22178a14935b0b881188f4", size = 9567424 }, + { url = "https://files.pythonhosted.org/packages/36/c2/24302e93ff431b8f4173ee1dd88976c8d80483cadbc5d3d777cef47b3a1c/matplotlib-3.10.5-cp313-cp313-win_amd64.whl", hash = "sha256:07442d2692c9bd1cceaa4afb4bbe5b57b98a7599de4dabfcca92d3eea70f9ebe", size = 8107809 }, + { url = "https://files.pythonhosted.org/packages/0b/33/423ec6a668d375dad825197557ed8fbdb74d62b432c1ed8235465945475f/matplotlib-3.10.5-cp313-cp313-win_arm64.whl", hash = "sha256:48fe6d47380b68a37ccfcc94f009530e84d41f71f5dae7eda7c4a5a84aa0a674", size = 7978078 }, + { url = "https://files.pythonhosted.org/packages/51/17/521fc16ec766455c7bb52cc046550cf7652f6765ca8650ff120aa2d197b6/matplotlib-3.10.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b80eb8621331449fc519541a7461987f10afa4f9cfd91afcd2276ebe19bd56c", size = 8295590 }, + { url = "https://files.pythonhosted.org/packages/f8/12/23c28b2c21114c63999bae129fce7fd34515641c517ae48ce7b7dcd33458/matplotlib-3.10.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:47a388908e469d6ca2a6015858fa924e0e8a2345a37125948d8e93a91c47933e", size = 8158518 }, + { url = "https://files.pythonhosted.org/packages/81/f8/aae4eb25e8e7190759f3cb91cbeaa344128159ac92bb6b409e24f8711f78/matplotlib-3.10.5-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8b6b49167d208358983ce26e43aa4196073b4702858670f2eb111f9a10652b4b", size = 8691815 }, + { url = "https://files.pythonhosted.org/packages/d0/ba/450c39ebdd486bd33a359fc17365ade46c6a96bf637bbb0df7824de2886c/matplotlib-3.10.5-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a8da0453a7fd8e3da114234ba70c5ba9ef0e98f190309ddfde0f089accd46ea", size = 9522814 }, + { url = "https://files.pythonhosted.org/packages/89/11/9c66f6a990e27bb9aa023f7988d2d5809cb98aa39c09cbf20fba75a542ef/matplotlib-3.10.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:52c6573dfcb7726a9907b482cd5b92e6b5499b284ffacb04ffbfe06b3e568124", size = 9573917 }, + { url = "https://files.pythonhosted.org/packages/b3/69/8b49394de92569419e5e05e82e83df9b749a0ff550d07631ea96ed2eb35a/matplotlib-3.10.5-cp313-cp313t-win_amd64.whl", hash = "sha256:a23193db2e9d64ece69cac0c8231849db7dd77ce59c7b89948cf9d0ce655a3ce", size = 8181034 }, + { url = "https://files.pythonhosted.org/packages/47/23/82dc435bb98a2fc5c20dffcac8f0b083935ac28286413ed8835df40d0baa/matplotlib-3.10.5-cp313-cp313t-win_arm64.whl", hash = "sha256:56da3b102cf6da2776fef3e71cd96fcf22103a13594a18ac9a9b31314e0be154", size = 8023337 }, + { url = "https://files.pythonhosted.org/packages/ac/e0/26b6cfde31f5383503ee45dcb7e691d45dadf0b3f54639332b59316a97f8/matplotlib-3.10.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:96ef8f5a3696f20f55597ffa91c28e2e73088df25c555f8d4754931515512715", size = 8253591 }, + { url = "https://files.pythonhosted.org/packages/c1/89/98488c7ef7ea20ea659af7499628c240a608b337af4be2066d644cfd0a0f/matplotlib-3.10.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:77fab633e94b9da60512d4fa0213daeb76d5a7b05156840c4fd0399b4b818837", size = 8112566 }, + { url = "https://files.pythonhosted.org/packages/52/67/42294dfedc82aea55e1a767daf3263aacfb5a125f44ba189e685bab41b6f/matplotlib-3.10.5-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27f52634315e96b1debbfdc5c416592edcd9c4221bc2f520fd39c33db5d9f202", size = 9513281 }, + { url = "https://files.pythonhosted.org/packages/e7/68/f258239e0cf34c2cbc816781c7ab6fca768452e6bf1119aedd2bd4a882a3/matplotlib-3.10.5-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:525f6e28c485c769d1f07935b660c864de41c37fd716bfa64158ea646f7084bb", size = 9780873 }, + { url = "https://files.pythonhosted.org/packages/89/64/f4881554006bd12e4558bd66778bdd15d47b00a1f6c6e8b50f6208eda4b3/matplotlib-3.10.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1f5f3ec4c191253c5f2b7c07096a142c6a1c024d9f738247bfc8e3f9643fc975", size = 9568954 }, + { url = "https://files.pythonhosted.org/packages/06/f8/42779d39c3f757e1f012f2dda3319a89fb602bd2ef98ce8faf0281f4febd/matplotlib-3.10.5-cp314-cp314-win_amd64.whl", hash = "sha256:707f9c292c4cd4716f19ab8a1f93f26598222cd931e0cd98fbbb1c5994bf7667", size = 8237465 }, + { url = "https://files.pythonhosted.org/packages/cf/f8/153fd06b5160f0cd27c8b9dd797fcc9fb56ac6a0ebf3c1f765b6b68d3c8a/matplotlib-3.10.5-cp314-cp314-win_arm64.whl", hash = "sha256:21a95b9bf408178d372814de7baacd61c712a62cae560b5e6f35d791776f6516", size = 8108898 }, + { url = "https://files.pythonhosted.org/packages/9a/ee/c4b082a382a225fe0d2a73f1f57cf6f6f132308805b493a54c8641006238/matplotlib-3.10.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:a6b310f95e1102a8c7c817ef17b60ee5d1851b8c71b63d9286b66b177963039e", size = 8295636 }, + { url = "https://files.pythonhosted.org/packages/30/73/2195fa2099718b21a20da82dfc753bf2af58d596b51aefe93e359dd5915a/matplotlib-3.10.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:94986a242747a0605cb3ff1cb98691c736f28a59f8ffe5175acaeb7397c49a5a", size = 8158575 }, + { url = "https://files.pythonhosted.org/packages/f6/e9/a08cdb34618a91fa08f75e6738541da5cacde7c307cea18ff10f0d03fcff/matplotlib-3.10.5-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ff10ea43288f0c8bab608a305dc6c918cc729d429c31dcbbecde3b9f4d5b569", size = 9522815 }, + { url = "https://files.pythonhosted.org/packages/4e/bb/34d8b7e0d1bb6d06ef45db01dfa560d5a67b1c40c0b998ce9ccde934bb09/matplotlib-3.10.5-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f6adb644c9d040ffb0d3434e440490a66cf73dbfa118a6f79cd7568431f7a012", size = 9783514 }, + { url = "https://files.pythonhosted.org/packages/12/09/d330d1e55dcca2e11b4d304cc5227f52e2512e46828d6249b88e0694176e/matplotlib-3.10.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4fa40a8f98428f789a9dcacd625f59b7bc4e3ef6c8c7c80187a7a709475cf592", size = 9573932 }, + { url = "https://files.pythonhosted.org/packages/eb/3b/f70258ac729aa004aca673800a53a2b0a26d49ca1df2eaa03289a1c40f81/matplotlib-3.10.5-cp314-cp314t-win_amd64.whl", hash = "sha256:95672a5d628b44207aab91ec20bf59c26da99de12b88f7e0b1fb0a84a86ff959", size = 8322003 }, + { url = "https://files.pythonhosted.org/packages/5b/60/3601f8ce6d76a7c81c7f25a0e15fde0d6b66226dd187aa6d2838e6374161/matplotlib-3.10.5-cp314-cp314t-win_arm64.whl", hash = "sha256:2efaf97d72629e74252e0b5e3c46813e9eeaa94e011ecf8084a971a31a97f40b", size = 8153849 }, + { url = "https://files.pythonhosted.org/packages/e4/eb/7d4c5de49eb78294e1a8e2be8a6ecff8b433e921b731412a56cd1abd3567/matplotlib-3.10.5-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b5fa2e941f77eb579005fb804026f9d0a1082276118d01cc6051d0d9626eaa7f", size = 8222360 }, + { url = "https://files.pythonhosted.org/packages/16/8a/e435db90927b66b16d69f8f009498775f4469f8de4d14b87856965e58eba/matplotlib-3.10.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1fc0d2a3241cdcb9daaca279204a3351ce9df3c0e7e621c7e04ec28aaacaca30", size = 8087462 }, + { url = "https://files.pythonhosted.org/packages/0b/dd/06c0e00064362f5647f318e00b435be2ff76a1bdced97c5eaf8347311fbe/matplotlib-3.10.5-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8dee65cb1424b7dc982fe87895b5613d4e691cc57117e8af840da0148ca6c1d7", size = 8659802 }, + { url = "https://files.pythonhosted.org/packages/dc/d6/e921be4e1a5f7aca5194e1f016cb67ec294548e530013251f630713e456d/matplotlib-3.10.5-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:160e125da27a749481eaddc0627962990f6029811dbeae23881833a011a0907f", size = 8233224 }, + { url = "https://files.pythonhosted.org/packages/ec/74/a2b9b04824b9c349c8f1b2d21d5af43fa7010039427f2b133a034cb09e59/matplotlib-3.10.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ac3d50760394d78a3c9be6b28318fe22b494c4fcf6407e8fd4794b538251899b", size = 8098539 }, + { url = "https://files.pythonhosted.org/packages/fc/66/cd29ebc7f6c0d2a15d216fb572573e8fc38bd5d6dec3bd9d7d904c0949f7/matplotlib-3.10.5-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6c49465bf689c4d59d174d0c7795fb42a21d4244d11d70e52b8011987367ac61", size = 8672192 }, ] [[package]] name = "mdurl" version = "0.1.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, ] [[package]] name = "mpmath" version = "1.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106 } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 }, ] [[package]] name = "msgpack" version = "1.0.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c2/d5/5662032db1571110b5b51647aed4b56dfbd01bfae789fa566a2be1f385d1/msgpack-1.0.7.tar.gz", hash = "sha256:572efc93db7a4d27e404501975ca6d2d9775705c2d922390d878fcf768d92c87", size = 166311, upload-time = "2023-09-28T13:20:36.726Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c2/d5/5662032db1571110b5b51647aed4b56dfbd01bfae789fa566a2be1f385d1/msgpack-1.0.7.tar.gz", hash = "sha256:572efc93db7a4d27e404501975ca6d2d9775705c2d922390d878fcf768d92c87", size = 166311 } wheels = [ - { url = "https://files.pythonhosted.org/packages/41/3a/2e2e902afcd751738e38d88af976fc4010b16e8e821945f4cbf32f75f9c3/msgpack-1.0.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:04ad6069c86e531682f9e1e71b71c1c3937d6014a7c3e9edd2aa81ad58842862", size = 304827, upload-time = "2023-09-28T13:18:30.258Z" }, - { url = "https://files.pythonhosted.org/packages/86/a6/490792a524a82e855bdf3885ecb73d7b3a0b17744b3cf4a40aea13ceca38/msgpack-1.0.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cca1b62fe70d761a282496b96a5e51c44c213e410a964bdffe0928e611368329", size = 234959, upload-time = "2023-09-28T13:18:32.146Z" }, - { url = "https://files.pythonhosted.org/packages/ad/72/d39ed43bfb2ec6968d768318477adb90c474bdc59b2437170c6697ee4115/msgpack-1.0.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e50ebce52f41370707f1e21a59514e3375e3edd6e1832f5e5235237db933c98b", size = 231970, upload-time = "2023-09-28T13:18:34.134Z" }, - { url = "https://files.pythonhosted.org/packages/a2/90/2d769e693654f036acfb462b54dacb3ae345699999897ca34f6bd9534fe9/msgpack-1.0.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b4f35de6a304b5533c238bee86b670b75b03d31b7797929caa7a624b5dda6", size = 522440, upload-time = "2023-09-28T13:18:35.866Z" }, - { url = "https://files.pythonhosted.org/packages/46/95/d0440400485eab1bf50f1efe5118967b539f3191d994c3dfc220657594cd/msgpack-1.0.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28efb066cde83c479dfe5a48141a53bc7e5f13f785b92ddde336c716663039ee", size = 530797, upload-time = "2023-09-28T13:18:37.653Z" }, - { url = "https://files.pythonhosted.org/packages/76/33/35df717bc095c6e938b3c65ed117b95048abc24d1614427685123fb2f0af/msgpack-1.0.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cb14ce54d9b857be9591ac364cb08dc2d6a5c4318c1182cb1d02274029d590d", size = 520372, upload-time = "2023-09-28T13:18:39.685Z" }, - { url = "https://files.pythonhosted.org/packages/af/d1/abbdd58a43827fbec5d98427a7a535c620890289b9d927154465313d6967/msgpack-1.0.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b573a43ef7c368ba4ea06050a957c2a7550f729c31f11dd616d2ac4aba99888d", size = 527287, upload-time = "2023-09-28T13:18:41.051Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ac/66625b05091b97ca2c7418eb2d2af152f033d969519f9315556a4ed800fe/msgpack-1.0.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ccf9a39706b604d884d2cb1e27fe973bc55f2890c52f38df742bc1d79ab9f5e1", size = 560715, upload-time = "2023-09-28T13:18:42.883Z" }, - { url = "https://files.pythonhosted.org/packages/de/4e/a0e8611f94bac32d2c1c4ad05bb1c0ae61132e3398e0b44a93e6d7830968/msgpack-1.0.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cb70766519500281815dfd7a87d3a178acf7ce95390544b8c90587d76b227681", size = 532614, upload-time = "2023-09-28T13:18:44.679Z" }, - { url = "https://files.pythonhosted.org/packages/9b/07/0b3f089684ca330602b2994248eda2898a7232e4b63882b9271164ef672e/msgpack-1.0.7-cp310-cp310-win32.whl", hash = "sha256:b610ff0f24e9f11c9ae653c67ff8cc03c075131401b3e5ef4b82570d1728f8a9", size = 216340, upload-time = "2023-09-28T13:18:46.588Z" }, - { url = "https://files.pythonhosted.org/packages/4b/14/c62fbc8dff118f1558e43b9469d56a1f37bbb35febadc3163efaedd01500/msgpack-1.0.7-cp310-cp310-win_amd64.whl", hash = "sha256:a40821a89dc373d6427e2b44b572efc36a2778d3f543299e2f24eb1a5de65415", size = 222828, upload-time = "2023-09-28T13:18:47.875Z" }, - { url = "https://files.pythonhosted.org/packages/f9/b3/309de40dc7406b7f3492332c5ee2b492a593c2a9bb97ea48ebf2f5279999/msgpack-1.0.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:576eb384292b139821c41995523654ad82d1916da6a60cff129c715a6223ea84", size = 305096, upload-time = "2023-09-28T13:18:49.678Z" }, - { url = "https://files.pythonhosted.org/packages/15/56/a677cd761a2cefb2e3ffe7e684633294dccb161d78e8ea6da9277e45b4a2/msgpack-1.0.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:730076207cb816138cf1af7f7237b208340a2c5e749707457d70705715c93b93", size = 235210, upload-time = "2023-09-28T13:18:51.039Z" }, - { url = "https://files.pythonhosted.org/packages/f5/4e/1ab4a982cbd90f988e49f849fc1212f2c04a59870c59daabf8950617e2aa/msgpack-1.0.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:85765fdf4b27eb5086f05ac0491090fc76f4f2b28e09d9350c31aac25a5aaff8", size = 231952, upload-time = "2023-09-28T13:18:52.871Z" }, - { url = "https://files.pythonhosted.org/packages/6d/74/bd02044eb628c7361ad2bd8c1a6147af5c6c2bbceb77b3b1da20f4a8a9c5/msgpack-1.0.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3476fae43db72bd11f29a5147ae2f3cb22e2f1a91d575ef130d2bf49afd21c46", size = 549511, upload-time = "2023-09-28T13:18:54.422Z" }, - { url = "https://files.pythonhosted.org/packages/df/09/dee50913ba5cc047f7fd7162f09453a676e7935c84b3bf3a398e12108677/msgpack-1.0.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d4c80667de2e36970ebf74f42d1088cc9ee7ef5f4e8c35eee1b40eafd33ca5b", size = 557980, upload-time = "2023-09-28T13:18:56.058Z" }, - { url = "https://files.pythonhosted.org/packages/26/a5/78a7d87f5f8ffe4c32167afa15d4957db649bab4822f909d8d765339bbab/msgpack-1.0.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b0bf0effb196ed76b7ad883848143427a73c355ae8e569fa538365064188b8e", size = 545547, upload-time = "2023-09-28T13:18:57.396Z" }, - { url = "https://files.pythonhosted.org/packages/d4/53/698c10913947f97f6fe7faad86a34e6aa1b66cea2df6f99105856bd346d9/msgpack-1.0.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f9a7c509542db4eceed3dcf21ee5267ab565a83555c9b88a8109dcecc4709002", size = 554669, upload-time = "2023-09-28T13:18:58.957Z" }, - { url = "https://files.pythonhosted.org/packages/f5/3f/9730c6cb574b15d349b80cd8523a7df4b82058528339f952ea1c32ac8a10/msgpack-1.0.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:84b0daf226913133f899ea9b30618722d45feffa67e4fe867b0b5ae83a34060c", size = 583353, upload-time = "2023-09-28T13:19:01.186Z" }, - { url = "https://files.pythonhosted.org/packages/4c/bc/dc184d943692671149848438fb3bed3a3de288ce7998cb91bc98f40f201b/msgpack-1.0.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ec79ff6159dffcc30853b2ad612ed572af86c92b5168aa3fc01a67b0fa40665e", size = 557455, upload-time = "2023-09-28T13:19:03.201Z" }, - { url = "https://files.pythonhosted.org/packages/cf/7b/1bc69d4a56c8d2f4f2dfbe4722d40344af9a85b6fb3b09cfb350ba6a42f6/msgpack-1.0.7-cp311-cp311-win32.whl", hash = "sha256:3e7bf4442b310ff154b7bb9d81eb2c016b7d597e364f97d72b1acc3817a0fdc1", size = 216367, upload-time = "2023-09-28T13:19:04.554Z" }, - { url = "https://files.pythonhosted.org/packages/b4/3d/c8dd23050eefa3d9b9c5b8329ed3308c2f2f80f65825e9ea4b7fa621cdab/msgpack-1.0.7-cp311-cp311-win_amd64.whl", hash = "sha256:3f0c8c6dfa6605ab8ff0611995ee30d4f9fcff89966cf562733b4008a3d60d82", size = 222860, upload-time = "2023-09-28T13:19:06.397Z" }, - { url = "https://files.pythonhosted.org/packages/d7/47/20dff6b4512cf3575550c8801bc53fe7d540f4efef9c5c37af51760fcdcf/msgpack-1.0.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f0936e08e0003f66bfd97e74ee530427707297b0d0361247e9b4f59ab78ddc8b", size = 305759, upload-time = "2023-09-28T13:19:08.148Z" }, - { url = "https://files.pythonhosted.org/packages/6f/8a/34f1726d2c9feccec3d946776e9bce8f20ae09d8b91899fc20b296c942af/msgpack-1.0.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98bbd754a422a0b123c66a4c341de0474cad4a5c10c164ceed6ea090f3563db4", size = 235330, upload-time = "2023-09-28T13:19:09.417Z" }, - { url = "https://files.pythonhosted.org/packages/9c/f6/e64c72577d6953789c3cb051b059a4b56317056b3c65013952338ed8a34e/msgpack-1.0.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b291f0ee7961a597cbbcc77709374087fa2a9afe7bdb6a40dbbd9b127e79afee", size = 232537, upload-time = "2023-09-28T13:19:10.898Z" }, - { url = "https://files.pythonhosted.org/packages/89/75/1ed3a96e12941873fd957e016cc40c0c178861a872bd45e75b9a188eb422/msgpack-1.0.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebbbba226f0a108a7366bf4b59bf0f30a12fd5e75100c630267d94d7f0ad20e5", size = 546561, upload-time = "2023-09-28T13:19:12.779Z" }, - { url = "https://files.pythonhosted.org/packages/e5/0a/c6a1390f9c6a31da0fecbbfdb86b1cb39ad302d9e24f9cca3d9e14c364f0/msgpack-1.0.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e2d69948e4132813b8d1131f29f9101bc2c915f26089a6d632001a5c1349672", size = 559009, upload-time = "2023-09-28T13:19:14.373Z" }, - { url = "https://files.pythonhosted.org/packages/a5/74/99f6077754665613ea1f37b3d91c10129f6976b7721ab4d0973023808e5a/msgpack-1.0.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdf38ba2d393c7911ae989c3bbba510ebbcdf4ecbdbfec36272abe350c454075", size = 543882, upload-time = "2023-09-28T13:19:16.277Z" }, - { url = "https://files.pythonhosted.org/packages/9c/7e/dc0dc8de2bf27743b31691149258f9b1bd4bf3c44c105df3df9b97081cd1/msgpack-1.0.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:993584fc821c58d5993521bfdcd31a4adf025c7d745bbd4d12ccfecf695af5ba", size = 546949, upload-time = "2023-09-28T13:19:18.114Z" }, - { url = "https://files.pythonhosted.org/packages/78/61/91bae9474def032f6c333d62889bbeda9e1554c6b123375ceeb1767efd78/msgpack-1.0.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:52700dc63a4676669b341ba33520f4d6e43d3ca58d422e22ba66d1736b0a6e4c", size = 579836, upload-time = "2023-09-28T13:19:19.729Z" }, - { url = "https://files.pythonhosted.org/packages/5d/4d/d98592099d4f18945f89cf3e634dc0cb128bb33b1b93f85a84173d35e181/msgpack-1.0.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e45ae4927759289c30ccba8d9fdce62bb414977ba158286b5ddaf8df2cddb5c5", size = 556587, upload-time = "2023-09-28T13:19:21.666Z" }, - { url = "https://files.pythonhosted.org/packages/5e/44/6556ffe169bf2c0e974e2ea25fb82a7e55ebcf52a81b03a5e01820de5f84/msgpack-1.0.7-cp312-cp312-win32.whl", hash = "sha256:27dcd6f46a21c18fa5e5deed92a43d4554e3df8d8ca5a47bf0615d6a5f39dbc9", size = 216509, upload-time = "2023-09-28T13:19:23.161Z" }, - { url = "https://files.pythonhosted.org/packages/dc/c1/63903f30d51d165e132e5221a2a4a1bbfab7508b68131c871d70bffac78a/msgpack-1.0.7-cp312-cp312-win_amd64.whl", hash = "sha256:7687e22a31e976a0e7fc99c2f4d11ca45eff652a81eb8c8085e9609298916dcf", size = 223287, upload-time = "2023-09-28T13:19:25.097Z" }, + { url = "https://files.pythonhosted.org/packages/41/3a/2e2e902afcd751738e38d88af976fc4010b16e8e821945f4cbf32f75f9c3/msgpack-1.0.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:04ad6069c86e531682f9e1e71b71c1c3937d6014a7c3e9edd2aa81ad58842862", size = 304827 }, + { url = "https://files.pythonhosted.org/packages/86/a6/490792a524a82e855bdf3885ecb73d7b3a0b17744b3cf4a40aea13ceca38/msgpack-1.0.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cca1b62fe70d761a282496b96a5e51c44c213e410a964bdffe0928e611368329", size = 234959 }, + { url = "https://files.pythonhosted.org/packages/ad/72/d39ed43bfb2ec6968d768318477adb90c474bdc59b2437170c6697ee4115/msgpack-1.0.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e50ebce52f41370707f1e21a59514e3375e3edd6e1832f5e5235237db933c98b", size = 231970 }, + { url = "https://files.pythonhosted.org/packages/a2/90/2d769e693654f036acfb462b54dacb3ae345699999897ca34f6bd9534fe9/msgpack-1.0.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b4f35de6a304b5533c238bee86b670b75b03d31b7797929caa7a624b5dda6", size = 522440 }, + { url = "https://files.pythonhosted.org/packages/46/95/d0440400485eab1bf50f1efe5118967b539f3191d994c3dfc220657594cd/msgpack-1.0.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28efb066cde83c479dfe5a48141a53bc7e5f13f785b92ddde336c716663039ee", size = 530797 }, + { url = "https://files.pythonhosted.org/packages/76/33/35df717bc095c6e938b3c65ed117b95048abc24d1614427685123fb2f0af/msgpack-1.0.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cb14ce54d9b857be9591ac364cb08dc2d6a5c4318c1182cb1d02274029d590d", size = 520372 }, + { url = "https://files.pythonhosted.org/packages/af/d1/abbdd58a43827fbec5d98427a7a535c620890289b9d927154465313d6967/msgpack-1.0.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b573a43ef7c368ba4ea06050a957c2a7550f729c31f11dd616d2ac4aba99888d", size = 527287 }, + { url = "https://files.pythonhosted.org/packages/0c/ac/66625b05091b97ca2c7418eb2d2af152f033d969519f9315556a4ed800fe/msgpack-1.0.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ccf9a39706b604d884d2cb1e27fe973bc55f2890c52f38df742bc1d79ab9f5e1", size = 560715 }, + { url = "https://files.pythonhosted.org/packages/de/4e/a0e8611f94bac32d2c1c4ad05bb1c0ae61132e3398e0b44a93e6d7830968/msgpack-1.0.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cb70766519500281815dfd7a87d3a178acf7ce95390544b8c90587d76b227681", size = 532614 }, + { url = "https://files.pythonhosted.org/packages/9b/07/0b3f089684ca330602b2994248eda2898a7232e4b63882b9271164ef672e/msgpack-1.0.7-cp310-cp310-win32.whl", hash = "sha256:b610ff0f24e9f11c9ae653c67ff8cc03c075131401b3e5ef4b82570d1728f8a9", size = 216340 }, + { url = "https://files.pythonhosted.org/packages/4b/14/c62fbc8dff118f1558e43b9469d56a1f37bbb35febadc3163efaedd01500/msgpack-1.0.7-cp310-cp310-win_amd64.whl", hash = "sha256:a40821a89dc373d6427e2b44b572efc36a2778d3f543299e2f24eb1a5de65415", size = 222828 }, + { url = "https://files.pythonhosted.org/packages/f9/b3/309de40dc7406b7f3492332c5ee2b492a593c2a9bb97ea48ebf2f5279999/msgpack-1.0.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:576eb384292b139821c41995523654ad82d1916da6a60cff129c715a6223ea84", size = 305096 }, + { url = "https://files.pythonhosted.org/packages/15/56/a677cd761a2cefb2e3ffe7e684633294dccb161d78e8ea6da9277e45b4a2/msgpack-1.0.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:730076207cb816138cf1af7f7237b208340a2c5e749707457d70705715c93b93", size = 235210 }, + { url = "https://files.pythonhosted.org/packages/f5/4e/1ab4a982cbd90f988e49f849fc1212f2c04a59870c59daabf8950617e2aa/msgpack-1.0.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:85765fdf4b27eb5086f05ac0491090fc76f4f2b28e09d9350c31aac25a5aaff8", size = 231952 }, + { url = "https://files.pythonhosted.org/packages/6d/74/bd02044eb628c7361ad2bd8c1a6147af5c6c2bbceb77b3b1da20f4a8a9c5/msgpack-1.0.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3476fae43db72bd11f29a5147ae2f3cb22e2f1a91d575ef130d2bf49afd21c46", size = 549511 }, + { url = "https://files.pythonhosted.org/packages/df/09/dee50913ba5cc047f7fd7162f09453a676e7935c84b3bf3a398e12108677/msgpack-1.0.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d4c80667de2e36970ebf74f42d1088cc9ee7ef5f4e8c35eee1b40eafd33ca5b", size = 557980 }, + { url = "https://files.pythonhosted.org/packages/26/a5/78a7d87f5f8ffe4c32167afa15d4957db649bab4822f909d8d765339bbab/msgpack-1.0.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b0bf0effb196ed76b7ad883848143427a73c355ae8e569fa538365064188b8e", size = 545547 }, + { url = "https://files.pythonhosted.org/packages/d4/53/698c10913947f97f6fe7faad86a34e6aa1b66cea2df6f99105856bd346d9/msgpack-1.0.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f9a7c509542db4eceed3dcf21ee5267ab565a83555c9b88a8109dcecc4709002", size = 554669 }, + { url = "https://files.pythonhosted.org/packages/f5/3f/9730c6cb574b15d349b80cd8523a7df4b82058528339f952ea1c32ac8a10/msgpack-1.0.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:84b0daf226913133f899ea9b30618722d45feffa67e4fe867b0b5ae83a34060c", size = 583353 }, + { url = "https://files.pythonhosted.org/packages/4c/bc/dc184d943692671149848438fb3bed3a3de288ce7998cb91bc98f40f201b/msgpack-1.0.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ec79ff6159dffcc30853b2ad612ed572af86c92b5168aa3fc01a67b0fa40665e", size = 557455 }, + { url = "https://files.pythonhosted.org/packages/cf/7b/1bc69d4a56c8d2f4f2dfbe4722d40344af9a85b6fb3b09cfb350ba6a42f6/msgpack-1.0.7-cp311-cp311-win32.whl", hash = "sha256:3e7bf4442b310ff154b7bb9d81eb2c016b7d597e364f97d72b1acc3817a0fdc1", size = 216367 }, + { url = "https://files.pythonhosted.org/packages/b4/3d/c8dd23050eefa3d9b9c5b8329ed3308c2f2f80f65825e9ea4b7fa621cdab/msgpack-1.0.7-cp311-cp311-win_amd64.whl", hash = "sha256:3f0c8c6dfa6605ab8ff0611995ee30d4f9fcff89966cf562733b4008a3d60d82", size = 222860 }, + { url = "https://files.pythonhosted.org/packages/d7/47/20dff6b4512cf3575550c8801bc53fe7d540f4efef9c5c37af51760fcdcf/msgpack-1.0.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f0936e08e0003f66bfd97e74ee530427707297b0d0361247e9b4f59ab78ddc8b", size = 305759 }, + { url = "https://files.pythonhosted.org/packages/6f/8a/34f1726d2c9feccec3d946776e9bce8f20ae09d8b91899fc20b296c942af/msgpack-1.0.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98bbd754a422a0b123c66a4c341de0474cad4a5c10c164ceed6ea090f3563db4", size = 235330 }, + { url = "https://files.pythonhosted.org/packages/9c/f6/e64c72577d6953789c3cb051b059a4b56317056b3c65013952338ed8a34e/msgpack-1.0.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b291f0ee7961a597cbbcc77709374087fa2a9afe7bdb6a40dbbd9b127e79afee", size = 232537 }, + { url = "https://files.pythonhosted.org/packages/89/75/1ed3a96e12941873fd957e016cc40c0c178861a872bd45e75b9a188eb422/msgpack-1.0.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebbbba226f0a108a7366bf4b59bf0f30a12fd5e75100c630267d94d7f0ad20e5", size = 546561 }, + { url = "https://files.pythonhosted.org/packages/e5/0a/c6a1390f9c6a31da0fecbbfdb86b1cb39ad302d9e24f9cca3d9e14c364f0/msgpack-1.0.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e2d69948e4132813b8d1131f29f9101bc2c915f26089a6d632001a5c1349672", size = 559009 }, + { url = "https://files.pythonhosted.org/packages/a5/74/99f6077754665613ea1f37b3d91c10129f6976b7721ab4d0973023808e5a/msgpack-1.0.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdf38ba2d393c7911ae989c3bbba510ebbcdf4ecbdbfec36272abe350c454075", size = 543882 }, + { url = "https://files.pythonhosted.org/packages/9c/7e/dc0dc8de2bf27743b31691149258f9b1bd4bf3c44c105df3df9b97081cd1/msgpack-1.0.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:993584fc821c58d5993521bfdcd31a4adf025c7d745bbd4d12ccfecf695af5ba", size = 546949 }, + { url = "https://files.pythonhosted.org/packages/78/61/91bae9474def032f6c333d62889bbeda9e1554c6b123375ceeb1767efd78/msgpack-1.0.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:52700dc63a4676669b341ba33520f4d6e43d3ca58d422e22ba66d1736b0a6e4c", size = 579836 }, + { url = "https://files.pythonhosted.org/packages/5d/4d/d98592099d4f18945f89cf3e634dc0cb128bb33b1b93f85a84173d35e181/msgpack-1.0.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e45ae4927759289c30ccba8d9fdce62bb414977ba158286b5ddaf8df2cddb5c5", size = 556587 }, + { url = "https://files.pythonhosted.org/packages/5e/44/6556ffe169bf2c0e974e2ea25fb82a7e55ebcf52a81b03a5e01820de5f84/msgpack-1.0.7-cp312-cp312-win32.whl", hash = "sha256:27dcd6f46a21c18fa5e5deed92a43d4554e3df8d8ca5a47bf0615d6a5f39dbc9", size = 216509 }, + { url = "https://files.pythonhosted.org/packages/dc/c1/63903f30d51d165e132e5221a2a4a1bbfab7508b68131c871d70bffac78a/msgpack-1.0.7-cp312-cp312-win_amd64.whl", hash = "sha256:7687e22a31e976a0e7fc99c2f4d11ca45eff652a81eb8c8085e9609298916dcf", size = 223287 }, ] [[package]] name = "mypy" -version = "1.16.1" +version = "1.17.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, @@ -1423,83 +1631,89 @@ dependencies = [ { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/81/69/92c7fa98112e4d9eb075a239caa4ef4649ad7d441545ccffbd5e34607cbb/mypy-1.16.1.tar.gz", hash = "sha256:6bd00a0a2094841c5e47e7374bb42b83d64c527a502e3334e1173a0c24437bab", size = 3324747, upload-time = "2025-06-16T16:51:35.145Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/22/ea637422dedf0bf36f3ef238eab4e455e2a0dcc3082b5cc067615347ab8e/mypy-1.17.1.tar.gz", hash = "sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01", size = 3352570 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/12/2bf23a80fcef5edb75de9a1e295d778e0f46ea89eb8b115818b663eff42b/mypy-1.16.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b4f0fed1022a63c6fec38f28b7fc77fca47fd490445c69d0a66266c59dd0b88a", size = 10958644, upload-time = "2025-06-16T16:51:11.649Z" }, - { url = "https://files.pythonhosted.org/packages/08/50/bfe47b3b278eacf348291742fd5e6613bbc4b3434b72ce9361896417cfe5/mypy-1.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86042bbf9f5a05ea000d3203cf87aa9d0ccf9a01f73f71c58979eb9249f46d72", size = 10087033, upload-time = "2025-06-16T16:35:30.089Z" }, - { url = "https://files.pythonhosted.org/packages/21/de/40307c12fe25675a0776aaa2cdd2879cf30d99eec91b898de00228dc3ab5/mypy-1.16.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ea7469ee5902c95542bea7ee545f7006508c65c8c54b06dc2c92676ce526f3ea", size = 11875645, upload-time = "2025-06-16T16:35:48.49Z" }, - { url = "https://files.pythonhosted.org/packages/a6/d8/85bdb59e4a98b7a31495bd8f1a4445d8ffc86cde4ab1f8c11d247c11aedc/mypy-1.16.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:352025753ef6a83cb9e7f2427319bb7875d1fdda8439d1e23de12ab164179574", size = 12616986, upload-time = "2025-06-16T16:48:39.526Z" }, - { url = "https://files.pythonhosted.org/packages/0e/d0/bb25731158fa8f8ee9e068d3e94fcceb4971fedf1424248496292512afe9/mypy-1.16.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ff9fa5b16e4c1364eb89a4d16bcda9987f05d39604e1e6c35378a2987c1aac2d", size = 12878632, upload-time = "2025-06-16T16:36:08.195Z" }, - { url = "https://files.pythonhosted.org/packages/2d/11/822a9beb7a2b825c0cb06132ca0a5183f8327a5e23ef89717c9474ba0bc6/mypy-1.16.1-cp310-cp310-win_amd64.whl", hash = "sha256:1256688e284632382f8f3b9e2123df7d279f603c561f099758e66dd6ed4e8bd6", size = 9484391, upload-time = "2025-06-16T16:37:56.151Z" }, - { url = "https://files.pythonhosted.org/packages/9a/61/ec1245aa1c325cb7a6c0f8570a2eee3bfc40fa90d19b1267f8e50b5c8645/mypy-1.16.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:472e4e4c100062488ec643f6162dd0d5208e33e2f34544e1fc931372e806c0cc", size = 10890557, upload-time = "2025-06-16T16:37:21.421Z" }, - { url = "https://files.pythonhosted.org/packages/6b/bb/6eccc0ba0aa0c7a87df24e73f0ad34170514abd8162eb0c75fd7128171fb/mypy-1.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea16e2a7d2714277e349e24d19a782a663a34ed60864006e8585db08f8ad1782", size = 10012921, upload-time = "2025-06-16T16:51:28.659Z" }, - { url = "https://files.pythonhosted.org/packages/5f/80/b337a12e2006715f99f529e732c5f6a8c143bb58c92bb142d5ab380963a5/mypy-1.16.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08e850ea22adc4d8a4014651575567b0318ede51e8e9fe7a68f25391af699507", size = 11802887, upload-time = "2025-06-16T16:50:53.627Z" }, - { url = "https://files.pythonhosted.org/packages/d9/59/f7af072d09793d581a745a25737c7c0a945760036b16aeb620f658a017af/mypy-1.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22d76a63a42619bfb90122889b903519149879ddbf2ba4251834727944c8baca", size = 12531658, upload-time = "2025-06-16T16:33:55.002Z" }, - { url = "https://files.pythonhosted.org/packages/82/c4/607672f2d6c0254b94a646cfc45ad589dd71b04aa1f3d642b840f7cce06c/mypy-1.16.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2c7ce0662b6b9dc8f4ed86eb7a5d505ee3298c04b40ec13b30e572c0e5ae17c4", size = 12732486, upload-time = "2025-06-16T16:37:03.301Z" }, - { url = "https://files.pythonhosted.org/packages/b6/5e/136555ec1d80df877a707cebf9081bd3a9f397dedc1ab9750518d87489ec/mypy-1.16.1-cp311-cp311-win_amd64.whl", hash = "sha256:211287e98e05352a2e1d4e8759c5490925a7c784ddc84207f4714822f8cf99b6", size = 9479482, upload-time = "2025-06-16T16:47:37.48Z" }, - { url = "https://files.pythonhosted.org/packages/b4/d6/39482e5fcc724c15bf6280ff5806548c7185e0c090712a3736ed4d07e8b7/mypy-1.16.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:af4792433f09575d9eeca5c63d7d90ca4aeceda9d8355e136f80f8967639183d", size = 11066493, upload-time = "2025-06-16T16:47:01.683Z" }, - { url = "https://files.pythonhosted.org/packages/e6/e5/26c347890efc6b757f4d5bb83f4a0cf5958b8cf49c938ac99b8b72b420a6/mypy-1.16.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66df38405fd8466ce3517eda1f6640611a0b8e70895e2a9462d1d4323c5eb4b9", size = 10081687, upload-time = "2025-06-16T16:48:19.367Z" }, - { url = "https://files.pythonhosted.org/packages/44/c7/b5cb264c97b86914487d6a24bd8688c0172e37ec0f43e93b9691cae9468b/mypy-1.16.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44e7acddb3c48bd2713994d098729494117803616e116032af192871aed80b79", size = 11839723, upload-time = "2025-06-16T16:49:20.912Z" }, - { url = "https://files.pythonhosted.org/packages/15/f8/491997a9b8a554204f834ed4816bda813aefda31cf873bb099deee3c9a99/mypy-1.16.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0ab5eca37b50188163fa7c1b73c685ac66c4e9bdee4a85c9adac0e91d8895e15", size = 12722980, upload-time = "2025-06-16T16:37:40.929Z" }, - { url = "https://files.pythonhosted.org/packages/df/f0/2bd41e174b5fd93bc9de9a28e4fb673113633b8a7f3a607fa4a73595e468/mypy-1.16.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb6229b2c9086247e21a83c309754b9058b438704ad2f6807f0d8227f6ebdd", size = 12903328, upload-time = "2025-06-16T16:34:35.099Z" }, - { url = "https://files.pythonhosted.org/packages/61/81/5572108a7bec2c46b8aff7e9b524f371fe6ab5efb534d38d6b37b5490da8/mypy-1.16.1-cp312-cp312-win_amd64.whl", hash = "sha256:1f0435cf920e287ff68af3d10a118a73f212deb2ce087619eb4e648116d1fe9b", size = 9562321, upload-time = "2025-06-16T16:48:58.823Z" }, - { url = "https://files.pythonhosted.org/packages/28/e3/96964af4a75a949e67df4b95318fe2b7427ac8189bbc3ef28f92a1c5bc56/mypy-1.16.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ddc91eb318c8751c69ddb200a5937f1232ee8efb4e64e9f4bc475a33719de438", size = 11063480, upload-time = "2025-06-16T16:47:56.205Z" }, - { url = "https://files.pythonhosted.org/packages/f5/4d/cd1a42b8e5be278fab7010fb289d9307a63e07153f0ae1510a3d7b703193/mypy-1.16.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:87ff2c13d58bdc4bbe7dc0dedfe622c0f04e2cb2a492269f3b418df2de05c536", size = 10090538, upload-time = "2025-06-16T16:46:43.92Z" }, - { url = "https://files.pythonhosted.org/packages/c9/4f/c3c6b4b66374b5f68bab07c8cabd63a049ff69796b844bc759a0ca99bb2a/mypy-1.16.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a7cfb0fe29fe5a9841b7c8ee6dffb52382c45acdf68f032145b75620acfbd6f", size = 11836839, upload-time = "2025-06-16T16:36:28.039Z" }, - { url = "https://files.pythonhosted.org/packages/b4/7e/81ca3b074021ad9775e5cb97ebe0089c0f13684b066a750b7dc208438403/mypy-1.16.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:051e1677689c9d9578b9c7f4d206d763f9bbd95723cd1416fad50db49d52f359", size = 12715634, upload-time = "2025-06-16T16:50:34.441Z" }, - { url = "https://files.pythonhosted.org/packages/e9/95/bdd40c8be346fa4c70edb4081d727a54d0a05382d84966869738cfa8a497/mypy-1.16.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d5d2309511cc56c021b4b4e462907c2b12f669b2dbeb68300110ec27723971be", size = 12895584, upload-time = "2025-06-16T16:34:54.857Z" }, - { url = "https://files.pythonhosted.org/packages/5a/fd/d486a0827a1c597b3b48b1bdef47228a6e9ee8102ab8c28f944cb83b65dc/mypy-1.16.1-cp313-cp313-win_amd64.whl", hash = "sha256:4f58ac32771341e38a853c5d0ec0dfe27e18e27da9cdb8bbc882d2249c71a3ee", size = 9573886, upload-time = "2025-06-16T16:36:43.589Z" }, - { url = "https://files.pythonhosted.org/packages/cf/d3/53e684e78e07c1a2bf7105715e5edd09ce951fc3f47cf9ed095ec1b7a037/mypy-1.16.1-py3-none-any.whl", hash = "sha256:5fc2ac4027d0ef28d6ba69a0343737a23c4d1b83672bf38d1fe237bdc0643b37", size = 2265923, upload-time = "2025-06-16T16:48:02.366Z" }, + { url = "https://files.pythonhosted.org/packages/77/a9/3d7aa83955617cdf02f94e50aab5c830d205cfa4320cf124ff64acce3a8e/mypy-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3fbe6d5555bf608c47203baa3e72dbc6ec9965b3d7c318aa9a4ca76f465bd972", size = 11003299 }, + { url = "https://files.pythonhosted.org/packages/83/e8/72e62ff837dd5caaac2b4a5c07ce769c8e808a00a65e5d8f94ea9c6f20ab/mypy-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80ef5c058b7bce08c83cac668158cb7edea692e458d21098c7d3bce35a5d43e7", size = 10125451 }, + { url = "https://files.pythonhosted.org/packages/7d/10/f3f3543f6448db11881776f26a0ed079865926b0c841818ee22de2c6bbab/mypy-1.17.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a580f8a70c69e4a75587bd925d298434057fe2a428faaf927ffe6e4b9a98df", size = 11916211 }, + { url = "https://files.pythonhosted.org/packages/06/bf/63e83ed551282d67bb3f7fea2cd5561b08d2bb6eb287c096539feb5ddbc5/mypy-1.17.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd86bb649299f09d987a2eebb4d52d10603224500792e1bee18303bbcc1ce390", size = 12652687 }, + { url = "https://files.pythonhosted.org/packages/69/66/68f2eeef11facf597143e85b694a161868b3b006a5fbad50e09ea117ef24/mypy-1.17.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a76906f26bd8d51ea9504966a9c25419f2e668f012e0bdf3da4ea1526c534d94", size = 12896322 }, + { url = "https://files.pythonhosted.org/packages/a3/87/8e3e9c2c8bd0d7e071a89c71be28ad088aaecbadf0454f46a540bda7bca6/mypy-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:e79311f2d904ccb59787477b7bd5d26f3347789c06fcd7656fa500875290264b", size = 9507962 }, + { url = "https://files.pythonhosted.org/packages/46/cf/eadc80c4e0a70db1c08921dcc220357ba8ab2faecb4392e3cebeb10edbfa/mypy-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad37544be07c5d7fba814eb370e006df58fed8ad1ef33ed1649cb1889ba6ff58", size = 10921009 }, + { url = "https://files.pythonhosted.org/packages/5d/c1/c869d8c067829ad30d9bdae051046561552516cfb3a14f7f0347b7d973ee/mypy-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:064e2ff508e5464b4bd807a7c1625bc5047c5022b85c70f030680e18f37273a5", size = 10047482 }, + { url = "https://files.pythonhosted.org/packages/98/b9/803672bab3fe03cee2e14786ca056efda4bb511ea02dadcedde6176d06d0/mypy-1.17.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70401bbabd2fa1aa7c43bb358f54037baf0586f41e83b0ae67dd0534fc64edfd", size = 11832883 }, + { url = "https://files.pythonhosted.org/packages/88/fb/fcdac695beca66800918c18697b48833a9a6701de288452b6715a98cfee1/mypy-1.17.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e92bdc656b7757c438660f775f872a669b8ff374edc4d18277d86b63edba6b8b", size = 12566215 }, + { url = "https://files.pythonhosted.org/packages/7f/37/a932da3d3dace99ee8eb2043b6ab03b6768c36eb29a02f98f46c18c0da0e/mypy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c1fdf4abb29ed1cb091cf432979e162c208a5ac676ce35010373ff29247bcad5", size = 12751956 }, + { url = "https://files.pythonhosted.org/packages/8c/cf/6438a429e0f2f5cab8bc83e53dbebfa666476f40ee322e13cac5e64b79e7/mypy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:ff2933428516ab63f961644bc49bc4cbe42bbffb2cd3b71cc7277c07d16b1a8b", size = 9507307 }, + { url = "https://files.pythonhosted.org/packages/17/a2/7034d0d61af8098ec47902108553122baa0f438df8a713be860f7407c9e6/mypy-1.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:69e83ea6553a3ba79c08c6e15dbd9bfa912ec1e493bf75489ef93beb65209aeb", size = 11086295 }, + { url = "https://files.pythonhosted.org/packages/14/1f/19e7e44b594d4b12f6ba8064dbe136505cec813549ca3e5191e40b1d3cc2/mypy-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b16708a66d38abb1e6b5702f5c2c87e133289da36f6a1d15f6a5221085c6403", size = 10112355 }, + { url = "https://files.pythonhosted.org/packages/5b/69/baa33927e29e6b4c55d798a9d44db5d394072eef2bdc18c3e2048c9ed1e9/mypy-1.17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89e972c0035e9e05823907ad5398c5a73b9f47a002b22359b177d40bdaee7056", size = 11875285 }, + { url = "https://files.pythonhosted.org/packages/90/13/f3a89c76b0a41e19490b01e7069713a30949d9a6c147289ee1521bcea245/mypy-1.17.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03b6d0ed2b188e35ee6d5c36b5580cffd6da23319991c49ab5556c023ccf1341", size = 12737895 }, + { url = "https://files.pythonhosted.org/packages/23/a1/c4ee79ac484241301564072e6476c5a5be2590bc2e7bfd28220033d2ef8f/mypy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c837b896b37cd103570d776bda106eabb8737aa6dd4f248451aecf53030cdbeb", size = 12931025 }, + { url = "https://files.pythonhosted.org/packages/89/b8/7409477be7919a0608900e6320b155c72caab4fef46427c5cc75f85edadd/mypy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:665afab0963a4b39dff7c1fa563cc8b11ecff7910206db4b2e64dd1ba25aed19", size = 9584664 }, + { url = "https://files.pythonhosted.org/packages/5b/82/aec2fc9b9b149f372850291827537a508d6c4d3664b1750a324b91f71355/mypy-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93378d3203a5c0800c6b6d850ad2f19f7a3cdf1a3701d3416dbf128805c6a6a7", size = 11075338 }, + { url = "https://files.pythonhosted.org/packages/07/ac/ee93fbde9d2242657128af8c86f5d917cd2887584cf948a8e3663d0cd737/mypy-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:15d54056f7fe7a826d897789f53dd6377ec2ea8ba6f776dc83c2902b899fee81", size = 10113066 }, + { url = "https://files.pythonhosted.org/packages/5a/68/946a1e0be93f17f7caa56c45844ec691ca153ee8b62f21eddda336a2d203/mypy-1.17.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:209a58fed9987eccc20f2ca94afe7257a8f46eb5df1fb69958650973230f91e6", size = 11875473 }, + { url = "https://files.pythonhosted.org/packages/9f/0f/478b4dce1cb4f43cf0f0d00fba3030b21ca04a01b74d1cd272a528cf446f/mypy-1.17.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:099b9a5da47de9e2cb5165e581f158e854d9e19d2e96b6698c0d64de911dd849", size = 12744296 }, + { url = "https://files.pythonhosted.org/packages/ca/70/afa5850176379d1b303f992a828de95fc14487429a7139a4e0bdd17a8279/mypy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ffadfbe6994d724c5a1bb6123a7d27dd68fc9c059561cd33b664a79578e14", size = 12914657 }, + { url = "https://files.pythonhosted.org/packages/53/f9/4a83e1c856a3d9c8f6edaa4749a4864ee98486e9b9dbfbc93842891029c2/mypy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:9a2b7d9180aed171f033c9f2fc6c204c1245cf60b0cb61cf2e7acc24eea78e0a", size = 9593320 }, + { url = "https://files.pythonhosted.org/packages/38/56/79c2fac86da57c7d8c48622a05873eaab40b905096c33597462713f5af90/mypy-1.17.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:15a83369400454c41ed3a118e0cc58bd8123921a602f385cb6d6ea5df050c733", size = 11040037 }, + { url = "https://files.pythonhosted.org/packages/4d/c3/adabe6ff53638e3cad19e3547268482408323b1e68bf082c9119000cd049/mypy-1.17.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:55b918670f692fc9fba55c3298d8a3beae295c5cded0a55dccdc5bbead814acd", size = 10131550 }, + { url = "https://files.pythonhosted.org/packages/b8/c5/2e234c22c3bdeb23a7817af57a58865a39753bde52c74e2c661ee0cfc640/mypy-1.17.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:62761474061feef6f720149d7ba876122007ddc64adff5ba6f374fda35a018a0", size = 11872963 }, + { url = "https://files.pythonhosted.org/packages/ab/26/c13c130f35ca8caa5f2ceab68a247775648fdcd6c9a18f158825f2bc2410/mypy-1.17.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c49562d3d908fd49ed0938e5423daed8d407774a479b595b143a3d7f87cdae6a", size = 12710189 }, + { url = "https://files.pythonhosted.org/packages/82/df/c7d79d09f6de8383fe800521d066d877e54d30b4fb94281c262be2df84ef/mypy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:397fba5d7616a5bc60b45c7ed204717eaddc38f826e3645402c426057ead9a91", size = 12900322 }, + { url = "https://files.pythonhosted.org/packages/b8/98/3d5a48978b4f708c55ae832619addc66d677f6dc59f3ebad71bae8285ca6/mypy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:9d6b20b97d373f41617bd0708fd46aa656059af57f2ef72aa8c7d6a2b73b74ed", size = 9751879 }, + { url = "https://files.pythonhosted.org/packages/1d/f3/8fcd2af0f5b806f6cf463efaffd3c9548a28f84220493ecd38d127b6b66d/mypy-1.17.1-py3-none-any.whl", hash = "sha256:a9f52c0351c21fe24c21d8c0eb1f62967b262d6729393397b6f443c3b773c3b9", size = 2283411 }, ] [[package]] name = "mypy-extensions" version = "1.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433, upload-time = "2023-02-04T12:11:27.157Z" } +sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 } wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695, upload-time = "2023-02-04T12:11:25.002Z" }, + { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, ] [[package]] name = "networkx" version = "3.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c4/80/a84676339aaae2f1cfdf9f418701dd634aef9cc76f708ef55c36ff39c3ca/networkx-3.2.1.tar.gz", hash = "sha256:9f1bb5cf3409bf324e0a722c20bdb4c20ee39bf1c30ce8ae499c8502b0b5e0c6", size = 2073928, upload-time = "2023-10-28T08:41:39.364Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c4/80/a84676339aaae2f1cfdf9f418701dd634aef9cc76f708ef55c36ff39c3ca/networkx-3.2.1.tar.gz", hash = "sha256:9f1bb5cf3409bf324e0a722c20bdb4c20ee39bf1c30ce8ae499c8502b0b5e0c6", size = 2073928 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/f0/8fbc882ca80cf077f1b246c0e3c3465f7f415439bdea6b899f6b19f61f70/networkx-3.2.1-py3-none-any.whl", hash = "sha256:f18c69adc97877c42332c170849c96cefa91881c99a7cb3e95b7c659ebdc1ec2", size = 1647772, upload-time = "2023-10-28T08:41:36.945Z" }, + { url = "https://files.pythonhosted.org/packages/d5/f0/8fbc882ca80cf077f1b246c0e3c3465f7f415439bdea6b899f6b19f61f70/networkx-3.2.1-py3-none-any.whl", hash = "sha256:f18c69adc97877c42332c170849c96cefa91881c99a7cb3e95b7c659ebdc1ec2", size = 1647772 }, ] [[package]] name = "numpy" version = "1.26.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", size = 15786129, upload-time = "2024-02-06T00:26:44.495Z" } +sdist = { url = "https://files.pythonhosted.org/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", size = 15786129 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/94/ace0fdea5241a27d13543ee117cbc65868e82213fb31a8eb7fe9ff23f313/numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", size = 20631468, upload-time = "2024-02-05T23:48:01.194Z" }, - { url = "https://files.pythonhosted.org/packages/20/f7/b24208eba89f9d1b58c1668bc6c8c4fd472b20c45573cb767f59d49fb0f6/numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", size = 13966411, upload-time = "2024-02-05T23:48:29.038Z" }, - { url = "https://files.pythonhosted.org/packages/fc/a5/4beee6488160798683eed5bdb7eead455892c3b4e1f78d79d8d3f3b084ac/numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", size = 14219016, upload-time = "2024-02-05T23:48:54.098Z" }, - { url = "https://files.pythonhosted.org/packages/4b/d7/ecf66c1cd12dc28b4040b15ab4d17b773b87fa9d29ca16125de01adb36cd/numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f", size = 18240889, upload-time = "2024-02-05T23:49:25.361Z" }, - { url = "https://files.pythonhosted.org/packages/24/03/6f229fe3187546435c4f6f89f6d26c129d4f5bed40552899fcf1f0bf9e50/numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", size = 13876746, upload-time = "2024-02-05T23:49:51.983Z" }, - { url = "https://files.pythonhosted.org/packages/39/fe/39ada9b094f01f5a35486577c848fe274e374bbf8d8f472e1423a0bbd26d/numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", size = 18078620, upload-time = "2024-02-05T23:50:22.515Z" }, - { url = "https://files.pythonhosted.org/packages/d5/ef/6ad11d51197aad206a9ad2286dc1aac6a378059e06e8cf22cd08ed4f20dc/numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", size = 5972659, upload-time = "2024-02-05T23:50:35.834Z" }, - { url = "https://files.pythonhosted.org/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", size = 15808905, upload-time = "2024-02-05T23:51:03.701Z" }, - { url = "https://files.pythonhosted.org/packages/11/57/baae43d14fe163fa0e4c47f307b6b2511ab8d7d30177c491960504252053/numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71", size = 20630554, upload-time = "2024-02-05T23:51:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/1a/2e/151484f49fd03944c4a3ad9c418ed193cfd02724e138ac8a9505d056c582/numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef", size = 13997127, upload-time = "2024-02-05T23:52:15.314Z" }, - { url = "https://files.pythonhosted.org/packages/79/ae/7e5b85136806f9dadf4878bf73cf223fe5c2636818ba3ab1c585d0403164/numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e", size = 14222994, upload-time = "2024-02-05T23:52:47.569Z" }, - { url = "https://files.pythonhosted.org/packages/3a/d0/edc009c27b406c4f9cbc79274d6e46d634d139075492ad055e3d68445925/numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5", size = 18252005, upload-time = "2024-02-05T23:53:15.637Z" }, - { url = "https://files.pythonhosted.org/packages/09/bf/2b1aaf8f525f2923ff6cfcf134ae5e750e279ac65ebf386c75a0cf6da06a/numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a", size = 13885297, upload-time = "2024-02-05T23:53:42.16Z" }, - { url = "https://files.pythonhosted.org/packages/df/a0/4e0f14d847cfc2a633a1c8621d00724f3206cfeddeb66d35698c4e2cf3d2/numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a", size = 18093567, upload-time = "2024-02-05T23:54:11.696Z" }, - { url = "https://files.pythonhosted.org/packages/d2/b7/a734c733286e10a7f1a8ad1ae8c90f2d33bf604a96548e0a4a3a6739b468/numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20", size = 5968812, upload-time = "2024-02-05T23:54:26.453Z" }, - { url = "https://files.pythonhosted.org/packages/3f/6b/5610004206cf7f8e7ad91c5a85a8c71b2f2f8051a0c0c4d5916b76d6cbb2/numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2", size = 15811913, upload-time = "2024-02-05T23:54:53.933Z" }, - { url = "https://files.pythonhosted.org/packages/95/12/8f2020a8e8b8383ac0177dc9570aad031a3beb12e38847f7129bacd96228/numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218", size = 20335901, upload-time = "2024-02-05T23:55:32.801Z" }, - { url = "https://files.pythonhosted.org/packages/75/5b/ca6c8bd14007e5ca171c7c03102d17b4f4e0ceb53957e8c44343a9546dcc/numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b", size = 13685868, upload-time = "2024-02-05T23:55:56.28Z" }, - { url = "https://files.pythonhosted.org/packages/79/f8/97f10e6755e2a7d027ca783f63044d5b1bc1ae7acb12afe6a9b4286eac17/numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b", size = 13925109, upload-time = "2024-02-05T23:56:20.368Z" }, - { url = "https://files.pythonhosted.org/packages/0f/50/de23fde84e45f5c4fda2488c759b69990fd4512387a8632860f3ac9cd225/numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed", size = 17950613, upload-time = "2024-02-05T23:56:56.054Z" }, - { url = "https://files.pythonhosted.org/packages/4c/0c/9c603826b6465e82591e05ca230dfc13376da512b25ccd0894709b054ed0/numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a", size = 13572172, upload-time = "2024-02-05T23:57:21.56Z" }, - { url = "https://files.pythonhosted.org/packages/76/8c/2ba3902e1a0fc1c74962ea9bb33a534bb05984ad7ff9515bf8d07527cadd/numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0", size = 17786643, upload-time = "2024-02-05T23:57:56.585Z" }, - { url = "https://files.pythonhosted.org/packages/28/4a/46d9e65106879492374999e76eb85f87b15328e06bd1550668f79f7b18c6/numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110", size = 5677803, upload-time = "2024-02-05T23:58:08.963Z" }, - { url = "https://files.pythonhosted.org/packages/16/2e/86f24451c2d530c88daf997cb8d6ac622c1d40d19f5a031ed68a4b73a374/numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818", size = 15517754, upload-time = "2024-02-05T23:58:36.364Z" }, + { url = "https://files.pythonhosted.org/packages/a7/94/ace0fdea5241a27d13543ee117cbc65868e82213fb31a8eb7fe9ff23f313/numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", size = 20631468 }, + { url = "https://files.pythonhosted.org/packages/20/f7/b24208eba89f9d1b58c1668bc6c8c4fd472b20c45573cb767f59d49fb0f6/numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", size = 13966411 }, + { url = "https://files.pythonhosted.org/packages/fc/a5/4beee6488160798683eed5bdb7eead455892c3b4e1f78d79d8d3f3b084ac/numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", size = 14219016 }, + { url = "https://files.pythonhosted.org/packages/4b/d7/ecf66c1cd12dc28b4040b15ab4d17b773b87fa9d29ca16125de01adb36cd/numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f", size = 18240889 }, + { url = "https://files.pythonhosted.org/packages/24/03/6f229fe3187546435c4f6f89f6d26c129d4f5bed40552899fcf1f0bf9e50/numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", size = 13876746 }, + { url = "https://files.pythonhosted.org/packages/39/fe/39ada9b094f01f5a35486577c848fe274e374bbf8d8f472e1423a0bbd26d/numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", size = 18078620 }, + { url = "https://files.pythonhosted.org/packages/d5/ef/6ad11d51197aad206a9ad2286dc1aac6a378059e06e8cf22cd08ed4f20dc/numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", size = 5972659 }, + { url = "https://files.pythonhosted.org/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", size = 15808905 }, + { url = "https://files.pythonhosted.org/packages/11/57/baae43d14fe163fa0e4c47f307b6b2511ab8d7d30177c491960504252053/numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71", size = 20630554 }, + { url = "https://files.pythonhosted.org/packages/1a/2e/151484f49fd03944c4a3ad9c418ed193cfd02724e138ac8a9505d056c582/numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef", size = 13997127 }, + { url = "https://files.pythonhosted.org/packages/79/ae/7e5b85136806f9dadf4878bf73cf223fe5c2636818ba3ab1c585d0403164/numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e", size = 14222994 }, + { url = "https://files.pythonhosted.org/packages/3a/d0/edc009c27b406c4f9cbc79274d6e46d634d139075492ad055e3d68445925/numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5", size = 18252005 }, + { url = "https://files.pythonhosted.org/packages/09/bf/2b1aaf8f525f2923ff6cfcf134ae5e750e279ac65ebf386c75a0cf6da06a/numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a", size = 13885297 }, + { url = "https://files.pythonhosted.org/packages/df/a0/4e0f14d847cfc2a633a1c8621d00724f3206cfeddeb66d35698c4e2cf3d2/numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a", size = 18093567 }, + { url = "https://files.pythonhosted.org/packages/d2/b7/a734c733286e10a7f1a8ad1ae8c90f2d33bf604a96548e0a4a3a6739b468/numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20", size = 5968812 }, + { url = "https://files.pythonhosted.org/packages/3f/6b/5610004206cf7f8e7ad91c5a85a8c71b2f2f8051a0c0c4d5916b76d6cbb2/numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2", size = 15811913 }, + { url = "https://files.pythonhosted.org/packages/95/12/8f2020a8e8b8383ac0177dc9570aad031a3beb12e38847f7129bacd96228/numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218", size = 20335901 }, + { url = "https://files.pythonhosted.org/packages/75/5b/ca6c8bd14007e5ca171c7c03102d17b4f4e0ceb53957e8c44343a9546dcc/numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b", size = 13685868 }, + { url = "https://files.pythonhosted.org/packages/79/f8/97f10e6755e2a7d027ca783f63044d5b1bc1ae7acb12afe6a9b4286eac17/numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b", size = 13925109 }, + { url = "https://files.pythonhosted.org/packages/0f/50/de23fde84e45f5c4fda2488c759b69990fd4512387a8632860f3ac9cd225/numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed", size = 17950613 }, + { url = "https://files.pythonhosted.org/packages/4c/0c/9c603826b6465e82591e05ca230dfc13376da512b25ccd0894709b054ed0/numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a", size = 13572172 }, + { url = "https://files.pythonhosted.org/packages/76/8c/2ba3902e1a0fc1c74962ea9bb33a534bb05984ad7ff9515bf8d07527cadd/numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0", size = 17786643 }, + { url = "https://files.pythonhosted.org/packages/28/4a/46d9e65106879492374999e76eb85f87b15328e06bd1550668f79f7b18c6/numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110", size = 5677803 }, + { url = "https://files.pythonhosted.org/packages/16/2e/86f24451c2d530c88daf997cb8d6ac622c1d40d19f5a031ed68a4b73a374/numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818", size = 15517754 }, ] [[package]] @@ -1510,31 +1724,31 @@ dependencies = [ { name = "numpy" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b3/fe/0978403c8d710ece2f34006367e78de80410743fe0e7680c8f33f2dab20d/onnx-1.16.0.tar.gz", hash = "sha256:237c6987c6c59d9f44b6136f5819af79574f8d96a760a1fa843bede11f3822f7", size = 12303017, upload-time = "2024-03-25T15:33:46.091Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/fe/0978403c8d710ece2f34006367e78de80410743fe0e7680c8f33f2dab20d/onnx-1.16.0.tar.gz", hash = "sha256:237c6987c6c59d9f44b6136f5819af79574f8d96a760a1fa843bede11f3822f7", size = 12303017 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/0b/f4705e4a3fa6fd0de971302fdae17ad176b024eca8c24360f0e37c00f9df/onnx-1.16.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:9eadbdce25b19d6216f426d6d99b8bc877a65ed92cbef9707751c6669190ba4f", size = 16514483, upload-time = "2024-03-25T15:25:07.947Z" }, - { url = "https://files.pythonhosted.org/packages/b8/1c/50310a559857951fc6e069cf5d89deebe34287997d1c5928bca435456f62/onnx-1.16.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:034ae21a2aaa2e9c14119a840d2926d213c27aad29e5e3edaa30145a745048e1", size = 15012939, upload-time = "2024-03-25T15:25:11.632Z" }, - { url = "https://files.pythonhosted.org/packages/ef/6e/96be6692ebcd8da568084d753f386ce08efa1f99b216f346ee281edd6cc3/onnx-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec22a43d74eb1f2303373e2fbe7fbcaa45fb225f4eb146edfed1356ada7a9aea", size = 15791856, upload-time = "2024-03-25T15:25:15.36Z" }, - { url = "https://files.pythonhosted.org/packages/49/5f/d8e1a24247f506a77cbe22341c72ca91bea3b468c5d6bca2047d885ea3c6/onnx-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:298f28a2b5ac09145fa958513d3d1e6b349ccf86a877dbdcccad57713fe360b3", size = 15922279, upload-time = "2024-03-25T15:25:18.939Z" }, - { url = "https://files.pythonhosted.org/packages/cb/14/562e4ac22cdf41f4465e3b114ef1a9467d513eeff0b9c2285c2da5db6ed1/onnx-1.16.0-cp310-cp310-win32.whl", hash = "sha256:66300197b52beca08bc6262d43c103289c5d45fde43fb51922ed1eb83658cf0c", size = 14335703, upload-time = "2024-03-25T15:25:22.611Z" }, - { url = "https://files.pythonhosted.org/packages/3b/e2/471ff83b3862967791d67f630000afce038756afbdf0665a3d767677c851/onnx-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:ae0029f5e47bf70a1a62e7f88c80bca4ef39b844a89910039184221775df5e43", size = 14435099, upload-time = "2024-03-25T15:25:25.05Z" }, - { url = "https://files.pythonhosted.org/packages/a4/b8/7accf3f93eee498711f0b7f07f6e93906e031622473e85ce9cd3578f6a92/onnx-1.16.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:f51179d4af3372b4f3800c558d204b592c61e4b4a18b8f61e0eea7f46211221a", size = 16514376, upload-time = "2024-03-25T15:25:27.899Z" }, - { url = "https://files.pythonhosted.org/packages/cc/24/a328236b594d5fea23f70a3a8139e730cb43334f0b24693831c47c9064f0/onnx-1.16.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:5202559070afec5144332db216c20f2fff8323cf7f6512b0ca11b215eacc5bf3", size = 15012839, upload-time = "2024-03-25T15:25:31.16Z" }, - { url = "https://files.pythonhosted.org/packages/80/12/57187bab3f830a47fa65eafe4fbaef01dfdf5042cf82a41fa440fab68766/onnx-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77579e7c15b4df39d29465b216639a5f9b74026bdd9e4b6306cd19a32dcfe67c", size = 15791944, upload-time = "2024-03-25T15:25:34.778Z" }, - { url = "https://files.pythonhosted.org/packages/df/48/63f68b65d041aedffab41eea930563ca52aab70dbaa7d4820501618c1a70/onnx-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e60ca76ac24b65c25860d0f2d2cdd96d6320d062a01dd8ce87c5743603789b8", size = 15922450, upload-time = "2024-03-25T15:25:37.983Z" }, - { url = "https://files.pythonhosted.org/packages/08/1b/4bdf4534f5ff08973725ba5409f95bbf64e2789cd20be615880dae689973/onnx-1.16.0-cp311-cp311-win32.whl", hash = "sha256:81b4ee01bc554e8a2b11ac6439882508a5377a1c6b452acd69a1eebb83571117", size = 14335808, upload-time = "2024-03-25T15:25:40.523Z" }, - { url = "https://files.pythonhosted.org/packages/aa/d0/0514d02d2e84e7bb48a105877eae4065e54d7dabb60d0b60214fe2677346/onnx-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:7449241e70b847b9c3eb8dae622df8c1b456d11032a9d7e26e0ee8a698d5bf86", size = 14434905, upload-time = "2024-03-25T15:25:42.905Z" }, - { url = "https://files.pythonhosted.org/packages/42/87/577adadda30ee08041e81ef02a331ca9d1a8df93a2e4c4c53ec56fbbc2ac/onnx-1.16.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:03a627488b1a9975d95d6a55582af3e14c7f3bb87444725b999935ddd271d352", size = 16516304, upload-time = "2024-03-25T15:25:45.875Z" }, - { url = "https://files.pythonhosted.org/packages/e3/1b/6e1ea37e081cc49a28f0e4d3830b4c8525081354cf9f5529c6c92268fc77/onnx-1.16.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:c392faeabd9283ee344ccb4b067d1fea9dfc614fa1f0de7c47589efd79e15e78", size = 15016538, upload-time = "2024-03-25T15:25:49.396Z" }, - { url = "https://files.pythonhosted.org/packages/6d/07/f8fefd5eb0984be42ef677f0b7db7527edc4529224a34a3c31f7b12ec80d/onnx-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0efeb46985de08f0efe758cb54ad3457e821a05c2eaf5ba2ccb8cd1602c08084", size = 15790415, upload-time = "2024-03-25T15:25:51.929Z" }, - { url = "https://files.pythonhosted.org/packages/11/71/c219ce6d4b5205c77405af7f2de2511ad4eeffbfeb77a422151e893de0ea/onnx-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddf14a3d32234f23e44abb73a755cb96a423fac7f004e8f046f36b10214151ee", size = 15922224, upload-time = "2024-03-25T15:25:55.049Z" }, - { url = "https://files.pythonhosted.org/packages/8e/a4/554a6e5741b42406c5b1970d04685d7f2012019d4178408ed4b3ec953033/onnx-1.16.0-cp312-cp312-win32.whl", hash = "sha256:62a2e27ae8ba5fc9b4a2620301446a517b5ffaaf8566611de7a7c2160f5bcf4c", size = 14336234, upload-time = "2024-03-25T15:25:57.998Z" }, - { url = "https://files.pythonhosted.org/packages/e9/a1/8aecec497010ad34e7656408df1868d94483c5c56bc991f4088c06150896/onnx-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:3e0860fea94efde777e81a6f68f65761ed5e5f3adea2e050d7fbe373a9ae05b3", size = 14436591, upload-time = "2024-03-25T15:26:01.252Z" }, + { url = "https://files.pythonhosted.org/packages/c8/0b/f4705e4a3fa6fd0de971302fdae17ad176b024eca8c24360f0e37c00f9df/onnx-1.16.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:9eadbdce25b19d6216f426d6d99b8bc877a65ed92cbef9707751c6669190ba4f", size = 16514483 }, + { url = "https://files.pythonhosted.org/packages/b8/1c/50310a559857951fc6e069cf5d89deebe34287997d1c5928bca435456f62/onnx-1.16.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:034ae21a2aaa2e9c14119a840d2926d213c27aad29e5e3edaa30145a745048e1", size = 15012939 }, + { url = "https://files.pythonhosted.org/packages/ef/6e/96be6692ebcd8da568084d753f386ce08efa1f99b216f346ee281edd6cc3/onnx-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec22a43d74eb1f2303373e2fbe7fbcaa45fb225f4eb146edfed1356ada7a9aea", size = 15791856 }, + { url = "https://files.pythonhosted.org/packages/49/5f/d8e1a24247f506a77cbe22341c72ca91bea3b468c5d6bca2047d885ea3c6/onnx-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:298f28a2b5ac09145fa958513d3d1e6b349ccf86a877dbdcccad57713fe360b3", size = 15922279 }, + { url = "https://files.pythonhosted.org/packages/cb/14/562e4ac22cdf41f4465e3b114ef1a9467d513eeff0b9c2285c2da5db6ed1/onnx-1.16.0-cp310-cp310-win32.whl", hash = "sha256:66300197b52beca08bc6262d43c103289c5d45fde43fb51922ed1eb83658cf0c", size = 14335703 }, + { url = "https://files.pythonhosted.org/packages/3b/e2/471ff83b3862967791d67f630000afce038756afbdf0665a3d767677c851/onnx-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:ae0029f5e47bf70a1a62e7f88c80bca4ef39b844a89910039184221775df5e43", size = 14435099 }, + { url = "https://files.pythonhosted.org/packages/a4/b8/7accf3f93eee498711f0b7f07f6e93906e031622473e85ce9cd3578f6a92/onnx-1.16.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:f51179d4af3372b4f3800c558d204b592c61e4b4a18b8f61e0eea7f46211221a", size = 16514376 }, + { url = "https://files.pythonhosted.org/packages/cc/24/a328236b594d5fea23f70a3a8139e730cb43334f0b24693831c47c9064f0/onnx-1.16.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:5202559070afec5144332db216c20f2fff8323cf7f6512b0ca11b215eacc5bf3", size = 15012839 }, + { url = "https://files.pythonhosted.org/packages/80/12/57187bab3f830a47fa65eafe4fbaef01dfdf5042cf82a41fa440fab68766/onnx-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77579e7c15b4df39d29465b216639a5f9b74026bdd9e4b6306cd19a32dcfe67c", size = 15791944 }, + { url = "https://files.pythonhosted.org/packages/df/48/63f68b65d041aedffab41eea930563ca52aab70dbaa7d4820501618c1a70/onnx-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e60ca76ac24b65c25860d0f2d2cdd96d6320d062a01dd8ce87c5743603789b8", size = 15922450 }, + { url = "https://files.pythonhosted.org/packages/08/1b/4bdf4534f5ff08973725ba5409f95bbf64e2789cd20be615880dae689973/onnx-1.16.0-cp311-cp311-win32.whl", hash = "sha256:81b4ee01bc554e8a2b11ac6439882508a5377a1c6b452acd69a1eebb83571117", size = 14335808 }, + { url = "https://files.pythonhosted.org/packages/aa/d0/0514d02d2e84e7bb48a105877eae4065e54d7dabb60d0b60214fe2677346/onnx-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:7449241e70b847b9c3eb8dae622df8c1b456d11032a9d7e26e0ee8a698d5bf86", size = 14434905 }, + { url = "https://files.pythonhosted.org/packages/42/87/577adadda30ee08041e81ef02a331ca9d1a8df93a2e4c4c53ec56fbbc2ac/onnx-1.16.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:03a627488b1a9975d95d6a55582af3e14c7f3bb87444725b999935ddd271d352", size = 16516304 }, + { url = "https://files.pythonhosted.org/packages/e3/1b/6e1ea37e081cc49a28f0e4d3830b4c8525081354cf9f5529c6c92268fc77/onnx-1.16.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:c392faeabd9283ee344ccb4b067d1fea9dfc614fa1f0de7c47589efd79e15e78", size = 15016538 }, + { url = "https://files.pythonhosted.org/packages/6d/07/f8fefd5eb0984be42ef677f0b7db7527edc4529224a34a3c31f7b12ec80d/onnx-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0efeb46985de08f0efe758cb54ad3457e821a05c2eaf5ba2ccb8cd1602c08084", size = 15790415 }, + { url = "https://files.pythonhosted.org/packages/11/71/c219ce6d4b5205c77405af7f2de2511ad4eeffbfeb77a422151e893de0ea/onnx-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddf14a3d32234f23e44abb73a755cb96a423fac7f004e8f046f36b10214151ee", size = 15922224 }, + { url = "https://files.pythonhosted.org/packages/8e/a4/554a6e5741b42406c5b1970d04685d7f2012019d4178408ed4b3ec953033/onnx-1.16.0-cp312-cp312-win32.whl", hash = "sha256:62a2e27ae8ba5fc9b4a2620301446a517b5ffaaf8566611de7a7c2160f5bcf4c", size = 14336234 }, + { url = "https://files.pythonhosted.org/packages/e9/a1/8aecec497010ad34e7656408df1868d94483c5c56bc991f4088c06150896/onnx-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:3e0860fea94efde777e81a6f68f65761ed5e5f3adea2e050d7fbe373a9ae05b3", size = 14436591 }, ] [[package]] name = "onnxruntime" -version = "1.22.0" +version = "1.22.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "coloredlogs" }, @@ -1545,30 +1759,30 @@ dependencies = [ { name = "sympy" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/67/3c/c99b21646a782b89c33cffd96fdee02a81bc43f0cb651de84d58ec11e30e/onnxruntime-1.22.0-cp310-cp310-macosx_13_0_universal2.whl", hash = "sha256:85d8826cc8054e4d6bf07f779dc742a363c39094015bdad6a08b3c18cfe0ba8c", size = 34273493, upload-time = "2025-05-09T20:25:55.66Z" }, - { url = "https://files.pythonhosted.org/packages/54/ab/fd9a3b5285008c060618be92e475337fcfbf8689787953d37273f7b52ab0/onnxruntime-1.22.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:468c9502a12f6f49ec335c2febd22fdceecc1e4cc96dfc27e419ba237dff5aff", size = 14445346, upload-time = "2025-05-09T20:25:41.322Z" }, - { url = "https://files.pythonhosted.org/packages/1f/ca/a5625644bc079e04e3076a5ac1fb954d1e90309b8eb987a4f800732ffee6/onnxruntime-1.22.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:681fe356d853630a898ee05f01ddb95728c9a168c9460e8361d0a240c9b7cb97", size = 16392959, upload-time = "2025-05-09T20:26:09.047Z" }, - { url = "https://files.pythonhosted.org/packages/6d/6b/8267490476e8d4dd1883632c7e46a4634384c7ff1c35ae44edc8ab0bb7a9/onnxruntime-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:20bca6495d06925631e201f2b257cc37086752e8fe7b6c83a67c6509f4759bc9", size = 12689974, upload-time = "2025-05-12T21:26:09.704Z" }, - { url = "https://files.pythonhosted.org/packages/7a/08/c008711d1b92ff1272f4fea0fbee57723171f161d42e5c680625535280af/onnxruntime-1.22.0-cp311-cp311-macosx_13_0_universal2.whl", hash = "sha256:8d6725c5b9a681d8fe72f2960c191a96c256367887d076b08466f52b4e0991df", size = 34282151, upload-time = "2025-05-09T20:25:59.246Z" }, - { url = "https://files.pythonhosted.org/packages/3e/8b/22989f6b59bc4ad1324f07a945c80b9ab825f0a581ad7a6064b93716d9b7/onnxruntime-1.22.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fef17d665a917866d1f68f09edc98223b9a27e6cb167dec69da4c66484ad12fd", size = 14446302, upload-time = "2025-05-09T20:25:44.299Z" }, - { url = "https://files.pythonhosted.org/packages/7a/d5/aa83d084d05bc8f6cf8b74b499c77431ffd6b7075c761ec48ec0c161a47f/onnxruntime-1.22.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b978aa63a9a22095479c38371a9b359d4c15173cbb164eaad5f2cd27d666aa65", size = 16393496, upload-time = "2025-05-09T20:26:11.588Z" }, - { url = "https://files.pythonhosted.org/packages/89/a5/1c6c10322201566015183b52ef011dfa932f5dd1b278de8d75c3b948411d/onnxruntime-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:03d3ef7fb11adf154149d6e767e21057e0e577b947dd3f66190b212528e1db31", size = 12691517, upload-time = "2025-05-12T21:26:13.354Z" }, - { url = "https://files.pythonhosted.org/packages/4d/de/9162872c6e502e9ac8c99a98a8738b2fab408123d11de55022ac4f92562a/onnxruntime-1.22.0-cp312-cp312-macosx_13_0_universal2.whl", hash = "sha256:f3c0380f53c1e72a41b3f4d6af2ccc01df2c17844072233442c3a7e74851ab97", size = 34298046, upload-time = "2025-05-09T20:26:02.399Z" }, - { url = "https://files.pythonhosted.org/packages/03/79/36f910cd9fc96b444b0e728bba14607016079786adf032dae61f7c63b4aa/onnxruntime-1.22.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8601128eaef79b636152aea76ae6981b7c9fc81a618f584c15d78d42b310f1c", size = 14443220, upload-time = "2025-05-09T20:25:47.078Z" }, - { url = "https://files.pythonhosted.org/packages/8c/60/16d219b8868cc8e8e51a68519873bdb9f5f24af080b62e917a13fff9989b/onnxruntime-1.22.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6964a975731afc19dc3418fad8d4e08c48920144ff590149429a5ebe0d15fb3c", size = 16406377, upload-time = "2025-05-09T20:26:14.478Z" }, - { url = "https://files.pythonhosted.org/packages/36/b4/3f1c71ce1d3d21078a6a74c5483bfa2b07e41a8d2b8fb1e9993e6a26d8d3/onnxruntime-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:c0d534a43d1264d1273c2d4f00a5a588fa98d21117a3345b7104fa0bbcaadb9a", size = 12692233, upload-time = "2025-05-12T21:26:16.963Z" }, - { url = "https://files.pythonhosted.org/packages/a9/65/5cb5018d5b0b7cba820d2c4a1d1b02d40df538d49138ba36a509457e4df6/onnxruntime-1.22.0-cp313-cp313-macosx_13_0_universal2.whl", hash = "sha256:fe7c051236aae16d8e2e9ffbfc1e115a0cc2450e873a9c4cb75c0cc96c1dae07", size = 34298715, upload-time = "2025-05-09T20:26:05.634Z" }, - { url = "https://files.pythonhosted.org/packages/e1/89/1dfe1b368831d1256b90b95cb8d11da8ab769febd5c8833ec85ec1f79d21/onnxruntime-1.22.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6a6bbed10bc5e770c04d422893d3045b81acbbadc9fb759a2cd1ca00993da919", size = 14443266, upload-time = "2025-05-09T20:25:49.479Z" }, - { url = "https://files.pythonhosted.org/packages/1e/70/342514ade3a33ad9dd505dcee96ff1f0e7be6d0e6e9c911fe0f1505abf42/onnxruntime-1.22.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9fe45ee3e756300fccfd8d61b91129a121d3d80e9d38e01f03ff1295badc32b8", size = 16406707, upload-time = "2025-05-09T20:26:17.454Z" }, - { url = "https://files.pythonhosted.org/packages/3e/89/2f64e250945fa87140fb917ba377d6d0e9122e029c8512f389a9b7f953f4/onnxruntime-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:5a31d84ef82b4b05d794a4ce8ba37b0d9deb768fd580e36e17b39e0b4840253b", size = 12691777, upload-time = "2025-05-12T21:26:20.19Z" }, - { url = "https://files.pythonhosted.org/packages/9f/48/d61d5f1ed098161edd88c56cbac49207d7b7b149e613d2cd7e33176c63b3/onnxruntime-1.22.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a2ac5bd9205d831541db4e508e586e764a74f14efdd3f89af7fd20e1bf4a1ed", size = 14454003, upload-time = "2025-05-09T20:25:52.287Z" }, - { url = "https://files.pythonhosted.org/packages/c3/16/873b955beda7bada5b0d798d3a601b2ff210e44ad5169f6d405b93892103/onnxruntime-1.22.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64845709f9e8a2809e8e009bc4c8f73b788cee9c6619b7d9930344eae4c9cd36", size = 16427482, upload-time = "2025-05-09T20:26:20.376Z" }, + { url = "https://files.pythonhosted.org/packages/76/b9/664a1ffee62fa51529fac27b37409d5d28cadee8d97db806fcba68339b7e/onnxruntime-1.22.1-cp310-cp310-macosx_13_0_universal2.whl", hash = "sha256:80e7f51da1f5201c1379b8d6ef6170505cd800e40da216290f5e06be01aadf95", size = 34319864 }, + { url = "https://files.pythonhosted.org/packages/b9/64/bc7221e92c994931024e22b22401b962c299e991558c3d57f7e34538b4b9/onnxruntime-1.22.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89ddfdbbdaf7e3a59515dee657f6515601d55cb21a0f0f48c81aefc54ff1b73", size = 14472246 }, + { url = "https://files.pythonhosted.org/packages/84/57/901eddbfb59ac4d008822b236450d5765cafcd450c787019416f8d3baf11/onnxruntime-1.22.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bddc75868bcf6f9ed76858a632f65f7b1846bdcefc6d637b1e359c2c68609964", size = 16459905 }, + { url = "https://files.pythonhosted.org/packages/de/90/d6a1eb9b47e66a18afe7d1cf7cf0b2ef966ffa6f44d9f32d94c2be2860fb/onnxruntime-1.22.1-cp310-cp310-win_amd64.whl", hash = "sha256:01e2f21b2793eb0c8642d2be3cee34cc7d96b85f45f6615e4e220424158877ce", size = 12689001 }, + { url = "https://files.pythonhosted.org/packages/82/ff/4a1a6747e039ef29a8d4ee4510060e9a805982b6da906a3da2306b7a3be6/onnxruntime-1.22.1-cp311-cp311-macosx_13_0_universal2.whl", hash = "sha256:f4581bccb786da68725d8eac7c63a8f31a89116b8761ff8b4989dc58b61d49a0", size = 34324148 }, + { url = "https://files.pythonhosted.org/packages/0b/05/9f1929723f1cca8c9fb1b2b97ac54ce61362c7201434d38053ea36ee4225/onnxruntime-1.22.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7ae7526cf10f93454beb0f751e78e5cb7619e3b92f9fc3bd51aa6f3b7a8977e5", size = 14473779 }, + { url = "https://files.pythonhosted.org/packages/59/f3/c93eb4167d4f36ea947930f82850231f7ce0900cb00e1a53dc4995b60479/onnxruntime-1.22.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f6effa1299ac549a05c784d50292e3378dbbf010346ded67400193b09ddc2f04", size = 16460799 }, + { url = "https://files.pythonhosted.org/packages/a8/01/e536397b03e4462d3260aee5387e6f606c8fa9d2b20b1728f988c3c72891/onnxruntime-1.22.1-cp311-cp311-win_amd64.whl", hash = "sha256:f28a42bb322b4ca6d255531bb334a2b3e21f172e37c1741bd5e66bc4b7b61f03", size = 12689881 }, + { url = "https://files.pythonhosted.org/packages/48/70/ca2a4d38a5deccd98caa145581becb20c53684f451e89eb3a39915620066/onnxruntime-1.22.1-cp312-cp312-macosx_13_0_universal2.whl", hash = "sha256:a938d11c0dc811badf78e435daa3899d9af38abee950d87f3ab7430eb5b3cf5a", size = 34342883 }, + { url = "https://files.pythonhosted.org/packages/29/e5/00b099b4d4f6223b610421080d0eed9327ef9986785c9141819bbba0d396/onnxruntime-1.22.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:984cea2a02fcc5dfea44ade9aca9fe0f7a8a2cd6f77c258fc4388238618f3928", size = 14473861 }, + { url = "https://files.pythonhosted.org/packages/0a/50/519828a5292a6ccd8d5cd6d2f72c6b36ea528a2ef68eca69647732539ffa/onnxruntime-1.22.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2d39a530aff1ec8d02e365f35e503193991417788641b184f5b1e8c9a6d5ce8d", size = 16475713 }, + { url = "https://files.pythonhosted.org/packages/5d/54/7139d463bb0a312890c9a5db87d7815d4a8cce9e6f5f28d04f0b55fcb160/onnxruntime-1.22.1-cp312-cp312-win_amd64.whl", hash = "sha256:6a64291d57ea966a245f749eb970f4fa05a64d26672e05a83fdb5db6b7d62f87", size = 12690910 }, + { url = "https://files.pythonhosted.org/packages/e0/39/77cefa829740bd830915095d8408dce6d731b244e24b1f64fe3df9f18e86/onnxruntime-1.22.1-cp313-cp313-macosx_13_0_universal2.whl", hash = "sha256:d29c7d87b6cbed8fecfd09dca471832384d12a69e1ab873e5effbb94adc3e966", size = 34342026 }, + { url = "https://files.pythonhosted.org/packages/d2/a6/444291524cb52875b5de980a6e918072514df63a57a7120bf9dfae3aeed1/onnxruntime-1.22.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:460487d83b7056ba98f1f7bac80287224c31d8149b15712b0d6f5078fcc33d0f", size = 14474014 }, + { url = "https://files.pythonhosted.org/packages/87/9d/45a995437879c18beff26eacc2322f4227224d04c6ac3254dce2e8950190/onnxruntime-1.22.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b0c37070268ba4e02a1a9d28560cd00cd1e94f0d4f275cbef283854f861a65fa", size = 16475427 }, + { url = "https://files.pythonhosted.org/packages/4c/06/9c765e66ad32a7e709ce4cb6b95d7eaa9cb4d92a6e11ea97c20ffecaf765/onnxruntime-1.22.1-cp313-cp313-win_amd64.whl", hash = "sha256:70980d729145a36a05f74b573435531f55ef9503bcda81fc6c3d6b9306199982", size = 12690841 }, + { url = "https://files.pythonhosted.org/packages/52/8c/02af24ee1c8dce4e6c14a1642a7a56cebe323d2fa01d9a360a638f7e4b75/onnxruntime-1.22.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:33a7980bbc4b7f446bac26c3785652fe8730ed02617d765399e89ac7d44e0f7d", size = 14479333 }, + { url = "https://files.pythonhosted.org/packages/5d/15/d75fd66aba116ce3732bb1050401394c5ec52074c4f7ee18db8838dd4667/onnxruntime-1.22.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7e823624b015ea879d976cbef8bfaed2f7e2cc233d7506860a76dd37f8f381", size = 16477261 }, ] [[package]] name = "onnxruntime-gpu" version = "1.19.2" -source = { registry = "https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/onnxruntime-cuda-12/pypi/simple" } +source = { registry = "https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/onnxruntime-cuda-12/pypi/simple/" } dependencies = [ { name = "coloredlogs" }, { name = "flatbuffers" }, @@ -1599,10 +1813,10 @@ dependencies = [ { name = "sympy" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/57/e9a080f2477b2a4c16925f766e4615fc545098b0f4e20cf8ad803e7a9672/onnxruntime_openvino-1.18.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:565b874d21bcd48126da7d62f57db019f5ec0e1f82ae9b0740afa2ad91f8d331", size = 41971800, upload-time = "2024-06-25T06:30:37.042Z" }, - { url = "https://files.pythonhosted.org/packages/34/7d/b75913bce58f4ee9bf6a02d1b513b9fc82303a496ec698e6fb1f9d597cb4/onnxruntime_openvino-1.18.0-cp310-cp310-win_amd64.whl", hash = "sha256:7f1931060f710a6c8e32121bb73044c4772ef5925802fc8776d3fe1e87ab3f75", size = 5963263, upload-time = "2024-06-24T13:38:15.906Z" }, - { url = "https://files.pythonhosted.org/packages/7e/d3/8299b7285dc8fa7bd986b6f0d7c50b7f0fd13db50dd3b88b93ec269b1e08/onnxruntime_openvino-1.18.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:eb1723d386f70a8e26398d983ebe35d2c25ba56e9cdb382670ebbf1f5139f8ba", size = 41971927, upload-time = "2024-06-25T06:30:43.765Z" }, - { url = "https://files.pythonhosted.org/packages/88/d9/ca0bfd7ed37153d9664ccdcfb4d0e5b1963563553b05cb4338b46968feb2/onnxruntime_openvino-1.18.0-cp311-cp311-win_amd64.whl", hash = "sha256:874a1e263dd86674593e5a879257650b06a8609c4d5768c3d8ed8dc4ae874b9c", size = 5963464, upload-time = "2024-06-24T13:38:18.437Z" }, + { url = "https://files.pythonhosted.org/packages/b3/57/e9a080f2477b2a4c16925f766e4615fc545098b0f4e20cf8ad803e7a9672/onnxruntime_openvino-1.18.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:565b874d21bcd48126da7d62f57db019f5ec0e1f82ae9b0740afa2ad91f8d331", size = 41971800 }, + { url = "https://files.pythonhosted.org/packages/34/7d/b75913bce58f4ee9bf6a02d1b513b9fc82303a496ec698e6fb1f9d597cb4/onnxruntime_openvino-1.18.0-cp310-cp310-win_amd64.whl", hash = "sha256:7f1931060f710a6c8e32121bb73044c4772ef5925802fc8776d3fe1e87ab3f75", size = 5963263 }, + { url = "https://files.pythonhosted.org/packages/7e/d3/8299b7285dc8fa7bd986b6f0d7c50b7f0fd13db50dd3b88b93ec269b1e08/onnxruntime_openvino-1.18.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:eb1723d386f70a8e26398d983ebe35d2c25ba56e9cdb382670ebbf1f5139f8ba", size = 41971927 }, + { url = "https://files.pythonhosted.org/packages/88/d9/ca0bfd7ed37153d9664ccdcfb4d0e5b1963563553b05cb4338b46968feb2/onnxruntime_openvino-1.18.0-cp311-cp311-win_amd64.whl", hash = "sha256:874a1e263dd86674593e5a879257650b06a8609c4d5768c3d8ed8dc4ae874b9c", size = 5963464 }, ] [[package]] @@ -1612,175 +1826,186 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/36/2f/5b2b3ba52c864848885ba988f24b7f105052f68da9ab0e693cc7c25b0b30/opencv-python-headless-4.11.0.86.tar.gz", hash = "sha256:996eb282ca4b43ec6a3972414de0e2331f5d9cda2b41091a49739c19fb843798", size = 95177929, upload-time = "2025-01-16T13:53:40.22Z" } +sdist = { url = "https://files.pythonhosted.org/packages/36/2f/5b2b3ba52c864848885ba988f24b7f105052f68da9ab0e693cc7c25b0b30/opencv-python-headless-4.11.0.86.tar.gz", hash = "sha256:996eb282ca4b43ec6a3972414de0e2331f5d9cda2b41091a49739c19fb843798", size = 95177929 } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/53/2c50afa0b1e05ecdb4603818e85f7d174e683d874ef63a6abe3ac92220c8/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:48128188ade4a7e517237c8e1e11a9cdf5c282761473383e77beb875bb1e61ca", size = 37326460, upload-time = "2025-01-16T13:52:57.015Z" }, - { url = "https://files.pythonhosted.org/packages/3b/43/68555327df94bb9b59a1fd645f63fafb0762515344d2046698762fc19d58/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_x86_64.whl", hash = "sha256:a66c1b286a9de872c343ee7c3553b084244299714ebb50fbdcd76f07ebbe6c81", size = 56723330, upload-time = "2025-01-16T13:55:45.731Z" }, - { url = "https://files.pythonhosted.org/packages/45/be/1438ce43ebe65317344a87e4b150865c5585f4c0db880a34cdae5ac46881/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6efabcaa9df731f29e5ea9051776715b1bdd1845d7c9530065c7951d2a2899eb", size = 29487060, upload-time = "2025-01-16T13:51:59.625Z" }, - { url = "https://files.pythonhosted.org/packages/dd/5c/c139a7876099916879609372bfa513b7f1257f7f1a908b0bdc1c2328241b/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e0a27c19dd1f40ddff94976cfe43066fbbe9dfbb2ec1907d66c19caef42a57b", size = 49969856, upload-time = "2025-01-16T13:53:29.654Z" }, - { url = "https://files.pythonhosted.org/packages/95/dd/ed1191c9dc91abcc9f752b499b7928aacabf10567bb2c2535944d848af18/opencv_python_headless-4.11.0.86-cp37-abi3-win32.whl", hash = "sha256:f447d8acbb0b6f2808da71fddd29c1cdd448d2bc98f72d9bb78a7a898fc9621b", size = 29324425, upload-time = "2025-01-16T13:52:49.048Z" }, - { url = "https://files.pythonhosted.org/packages/86/8a/69176a64335aed183529207ba8bc3d329c2999d852b4f3818027203f50e6/opencv_python_headless-4.11.0.86-cp37-abi3-win_amd64.whl", hash = "sha256:6c304df9caa7a6a5710b91709dd4786bf20a74d57672b3c31f7033cc638174ca", size = 39402386, upload-time = "2025-01-16T13:52:56.418Z" }, + { url = "https://files.pythonhosted.org/packages/dc/53/2c50afa0b1e05ecdb4603818e85f7d174e683d874ef63a6abe3ac92220c8/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:48128188ade4a7e517237c8e1e11a9cdf5c282761473383e77beb875bb1e61ca", size = 37326460 }, + { url = "https://files.pythonhosted.org/packages/3b/43/68555327df94bb9b59a1fd645f63fafb0762515344d2046698762fc19d58/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_x86_64.whl", hash = "sha256:a66c1b286a9de872c343ee7c3553b084244299714ebb50fbdcd76f07ebbe6c81", size = 56723330 }, + { url = "https://files.pythonhosted.org/packages/45/be/1438ce43ebe65317344a87e4b150865c5585f4c0db880a34cdae5ac46881/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6efabcaa9df731f29e5ea9051776715b1bdd1845d7c9530065c7951d2a2899eb", size = 29487060 }, + { url = "https://files.pythonhosted.org/packages/dd/5c/c139a7876099916879609372bfa513b7f1257f7f1a908b0bdc1c2328241b/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e0a27c19dd1f40ddff94976cfe43066fbbe9dfbb2ec1907d66c19caef42a57b", size = 49969856 }, + { url = "https://files.pythonhosted.org/packages/95/dd/ed1191c9dc91abcc9f752b499b7928aacabf10567bb2c2535944d848af18/opencv_python_headless-4.11.0.86-cp37-abi3-win32.whl", hash = "sha256:f447d8acbb0b6f2808da71fddd29c1cdd448d2bc98f72d9bb78a7a898fc9621b", size = 29324425 }, + { url = "https://files.pythonhosted.org/packages/86/8a/69176a64335aed183529207ba8bc3d329c2999d852b4f3818027203f50e6/opencv_python_headless-4.11.0.86-cp37-abi3-win_amd64.whl", hash = "sha256:6c304df9caa7a6a5710b91709dd4786bf20a74d57672b3c31f7033cc638174ca", size = 39402386 }, ] [[package]] name = "orjson" -version = "3.10.18" +version = "3.11.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/81/0b/fea456a3ffe74e70ba30e01ec183a9b26bec4d497f61dcfce1b601059c60/orjson-3.10.18.tar.gz", hash = "sha256:e8da3947d92123eda795b68228cafe2724815621fe35e8e320a9e9593a4bcd53", size = 5422810, upload-time = "2025-04-29T23:30:08.423Z" } +sdist = { url = "https://files.pythonhosted.org/packages/19/3b/fd9ff8ff64ae3900f11554d5cfc835fb73e501e043c420ad32ec574fe27f/orjson-3.11.1.tar.gz", hash = "sha256:48d82770a5fd88778063604c566f9c7c71820270c9cc9338d25147cbf34afd96", size = 5393373 } wheels = [ - { url = "https://files.pythonhosted.org/packages/27/16/2ceb9fb7bc2b11b1e4a3ea27794256e93dee2309ebe297fd131a778cd150/orjson-3.10.18-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a45e5d68066b408e4bc383b6e4ef05e717c65219a9e1390abc6155a520cac402", size = 248927, upload-time = "2025-04-29T23:28:08.643Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e1/d3c0a2bba5b9906badd121da449295062b289236c39c3a7801f92c4682b0/orjson-3.10.18-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be3b9b143e8b9db05368b13b04c84d37544ec85bb97237b3a923f076265ec89c", size = 136995, upload-time = "2025-04-29T23:28:11.503Z" }, - { url = "https://files.pythonhosted.org/packages/d7/51/698dd65e94f153ee5ecb2586c89702c9e9d12f165a63e74eb9ea1299f4e1/orjson-3.10.18-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9b0aa09745e2c9b3bf779b096fa71d1cc2d801a604ef6dd79c8b1bfef52b2f92", size = 132893, upload-time = "2025-04-29T23:28:12.751Z" }, - { url = "https://files.pythonhosted.org/packages/b3/e5/155ce5a2c43a85e790fcf8b985400138ce5369f24ee6770378ee6b691036/orjson-3.10.18-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53a245c104d2792e65c8d225158f2b8262749ffe64bc7755b00024757d957a13", size = 137017, upload-time = "2025-04-29T23:28:14.498Z" }, - { url = "https://files.pythonhosted.org/packages/46/bb/6141ec3beac3125c0b07375aee01b5124989907d61c72c7636136e4bd03e/orjson-3.10.18-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f9495ab2611b7f8a0a8a505bcb0f0cbdb5469caafe17b0e404c3c746f9900469", size = 138290, upload-time = "2025-04-29T23:28:16.211Z" }, - { url = "https://files.pythonhosted.org/packages/77/36/6961eca0b66b7809d33c4ca58c6bd4c23a1b914fb23aba2fa2883f791434/orjson-3.10.18-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73be1cbcebadeabdbc468f82b087df435843c809cd079a565fb16f0f3b23238f", size = 142828, upload-time = "2025-04-29T23:28:18.065Z" }, - { url = "https://files.pythonhosted.org/packages/8b/2f/0c646d5fd689d3be94f4d83fa9435a6c4322c9b8533edbb3cd4bc8c5f69a/orjson-3.10.18-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe8936ee2679e38903df158037a2f1c108129dee218975122e37847fb1d4ac68", size = 132806, upload-time = "2025-04-29T23:28:19.782Z" }, - { url = "https://files.pythonhosted.org/packages/ea/af/65907b40c74ef4c3674ef2bcfa311c695eb934710459841b3c2da212215c/orjson-3.10.18-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7115fcbc8525c74e4c2b608129bef740198e9a120ae46184dac7683191042056", size = 135005, upload-time = "2025-04-29T23:28:21.367Z" }, - { url = "https://files.pythonhosted.org/packages/c7/d1/68bd20ac6a32cd1f1b10d23e7cc58ee1e730e80624e3031d77067d7150fc/orjson-3.10.18-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:771474ad34c66bc4d1c01f645f150048030694ea5b2709b87d3bda273ffe505d", size = 413418, upload-time = "2025-04-29T23:28:23.097Z" }, - { url = "https://files.pythonhosted.org/packages/31/31/c701ec0bcc3e80e5cb6e319c628ef7b768aaa24b0f3b4c599df2eaacfa24/orjson-3.10.18-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7c14047dbbea52886dd87169f21939af5d55143dad22d10db6a7514f058156a8", size = 153288, upload-time = "2025-04-29T23:28:25.02Z" }, - { url = "https://files.pythonhosted.org/packages/d9/31/5e1aa99a10893a43cfc58009f9da840990cc8a9ebb75aa452210ba18587e/orjson-3.10.18-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:641481b73baec8db14fdf58f8967e52dc8bda1f2aba3aa5f5c1b07ed6df50b7f", size = 137181, upload-time = "2025-04-29T23:28:26.318Z" }, - { url = "https://files.pythonhosted.org/packages/bf/8c/daba0ac1b8690011d9242a0f37235f7d17df6d0ad941021048523b76674e/orjson-3.10.18-cp310-cp310-win32.whl", hash = "sha256:607eb3ae0909d47280c1fc657c4284c34b785bae371d007595633f4b1a2bbe06", size = 142694, upload-time = "2025-04-29T23:28:28.092Z" }, - { url = "https://files.pythonhosted.org/packages/16/62/8b687724143286b63e1d0fab3ad4214d54566d80b0ba9d67c26aaf28a2f8/orjson-3.10.18-cp310-cp310-win_amd64.whl", hash = "sha256:8770432524ce0eca50b7efc2a9a5f486ee0113a5fbb4231526d414e6254eba92", size = 134600, upload-time = "2025-04-29T23:28:29.422Z" }, - { url = "https://files.pythonhosted.org/packages/97/c7/c54a948ce9a4278794f669a353551ce7db4ffb656c69a6e1f2264d563e50/orjson-3.10.18-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e0a183ac3b8e40471e8d843105da6fbe7c070faab023be3b08188ee3f85719b8", size = 248929, upload-time = "2025-04-29T23:28:30.716Z" }, - { url = "https://files.pythonhosted.org/packages/9e/60/a9c674ef1dd8ab22b5b10f9300e7e70444d4e3cda4b8258d6c2488c32143/orjson-3.10.18-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:5ef7c164d9174362f85238d0cd4afdeeb89d9e523e4651add6a5d458d6f7d42d", size = 133364, upload-time = "2025-04-29T23:28:32.392Z" }, - { url = "https://files.pythonhosted.org/packages/c1/4e/f7d1bdd983082216e414e6d7ef897b0c2957f99c545826c06f371d52337e/orjson-3.10.18-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afd14c5d99cdc7bf93f22b12ec3b294931518aa019e2a147e8aa2f31fd3240f7", size = 136995, upload-time = "2025-04-29T23:28:34.024Z" }, - { url = "https://files.pythonhosted.org/packages/17/89/46b9181ba0ea251c9243b0c8ce29ff7c9796fa943806a9c8b02592fce8ea/orjson-3.10.18-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7b672502323b6cd133c4af6b79e3bea36bad2d16bca6c1f645903fce83909a7a", size = 132894, upload-time = "2025-04-29T23:28:35.318Z" }, - { url = "https://files.pythonhosted.org/packages/ca/dd/7bce6fcc5b8c21aef59ba3c67f2166f0a1a9b0317dcca4a9d5bd7934ecfd/orjson-3.10.18-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51f8c63be6e070ec894c629186b1c0fe798662b8687f3d9fdfa5e401c6bd7679", size = 137016, upload-time = "2025-04-29T23:28:36.674Z" }, - { url = "https://files.pythonhosted.org/packages/1c/4a/b8aea1c83af805dcd31c1f03c95aabb3e19a016b2a4645dd822c5686e94d/orjson-3.10.18-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f9478ade5313d724e0495d167083c6f3be0dd2f1c9c8a38db9a9e912cdaf947", size = 138290, upload-time = "2025-04-29T23:28:38.3Z" }, - { url = "https://files.pythonhosted.org/packages/36/d6/7eb05c85d987b688707f45dcf83c91abc2251e0dd9fb4f7be96514f838b1/orjson-3.10.18-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:187aefa562300a9d382b4b4eb9694806e5848b0cedf52037bb5c228c61bb66d4", size = 142829, upload-time = "2025-04-29T23:28:39.657Z" }, - { url = "https://files.pythonhosted.org/packages/d2/78/ddd3ee7873f2b5f90f016bc04062713d567435c53ecc8783aab3a4d34915/orjson-3.10.18-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da552683bc9da222379c7a01779bddd0ad39dd699dd6300abaf43eadee38334", size = 132805, upload-time = "2025-04-29T23:28:40.969Z" }, - { url = "https://files.pythonhosted.org/packages/8c/09/c8e047f73d2c5d21ead9c180203e111cddeffc0848d5f0f974e346e21c8e/orjson-3.10.18-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e450885f7b47a0231979d9c49b567ed1c4e9f69240804621be87c40bc9d3cf17", size = 135008, upload-time = "2025-04-29T23:28:42.284Z" }, - { url = "https://files.pythonhosted.org/packages/0c/4b/dccbf5055ef8fb6eda542ab271955fc1f9bf0b941a058490293f8811122b/orjson-3.10.18-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5e3c9cc2ba324187cd06287ca24f65528f16dfc80add48dc99fa6c836bb3137e", size = 413419, upload-time = "2025-04-29T23:28:43.673Z" }, - { url = "https://files.pythonhosted.org/packages/8a/f3/1eac0c5e2d6d6790bd2025ebfbefcbd37f0d097103d76f9b3f9302af5a17/orjson-3.10.18-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:50ce016233ac4bfd843ac5471e232b865271d7d9d44cf9d33773bcd883ce442b", size = 153292, upload-time = "2025-04-29T23:28:45.573Z" }, - { url = "https://files.pythonhosted.org/packages/1f/b4/ef0abf64c8f1fabf98791819ab502c2c8c1dc48b786646533a93637d8999/orjson-3.10.18-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b3ceff74a8f7ffde0b2785ca749fc4e80e4315c0fd887561144059fb1c138aa7", size = 137182, upload-time = "2025-04-29T23:28:47.229Z" }, - { url = "https://files.pythonhosted.org/packages/a9/a3/6ea878e7b4a0dc5c888d0370d7752dcb23f402747d10e2257478d69b5e63/orjson-3.10.18-cp311-cp311-win32.whl", hash = "sha256:fdba703c722bd868c04702cac4cb8c6b8ff137af2623bc0ddb3b3e6a2c8996c1", size = 142695, upload-time = "2025-04-29T23:28:48.564Z" }, - { url = "https://files.pythonhosted.org/packages/79/2a/4048700a3233d562f0e90d5572a849baa18ae4e5ce4c3ba6247e4ece57b0/orjson-3.10.18-cp311-cp311-win_amd64.whl", hash = "sha256:c28082933c71ff4bc6ccc82a454a2bffcef6e1d7379756ca567c772e4fb3278a", size = 134603, upload-time = "2025-04-29T23:28:50.442Z" }, - { url = "https://files.pythonhosted.org/packages/03/45/10d934535a4993d27e1c84f1810e79ccf8b1b7418cef12151a22fe9bb1e1/orjson-3.10.18-cp311-cp311-win_arm64.whl", hash = "sha256:a6c7c391beaedd3fa63206e5c2b7b554196f14debf1ec9deb54b5d279b1b46f5", size = 131400, upload-time = "2025-04-29T23:28:51.838Z" }, - { url = "https://files.pythonhosted.org/packages/21/1a/67236da0916c1a192d5f4ccbe10ec495367a726996ceb7614eaa687112f2/orjson-3.10.18-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:50c15557afb7f6d63bc6d6348e0337a880a04eaa9cd7c9d569bcb4e760a24753", size = 249184, upload-time = "2025-04-29T23:28:53.612Z" }, - { url = "https://files.pythonhosted.org/packages/b3/bc/c7f1db3b1d094dc0c6c83ed16b161a16c214aaa77f311118a93f647b32dc/orjson-3.10.18-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:356b076f1662c9813d5fa56db7d63ccceef4c271b1fb3dd522aca291375fcf17", size = 133279, upload-time = "2025-04-29T23:28:55.055Z" }, - { url = "https://files.pythonhosted.org/packages/af/84/664657cd14cc11f0d81e80e64766c7ba5c9b7fc1ec304117878cc1b4659c/orjson-3.10.18-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:559eb40a70a7494cd5beab2d73657262a74a2c59aff2068fdba8f0424ec5b39d", size = 136799, upload-time = "2025-04-29T23:28:56.828Z" }, - { url = "https://files.pythonhosted.org/packages/9a/bb/f50039c5bb05a7ab024ed43ba25d0319e8722a0ac3babb0807e543349978/orjson-3.10.18-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f3c29eb9a81e2fbc6fd7ddcfba3e101ba92eaff455b8d602bf7511088bbc0eae", size = 132791, upload-time = "2025-04-29T23:28:58.751Z" }, - { url = "https://files.pythonhosted.org/packages/93/8c/ee74709fc072c3ee219784173ddfe46f699598a1723d9d49cbc78d66df65/orjson-3.10.18-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6612787e5b0756a171c7d81ba245ef63a3533a637c335aa7fcb8e665f4a0966f", size = 137059, upload-time = "2025-04-29T23:29:00.129Z" }, - { url = "https://files.pythonhosted.org/packages/6a/37/e6d3109ee004296c80426b5a62b47bcadd96a3deab7443e56507823588c5/orjson-3.10.18-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ac6bd7be0dcab5b702c9d43d25e70eb456dfd2e119d512447468f6405b4a69c", size = 138359, upload-time = "2025-04-29T23:29:01.704Z" }, - { url = "https://files.pythonhosted.org/packages/4f/5d/387dafae0e4691857c62bd02839a3bf3fa648eebd26185adfac58d09f207/orjson-3.10.18-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9f72f100cee8dde70100406d5c1abba515a7df926d4ed81e20a9730c062fe9ad", size = 142853, upload-time = "2025-04-29T23:29:03.576Z" }, - { url = "https://files.pythonhosted.org/packages/27/6f/875e8e282105350b9a5341c0222a13419758545ae32ad6e0fcf5f64d76aa/orjson-3.10.18-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9dca85398d6d093dd41dc0983cbf54ab8e6afd1c547b6b8a311643917fbf4e0c", size = 133131, upload-time = "2025-04-29T23:29:05.753Z" }, - { url = "https://files.pythonhosted.org/packages/48/b2/73a1f0b4790dcb1e5a45f058f4f5dcadc8a85d90137b50d6bbc6afd0ae50/orjson-3.10.18-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:22748de2a07fcc8781a70edb887abf801bb6142e6236123ff93d12d92db3d406", size = 134834, upload-time = "2025-04-29T23:29:07.35Z" }, - { url = "https://files.pythonhosted.org/packages/56/f5/7ed133a5525add9c14dbdf17d011dd82206ca6840811d32ac52a35935d19/orjson-3.10.18-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:3a83c9954a4107b9acd10291b7f12a6b29e35e8d43a414799906ea10e75438e6", size = 413368, upload-time = "2025-04-29T23:29:09.301Z" }, - { url = "https://files.pythonhosted.org/packages/11/7c/439654221ed9c3324bbac7bdf94cf06a971206b7b62327f11a52544e4982/orjson-3.10.18-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:303565c67a6c7b1f194c94632a4a39918e067bd6176a48bec697393865ce4f06", size = 153359, upload-time = "2025-04-29T23:29:10.813Z" }, - { url = "https://files.pythonhosted.org/packages/48/e7/d58074fa0cc9dd29a8fa2a6c8d5deebdfd82c6cfef72b0e4277c4017563a/orjson-3.10.18-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:86314fdb5053a2f5a5d881f03fca0219bfdf832912aa88d18676a5175c6916b5", size = 137466, upload-time = "2025-04-29T23:29:12.26Z" }, - { url = "https://files.pythonhosted.org/packages/57/4d/fe17581cf81fb70dfcef44e966aa4003360e4194d15a3f38cbffe873333a/orjson-3.10.18-cp312-cp312-win32.whl", hash = "sha256:187ec33bbec58c76dbd4066340067d9ece6e10067bb0cc074a21ae3300caa84e", size = 142683, upload-time = "2025-04-29T23:29:13.865Z" }, - { url = "https://files.pythonhosted.org/packages/e6/22/469f62d25ab5f0f3aee256ea732e72dc3aab6d73bac777bd6277955bceef/orjson-3.10.18-cp312-cp312-win_amd64.whl", hash = "sha256:f9f94cf6d3f9cd720d641f8399e390e7411487e493962213390d1ae45c7814fc", size = 134754, upload-time = "2025-04-29T23:29:15.338Z" }, - { url = "https://files.pythonhosted.org/packages/10/b0/1040c447fac5b91bc1e9c004b69ee50abb0c1ffd0d24406e1350c58a7fcb/orjson-3.10.18-cp312-cp312-win_arm64.whl", hash = "sha256:3d600be83fe4514944500fa8c2a0a77099025ec6482e8087d7659e891f23058a", size = 131218, upload-time = "2025-04-29T23:29:17.324Z" }, - { url = "https://files.pythonhosted.org/packages/04/f0/8aedb6574b68096f3be8f74c0b56d36fd94bcf47e6c7ed47a7bd1474aaa8/orjson-3.10.18-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:69c34b9441b863175cc6a01f2935de994025e773f814412030f269da4f7be147", size = 249087, upload-time = "2025-04-29T23:29:19.083Z" }, - { url = "https://files.pythonhosted.org/packages/bc/f7/7118f965541aeac6844fcb18d6988e111ac0d349c9b80cda53583e758908/orjson-3.10.18-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:1ebeda919725f9dbdb269f59bc94f861afbe2a27dce5608cdba2d92772364d1c", size = 133273, upload-time = "2025-04-29T23:29:20.602Z" }, - { url = "https://files.pythonhosted.org/packages/fb/d9/839637cc06eaf528dd8127b36004247bf56e064501f68df9ee6fd56a88ee/orjson-3.10.18-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5adf5f4eed520a4959d29ea80192fa626ab9a20b2ea13f8f6dc58644f6927103", size = 136779, upload-time = "2025-04-29T23:29:22.062Z" }, - { url = "https://files.pythonhosted.org/packages/2b/6d/f226ecfef31a1f0e7d6bf9a31a0bbaf384c7cbe3fce49cc9c2acc51f902a/orjson-3.10.18-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7592bb48a214e18cd670974f289520f12b7aed1fa0b2e2616b8ed9e069e08595", size = 132811, upload-time = "2025-04-29T23:29:23.602Z" }, - { url = "https://files.pythonhosted.org/packages/73/2d/371513d04143c85b681cf8f3bce743656eb5b640cb1f461dad750ac4b4d4/orjson-3.10.18-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f872bef9f042734110642b7a11937440797ace8c87527de25e0c53558b579ccc", size = 137018, upload-time = "2025-04-29T23:29:25.094Z" }, - { url = "https://files.pythonhosted.org/packages/69/cb/a4d37a30507b7a59bdc484e4a3253c8141bf756d4e13fcc1da760a0b00cb/orjson-3.10.18-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0315317601149c244cb3ecef246ef5861a64824ccbcb8018d32c66a60a84ffbc", size = 138368, upload-time = "2025-04-29T23:29:26.609Z" }, - { url = "https://files.pythonhosted.org/packages/1e/ae/cd10883c48d912d216d541eb3db8b2433415fde67f620afe6f311f5cd2ca/orjson-3.10.18-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0da26957e77e9e55a6c2ce2e7182a36a6f6b180ab7189315cb0995ec362e049", size = 142840, upload-time = "2025-04-29T23:29:28.153Z" }, - { url = "https://files.pythonhosted.org/packages/6d/4c/2bda09855c6b5f2c055034c9eda1529967b042ff8d81a05005115c4e6772/orjson-3.10.18-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb70d489bc79b7519e5803e2cc4c72343c9dc1154258adf2f8925d0b60da7c58", size = 133135, upload-time = "2025-04-29T23:29:29.726Z" }, - { url = "https://files.pythonhosted.org/packages/13/4a/35971fd809a8896731930a80dfff0b8ff48eeb5d8b57bb4d0d525160017f/orjson-3.10.18-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e9e86a6af31b92299b00736c89caf63816f70a4001e750bda179e15564d7a034", size = 134810, upload-time = "2025-04-29T23:29:31.269Z" }, - { url = "https://files.pythonhosted.org/packages/99/70/0fa9e6310cda98365629182486ff37a1c6578e34c33992df271a476ea1cd/orjson-3.10.18-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:c382a5c0b5931a5fc5405053d36c1ce3fd561694738626c77ae0b1dfc0242ca1", size = 413491, upload-time = "2025-04-29T23:29:33.315Z" }, - { url = "https://files.pythonhosted.org/packages/32/cb/990a0e88498babddb74fb97855ae4fbd22a82960e9b06eab5775cac435da/orjson-3.10.18-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8e4b2ae732431127171b875cb2668f883e1234711d3c147ffd69fe5be51a8012", size = 153277, upload-time = "2025-04-29T23:29:34.946Z" }, - { url = "https://files.pythonhosted.org/packages/92/44/473248c3305bf782a384ed50dd8bc2d3cde1543d107138fd99b707480ca1/orjson-3.10.18-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d808e34ddb24fc29a4d4041dcfafbae13e129c93509b847b14432717d94b44f", size = 137367, upload-time = "2025-04-29T23:29:36.52Z" }, - { url = "https://files.pythonhosted.org/packages/ad/fd/7f1d3edd4ffcd944a6a40e9f88af2197b619c931ac4d3cfba4798d4d3815/orjson-3.10.18-cp313-cp313-win32.whl", hash = "sha256:ad8eacbb5d904d5591f27dee4031e2c1db43d559edb8f91778efd642d70e6bea", size = 142687, upload-time = "2025-04-29T23:29:38.292Z" }, - { url = "https://files.pythonhosted.org/packages/4b/03/c75c6ad46be41c16f4cfe0352a2d1450546f3c09ad2c9d341110cd87b025/orjson-3.10.18-cp313-cp313-win_amd64.whl", hash = "sha256:aed411bcb68bf62e85588f2a7e03a6082cc42e5a2796e06e72a962d7c6310b52", size = 134794, upload-time = "2025-04-29T23:29:40.349Z" }, - { url = "https://files.pythonhosted.org/packages/c2/28/f53038a5a72cc4fd0b56c1eafb4ef64aec9685460d5ac34de98ca78b6e29/orjson-3.10.18-cp313-cp313-win_arm64.whl", hash = "sha256:f54c1385a0e6aba2f15a40d703b858bedad36ded0491e55d35d905b2c34a4cc3", size = 131186, upload-time = "2025-04-29T23:29:41.922Z" }, + { url = "https://files.pythonhosted.org/packages/94/8b/7dd88f416e2e5834fd9809d871f471aae7d12dfd83d4786166fa5a926601/orjson-3.11.1-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:92d771c492b64119456afb50f2dff3e03a2db8b5af0eba32c5932d306f970532", size = 241312 }, + { url = "https://files.pythonhosted.org/packages/f3/5d/5bfc371bd010ffbec90e64338aa59abcb13ed94191112199048653ee2f34/orjson-3.11.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0085ef83a4141c2ed23bfec5fecbfdb1e95dd42fc8e8c76057bdeeec1608ea65", size = 132791 }, + { url = "https://files.pythonhosted.org/packages/48/e2/c07854a6bad71e4249345efadb686c0aff250073bdab8ba9be7626af6516/orjson-3.11.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5caf7f13f2e1b4e137060aed892d4541d07dabc3f29e6d891e2383c7ed483440", size = 128690 }, + { url = "https://files.pythonhosted.org/packages/48/e4/2e075348e7772aa1404d51d8df25ff4d6ee3daf682732cb21308e3b59c32/orjson-3.11.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f716bcc166524eddfcf9f13f8209ac19a7f27b05cf591e883419079d98c8c99d", size = 130646 }, + { url = "https://files.pythonhosted.org/packages/97/09/50daacd3ac7ae564186924c8d1121940f2c78c64d6804dbe81dd735ab087/orjson-3.11.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:507d6012fab05465d8bf21f5d7f4635ba4b6d60132874e349beff12fb51af7fe", size = 132620 }, + { url = "https://files.pythonhosted.org/packages/da/21/5f22093fa90e6d6fcf8111942b530a4ad19ee1cc0b06ddad4a63b16ab852/orjson-3.11.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b1545083b0931f754c80fd2422a73d83bea7a6d1b6de104a5f2c8dd3d64c291e", size = 135121 }, + { url = "https://files.pythonhosted.org/packages/48/90/77ad4bfa6bd400a3d241695e3e39975e32fe027aea5cb0b171bd2080c427/orjson-3.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e217ce3bad76351e1eb29ebe5ca630326f45cd2141f62620107a229909501a3", size = 131131 }, + { url = "https://files.pythonhosted.org/packages/5a/64/d383675229f7ffd971b6ec6cdd3016b00877bb6b2d5fc1fd099c2ec2ad57/orjson-3.11.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06ef26e009304bda4df42e4afe518994cde6f89b4b04c0ff24021064f83f4fbb", size = 131025 }, + { url = "https://files.pythonhosted.org/packages/d4/82/e4017d8d98597f6056afaf75021ff390154d1e2722c66ba45a4d50f82606/orjson-3.11.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:ba49683b87bea3ae1489a88e766e767d4f423a669a61270b6d6a7ead1c33bd65", size = 404464 }, + { url = "https://files.pythonhosted.org/packages/77/7e/45c7f813c30d386c0168a32ce703494262458af6b222a3eeac1c0bb88822/orjson-3.11.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5072488fcc5cbcda2ece966d248e43ea1d222e19dd4c56d3f82747777f24d864", size = 146416 }, + { url = "https://files.pythonhosted.org/packages/41/71/6ccb4d7875ec3349409960769a28349f477856f05de9fd961454c2b99230/orjson-3.11.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f58ae2bcd119226fe4aa934b5880fe57b8e97b69e51d5d91c88a89477a307016", size = 135497 }, + { url = "https://files.pythonhosted.org/packages/2c/ce/df8dac7da075962fdbfca55d53e3601aa910c9f23606033bf0f084835720/orjson-3.11.1-cp310-cp310-win32.whl", hash = "sha256:6723be919c07906781b9c63cc52dc7d2fb101336c99dd7e85d3531d73fb493f7", size = 136807 }, + { url = "https://files.pythonhosted.org/packages/7b/a0/f6c2be24709d1742d878b4530fa0c3f4a5e190d51397b680abbf44d11dbf/orjson-3.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:5fd44d69ddfdfb4e8d0d83f09d27a4db34930fba153fbf79f8d4ae8b47914e04", size = 131561 }, + { url = "https://files.pythonhosted.org/packages/a5/92/7ab270b5b3df8d5b0d3e572ddf2f03c9f6a79726338badf1ec8594e1469d/orjson-3.11.1-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:15e2a57ce3b57c1a36acffcc02e823afefceee0a532180c2568c62213c98e3ef", size = 240918 }, + { url = "https://files.pythonhosted.org/packages/80/41/df44684cfbd2e2e03bf9b09fdb14b7abcfff267998790b6acfb69ad435f0/orjson-3.11.1-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:17040a83ecaa130474af05bbb59a13cfeb2157d76385556041f945da936b1afd", size = 129386 }, + { url = "https://files.pythonhosted.org/packages/c1/08/958f56edd18ba1827ad0c74b2b41a7ae0864718adee8ccb5d1a5528f8761/orjson-3.11.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a68f23f09e5626cc0867a96cf618f68b91acb4753d33a80bf16111fd7f9928c", size = 132508 }, + { url = "https://files.pythonhosted.org/packages/cc/b6/5e56e189dacbf51e53ba8150c20e61ee746f6d57b697f5c52315ffc88a83/orjson-3.11.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47e07528bb6ccbd6e32a55e330979048b59bfc5518b47c89bc7ab9e3de15174a", size = 128501 }, + { url = "https://files.pythonhosted.org/packages/fe/de/f6c301a514f5934405fd4b8f3d3efc758c911d06c3de3f4be1e30d675fa4/orjson-3.11.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3807cce72bf40a9d251d689cbec28d2efd27e0f6673709f948f971afd52cb09", size = 130465 }, + { url = "https://files.pythonhosted.org/packages/47/08/f7dbaab87d6f05eebff2d7b8e6a8ed5f13b2fe3e3ae49472b527d03dbd7a/orjson-3.11.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b2dc7e88da4ca201c940f5e6127998d9e89aa64264292334dad62854bc7fc27", size = 132416 }, + { url = "https://files.pythonhosted.org/packages/43/3f/dd5a185273b7ba6aa238cfc67bf9edaa1885ae51ce942bc1a71d0f99f574/orjson-3.11.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3091dad33ac9e67c0a550cfff8ad5be156e2614d6f5d2a9247df0627751a1495", size = 134924 }, + { url = "https://files.pythonhosted.org/packages/db/ef/729d23510eaa81f0ce9d938d99d72dcf5e4ed3609d9d0bcf9c8a282cc41a/orjson-3.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ed0fce2307843b79a0c83de49f65b86197f1e2310de07af9db2a1a77a61ce4c", size = 130938 }, + { url = "https://files.pythonhosted.org/packages/82/96/120feb6807f9e1f4c68fc842a0f227db8575eafb1a41b2537567b91c19d8/orjson-3.11.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5a31e84782a18c30abd56774c0cfa7b9884589f4d37d9acabfa0504dad59bb9d", size = 130811 }, + { url = "https://files.pythonhosted.org/packages/89/66/4695e946a453fa22ff945da4b1ed0691b3f4ec86b828d398288db4a0ff79/orjson-3.11.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:26b6c821abf1ae515fbb8e140a2406c9f9004f3e52acb780b3dee9bfffddbd84", size = 404272 }, + { url = "https://files.pythonhosted.org/packages/cd/7b/1c953e2c9e55af126c6cb678a30796deb46d7713abdeb706b8765929464c/orjson-3.11.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f857b3d134b36a8436f1e24dcb525b6b945108b30746c1b0b556200b5cb76d39", size = 146196 }, + { url = "https://files.pythonhosted.org/packages/bf/c2/bef5d3bc83f2e178592ff317e2cf7bd38ebc16b641f076ea49f27aadd1d3/orjson-3.11.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:df146f2a14116ce80f7da669785fcb411406d8e80136558b0ecda4c924b9ac55", size = 135336 }, + { url = "https://files.pythonhosted.org/packages/92/95/bc6006881ebdb4608ed900a763c3e3c6be0d24c3aadd62beb774f9464ec6/orjson-3.11.1-cp311-cp311-win32.whl", hash = "sha256:d777c57c1f86855fe5492b973f1012be776e0398571f7cc3970e9a58ecf4dc17", size = 136665 }, + { url = "https://files.pythonhosted.org/packages/59/c3/1f2b9cc0c60ea2473d386fed2df2b25ece50aeb73c798d4669aadff3061e/orjson-3.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:e9a5fd589951f02ec2fcb8d69339258bbf74b41b104c556e6d4420ea5e059313", size = 131388 }, + { url = "https://files.pythonhosted.org/packages/b0/e5/40c97e5a6b85944022fe54b463470045b8651b7bb2f1e16a95c42812bf97/orjson-3.11.1-cp311-cp311-win_arm64.whl", hash = "sha256:4cddbe41ee04fddad35d75b9cf3e3736ad0b80588280766156b94783167777af", size = 126786 }, + { url = "https://files.pythonhosted.org/packages/98/77/e55513826b712807caadb2b733eee192c1df105c6bbf0d965c253b72f124/orjson-3.11.1-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:2b7c8be96db3a977367250c6367793a3c5851a6ca4263f92f0b48d00702f9910", size = 240955 }, + { url = "https://files.pythonhosted.org/packages/c9/88/a78132dddcc9c3b80a9fa050b3516bb2c996a9d78ca6fb47c8da2a80a696/orjson-3.11.1-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:72e18088f567bd4a45db5e3196677d9ed1605e356e500c8e32dd6e303167a13d", size = 129294 }, + { url = "https://files.pythonhosted.org/packages/09/02/6591e0dcb2af6bceea96cb1b5f4b48c1445492a3ef2891ac4aa306bb6f73/orjson-3.11.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d346e2ae1ce17888f7040b65a5a4a0c9734cb20ffbd228728661e020b4c8b3a5", size = 132310 }, + { url = "https://files.pythonhosted.org/packages/e9/36/c1cfbc617bcfa4835db275d5e0fe9bbdbe561a4b53d3b2de16540ec29c50/orjson-3.11.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4bda5426ebb02ceb806a7d7ec9ba9ee5e0c93fca62375151a7b1c00bc634d06b", size = 128529 }, + { url = "https://files.pythonhosted.org/packages/7c/bd/91a156c5df3aaf1d68b2ab5be06f1969955a8d3e328d7794f4338ac1d017/orjson-3.11.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10506cebe908542c4f024861102673db534fd2e03eb9b95b30d94438fa220abf", size = 130925 }, + { url = "https://files.pythonhosted.org/packages/a3/4c/a65cc24e9a5f87c9833a50161ab97b5edbec98bec99dfbba13827549debc/orjson-3.11.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45202ee3f5494644e064c41abd1320497fb92fd31fc73af708708af664ac3b56", size = 132432 }, + { url = "https://files.pythonhosted.org/packages/2e/4d/3fc3e5d7115f4f7d01b481e29e5a79bcbcc45711a2723242787455424f40/orjson-3.11.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5adaf01b92e0402a9ac5c3ebe04effe2bbb115f0914a0a53d34ea239a746289", size = 135069 }, + { url = "https://files.pythonhosted.org/packages/dc/c6/7585aa8522af896060dc0cd7c336ba6c574ae854416811ee6642c505cc95/orjson-3.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6162a1a757a1f1f4a94bc6ffac834a3602e04ad5db022dd8395a54ed9dd51c81", size = 131045 }, + { url = "https://files.pythonhosted.org/packages/6a/4e/b8a0a943793d2708ebc39e743c943251e08ee0f3279c880aefd8e9cb0c70/orjson-3.11.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:78404206977c9f946613d3f916727c189d43193e708d760ea5d4b2087d6b0968", size = 130597 }, + { url = "https://files.pythonhosted.org/packages/72/2b/7d30e2aed2f585d5d385fb45c71d9b16ba09be58c04e8767ae6edc6c9282/orjson-3.11.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:db48f8e81072e26df6cdb0e9fff808c28597c6ac20a13d595756cf9ba1fed48a", size = 404207 }, + { url = "https://files.pythonhosted.org/packages/1b/7e/772369ec66fcbce79477f0891918309594cd00e39b67a68d4c445d2ab754/orjson-3.11.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0c1e394e67ced6bb16fea7054d99fbdd99a539cf4d446d40378d4c06e0a8548d", size = 146628 }, + { url = "https://files.pythonhosted.org/packages/b4/c8/62bdb59229d7e393ae309cef41e32cc1f0b567b21dfd0742da70efb8b40c/orjson-3.11.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e7a840752c93d4eecd1378e9bb465c3703e127b58f675cd5c620f361b6cf57a4", size = 135449 }, + { url = "https://files.pythonhosted.org/packages/02/47/1c99aa60e19f781424eabeaacd9e999eafe5b59c81ead4273b773f0f3af1/orjson-3.11.1-cp312-cp312-win32.whl", hash = "sha256:4537b0e09f45d2b74cb69c7f39ca1e62c24c0488d6bf01cd24673c74cd9596bf", size = 136653 }, + { url = "https://files.pythonhosted.org/packages/31/9a/132999929a2892ab07e916669accecc83e5bff17e11a1186b4c6f23231f0/orjson-3.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:dbee6b050062540ae404530cacec1bf25e56e8d87d8d9b610b935afeb6725cae", size = 131426 }, + { url = "https://files.pythonhosted.org/packages/9c/77/d984ee5a1ca341090902e080b187721ba5d1573a8d9759e0c540975acfb2/orjson-3.11.1-cp312-cp312-win_arm64.whl", hash = "sha256:f55e557d4248322d87c4673e085c7634039ff04b47bfc823b87149ae12bef60d", size = 126635 }, + { url = "https://files.pythonhosted.org/packages/c9/e9/880ef869e6f66279ce3a381a32afa0f34e29a94250146911eee029e56efc/orjson-3.11.1-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:53cfefe4af059e65aabe9683f76b9c88bf34b4341a77d329227c2424e0e59b0e", size = 240835 }, + { url = "https://files.pythonhosted.org/packages/f0/1f/52039ef3d03eeea21763b46bc99ebe11d9de8510c72b7b5569433084a17e/orjson-3.11.1-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:93d5abed5a6f9e1b6f9b5bf6ed4423c11932b5447c2f7281d3b64e0f26c6d064", size = 129226 }, + { url = "https://files.pythonhosted.org/packages/ee/da/59fdffc9465a760be2cd3764ef9cd5535eec8f095419f972fddb123b6d0e/orjson-3.11.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dbf06642f3db2966df504944cdd0eb68ca2717f0353bb20b20acd78109374a6", size = 132261 }, + { url = "https://files.pythonhosted.org/packages/bb/5c/8610911c7e969db7cf928c8baac4b2f1e68d314bc3057acf5ca64f758435/orjson-3.11.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dddf4e78747fa7f2188273f84562017a3c4f0824485b78372513c1681ea7a894", size = 128614 }, + { url = "https://files.pythonhosted.org/packages/f7/a1/a1db9d4310d014c90f3b7e9b72c6fb162cba82c5f46d0b345669eaebdd3a/orjson-3.11.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fa3fe8653c9f57f0e16f008e43626485b6723b84b2f741f54d1258095b655912", size = 130968 }, + { url = "https://files.pythonhosted.org/packages/56/ff/11acd1fd7c38ea7a1b5d6bf582ae3da05931bee64620995eb08fd63c77fe/orjson-3.11.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6334d2382aff975a61f6f4d1c3daf39368b887c7de08f7c16c58f485dcf7adb2", size = 132439 }, + { url = "https://files.pythonhosted.org/packages/70/f9/bb564dd9450bf8725e034a8ad7f4ae9d4710a34caf63b85ce1c0c6d40af0/orjson-3.11.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a3d0855b643f259ee0cb76fe3df4c04483354409a520a902b067c674842eb6b8", size = 135299 }, + { url = "https://files.pythonhosted.org/packages/94/bb/c8eafe6051405e241dda3691db4d9132d3c3462d1d10a17f50837dd130b4/orjson-3.11.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0eacdfeefd0a79987926476eb16e0245546bedeb8febbbbcf4b653e79257a8e4", size = 131004 }, + { url = "https://files.pythonhosted.org/packages/a2/40/bed8d7dcf1bd2df8813bf010a25f645863a2f75e8e0ebdb2b55784cf1a62/orjson-3.11.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0ed07faf9e4873518c60480325dcbc16d17c59a165532cccfb409b4cdbaeff24", size = 130583 }, + { url = "https://files.pythonhosted.org/packages/57/e7/cfa2eb803ad52d74fbb5424a429b5be164e51d23f1d853e5e037173a5c48/orjson-3.11.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:d6d308dd578ae3658f62bb9eba54801533225823cd3248c902be1ebc79b5e014", size = 404218 }, + { url = "https://files.pythonhosted.org/packages/d5/21/bc703af5bc6e9c7e18dcf4404dcc4ec305ab9bb6c82d3aee5952c0c56abf/orjson-3.11.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c4aa13ca959ba6b15c0a98d3d204b850f9dc36c08c9ce422ffb024eb30d6e058", size = 146605 }, + { url = "https://files.pythonhosted.org/packages/8f/fe/d26a0150534c4965a06f556aa68bf3c3b82999d5d7b0facd3af7b390c4af/orjson-3.11.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:be3d0653322abc9b68e5bcdaee6cfd58fcbe9973740ab222b87f4d687232ab1f", size = 135434 }, + { url = "https://files.pythonhosted.org/packages/89/b6/1cb28365f08cbcffc464f8512320c6eb6db6a653f03d66de47ea3c19385f/orjson-3.11.1-cp313-cp313-win32.whl", hash = "sha256:4dd34e7e2518de8d7834268846f8cab7204364f427c56fb2251e098da86f5092", size = 136596 }, + { url = "https://files.pythonhosted.org/packages/f9/35/7870d0d3ed843652676d84d8a6038791113eacc85237b673b925802826b8/orjson-3.11.1-cp313-cp313-win_amd64.whl", hash = "sha256:d6895d32032b6362540e6d0694b19130bb4f2ad04694002dce7d8af588ca5f77", size = 131319 }, + { url = "https://files.pythonhosted.org/packages/b7/3e/5bcd50fd865eb664d4edfdaaaff51e333593ceb5695a22c0d0a0d2b187ba/orjson-3.11.1-cp313-cp313-win_arm64.whl", hash = "sha256:bb7c36d5d3570fcbb01d24fa447a21a7fe5a41141fd88e78f7994053cc4e28f4", size = 126613 }, + { url = "https://files.pythonhosted.org/packages/61/d8/0a5cd31ed100b4e569e143cb0cddefc21f0bcb8ce284f44bca0bb0e10f3d/orjson-3.11.1-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:7b71ef394327b3d0b39f6ea7ade2ecda2731a56c6a7cbf0d6a7301203b92a89b", size = 240819 }, + { url = "https://files.pythonhosted.org/packages/b9/95/7eb2c76c92192ceca16bc81845ff100bbb93f568b4b94d914b6a4da47d61/orjson-3.11.1-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:77c0fe28ed659b62273995244ae2aa430e432c71f86e4573ab16caa2f2e3ca5e", size = 129218 }, + { url = "https://files.pythonhosted.org/packages/da/84/e6b67f301b18adbbc346882f456bea44daebbd032ba725dbd7b741e3a7f1/orjson-3.11.1-cp314-cp314-manylinux_2_34_aarch64.whl", hash = "sha256:1495692f1f1ba2467df429343388a0ed259382835922e124c0cfdd56b3d1f727", size = 132238 }, + { url = "https://files.pythonhosted.org/packages/84/78/a45a86e29d9b2f391f9d00b22da51bc4b46b86b788fd42df2c5fcf3e8005/orjson-3.11.1-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:08c6a762fca63ca4dc04f66c48ea5d2428db55839fec996890e1bfaf057b658c", size = 130998 }, + { url = "https://files.pythonhosted.org/packages/ea/8f/6eb3ee6760d93b2ce996a8529164ee1f5bafbdf64b74c7314b68db622b32/orjson-3.11.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9e26794fe3976810b2c01fda29bd9ac7c91a3c1284b29cc9a383989f7b614037", size = 130559 }, + { url = "https://files.pythonhosted.org/packages/1b/78/9572ae94bdba6813917c9387e7834224c011ea6b4530ade07d718fd31598/orjson-3.11.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4b4b4f8f0b1d3ef8dc73e55363a0ffe012a42f4e2f1a140bf559698dca39b3fa", size = 404231 }, + { url = "https://files.pythonhosted.org/packages/1f/a3/68381ad0757e084927c5ee6cfdeab1c6c89405949ee493db557e60871c4c/orjson-3.11.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:848be553ea35aa89bfefbed2e27c8a41244c862956ab8ba00dc0b27e84fd58de", size = 146658 }, + { url = "https://files.pythonhosted.org/packages/00/db/fac56acf77aab778296c3f541a3eec643266f28ecd71d6c0cba251e47655/orjson-3.11.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c964c29711a4b1df52f8d9966f015402a6cf87753a406c1c4405c407dd66fd45", size = 135443 }, + { url = "https://files.pythonhosted.org/packages/76/b1/326fa4b87426197ead61c1eec2eeb3babc9eb33b480ac1f93894e40c8c08/orjson-3.11.1-cp314-cp314-win32.whl", hash = "sha256:33aada2e6b6bc9c540d396528b91e666cedb383740fee6e6a917f561b390ecb1", size = 136643 }, + { url = "https://files.pythonhosted.org/packages/0f/8e/2987ae2109f3bfd39680f8a187d1bc09ad7f8fb019dcdc719b08c7242ade/orjson-3.11.1-cp314-cp314-win_amd64.whl", hash = "sha256:68e10fd804e44e36188b9952543e3fa22f5aa8394da1b5283ca2b423735c06e8", size = 131324 }, + { url = "https://files.pythonhosted.org/packages/21/5f/253e08e6974752b124fbf3a4de3ad53baa766b0cb4a333d47706d307e396/orjson-3.11.1-cp314-cp314-win_arm64.whl", hash = "sha256:f3cf6c07f8b32127d836be8e1c55d4f34843f7df346536da768e9f73f22078a1", size = 126605 }, ] [[package]] name = "packaging" version = "23.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fb/2b/9b9c33ffed44ee921d0967086d653047286054117d584f1b1a7c22ceaf7b/packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", size = 146714, upload-time = "2023-10-01T13:50:05.279Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fb/2b/9b9c33ffed44ee921d0967086d653047286054117d584f1b1a7c22ceaf7b/packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", size = 146714 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/1a/610693ac4ee14fcdf2d9bf3c493370e4f2ef7ae2e19217d7a237ff42367d/packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7", size = 53011, upload-time = "2023-10-01T13:50:03.745Z" }, + { url = "https://files.pythonhosted.org/packages/ec/1a/610693ac4ee14fcdf2d9bf3c493370e4f2ef7ae2e19217d7a237ff42367d/packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7", size = 53011 }, ] [[package]] name = "pathspec" version = "0.12.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 } wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 }, ] [[package]] name = "pillow" version = "10.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/74/ad3d526f3bf7b6d3f408b73fde271ec69dfac8b81341a318ce825f2b3812/pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06", size = 46555059, upload-time = "2024-07-01T09:48:43.583Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/74/ad3d526f3bf7b6d3f408b73fde271ec69dfac8b81341a318ce825f2b3812/pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06", size = 46555059 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/69/a31cccd538ca0b5272be2a38347f8839b97a14be104ea08b0db92f749c74/pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e", size = 3509271, upload-time = "2024-07-01T09:45:22.07Z" }, - { url = "https://files.pythonhosted.org/packages/9a/9e/4143b907be8ea0bce215f2ae4f7480027473f8b61fcedfda9d851082a5d2/pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d", size = 3375658, upload-time = "2024-07-01T09:45:25.292Z" }, - { url = "https://files.pythonhosted.org/packages/8a/25/1fc45761955f9359b1169aa75e241551e74ac01a09f487adaaf4c3472d11/pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856", size = 4332075, upload-time = "2024-07-01T09:45:27.94Z" }, - { url = "https://files.pythonhosted.org/packages/5e/dd/425b95d0151e1d6c951f45051112394f130df3da67363b6bc75dc4c27aba/pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f", size = 4444808, upload-time = "2024-07-01T09:45:30.305Z" }, - { url = "https://files.pythonhosted.org/packages/b1/84/9a15cc5726cbbfe7f9f90bfb11f5d028586595907cd093815ca6644932e3/pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b", size = 4356290, upload-time = "2024-07-01T09:45:32.868Z" }, - { url = "https://files.pythonhosted.org/packages/b5/5b/6651c288b08df3b8c1e2f8c1152201e0b25d240e22ddade0f1e242fc9fa0/pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc", size = 4525163, upload-time = "2024-07-01T09:45:35.279Z" }, - { url = "https://files.pythonhosted.org/packages/07/8b/34854bf11a83c248505c8cb0fcf8d3d0b459a2246c8809b967963b6b12ae/pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e", size = 4463100, upload-time = "2024-07-01T09:45:37.74Z" }, - { url = "https://files.pythonhosted.org/packages/78/63/0632aee4e82476d9cbe5200c0cdf9ba41ee04ed77887432845264d81116d/pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46", size = 4592880, upload-time = "2024-07-01T09:45:39.89Z" }, - { url = "https://files.pythonhosted.org/packages/df/56/b8663d7520671b4398b9d97e1ed9f583d4afcbefbda3c6188325e8c297bd/pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984", size = 2235218, upload-time = "2024-07-01T09:45:42.771Z" }, - { url = "https://files.pythonhosted.org/packages/f4/72/0203e94a91ddb4a9d5238434ae6c1ca10e610e8487036132ea9bf806ca2a/pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141", size = 2554487, upload-time = "2024-07-01T09:45:45.176Z" }, - { url = "https://files.pythonhosted.org/packages/bd/52/7e7e93d7a6e4290543f17dc6f7d3af4bd0b3dd9926e2e8a35ac2282bc5f4/pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1", size = 2243219, upload-time = "2024-07-01T09:45:47.274Z" }, - { url = "https://files.pythonhosted.org/packages/a7/62/c9449f9c3043c37f73e7487ec4ef0c03eb9c9afc91a92b977a67b3c0bbc5/pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c", size = 3509265, upload-time = "2024-07-01T09:45:49.812Z" }, - { url = "https://files.pythonhosted.org/packages/f4/5f/491dafc7bbf5a3cc1845dc0430872e8096eb9e2b6f8161509d124594ec2d/pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be", size = 3375655, upload-time = "2024-07-01T09:45:52.462Z" }, - { url = "https://files.pythonhosted.org/packages/73/d5/c4011a76f4207a3c151134cd22a1415741e42fa5ddecec7c0182887deb3d/pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3", size = 4340304, upload-time = "2024-07-01T09:45:55.006Z" }, - { url = "https://files.pythonhosted.org/packages/ac/10/c67e20445a707f7a610699bba4fe050583b688d8cd2d202572b257f46600/pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6", size = 4452804, upload-time = "2024-07-01T09:45:58.437Z" }, - { url = "https://files.pythonhosted.org/packages/a9/83/6523837906d1da2b269dee787e31df3b0acb12e3d08f024965a3e7f64665/pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe", size = 4365126, upload-time = "2024-07-01T09:46:00.713Z" }, - { url = "https://files.pythonhosted.org/packages/ba/e5/8c68ff608a4203085158cff5cc2a3c534ec384536d9438c405ed6370d080/pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319", size = 4533541, upload-time = "2024-07-01T09:46:03.235Z" }, - { url = "https://files.pythonhosted.org/packages/f4/7c/01b8dbdca5bc6785573f4cee96e2358b0918b7b2c7b60d8b6f3abf87a070/pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d", size = 4471616, upload-time = "2024-07-01T09:46:05.356Z" }, - { url = "https://files.pythonhosted.org/packages/c8/57/2899b82394a35a0fbfd352e290945440e3b3785655a03365c0ca8279f351/pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696", size = 4600802, upload-time = "2024-07-01T09:46:08.145Z" }, - { url = "https://files.pythonhosted.org/packages/4d/d7/a44f193d4c26e58ee5d2d9db3d4854b2cfb5b5e08d360a5e03fe987c0086/pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496", size = 2235213, upload-time = "2024-07-01T09:46:10.211Z" }, - { url = "https://files.pythonhosted.org/packages/c1/d0/5866318eec2b801cdb8c82abf190c8343d8a1cd8bf5a0c17444a6f268291/pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91", size = 2554498, upload-time = "2024-07-01T09:46:12.685Z" }, - { url = "https://files.pythonhosted.org/packages/d4/c8/310ac16ac2b97e902d9eb438688de0d961660a87703ad1561fd3dfbd2aa0/pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22", size = 2243219, upload-time = "2024-07-01T09:46:14.83Z" }, - { url = "https://files.pythonhosted.org/packages/05/cb/0353013dc30c02a8be34eb91d25e4e4cf594b59e5a55ea1128fde1e5f8ea/pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94", size = 3509350, upload-time = "2024-07-01T09:46:17.177Z" }, - { url = "https://files.pythonhosted.org/packages/e7/cf/5c558a0f247e0bf9cec92bff9b46ae6474dd736f6d906315e60e4075f737/pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597", size = 3374980, upload-time = "2024-07-01T09:46:19.169Z" }, - { url = "https://files.pythonhosted.org/packages/84/48/6e394b86369a4eb68b8a1382c78dc092245af517385c086c5094e3b34428/pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80", size = 4343799, upload-time = "2024-07-01T09:46:21.883Z" }, - { url = "https://files.pythonhosted.org/packages/3b/f3/a8c6c11fa84b59b9df0cd5694492da8c039a24cd159f0f6918690105c3be/pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca", size = 4459973, upload-time = "2024-07-01T09:46:24.321Z" }, - { url = "https://files.pythonhosted.org/packages/7d/1b/c14b4197b80150fb64453585247e6fb2e1d93761fa0fa9cf63b102fde822/pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef", size = 4370054, upload-time = "2024-07-01T09:46:26.825Z" }, - { url = "https://files.pythonhosted.org/packages/55/77/40daddf677897a923d5d33329acd52a2144d54a9644f2a5422c028c6bf2d/pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a", size = 4539484, upload-time = "2024-07-01T09:46:29.355Z" }, - { url = "https://files.pythonhosted.org/packages/40/54/90de3e4256b1207300fb2b1d7168dd912a2fb4b2401e439ba23c2b2cabde/pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b", size = 4477375, upload-time = "2024-07-01T09:46:31.756Z" }, - { url = "https://files.pythonhosted.org/packages/13/24/1bfba52f44193860918ff7c93d03d95e3f8748ca1de3ceaf11157a14cf16/pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9", size = 4608773, upload-time = "2024-07-01T09:46:33.73Z" }, - { url = "https://files.pythonhosted.org/packages/55/04/5e6de6e6120451ec0c24516c41dbaf80cce1b6451f96561235ef2429da2e/pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42", size = 2235690, upload-time = "2024-07-01T09:46:36.587Z" }, - { url = "https://files.pythonhosted.org/packages/74/0a/d4ce3c44bca8635bd29a2eab5aa181b654a734a29b263ca8efe013beea98/pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a", size = 2554951, upload-time = "2024-07-01T09:46:38.777Z" }, - { url = "https://files.pythonhosted.org/packages/b5/ca/184349ee40f2e92439be9b3502ae6cfc43ac4b50bc4fc6b3de7957563894/pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9", size = 2243427, upload-time = "2024-07-01T09:46:43.15Z" }, - { url = "https://files.pythonhosted.org/packages/c3/00/706cebe7c2c12a6318aabe5d354836f54adff7156fd9e1bd6c89f4ba0e98/pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3", size = 3525685, upload-time = "2024-07-01T09:46:45.194Z" }, - { url = "https://files.pythonhosted.org/packages/cf/76/f658cbfa49405e5ecbfb9ba42d07074ad9792031267e782d409fd8fe7c69/pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb", size = 3374883, upload-time = "2024-07-01T09:46:47.331Z" }, - { url = "https://files.pythonhosted.org/packages/46/2b/99c28c4379a85e65378211971c0b430d9c7234b1ec4d59b2668f6299e011/pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70", size = 4339837, upload-time = "2024-07-01T09:46:49.647Z" }, - { url = "https://files.pythonhosted.org/packages/f1/74/b1ec314f624c0c43711fdf0d8076f82d9d802afd58f1d62c2a86878e8615/pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be", size = 4455562, upload-time = "2024-07-01T09:46:51.811Z" }, - { url = "https://files.pythonhosted.org/packages/4a/2a/4b04157cb7b9c74372fa867096a1607e6fedad93a44deeff553ccd307868/pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0", size = 4366761, upload-time = "2024-07-01T09:46:53.961Z" }, - { url = "https://files.pythonhosted.org/packages/ac/7b/8f1d815c1a6a268fe90481232c98dd0e5fa8c75e341a75f060037bd5ceae/pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc", size = 4536767, upload-time = "2024-07-01T09:46:56.664Z" }, - { url = "https://files.pythonhosted.org/packages/e5/77/05fa64d1f45d12c22c314e7b97398ffb28ef2813a485465017b7978b3ce7/pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a", size = 4477989, upload-time = "2024-07-01T09:46:58.977Z" }, - { url = "https://files.pythonhosted.org/packages/12/63/b0397cfc2caae05c3fb2f4ed1b4fc4fc878f0243510a7a6034ca59726494/pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309", size = 4610255, upload-time = "2024-07-01T09:47:01.189Z" }, - { url = "https://files.pythonhosted.org/packages/7b/f9/cfaa5082ca9bc4a6de66ffe1c12c2d90bf09c309a5f52b27759a596900e7/pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060", size = 2235603, upload-time = "2024-07-01T09:47:03.918Z" }, - { url = "https://files.pythonhosted.org/packages/01/6a/30ff0eef6e0c0e71e55ded56a38d4859bf9d3634a94a88743897b5f96936/pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea", size = 2554972, upload-time = "2024-07-01T09:47:06.152Z" }, - { url = "https://files.pythonhosted.org/packages/48/2c/2e0a52890f269435eee38b21c8218e102c621fe8d8df8b9dd06fabf879ba/pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d", size = 2243375, upload-time = "2024-07-01T09:47:09.065Z" }, - { url = "https://files.pythonhosted.org/packages/38/30/095d4f55f3a053392f75e2eae45eba3228452783bab3d9a920b951ac495c/pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4", size = 3493889, upload-time = "2024-07-01T09:48:04.815Z" }, - { url = "https://files.pythonhosted.org/packages/f3/e8/4ff79788803a5fcd5dc35efdc9386af153569853767bff74540725b45863/pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da", size = 3346160, upload-time = "2024-07-01T09:48:07.206Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ac/4184edd511b14f760c73f5bb8a5d6fd85c591c8aff7c2229677a355c4179/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026", size = 3435020, upload-time = "2024-07-01T09:48:09.66Z" }, - { url = "https://files.pythonhosted.org/packages/da/21/1749cd09160149c0a246a81d646e05f35041619ce76f6493d6a96e8d1103/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e", size = 3490539, upload-time = "2024-07-01T09:48:12.529Z" }, - { url = "https://files.pythonhosted.org/packages/b6/f5/f71fe1888b96083b3f6dfa0709101f61fc9e972c0c8d04e9d93ccef2a045/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5", size = 3476125, upload-time = "2024-07-01T09:48:14.891Z" }, - { url = "https://files.pythonhosted.org/packages/96/b9/c0362c54290a31866c3526848583a2f45a535aa9d725fd31e25d318c805f/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885", size = 3579373, upload-time = "2024-07-01T09:48:17.601Z" }, - { url = "https://files.pythonhosted.org/packages/52/3b/ce7a01026a7cf46e5452afa86f97a5e88ca97f562cafa76570178ab56d8d/pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5", size = 2554661, upload-time = "2024-07-01T09:48:20.293Z" }, + { url = "https://files.pythonhosted.org/packages/0e/69/a31cccd538ca0b5272be2a38347f8839b97a14be104ea08b0db92f749c74/pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e", size = 3509271 }, + { url = "https://files.pythonhosted.org/packages/9a/9e/4143b907be8ea0bce215f2ae4f7480027473f8b61fcedfda9d851082a5d2/pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d", size = 3375658 }, + { url = "https://files.pythonhosted.org/packages/8a/25/1fc45761955f9359b1169aa75e241551e74ac01a09f487adaaf4c3472d11/pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856", size = 4332075 }, + { url = "https://files.pythonhosted.org/packages/5e/dd/425b95d0151e1d6c951f45051112394f130df3da67363b6bc75dc4c27aba/pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f", size = 4444808 }, + { url = "https://files.pythonhosted.org/packages/b1/84/9a15cc5726cbbfe7f9f90bfb11f5d028586595907cd093815ca6644932e3/pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b", size = 4356290 }, + { url = "https://files.pythonhosted.org/packages/b5/5b/6651c288b08df3b8c1e2f8c1152201e0b25d240e22ddade0f1e242fc9fa0/pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc", size = 4525163 }, + { url = "https://files.pythonhosted.org/packages/07/8b/34854bf11a83c248505c8cb0fcf8d3d0b459a2246c8809b967963b6b12ae/pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e", size = 4463100 }, + { url = "https://files.pythonhosted.org/packages/78/63/0632aee4e82476d9cbe5200c0cdf9ba41ee04ed77887432845264d81116d/pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46", size = 4592880 }, + { url = "https://files.pythonhosted.org/packages/df/56/b8663d7520671b4398b9d97e1ed9f583d4afcbefbda3c6188325e8c297bd/pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984", size = 2235218 }, + { url = "https://files.pythonhosted.org/packages/f4/72/0203e94a91ddb4a9d5238434ae6c1ca10e610e8487036132ea9bf806ca2a/pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141", size = 2554487 }, + { url = "https://files.pythonhosted.org/packages/bd/52/7e7e93d7a6e4290543f17dc6f7d3af4bd0b3dd9926e2e8a35ac2282bc5f4/pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1", size = 2243219 }, + { url = "https://files.pythonhosted.org/packages/a7/62/c9449f9c3043c37f73e7487ec4ef0c03eb9c9afc91a92b977a67b3c0bbc5/pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c", size = 3509265 }, + { url = "https://files.pythonhosted.org/packages/f4/5f/491dafc7bbf5a3cc1845dc0430872e8096eb9e2b6f8161509d124594ec2d/pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be", size = 3375655 }, + { url = "https://files.pythonhosted.org/packages/73/d5/c4011a76f4207a3c151134cd22a1415741e42fa5ddecec7c0182887deb3d/pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3", size = 4340304 }, + { url = "https://files.pythonhosted.org/packages/ac/10/c67e20445a707f7a610699bba4fe050583b688d8cd2d202572b257f46600/pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6", size = 4452804 }, + { url = "https://files.pythonhosted.org/packages/a9/83/6523837906d1da2b269dee787e31df3b0acb12e3d08f024965a3e7f64665/pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe", size = 4365126 }, + { url = "https://files.pythonhosted.org/packages/ba/e5/8c68ff608a4203085158cff5cc2a3c534ec384536d9438c405ed6370d080/pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319", size = 4533541 }, + { url = "https://files.pythonhosted.org/packages/f4/7c/01b8dbdca5bc6785573f4cee96e2358b0918b7b2c7b60d8b6f3abf87a070/pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d", size = 4471616 }, + { url = "https://files.pythonhosted.org/packages/c8/57/2899b82394a35a0fbfd352e290945440e3b3785655a03365c0ca8279f351/pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696", size = 4600802 }, + { url = "https://files.pythonhosted.org/packages/4d/d7/a44f193d4c26e58ee5d2d9db3d4854b2cfb5b5e08d360a5e03fe987c0086/pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496", size = 2235213 }, + { url = "https://files.pythonhosted.org/packages/c1/d0/5866318eec2b801cdb8c82abf190c8343d8a1cd8bf5a0c17444a6f268291/pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91", size = 2554498 }, + { url = "https://files.pythonhosted.org/packages/d4/c8/310ac16ac2b97e902d9eb438688de0d961660a87703ad1561fd3dfbd2aa0/pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22", size = 2243219 }, + { url = "https://files.pythonhosted.org/packages/05/cb/0353013dc30c02a8be34eb91d25e4e4cf594b59e5a55ea1128fde1e5f8ea/pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94", size = 3509350 }, + { url = "https://files.pythonhosted.org/packages/e7/cf/5c558a0f247e0bf9cec92bff9b46ae6474dd736f6d906315e60e4075f737/pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597", size = 3374980 }, + { url = "https://files.pythonhosted.org/packages/84/48/6e394b86369a4eb68b8a1382c78dc092245af517385c086c5094e3b34428/pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80", size = 4343799 }, + { url = "https://files.pythonhosted.org/packages/3b/f3/a8c6c11fa84b59b9df0cd5694492da8c039a24cd159f0f6918690105c3be/pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca", size = 4459973 }, + { url = "https://files.pythonhosted.org/packages/7d/1b/c14b4197b80150fb64453585247e6fb2e1d93761fa0fa9cf63b102fde822/pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef", size = 4370054 }, + { url = "https://files.pythonhosted.org/packages/55/77/40daddf677897a923d5d33329acd52a2144d54a9644f2a5422c028c6bf2d/pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a", size = 4539484 }, + { url = "https://files.pythonhosted.org/packages/40/54/90de3e4256b1207300fb2b1d7168dd912a2fb4b2401e439ba23c2b2cabde/pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b", size = 4477375 }, + { url = "https://files.pythonhosted.org/packages/13/24/1bfba52f44193860918ff7c93d03d95e3f8748ca1de3ceaf11157a14cf16/pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9", size = 4608773 }, + { url = "https://files.pythonhosted.org/packages/55/04/5e6de6e6120451ec0c24516c41dbaf80cce1b6451f96561235ef2429da2e/pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42", size = 2235690 }, + { url = "https://files.pythonhosted.org/packages/74/0a/d4ce3c44bca8635bd29a2eab5aa181b654a734a29b263ca8efe013beea98/pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a", size = 2554951 }, + { url = "https://files.pythonhosted.org/packages/b5/ca/184349ee40f2e92439be9b3502ae6cfc43ac4b50bc4fc6b3de7957563894/pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9", size = 2243427 }, + { url = "https://files.pythonhosted.org/packages/c3/00/706cebe7c2c12a6318aabe5d354836f54adff7156fd9e1bd6c89f4ba0e98/pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3", size = 3525685 }, + { url = "https://files.pythonhosted.org/packages/cf/76/f658cbfa49405e5ecbfb9ba42d07074ad9792031267e782d409fd8fe7c69/pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb", size = 3374883 }, + { url = "https://files.pythonhosted.org/packages/46/2b/99c28c4379a85e65378211971c0b430d9c7234b1ec4d59b2668f6299e011/pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70", size = 4339837 }, + { url = "https://files.pythonhosted.org/packages/f1/74/b1ec314f624c0c43711fdf0d8076f82d9d802afd58f1d62c2a86878e8615/pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be", size = 4455562 }, + { url = "https://files.pythonhosted.org/packages/4a/2a/4b04157cb7b9c74372fa867096a1607e6fedad93a44deeff553ccd307868/pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0", size = 4366761 }, + { url = "https://files.pythonhosted.org/packages/ac/7b/8f1d815c1a6a268fe90481232c98dd0e5fa8c75e341a75f060037bd5ceae/pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc", size = 4536767 }, + { url = "https://files.pythonhosted.org/packages/e5/77/05fa64d1f45d12c22c314e7b97398ffb28ef2813a485465017b7978b3ce7/pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a", size = 4477989 }, + { url = "https://files.pythonhosted.org/packages/12/63/b0397cfc2caae05c3fb2f4ed1b4fc4fc878f0243510a7a6034ca59726494/pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309", size = 4610255 }, + { url = "https://files.pythonhosted.org/packages/7b/f9/cfaa5082ca9bc4a6de66ffe1c12c2d90bf09c309a5f52b27759a596900e7/pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060", size = 2235603 }, + { url = "https://files.pythonhosted.org/packages/01/6a/30ff0eef6e0c0e71e55ded56a38d4859bf9d3634a94a88743897b5f96936/pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea", size = 2554972 }, + { url = "https://files.pythonhosted.org/packages/48/2c/2e0a52890f269435eee38b21c8218e102c621fe8d8df8b9dd06fabf879ba/pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d", size = 2243375 }, + { url = "https://files.pythonhosted.org/packages/38/30/095d4f55f3a053392f75e2eae45eba3228452783bab3d9a920b951ac495c/pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4", size = 3493889 }, + { url = "https://files.pythonhosted.org/packages/f3/e8/4ff79788803a5fcd5dc35efdc9386af153569853767bff74540725b45863/pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da", size = 3346160 }, + { url = "https://files.pythonhosted.org/packages/d7/ac/4184edd511b14f760c73f5bb8a5d6fd85c591c8aff7c2229677a355c4179/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026", size = 3435020 }, + { url = "https://files.pythonhosted.org/packages/da/21/1749cd09160149c0a246a81d646e05f35041619ce76f6493d6a96e8d1103/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e", size = 3490539 }, + { url = "https://files.pythonhosted.org/packages/b6/f5/f71fe1888b96083b3f6dfa0709101f61fc9e972c0c8d04e9d93ccef2a045/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5", size = 3476125 }, + { url = "https://files.pythonhosted.org/packages/96/b9/c0362c54290a31866c3526848583a2f45a535aa9d725fd31e25d318c805f/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885", size = 3579373 }, + { url = "https://files.pythonhosted.org/packages/52/3b/ce7a01026a7cf46e5452afa86f97a5e88ca97f562cafa76570178ab56d8d/pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5", size = 2554661 }, ] [[package]] name = "platformdirs" version = "4.3.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b6/2d/7d512a3913d60623e7eb945c6d1b4f0bddf1d0b7ada5225274c87e5b53d1/platformdirs-4.3.7.tar.gz", hash = "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351", size = 21291, upload-time = "2025-03-19T20:36:10.989Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/2d/7d512a3913d60623e7eb945c6d1b4f0bddf1d0b7ada5225274c87e5b53d1/platformdirs-4.3.7.tar.gz", hash = "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351", size = 21291 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/45/59578566b3275b8fd9157885918fcd0c4d74162928a5310926887b856a51/platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94", size = 18499, upload-time = "2025-03-19T20:36:09.038Z" }, + { url = "https://files.pythonhosted.org/packages/6d/45/59578566b3275b8fd9157885918fcd0c4d74162928a5310926887b856a51/platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94", size = 18499 }, ] [[package]] name = "pluggy" version = "1.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955, upload-time = "2024-04-20T21:34:42.531Z" } +sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556, upload-time = "2024-04-20T21:34:40.434Z" }, + { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, ] [[package]] @@ -1790,46 +2015,46 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wcwidth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e1/c0/5e9c4d2a643a00a6f67578ef35485173de273a4567279e4f0c200c01386b/prettytable-3.9.0.tar.gz", hash = "sha256:f4ed94803c23073a90620b201965e5dc0bccf1760b7a7eaf3158cab8aaffdf34", size = 47874, upload-time = "2023-09-11T14:04:14.548Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/c0/5e9c4d2a643a00a6f67578ef35485173de273a4567279e4f0c200c01386b/prettytable-3.9.0.tar.gz", hash = "sha256:f4ed94803c23073a90620b201965e5dc0bccf1760b7a7eaf3158cab8aaffdf34", size = 47874 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/81/316b6a55a0d1f327d04cc7b0ba9d04058cb62de6c3a4d4b0df280cbe3b0b/prettytable-3.9.0-py3-none-any.whl", hash = "sha256:a71292ab7769a5de274b146b276ce938786f56c31cf7cea88b6f3775d82fe8c8", size = 27772, upload-time = "2023-09-11T14:03:45.582Z" }, + { url = "https://files.pythonhosted.org/packages/4d/81/316b6a55a0d1f327d04cc7b0ba9d04058cb62de6c3a4d4b0df280cbe3b0b/prettytable-3.9.0-py3-none-any.whl", hash = "sha256:a71292ab7769a5de274b146b276ce938786f56c31cf7cea88b6f3775d82fe8c8", size = 27772 }, ] [[package]] name = "protobuf" version = "4.25.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/db/a5/05ea470f4e793c9408bc975ce1c6957447e3134ce7f7a58c13be8b2c216f/protobuf-4.25.2.tar.gz", hash = "sha256:fe599e175cb347efc8ee524bcd4b902d11f7262c0e569ececcb89995c15f0a5e", size = 380282, upload-time = "2024-01-10T19:37:42.958Z" } +sdist = { url = "https://files.pythonhosted.org/packages/db/a5/05ea470f4e793c9408bc975ce1c6957447e3134ce7f7a58c13be8b2c216f/protobuf-4.25.2.tar.gz", hash = "sha256:fe599e175cb347efc8ee524bcd4b902d11f7262c0e569ececcb89995c15f0a5e", size = 380282 } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/2f/01f63896ddf22cbb0173ab51f54fde70b0208ca6c2f5e8416950977930e1/protobuf-4.25.2-cp310-abi3-win32.whl", hash = "sha256:b50c949608682b12efb0b2717f53256f03636af5f60ac0c1d900df6213910fd6", size = 392408, upload-time = "2024-01-10T19:37:23.466Z" }, - { url = "https://files.pythonhosted.org/packages/c1/00/c3ae19cabb36cfabc94ff0b102aac21b471c9f91a1357f8aafffb9efe8e0/protobuf-4.25.2-cp310-abi3-win_amd64.whl", hash = "sha256:8f62574857ee1de9f770baf04dde4165e30b15ad97ba03ceac65f760ff018ac9", size = 413397, upload-time = "2024-01-10T19:37:26.321Z" }, - { url = "https://files.pythonhosted.org/packages/b3/81/0017aefacf23273d4efd1154ef958a27eed9c177c4cc09d2d4ba398fb47f/protobuf-4.25.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:2db9f8fa64fbdcdc93767d3cf81e0f2aef176284071507e3ede160811502fd3d", size = 394159, upload-time = "2024-01-10T19:37:28.932Z" }, - { url = "https://files.pythonhosted.org/packages/23/17/405ba44f60a693dfe96c7a18e843707cffa0fcfad80bd8fc4f227f499ea5/protobuf-4.25.2-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:10894a2885b7175d3984f2be8d9850712c57d5e7587a2410720af8be56cdaf62", size = 293698, upload-time = "2024-01-10T19:37:30.666Z" }, - { url = "https://files.pythonhosted.org/packages/81/9e/63501b8d5b4e40c7260049836bd15ec3270c936e83bc57b85e4603cc212c/protobuf-4.25.2-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:fc381d1dd0516343f1440019cedf08a7405f791cd49eef4ae1ea06520bc1c020", size = 294609, upload-time = "2024-01-10T19:37:32.777Z" }, - { url = "https://files.pythonhosted.org/packages/ff/52/5d23df1fe3b368133ec3e2436fb3dd4ccedf44c8d5ac7f4a88087c75180b/protobuf-4.25.2-py3-none-any.whl", hash = "sha256:a8b7a98d4ce823303145bf3c1a8bdb0f2f4642a414b196f04ad9853ed0c8f830", size = 156463, upload-time = "2024-01-10T19:37:41.24Z" }, + { url = "https://files.pythonhosted.org/packages/36/2f/01f63896ddf22cbb0173ab51f54fde70b0208ca6c2f5e8416950977930e1/protobuf-4.25.2-cp310-abi3-win32.whl", hash = "sha256:b50c949608682b12efb0b2717f53256f03636af5f60ac0c1d900df6213910fd6", size = 392408 }, + { url = "https://files.pythonhosted.org/packages/c1/00/c3ae19cabb36cfabc94ff0b102aac21b471c9f91a1357f8aafffb9efe8e0/protobuf-4.25.2-cp310-abi3-win_amd64.whl", hash = "sha256:8f62574857ee1de9f770baf04dde4165e30b15ad97ba03ceac65f760ff018ac9", size = 413397 }, + { url = "https://files.pythonhosted.org/packages/b3/81/0017aefacf23273d4efd1154ef958a27eed9c177c4cc09d2d4ba398fb47f/protobuf-4.25.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:2db9f8fa64fbdcdc93767d3cf81e0f2aef176284071507e3ede160811502fd3d", size = 394159 }, + { url = "https://files.pythonhosted.org/packages/23/17/405ba44f60a693dfe96c7a18e843707cffa0fcfad80bd8fc4f227f499ea5/protobuf-4.25.2-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:10894a2885b7175d3984f2be8d9850712c57d5e7587a2410720af8be56cdaf62", size = 293698 }, + { url = "https://files.pythonhosted.org/packages/81/9e/63501b8d5b4e40c7260049836bd15ec3270c936e83bc57b85e4603cc212c/protobuf-4.25.2-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:fc381d1dd0516343f1440019cedf08a7405f791cd49eef4ae1ea06520bc1c020", size = 294609 }, + { url = "https://files.pythonhosted.org/packages/ff/52/5d23df1fe3b368133ec3e2436fb3dd4ccedf44c8d5ac7f4a88087c75180b/protobuf-4.25.2-py3-none-any.whl", hash = "sha256:a8b7a98d4ce823303145bf3c1a8bdb0f2f4642a414b196f04ad9853ed0c8f830", size = 156463 }, ] [[package]] name = "psutil" version = "5.9.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a0/d0/c9ae661a302931735237791f04cb7086ac244377f78692ba3b3eae3a9619/psutil-5.9.7.tar.gz", hash = "sha256:3f02134e82cfb5d089fddf20bb2e03fd5cd52395321d1c8458a9e58500ff417c", size = 498429, upload-time = "2023-12-17T11:25:21.22Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a0/d0/c9ae661a302931735237791f04cb7086ac244377f78692ba3b3eae3a9619/psutil-5.9.7.tar.gz", hash = "sha256:3f02134e82cfb5d089fddf20bb2e03fd5cd52395321d1c8458a9e58500ff417c", size = 498429 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/63/86a4ccc640b4ee1193800f57bbd20b766853c0cdbdbb248a27cdfafe6cbf/psutil-5.9.7-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ea36cc62e69a13ec52b2f625c27527f6e4479bca2b340b7a452af55b34fcbe2e", size = 245972, upload-time = "2023-12-17T11:25:48.202Z" }, - { url = "https://files.pythonhosted.org/packages/58/80/cc6666b3968646f2d94de66bbc63d701d501f4aa04de43dd7d1f5dc477dd/psutil-5.9.7-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1132704b876e58d277168cd729d64750633d5ff0183acf5b3c986b8466cd0284", size = 282514, upload-time = "2023-12-17T11:25:51.371Z" }, - { url = "https://files.pythonhosted.org/packages/be/fa/f1f626620e3b47e6237dcc64cb8cc1472f139e99422e5b9fa5bbcf457f48/psutil-5.9.7-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe8b7f07948f1304497ce4f4684881250cd859b16d06a1dc4d7941eeb6233bfe", size = 285469, upload-time = "2023-12-17T11:25:54.25Z" }, - { url = "https://files.pythonhosted.org/packages/7c/b8/dc6ebfc030b47cccc5f5229eeb15e64142b4782796c3ce169ccd60b4d511/psutil-5.9.7-cp37-abi3-win32.whl", hash = "sha256:c727ca5a9b2dd5193b8644b9f0c883d54f1248310023b5ad3e92036c5e2ada68", size = 248406, upload-time = "2023-12-17T12:38:50.326Z" }, - { url = "https://files.pythonhosted.org/packages/50/28/92b74d95dd991c837813ffac0c79a581a3d129eb0fa7c1dd616d9901e0f3/psutil-5.9.7-cp37-abi3-win_amd64.whl", hash = "sha256:f37f87e4d73b79e6c5e749440c3113b81d1ee7d26f21c19c47371ddea834f414", size = 252245, upload-time = "2023-12-17T12:39:00.686Z" }, - { url = "https://files.pythonhosted.org/packages/ba/8a/000d0e80156f0b96c55bda6c60f5ed6543d7b5e893ccab83117e50de1400/psutil-5.9.7-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:032f4f2c909818c86cea4fe2cc407f1c0f0cde8e6c6d702b28b8ce0c0d143340", size = 246739, upload-time = "2023-12-17T11:25:57.305Z" }, + { url = "https://files.pythonhosted.org/packages/6c/63/86a4ccc640b4ee1193800f57bbd20b766853c0cdbdbb248a27cdfafe6cbf/psutil-5.9.7-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ea36cc62e69a13ec52b2f625c27527f6e4479bca2b340b7a452af55b34fcbe2e", size = 245972 }, + { url = "https://files.pythonhosted.org/packages/58/80/cc6666b3968646f2d94de66bbc63d701d501f4aa04de43dd7d1f5dc477dd/psutil-5.9.7-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1132704b876e58d277168cd729d64750633d5ff0183acf5b3c986b8466cd0284", size = 282514 }, + { url = "https://files.pythonhosted.org/packages/be/fa/f1f626620e3b47e6237dcc64cb8cc1472f139e99422e5b9fa5bbcf457f48/psutil-5.9.7-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe8b7f07948f1304497ce4f4684881250cd859b16d06a1dc4d7941eeb6233bfe", size = 285469 }, + { url = "https://files.pythonhosted.org/packages/7c/b8/dc6ebfc030b47cccc5f5229eeb15e64142b4782796c3ce169ccd60b4d511/psutil-5.9.7-cp37-abi3-win32.whl", hash = "sha256:c727ca5a9b2dd5193b8644b9f0c883d54f1248310023b5ad3e92036c5e2ada68", size = 248406 }, + { url = "https://files.pythonhosted.org/packages/50/28/92b74d95dd991c837813ffac0c79a581a3d129eb0fa7c1dd616d9901e0f3/psutil-5.9.7-cp37-abi3-win_amd64.whl", hash = "sha256:f37f87e4d73b79e6c5e749440c3113b81d1ee7d26f21c19c47371ddea834f414", size = 252245 }, + { url = "https://files.pythonhosted.org/packages/ba/8a/000d0e80156f0b96c55bda6c60f5ed6543d7b5e893ccab83117e50de1400/psutil-5.9.7-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:032f4f2c909818c86cea4fe2cc407f1c0f0cde8e6c6d702b28b8ce0c0d143340", size = 246739 }, ] [[package]] name = "pycparser" version = "2.21" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5e/0b/95d387f5f4433cb0f53ff7ad859bd2c6051051cebbb564f139a999ab46de/pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206", size = 170877, upload-time = "2021-11-06T12:48:46.095Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/0b/95d387f5f4433cb0f53ff7ad859bd2c6051051cebbb564f139a999ab46de/pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206", size = 170877 } wheels = [ - { url = "https://files.pythonhosted.org/packages/62/d5/5f610ebe421e85889f2e55e33b7f9a6795bd982198517d912eb1c76e1a53/pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", size = 118697, upload-time = "2021-11-06T12:50:13.61Z" }, + { url = "https://files.pythonhosted.org/packages/62/d5/5f610ebe421e85889f2e55e33b7f9a6795bd982198517d912eb1c76e1a53/pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", size = 118697 }, ] [[package]] @@ -1842,9 +2067,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" } +sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782 }, ] [[package]] @@ -1854,84 +2079,84 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/92/b31726561b5dae176c2d2c2dc43a9c5bfba5d32f96f8b4c0a600dd492447/pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8", size = 2028817, upload-time = "2025-04-23T18:30:43.919Z" }, - { url = "https://files.pythonhosted.org/packages/a3/44/3f0b95fafdaca04a483c4e685fe437c6891001bf3ce8b2fded82b9ea3aa1/pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d", size = 1861357, upload-time = "2025-04-23T18:30:46.372Z" }, - { url = "https://files.pythonhosted.org/packages/30/97/e8f13b55766234caae05372826e8e4b3b96e7b248be3157f53237682e43c/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d", size = 1898011, upload-time = "2025-04-23T18:30:47.591Z" }, - { url = "https://files.pythonhosted.org/packages/9b/a3/99c48cf7bafc991cc3ee66fd544c0aae8dc907b752f1dad2d79b1b5a471f/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572", size = 1982730, upload-time = "2025-04-23T18:30:49.328Z" }, - { url = "https://files.pythonhosted.org/packages/de/8e/a5b882ec4307010a840fb8b58bd9bf65d1840c92eae7534c7441709bf54b/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02", size = 2136178, upload-time = "2025-04-23T18:30:50.907Z" }, - { url = "https://files.pythonhosted.org/packages/e4/bb/71e35fc3ed05af6834e890edb75968e2802fe98778971ab5cba20a162315/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b", size = 2736462, upload-time = "2025-04-23T18:30:52.083Z" }, - { url = "https://files.pythonhosted.org/packages/31/0d/c8f7593e6bc7066289bbc366f2235701dcbebcd1ff0ef8e64f6f239fb47d/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2", size = 2005652, upload-time = "2025-04-23T18:30:53.389Z" }, - { url = "https://files.pythonhosted.org/packages/d2/7a/996d8bd75f3eda405e3dd219ff5ff0a283cd8e34add39d8ef9157e722867/pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a", size = 2113306, upload-time = "2025-04-23T18:30:54.661Z" }, - { url = "https://files.pythonhosted.org/packages/ff/84/daf2a6fb2db40ffda6578a7e8c5a6e9c8affb251a05c233ae37098118788/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac", size = 2073720, upload-time = "2025-04-23T18:30:56.11Z" }, - { url = "https://files.pythonhosted.org/packages/77/fb/2258da019f4825128445ae79456a5499c032b55849dbd5bed78c95ccf163/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a", size = 2244915, upload-time = "2025-04-23T18:30:57.501Z" }, - { url = "https://files.pythonhosted.org/packages/d8/7a/925ff73756031289468326e355b6fa8316960d0d65f8b5d6b3a3e7866de7/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b", size = 2241884, upload-time = "2025-04-23T18:30:58.867Z" }, - { url = "https://files.pythonhosted.org/packages/0b/b0/249ee6d2646f1cdadcb813805fe76265745c4010cf20a8eba7b0e639d9b2/pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22", size = 1910496, upload-time = "2025-04-23T18:31:00.078Z" }, - { url = "https://files.pythonhosted.org/packages/66/ff/172ba8f12a42d4b552917aa65d1f2328990d3ccfc01d5b7c943ec084299f/pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640", size = 1955019, upload-time = "2025-04-23T18:31:01.335Z" }, - { url = "https://files.pythonhosted.org/packages/3f/8d/71db63483d518cbbf290261a1fc2839d17ff89fce7089e08cad07ccfce67/pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7", size = 2028584, upload-time = "2025-04-23T18:31:03.106Z" }, - { url = "https://files.pythonhosted.org/packages/24/2f/3cfa7244ae292dd850989f328722d2aef313f74ffc471184dc509e1e4e5a/pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246", size = 1855071, upload-time = "2025-04-23T18:31:04.621Z" }, - { url = "https://files.pythonhosted.org/packages/b3/d3/4ae42d33f5e3f50dd467761304be2fa0a9417fbf09735bc2cce003480f2a/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f", size = 1897823, upload-time = "2025-04-23T18:31:06.377Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f3/aa5976e8352b7695ff808599794b1fba2a9ae2ee954a3426855935799488/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc", size = 1983792, upload-time = "2025-04-23T18:31:07.93Z" }, - { url = "https://files.pythonhosted.org/packages/d5/7a/cda9b5a23c552037717f2b2a5257e9b2bfe45e687386df9591eff7b46d28/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de", size = 2136338, upload-time = "2025-04-23T18:31:09.283Z" }, - { url = "https://files.pythonhosted.org/packages/2b/9f/b8f9ec8dd1417eb9da784e91e1667d58a2a4a7b7b34cf4af765ef663a7e5/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a", size = 2730998, upload-time = "2025-04-23T18:31:11.7Z" }, - { url = "https://files.pythonhosted.org/packages/47/bc/cd720e078576bdb8255d5032c5d63ee5c0bf4b7173dd955185a1d658c456/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef", size = 2003200, upload-time = "2025-04-23T18:31:13.536Z" }, - { url = "https://files.pythonhosted.org/packages/ca/22/3602b895ee2cd29d11a2b349372446ae9727c32e78a94b3d588a40fdf187/pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e", size = 2113890, upload-time = "2025-04-23T18:31:15.011Z" }, - { url = "https://files.pythonhosted.org/packages/ff/e6/e3c5908c03cf00d629eb38393a98fccc38ee0ce8ecce32f69fc7d7b558a7/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d", size = 2073359, upload-time = "2025-04-23T18:31:16.393Z" }, - { url = "https://files.pythonhosted.org/packages/12/e7/6a36a07c59ebefc8777d1ffdaf5ae71b06b21952582e4b07eba88a421c79/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30", size = 2245883, upload-time = "2025-04-23T18:31:17.892Z" }, - { url = "https://files.pythonhosted.org/packages/16/3f/59b3187aaa6cc0c1e6616e8045b284de2b6a87b027cce2ffcea073adf1d2/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf", size = 2241074, upload-time = "2025-04-23T18:31:19.205Z" }, - { url = "https://files.pythonhosted.org/packages/e0/ed/55532bb88f674d5d8f67ab121a2a13c385df382de2a1677f30ad385f7438/pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51", size = 1910538, upload-time = "2025-04-23T18:31:20.541Z" }, - { url = "https://files.pythonhosted.org/packages/fe/1b/25b7cccd4519c0b23c2dd636ad39d381abf113085ce4f7bec2b0dc755eb1/pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab", size = 1952909, upload-time = "2025-04-23T18:31:22.371Z" }, - { url = "https://files.pythonhosted.org/packages/49/a9/d809358e49126438055884c4366a1f6227f0f84f635a9014e2deb9b9de54/pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65", size = 1897786, upload-time = "2025-04-23T18:31:24.161Z" }, - { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000, upload-time = "2025-04-23T18:31:25.863Z" }, - { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996, upload-time = "2025-04-23T18:31:27.341Z" }, - { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957, upload-time = "2025-04-23T18:31:28.956Z" }, - { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199, upload-time = "2025-04-23T18:31:31.025Z" }, - { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296, upload-time = "2025-04-23T18:31:32.514Z" }, - { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109, upload-time = "2025-04-23T18:31:33.958Z" }, - { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028, upload-time = "2025-04-23T18:31:39.095Z" }, - { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044, upload-time = "2025-04-23T18:31:41.034Z" }, - { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881, upload-time = "2025-04-23T18:31:42.757Z" }, - { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034, upload-time = "2025-04-23T18:31:44.304Z" }, - { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187, upload-time = "2025-04-23T18:31:45.891Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628, upload-time = "2025-04-23T18:31:47.819Z" }, - { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866, upload-time = "2025-04-23T18:31:49.635Z" }, - { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894, upload-time = "2025-04-23T18:31:51.609Z" }, - { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" }, - { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" }, - { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" }, - { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" }, - { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" }, - { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" }, - { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" }, - { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" }, - { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" }, - { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" }, - { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" }, - { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" }, - { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" }, - { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" }, - { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" }, - { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" }, - { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" }, - { url = "https://files.pythonhosted.org/packages/30/68/373d55e58b7e83ce371691f6eaa7175e3a24b956c44628eb25d7da007917/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa", size = 2023982, upload-time = "2025-04-23T18:32:53.14Z" }, - { url = "https://files.pythonhosted.org/packages/a4/16/145f54ac08c96a63d8ed6442f9dec17b2773d19920b627b18d4f10a061ea/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29", size = 1858412, upload-time = "2025-04-23T18:32:55.52Z" }, - { url = "https://files.pythonhosted.org/packages/41/b1/c6dc6c3e2de4516c0bb2c46f6a373b91b5660312342a0cf5826e38ad82fa/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d", size = 1892749, upload-time = "2025-04-23T18:32:57.546Z" }, - { url = "https://files.pythonhosted.org/packages/12/73/8cd57e20afba760b21b742106f9dbdfa6697f1570b189c7457a1af4cd8a0/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e", size = 2067527, upload-time = "2025-04-23T18:32:59.771Z" }, - { url = "https://files.pythonhosted.org/packages/e3/d5/0bb5d988cc019b3cba4a78f2d4b3854427fc47ee8ec8e9eaabf787da239c/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c", size = 2108225, upload-time = "2025-04-23T18:33:04.51Z" }, - { url = "https://files.pythonhosted.org/packages/f1/c5/00c02d1571913d496aabf146106ad8239dc132485ee22efe08085084ff7c/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec", size = 2069490, upload-time = "2025-04-23T18:33:06.391Z" }, - { url = "https://files.pythonhosted.org/packages/22/a8/dccc38768274d3ed3a59b5d06f59ccb845778687652daa71df0cab4040d7/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052", size = 2237525, upload-time = "2025-04-23T18:33:08.44Z" }, - { url = "https://files.pythonhosted.org/packages/d4/e7/4f98c0b125dda7cf7ccd14ba936218397b44f50a56dd8c16a3091df116c3/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c", size = 2238446, upload-time = "2025-04-23T18:33:10.313Z" }, - { url = "https://files.pythonhosted.org/packages/ce/91/2ec36480fdb0b783cd9ef6795753c1dea13882f2e68e73bce76ae8c21e6a/pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808", size = 2066678, upload-time = "2025-04-23T18:33:12.224Z" }, - { url = "https://files.pythonhosted.org/packages/7b/27/d4ae6487d73948d6f20dddcd94be4ea43e74349b56eba82e9bdee2d7494c/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8", size = 2025200, upload-time = "2025-04-23T18:33:14.199Z" }, - { url = "https://files.pythonhosted.org/packages/f1/b8/b3cb95375f05d33801024079b9392a5ab45267a63400bf1866e7ce0f0de4/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593", size = 1859123, upload-time = "2025-04-23T18:33:16.555Z" }, - { url = "https://files.pythonhosted.org/packages/05/bc/0d0b5adeda59a261cd30a1235a445bf55c7e46ae44aea28f7bd6ed46e091/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612", size = 1892852, upload-time = "2025-04-23T18:33:18.513Z" }, - { url = "https://files.pythonhosted.org/packages/3e/11/d37bdebbda2e449cb3f519f6ce950927b56d62f0b84fd9cb9e372a26a3d5/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7", size = 2067484, upload-time = "2025-04-23T18:33:20.475Z" }, - { url = "https://files.pythonhosted.org/packages/8c/55/1f95f0a05ce72ecb02a8a8a1c3be0579bbc29b1d5ab68f1378b7bebc5057/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e", size = 2108896, upload-time = "2025-04-23T18:33:22.501Z" }, - { url = "https://files.pythonhosted.org/packages/53/89/2b2de6c81fa131f423246a9109d7b2a375e83968ad0800d6e57d0574629b/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8", size = 2069475, upload-time = "2025-04-23T18:33:24.528Z" }, - { url = "https://files.pythonhosted.org/packages/b8/e9/1f7efbe20d0b2b10f6718944b5d8ece9152390904f29a78e68d4e7961159/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf", size = 2239013, upload-time = "2025-04-23T18:33:26.621Z" }, - { url = "https://files.pythonhosted.org/packages/3c/b2/5309c905a93811524a49b4e031e9851a6b00ff0fb668794472ea7746b448/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb", size = 2238715, upload-time = "2025-04-23T18:33:28.656Z" }, - { url = "https://files.pythonhosted.org/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", size = 2066757, upload-time = "2025-04-23T18:33:30.645Z" }, + { url = "https://files.pythonhosted.org/packages/e5/92/b31726561b5dae176c2d2c2dc43a9c5bfba5d32f96f8b4c0a600dd492447/pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8", size = 2028817 }, + { url = "https://files.pythonhosted.org/packages/a3/44/3f0b95fafdaca04a483c4e685fe437c6891001bf3ce8b2fded82b9ea3aa1/pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d", size = 1861357 }, + { url = "https://files.pythonhosted.org/packages/30/97/e8f13b55766234caae05372826e8e4b3b96e7b248be3157f53237682e43c/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d", size = 1898011 }, + { url = "https://files.pythonhosted.org/packages/9b/a3/99c48cf7bafc991cc3ee66fd544c0aae8dc907b752f1dad2d79b1b5a471f/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572", size = 1982730 }, + { url = "https://files.pythonhosted.org/packages/de/8e/a5b882ec4307010a840fb8b58bd9bf65d1840c92eae7534c7441709bf54b/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02", size = 2136178 }, + { url = "https://files.pythonhosted.org/packages/e4/bb/71e35fc3ed05af6834e890edb75968e2802fe98778971ab5cba20a162315/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b", size = 2736462 }, + { url = "https://files.pythonhosted.org/packages/31/0d/c8f7593e6bc7066289bbc366f2235701dcbebcd1ff0ef8e64f6f239fb47d/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2", size = 2005652 }, + { url = "https://files.pythonhosted.org/packages/d2/7a/996d8bd75f3eda405e3dd219ff5ff0a283cd8e34add39d8ef9157e722867/pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a", size = 2113306 }, + { url = "https://files.pythonhosted.org/packages/ff/84/daf2a6fb2db40ffda6578a7e8c5a6e9c8affb251a05c233ae37098118788/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac", size = 2073720 }, + { url = "https://files.pythonhosted.org/packages/77/fb/2258da019f4825128445ae79456a5499c032b55849dbd5bed78c95ccf163/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a", size = 2244915 }, + { url = "https://files.pythonhosted.org/packages/d8/7a/925ff73756031289468326e355b6fa8316960d0d65f8b5d6b3a3e7866de7/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b", size = 2241884 }, + { url = "https://files.pythonhosted.org/packages/0b/b0/249ee6d2646f1cdadcb813805fe76265745c4010cf20a8eba7b0e639d9b2/pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22", size = 1910496 }, + { url = "https://files.pythonhosted.org/packages/66/ff/172ba8f12a42d4b552917aa65d1f2328990d3ccfc01d5b7c943ec084299f/pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640", size = 1955019 }, + { url = "https://files.pythonhosted.org/packages/3f/8d/71db63483d518cbbf290261a1fc2839d17ff89fce7089e08cad07ccfce67/pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7", size = 2028584 }, + { url = "https://files.pythonhosted.org/packages/24/2f/3cfa7244ae292dd850989f328722d2aef313f74ffc471184dc509e1e4e5a/pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246", size = 1855071 }, + { url = "https://files.pythonhosted.org/packages/b3/d3/4ae42d33f5e3f50dd467761304be2fa0a9417fbf09735bc2cce003480f2a/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f", size = 1897823 }, + { url = "https://files.pythonhosted.org/packages/f4/f3/aa5976e8352b7695ff808599794b1fba2a9ae2ee954a3426855935799488/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc", size = 1983792 }, + { url = "https://files.pythonhosted.org/packages/d5/7a/cda9b5a23c552037717f2b2a5257e9b2bfe45e687386df9591eff7b46d28/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de", size = 2136338 }, + { url = "https://files.pythonhosted.org/packages/2b/9f/b8f9ec8dd1417eb9da784e91e1667d58a2a4a7b7b34cf4af765ef663a7e5/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a", size = 2730998 }, + { url = "https://files.pythonhosted.org/packages/47/bc/cd720e078576bdb8255d5032c5d63ee5c0bf4b7173dd955185a1d658c456/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef", size = 2003200 }, + { url = "https://files.pythonhosted.org/packages/ca/22/3602b895ee2cd29d11a2b349372446ae9727c32e78a94b3d588a40fdf187/pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e", size = 2113890 }, + { url = "https://files.pythonhosted.org/packages/ff/e6/e3c5908c03cf00d629eb38393a98fccc38ee0ce8ecce32f69fc7d7b558a7/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d", size = 2073359 }, + { url = "https://files.pythonhosted.org/packages/12/e7/6a36a07c59ebefc8777d1ffdaf5ae71b06b21952582e4b07eba88a421c79/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30", size = 2245883 }, + { url = "https://files.pythonhosted.org/packages/16/3f/59b3187aaa6cc0c1e6616e8045b284de2b6a87b027cce2ffcea073adf1d2/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf", size = 2241074 }, + { url = "https://files.pythonhosted.org/packages/e0/ed/55532bb88f674d5d8f67ab121a2a13c385df382de2a1677f30ad385f7438/pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51", size = 1910538 }, + { url = "https://files.pythonhosted.org/packages/fe/1b/25b7cccd4519c0b23c2dd636ad39d381abf113085ce4f7bec2b0dc755eb1/pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab", size = 1952909 }, + { url = "https://files.pythonhosted.org/packages/49/a9/d809358e49126438055884c4366a1f6227f0f84f635a9014e2deb9b9de54/pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65", size = 1897786 }, + { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000 }, + { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996 }, + { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957 }, + { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199 }, + { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296 }, + { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109 }, + { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028 }, + { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044 }, + { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881 }, + { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034 }, + { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187 }, + { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628 }, + { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866 }, + { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894 }, + { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688 }, + { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808 }, + { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580 }, + { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859 }, + { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810 }, + { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498 }, + { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611 }, + { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924 }, + { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196 }, + { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389 }, + { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223 }, + { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473 }, + { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269 }, + { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921 }, + { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162 }, + { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560 }, + { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777 }, + { url = "https://files.pythonhosted.org/packages/30/68/373d55e58b7e83ce371691f6eaa7175e3a24b956c44628eb25d7da007917/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa", size = 2023982 }, + { url = "https://files.pythonhosted.org/packages/a4/16/145f54ac08c96a63d8ed6442f9dec17b2773d19920b627b18d4f10a061ea/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29", size = 1858412 }, + { url = "https://files.pythonhosted.org/packages/41/b1/c6dc6c3e2de4516c0bb2c46f6a373b91b5660312342a0cf5826e38ad82fa/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d", size = 1892749 }, + { url = "https://files.pythonhosted.org/packages/12/73/8cd57e20afba760b21b742106f9dbdfa6697f1570b189c7457a1af4cd8a0/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e", size = 2067527 }, + { url = "https://files.pythonhosted.org/packages/e3/d5/0bb5d988cc019b3cba4a78f2d4b3854427fc47ee8ec8e9eaabf787da239c/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c", size = 2108225 }, + { url = "https://files.pythonhosted.org/packages/f1/c5/00c02d1571913d496aabf146106ad8239dc132485ee22efe08085084ff7c/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec", size = 2069490 }, + { url = "https://files.pythonhosted.org/packages/22/a8/dccc38768274d3ed3a59b5d06f59ccb845778687652daa71df0cab4040d7/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052", size = 2237525 }, + { url = "https://files.pythonhosted.org/packages/d4/e7/4f98c0b125dda7cf7ccd14ba936218397b44f50a56dd8c16a3091df116c3/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c", size = 2238446 }, + { url = "https://files.pythonhosted.org/packages/ce/91/2ec36480fdb0b783cd9ef6795753c1dea13882f2e68e73bce76ae8c21e6a/pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808", size = 2066678 }, + { url = "https://files.pythonhosted.org/packages/7b/27/d4ae6487d73948d6f20dddcd94be4ea43e74349b56eba82e9bdee2d7494c/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8", size = 2025200 }, + { url = "https://files.pythonhosted.org/packages/f1/b8/b3cb95375f05d33801024079b9392a5ab45267a63400bf1866e7ce0f0de4/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593", size = 1859123 }, + { url = "https://files.pythonhosted.org/packages/05/bc/0d0b5adeda59a261cd30a1235a445bf55c7e46ae44aea28f7bd6ed46e091/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612", size = 1892852 }, + { url = "https://files.pythonhosted.org/packages/3e/11/d37bdebbda2e449cb3f519f6ce950927b56d62f0b84fd9cb9e372a26a3d5/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7", size = 2067484 }, + { url = "https://files.pythonhosted.org/packages/8c/55/1f95f0a05ce72ecb02a8a8a1c3be0579bbc29b1d5ab68f1378b7bebc5057/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e", size = 2108896 }, + { url = "https://files.pythonhosted.org/packages/53/89/2b2de6c81fa131f423246a9109d7b2a375e83968ad0800d6e57d0574629b/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8", size = 2069475 }, + { url = "https://files.pythonhosted.org/packages/b8/e9/1f7efbe20d0b2b10f6718944b5d8ece9152390904f29a78e68d4e7961159/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf", size = 2239013 }, + { url = "https://files.pythonhosted.org/packages/3c/b2/5309c905a93811524a49b4e031e9851a6b00ff0fb668794472ea7746b448/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb", size = 2238715 }, + { url = "https://files.pythonhosted.org/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", size = 2066757 }, ] [[package]] @@ -1943,36 +2168,36 @@ dependencies = [ { name = "python-dotenv" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/68/85/1ea668bbab3c50071ca613c6ab30047fb36ab0da1b92fa8f17bbc38fd36c/pydantic_settings-2.10.1.tar.gz", hash = "sha256:06f0062169818d0f5524420a360d632d5857b83cffd4d42fe29597807a1614ee", size = 172583, upload-time = "2025-06-24T13:26:46.841Z" } +sdist = { url = "https://files.pythonhosted.org/packages/68/85/1ea668bbab3c50071ca613c6ab30047fb36ab0da1b92fa8f17bbc38fd36c/pydantic_settings-2.10.1.tar.gz", hash = "sha256:06f0062169818d0f5524420a360d632d5857b83cffd4d42fe29597807a1614ee", size = 172583 } wheels = [ - { url = "https://files.pythonhosted.org/packages/58/f0/427018098906416f580e3cf1366d3b1abfb408a0652e9f31600c24a1903c/pydantic_settings-2.10.1-py3-none-any.whl", hash = "sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796", size = 45235, upload-time = "2025-06-24T13:26:45.485Z" }, + { url = "https://files.pythonhosted.org/packages/58/f0/427018098906416f580e3cf1366d3b1abfb408a0652e9f31600c24a1903c/pydantic_settings-2.10.1-py3-none-any.whl", hash = "sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796", size = 45235 }, ] [[package]] name = "pygments" version = "2.17.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/55/59/8bccf4157baf25e4aa5a0bb7fa3ba8600907de105ebc22b0c78cfbf6f565/pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367", size = 4827772, upload-time = "2023-11-21T20:43:53.875Z" } +sdist = { url = "https://files.pythonhosted.org/packages/55/59/8bccf4157baf25e4aa5a0bb7fa3ba8600907de105ebc22b0c78cfbf6f565/pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367", size = 4827772 } wheels = [ - { url = "https://files.pythonhosted.org/packages/97/9c/372fef8377a6e340b1704768d20daaded98bf13282b5327beb2e2fe2c7ef/pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c", size = 1179756, upload-time = "2023-11-21T20:43:49.423Z" }, + { url = "https://files.pythonhosted.org/packages/97/9c/372fef8377a6e340b1704768d20daaded98bf13282b5327beb2e2fe2c7ef/pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c", size = 1179756 }, ] [[package]] name = "pyparsing" version = "3.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/37/fe/65c989f70bd630b589adfbbcd6ed238af22319e90f059946c26b4835e44b/pyparsing-3.1.1.tar.gz", hash = "sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db", size = 884814, upload-time = "2023-07-30T15:07:02.617Z" } +sdist = { url = "https://files.pythonhosted.org/packages/37/fe/65c989f70bd630b589adfbbcd6ed238af22319e90f059946c26b4835e44b/pyparsing-3.1.1.tar.gz", hash = "sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db", size = 884814 } wheels = [ - { url = "https://files.pythonhosted.org/packages/39/92/8486ede85fcc088f1b3dba4ce92dd29d126fd96b0008ea213167940a2475/pyparsing-3.1.1-py3-none-any.whl", hash = "sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb", size = 103139, upload-time = "2023-07-30T15:06:59.829Z" }, + { url = "https://files.pythonhosted.org/packages/39/92/8486ede85fcc088f1b3dba4ce92dd29d126fd96b0008ea213167940a2475/pyparsing-3.1.1-py3-none-any.whl", hash = "sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb", size = 103139 }, ] [[package]] name = "pyreadline3" version = "3.4.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/86/3d61a61f36a0067874a00cb4dceb9028d34b6060e47828f7fc86fb9f7ee9/pyreadline3-3.4.1.tar.gz", hash = "sha256:6f3d1f7b8a31ba32b73917cefc1f28cc660562f39aea8646d30bd6eff21f7bae", size = 86465, upload-time = "2022-01-24T20:05:11.66Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/86/3d61a61f36a0067874a00cb4dceb9028d34b6060e47828f7fc86fb9f7ee9/pyreadline3-3.4.1.tar.gz", hash = "sha256:6f3d1f7b8a31ba32b73917cefc1f28cc660562f39aea8646d30bd6eff21f7bae", size = 86465 } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/fc/a3c13ded7b3057680c8ae95a9b6cc83e63657c38e0005c400a5d018a33a7/pyreadline3-3.4.1-py3-none-any.whl", hash = "sha256:b0efb6516fd4fb07b45949053826a62fa4cb353db5be2bbb4a7aa1fdd1e345fb", size = 95203, upload-time = "2022-01-24T20:05:10.442Z" }, + { url = "https://files.pythonhosted.org/packages/56/fc/a3c13ded7b3057680c8ae95a9b6cc83e63657c38e0005c400a5d018a33a7/pyreadline3-3.4.1-py3-none-any.whl", hash = "sha256:b0efb6516fd4fb07b45949053826a62fa4cb353db5be2bbb4a7aa1fdd1e345fb", size = 95203 }, ] [[package]] @@ -1988,21 +2213,22 @@ dependencies = [ { name = "pygments" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } +sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714 } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, + { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474 }, ] [[package]] name = "pytest-asyncio" -version = "1.0.0" +version = "1.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d0/d4/14f53324cb1a6381bef29d698987625d80052bb33932d8e7cbf9b337b17c/pytest_asyncio-1.0.0.tar.gz", hash = "sha256:d15463d13f4456e1ead2594520216b225a16f781e144f8fdf6c5bb4667c48b3f", size = 46960, upload-time = "2025-05-26T04:54:40.484Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/51/f8794af39eeb870e87a8c8068642fc07bce0c854d6865d7dd0f2a9d338c2/pytest_asyncio-1.1.0.tar.gz", hash = "sha256:796aa822981e01b68c12e4827b8697108f7205020f24b5793b3c41555dab68ea", size = 46652 } wheels = [ - { url = "https://files.pythonhosted.org/packages/30/05/ce271016e351fddc8399e546f6e23761967ee09c8c568bbfbecb0c150171/pytest_asyncio-1.0.0-py3-none-any.whl", hash = "sha256:4f024da9f1ef945e680dc68610b52550e36590a67fd31bb3b4943979a1f90ef3", size = 15976, upload-time = "2025-05-26T04:54:39.035Z" }, + { url = "https://files.pythonhosted.org/packages/c7/9d/bf86eddabf8c6c9cb1ea9a869d6873b46f105a5d292d3a6f7071f5b07935/pytest_asyncio-1.1.0-py3-none-any.whl", hash = "sha256:5fe2d69607b0bd75c656d1211f969cadba035030156745ee09e7d71740e58ecf", size = 15157 }, ] [[package]] @@ -2014,9 +2240,9 @@ dependencies = [ { name = "pluggy" }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/18/99/668cade231f434aaa59bbfbf49469068d2ddd945000621d3d165d2e7dd7b/pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2", size = 69432, upload-time = "2025-06-12T10:47:47.684Z" } +sdist = { url = "https://files.pythonhosted.org/packages/18/99/668cade231f434aaa59bbfbf49469068d2ddd945000621d3d165d2e7dd7b/pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2", size = 69432 } wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/16/4ea354101abb1287856baa4af2732be351c7bee728065aed451b678153fd/pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5", size = 24644, upload-time = "2025-06-12T10:47:45.932Z" }, + { url = "https://files.pythonhosted.org/packages/bc/16/4ea354101abb1287856baa4af2732be351c7bee728065aed451b678153fd/pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5", size = 24644 }, ] [[package]] @@ -2026,9 +2252,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/71/28/67172c96ba684058a4d24ffe144d64783d2a270d0af0d9e792737bddc75c/pytest_mock-3.14.1.tar.gz", hash = "sha256:159e9edac4c451ce77a5cdb9fc5d1100708d2dd4ba3c3df572f14097351af80e", size = 33241, upload-time = "2025-05-26T13:58:45.167Z" } +sdist = { url = "https://files.pythonhosted.org/packages/71/28/67172c96ba684058a4d24ffe144d64783d2a270d0af0d9e792737bddc75c/pytest_mock-3.14.1.tar.gz", hash = "sha256:159e9edac4c451ce77a5cdb9fc5d1100708d2dd4ba3c3df572f14097351af80e", size = 33241 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/05/77b60e520511c53d1c1ca75f1930c7dd8e971d0c4379b7f4b3f9644685ba/pytest_mock-3.14.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0", size = 9923, upload-time = "2025-05-26T13:58:43.487Z" }, + { url = "https://files.pythonhosted.org/packages/b2/05/77b60e520511c53d1c1ca75f1930c7dd8e971d0c4379b7f4b3f9644685ba/pytest_mock-3.14.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0", size = 9923 }, ] [[package]] @@ -2038,18 +2264,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4c/c4/13b4776ea2d76c115c1d1b84579f3764ee6d57204f6be27119f13a61d0a9/python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", size = 357324, upload-time = "2021-07-14T08:19:19.783Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/c4/13b4776ea2d76c115c1d1b84579f3764ee6d57204f6be27119f13a61d0a9/python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", size = 357324 } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/7a/87837f39d0296e723bb9b62bbb257d0355c7f6128853c78955f57342a56d/python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9", size = 247702, upload-time = "2021-07-14T08:19:18.161Z" }, + { url = "https://files.pythonhosted.org/packages/36/7a/87837f39d0296e723bb9b62bbb257d0355c7f6128853c78955f57342a56d/python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9", size = 247702 }, ] [[package]] name = "python-dotenv" version = "1.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/31/06/1ef763af20d0572c032fa22882cfbfb005fba6e7300715a37840858c919e/python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba", size = 37399, upload-time = "2023-02-24T06:46:37.282Z" } +sdist = { url = "https://files.pythonhosted.org/packages/31/06/1ef763af20d0572c032fa22882cfbfb005fba6e7300715a37840858c919e/python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba", size = 37399 } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/2f/62ea1c8b593f4e093cc1a7768f0d46112107e790c3e478532329e434f00b/python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a", size = 19482, upload-time = "2023-02-24T06:46:36.009Z" }, + { url = "https://files.pythonhosted.org/packages/44/2f/62ea1c8b593f4e093cc1a7768f0d46112107e790c3e478532329e434f00b/python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a", size = 19482 }, ] [[package]] @@ -2059,18 +2285,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "simple-websocket" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ba/0b/67295279b66835f9fa7a491650efcd78b20321c127036eef62c11a31e028/python_engineio-4.12.2.tar.gz", hash = "sha256:e7e712ffe1be1f6a05ee5f951e72d434854a32fcfc7f6e4d9d3cae24ec70defa", size = 91677, upload-time = "2025-06-04T19:22:18.789Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/0b/67295279b66835f9fa7a491650efcd78b20321c127036eef62c11a31e028/python_engineio-4.12.2.tar.gz", hash = "sha256:e7e712ffe1be1f6a05ee5f951e72d434854a32fcfc7f6e4d9d3cae24ec70defa", size = 91677 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/fa/df59acedf7bbb937f69174d00f921a7b93aa5a5f5c17d05296c814fff6fc/python_engineio-4.12.2-py3-none-any.whl", hash = "sha256:8218ab66950e179dfec4b4bbb30aecf3f5d86f5e58e6fc1aa7fde2c698b2804f", size = 59536, upload-time = "2025-06-04T19:22:16.916Z" }, + { url = "https://files.pythonhosted.org/packages/0c/fa/df59acedf7bbb937f69174d00f921a7b93aa5a5f5c17d05296c814fff6fc/python_engineio-4.12.2-py3-none-any.whl", hash = "sha256:8218ab66950e179dfec4b4bbb30aecf3f5d86f5e58e6fc1aa7fde2c698b2804f", size = 59536 }, ] [[package]] name = "python-multipart" version = "0.0.20" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158, upload-time = "2024-12-16T19:45:46.972Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158 } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" }, + { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546 }, ] [[package]] @@ -2081,9 +2307,9 @@ dependencies = [ { name = "bidict" }, { name = "python-engineio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/21/1a/396d50ccf06ee539fa758ce5623b59a9cb27637fc4b2dc07ed08bf495e77/python_socketio-5.13.0.tar.gz", hash = "sha256:ac4e19a0302ae812e23b712ec8b6427ca0521f7c582d6abb096e36e24a263029", size = 121125, upload-time = "2025-04-12T15:46:59.933Z" } +sdist = { url = "https://files.pythonhosted.org/packages/21/1a/396d50ccf06ee539fa758ce5623b59a9cb27637fc4b2dc07ed08bf495e77/python_socketio-5.13.0.tar.gz", hash = "sha256:ac4e19a0302ae812e23b712ec8b6427ca0521f7c582d6abb096e36e24a263029", size = 121125 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/32/b4fb8585d1be0f68bde7e110dffbcf354915f77ad8c778563f0ad9655c02/python_socketio-5.13.0-py3-none-any.whl", hash = "sha256:51f68d6499f2df8524668c24bcec13ba1414117cfb3a90115c559b601ab10caf", size = 77800, upload-time = "2025-04-12T15:46:58.412Z" }, + { url = "https://files.pythonhosted.org/packages/3c/32/b4fb8585d1be0f68bde7e110dffbcf354915f77ad8c778563f0ad9655c02/python_socketio-5.13.0-py3-none-any.whl", hash = "sha256:51f68d6499f2df8524668c24bcec13ba1414117cfb3a90115c559b601ab10caf", size = 77800 }, ] [package.optional-dependencies] @@ -2097,45 +2323,45 @@ name = "pywin32" version = "306" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/08/dc/28c668097edfaf4eac4617ef7adf081b9cf50d254672fcf399a70f5efc41/pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d", size = 8506422, upload-time = "2023-03-26T03:27:46.303Z" }, - { url = "https://files.pythonhosted.org/packages/d3/d6/891894edec688e72c2e308b3243fad98b4066e1839fd2fe78f04129a9d31/pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8", size = 9226392, upload-time = "2023-03-26T03:27:53.591Z" }, - { url = "https://files.pythonhosted.org/packages/8b/1e/fc18ad83ca553e01b97aa8393ff10e33c1fb57801db05488b83282ee9913/pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407", size = 8507689, upload-time = "2023-03-25T23:50:08.499Z" }, - { url = "https://files.pythonhosted.org/packages/7e/9e/ad6b1ae2a5ad1066dc509350e0fbf74d8d50251a51e420a2a8feaa0cecbd/pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e", size = 9227547, upload-time = "2023-03-25T23:50:20.331Z" }, - { url = "https://files.pythonhosted.org/packages/91/20/f744bff1da8f43388498503634378dbbefbe493e65675f2cc52f7185c2c2/pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a", size = 10388324, upload-time = "2023-03-25T23:50:30.904Z" }, - { url = "https://files.pythonhosted.org/packages/14/91/17e016d5923e178346aabda3dfec6629d1a26efe587d19667542105cf0a6/pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b", size = 8507705, upload-time = "2023-03-25T23:50:40.279Z" }, - { url = "https://files.pythonhosted.org/packages/83/1c/25b79fc3ec99b19b0a0730cc47356f7e2959863bf9f3cd314332bddb4f68/pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e", size = 9227429, upload-time = "2023-03-25T23:50:50.222Z" }, - { url = "https://files.pythonhosted.org/packages/1c/43/e3444dc9a12f8365d9603c2145d16bf0a2f8180f343cf87be47f5579e547/pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040", size = 10388145, upload-time = "2023-03-25T23:51:01.401Z" }, + { url = "https://files.pythonhosted.org/packages/08/dc/28c668097edfaf4eac4617ef7adf081b9cf50d254672fcf399a70f5efc41/pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d", size = 8506422 }, + { url = "https://files.pythonhosted.org/packages/d3/d6/891894edec688e72c2e308b3243fad98b4066e1839fd2fe78f04129a9d31/pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8", size = 9226392 }, + { url = "https://files.pythonhosted.org/packages/8b/1e/fc18ad83ca553e01b97aa8393ff10e33c1fb57801db05488b83282ee9913/pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407", size = 8507689 }, + { url = "https://files.pythonhosted.org/packages/7e/9e/ad6b1ae2a5ad1066dc509350e0fbf74d8d50251a51e420a2a8feaa0cecbd/pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e", size = 9227547 }, + { url = "https://files.pythonhosted.org/packages/91/20/f744bff1da8f43388498503634378dbbefbe493e65675f2cc52f7185c2c2/pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a", size = 10388324 }, + { url = "https://files.pythonhosted.org/packages/14/91/17e016d5923e178346aabda3dfec6629d1a26efe587d19667542105cf0a6/pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b", size = 8507705 }, + { url = "https://files.pythonhosted.org/packages/83/1c/25b79fc3ec99b19b0a0730cc47356f7e2959863bf9f3cd314332bddb4f68/pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e", size = 9227429 }, + { url = "https://files.pythonhosted.org/packages/1c/43/e3444dc9a12f8365d9603c2145d16bf0a2f8180f343cf87be47f5579e547/pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040", size = 10388145 }, ] [[package]] name = "pyyaml" version = "6.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/e5/af35f7ea75cf72f2cd079c95ee16797de7cd71f29ea7c68ae5ce7be1eda0/PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43", size = 125201, upload-time = "2023-07-18T00:00:23.308Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/e5/af35f7ea75cf72f2cd079c95ee16797de7cd71f29ea7c68ae5ce7be1eda0/PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43", size = 125201 } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/06/4beb652c0fe16834032e54f0956443d4cc797fe645527acee59e7deaa0a2/PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a", size = 189447, upload-time = "2023-07-17T23:57:04.325Z" }, - { url = "https://files.pythonhosted.org/packages/5b/07/10033a403b23405a8fc48975444463d3d10a5c2736b7eb2550b07b367429/PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f", size = 169264, upload-time = "2023-07-17T23:57:07.787Z" }, - { url = "https://files.pythonhosted.org/packages/f1/26/55e4f21db1f72eaef092015d9017c11510e7e6301c62a6cfee91295d13c6/PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938", size = 677003, upload-time = "2023-07-17T23:57:13.144Z" }, - { url = "https://files.pythonhosted.org/packages/ba/91/090818dfa62e85181f3ae23dd1e8b7ea7f09684864a900cab72d29c57346/PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d", size = 699070, upload-time = "2023-07-17T23:57:19.402Z" }, - { url = "https://files.pythonhosted.org/packages/29/61/bf33c6c85c55bc45a29eee3195848ff2d518d84735eb0e2d8cb42e0d285e/PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515", size = 705525, upload-time = "2023-07-17T23:57:25.272Z" }, - { url = "https://files.pythonhosted.org/packages/07/91/45dfd0ef821a7f41d9d0136ea3608bb5b1653e42fd56a7970532cb5c003f/PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290", size = 707514, upload-time = "2023-08-28T18:43:20.945Z" }, - { url = "https://files.pythonhosted.org/packages/b6/a0/b6700da5d49e9fed49dc3243d3771b598dad07abb37cc32e524607f96adc/PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924", size = 130488, upload-time = "2023-07-17T23:57:28.144Z" }, - { url = "https://files.pythonhosted.org/packages/24/97/9b59b43431f98d01806b288532da38099cc6f2fea0f3d712e21e269c0279/PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d", size = 145338, upload-time = "2023-07-17T23:57:31.118Z" }, - { url = "https://files.pythonhosted.org/packages/ec/0d/26fb23e8863e0aeaac0c64e03fd27367ad2ae3f3cccf3798ee98ce160368/PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007", size = 187867, upload-time = "2023-07-17T23:57:34.35Z" }, - { url = "https://files.pythonhosted.org/packages/28/09/55f715ddbf95a054b764b547f617e22f1d5e45d83905660e9a088078fe67/PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab", size = 167530, upload-time = "2023-07-17T23:57:36.975Z" }, - { url = "https://files.pythonhosted.org/packages/5e/94/7d5ee059dfb92ca9e62f4057dcdec9ac08a9e42679644854dc01177f8145/PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d", size = 732244, upload-time = "2023-07-17T23:57:43.774Z" }, - { url = "https://files.pythonhosted.org/packages/06/92/e0224aa6ebf9dc54a06a4609da37da40bb08d126f5535d81bff6b417b2ae/PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc", size = 752871, upload-time = "2023-07-17T23:57:51.921Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5e/efd033ab7199a0b2044dab3b9f7a4f6670e6a52c089de572e928d2873b06/PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673", size = 757729, upload-time = "2023-07-17T23:57:59.865Z" }, - { url = "https://files.pythonhosted.org/packages/03/5c/c4671451b2f1d76ebe352c0945d4cd13500adb5d05f5a51ee296d80152f7/PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b", size = 748528, upload-time = "2023-08-28T18:43:23.207Z" }, - { url = "https://files.pythonhosted.org/packages/73/9c/766e78d1efc0d1fca637a6b62cea1b4510a7fb93617eb805223294fef681/PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741", size = 130286, upload-time = "2023-07-17T23:58:02.964Z" }, - { url = "https://files.pythonhosted.org/packages/b3/34/65bb4b2d7908044963ebf614fe0fdb080773fc7030d7e39c8d3eddcd4257/PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34", size = 144699, upload-time = "2023-07-17T23:58:05.586Z" }, - { url = "https://files.pythonhosted.org/packages/bc/06/1b305bf6aa704343be85444c9d011f626c763abb40c0edc1cad13bfd7f86/PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28", size = 178692, upload-time = "2023-08-28T18:43:24.924Z" }, - { url = "https://files.pythonhosted.org/packages/84/02/404de95ced348b73dd84f70e15a41843d817ff8c1744516bf78358f2ffd2/PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9", size = 165622, upload-time = "2023-08-28T18:43:26.54Z" }, - { url = "https://files.pythonhosted.org/packages/c7/4c/4a2908632fc980da6d918b9de9c1d9d7d7e70b2672b1ad5166ed27841ef7/PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef", size = 696937, upload-time = "2024-01-18T20:40:22.92Z" }, - { url = "https://files.pythonhosted.org/packages/b4/33/720548182ffa8344418126017aa1d4ab4aeec9a2275f04ce3f3573d8ace8/PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0", size = 724969, upload-time = "2023-08-28T18:43:28.56Z" }, - { url = "https://files.pythonhosted.org/packages/4f/78/77b40157b6cb5f2d3d31a3d9b2efd1ba3505371f76730d267e8b32cf4b7f/PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4", size = 712604, upload-time = "2023-08-28T18:43:30.206Z" }, - { url = "https://files.pythonhosted.org/packages/2e/97/3e0e089ee85e840f4b15bfa00e4e63d84a3691ababbfea92d6f820ea6f21/PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54", size = 126098, upload-time = "2023-08-28T18:43:31.835Z" }, - { url = "https://files.pythonhosted.org/packages/2b/9f/fbade56564ad486809c27b322d0f7e6a89c01f6b4fe208402e90d4443a99/PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df", size = 138675, upload-time = "2023-08-28T18:43:33.613Z" }, + { url = "https://files.pythonhosted.org/packages/96/06/4beb652c0fe16834032e54f0956443d4cc797fe645527acee59e7deaa0a2/PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a", size = 189447 }, + { url = "https://files.pythonhosted.org/packages/5b/07/10033a403b23405a8fc48975444463d3d10a5c2736b7eb2550b07b367429/PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f", size = 169264 }, + { url = "https://files.pythonhosted.org/packages/f1/26/55e4f21db1f72eaef092015d9017c11510e7e6301c62a6cfee91295d13c6/PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938", size = 677003 }, + { url = "https://files.pythonhosted.org/packages/ba/91/090818dfa62e85181f3ae23dd1e8b7ea7f09684864a900cab72d29c57346/PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d", size = 699070 }, + { url = "https://files.pythonhosted.org/packages/29/61/bf33c6c85c55bc45a29eee3195848ff2d518d84735eb0e2d8cb42e0d285e/PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515", size = 705525 }, + { url = "https://files.pythonhosted.org/packages/07/91/45dfd0ef821a7f41d9d0136ea3608bb5b1653e42fd56a7970532cb5c003f/PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290", size = 707514 }, + { url = "https://files.pythonhosted.org/packages/b6/a0/b6700da5d49e9fed49dc3243d3771b598dad07abb37cc32e524607f96adc/PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924", size = 130488 }, + { url = "https://files.pythonhosted.org/packages/24/97/9b59b43431f98d01806b288532da38099cc6f2fea0f3d712e21e269c0279/PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d", size = 145338 }, + { url = "https://files.pythonhosted.org/packages/ec/0d/26fb23e8863e0aeaac0c64e03fd27367ad2ae3f3cccf3798ee98ce160368/PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007", size = 187867 }, + { url = "https://files.pythonhosted.org/packages/28/09/55f715ddbf95a054b764b547f617e22f1d5e45d83905660e9a088078fe67/PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab", size = 167530 }, + { url = "https://files.pythonhosted.org/packages/5e/94/7d5ee059dfb92ca9e62f4057dcdec9ac08a9e42679644854dc01177f8145/PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d", size = 732244 }, + { url = "https://files.pythonhosted.org/packages/06/92/e0224aa6ebf9dc54a06a4609da37da40bb08d126f5535d81bff6b417b2ae/PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc", size = 752871 }, + { url = "https://files.pythonhosted.org/packages/7b/5e/efd033ab7199a0b2044dab3b9f7a4f6670e6a52c089de572e928d2873b06/PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673", size = 757729 }, + { url = "https://files.pythonhosted.org/packages/03/5c/c4671451b2f1d76ebe352c0945d4cd13500adb5d05f5a51ee296d80152f7/PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b", size = 748528 }, + { url = "https://files.pythonhosted.org/packages/73/9c/766e78d1efc0d1fca637a6b62cea1b4510a7fb93617eb805223294fef681/PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741", size = 130286 }, + { url = "https://files.pythonhosted.org/packages/b3/34/65bb4b2d7908044963ebf614fe0fdb080773fc7030d7e39c8d3eddcd4257/PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34", size = 144699 }, + { url = "https://files.pythonhosted.org/packages/bc/06/1b305bf6aa704343be85444c9d011f626c763abb40c0edc1cad13bfd7f86/PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28", size = 178692 }, + { url = "https://files.pythonhosted.org/packages/84/02/404de95ced348b73dd84f70e15a41843d817ff8c1744516bf78358f2ffd2/PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9", size = 165622 }, + { url = "https://files.pythonhosted.org/packages/c7/4c/4a2908632fc980da6d918b9de9c1d9d7d7e70b2672b1ad5166ed27841ef7/PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef", size = 696937 }, + { url = "https://files.pythonhosted.org/packages/b4/33/720548182ffa8344418126017aa1d4ab4aeec9a2275f04ce3f3573d8ace8/PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0", size = 724969 }, + { url = "https://files.pythonhosted.org/packages/4f/78/77b40157b6cb5f2d3d31a3d9b2efd1ba3505371f76730d267e8b32cf4b7f/PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4", size = 712604 }, + { url = "https://files.pythonhosted.org/packages/2e/97/3e0e089ee85e840f4b15bfa00e4e63d84a3691ababbfea92d6f820ea6f21/PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54", size = 126098 }, + { url = "https://files.pythonhosted.org/packages/2b/9f/fbade56564ad486809c27b322d0f7e6a89c01f6b4fe208402e90d4443a99/PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df", size = 138675 }, ] [[package]] @@ -2145,46 +2371,46 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "implementation_name == 'pypy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3a/33/1a3683fc9a4bd64d8ccc0290da75c8f042184a1a49c146d28398414d3341/pyzmq-25.1.2.tar.gz", hash = "sha256:93f1aa311e8bb912e34f004cf186407a4e90eec4f0ecc0efd26056bf7eda0226", size = 1402339, upload-time = "2023-12-05T07:34:47.976Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/33/1a3683fc9a4bd64d8ccc0290da75c8f042184a1a49c146d28398414d3341/pyzmq-25.1.2.tar.gz", hash = "sha256:93f1aa311e8bb912e34f004cf186407a4e90eec4f0ecc0efd26056bf7eda0226", size = 1402339 } wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/f4/901edb48b2b2c00ad73de0db2ee76e24ce5903ef815ad0ad10e14555d989/pyzmq-25.1.2-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:e624c789359f1a16f83f35e2c705d07663ff2b4d4479bad35621178d8f0f6ea4", size = 1872310, upload-time = "2023-12-05T07:48:13.713Z" }, - { url = "https://files.pythonhosted.org/packages/5e/46/2de69c7c79fd78bf4c22a9e8165fa6312f5d49410f1be6ddab51a6fe7236/pyzmq-25.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:49151b0efece79f6a79d41a461d78535356136ee70084a1c22532fc6383f4ad0", size = 1249619, upload-time = "2023-12-05T07:50:38.691Z" }, - { url = "https://files.pythonhosted.org/packages/d1/f5/d6b9755713843bf9701ae86bf6fd97ec294a52cf2af719cd14fdf9392f65/pyzmq-25.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9a5f194cf730f2b24d6af1f833c14c10f41023da46a7f736f48b6d35061e76e", size = 897360, upload-time = "2023-12-05T07:42:26.268Z" }, - { url = "https://files.pythonhosted.org/packages/7c/88/c1aef8820f12e710d136024d231e70e24684a01314aa1814f0758960ba01/pyzmq-25.1.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:faf79a302f834d9e8304fafdc11d0d042266667ac45209afa57e5efc998e3872", size = 1156959, upload-time = "2023-12-05T07:44:29.904Z" }, - { url = "https://files.pythonhosted.org/packages/82/1b/b25d2c4ac3b4dae238c98e63395dbb88daf11968b168948d3c6289c3e95c/pyzmq-25.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f51a7b4ead28d3fca8dda53216314a553b0f7a91ee8fc46a72b402a78c3e43d", size = 1100585, upload-time = "2023-12-05T07:45:05.518Z" }, - { url = "https://files.pythonhosted.org/packages/67/bf/6bc0977acd934b66eacab79cec303ecf08ae4a6150d57c628aa919615488/pyzmq-25.1.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:0ddd6d71d4ef17ba5a87becf7ddf01b371eaba553c603477679ae817a8d84d75", size = 1109267, upload-time = "2023-12-05T07:39:51.21Z" }, - { url = "https://files.pythonhosted.org/packages/64/fb/4f07424e56c6a5fb47306d9ba744c3c250250c2e7272f9c81efbf8daaccf/pyzmq-25.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:246747b88917e4867e2367b005fc8eefbb4a54b7db363d6c92f89d69abfff4b6", size = 1431853, upload-time = "2023-12-05T07:41:09.261Z" }, - { url = "https://files.pythonhosted.org/packages/a2/10/2b88c1d4beb59a1d45c13983c4b7c5dcd6ef7988db3c03d23b0cabc5adca/pyzmq-25.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:00c48ae2fd81e2a50c3485de1b9d5c7c57cd85dc8ec55683eac16846e57ac979", size = 1766212, upload-time = "2023-12-05T07:49:05.926Z" }, - { url = "https://files.pythonhosted.org/packages/bc/ab/c9a22eacfd5bd82620501ae426a3dd6ffa374ac335b21e54209d7a93d3fb/pyzmq-25.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5a68d491fc20762b630e5db2191dd07ff89834086740f70e978bb2ef2668be08", size = 1653737, upload-time = "2023-12-05T07:49:09.096Z" }, - { url = "https://files.pythonhosted.org/packages/d6/e5/71bd89e47eedb7ebec31ef9a49dcdb0517dbbb063bd5de363980a6911eb1/pyzmq-25.1.2-cp310-cp310-win32.whl", hash = "sha256:09dfe949e83087da88c4a76767df04b22304a682d6154de2c572625c62ad6886", size = 906288, upload-time = "2023-12-05T07:42:05.509Z" }, - { url = "https://files.pythonhosted.org/packages/9d/5f/2defc8a579e8b5679d92720ab3a4cb93e3a77d923070bf4c1a103d3ae478/pyzmq-25.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:fa99973d2ed20417744fca0073390ad65ce225b546febb0580358e36aa90dba6", size = 1170923, upload-time = "2023-12-05T07:44:54.296Z" }, - { url = "https://files.pythonhosted.org/packages/35/de/7579518bc58cebf92568b48e354a702fb52525d0fab166dc544f2a0615dc/pyzmq-25.1.2-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:82544e0e2d0c1811482d37eef297020a040c32e0687c1f6fc23a75b75db8062c", size = 1870360, upload-time = "2023-12-05T07:48:16.153Z" }, - { url = "https://files.pythonhosted.org/packages/ce/f9/58b6cc9a110b1832f666fa6b5a67dc4d520fabfc680ca87a8167b2061d5d/pyzmq-25.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:01171fc48542348cd1a360a4b6c3e7d8f46cdcf53a8d40f84db6707a6768acc1", size = 1249008, upload-time = "2023-12-05T07:50:40.442Z" }, - { url = "https://files.pythonhosted.org/packages/bc/4a/ac6469c01813cb3652ab4e30ec4a37815cc9949afc18af33f64e2ec704aa/pyzmq-25.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc69c96735ab501419c432110016329bf0dea8898ce16fab97c6d9106dc0b348", size = 904394, upload-time = "2023-12-05T07:42:27.815Z" }, - { url = "https://files.pythonhosted.org/packages/77/b7/8cee519b11bdd3f76c1a6eb537ab13c1bfef2964d725717705c86f524e4c/pyzmq-25.1.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3e124e6b1dd3dfbeb695435dff0e383256655bb18082e094a8dd1f6293114642", size = 1161453, upload-time = "2023-12-05T07:44:32.003Z" }, - { url = "https://files.pythonhosted.org/packages/b6/1d/c35a956a44b333b064ae1b1c588c2dfa0e01b7ec90884c1972bfcef119c3/pyzmq-25.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7598d2ba821caa37a0f9d54c25164a4fa351ce019d64d0b44b45540950458840", size = 1105501, upload-time = "2023-12-05T07:45:07.18Z" }, - { url = "https://files.pythonhosted.org/packages/18/d1/b3d1e985318ed7287737ea9e6b6e21748cc7c89accc2443347cd2c8d5f0f/pyzmq-25.1.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d1299d7e964c13607efd148ca1f07dcbf27c3ab9e125d1d0ae1d580a1682399d", size = 1109513, upload-time = "2023-12-05T07:39:53.338Z" }, - { url = "https://files.pythonhosted.org/packages/14/9b/341cdfb47440069010101403298dc24d449150370c6cb322e73bfa1949bd/pyzmq-25.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4e6f689880d5ad87918430957297c975203a082d9a036cc426648fcbedae769b", size = 1433541, upload-time = "2023-12-05T07:41:10.786Z" }, - { url = "https://files.pythonhosted.org/packages/fa/52/c6d4e76e020c554e965459d41a98201b4d45277a288648f53a4e5a2429cc/pyzmq-25.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cc69949484171cc961e6ecd4a8911b9ce7a0d1f738fcae717177c231bf77437b", size = 1766133, upload-time = "2023-12-05T07:49:11.204Z" }, - { url = "https://files.pythonhosted.org/packages/1d/6d/0cbd8dd5b8979fd6b9cf1852ed067b9d2cd6fa0c09c3bafe6874d2d2e03c/pyzmq-25.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9880078f683466b7f567b8624bfc16cad65077be046b6e8abb53bed4eeb82dd3", size = 1653636, upload-time = "2023-12-05T07:49:13.787Z" }, - { url = "https://files.pythonhosted.org/packages/f5/af/d90eed9cf3840685d54d4a35d5f9e242a8a48b5410d41146f14c1e098302/pyzmq-25.1.2-cp311-cp311-win32.whl", hash = "sha256:4e5837af3e5aaa99a091302df5ee001149baff06ad22b722d34e30df5f0d9097", size = 904865, upload-time = "2023-12-05T07:42:07.189Z" }, - { url = "https://files.pythonhosted.org/packages/20/d2/09443dc73053ad01c846d7fb77e09fe9d93c09d4e900215f3c8b7b56bfec/pyzmq-25.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:25c2dbb97d38b5ac9fd15586e048ec5eb1e38f3d47fe7d92167b0c77bb3584e9", size = 1171332, upload-time = "2023-12-05T07:44:56.111Z" }, - { url = "https://files.pythonhosted.org/packages/6e/f0/d71cf69dc039c9adc8b625efc3bad3684f3660a570e47f0f0c64df787b41/pyzmq-25.1.2-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:11e70516688190e9c2db14fcf93c04192b02d457b582a1f6190b154691b4c93a", size = 1871111, upload-time = "2023-12-05T07:48:17.868Z" }, - { url = "https://files.pythonhosted.org/packages/68/62/d365773edf56ad71993579ee574105f02f83530caf600ebf28bea15d88d0/pyzmq-25.1.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:313c3794d650d1fccaaab2df942af9f2c01d6217c846177cfcbc693c7410839e", size = 1248844, upload-time = "2023-12-05T07:50:42.922Z" }, - { url = "https://files.pythonhosted.org/packages/72/55/cc3163e20f40615a49245fa7041badec6103e8ee7e482dbb0feea00a7b84/pyzmq-25.1.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b3cbba2f47062b85fe0ef9de5b987612140a9ba3a9c6d2543c6dec9f7c2ab27", size = 899373, upload-time = "2023-12-05T07:42:29.595Z" }, - { url = "https://files.pythonhosted.org/packages/40/aa/ae292bd85deda637230970bbc53c1dc53696a99e82fc7cd6d373ec173853/pyzmq-25.1.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc31baa0c32a2ca660784d5af3b9487e13b61b3032cb01a115fce6588e1bed30", size = 1160901, upload-time = "2023-12-05T07:44:33.819Z" }, - { url = "https://files.pythonhosted.org/packages/93/b7/6e291eafbbbc66d0e87658dd21383ec2b4ab35edcfb283902c580a6db76f/pyzmq-25.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02c9087b109070c5ab0b383079fa1b5f797f8d43e9a66c07a4b8b8bdecfd88ee", size = 1101147, upload-time = "2023-12-05T07:45:10.058Z" }, - { url = "https://files.pythonhosted.org/packages/3a/f1/e296d5a507eac519d1fe1382851b1a4575f690bc2b2d2c8eca2ed7e4bd1f/pyzmq-25.1.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:f8429b17cbb746c3e043cb986328da023657e79d5ed258b711c06a70c2ea7537", size = 1105315, upload-time = "2023-12-05T07:39:55.851Z" }, - { url = "https://files.pythonhosted.org/packages/56/63/5c2abb556ab4cf013d98e01782d5bd642238a0ed9b019e965a7d7e957f56/pyzmq-25.1.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5074adeacede5f810b7ef39607ee59d94e948b4fd954495bdb072f8c54558181", size = 1427747, upload-time = "2023-12-05T07:41:13.219Z" }, - { url = "https://files.pythonhosted.org/packages/b1/71/5dba5f6b12ef54fb977c9b9279075e151c04fc0dd6851e9663d9e66b593f/pyzmq-25.1.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7ae8f354b895cbd85212da245f1a5ad8159e7840e37d78b476bb4f4c3f32a9fe", size = 1762221, upload-time = "2023-12-05T07:49:16.352Z" }, - { url = "https://files.pythonhosted.org/packages/cf/49/54d7e8bb3df82a3509325b11491d33450dc91580d4826b62fa5e554bb9cf/pyzmq-25.1.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b264bf2cc96b5bc43ce0e852be995e400376bd87ceb363822e2cb1964fcdc737", size = 1649505, upload-time = "2023-12-05T07:49:18.952Z" }, - { url = "https://files.pythonhosted.org/packages/34/14/58e5037229bc37963e2ce804c2c075a3a541e3f84bf1c231e7c9779d36f1/pyzmq-25.1.2-cp312-cp312-win32.whl", hash = "sha256:02bbc1a87b76e04fd780b45e7f695471ae6de747769e540da909173d50ff8e2d", size = 954891, upload-time = "2023-12-05T07:42:09.208Z" }, - { url = "https://files.pythonhosted.org/packages/2c/2d/04fab685ef3a8e6e955220fd2a54dc99efaee960a88675bf5c92cd277164/pyzmq-25.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:ced111c2e81506abd1dc142e6cd7b68dd53747b3b7ae5edbea4578c5eeff96b7", size = 1252773, upload-time = "2023-12-05T07:44:58.16Z" }, - { url = "https://files.pythonhosted.org/packages/6b/fe/ed38fe12c540bafc1cae32c3ff638e9df32528f5cf91b5e400e6a8f5b3ec/pyzmq-25.1.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a8c1d566344aee826b74e472e16edae0a02e2a044f14f7c24e123002dcff1c05", size = 963654, upload-time = "2023-12-05T07:47:03.874Z" }, - { url = "https://files.pythonhosted.org/packages/44/97/a760a2dff0672c408f22f726f2ea10a7a516ffa5001ca5a3641e355a45f9/pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:759cfd391a0996345ba94b6a5110fca9c557ad4166d86a6e81ea526c376a01e8", size = 609436, upload-time = "2023-12-05T07:42:37.762Z" }, - { url = "https://files.pythonhosted.org/packages/41/81/ace39daa19c78b2f4fc12ef217d9d5f1ac658d5828d692bbbb68240cd55b/pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c61e346ac34b74028ede1c6b4bcecf649d69b707b3ff9dc0fab453821b04d1e", size = 843396, upload-time = "2023-12-05T07:44:43.727Z" }, - { url = "https://files.pythonhosted.org/packages/4c/43/150b0b203f5461a9aeadaa925c55167e2b4215c9322b6911a64360d2243e/pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cb8fc1f8d69b411b8ec0b5f1ffbcaf14c1db95b6bccea21d83610987435f1a4", size = 800856, upload-time = "2023-12-05T07:45:21.117Z" }, - { url = "https://files.pythonhosted.org/packages/5f/91/a618b56aaabe40dddcd25db85624d7408768fd32f5bfcf81bc0af5b1ce75/pyzmq-25.1.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3c00c9b7d1ca8165c610437ca0c92e7b5607b2f9076f4eb4b095c85d6e680a1d", size = 413836, upload-time = "2023-12-05T07:53:22.583Z" }, + { url = "https://files.pythonhosted.org/packages/5e/f4/901edb48b2b2c00ad73de0db2ee76e24ce5903ef815ad0ad10e14555d989/pyzmq-25.1.2-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:e624c789359f1a16f83f35e2c705d07663ff2b4d4479bad35621178d8f0f6ea4", size = 1872310 }, + { url = "https://files.pythonhosted.org/packages/5e/46/2de69c7c79fd78bf4c22a9e8165fa6312f5d49410f1be6ddab51a6fe7236/pyzmq-25.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:49151b0efece79f6a79d41a461d78535356136ee70084a1c22532fc6383f4ad0", size = 1249619 }, + { url = "https://files.pythonhosted.org/packages/d1/f5/d6b9755713843bf9701ae86bf6fd97ec294a52cf2af719cd14fdf9392f65/pyzmq-25.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9a5f194cf730f2b24d6af1f833c14c10f41023da46a7f736f48b6d35061e76e", size = 897360 }, + { url = "https://files.pythonhosted.org/packages/7c/88/c1aef8820f12e710d136024d231e70e24684a01314aa1814f0758960ba01/pyzmq-25.1.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:faf79a302f834d9e8304fafdc11d0d042266667ac45209afa57e5efc998e3872", size = 1156959 }, + { url = "https://files.pythonhosted.org/packages/82/1b/b25d2c4ac3b4dae238c98e63395dbb88daf11968b168948d3c6289c3e95c/pyzmq-25.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f51a7b4ead28d3fca8dda53216314a553b0f7a91ee8fc46a72b402a78c3e43d", size = 1100585 }, + { url = "https://files.pythonhosted.org/packages/67/bf/6bc0977acd934b66eacab79cec303ecf08ae4a6150d57c628aa919615488/pyzmq-25.1.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:0ddd6d71d4ef17ba5a87becf7ddf01b371eaba553c603477679ae817a8d84d75", size = 1109267 }, + { url = "https://files.pythonhosted.org/packages/64/fb/4f07424e56c6a5fb47306d9ba744c3c250250c2e7272f9c81efbf8daaccf/pyzmq-25.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:246747b88917e4867e2367b005fc8eefbb4a54b7db363d6c92f89d69abfff4b6", size = 1431853 }, + { url = "https://files.pythonhosted.org/packages/a2/10/2b88c1d4beb59a1d45c13983c4b7c5dcd6ef7988db3c03d23b0cabc5adca/pyzmq-25.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:00c48ae2fd81e2a50c3485de1b9d5c7c57cd85dc8ec55683eac16846e57ac979", size = 1766212 }, + { url = "https://files.pythonhosted.org/packages/bc/ab/c9a22eacfd5bd82620501ae426a3dd6ffa374ac335b21e54209d7a93d3fb/pyzmq-25.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5a68d491fc20762b630e5db2191dd07ff89834086740f70e978bb2ef2668be08", size = 1653737 }, + { url = "https://files.pythonhosted.org/packages/d6/e5/71bd89e47eedb7ebec31ef9a49dcdb0517dbbb063bd5de363980a6911eb1/pyzmq-25.1.2-cp310-cp310-win32.whl", hash = "sha256:09dfe949e83087da88c4a76767df04b22304a682d6154de2c572625c62ad6886", size = 906288 }, + { url = "https://files.pythonhosted.org/packages/9d/5f/2defc8a579e8b5679d92720ab3a4cb93e3a77d923070bf4c1a103d3ae478/pyzmq-25.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:fa99973d2ed20417744fca0073390ad65ce225b546febb0580358e36aa90dba6", size = 1170923 }, + { url = "https://files.pythonhosted.org/packages/35/de/7579518bc58cebf92568b48e354a702fb52525d0fab166dc544f2a0615dc/pyzmq-25.1.2-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:82544e0e2d0c1811482d37eef297020a040c32e0687c1f6fc23a75b75db8062c", size = 1870360 }, + { url = "https://files.pythonhosted.org/packages/ce/f9/58b6cc9a110b1832f666fa6b5a67dc4d520fabfc680ca87a8167b2061d5d/pyzmq-25.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:01171fc48542348cd1a360a4b6c3e7d8f46cdcf53a8d40f84db6707a6768acc1", size = 1249008 }, + { url = "https://files.pythonhosted.org/packages/bc/4a/ac6469c01813cb3652ab4e30ec4a37815cc9949afc18af33f64e2ec704aa/pyzmq-25.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc69c96735ab501419c432110016329bf0dea8898ce16fab97c6d9106dc0b348", size = 904394 }, + { url = "https://files.pythonhosted.org/packages/77/b7/8cee519b11bdd3f76c1a6eb537ab13c1bfef2964d725717705c86f524e4c/pyzmq-25.1.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3e124e6b1dd3dfbeb695435dff0e383256655bb18082e094a8dd1f6293114642", size = 1161453 }, + { url = "https://files.pythonhosted.org/packages/b6/1d/c35a956a44b333b064ae1b1c588c2dfa0e01b7ec90884c1972bfcef119c3/pyzmq-25.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7598d2ba821caa37a0f9d54c25164a4fa351ce019d64d0b44b45540950458840", size = 1105501 }, + { url = "https://files.pythonhosted.org/packages/18/d1/b3d1e985318ed7287737ea9e6b6e21748cc7c89accc2443347cd2c8d5f0f/pyzmq-25.1.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d1299d7e964c13607efd148ca1f07dcbf27c3ab9e125d1d0ae1d580a1682399d", size = 1109513 }, + { url = "https://files.pythonhosted.org/packages/14/9b/341cdfb47440069010101403298dc24d449150370c6cb322e73bfa1949bd/pyzmq-25.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4e6f689880d5ad87918430957297c975203a082d9a036cc426648fcbedae769b", size = 1433541 }, + { url = "https://files.pythonhosted.org/packages/fa/52/c6d4e76e020c554e965459d41a98201b4d45277a288648f53a4e5a2429cc/pyzmq-25.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cc69949484171cc961e6ecd4a8911b9ce7a0d1f738fcae717177c231bf77437b", size = 1766133 }, + { url = "https://files.pythonhosted.org/packages/1d/6d/0cbd8dd5b8979fd6b9cf1852ed067b9d2cd6fa0c09c3bafe6874d2d2e03c/pyzmq-25.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9880078f683466b7f567b8624bfc16cad65077be046b6e8abb53bed4eeb82dd3", size = 1653636 }, + { url = "https://files.pythonhosted.org/packages/f5/af/d90eed9cf3840685d54d4a35d5f9e242a8a48b5410d41146f14c1e098302/pyzmq-25.1.2-cp311-cp311-win32.whl", hash = "sha256:4e5837af3e5aaa99a091302df5ee001149baff06ad22b722d34e30df5f0d9097", size = 904865 }, + { url = "https://files.pythonhosted.org/packages/20/d2/09443dc73053ad01c846d7fb77e09fe9d93c09d4e900215f3c8b7b56bfec/pyzmq-25.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:25c2dbb97d38b5ac9fd15586e048ec5eb1e38f3d47fe7d92167b0c77bb3584e9", size = 1171332 }, + { url = "https://files.pythonhosted.org/packages/6e/f0/d71cf69dc039c9adc8b625efc3bad3684f3660a570e47f0f0c64df787b41/pyzmq-25.1.2-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:11e70516688190e9c2db14fcf93c04192b02d457b582a1f6190b154691b4c93a", size = 1871111 }, + { url = "https://files.pythonhosted.org/packages/68/62/d365773edf56ad71993579ee574105f02f83530caf600ebf28bea15d88d0/pyzmq-25.1.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:313c3794d650d1fccaaab2df942af9f2c01d6217c846177cfcbc693c7410839e", size = 1248844 }, + { url = "https://files.pythonhosted.org/packages/72/55/cc3163e20f40615a49245fa7041badec6103e8ee7e482dbb0feea00a7b84/pyzmq-25.1.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b3cbba2f47062b85fe0ef9de5b987612140a9ba3a9c6d2543c6dec9f7c2ab27", size = 899373 }, + { url = "https://files.pythonhosted.org/packages/40/aa/ae292bd85deda637230970bbc53c1dc53696a99e82fc7cd6d373ec173853/pyzmq-25.1.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc31baa0c32a2ca660784d5af3b9487e13b61b3032cb01a115fce6588e1bed30", size = 1160901 }, + { url = "https://files.pythonhosted.org/packages/93/b7/6e291eafbbbc66d0e87658dd21383ec2b4ab35edcfb283902c580a6db76f/pyzmq-25.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02c9087b109070c5ab0b383079fa1b5f797f8d43e9a66c07a4b8b8bdecfd88ee", size = 1101147 }, + { url = "https://files.pythonhosted.org/packages/3a/f1/e296d5a507eac519d1fe1382851b1a4575f690bc2b2d2c8eca2ed7e4bd1f/pyzmq-25.1.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:f8429b17cbb746c3e043cb986328da023657e79d5ed258b711c06a70c2ea7537", size = 1105315 }, + { url = "https://files.pythonhosted.org/packages/56/63/5c2abb556ab4cf013d98e01782d5bd642238a0ed9b019e965a7d7e957f56/pyzmq-25.1.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5074adeacede5f810b7ef39607ee59d94e948b4fd954495bdb072f8c54558181", size = 1427747 }, + { url = "https://files.pythonhosted.org/packages/b1/71/5dba5f6b12ef54fb977c9b9279075e151c04fc0dd6851e9663d9e66b593f/pyzmq-25.1.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7ae8f354b895cbd85212da245f1a5ad8159e7840e37d78b476bb4f4c3f32a9fe", size = 1762221 }, + { url = "https://files.pythonhosted.org/packages/cf/49/54d7e8bb3df82a3509325b11491d33450dc91580d4826b62fa5e554bb9cf/pyzmq-25.1.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b264bf2cc96b5bc43ce0e852be995e400376bd87ceb363822e2cb1964fcdc737", size = 1649505 }, + { url = "https://files.pythonhosted.org/packages/34/14/58e5037229bc37963e2ce804c2c075a3a541e3f84bf1c231e7c9779d36f1/pyzmq-25.1.2-cp312-cp312-win32.whl", hash = "sha256:02bbc1a87b76e04fd780b45e7f695471ae6de747769e540da909173d50ff8e2d", size = 954891 }, + { url = "https://files.pythonhosted.org/packages/2c/2d/04fab685ef3a8e6e955220fd2a54dc99efaee960a88675bf5c92cd277164/pyzmq-25.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:ced111c2e81506abd1dc142e6cd7b68dd53747b3b7ae5edbea4578c5eeff96b7", size = 1252773 }, + { url = "https://files.pythonhosted.org/packages/6b/fe/ed38fe12c540bafc1cae32c3ff638e9df32528f5cf91b5e400e6a8f5b3ec/pyzmq-25.1.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a8c1d566344aee826b74e472e16edae0a02e2a044f14f7c24e123002dcff1c05", size = 963654 }, + { url = "https://files.pythonhosted.org/packages/44/97/a760a2dff0672c408f22f726f2ea10a7a516ffa5001ca5a3641e355a45f9/pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:759cfd391a0996345ba94b6a5110fca9c557ad4166d86a6e81ea526c376a01e8", size = 609436 }, + { url = "https://files.pythonhosted.org/packages/41/81/ace39daa19c78b2f4fc12ef217d9d5f1ac658d5828d692bbbb68240cd55b/pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c61e346ac34b74028ede1c6b4bcecf649d69b707b3ff9dc0fab453821b04d1e", size = 843396 }, + { url = "https://files.pythonhosted.org/packages/4c/43/150b0b203f5461a9aeadaa925c55167e2b4215c9322b6911a64360d2243e/pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cb8fc1f8d69b411b8ec0b5f1ffbcaf14c1db95b6bccea21d83610987435f1a4", size = 800856 }, + { url = "https://files.pythonhosted.org/packages/5f/91/a618b56aaabe40dddcd25db85624d7408768fd32f5bfcf81bc0af5b1ce75/pyzmq-25.1.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3c00c9b7d1ca8165c610437ca0c92e7b5607b2f9076f4eb4b095c85d6e680a1d", size = 413836 }, ] [[package]] @@ -2194,12 +2420,13 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, { name = "opencv-python-headless" }, - { name = "scikit-learn" }, + { name = "scikit-learn", version = "1.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scikit-learn", version = "1.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3e/2d/bab8babd9dc9a9e4df6eb115540cee4322c1a74078fb6f3b3ebc452a22b3/qudida-0.0.4.tar.gz", hash = "sha256:db198e2887ab0c9aa0023e565afbff41dfb76b361f85fd5e13f780d75ba18cc8", size = 3100, upload-time = "2021-08-09T16:47:55.807Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3e/2d/bab8babd9dc9a9e4df6eb115540cee4322c1a74078fb6f3b3ebc452a22b3/qudida-0.0.4.tar.gz", hash = "sha256:db198e2887ab0c9aa0023e565afbff41dfb76b361f85fd5e13f780d75ba18cc8", size = 3100 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f0/a1/a5f4bebaa31d109003909809d88aeb0d4b201463a9ea29308d9e4f9e7655/qudida-0.0.4-py3-none-any.whl", hash = "sha256:4519714c40cd0f2e6c51e1735edae8f8b19f4efe1f33be13e9d644ca5f736dd6", size = 3478, upload-time = "2021-08-09T16:47:54.637Z" }, + { url = "https://files.pythonhosted.org/packages/f0/a1/a5f4bebaa31d109003909809d88aeb0d4b201463a9ea29308d9e4f9e7655/qudida-0.0.4-py3-none-any.whl", hash = "sha256:4519714c40cd0f2e6c51e1735edae8f8b19f4efe1f33be13e9d644ca5f736dd6", size = 3478 }, ] [[package]] @@ -2212,23 +2439,22 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218, upload-time = "2024-05-29T15:37:49.536Z" } +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload-time = "2024-05-29T15:37:47.027Z" }, + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, ] [[package]] name = "rich" -version = "14.0.0" +version = "14.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078, upload-time = "2025-03-30T14:15:14.23Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/75/af448d8e52bf1d8fa6a9d089ca6c07ff4453d86c65c145d0a300bb073b9b/rich-14.1.0.tar.gz", hash = "sha256:e497a48b844b0320d45007cdebfeaeed8db2a4f4bcf49f15e455cfc4af11eaa8", size = 224441 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229, upload-time = "2025-03-30T14:15:12.283Z" }, + { url = "https://files.pythonhosted.org/packages/e3/30/3c4d035596d3cf444529e0b2953ad0466f6049528a879d27534700580395/rich-14.1.0-py3-none-any.whl", hash = "sha256:536f5f1785986d6dbdea3c75205c473f970777b4a0d6c6dd1b696aa05a3fa04f", size = 243368 }, ] [[package]] @@ -2241,9 +2467,9 @@ dependencies = [ { name = "ruamel-yaml" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/db/76b40afe343f8a8c5222300da425e0dace30ce639a94776468b1d157311b/rknn_toolkit_lite2-2.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:821e80c95e6838308c133915660b1a6ae78bb8d079b2cbbd46a02dae61192d33", size = 559386, upload-time = "2025-04-09T09:39:54.414Z" }, - { url = "https://files.pythonhosted.org/packages/c1/3d/e80e1742420f62cb628d40a8bf547d6f7c9dbe4e13dcb7b7e7c0b5620e74/rknn_toolkit_lite2-2.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bda74f1179e15fccb8726054a24898982522784b65bb340b20146955d254e800", size = 569160, upload-time = "2025-04-09T09:39:56.149Z" }, - { url = "https://files.pythonhosted.org/packages/ff/db/64c756f3f06b219e92ff4f0fd4e000870ee49f214d505ff01c8b0275e26d/rknn_toolkit_lite2-2.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1e4ec691fed900c0e6fde5e7d8eeba17f806aa45092b63b361ee775e2c1b50e", size = 527458, upload-time = "2025-04-09T09:39:58.881Z" }, + { url = "https://files.pythonhosted.org/packages/ab/db/76b40afe343f8a8c5222300da425e0dace30ce639a94776468b1d157311b/rknn_toolkit_lite2-2.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:821e80c95e6838308c133915660b1a6ae78bb8d079b2cbbd46a02dae61192d33", size = 559386 }, + { url = "https://files.pythonhosted.org/packages/c1/3d/e80e1742420f62cb628d40a8bf547d6f7c9dbe4e13dcb7b7e7c0b5620e74/rknn_toolkit_lite2-2.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bda74f1179e15fccb8726054a24898982522784b65bb340b20146955d254e800", size = 569160 }, + { url = "https://files.pythonhosted.org/packages/ff/db/64c756f3f06b219e92ff4f0fd4e000870ee49f214d505ff01c8b0275e26d/rknn_toolkit_lite2-2.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1e4ec691fed900c0e6fde5e7d8eeba17f806aa45092b63b361ee775e2c1b50e", size = 527458 }, ] [[package]] @@ -2253,78 +2479,78 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ruamel-yaml-clib", marker = "python_full_version < '3.13' and platform_python_implementation == 'CPython'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ea/46/f44d8be06b85bc7c4d8c95d658be2b68f27711f279bf9dd0612a5e4794f5/ruamel.yaml-0.18.10.tar.gz", hash = "sha256:20c86ab29ac2153f80a428e1254a8adf686d3383df04490514ca3b79a362db58", size = 143447, upload-time = "2025-01-06T14:08:51.334Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ea/46/f44d8be06b85bc7c4d8c95d658be2b68f27711f279bf9dd0612a5e4794f5/ruamel.yaml-0.18.10.tar.gz", hash = "sha256:20c86ab29ac2153f80a428e1254a8adf686d3383df04490514ca3b79a362db58", size = 143447 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/36/dfc1ebc0081e6d39924a2cc53654497f967a084a436bb64402dfce4254d9/ruamel.yaml-0.18.10-py3-none-any.whl", hash = "sha256:30f22513ab2301b3d2b577adc121c6471f28734d3d9728581245f1e76468b4f1", size = 117729, upload-time = "2025-01-06T14:08:47.471Z" }, + { url = "https://files.pythonhosted.org/packages/c2/36/dfc1ebc0081e6d39924a2cc53654497f967a084a436bb64402dfce4254d9/ruamel.yaml-0.18.10-py3-none-any.whl", hash = "sha256:30f22513ab2301b3d2b577adc121c6471f28734d3d9728581245f1e76468b4f1", size = 117729 }, ] [[package]] name = "ruamel-yaml-clib" version = "0.2.12" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/84/80203abff8ea4993a87d823a5f632e4d92831ef75d404c9fc78d0176d2b5/ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f", size = 225315, upload-time = "2024-10-20T10:10:56.22Z" } +sdist = { url = "https://files.pythonhosted.org/packages/20/84/80203abff8ea4993a87d823a5f632e4d92831ef75d404c9fc78d0176d2b5/ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f", size = 225315 } wheels = [ - { url = "https://files.pythonhosted.org/packages/70/57/40a958e863e299f0c74ef32a3bde9f2d1ea8d69669368c0c502a0997f57f/ruamel.yaml.clib-0.2.12-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:11f891336688faf5156a36293a9c362bdc7c88f03a8a027c2c1d8e0bcde998e5", size = 131301, upload-time = "2024-10-20T10:12:35.876Z" }, - { url = "https://files.pythonhosted.org/packages/98/a8/29a3eb437b12b95f50a6bcc3d7d7214301c6c529d8fdc227247fa84162b5/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a606ef75a60ecf3d924613892cc603b154178ee25abb3055db5062da811fd969", size = 633728, upload-time = "2024-10-20T10:12:37.858Z" }, - { url = "https://files.pythonhosted.org/packages/35/6d/ae05a87a3ad540259c3ad88d71275cbd1c0f2d30ae04c65dcbfb6dcd4b9f/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd5415dded15c3822597455bc02bcd66e81ef8b7a48cb71a33628fc9fdde39df", size = 722230, upload-time = "2024-10-20T10:12:39.457Z" }, - { url = "https://files.pythonhosted.org/packages/7f/b7/20c6f3c0b656fe609675d69bc135c03aac9e3865912444be6339207b6648/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f66efbc1caa63c088dead1c4170d148eabc9b80d95fb75b6c92ac0aad2437d76", size = 686712, upload-time = "2024-10-20T10:12:41.119Z" }, - { url = "https://files.pythonhosted.org/packages/cd/11/d12dbf683471f888d354dac59593873c2b45feb193c5e3e0f2ebf85e68b9/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22353049ba4181685023b25b5b51a574bce33e7f51c759371a7422dcae5402a6", size = 663936, upload-time = "2024-10-21T11:26:37.419Z" }, - { url = "https://files.pythonhosted.org/packages/72/14/4c268f5077db5c83f743ee1daeb236269fa8577133a5cfa49f8b382baf13/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:932205970b9f9991b34f55136be327501903f7c66830e9760a8ffb15b07f05cd", size = 696580, upload-time = "2024-10-21T11:26:39.503Z" }, - { url = "https://files.pythonhosted.org/packages/30/fc/8cd12f189c6405a4c1cf37bd633aa740a9538c8e40497c231072d0fef5cf/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a52d48f4e7bf9005e8f0a89209bf9a73f7190ddf0489eee5eb51377385f59f2a", size = 663393, upload-time = "2024-12-11T19:58:13.873Z" }, - { url = "https://files.pythonhosted.org/packages/80/29/c0a017b704aaf3cbf704989785cd9c5d5b8ccec2dae6ac0c53833c84e677/ruamel.yaml.clib-0.2.12-cp310-cp310-win32.whl", hash = "sha256:3eac5a91891ceb88138c113f9db04f3cebdae277f5d44eaa3651a4f573e6a5da", size = 100326, upload-time = "2024-10-20T10:12:42.967Z" }, - { url = "https://files.pythonhosted.org/packages/3a/65/fa39d74db4e2d0cd252355732d966a460a41cd01c6353b820a0952432839/ruamel.yaml.clib-0.2.12-cp310-cp310-win_amd64.whl", hash = "sha256:ab007f2f5a87bd08ab1499bdf96f3d5c6ad4dcfa364884cb4549aa0154b13a28", size = 118079, upload-time = "2024-10-20T10:12:44.117Z" }, - { url = "https://files.pythonhosted.org/packages/fb/8f/683c6ad562f558cbc4f7c029abcd9599148c51c54b5ef0f24f2638da9fbb/ruamel.yaml.clib-0.2.12-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6", size = 132224, upload-time = "2024-10-20T10:12:45.162Z" }, - { url = "https://files.pythonhosted.org/packages/3c/d2/b79b7d695e2f21da020bd44c782490578f300dd44f0a4c57a92575758a76/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:d84318609196d6bd6da0edfa25cedfbabd8dbde5140a0a23af29ad4b8f91fb1e", size = 641480, upload-time = "2024-10-20T10:12:46.758Z" }, - { url = "https://files.pythonhosted.org/packages/68/6e/264c50ce2a31473a9fdbf4fa66ca9b2b17c7455b31ef585462343818bd6c/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb43a269eb827806502c7c8efb7ae7e9e9d0573257a46e8e952f4d4caba4f31e", size = 739068, upload-time = "2024-10-20T10:12:48.605Z" }, - { url = "https://files.pythonhosted.org/packages/86/29/88c2567bc893c84d88b4c48027367c3562ae69121d568e8a3f3a8d363f4d/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52", size = 703012, upload-time = "2024-10-20T10:12:51.124Z" }, - { url = "https://files.pythonhosted.org/packages/11/46/879763c619b5470820f0cd6ca97d134771e502776bc2b844d2adb6e37753/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642", size = 704352, upload-time = "2024-10-21T11:26:41.438Z" }, - { url = "https://files.pythonhosted.org/packages/02/80/ece7e6034256a4186bbe50dee28cd032d816974941a6abf6a9d65e4228a7/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2", size = 737344, upload-time = "2024-10-21T11:26:43.62Z" }, - { url = "https://files.pythonhosted.org/packages/f0/ca/e4106ac7e80efbabdf4bf91d3d32fc424e41418458251712f5672eada9ce/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3", size = 714498, upload-time = "2024-12-11T19:58:15.592Z" }, - { url = "https://files.pythonhosted.org/packages/67/58/b1f60a1d591b771298ffa0428237afb092c7f29ae23bad93420b1eb10703/ruamel.yaml.clib-0.2.12-cp311-cp311-win32.whl", hash = "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4", size = 100205, upload-time = "2024-10-20T10:12:52.865Z" }, - { url = "https://files.pythonhosted.org/packages/b4/4f/b52f634c9548a9291a70dfce26ca7ebce388235c93588a1068028ea23fcc/ruamel.yaml.clib-0.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb", size = 118185, upload-time = "2024-10-20T10:12:54.652Z" }, - { url = "https://files.pythonhosted.org/packages/48/41/e7a405afbdc26af961678474a55373e1b323605a4f5e2ddd4a80ea80f628/ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632", size = 133433, upload-time = "2024-10-20T10:12:55.657Z" }, - { url = "https://files.pythonhosted.org/packages/ec/b0/b850385604334c2ce90e3ee1013bd911aedf058a934905863a6ea95e9eb4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:943f32bc9dedb3abff9879edc134901df92cfce2c3d5c9348f172f62eb2d771d", size = 647362, upload-time = "2024-10-20T10:12:57.155Z" }, - { url = "https://files.pythonhosted.org/packages/44/d0/3f68a86e006448fb6c005aee66565b9eb89014a70c491d70c08de597f8e4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c3829bb364fdb8e0332c9931ecf57d9be3519241323c5274bd82f709cebc0c", size = 754118, upload-time = "2024-10-20T10:12:58.501Z" }, - { url = "https://files.pythonhosted.org/packages/52/a9/d39f3c5ada0a3bb2870d7db41901125dbe2434fa4f12ca8c5b83a42d7c53/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd", size = 706497, upload-time = "2024-10-20T10:13:00.211Z" }, - { url = "https://files.pythonhosted.org/packages/b0/fa/097e38135dadd9ac25aecf2a54be17ddf6e4c23e43d538492a90ab3d71c6/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31", size = 698042, upload-time = "2024-10-21T11:26:46.038Z" }, - { url = "https://files.pythonhosted.org/packages/ec/d5/a659ca6f503b9379b930f13bc6b130c9f176469b73b9834296822a83a132/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680", size = 745831, upload-time = "2024-10-21T11:26:47.487Z" }, - { url = "https://files.pythonhosted.org/packages/db/5d/36619b61ffa2429eeaefaab4f3374666adf36ad8ac6330d855848d7d36fd/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d", size = 715692, upload-time = "2024-12-11T19:58:17.252Z" }, - { url = "https://files.pythonhosted.org/packages/b1/82/85cb92f15a4231c89b95dfe08b09eb6adca929ef7df7e17ab59902b6f589/ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5", size = 98777, upload-time = "2024-10-20T10:13:01.395Z" }, - { url = "https://files.pythonhosted.org/packages/d7/8f/c3654f6f1ddb75daf3922c3d8fc6005b1ab56671ad56ffb874d908bfa668/ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4", size = 115523, upload-time = "2024-10-20T10:13:02.768Z" }, - { url = "https://files.pythonhosted.org/packages/29/00/4864119668d71a5fa45678f380b5923ff410701565821925c69780356ffa/ruamel.yaml.clib-0.2.12-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a", size = 132011, upload-time = "2024-10-20T10:13:04.377Z" }, - { url = "https://files.pythonhosted.org/packages/7f/5e/212f473a93ae78c669ffa0cb051e3fee1139cb2d385d2ae1653d64281507/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:e7e3736715fbf53e9be2a79eb4db68e4ed857017344d697e8b9749444ae57475", size = 642488, upload-time = "2024-10-20T10:13:05.906Z" }, - { url = "https://files.pythonhosted.org/packages/1f/8f/ecfbe2123ade605c49ef769788f79c38ddb1c8fa81e01f4dbf5cf1a44b16/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7e75b4965e1d4690e93021adfcecccbca7d61c7bddd8e22406ef2ff20d74ef", size = 745066, upload-time = "2024-10-20T10:13:07.26Z" }, - { url = "https://files.pythonhosted.org/packages/e2/a9/28f60726d29dfc01b8decdb385de4ced2ced9faeb37a847bd5cf26836815/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6", size = 701785, upload-time = "2024-10-20T10:13:08.504Z" }, - { url = "https://files.pythonhosted.org/packages/84/7e/8e7ec45920daa7f76046578e4f677a3215fe8f18ee30a9cb7627a19d9b4c/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf", size = 693017, upload-time = "2024-10-21T11:26:48.866Z" }, - { url = "https://files.pythonhosted.org/packages/c5/b3/d650eaade4ca225f02a648321e1ab835b9d361c60d51150bac49063b83fa/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1", size = 741270, upload-time = "2024-10-21T11:26:50.213Z" }, - { url = "https://files.pythonhosted.org/packages/87/b8/01c29b924dcbbed75cc45b30c30d565d763b9c4d540545a0eeecffb8f09c/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f6f3eac23941b32afccc23081e1f50612bdbe4e982012ef4f5797986828cd01", size = 709059, upload-time = "2024-12-11T19:58:18.846Z" }, - { url = "https://files.pythonhosted.org/packages/30/8c/ed73f047a73638257aa9377ad356bea4d96125b305c34a28766f4445cc0f/ruamel.yaml.clib-0.2.12-cp313-cp313-win32.whl", hash = "sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6", size = 98583, upload-time = "2024-10-20T10:13:09.658Z" }, - { url = "https://files.pythonhosted.org/packages/b0/85/e8e751d8791564dd333d5d9a4eab0a7a115f7e349595417fd50ecae3395c/ruamel.yaml.clib-0.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3", size = 115190, upload-time = "2024-10-20T10:13:10.66Z" }, + { url = "https://files.pythonhosted.org/packages/70/57/40a958e863e299f0c74ef32a3bde9f2d1ea8d69669368c0c502a0997f57f/ruamel.yaml.clib-0.2.12-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:11f891336688faf5156a36293a9c362bdc7c88f03a8a027c2c1d8e0bcde998e5", size = 131301 }, + { url = "https://files.pythonhosted.org/packages/98/a8/29a3eb437b12b95f50a6bcc3d7d7214301c6c529d8fdc227247fa84162b5/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a606ef75a60ecf3d924613892cc603b154178ee25abb3055db5062da811fd969", size = 633728 }, + { url = "https://files.pythonhosted.org/packages/35/6d/ae05a87a3ad540259c3ad88d71275cbd1c0f2d30ae04c65dcbfb6dcd4b9f/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd5415dded15c3822597455bc02bcd66e81ef8b7a48cb71a33628fc9fdde39df", size = 722230 }, + { url = "https://files.pythonhosted.org/packages/7f/b7/20c6f3c0b656fe609675d69bc135c03aac9e3865912444be6339207b6648/ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f66efbc1caa63c088dead1c4170d148eabc9b80d95fb75b6c92ac0aad2437d76", size = 686712 }, + { url = "https://files.pythonhosted.org/packages/cd/11/d12dbf683471f888d354dac59593873c2b45feb193c5e3e0f2ebf85e68b9/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22353049ba4181685023b25b5b51a574bce33e7f51c759371a7422dcae5402a6", size = 663936 }, + { url = "https://files.pythonhosted.org/packages/72/14/4c268f5077db5c83f743ee1daeb236269fa8577133a5cfa49f8b382baf13/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:932205970b9f9991b34f55136be327501903f7c66830e9760a8ffb15b07f05cd", size = 696580 }, + { url = "https://files.pythonhosted.org/packages/30/fc/8cd12f189c6405a4c1cf37bd633aa740a9538c8e40497c231072d0fef5cf/ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a52d48f4e7bf9005e8f0a89209bf9a73f7190ddf0489eee5eb51377385f59f2a", size = 663393 }, + { url = "https://files.pythonhosted.org/packages/80/29/c0a017b704aaf3cbf704989785cd9c5d5b8ccec2dae6ac0c53833c84e677/ruamel.yaml.clib-0.2.12-cp310-cp310-win32.whl", hash = "sha256:3eac5a91891ceb88138c113f9db04f3cebdae277f5d44eaa3651a4f573e6a5da", size = 100326 }, + { url = "https://files.pythonhosted.org/packages/3a/65/fa39d74db4e2d0cd252355732d966a460a41cd01c6353b820a0952432839/ruamel.yaml.clib-0.2.12-cp310-cp310-win_amd64.whl", hash = "sha256:ab007f2f5a87bd08ab1499bdf96f3d5c6ad4dcfa364884cb4549aa0154b13a28", size = 118079 }, + { url = "https://files.pythonhosted.org/packages/fb/8f/683c6ad562f558cbc4f7c029abcd9599148c51c54b5ef0f24f2638da9fbb/ruamel.yaml.clib-0.2.12-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6", size = 132224 }, + { url = "https://files.pythonhosted.org/packages/3c/d2/b79b7d695e2f21da020bd44c782490578f300dd44f0a4c57a92575758a76/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:d84318609196d6bd6da0edfa25cedfbabd8dbde5140a0a23af29ad4b8f91fb1e", size = 641480 }, + { url = "https://files.pythonhosted.org/packages/68/6e/264c50ce2a31473a9fdbf4fa66ca9b2b17c7455b31ef585462343818bd6c/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb43a269eb827806502c7c8efb7ae7e9e9d0573257a46e8e952f4d4caba4f31e", size = 739068 }, + { url = "https://files.pythonhosted.org/packages/86/29/88c2567bc893c84d88b4c48027367c3562ae69121d568e8a3f3a8d363f4d/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52", size = 703012 }, + { url = "https://files.pythonhosted.org/packages/11/46/879763c619b5470820f0cd6ca97d134771e502776bc2b844d2adb6e37753/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642", size = 704352 }, + { url = "https://files.pythonhosted.org/packages/02/80/ece7e6034256a4186bbe50dee28cd032d816974941a6abf6a9d65e4228a7/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2", size = 737344 }, + { url = "https://files.pythonhosted.org/packages/f0/ca/e4106ac7e80efbabdf4bf91d3d32fc424e41418458251712f5672eada9ce/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3", size = 714498 }, + { url = "https://files.pythonhosted.org/packages/67/58/b1f60a1d591b771298ffa0428237afb092c7f29ae23bad93420b1eb10703/ruamel.yaml.clib-0.2.12-cp311-cp311-win32.whl", hash = "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4", size = 100205 }, + { url = "https://files.pythonhosted.org/packages/b4/4f/b52f634c9548a9291a70dfce26ca7ebce388235c93588a1068028ea23fcc/ruamel.yaml.clib-0.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb", size = 118185 }, + { url = "https://files.pythonhosted.org/packages/48/41/e7a405afbdc26af961678474a55373e1b323605a4f5e2ddd4a80ea80f628/ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632", size = 133433 }, + { url = "https://files.pythonhosted.org/packages/ec/b0/b850385604334c2ce90e3ee1013bd911aedf058a934905863a6ea95e9eb4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:943f32bc9dedb3abff9879edc134901df92cfce2c3d5c9348f172f62eb2d771d", size = 647362 }, + { url = "https://files.pythonhosted.org/packages/44/d0/3f68a86e006448fb6c005aee66565b9eb89014a70c491d70c08de597f8e4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c3829bb364fdb8e0332c9931ecf57d9be3519241323c5274bd82f709cebc0c", size = 754118 }, + { url = "https://files.pythonhosted.org/packages/52/a9/d39f3c5ada0a3bb2870d7db41901125dbe2434fa4f12ca8c5b83a42d7c53/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd", size = 706497 }, + { url = "https://files.pythonhosted.org/packages/b0/fa/097e38135dadd9ac25aecf2a54be17ddf6e4c23e43d538492a90ab3d71c6/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31", size = 698042 }, + { url = "https://files.pythonhosted.org/packages/ec/d5/a659ca6f503b9379b930f13bc6b130c9f176469b73b9834296822a83a132/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680", size = 745831 }, + { url = "https://files.pythonhosted.org/packages/db/5d/36619b61ffa2429eeaefaab4f3374666adf36ad8ac6330d855848d7d36fd/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d", size = 715692 }, + { url = "https://files.pythonhosted.org/packages/b1/82/85cb92f15a4231c89b95dfe08b09eb6adca929ef7df7e17ab59902b6f589/ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5", size = 98777 }, + { url = "https://files.pythonhosted.org/packages/d7/8f/c3654f6f1ddb75daf3922c3d8fc6005b1ab56671ad56ffb874d908bfa668/ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4", size = 115523 }, + { url = "https://files.pythonhosted.org/packages/29/00/4864119668d71a5fa45678f380b5923ff410701565821925c69780356ffa/ruamel.yaml.clib-0.2.12-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a", size = 132011 }, + { url = "https://files.pythonhosted.org/packages/7f/5e/212f473a93ae78c669ffa0cb051e3fee1139cb2d385d2ae1653d64281507/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:e7e3736715fbf53e9be2a79eb4db68e4ed857017344d697e8b9749444ae57475", size = 642488 }, + { url = "https://files.pythonhosted.org/packages/1f/8f/ecfbe2123ade605c49ef769788f79c38ddb1c8fa81e01f4dbf5cf1a44b16/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7e75b4965e1d4690e93021adfcecccbca7d61c7bddd8e22406ef2ff20d74ef", size = 745066 }, + { url = "https://files.pythonhosted.org/packages/e2/a9/28f60726d29dfc01b8decdb385de4ced2ced9faeb37a847bd5cf26836815/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6", size = 701785 }, + { url = "https://files.pythonhosted.org/packages/84/7e/8e7ec45920daa7f76046578e4f677a3215fe8f18ee30a9cb7627a19d9b4c/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf", size = 693017 }, + { url = "https://files.pythonhosted.org/packages/c5/b3/d650eaade4ca225f02a648321e1ab835b9d361c60d51150bac49063b83fa/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1", size = 741270 }, + { url = "https://files.pythonhosted.org/packages/87/b8/01c29b924dcbbed75cc45b30c30d565d763b9c4d540545a0eeecffb8f09c/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f6f3eac23941b32afccc23081e1f50612bdbe4e982012ef4f5797986828cd01", size = 709059 }, + { url = "https://files.pythonhosted.org/packages/30/8c/ed73f047a73638257aa9377ad356bea4d96125b305c34a28766f4445cc0f/ruamel.yaml.clib-0.2.12-cp313-cp313-win32.whl", hash = "sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6", size = 98583 }, + { url = "https://files.pythonhosted.org/packages/b0/85/e8e751d8791564dd333d5d9a4eab0a7a115f7e349595417fd50ecae3395c/ruamel.yaml.clib-0.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3", size = 115190 }, ] [[package]] name = "ruff" -version = "0.12.2" +version = "0.12.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6c/3d/d9a195676f25d00dbfcf3cf95fdd4c685c497fcfa7e862a44ac5e4e96480/ruff-0.12.2.tar.gz", hash = "sha256:d7b4f55cd6f325cb7621244f19c873c565a08aff5a4ba9c69aa7355f3f7afd3e", size = 4432239, upload-time = "2025-07-03T16:40:19.566Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4b/da/5bd7565be729e86e1442dad2c9a364ceeff82227c2dece7c29697a9795eb/ruff-0.12.8.tar.gz", hash = "sha256:4cb3a45525176e1009b2b64126acf5f9444ea59066262791febf55e40493a033", size = 5242373 } wheels = [ - { url = "https://files.pythonhosted.org/packages/74/b6/2098d0126d2d3318fd5bec3ad40d06c25d377d95749f7a0c5af17129b3b1/ruff-0.12.2-py3-none-linux_armv6l.whl", hash = "sha256:093ea2b221df1d2b8e7ad92fc6ffdca40a2cb10d8564477a987b44fd4008a7be", size = 10369761, upload-time = "2025-07-03T16:39:38.847Z" }, - { url = "https://files.pythonhosted.org/packages/b1/4b/5da0142033dbe155dc598cfb99262d8ee2449d76920ea92c4eeb9547c208/ruff-0.12.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:09e4cf27cc10f96b1708100fa851e0daf21767e9709e1649175355280e0d950e", size = 11155659, upload-time = "2025-07-03T16:39:42.294Z" }, - { url = "https://files.pythonhosted.org/packages/3e/21/967b82550a503d7c5c5c127d11c935344b35e8c521f52915fc858fb3e473/ruff-0.12.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:8ae64755b22f4ff85e9c52d1f82644abd0b6b6b6deedceb74bd71f35c24044cc", size = 10537769, upload-time = "2025-07-03T16:39:44.75Z" }, - { url = "https://files.pythonhosted.org/packages/33/91/00cff7102e2ec71a4890fb7ba1803f2cdb122d82787c7d7cf8041fe8cbc1/ruff-0.12.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3eb3a6b2db4d6e2c77e682f0b988d4d61aff06860158fdb413118ca133d57922", size = 10717602, upload-time = "2025-07-03T16:39:47.652Z" }, - { url = "https://files.pythonhosted.org/packages/9b/eb/928814daec4e1ba9115858adcda44a637fb9010618721937491e4e2283b8/ruff-0.12.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:73448de992d05517170fc37169cbca857dfeaeaa8c2b9be494d7bcb0d36c8f4b", size = 10198772, upload-time = "2025-07-03T16:39:49.641Z" }, - { url = "https://files.pythonhosted.org/packages/50/fa/f15089bc20c40f4f72334f9145dde55ab2b680e51afb3b55422effbf2fb6/ruff-0.12.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b8b94317cbc2ae4a2771af641739f933934b03555e51515e6e021c64441532d", size = 11845173, upload-time = "2025-07-03T16:39:52.069Z" }, - { url = "https://files.pythonhosted.org/packages/43/9f/1f6f98f39f2b9302acc161a4a2187b1e3a97634fe918a8e731e591841cf4/ruff-0.12.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:45fc42c3bf1d30d2008023a0a9a0cfb06bf9835b147f11fe0679f21ae86d34b1", size = 12553002, upload-time = "2025-07-03T16:39:54.551Z" }, - { url = "https://files.pythonhosted.org/packages/d8/70/08991ac46e38ddd231c8f4fd05ef189b1b94be8883e8c0c146a025c20a19/ruff-0.12.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce48f675c394c37e958bf229fb5c1e843e20945a6d962cf3ea20b7a107dcd9f4", size = 12171330, upload-time = "2025-07-03T16:39:57.55Z" }, - { url = "https://files.pythonhosted.org/packages/88/a9/5a55266fec474acfd0a1c73285f19dd22461d95a538f29bba02edd07a5d9/ruff-0.12.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:793d8859445ea47591272021a81391350205a4af65a9392401f418a95dfb75c9", size = 11774717, upload-time = "2025-07-03T16:39:59.78Z" }, - { url = "https://files.pythonhosted.org/packages/87/e5/0c270e458fc73c46c0d0f7cf970bb14786e5fdb88c87b5e423a4bd65232b/ruff-0.12.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6932323db80484dda89153da3d8e58164d01d6da86857c79f1961934354992da", size = 11646659, upload-time = "2025-07-03T16:40:01.934Z" }, - { url = "https://files.pythonhosted.org/packages/b7/b6/45ab96070c9752af37f0be364d849ed70e9ccede07675b0ec4e3ef76b63b/ruff-0.12.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6aa7e623a3a11538108f61e859ebf016c4f14a7e6e4eba1980190cacb57714ce", size = 10604012, upload-time = "2025-07-03T16:40:04.363Z" }, - { url = "https://files.pythonhosted.org/packages/86/91/26a6e6a424eb147cc7627eebae095cfa0b4b337a7c1c413c447c9ebb72fd/ruff-0.12.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2a4a20aeed74671b2def096bdf2eac610c7d8ffcbf4fb0e627c06947a1d7078d", size = 10176799, upload-time = "2025-07-03T16:40:06.514Z" }, - { url = "https://files.pythonhosted.org/packages/f5/0c/9f344583465a61c8918a7cda604226e77b2c548daf8ef7c2bfccf2b37200/ruff-0.12.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:71a4c550195612f486c9d1f2b045a600aeba851b298c667807ae933478fcef04", size = 11241507, upload-time = "2025-07-03T16:40:08.708Z" }, - { url = "https://files.pythonhosted.org/packages/1c/b7/99c34ded8fb5f86c0280278fa89a0066c3760edc326e935ce0b1550d315d/ruff-0.12.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:4987b8f4ceadf597c927beee65a5eaf994c6e2b631df963f86d8ad1bdea99342", size = 11717609, upload-time = "2025-07-03T16:40:10.836Z" }, - { url = "https://files.pythonhosted.org/packages/51/de/8589fa724590faa057e5a6d171e7f2f6cffe3287406ef40e49c682c07d89/ruff-0.12.2-py3-none-win32.whl", hash = "sha256:369ffb69b70cd55b6c3fc453b9492d98aed98062db9fec828cdfd069555f5f1a", size = 10523823, upload-time = "2025-07-03T16:40:13.203Z" }, - { url = "https://files.pythonhosted.org/packages/94/47/8abf129102ae4c90cba0c2199a1a9b0fa896f6f806238d6f8c14448cc748/ruff-0.12.2-py3-none-win_amd64.whl", hash = "sha256:dca8a3b6d6dc9810ed8f328d406516bf4d660c00caeaef36eb831cf4871b0639", size = 11629831, upload-time = "2025-07-03T16:40:15.478Z" }, - { url = "https://files.pythonhosted.org/packages/e2/1f/72d2946e3cc7456bb837e88000eb3437e55f80db339c840c04015a11115d/ruff-0.12.2-py3-none-win_arm64.whl", hash = "sha256:48d6c6bfb4761df68bc05ae630e24f506755e702d4fb08f08460be778c7ccb12", size = 10735334, upload-time = "2025-07-03T16:40:17.677Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1e/c843bfa8ad1114fab3eb2b78235dda76acd66384c663a4e0415ecc13aa1e/ruff-0.12.8-py3-none-linux_armv6l.whl", hash = "sha256:63cb5a5e933fc913e5823a0dfdc3c99add73f52d139d6cd5cc8639d0e0465513", size = 11675315 }, + { url = "https://files.pythonhosted.org/packages/24/ee/af6e5c2a8ca3a81676d5480a1025494fd104b8896266502bb4de2a0e8388/ruff-0.12.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9a9bbe28f9f551accf84a24c366c1aa8774d6748438b47174f8e8565ab9dedbc", size = 12456653 }, + { url = "https://files.pythonhosted.org/packages/99/9d/e91f84dfe3866fa648c10512904991ecc326fd0b66578b324ee6ecb8f725/ruff-0.12.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:2fae54e752a3150f7ee0e09bce2e133caf10ce9d971510a9b925392dc98d2fec", size = 11659690 }, + { url = "https://files.pythonhosted.org/packages/fe/ac/a363d25ec53040408ebdd4efcee929d48547665858ede0505d1d8041b2e5/ruff-0.12.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0acbcf01206df963d9331b5838fb31f3b44fa979ee7fa368b9b9057d89f4a53", size = 11896923 }, + { url = "https://files.pythonhosted.org/packages/58/9f/ea356cd87c395f6ade9bb81365bd909ff60860975ca1bc39f0e59de3da37/ruff-0.12.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ae3e7504666ad4c62f9ac8eedb52a93f9ebdeb34742b8b71cd3cccd24912719f", size = 11477612 }, + { url = "https://files.pythonhosted.org/packages/1a/46/92e8fa3c9dcfd49175225c09053916cb97bb7204f9f899c2f2baca69e450/ruff-0.12.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb82efb5d35d07497813a1c5647867390a7d83304562607f3579602fa3d7d46f", size = 13182745 }, + { url = "https://files.pythonhosted.org/packages/5e/c4/f2176a310f26e6160deaf661ef60db6c3bb62b7a35e57ae28f27a09a7d63/ruff-0.12.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:dbea798fc0065ad0b84a2947b0aff4233f0cb30f226f00a2c5850ca4393de609", size = 14206885 }, + { url = "https://files.pythonhosted.org/packages/87/9d/98e162f3eeeb6689acbedbae5050b4b3220754554526c50c292b611d3a63/ruff-0.12.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:49ebcaccc2bdad86fd51b7864e3d808aad404aab8df33d469b6e65584656263a", size = 13639381 }, + { url = "https://files.pythonhosted.org/packages/81/4e/1b7478b072fcde5161b48f64774d6edd59d6d198e4ba8918d9f4702b8043/ruff-0.12.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ac9c570634b98c71c88cb17badd90f13fc076a472ba6ef1d113d8ed3df109fb", size = 12613271 }, + { url = "https://files.pythonhosted.org/packages/e8/67/0c3c9179a3ad19791ef1b8f7138aa27d4578c78700551c60d9260b2c660d/ruff-0.12.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:560e0cd641e45591a3e42cb50ef61ce07162b9c233786663fdce2d8557d99818", size = 12847783 }, + { url = "https://files.pythonhosted.org/packages/4e/2a/0b6ac3dd045acf8aa229b12c9c17bb35508191b71a14904baf99573a21bd/ruff-0.12.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:71c83121512e7743fba5a8848c261dcc454cafb3ef2934a43f1b7a4eb5a447ea", size = 11702672 }, + { url = "https://files.pythonhosted.org/packages/9d/ee/f9fdc9f341b0430110de8b39a6ee5fa68c5706dc7c0aa940817947d6937e/ruff-0.12.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:de4429ef2ba091ecddedd300f4c3f24bca875d3d8b23340728c3cb0da81072c3", size = 11440626 }, + { url = "https://files.pythonhosted.org/packages/89/fb/b3aa2d482d05f44e4d197d1de5e3863feb13067b22c571b9561085c999dc/ruff-0.12.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a2cab5f60d5b65b50fba39a8950c8746df1627d54ba1197f970763917184b161", size = 12462162 }, + { url = "https://files.pythonhosted.org/packages/18/9f/5c5d93e1d00d854d5013c96e1a92c33b703a0332707a7cdbd0a4880a84fb/ruff-0.12.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:45c32487e14f60b88aad6be9fd5da5093dbefb0e3e1224131cb1d441d7cb7d46", size = 12913212 }, + { url = "https://files.pythonhosted.org/packages/71/13/ab9120add1c0e4604c71bfc2e4ef7d63bebece0cfe617013da289539cef8/ruff-0.12.8-py3-none-win32.whl", hash = "sha256:daf3475060a617fd5bc80638aeaf2f5937f10af3ec44464e280a9d2218e720d3", size = 11694382 }, + { url = "https://files.pythonhosted.org/packages/f6/dc/a2873b7c5001c62f46266685863bee2888caf469d1edac84bf3242074be2/ruff-0.12.8-py3-none-win_amd64.whl", hash = "sha256:7209531f1a1fcfbe8e46bcd7ab30e2f43604d8ba1c49029bb420b103d0b5f76e", size = 12740482 }, + { url = "https://files.pythonhosted.org/packages/cb/5c/799a1efb8b5abab56e8a9f2a0b72d12bd64bb55815e9476c7d0a2887d2f7/ruff-0.12.8-py3-none-win_arm64.whl", hash = "sha256:c90e1a334683ce41b0e7a04f41790c429bf5073b62c1ae701c9dc5b3d14f0749", size = 11884718 }, ] [[package]] @@ -2338,93 +2564,236 @@ dependencies = [ { name = "numpy" }, { name = "packaging" }, { name = "pillow" }, - { name = "scipy" }, + { name = "scipy", version = "1.11.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "tifffile" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/65/c1/a49da20845f0f0e1afbb1c2586d406dc0acb84c26ae293bad6d7e7f718bc/scikit_image-0.22.0.tar.gz", hash = "sha256:018d734df1d2da2719087d15f679d19285fce97cd37695103deadfaef2873236", size = 22685018, upload-time = "2023-10-03T21:36:34.274Z" } +sdist = { url = "https://files.pythonhosted.org/packages/65/c1/a49da20845f0f0e1afbb1c2586d406dc0acb84c26ae293bad6d7e7f718bc/scikit_image-0.22.0.tar.gz", hash = "sha256:018d734df1d2da2719087d15f679d19285fce97cd37695103deadfaef2873236", size = 22685018 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/8c/381ae42b37cf3e9e99a1deb3ffe76ca5ff5dd18ffa368293476164507fad/scikit_image-0.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74ec5c1d4693506842cc7c9487c89d8fc32aed064e9363def7af08b8f8cbb31d", size = 13905039, upload-time = "2023-10-03T21:35:27.279Z" }, - { url = "https://files.pythonhosted.org/packages/16/06/4bfba08f5cce26d5070bb2cf4e3f9f479480978806355d1c5bea6f26a17c/scikit_image-0.22.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:a05ae4fe03d802587ed8974e900b943275548cde6a6807b785039d63e9a7a5ff", size = 13279212, upload-time = "2023-10-03T21:35:30.864Z" }, - { url = "https://files.pythonhosted.org/packages/74/57/dbf744ca00eea2a09b1848c9dec28a43978c16dc049b1fba949cb050bedf/scikit_image-0.22.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a92dca3d95b1301442af055e196a54b5a5128c6768b79fc0a4098f1d662dee6", size = 14091779, upload-time = "2023-10-03T21:35:34.273Z" }, - { url = "https://files.pythonhosted.org/packages/f1/6c/49f5a0ce8ddcdbdac5ac69c129654938cc6de0a936303caa6cad495ceb2a/scikit_image-0.22.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3663d063d8bf2fb9bdfb0ca967b9ee3b6593139c860c7abc2d2351a8a8863938", size = 14682042, upload-time = "2023-10-03T21:35:37.787Z" }, - { url = "https://files.pythonhosted.org/packages/86/f0/18895318109f9b508f2310f136922e455a453550826a8240b412063c2528/scikit_image-0.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:ebdbdc901bae14dab637f8d5c99f6d5cc7aaf4a3b6f4003194e003e9f688a6fc", size = 24492345, upload-time = "2023-10-03T21:35:41.122Z" }, - { url = "https://files.pythonhosted.org/packages/9f/d9/dc99e527d1a0050f0353d2fff3548273b4df6151884806e324f26572fd6b/scikit_image-0.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:95d6da2d8a44a36ae04437c76d32deb4e3c993ffc846b394b9949fd8ded73cb2", size = 13883619, upload-time = "2023-10-03T21:35:44.88Z" }, - { url = "https://files.pythonhosted.org/packages/80/37/7670020b112ff9a47e49b1e36f438d000db5b632aab8a8fd7e6be545d065/scikit_image-0.22.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:2c6ef454a85f569659b813ac2a93948022b0298516b757c9c6c904132be327e2", size = 13264761, upload-time = "2023-10-03T21:35:48.865Z" }, - { url = "https://files.pythonhosted.org/packages/ad/85/dadf1194793ac1c895370f3ed048bb91dda083775b42e11d9672a50494d5/scikit_image-0.22.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e87872f067444ee90a00dd49ca897208308645382e8a24bd3e76f301af2352cd", size = 14070710, upload-time = "2023-10-03T21:35:51.711Z" }, - { url = "https://files.pythonhosted.org/packages/d4/34/e27bf2bfe7b52b884b49bd71ea91ff81e4737246735ee5ea383314c31876/scikit_image-0.22.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5c378db54e61b491b9edeefff87e49fcf7fdf729bb93c777d7a5f15d36f743e", size = 14664172, upload-time = "2023-10-03T21:35:55.752Z" }, - { url = "https://files.pythonhosted.org/packages/ce/d0/a3f60c9f57ed295b3076e4acdb29a37bbd8823452562ab2ad51b03d6f377/scikit_image-0.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:2bcb74adb0634258a67f66c2bb29978c9a3e222463e003b67ba12056c003971b", size = 24491321, upload-time = "2023-10-03T21:35:58.847Z" }, - { url = "https://files.pythonhosted.org/packages/da/a4/b0b69bde4d6360e801d647691591dc9967a25a18a4c63ecf7f87d94e3fac/scikit_image-0.22.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:003ca2274ac0fac252280e7179ff986ff783407001459ddea443fe7916e38cff", size = 13968808, upload-time = "2023-10-03T21:36:02.526Z" }, - { url = "https://files.pythonhosted.org/packages/e4/65/3c0f77e7a9bae100a8f7f5cebde410fca1a3cf64e1ecdd343666e27b11d4/scikit_image-0.22.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:cf3c0c15b60ae3e557a0c7575fbd352f0c3ce0afca562febfe3ab80efbeec0e9", size = 13323763, upload-time = "2023-10-03T21:36:05.504Z" }, - { url = "https://files.pythonhosted.org/packages/4a/ed/7faf9f7a55d5b3095d33990a85603b66866cce2a608b27f0e1487d70a451/scikit_image-0.22.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5b23908dd4d120e6aecb1ed0277563e8cbc8d6c0565bdc4c4c6475d53608452", size = 13877233, upload-time = "2023-10-03T21:36:08.352Z" }, - { url = "https://files.pythonhosted.org/packages/ae/9d/09d06f36ce71fa276e1d9453fb4b04250a7038292b13b8c273a5a1a8f7c0/scikit_image-0.22.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be79d7493f320a964f8fcf603121595ba82f84720de999db0fcca002266a549a", size = 14954814, upload-time = "2023-10-03T21:36:11.871Z" }, - { url = "https://files.pythonhosted.org/packages/dc/35/e6327ae498c6f557cb0a7c3fc284effe7958d2d1c43fb61cd77804fc2c4f/scikit_image-0.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:722b970aa5da725dca55252c373b18bbea7858c1cdb406e19f9b01a4a73b30b2", size = 25004857, upload-time = "2023-10-03T21:36:15.457Z" }, + { url = "https://files.pythonhosted.org/packages/9c/8c/381ae42b37cf3e9e99a1deb3ffe76ca5ff5dd18ffa368293476164507fad/scikit_image-0.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74ec5c1d4693506842cc7c9487c89d8fc32aed064e9363def7af08b8f8cbb31d", size = 13905039 }, + { url = "https://files.pythonhosted.org/packages/16/06/4bfba08f5cce26d5070bb2cf4e3f9f479480978806355d1c5bea6f26a17c/scikit_image-0.22.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:a05ae4fe03d802587ed8974e900b943275548cde6a6807b785039d63e9a7a5ff", size = 13279212 }, + { url = "https://files.pythonhosted.org/packages/74/57/dbf744ca00eea2a09b1848c9dec28a43978c16dc049b1fba949cb050bedf/scikit_image-0.22.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a92dca3d95b1301442af055e196a54b5a5128c6768b79fc0a4098f1d662dee6", size = 14091779 }, + { url = "https://files.pythonhosted.org/packages/f1/6c/49f5a0ce8ddcdbdac5ac69c129654938cc6de0a936303caa6cad495ceb2a/scikit_image-0.22.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3663d063d8bf2fb9bdfb0ca967b9ee3b6593139c860c7abc2d2351a8a8863938", size = 14682042 }, + { url = "https://files.pythonhosted.org/packages/86/f0/18895318109f9b508f2310f136922e455a453550826a8240b412063c2528/scikit_image-0.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:ebdbdc901bae14dab637f8d5c99f6d5cc7aaf4a3b6f4003194e003e9f688a6fc", size = 24492345 }, + { url = "https://files.pythonhosted.org/packages/9f/d9/dc99e527d1a0050f0353d2fff3548273b4df6151884806e324f26572fd6b/scikit_image-0.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:95d6da2d8a44a36ae04437c76d32deb4e3c993ffc846b394b9949fd8ded73cb2", size = 13883619 }, + { url = "https://files.pythonhosted.org/packages/80/37/7670020b112ff9a47e49b1e36f438d000db5b632aab8a8fd7e6be545d065/scikit_image-0.22.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:2c6ef454a85f569659b813ac2a93948022b0298516b757c9c6c904132be327e2", size = 13264761 }, + { url = "https://files.pythonhosted.org/packages/ad/85/dadf1194793ac1c895370f3ed048bb91dda083775b42e11d9672a50494d5/scikit_image-0.22.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e87872f067444ee90a00dd49ca897208308645382e8a24bd3e76f301af2352cd", size = 14070710 }, + { url = "https://files.pythonhosted.org/packages/d4/34/e27bf2bfe7b52b884b49bd71ea91ff81e4737246735ee5ea383314c31876/scikit_image-0.22.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5c378db54e61b491b9edeefff87e49fcf7fdf729bb93c777d7a5f15d36f743e", size = 14664172 }, + { url = "https://files.pythonhosted.org/packages/ce/d0/a3f60c9f57ed295b3076e4acdb29a37bbd8823452562ab2ad51b03d6f377/scikit_image-0.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:2bcb74adb0634258a67f66c2bb29978c9a3e222463e003b67ba12056c003971b", size = 24491321 }, + { url = "https://files.pythonhosted.org/packages/da/a4/b0b69bde4d6360e801d647691591dc9967a25a18a4c63ecf7f87d94e3fac/scikit_image-0.22.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:003ca2274ac0fac252280e7179ff986ff783407001459ddea443fe7916e38cff", size = 13968808 }, + { url = "https://files.pythonhosted.org/packages/e4/65/3c0f77e7a9bae100a8f7f5cebde410fca1a3cf64e1ecdd343666e27b11d4/scikit_image-0.22.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:cf3c0c15b60ae3e557a0c7575fbd352f0c3ce0afca562febfe3ab80efbeec0e9", size = 13323763 }, + { url = "https://files.pythonhosted.org/packages/4a/ed/7faf9f7a55d5b3095d33990a85603b66866cce2a608b27f0e1487d70a451/scikit_image-0.22.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5b23908dd4d120e6aecb1ed0277563e8cbc8d6c0565bdc4c4c6475d53608452", size = 13877233 }, + { url = "https://files.pythonhosted.org/packages/ae/9d/09d06f36ce71fa276e1d9453fb4b04250a7038292b13b8c273a5a1a8f7c0/scikit_image-0.22.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be79d7493f320a964f8fcf603121595ba82f84720de999db0fcca002266a549a", size = 14954814 }, + { url = "https://files.pythonhosted.org/packages/dc/35/e6327ae498c6f557cb0a7c3fc284effe7958d2d1c43fb61cd77804fc2c4f/scikit_image-0.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:722b970aa5da725dca55252c373b18bbea7858c1cdb406e19f9b01a4a73b30b2", size = 25004857 }, ] [[package]] name = "scikit-learn" version = "1.3.2" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "joblib" }, - { name = "numpy" }, - { name = "scipy" }, - { name = "threadpoolctl" }, +resolution-markers = [ + "python_full_version < '3.11' and sys_platform == 'darwin'", + "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')", ] -sdist = { url = "https://files.pythonhosted.org/packages/88/00/835e3d280fdd7784e76bdef91dd9487582d7951a7254f59fc8004fc8b213/scikit-learn-1.3.2.tar.gz", hash = "sha256:a2f54c76accc15a34bfb9066e6c7a56c1e7235dda5762b990792330b52ccfb05", size = 7510251, upload-time = "2023-10-23T13:47:55.287Z" } +dependencies = [ + { name = "joblib", marker = "python_full_version < '3.11'" }, + { name = "numpy", marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.11.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "threadpoolctl", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/88/00/835e3d280fdd7784e76bdef91dd9487582d7951a7254f59fc8004fc8b213/scikit-learn-1.3.2.tar.gz", hash = "sha256:a2f54c76accc15a34bfb9066e6c7a56c1e7235dda5762b990792330b52ccfb05", size = 7510251 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/53/570b55a6e10b8694ac1e3024d2df5cd443f1b4ff6d28430845da8b9019b3/scikit_learn-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e326c0eb5cf4d6ba40f93776a20e9a7a69524c4db0757e7ce24ba222471ee8a1", size = 10209999, upload-time = "2023-10-23T13:46:30.373Z" }, - { url = "https://files.pythonhosted.org/packages/70/d0/50ace22129f79830e3cf682d0a2bd4843ef91573299d43112d52790163a8/scikit_learn-1.3.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:535805c2a01ccb40ca4ab7d081d771aea67e535153e35a1fd99418fcedd1648a", size = 9479353, upload-time = "2023-10-23T13:46:34.368Z" }, - { url = "https://files.pythonhosted.org/packages/8f/46/fcc35ed7606c50d3072eae5a107a45cfa5b7f5fa8cc48610edd8cc8e8550/scikit_learn-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1215e5e58e9880b554b01187b8c9390bf4dc4692eedeaf542d3273f4785e342c", size = 10304705, upload-time = "2023-10-23T13:46:37.868Z" }, - { url = "https://files.pythonhosted.org/packages/d0/0b/26ad95cf0b747be967b15fb71a06f5ac67aba0fd2f9cd174de6edefc4674/scikit_learn-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ee107923a623b9f517754ea2f69ea3b62fc898a3641766cb7deb2f2ce450161", size = 10827807, upload-time = "2023-10-23T13:46:41.59Z" }, - { url = "https://files.pythonhosted.org/packages/69/8a/cf17d6443f5f537e099be81535a56ab68a473f9393fbffda38cd19899fc8/scikit_learn-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:35a22e8015048c628ad099da9df5ab3004cdbf81edc75b396fd0cff8699ac58c", size = 9255427, upload-time = "2023-10-23T13:46:44.826Z" }, - { url = "https://files.pythonhosted.org/packages/08/5d/e5acecd6e99a6b656e42e7a7b18284e2f9c9f512e8ed6979e1e75d25f05f/scikit_learn-1.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6fb6bc98f234fda43163ddbe36df8bcde1d13ee176c6dc9b92bb7d3fc842eb66", size = 10116376, upload-time = "2023-10-23T13:46:48.147Z" }, - { url = "https://files.pythonhosted.org/packages/40/c6/2e91eefb757822e70d351e02cc38d07c137212ae7c41ac12746415b4860a/scikit_learn-1.3.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:18424efee518a1cde7b0b53a422cde2f6625197de6af36da0b57ec502f126157", size = 9383415, upload-time = "2023-10-23T13:46:51.324Z" }, - { url = "https://files.pythonhosted.org/packages/fa/fd/b3637639e73bb72b12803c5245f2a7299e09b2acd85a0f23937c53369a1c/scikit_learn-1.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3271552a5eb16f208a6f7f617b8cc6d1f137b52c8a1ef8edf547db0259b2c9fb", size = 10279163, upload-time = "2023-10-23T13:46:54.642Z" }, - { url = "https://files.pythonhosted.org/packages/0c/2a/d3ff6091406bc2207e0adb832ebd15e40ac685811c7e2e3b432bfd969b71/scikit_learn-1.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4144a5004a676d5022b798d9e573b05139e77f271253a4703eed295bde0433", size = 10884422, upload-time = "2023-10-23T13:46:58.087Z" }, - { url = "https://files.pythonhosted.org/packages/4e/ba/ce9bd1cd4953336a0e213b29cb80bb11816f2a93de8c99f88ef0b446ad0c/scikit_learn-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:67f37d708f042a9b8d59551cf94d30431e01374e00dc2645fa186059c6c5d78b", size = 9207060, upload-time = "2023-10-23T13:47:00.948Z" }, - { url = "https://files.pythonhosted.org/packages/26/7e/2c3b82c8c29aa384c8bf859740419278627d2cdd0050db503c8840e72477/scikit_learn-1.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8db94cd8a2e038b37a80a04df8783e09caac77cbe052146432e67800e430c028", size = 9979322, upload-time = "2023-10-23T13:47:03.977Z" }, - { url = "https://files.pythonhosted.org/packages/cf/fc/6c52ffeb587259b6b893b7cac268f1eb1b5426bcce1aa20e53523bfe6944/scikit_learn-1.3.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:61a6efd384258789aa89415a410dcdb39a50e19d3d8410bd29be365bcdd512d5", size = 9270688, upload-time = "2023-10-23T13:47:07.316Z" }, - { url = "https://files.pythonhosted.org/packages/e5/a7/6f4ae76f72ae9de162b97acbf1f53acbe404c555f968d13da21e4112a002/scikit_learn-1.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb06f8dce3f5ddc5dee1715a9b9f19f20d295bed8e3cd4fa51e1d050347de525", size = 10280398, upload-time = "2023-10-23T13:47:10.796Z" }, - { url = "https://files.pythonhosted.org/packages/5d/b7/ee35904c07a0666784349529412fbb9814a56382b650d30fd9d6be5e5054/scikit_learn-1.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b2de18d86f630d68fe1f87af690d451388bb186480afc719e5f770590c2ef6c", size = 10796478, upload-time = "2023-10-23T13:47:14.077Z" }, - { url = "https://files.pythonhosted.org/packages/fe/6b/db949ed5ac367987b1f250f070f340b7715d22f0c9c965bdf07de6ca75a3/scikit_learn-1.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:0402638c9a7c219ee52c94cbebc8fcb5eb9fe9c773717965c1f4185588ad3107", size = 9133979, upload-time = "2023-10-23T13:47:17.389Z" }, + { url = "https://files.pythonhosted.org/packages/0d/53/570b55a6e10b8694ac1e3024d2df5cd443f1b4ff6d28430845da8b9019b3/scikit_learn-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e326c0eb5cf4d6ba40f93776a20e9a7a69524c4db0757e7ce24ba222471ee8a1", size = 10209999 }, + { url = "https://files.pythonhosted.org/packages/70/d0/50ace22129f79830e3cf682d0a2bd4843ef91573299d43112d52790163a8/scikit_learn-1.3.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:535805c2a01ccb40ca4ab7d081d771aea67e535153e35a1fd99418fcedd1648a", size = 9479353 }, + { url = "https://files.pythonhosted.org/packages/8f/46/fcc35ed7606c50d3072eae5a107a45cfa5b7f5fa8cc48610edd8cc8e8550/scikit_learn-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1215e5e58e9880b554b01187b8c9390bf4dc4692eedeaf542d3273f4785e342c", size = 10304705 }, + { url = "https://files.pythonhosted.org/packages/d0/0b/26ad95cf0b747be967b15fb71a06f5ac67aba0fd2f9cd174de6edefc4674/scikit_learn-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ee107923a623b9f517754ea2f69ea3b62fc898a3641766cb7deb2f2ce450161", size = 10827807 }, + { url = "https://files.pythonhosted.org/packages/69/8a/cf17d6443f5f537e099be81535a56ab68a473f9393fbffda38cd19899fc8/scikit_learn-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:35a22e8015048c628ad099da9df5ab3004cdbf81edc75b396fd0cff8699ac58c", size = 9255427 }, + { url = "https://files.pythonhosted.org/packages/08/5d/e5acecd6e99a6b656e42e7a7b18284e2f9c9f512e8ed6979e1e75d25f05f/scikit_learn-1.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6fb6bc98f234fda43163ddbe36df8bcde1d13ee176c6dc9b92bb7d3fc842eb66", size = 10116376 }, + { url = "https://files.pythonhosted.org/packages/40/c6/2e91eefb757822e70d351e02cc38d07c137212ae7c41ac12746415b4860a/scikit_learn-1.3.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:18424efee518a1cde7b0b53a422cde2f6625197de6af36da0b57ec502f126157", size = 9383415 }, + { url = "https://files.pythonhosted.org/packages/fa/fd/b3637639e73bb72b12803c5245f2a7299e09b2acd85a0f23937c53369a1c/scikit_learn-1.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3271552a5eb16f208a6f7f617b8cc6d1f137b52c8a1ef8edf547db0259b2c9fb", size = 10279163 }, + { url = "https://files.pythonhosted.org/packages/0c/2a/d3ff6091406bc2207e0adb832ebd15e40ac685811c7e2e3b432bfd969b71/scikit_learn-1.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4144a5004a676d5022b798d9e573b05139e77f271253a4703eed295bde0433", size = 10884422 }, + { url = "https://files.pythonhosted.org/packages/4e/ba/ce9bd1cd4953336a0e213b29cb80bb11816f2a93de8c99f88ef0b446ad0c/scikit_learn-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:67f37d708f042a9b8d59551cf94d30431e01374e00dc2645fa186059c6c5d78b", size = 9207060 }, + { url = "https://files.pythonhosted.org/packages/26/7e/2c3b82c8c29aa384c8bf859740419278627d2cdd0050db503c8840e72477/scikit_learn-1.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8db94cd8a2e038b37a80a04df8783e09caac77cbe052146432e67800e430c028", size = 9979322 }, + { url = "https://files.pythonhosted.org/packages/cf/fc/6c52ffeb587259b6b893b7cac268f1eb1b5426bcce1aa20e53523bfe6944/scikit_learn-1.3.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:61a6efd384258789aa89415a410dcdb39a50e19d3d8410bd29be365bcdd512d5", size = 9270688 }, + { url = "https://files.pythonhosted.org/packages/e5/a7/6f4ae76f72ae9de162b97acbf1f53acbe404c555f968d13da21e4112a002/scikit_learn-1.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb06f8dce3f5ddc5dee1715a9b9f19f20d295bed8e3cd4fa51e1d050347de525", size = 10280398 }, + { url = "https://files.pythonhosted.org/packages/5d/b7/ee35904c07a0666784349529412fbb9814a56382b650d30fd9d6be5e5054/scikit_learn-1.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b2de18d86f630d68fe1f87af690d451388bb186480afc719e5f770590c2ef6c", size = 10796478 }, + { url = "https://files.pythonhosted.org/packages/fe/6b/db949ed5ac367987b1f250f070f340b7715d22f0c9c965bdf07de6ca75a3/scikit_learn-1.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:0402638c9a7c219ee52c94cbebc8fcb5eb9fe9c773717965c1f4185588ad3107", size = 9133979 }, +] + +[[package]] +name = "scikit-learn" +version = "1.7.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and sys_platform == 'darwin'", + "python_full_version == '3.12.*' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version == '3.13.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version >= '3.14' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform != 'darwin' and sys_platform != 'linux')", + "(python_full_version == '3.13.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform != 'darwin' and sys_platform != 'linux')", + "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version == '3.11.*' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')", +] +dependencies = [ + { name = "joblib", marker = "python_full_version >= '3.11'" }, + { name = "numpy", marker = "python_full_version >= '3.11'" }, + { name = "scipy", version = "1.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "threadpoolctl", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/41/84/5f4af978fff619706b8961accac84780a6d298d82a8873446f72edb4ead0/scikit_learn-1.7.1.tar.gz", hash = "sha256:24b3f1e976a4665aa74ee0fcaac2b8fccc6ae77c8e07ab25da3ba6d3292b9802", size = 7190445 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/74/88/0dd5be14ef19f2d80a77780be35a33aa94e8a3b3223d80bee8892a7832b4/scikit_learn-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:406204dd4004f0517f0b23cf4b28c6245cbd51ab1b6b78153bc784def214946d", size = 9338868 }, + { url = "https://files.pythonhosted.org/packages/fd/52/3056b6adb1ac58a0bc335fc2ed2fcf599974d908855e8cb0ca55f797593c/scikit_learn-1.7.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:16af2e44164f05d04337fd1fc3ae7c4ea61fd9b0d527e22665346336920fe0e1", size = 8655943 }, + { url = "https://files.pythonhosted.org/packages/fb/a4/e488acdece6d413f370a9589a7193dac79cd486b2e418d3276d6ea0b9305/scikit_learn-1.7.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2f2e78e56a40c7587dea9a28dc4a49500fa2ead366869418c66f0fd75b80885c", size = 9652056 }, + { url = "https://files.pythonhosted.org/packages/18/41/bceacec1285b94eb9e4659b24db46c23346d7e22cf258d63419eb5dec6f7/scikit_learn-1.7.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b62b76ad408a821475b43b7bb90a9b1c9a4d8d125d505c2df0539f06d6e631b1", size = 9473691 }, + { url = "https://files.pythonhosted.org/packages/12/7b/e1ae4b7e1dd85c4ca2694ff9cc4a9690970fd6150d81b975e6c5c6f8ee7c/scikit_learn-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:9963b065677a4ce295e8ccdee80a1dd62b37249e667095039adcd5bce6e90deb", size = 8900873 }, + { url = "https://files.pythonhosted.org/packages/b4/bd/a23177930abd81b96daffa30ef9c54ddbf544d3226b8788ce4c3ef1067b4/scikit_learn-1.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:90c8494ea23e24c0fb371afc474618c1019dc152ce4a10e4607e62196113851b", size = 9334838 }, + { url = "https://files.pythonhosted.org/packages/8d/a1/d3a7628630a711e2ac0d1a482910da174b629f44e7dd8cfcd6924a4ef81a/scikit_learn-1.7.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:bb870c0daf3bf3be145ec51df8ac84720d9972170786601039f024bf6d61a518", size = 8651241 }, + { url = "https://files.pythonhosted.org/packages/26/92/85ec172418f39474c1cd0221d611345d4f433fc4ee2fc68e01f524ccc4e4/scikit_learn-1.7.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:40daccd1b5623f39e8943ab39735cadf0bdce80e67cdca2adcb5426e987320a8", size = 9718677 }, + { url = "https://files.pythonhosted.org/packages/df/ce/abdb1dcbb1d2b66168ec43b23ee0cee356b4cc4100ddee3943934ebf1480/scikit_learn-1.7.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:30d1f413cfc0aa5a99132a554f1d80517563c34a9d3e7c118fde2d273c6fe0f7", size = 9511189 }, + { url = "https://files.pythonhosted.org/packages/b2/3b/47b5eaee01ef2b5a80ba3f7f6ecf79587cb458690857d4777bfd77371c6f/scikit_learn-1.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:c711d652829a1805a95d7fe96654604a8f16eab5a9e9ad87b3e60173415cb650", size = 8914794 }, + { url = "https://files.pythonhosted.org/packages/cb/16/57f176585b35ed865f51b04117947fe20f130f78940c6477b6d66279c9c2/scikit_learn-1.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3cee419b49b5bbae8796ecd690f97aa412ef1674410c23fc3257c6b8b85b8087", size = 9260431 }, + { url = "https://files.pythonhosted.org/packages/67/4e/899317092f5efcab0e9bc929e3391341cec8fb0e816c4789686770024580/scikit_learn-1.7.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2fd8b8d35817b0d9ebf0b576f7d5ffbbabdb55536b0655a8aaae629d7ffd2e1f", size = 8637191 }, + { url = "https://files.pythonhosted.org/packages/f3/1b/998312db6d361ded1dd56b457ada371a8d8d77ca2195a7d18fd8a1736f21/scikit_learn-1.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:588410fa19a96a69763202f1d6b7b91d5d7a5d73be36e189bc6396bfb355bd87", size = 9486346 }, + { url = "https://files.pythonhosted.org/packages/ad/09/a2aa0b4e644e5c4ede7006748f24e72863ba2ae71897fecfd832afea01b4/scikit_learn-1.7.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e3142f0abe1ad1d1c31a2ae987621e41f6b578144a911ff4ac94781a583adad7", size = 9290988 }, + { url = "https://files.pythonhosted.org/packages/15/fa/c61a787e35f05f17fc10523f567677ec4eeee5f95aa4798dbbbcd9625617/scikit_learn-1.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:3ddd9092c1bd469acab337d87930067c87eac6bd544f8d5027430983f1e1ae88", size = 8735568 }, + { url = "https://files.pythonhosted.org/packages/52/f8/e0533303f318a0f37b88300d21f79b6ac067188d4824f1047a37214ab718/scikit_learn-1.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b7839687fa46d02e01035ad775982f2470be2668e13ddd151f0f55a5bf123bae", size = 9213143 }, + { url = "https://files.pythonhosted.org/packages/71/f3/f1df377d1bdfc3e3e2adc9c119c238b182293e6740df4cbeac6de2cc3e23/scikit_learn-1.7.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:a10f276639195a96c86aa572ee0698ad64ee939a7b042060b98bd1930c261d10", size = 8591977 }, + { url = "https://files.pythonhosted.org/packages/99/72/c86a4cd867816350fe8dee13f30222340b9cd6b96173955819a5561810c5/scikit_learn-1.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:13679981fdaebc10cc4c13c43344416a86fcbc61449cb3e6517e1df9d12c8309", size = 9436142 }, + { url = "https://files.pythonhosted.org/packages/e8/66/277967b29bd297538dc7a6ecfb1a7dce751beabd0d7f7a2233be7a4f7832/scikit_learn-1.7.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4f1262883c6a63f067a980a8cdd2d2e7f2513dddcef6a9eaada6416a7a7cbe43", size = 9282996 }, + { url = "https://files.pythonhosted.org/packages/e2/47/9291cfa1db1dae9880420d1e07dbc7e8dd4a7cdbc42eaba22512e6bde958/scikit_learn-1.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:ca6d31fb10e04d50bfd2b50d66744729dbb512d4efd0223b864e2fdbfc4cee11", size = 8707418 }, + { url = "https://files.pythonhosted.org/packages/61/95/45726819beccdaa34d3362ea9b2ff9f2b5d3b8bf721bd632675870308ceb/scikit_learn-1.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:781674d096303cfe3d351ae6963ff7c958db61cde3421cd490e3a5a58f2a94ae", size = 9561466 }, + { url = "https://files.pythonhosted.org/packages/ee/1c/6f4b3344805de783d20a51eb24d4c9ad4b11a7f75c1801e6ec6d777361fd/scikit_learn-1.7.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:10679f7f125fe7ecd5fad37dd1aa2daae7e3ad8df7f3eefa08901b8254b3e12c", size = 9040467 }, + { url = "https://files.pythonhosted.org/packages/6f/80/abe18fe471af9f1d181904203d62697998b27d9b62124cd281d740ded2f9/scikit_learn-1.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1f812729e38c8cb37f760dce71a9b83ccfb04f59b3dca7c6079dcdc60544fa9e", size = 9532052 }, + { url = "https://files.pythonhosted.org/packages/14/82/b21aa1e0c4cee7e74864d3a5a721ab8fcae5ca55033cb6263dca297ed35b/scikit_learn-1.7.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:88e1a20131cf741b84b89567e1717f27a2ced228e0f29103426102bc2e3b8ef7", size = 9361575 }, + { url = "https://files.pythonhosted.org/packages/f2/20/f4777fcd5627dc6695fa6b92179d0edb7a3ac1b91bcd9a1c7f64fa7ade23/scikit_learn-1.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b1bd1d919210b6a10b7554b717c9000b5485aa95a1d0f177ae0d7ee8ec750da5", size = 9277310 }, ] [[package]] name = "scipy" version = "1.11.4" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, +resolution-markers = [ + "python_full_version < '3.11' and sys_platform == 'darwin'", + "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')", ] -sdist = { url = "https://files.pythonhosted.org/packages/6e/1f/91144ba78dccea567a6466262922786ffc97be1e9b06ed9574ef0edc11e1/scipy-1.11.4.tar.gz", hash = "sha256:90a2b78e7f5733b9de748f589f09225013685f9b218275257f8a8168ededaeaa", size = 56336202, upload-time = "2023-11-18T21:06:08.277Z" } +dependencies = [ + { name = "numpy", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6e/1f/91144ba78dccea567a6466262922786ffc97be1e9b06ed9574ef0edc11e1/scipy-1.11.4.tar.gz", hash = "sha256:90a2b78e7f5733b9de748f589f09225013685f9b218275257f8a8168ededaeaa", size = 56336202 } wheels = [ - { url = "https://files.pythonhosted.org/packages/34/c6/a32add319475d21f89733c034b99c81b3a7c6c7c19f96f80c7ca3ff1bbd4/scipy-1.11.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc9a714581f561af0848e6b69947fda0614915f072dfd14142ed1bfe1b806710", size = 37293259, upload-time = "2023-11-18T21:01:18.805Z" }, - { url = "https://files.pythonhosted.org/packages/de/0d/4fa68303568c70fd56fbf40668b6c6807cfee4cad975f07d80bdd26d013e/scipy-1.11.4-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:cf00bd2b1b0211888d4dc75656c0412213a8b25e80d73898083f402b50f47e41", size = 29760656, upload-time = "2023-11-18T21:01:41.815Z" }, - { url = "https://files.pythonhosted.org/packages/13/e5/8012be7857db6cbbbdbeea8a154dbacdfae845e95e1e19c028e82236d4a0/scipy-1.11.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9999c008ccf00e8fbcce1236f85ade5c569d13144f77a1946bef8863e8f6eb4", size = 32922489, upload-time = "2023-11-18T21:01:50.637Z" }, - { url = "https://files.pythonhosted.org/packages/e0/9e/80e2205d138960a49caea391f3710600895dd8292b6868dc9aff7aa593f9/scipy-1.11.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:933baf588daa8dc9a92c20a0be32f56d43faf3d1a60ab11b3f08c356430f6e56", size = 36442040, upload-time = "2023-11-18T21:02:00.119Z" }, - { url = "https://files.pythonhosted.org/packages/69/60/30a9c3fbe5066a3a93eefe3e2d44553df13587e6f792e1bff20dfed3d17e/scipy-1.11.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8fce70f39076a5aa62e92e69a7f62349f9574d8405c0a5de6ed3ef72de07f446", size = 36643257, upload-time = "2023-11-18T21:02:06.798Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ec/b46756f80e3f4c5f0989f6e4492c2851f156d9c239d554754a3c8cffd4e2/scipy-1.11.4-cp310-cp310-win_amd64.whl", hash = "sha256:6550466fbeec7453d7465e74d4f4b19f905642c89a7525571ee91dd7adabb5a3", size = 44149285, upload-time = "2023-11-18T21:02:15.592Z" }, - { url = "https://files.pythonhosted.org/packages/b8/f2/1aefbd5e54ebd8c6163ccf7f73e5d17bc8cb38738d312befc524fce84bb4/scipy-1.11.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f313b39a7e94f296025e3cffc2c567618174c0b1dde173960cf23808f9fae4be", size = 37159197, upload-time = "2023-11-18T21:02:21.959Z" }, - { url = "https://files.pythonhosted.org/packages/4b/48/20e77ddb1f473d4717a7d4d3fc8d15557f406f7708496054c59f635b7734/scipy-1.11.4-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:1b7c3dca977f30a739e0409fb001056484661cb2541a01aba0bb0029f7b68db8", size = 29675057, upload-time = "2023-11-18T21:02:28.169Z" }, - { url = "https://files.pythonhosted.org/packages/75/2e/a781862190d0e7e76afa74752ef363488a9a9d6ea86e46d5e5506cee8df6/scipy-1.11.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00150c5eae7b610c32589dda259eacc7c4f1665aedf25d921907f4d08a951b1c", size = 32882747, upload-time = "2023-11-18T21:02:33.683Z" }, - { url = "https://files.pythonhosted.org/packages/6b/d4/d62ce38ba00dc67d7ec4ec5cc19d36958d8ed70e63778715ad626bcbc796/scipy-1.11.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:530f9ad26440e85766509dbf78edcfe13ffd0ab7fec2560ee5c36ff74d6269ff", size = 36402732, upload-time = "2023-11-18T21:02:39.762Z" }, - { url = "https://files.pythonhosted.org/packages/88/86/827b56aea1ed04adbb044a675672a73c84d81076a350092bbfcfc1ae723b/scipy-1.11.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5e347b14fe01003d3b78e196e84bd3f48ffe4c8a7b8a1afbcb8f5505cb710993", size = 36622138, upload-time = "2023-11-18T21:02:45.968Z" }, - { url = "https://files.pythonhosted.org/packages/43/d0/f3cd75b62e1b90f48dbf091261b2fc7ceec14a700e308c50f6a69c83d337/scipy-1.11.4-cp311-cp311-win_amd64.whl", hash = "sha256:acf8ed278cc03f5aff035e69cb511741e0418681d25fbbb86ca65429c4f4d9cd", size = 44095631, upload-time = "2023-11-18T21:02:52.859Z" }, - { url = "https://files.pythonhosted.org/packages/df/64/8a690570485b636da614acff35fd725fcbc487f8b1fa9bdb12871b77412f/scipy-1.11.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:028eccd22e654b3ea01ee63705681ee79933652b2d8f873e7949898dda6d11b6", size = 37053653, upload-time = "2023-11-18T21:03:00.107Z" }, - { url = "https://files.pythonhosted.org/packages/5e/43/abf331745a7e5f4af51f13d40e2a72f516048db41ecbcf3ac6f86ada54a3/scipy-1.11.4-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2c6ff6ef9cc27f9b3db93a6f8b38f97387e6e0591600369a297a50a8e96e835d", size = 29641601, upload-time = "2023-11-18T21:03:06.708Z" }, - { url = "https://files.pythonhosted.org/packages/47/9b/62d0ec086dd2871009da8769c504bec6e39b80f4c182c6ead0fcebd8b323/scipy-1.11.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b030c6674b9230d37c5c60ab456e2cf12f6784596d15ce8da9365e70896effc4", size = 32272137, upload-time = "2023-11-18T21:03:14.877Z" }, - { url = "https://files.pythonhosted.org/packages/08/77/f90f7306d755ac68bd159c50bb86fffe38400e533e8c609dd8484bd0f172/scipy-1.11.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad669df80528aeca5f557712102538f4f37e503f0c5b9541655016dd0932ca79", size = 35777534, upload-time = "2023-11-18T21:03:21.451Z" }, - { url = "https://files.pythonhosted.org/packages/00/de/b9f6938090c37b5092969ba1c67118e9114e8e6ef9d197251671444e839c/scipy-1.11.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ce7fff2e23ab2cc81ff452a9444c215c28e6305f396b2ba88343a567feec9660", size = 35963721, upload-time = "2023-11-18T21:03:27.85Z" }, - { url = "https://files.pythonhosted.org/packages/c6/a1/357e4cd43af2748e1e0407ae0e9a5ea8aaaa6b702833c81be11670dcbad8/scipy-1.11.4-cp312-cp312-win_amd64.whl", hash = "sha256:36750b7733d960d7994888f0d148d31ea3017ac15eef664194b4ef68d36a4a97", size = 43730653, upload-time = "2023-11-18T21:03:34.758Z" }, + { url = "https://files.pythonhosted.org/packages/34/c6/a32add319475d21f89733c034b99c81b3a7c6c7c19f96f80c7ca3ff1bbd4/scipy-1.11.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc9a714581f561af0848e6b69947fda0614915f072dfd14142ed1bfe1b806710", size = 37293259 }, + { url = "https://files.pythonhosted.org/packages/de/0d/4fa68303568c70fd56fbf40668b6c6807cfee4cad975f07d80bdd26d013e/scipy-1.11.4-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:cf00bd2b1b0211888d4dc75656c0412213a8b25e80d73898083f402b50f47e41", size = 29760656 }, + { url = "https://files.pythonhosted.org/packages/13/e5/8012be7857db6cbbbdbeea8a154dbacdfae845e95e1e19c028e82236d4a0/scipy-1.11.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9999c008ccf00e8fbcce1236f85ade5c569d13144f77a1946bef8863e8f6eb4", size = 32922489 }, + { url = "https://files.pythonhosted.org/packages/e0/9e/80e2205d138960a49caea391f3710600895dd8292b6868dc9aff7aa593f9/scipy-1.11.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:933baf588daa8dc9a92c20a0be32f56d43faf3d1a60ab11b3f08c356430f6e56", size = 36442040 }, + { url = "https://files.pythonhosted.org/packages/69/60/30a9c3fbe5066a3a93eefe3e2d44553df13587e6f792e1bff20dfed3d17e/scipy-1.11.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8fce70f39076a5aa62e92e69a7f62349f9574d8405c0a5de6ed3ef72de07f446", size = 36643257 }, + { url = "https://files.pythonhosted.org/packages/f8/ec/b46756f80e3f4c5f0989f6e4492c2851f156d9c239d554754a3c8cffd4e2/scipy-1.11.4-cp310-cp310-win_amd64.whl", hash = "sha256:6550466fbeec7453d7465e74d4f4b19f905642c89a7525571ee91dd7adabb5a3", size = 44149285 }, + { url = "https://files.pythonhosted.org/packages/b8/f2/1aefbd5e54ebd8c6163ccf7f73e5d17bc8cb38738d312befc524fce84bb4/scipy-1.11.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f313b39a7e94f296025e3cffc2c567618174c0b1dde173960cf23808f9fae4be", size = 37159197 }, + { url = "https://files.pythonhosted.org/packages/4b/48/20e77ddb1f473d4717a7d4d3fc8d15557f406f7708496054c59f635b7734/scipy-1.11.4-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:1b7c3dca977f30a739e0409fb001056484661cb2541a01aba0bb0029f7b68db8", size = 29675057 }, + { url = "https://files.pythonhosted.org/packages/75/2e/a781862190d0e7e76afa74752ef363488a9a9d6ea86e46d5e5506cee8df6/scipy-1.11.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00150c5eae7b610c32589dda259eacc7c4f1665aedf25d921907f4d08a951b1c", size = 32882747 }, + { url = "https://files.pythonhosted.org/packages/6b/d4/d62ce38ba00dc67d7ec4ec5cc19d36958d8ed70e63778715ad626bcbc796/scipy-1.11.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:530f9ad26440e85766509dbf78edcfe13ffd0ab7fec2560ee5c36ff74d6269ff", size = 36402732 }, + { url = "https://files.pythonhosted.org/packages/88/86/827b56aea1ed04adbb044a675672a73c84d81076a350092bbfcfc1ae723b/scipy-1.11.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5e347b14fe01003d3b78e196e84bd3f48ffe4c8a7b8a1afbcb8f5505cb710993", size = 36622138 }, + { url = "https://files.pythonhosted.org/packages/43/d0/f3cd75b62e1b90f48dbf091261b2fc7ceec14a700e308c50f6a69c83d337/scipy-1.11.4-cp311-cp311-win_amd64.whl", hash = "sha256:acf8ed278cc03f5aff035e69cb511741e0418681d25fbbb86ca65429c4f4d9cd", size = 44095631 }, + { url = "https://files.pythonhosted.org/packages/df/64/8a690570485b636da614acff35fd725fcbc487f8b1fa9bdb12871b77412f/scipy-1.11.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:028eccd22e654b3ea01ee63705681ee79933652b2d8f873e7949898dda6d11b6", size = 37053653 }, + { url = "https://files.pythonhosted.org/packages/5e/43/abf331745a7e5f4af51f13d40e2a72f516048db41ecbcf3ac6f86ada54a3/scipy-1.11.4-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2c6ff6ef9cc27f9b3db93a6f8b38f97387e6e0591600369a297a50a8e96e835d", size = 29641601 }, + { url = "https://files.pythonhosted.org/packages/47/9b/62d0ec086dd2871009da8769c504bec6e39b80f4c182c6ead0fcebd8b323/scipy-1.11.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b030c6674b9230d37c5c60ab456e2cf12f6784596d15ce8da9365e70896effc4", size = 32272137 }, + { url = "https://files.pythonhosted.org/packages/08/77/f90f7306d755ac68bd159c50bb86fffe38400e533e8c609dd8484bd0f172/scipy-1.11.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad669df80528aeca5f557712102538f4f37e503f0c5b9541655016dd0932ca79", size = 35777534 }, + { url = "https://files.pythonhosted.org/packages/00/de/b9f6938090c37b5092969ba1c67118e9114e8e6ef9d197251671444e839c/scipy-1.11.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ce7fff2e23ab2cc81ff452a9444c215c28e6305f396b2ba88343a567feec9660", size = 35963721 }, + { url = "https://files.pythonhosted.org/packages/c6/a1/357e4cd43af2748e1e0407ae0e9a5ea8aaaa6b702833c81be11670dcbad8/scipy-1.11.4-cp312-cp312-win_amd64.whl", hash = "sha256:36750b7733d960d7994888f0d148d31ea3017ac15eef664194b4ef68d36a4a97", size = 43730653 }, +] + +[[package]] +name = "scipy" +version = "1.16.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and sys_platform == 'darwin'", + "python_full_version == '3.12.*' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version == '3.13.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version >= '3.14' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform != 'darwin' and sys_platform != 'linux')", + "(python_full_version == '3.13.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform != 'darwin' and sys_platform != 'linux')", + "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version == '3.11.*' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')", +] +dependencies = [ + { name = "numpy", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/4a/b927028464795439faec8eaf0b03b011005c487bb2d07409f28bf30879c4/scipy-1.16.1.tar.gz", hash = "sha256:44c76f9e8b6e8e488a586190ab38016e4ed2f8a038af7cd3defa903c0a2238b3", size = 30580861 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/91/812adc6f74409b461e3a5fa97f4f74c769016919203138a3bf6fc24ba4c5/scipy-1.16.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:c033fa32bab91dc98ca59d0cf23bb876454e2bb02cbe592d5023138778f70030", size = 36552519 }, + { url = "https://files.pythonhosted.org/packages/47/18/8e355edcf3b71418d9e9f9acd2708cc3a6c27e8f98fde0ac34b8a0b45407/scipy-1.16.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:6e5c2f74e5df33479b5cd4e97a9104c511518fbd979aa9b8f6aec18b2e9ecae7", size = 28638010 }, + { url = "https://files.pythonhosted.org/packages/d9/eb/e931853058607bdfbc11b86df19ae7a08686121c203483f62f1ecae5989c/scipy-1.16.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0a55ffe0ba0f59666e90951971a884d1ff6f4ec3275a48f472cfb64175570f77", size = 20909790 }, + { url = "https://files.pythonhosted.org/packages/45/0c/be83a271d6e96750cd0be2e000f35ff18880a46f05ce8b5d3465dc0f7a2a/scipy-1.16.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:f8a5d6cd147acecc2603fbd382fed6c46f474cccfcf69ea32582e033fb54dcfe", size = 23513352 }, + { url = "https://files.pythonhosted.org/packages/7c/bf/fe6eb47e74f762f933cca962db7f2c7183acfdc4483bd1c3813cfe83e538/scipy-1.16.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb18899127278058bcc09e7b9966d41a5a43740b5bb8dcba401bd983f82e885b", size = 33534643 }, + { url = "https://files.pythonhosted.org/packages/bb/ba/63f402e74875486b87ec6506a4f93f6d8a0d94d10467280f3d9d7837ce3a/scipy-1.16.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adccd93a2fa937a27aae826d33e3bfa5edf9aa672376a4852d23a7cd67a2e5b7", size = 35376776 }, + { url = "https://files.pythonhosted.org/packages/c3/b4/04eb9d39ec26a1b939689102da23d505ea16cdae3dbb18ffc53d1f831044/scipy-1.16.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:18aca1646a29ee9a0625a1be5637fa798d4d81fdf426481f06d69af828f16958", size = 35698906 }, + { url = "https://files.pythonhosted.org/packages/04/d6/bb5468da53321baeb001f6e4e0d9049eadd175a4a497709939128556e3ec/scipy-1.16.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d85495cef541729a70cdddbbf3e6b903421bc1af3e8e3a9a72a06751f33b7c39", size = 38129275 }, + { url = "https://files.pythonhosted.org/packages/c4/94/994369978509f227cba7dfb9e623254d0d5559506fe994aef4bea3ed469c/scipy-1.16.1-cp311-cp311-win_amd64.whl", hash = "sha256:226652fca853008119c03a8ce71ffe1b3f6d2844cc1686e8f9806edafae68596", size = 38644572 }, + { url = "https://files.pythonhosted.org/packages/f8/d9/ec4864f5896232133f51382b54a08de91a9d1af7a76dfa372894026dfee2/scipy-1.16.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:81b433bbeaf35728dad619afc002db9b189e45eebe2cd676effe1fb93fef2b9c", size = 36575194 }, + { url = "https://files.pythonhosted.org/packages/5c/6d/40e81ecfb688e9d25d34a847dca361982a6addf8e31f0957b1a54fbfa994/scipy-1.16.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:886cc81fdb4c6903a3bb0464047c25a6d1016fef77bb97949817d0c0d79f9e04", size = 28594590 }, + { url = "https://files.pythonhosted.org/packages/0e/37/9f65178edfcc629377ce9a64fc09baebea18c80a9e57ae09a52edf84880b/scipy-1.16.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:15240c3aac087a522b4eaedb09f0ad061753c5eebf1ea430859e5bf8640d5919", size = 20866458 }, + { url = "https://files.pythonhosted.org/packages/2c/7b/749a66766871ea4cb1d1ea10f27004db63023074c22abed51f22f09770e0/scipy-1.16.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:65f81a25805f3659b48126b5053d9e823d3215e4a63730b5e1671852a1705921", size = 23539318 }, + { url = "https://files.pythonhosted.org/packages/c4/db/8d4afec60eb833a666434d4541a3151eedbf2494ea6d4d468cbe877f00cd/scipy-1.16.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6c62eea7f607f122069b9bad3f99489ddca1a5173bef8a0c75555d7488b6f725", size = 33292899 }, + { url = "https://files.pythonhosted.org/packages/51/1e/79023ca3bbb13a015d7d2757ecca3b81293c663694c35d6541b4dca53e98/scipy-1.16.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f965bbf3235b01c776115ab18f092a95aa74c271a52577bcb0563e85738fd618", size = 35162637 }, + { url = "https://files.pythonhosted.org/packages/b6/49/0648665f9c29fdaca4c679182eb972935b3b4f5ace41d323c32352f29816/scipy-1.16.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f006e323874ffd0b0b816d8c6a8e7f9a73d55ab3b8c3f72b752b226d0e3ac83d", size = 35490507 }, + { url = "https://files.pythonhosted.org/packages/62/8f/66cbb9d6bbb18d8c658f774904f42a92078707a7c71e5347e8bf2f52bb89/scipy-1.16.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8fd15fc5085ab4cca74cb91fe0a4263b1f32e4420761ddae531ad60934c2119", size = 37923998 }, + { url = "https://files.pythonhosted.org/packages/14/c3/61f273ae550fbf1667675701112e380881905e28448c080b23b5a181df7c/scipy-1.16.1-cp312-cp312-win_amd64.whl", hash = "sha256:f7b8013c6c066609577d910d1a2a077021727af07b6fab0ee22c2f901f22352a", size = 38508060 }, + { url = "https://files.pythonhosted.org/packages/93/0b/b5c99382b839854a71ca9482c684e3472badc62620287cbbdab499b75ce6/scipy-1.16.1-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:5451606823a5e73dfa621a89948096c6528e2896e40b39248295d3a0138d594f", size = 36533717 }, + { url = "https://files.pythonhosted.org/packages/eb/e5/69ab2771062c91e23e07c12e7d5033a6b9b80b0903ee709c3c36b3eb520c/scipy-1.16.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:89728678c5ca5abd610aee148c199ac1afb16e19844401ca97d43dc548a354eb", size = 28570009 }, + { url = "https://files.pythonhosted.org/packages/f4/69/bd75dbfdd3cf524f4d753484d723594aed62cfaac510123e91a6686d520b/scipy-1.16.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e756d688cb03fd07de0fffad475649b03cb89bee696c98ce508b17c11a03f95c", size = 20841942 }, + { url = "https://files.pythonhosted.org/packages/ea/74/add181c87663f178ba7d6144b370243a87af8476664d5435e57d599e6874/scipy-1.16.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5aa2687b9935da3ed89c5dbed5234576589dd28d0bf7cd237501ccfbdf1ad608", size = 23498507 }, + { url = "https://files.pythonhosted.org/packages/1d/74/ece2e582a0d9550cee33e2e416cc96737dce423a994d12bbe59716f47ff1/scipy-1.16.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0851f6a1e537fe9399f35986897e395a1aa61c574b178c0d456be5b1a0f5ca1f", size = 33286040 }, + { url = "https://files.pythonhosted.org/packages/e4/82/08e4076df538fb56caa1d489588d880ec7c52d8273a606bb54d660528f7c/scipy-1.16.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fedc2cbd1baed37474b1924c331b97bdff611d762c196fac1a9b71e67b813b1b", size = 35176096 }, + { url = "https://files.pythonhosted.org/packages/fa/79/cd710aab8c921375711a8321c6be696e705a120e3011a643efbbcdeeabcc/scipy-1.16.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2ef500e72f9623a6735769e4b93e9dcb158d40752cdbb077f305487e3e2d1f45", size = 35490328 }, + { url = "https://files.pythonhosted.org/packages/71/73/e9cc3d35ee4526d784520d4494a3e1ca969b071fb5ae5910c036a375ceec/scipy-1.16.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:978d8311674b05a8f7ff2ea6c6bce5d8b45a0cb09d4c5793e0318f448613ea65", size = 37939921 }, + { url = "https://files.pythonhosted.org/packages/21/12/c0efd2941f01940119b5305c375ae5c0fcb7ec193f806bd8f158b73a1782/scipy-1.16.1-cp313-cp313-win_amd64.whl", hash = "sha256:81929ed0fa7a5713fcdd8b2e6f73697d3b4c4816d090dd34ff937c20fa90e8ab", size = 38479462 }, + { url = "https://files.pythonhosted.org/packages/7a/19/c3d08b675260046a991040e1ea5d65f91f40c7df1045fffff412dcfc6765/scipy-1.16.1-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:bcc12db731858abda693cecdb3bdc9e6d4bd200213f49d224fe22df82687bdd6", size = 36938832 }, + { url = "https://files.pythonhosted.org/packages/81/f2/ce53db652c033a414a5b34598dba6b95f3d38153a2417c5a3883da429029/scipy-1.16.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:744d977daa4becb9fc59135e75c069f8d301a87d64f88f1e602a9ecf51e77b27", size = 29093084 }, + { url = "https://files.pythonhosted.org/packages/a9/ae/7a10ff04a7dc15f9057d05b33737ade244e4bd195caa3f7cc04d77b9e214/scipy-1.16.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:dc54f76ac18073bcecffb98d93f03ed6b81a92ef91b5d3b135dcc81d55a724c7", size = 21365098 }, + { url = "https://files.pythonhosted.org/packages/36/ac/029ff710959932ad3c2a98721b20b405f05f752f07344622fd61a47c5197/scipy-1.16.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:367d567ee9fc1e9e2047d31f39d9d6a7a04e0710c86e701e053f237d14a9b4f6", size = 23896858 }, + { url = "https://files.pythonhosted.org/packages/71/13/d1ef77b6bd7898720e1f0b6b3743cb945f6c3cafa7718eaac8841035ab60/scipy-1.16.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4cf5785e44e19dcd32a0e4807555e1e9a9b8d475c6afff3d21c3c543a6aa84f4", size = 33438311 }, + { url = "https://files.pythonhosted.org/packages/2d/e0/e64a6821ffbb00b4c5b05169f1c1fddb4800e9307efe3db3788995a82a2c/scipy-1.16.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3d0b80fb26d3e13a794c71d4b837e2a589d839fd574a6bbb4ee1288c213ad4a3", size = 35279542 }, + { url = "https://files.pythonhosted.org/packages/57/59/0dc3c8b43e118f1e4ee2b798dcc96ac21bb20014e5f1f7a8e85cc0653bdb/scipy-1.16.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8503517c44c18d1030d666cb70aaac1cc8913608816e06742498833b128488b7", size = 35667665 }, + { url = "https://files.pythonhosted.org/packages/45/5f/844ee26e34e2f3f9f8febb9343748e72daeaec64fe0c70e9bf1ff84ec955/scipy-1.16.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:30cc4bb81c41831ecfd6dc450baf48ffd80ef5aed0f5cf3ea775740e80f16ecc", size = 38045210 }, + { url = "https://files.pythonhosted.org/packages/8d/d7/210f2b45290f444f1de64bc7353aa598ece9f0e90c384b4a156f9b1a5063/scipy-1.16.1-cp313-cp313t-win_amd64.whl", hash = "sha256:c24fa02f7ed23ae514460a22c57eca8f530dbfa50b1cfdbf4f37c05b5309cc39", size = 38593661 }, + { url = "https://files.pythonhosted.org/packages/81/ea/84d481a5237ed223bd3d32d6e82d7a6a96e34756492666c260cef16011d1/scipy-1.16.1-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:796a5a9ad36fa3a782375db8f4241ab02a091308eb079746bc0f874c9b998318", size = 36525921 }, + { url = "https://files.pythonhosted.org/packages/4e/9f/d9edbdeff9f3a664807ae3aea383e10afaa247e8e6255e6d2aa4515e8863/scipy-1.16.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:3ea0733a2ff73fd6fdc5fecca54ee9b459f4d74f00b99aced7d9a3adb43fb1cc", size = 28564152 }, + { url = "https://files.pythonhosted.org/packages/3b/95/8125bcb1fe04bc267d103e76516243e8d5e11229e6b306bda1024a5423d1/scipy-1.16.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:85764fb15a2ad994e708258bb4ed8290d1305c62a4e1ef07c414356a24fcfbf8", size = 20836028 }, + { url = "https://files.pythonhosted.org/packages/77/9c/bf92e215701fc70bbcd3d14d86337cf56a9b912a804b9c776a269524a9e9/scipy-1.16.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:ca66d980469cb623b1759bdd6e9fd97d4e33a9fad5b33771ced24d0cb24df67e", size = 23489666 }, + { url = "https://files.pythonhosted.org/packages/5e/00/5e941d397d9adac41b02839011594620d54d99488d1be5be755c00cde9ee/scipy-1.16.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e7cc1ffcc230f568549fc56670bcf3df1884c30bd652c5da8138199c8c76dae0", size = 33358318 }, + { url = "https://files.pythonhosted.org/packages/0e/87/8db3aa10dde6e3e8e7eb0133f24baa011377d543f5b19c71469cf2648026/scipy-1.16.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3ddfb1e8d0b540cb4ee9c53fc3dea3186f97711248fb94b4142a1b27178d8b4b", size = 35185724 }, + { url = "https://files.pythonhosted.org/packages/89/b4/6ab9ae443216807622bcff02690262d8184078ea467efee2f8c93288a3b1/scipy-1.16.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4dc0e7be79e95d8ba3435d193e0d8ce372f47f774cffd882f88ea4e1e1ddc731", size = 35554335 }, + { url = "https://files.pythonhosted.org/packages/9c/9a/d0e9dc03c5269a1afb60661118296a32ed5d2c24298af61b676c11e05e56/scipy-1.16.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f23634f9e5adb51b2a77766dac217063e764337fbc816aa8ad9aaebcd4397fd3", size = 37960310 }, + { url = "https://files.pythonhosted.org/packages/5e/00/c8f3130a50521a7977874817ca89e0599b1b4ee8e938bad8ae798a0e1f0d/scipy-1.16.1-cp314-cp314-win_amd64.whl", hash = "sha256:57d75524cb1c5a374958a2eae3d84e1929bb971204cc9d52213fb8589183fc19", size = 39319239 }, + { url = "https://files.pythonhosted.org/packages/f2/f2/1ca3eda54c3a7e4c92f6acef7db7b3a057deb135540d23aa6343ef8ad333/scipy-1.16.1-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:d8da7c3dd67bcd93f15618938f43ed0995982eb38973023d46d4646c4283ad65", size = 36939460 }, + { url = "https://files.pythonhosted.org/packages/80/30/98c2840b293a132400c0940bb9e140171dcb8189588619048f42b2ce7b4f/scipy-1.16.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:cc1d2f2fd48ba1e0620554fe5bc44d3e8f5d4185c8c109c7fbdf5af2792cfad2", size = 29093322 }, + { url = "https://files.pythonhosted.org/packages/c1/e6/1e6e006e850622cf2a039b62d1a6ddc4497d4851e58b68008526f04a9a00/scipy-1.16.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:21a611ced9275cb861bacadbada0b8c0623bc00b05b09eb97f23b370fc2ae56d", size = 21365329 }, + { url = "https://files.pythonhosted.org/packages/8e/02/72a5aa5b820589dda9a25e329ca752842bfbbaf635e36bc7065a9b42216e/scipy-1.16.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:8dfbb25dffc4c3dd9371d8ab456ca81beeaf6f9e1c2119f179392f0dc1ab7695", size = 23897544 }, + { url = "https://files.pythonhosted.org/packages/2b/dc/7122d806a6f9eb8a33532982234bed91f90272e990f414f2830cfe656e0b/scipy-1.16.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f0ebb7204f063fad87fc0a0e4ff4a2ff40b2a226e4ba1b7e34bf4b79bf97cd86", size = 33442112 }, + { url = "https://files.pythonhosted.org/packages/24/39/e383af23564daa1021a5b3afbe0d8d6a68ec639b943661841f44ac92de85/scipy-1.16.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f1b9e5962656f2734c2b285a8745358ecb4e4efbadd00208c80a389227ec61ff", size = 35286594 }, + { url = "https://files.pythonhosted.org/packages/95/47/1a0b0aff40c3056d955f38b0df5d178350c3d74734ec54f9c68d23910be5/scipy-1.16.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e1a106f8c023d57a2a903e771228bf5c5b27b5d692088f457acacd3b54511e4", size = 35665080 }, + { url = "https://files.pythonhosted.org/packages/64/df/ce88803e9ed6e27fe9b9abefa157cf2c80e4fa527cf17ee14be41f790ad4/scipy-1.16.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:709559a1db68a9abc3b2c8672c4badf1614f3b440b3ab326d86a5c0491eafae3", size = 38050306 }, + { url = "https://files.pythonhosted.org/packages/6e/6c/a76329897a7cae4937d403e623aa6aaea616a0bb5b36588f0b9d1c9a3739/scipy-1.16.1-cp314-cp314t-win_amd64.whl", hash = "sha256:c0c804d60492a0aad7f5b2bb1862f4548b990049e27e828391ff2bf6f7199998", size = 39427705 }, ] [[package]] name = "setuptools" version = "70.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/d8/10a70e86f6c28ae59f101a9de6d77bf70f147180fbf40c3af0f64080adc3/setuptools-70.3.0.tar.gz", hash = "sha256:f171bab1dfbc86b132997f26a119f6056a57950d058587841a0082e8830f9dc5", size = 2333112, upload-time = "2024-07-09T16:08:06.251Z" } +sdist = { url = "https://files.pythonhosted.org/packages/65/d8/10a70e86f6c28ae59f101a9de6d77bf70f147180fbf40c3af0f64080adc3/setuptools-70.3.0.tar.gz", hash = "sha256:f171bab1dfbc86b132997f26a119f6056a57950d058587841a0082e8830f9dc5", size = 2333112 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/15/88e46eb9387e905704b69849618e699dc2f54407d8953cc4ec4b8b46528d/setuptools-70.3.0-py3-none-any.whl", hash = "sha256:fe384da74336c398e0d956d1cae0669bc02eed936cdb1d49b57de1990dc11ffc", size = 931070, upload-time = "2024-07-09T16:07:58.829Z" }, + { url = "https://files.pythonhosted.org/packages/ef/15/88e46eb9387e905704b69849618e699dc2f54407d8953cc4ec4b8b46528d/setuptools-70.3.0-py3-none-any.whl", hash = "sha256:fe384da74336c398e0d956d1cae0669bc02eed936cdb1d49b57de1990dc11ffc", size = 931070 }, ] [[package]] @@ -2434,27 +2803,27 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wsproto" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b0/d4/bfa032f961103eba93de583b161f0e6a5b63cebb8f2c7d0c6e6efe1e3d2e/simple_websocket-1.1.0.tar.gz", hash = "sha256:7939234e7aa067c534abdab3a9ed933ec9ce4691b0713c78acb195560aa52ae4", size = 17300, upload-time = "2024-10-10T22:39:31.412Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/d4/bfa032f961103eba93de583b161f0e6a5b63cebb8f2c7d0c6e6efe1e3d2e/simple_websocket-1.1.0.tar.gz", hash = "sha256:7939234e7aa067c534abdab3a9ed933ec9ce4691b0713c78acb195560aa52ae4", size = 17300 } wheels = [ - { url = "https://files.pythonhosted.org/packages/52/59/0782e51887ac6b07ffd1570e0364cf901ebc36345fea669969d2084baebb/simple_websocket-1.1.0-py3-none-any.whl", hash = "sha256:4af6069630a38ed6c561010f0e11a5bc0d4ca569b36306eb257cd9a192497c8c", size = 13842, upload-time = "2024-10-10T22:39:29.645Z" }, + { url = "https://files.pythonhosted.org/packages/52/59/0782e51887ac6b07ffd1570e0364cf901ebc36345fea669969d2084baebb/simple_websocket-1.1.0-py3-none-any.whl", hash = "sha256:4af6069630a38ed6c561010f0e11a5bc0d4ca569b36306eb257cd9a192497c8c", size = 13842 }, ] [[package]] name = "six" version = "1.16.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041, upload-time = "2021-05-05T14:18:18.379Z" } +sdist = { url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053, upload-time = "2021-05-05T14:18:17.237Z" }, + { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053 }, ] [[package]] name = "sniffio" version = "1.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/50/d49c388cae4ec10e8109b1b833fd265511840706808576df3ada99ecb0ac/sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101", size = 17103, upload-time = "2022-09-01T12:31:36.968Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/50/d49c388cae4ec10e8109b1b833fd265511840706808576df3ada99ecb0ac/sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101", size = 17103 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/a0/5dba8ed157b0136607c7f2151db695885606968d1fae123dc3391e0cfdbf/sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384", size = 10165, upload-time = "2022-09-01T12:31:34.186Z" }, + { url = "https://files.pythonhosted.org/packages/c3/a0/5dba8ed157b0136607c7f2151db695885606968d1fae123dc3391e0cfdbf/sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384", size = 10165 }, ] [[package]] @@ -2464,9 +2833,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3e/da/1fb4bdb72ae12b834becd7e1e7e47001d32f91ec0ce8d7bc1b618d9f0bd9/starlette-0.41.2.tar.gz", hash = "sha256:9834fd799d1a87fd346deb76158668cfa0b0d56f85caefe8268e2d97c3468b62", size = 2573867, upload-time = "2024-10-27T08:20:02.818Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3e/da/1fb4bdb72ae12b834becd7e1e7e47001d32f91ec0ce8d7bc1b618d9f0bd9/starlette-0.41.2.tar.gz", hash = "sha256:9834fd799d1a87fd346deb76158668cfa0b0d56f85caefe8268e2d97c3468b62", size = 2573867 } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/43/f185bfd0ca1d213beb4293bed51d92254df23d8ceaf6c0e17146d508a776/starlette-0.41.2-py3-none-any.whl", hash = "sha256:fbc189474b4731cf30fcef52f18a8d070e3f3b46c6a04c97579e85e6ffca942d", size = 73259, upload-time = "2024-10-27T08:20:00.052Z" }, + { url = "https://files.pythonhosted.org/packages/54/43/f185bfd0ca1d213beb4293bed51d92254df23d8ceaf6c0e17146d508a776/starlette-0.41.2-py3-none-any.whl", hash = "sha256:fbc189474b4731cf30fcef52f18a8d070e3f3b46c6a04c97579e85e6ffca942d", size = 73259 }, ] [[package]] @@ -2476,18 +2845,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mpmath" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e5/57/3485a1a3dff51bfd691962768b14310dae452431754bfc091250be50dd29/sympy-1.12.tar.gz", hash = "sha256:ebf595c8dac3e0fdc4152c51878b498396ec7f30e7a914d6071e674d49420fb8", size = 6722203, upload-time = "2023-05-10T18:23:00.378Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/57/3485a1a3dff51bfd691962768b14310dae452431754bfc091250be50dd29/sympy-1.12.tar.gz", hash = "sha256:ebf595c8dac3e0fdc4152c51878b498396ec7f30e7a914d6071e674d49420fb8", size = 6722203 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/05/e6600db80270777c4a64238a98d442f0fd07cc8915be2a1c16da7f2b9e74/sympy-1.12-py3-none-any.whl", hash = "sha256:c3588cd4295d0c0f603d0f2ae780587e64e2efeedb3521e46b9bb1d08d184fa5", size = 5742435, upload-time = "2023-05-10T18:22:14.76Z" }, + { url = "https://files.pythonhosted.org/packages/d2/05/e6600db80270777c4a64238a98d442f0fd07cc8915be2a1c16da7f2b9e74/sympy-1.12-py3-none-any.whl", hash = "sha256:c3588cd4295d0c0f603d0f2ae780587e64e2efeedb3521e46b9bb1d08d184fa5", size = 5742435 }, ] [[package]] name = "threadpoolctl" version = "3.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/8a/c05f7831beb32aff70f808766224f11c650f7edfd49b27a8fc6666107006/threadpoolctl-3.2.0.tar.gz", hash = "sha256:c96a0ba3bdddeaca37dc4cc7344aafad41cdb8c313f74fdfe387a867bba93355", size = 36266, upload-time = "2023-07-13T14:53:53.299Z" } +sdist = { url = "https://files.pythonhosted.org/packages/47/8a/c05f7831beb32aff70f808766224f11c650f7edfd49b27a8fc6666107006/threadpoolctl-3.2.0.tar.gz", hash = "sha256:c96a0ba3bdddeaca37dc4cc7344aafad41cdb8c313f74fdfe387a867bba93355", size = 36266 } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/12/fd4dea011af9d69e1cad05c75f3f7202cdcbeac9b712eea58ca779a72865/threadpoolctl-3.2.0-py3-none-any.whl", hash = "sha256:2b7818516e423bdaebb97c723f86a7c6b0a83d3f3b0970328d66f4d9104dc032", size = 15539, upload-time = "2023-07-13T14:53:39.336Z" }, + { url = "https://files.pythonhosted.org/packages/81/12/fd4dea011af9d69e1cad05c75f3f7202cdcbeac9b712eea58ca779a72865/threadpoolctl-3.2.0-py3-none-any.whl", hash = "sha256:2b7818516e423bdaebb97c723f86a7c6b0a83d3f3b0970328d66f4d9104dc032", size = 15539 }, ] [[package]] @@ -2497,43 +2866,43 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f8/a4/6c0eadea1ccfcda27e6cce400c366098b5b082138a073f4252fe399f4148/tifffile-2023.12.9.tar.gz", hash = "sha256:9dd1da91180a6453018a241ff219e1905f169384355cd89c9ef4034c1b46cdb8", size = 353467, upload-time = "2023-12-09T20:46:29.203Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/a4/6c0eadea1ccfcda27e6cce400c366098b5b082138a073f4252fe399f4148/tifffile-2023.12.9.tar.gz", hash = "sha256:9dd1da91180a6453018a241ff219e1905f169384355cd89c9ef4034c1b46cdb8", size = 353467 } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/a4/569fc717831969cf48bced350bdaf070cdeab06918d179429899e144358d/tifffile-2023.12.9-py3-none-any.whl", hash = "sha256:9b066e4b1a900891ea42ffd33dab8ba34c537935618b9893ddef42d7d422692f", size = 223627, upload-time = "2023-12-09T20:46:26.569Z" }, + { url = "https://files.pythonhosted.org/packages/54/a4/569fc717831969cf48bced350bdaf070cdeab06918d179429899e144358d/tifffile-2023.12.9-py3-none-any.whl", hash = "sha256:9b066e4b1a900891ea42ffd33dab8ba34c537935618b9893ddef42d7d422692f", size = 223627 }, ] [[package]] name = "tokenizers" -version = "0.21.2" +version = "0.21.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ab/2d/b0fce2b8201635f60e8c95990080f58461cc9ca3d5026de2e900f38a7f21/tokenizers-0.21.2.tar.gz", hash = "sha256:fdc7cffde3e2113ba0e6cc7318c40e3438a4d74bbc62bf04bcc63bdfb082ac77", size = 351545, upload-time = "2025-06-24T10:24:52.449Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c2/2f/402986d0823f8d7ca139d969af2917fefaa9b947d1fb32f6168c509f2492/tokenizers-0.21.4.tar.gz", hash = "sha256:fa23f85fbc9a02ec5c6978da172cdcbac23498c3ca9f3645c5c68740ac007880", size = 351253 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/cc/2936e2d45ceb130a21d929743f1e9897514691bec123203e10837972296f/tokenizers-0.21.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:342b5dfb75009f2255ab8dec0041287260fed5ce00c323eb6bab639066fef8ec", size = 2875206, upload-time = "2025-06-24T10:24:42.755Z" }, - { url = "https://files.pythonhosted.org/packages/6c/e6/33f41f2cc7861faeba8988e7a77601407bf1d9d28fc79c5903f8f77df587/tokenizers-0.21.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:126df3205d6f3a93fea80c7a8a266a78c1bd8dd2fe043386bafdd7736a23e45f", size = 2732655, upload-time = "2025-06-24T10:24:41.56Z" }, - { url = "https://files.pythonhosted.org/packages/33/2b/1791eb329c07122a75b01035b1a3aa22ad139f3ce0ece1b059b506d9d9de/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a32cd81be21168bd0d6a0f0962d60177c447a1aa1b1e48fa6ec9fc728ee0b12", size = 3019202, upload-time = "2025-06-24T10:24:31.791Z" }, - { url = "https://files.pythonhosted.org/packages/05/15/fd2d8104faa9f86ac68748e6f7ece0b5eb7983c7efc3a2c197cb98c99030/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8bd8999538c405133c2ab999b83b17c08b7fc1b48c1ada2469964605a709ef91", size = 2934539, upload-time = "2025-06-24T10:24:34.567Z" }, - { url = "https://files.pythonhosted.org/packages/a5/2e/53e8fd053e1f3ffbe579ca5f9546f35ac67cf0039ed357ad7ec57f5f5af0/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5e9944e61239b083a41cf8fc42802f855e1dca0f499196df37a8ce219abac6eb", size = 3248665, upload-time = "2025-06-24T10:24:39.024Z" }, - { url = "https://files.pythonhosted.org/packages/00/15/79713359f4037aa8f4d1f06ffca35312ac83629da062670e8830917e2153/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:514cd43045c5d546f01142ff9c79a96ea69e4b5cda09e3027708cb2e6d5762ab", size = 3451305, upload-time = "2025-06-24T10:24:36.133Z" }, - { url = "https://files.pythonhosted.org/packages/38/5f/959f3a8756fc9396aeb704292777b84f02a5c6f25c3fc3ba7530db5feb2c/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b1b9405822527ec1e0f7d8d2fdb287a5730c3a6518189c968254a8441b21faae", size = 3214757, upload-time = "2025-06-24T10:24:37.784Z" }, - { url = "https://files.pythonhosted.org/packages/c5/74/f41a432a0733f61f3d21b288de6dfa78f7acff309c6f0f323b2833e9189f/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed9a4d51c395103ad24f8e7eb976811c57fbec2af9f133df471afcd922e5020", size = 3121887, upload-time = "2025-06-24T10:24:40.293Z" }, - { url = "https://files.pythonhosted.org/packages/3c/6a/bc220a11a17e5d07b0dfb3b5c628621d4dcc084bccd27cfaead659963016/tokenizers-0.21.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2c41862df3d873665ec78b6be36fcc30a26e3d4902e9dd8608ed61d49a48bc19", size = 9091965, upload-time = "2025-06-24T10:24:44.431Z" }, - { url = "https://files.pythonhosted.org/packages/6c/bd/ac386d79c4ef20dc6f39c4706640c24823dca7ebb6f703bfe6b5f0292d88/tokenizers-0.21.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ed21dc7e624e4220e21758b2e62893be7101453525e3d23264081c9ef9a6d00d", size = 9053372, upload-time = "2025-06-24T10:24:46.455Z" }, - { url = "https://files.pythonhosted.org/packages/63/7b/5440bf203b2a5358f074408f7f9c42884849cd9972879e10ee6b7a8c3b3d/tokenizers-0.21.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:0e73770507e65a0e0e2a1affd6b03c36e3bc4377bd10c9ccf51a82c77c0fe365", size = 9298632, upload-time = "2025-06-24T10:24:48.446Z" }, - { url = "https://files.pythonhosted.org/packages/a4/d2/faa1acac3f96a7427866e94ed4289949b2524f0c1878512516567d80563c/tokenizers-0.21.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:106746e8aa9014a12109e58d540ad5465b4c183768ea96c03cbc24c44d329958", size = 9470074, upload-time = "2025-06-24T10:24:50.378Z" }, - { url = "https://files.pythonhosted.org/packages/d8/a5/896e1ef0707212745ae9f37e84c7d50269411aef2e9ccd0de63623feecdf/tokenizers-0.21.2-cp39-abi3-win32.whl", hash = "sha256:cabda5a6d15d620b6dfe711e1af52205266d05b379ea85a8a301b3593c60e962", size = 2330115, upload-time = "2025-06-24T10:24:55.069Z" }, - { url = "https://files.pythonhosted.org/packages/13/c3/cc2755ee10be859c4338c962a35b9a663788c0c0b50c0bdd8078fb6870cf/tokenizers-0.21.2-cp39-abi3-win_amd64.whl", hash = "sha256:58747bb898acdb1007f37a7bbe614346e98dc28708ffb66a3fd50ce169ac6c98", size = 2509918, upload-time = "2025-06-24T10:24:53.71Z" }, + { url = "https://files.pythonhosted.org/packages/98/c6/fdb6f72bf6454f52eb4a2510be7fb0f614e541a2554d6210e370d85efff4/tokenizers-0.21.4-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:2ccc10a7c3bcefe0f242867dc914fc1226ee44321eb618cfe3019b5df3400133", size = 2863987 }, + { url = "https://files.pythonhosted.org/packages/8d/a6/28975479e35ddc751dc1ddc97b9b69bf7fcf074db31548aab37f8116674c/tokenizers-0.21.4-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:5e2f601a8e0cd5be5cc7506b20a79112370b9b3e9cb5f13f68ab11acd6ca7d60", size = 2732457 }, + { url = "https://files.pythonhosted.org/packages/aa/8f/24f39d7b5c726b7b0be95dca04f344df278a3fe3a4deb15a975d194cbb32/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b376f5a1aee67b4d29032ee85511bbd1b99007ec735f7f35c8a2eb104eade5", size = 3012624 }, + { url = "https://files.pythonhosted.org/packages/58/47/26358925717687a58cb74d7a508de96649544fad5778f0cd9827398dc499/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2107ad649e2cda4488d41dfd031469e9da3fcbfd6183e74e4958fa729ffbf9c6", size = 2939681 }, + { url = "https://files.pythonhosted.org/packages/99/6f/cc300fea5db2ab5ddc2c8aea5757a27b89c84469899710c3aeddc1d39801/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c73012da95afafdf235ba80047699df4384fdc481527448a078ffd00e45a7d9", size = 3247445 }, + { url = "https://files.pythonhosted.org/packages/be/bf/98cb4b9c3c4afd8be89cfa6423704337dc20b73eb4180397a6e0d456c334/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f23186c40395fc390d27f519679a58023f368a0aad234af145e0f39ad1212732", size = 3428014 }, + { url = "https://files.pythonhosted.org/packages/75/c7/96c1cc780e6ca7f01a57c13235dd05b7bc1c0f3588512ebe9d1331b5f5ae/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cc88bb34e23a54cc42713d6d98af5f1bf79c07653d24fe984d2d695ba2c922a2", size = 3193197 }, + { url = "https://files.pythonhosted.org/packages/f2/90/273b6c7ec78af547694eddeea9e05de771278bd20476525ab930cecaf7d8/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51b7eabb104f46c1c50b486520555715457ae833d5aee9ff6ae853d1130506ff", size = 3115426 }, + { url = "https://files.pythonhosted.org/packages/91/43/c640d5a07e95f1cf9d2c92501f20a25f179ac53a4f71e1489a3dcfcc67ee/tokenizers-0.21.4-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:714b05b2e1af1288bd1bc56ce496c4cebb64a20d158ee802887757791191e6e2", size = 9089127 }, + { url = "https://files.pythonhosted.org/packages/44/a1/dd23edd6271d4dca788e5200a807b49ec3e6987815cd9d0a07ad9c96c7c2/tokenizers-0.21.4-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:1340ff877ceedfa937544b7d79f5b7becf33a4cfb58f89b3b49927004ef66f78", size = 9055243 }, + { url = "https://files.pythonhosted.org/packages/21/2b/b410d6e9021c4b7ddb57248304dc817c4d4970b73b6ee343674914701197/tokenizers-0.21.4-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:3c1f4317576e465ac9ef0d165b247825a2a4078bcd01cba6b54b867bdf9fdd8b", size = 9298237 }, + { url = "https://files.pythonhosted.org/packages/b7/0a/42348c995c67e2e6e5c89ffb9cfd68507cbaeb84ff39c49ee6e0a6dd0fd2/tokenizers-0.21.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:c212aa4e45ec0bb5274b16b6f31dd3f1c41944025c2358faaa5782c754e84c24", size = 9461980 }, + { url = "https://files.pythonhosted.org/packages/3d/d3/dacccd834404cd71b5c334882f3ba40331ad2120e69ded32cf5fda9a7436/tokenizers-0.21.4-cp39-abi3-win32.whl", hash = "sha256:6c42a930bc5f4c47f4ea775c91de47d27910881902b0f20e4990ebe045a415d0", size = 2329871 }, + { url = "https://files.pythonhosted.org/packages/41/f2/fd673d979185f5dcbac4be7d09461cbb99751554ffb6718d0013af8604cb/tokenizers-0.21.4-cp39-abi3-win_amd64.whl", hash = "sha256:475d807a5c3eb72c59ad9b5fcdb254f6e17f53dfcbb9903233b0dfa9c943b597", size = 2507568 }, ] [[package]] name = "tomli" version = "2.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c0/3f/d7af728f075fb08564c5949a9c95e44352e23dee646869fa104a3b2060a3/tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f", size = 15164, upload-time = "2022-02-08T10:54:04.006Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/3f/d7af728f075fb08564c5949a9c95e44352e23dee646869fa104a3b2060a3/tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f", size = 15164 } wheels = [ - { url = "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", size = 12757, upload-time = "2022-02-08T10:54:02.017Z" }, + { url = "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", size = 12757 }, ] [[package]] @@ -2543,18 +2912,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/03/00/6a9b3aedb0b60a80995ade30f718f1a9902612f22a1aaf531c85a02987f7/tqdm-4.66.3.tar.gz", hash = "sha256:23097a41eba115ba99ecae40d06444c15d1c0c698d527a01c6c8bd1c5d0647e5", size = 169551, upload-time = "2024-05-02T21:44:05.084Z" } +sdist = { url = "https://files.pythonhosted.org/packages/03/00/6a9b3aedb0b60a80995ade30f718f1a9902612f22a1aaf531c85a02987f7/tqdm-4.66.3.tar.gz", hash = "sha256:23097a41eba115ba99ecae40d06444c15d1c0c698d527a01c6c8bd1c5d0647e5", size = 169551 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/ad/7d47bbf2cae78ff79f29db0bed5016ec9c56b212a93fca624bb88b551a7c/tqdm-4.66.3-py3-none-any.whl", hash = "sha256:4f41d54107ff9a223dca80b53efe4fb654c67efaba7f47bada3ee9d50e05bd53", size = 78374, upload-time = "2024-05-02T21:44:01.541Z" }, + { url = "https://files.pythonhosted.org/packages/d1/ad/7d47bbf2cae78ff79f29db0bed5016ec9c56b212a93fca624bb88b551a7c/tqdm-4.66.3-py3-none-any.whl", hash = "sha256:4f41d54107ff9a223dca80b53efe4fb654c67efaba7f47bada3ee9d50e05bd53", size = 78374 }, ] [[package]] name = "types-pyyaml" version = "6.0.12.20250516" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4e/22/59e2aeb48ceeee1f7cd4537db9568df80d62bdb44a7f9e743502ea8aab9c/types_pyyaml-6.0.12.20250516.tar.gz", hash = "sha256:9f21a70216fc0fa1b216a8176db5f9e0af6eb35d2f2932acb87689d03a5bf6ba", size = 17378, upload-time = "2025-05-16T03:08:04.897Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/22/59e2aeb48ceeee1f7cd4537db9568df80d62bdb44a7f9e743502ea8aab9c/types_pyyaml-6.0.12.20250516.tar.gz", hash = "sha256:9f21a70216fc0fa1b216a8176db5f9e0af6eb35d2f2932acb87689d03a5bf6ba", size = 17378 } wheels = [ - { url = "https://files.pythonhosted.org/packages/99/5f/e0af6f7f6a260d9af67e1db4f54d732abad514252a7a378a6c4d17dd1036/types_pyyaml-6.0.12.20250516-py3-none-any.whl", hash = "sha256:8478208feaeb53a34cb5d970c56a7cd76b72659442e733e268a94dc72b2d0530", size = 20312, upload-time = "2025-05-16T03:08:04.019Z" }, + { url = "https://files.pythonhosted.org/packages/99/5f/e0af6f7f6a260d9af67e1db4f54d732abad514252a7a378a6c4d17dd1036/types_pyyaml-6.0.12.20250516-py3-none-any.whl", hash = "sha256:8478208feaeb53a34cb5d970c56a7cd76b72659442e733e268a94dc72b2d0530", size = 20312 }, ] [[package]] @@ -2564,45 +2933,45 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6d/7f/73b3a04a53b0fd2a911d4ec517940ecd6600630b559e4505cc7b68beb5a0/types_requests-2.32.4.20250611.tar.gz", hash = "sha256:741c8777ed6425830bf51e54d6abe245f79b4dcb9019f1622b773463946bf826", size = 23118, upload-time = "2025-06-11T03:11:41.272Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/7f/73b3a04a53b0fd2a911d4ec517940ecd6600630b559e4505cc7b68beb5a0/types_requests-2.32.4.20250611.tar.gz", hash = "sha256:741c8777ed6425830bf51e54d6abe245f79b4dcb9019f1622b773463946bf826", size = 23118 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/ea/0be9258c5a4fa1ba2300111aa5a0767ee6d18eb3fd20e91616c12082284d/types_requests-2.32.4.20250611-py3-none-any.whl", hash = "sha256:ad2fe5d3b0cb3c2c902c8815a70e7fb2302c4b8c1f77bdcd738192cdb3878072", size = 20643, upload-time = "2025-06-11T03:11:40.186Z" }, + { url = "https://files.pythonhosted.org/packages/3d/ea/0be9258c5a4fa1ba2300111aa5a0767ee6d18eb3fd20e91616c12082284d/types_requests-2.32.4.20250611-py3-none-any.whl", hash = "sha256:ad2fe5d3b0cb3c2c902c8815a70e7fb2302c4b8c1f77bdcd738192cdb3878072", size = 20643 }, ] [[package]] name = "types-setuptools" -version = "80.9.0.20250529" +version = "80.9.0.20250809" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/79/66/1b276526aad4696a9519919e637801f2c103419d2c248a6feb2729e034d1/types_setuptools-80.9.0.20250529.tar.gz", hash = "sha256:79e088ba0cba2186c8d6499cbd3e143abb142d28a44b042c28d3148b1e353c91", size = 41337, upload-time = "2025-05-29T03:07:34.487Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/4f/d78a04083ee3cc0a7c14406afb2f1e7b63e70da95b777571d665d89b1765/types_setuptools-80.9.0.20250809.tar.gz", hash = "sha256:e986ba37ffde364073d76189e1d79d9928fb6f5278c7d07589cde353d0218864", size = 41209 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/d8/83790d67ec771bf029a45ff1bd1aedbb738d8aa58c09dd0cc3033eea0e69/types_setuptools-80.9.0.20250529-py3-none-any.whl", hash = "sha256:00dfcedd73e333a430e10db096e4d46af93faf9314f832f13b6bbe3d6757e95f", size = 63263, upload-time = "2025-05-29T03:07:33.064Z" }, + { url = "https://files.pythonhosted.org/packages/ca/1d/ad4fd409b377904324cbd2dc3a11e29ba13e2cf603c5a14cd88a35da5be0/types_setuptools-80.9.0.20250809-py3-none-any.whl", hash = "sha256:7c6539b4c7ac7b4ab4db2be66d8a58fb1e28affa3ee3834be48acafd94f5976a", size = 63160 }, ] [[package]] name = "types-simplejson" version = "3.20.0.20250326" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/14/e26fc55e1ea56f9ea470917d3e2f8240e6d043ca914181021d04115ae0f7/types_simplejson-3.20.0.20250326.tar.gz", hash = "sha256:b2689bc91e0e672d7a5a947b4cb546b76ae7ddc2899c6678e72a10bf96cd97d2", size = 10489, upload-time = "2025-03-26T02:53:35.825Z" } +sdist = { url = "https://files.pythonhosted.org/packages/af/14/e26fc55e1ea56f9ea470917d3e2f8240e6d043ca914181021d04115ae0f7/types_simplejson-3.20.0.20250326.tar.gz", hash = "sha256:b2689bc91e0e672d7a5a947b4cb546b76ae7ddc2899c6678e72a10bf96cd97d2", size = 10489 } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/bf/d3f3a5ba47fd18115e8446d39f025b85905d2008677c29ee4d03b4cddd57/types_simplejson-3.20.0.20250326-py3-none-any.whl", hash = "sha256:db1ddea7b8f7623b27a137578f22fc6c618db8c83ccfb1828ca0d2f0ec11efa7", size = 10462, upload-time = "2025-03-26T02:53:35.036Z" }, + { url = "https://files.pythonhosted.org/packages/76/bf/d3f3a5ba47fd18115e8446d39f025b85905d2008677c29ee4d03b4cddd57/types_simplejson-3.20.0.20250326-py3-none-any.whl", hash = "sha256:db1ddea7b8f7623b27a137578f22fc6c618db8c83ccfb1828ca0d2f0ec11efa7", size = 10462 }, ] [[package]] name = "types-ujson" version = "5.10.0.20250326" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cc/5c/c974451c4babdb4ae3588925487edde492d59a8403010b4642a554d09954/types_ujson-5.10.0.20250326.tar.gz", hash = "sha256:5469e05f2c31ecb3c4c0267cc8fe41bcd116826fbb4ded69801a645c687dd014", size = 8340, upload-time = "2025-03-26T02:53:39.197Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/5c/c974451c4babdb4ae3588925487edde492d59a8403010b4642a554d09954/types_ujson-5.10.0.20250326.tar.gz", hash = "sha256:5469e05f2c31ecb3c4c0267cc8fe41bcd116826fbb4ded69801a645c687dd014", size = 8340 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/c9/8a73a5f8fa6e70fc02eed506d5ac0ae9ceafbd2b8c9ad34a7de0f29900d6/types_ujson-5.10.0.20250326-py3-none-any.whl", hash = "sha256:acc0913f569def62ef6a892c8a47703f65d05669a3252391a97765cf207dca5b", size = 7644, upload-time = "2025-03-26T02:53:38.2Z" }, + { url = "https://files.pythonhosted.org/packages/3e/c9/8a73a5f8fa6e70fc02eed506d5ac0ae9ceafbd2b8c9ad34a7de0f29900d6/types_ujson-5.10.0.20250326-py3-none-any.whl", hash = "sha256:acc0913f569def62ef6a892c8a47703f65d05669a3252391a97765cf207dca5b", size = 7644 }, ] [[package]] name = "typing-extensions" version = "4.12.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321, upload-time = "2024-06-07T18:52:15.995Z" } +sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } wheels = [ - { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438, upload-time = "2024-06-07T18:52:13.582Z" }, + { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, ] [[package]] @@ -2612,18 +2981,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222, upload-time = "2025-02-25T17:27:59.638Z" } +sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125, upload-time = "2025-02-25T17:27:57.754Z" }, + { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125 }, ] [[package]] name = "urllib3" version = "2.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/36/dd/a6b232f449e1bc71802a5b7950dc3675d32c6dbc2a1bd6d71f065551adb6/urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54", size = 263900, upload-time = "2023-11-13T12:29:45.049Z" } +sdist = { url = "https://files.pythonhosted.org/packages/36/dd/a6b232f449e1bc71802a5b7950dc3675d32c6dbc2a1bd6d71f065551adb6/urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54", size = 263900 } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/94/c31f58c7a7f470d5665935262ebd7455c7e4c7782eb525658d3dbf4b9403/urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3", size = 104579, upload-time = "2023-11-13T12:29:42.719Z" }, + { url = "https://files.pythonhosted.org/packages/96/94/c31f58c7a7f470d5665935262ebd7455c7e4c7782eb525658d3dbf4b9403/urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3", size = 104579 }, ] [[package]] @@ -2635,9 +3004,9 @@ dependencies = [ { name = "h11" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5e/42/e0e305207bb88c6b8d3061399c6a961ffe5fbb7e2aa63c9234df7259e9cd/uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01", size = 78473, upload-time = "2025-06-28T16:15:46.058Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/42/e0e305207bb88c6b8d3061399c6a961ffe5fbb7e2aa63c9234df7259e9cd/uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01", size = 78473 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/e2/dc81b1bd1dcfe91735810265e9d26bc8ec5da45b4c0f6237e286819194c3/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a", size = 66406, upload-time = "2025-06-28T16:15:44.816Z" }, + { url = "https://files.pythonhosted.org/packages/d2/e2/dc81b1bd1dcfe91735810265e9d26bc8ec5da45b4c0f6237e286819194c3/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a", size = 66406 }, ] [package.optional-dependencies] @@ -2655,26 +3024,26 @@ standard = [ name = "uvloop" version = "0.19.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9c/16/728cc5dde368e6eddb299c5aec4d10eaf25335a5af04e8c0abd68e2e9d32/uvloop-0.19.0.tar.gz", hash = "sha256:0246f4fd1bf2bf702e06b0d45ee91677ee5c31242f39aab4ea6fe0c51aedd0fd", size = 2318492, upload-time = "2023-10-22T22:03:57.665Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/16/728cc5dde368e6eddb299c5aec4d10eaf25335a5af04e8c0abd68e2e9d32/uvloop-0.19.0.tar.gz", hash = "sha256:0246f4fd1bf2bf702e06b0d45ee91677ee5c31242f39aab4ea6fe0c51aedd0fd", size = 2318492 } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/c2/27bf858a576b1fa35b5c2c2029c8cec424a8789e87545ed2f25466d1f21d/uvloop-0.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de4313d7f575474c8f5a12e163f6d89c0a878bc49219641d49e6f1444369a90e", size = 1443484, upload-time = "2023-10-22T22:02:54.169Z" }, - { url = "https://files.pythonhosted.org/packages/4e/35/05b6064b93f4113412d1fd92bdcb6018607e78ae94d1712e63e533f9b2fa/uvloop-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5588bd21cf1fcf06bded085f37e43ce0e00424197e7c10e77afd4bbefffef428", size = 793850, upload-time = "2023-10-22T22:02:56.311Z" }, - { url = "https://files.pythonhosted.org/packages/aa/56/b62ab4e10458ce96bb30c98d327c127f989d3bb4ef899e4c410c739f7ef6/uvloop-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b1fd71c3843327f3bbc3237bedcdb6504fd50368ab3e04d0410e52ec293f5b8", size = 3418601, upload-time = "2023-10-22T22:02:58.717Z" }, - { url = "https://files.pythonhosted.org/packages/ab/ed/12729fba5e3b7e02ee70b3ea230b88e60a50375cf63300db22607694d2f0/uvloop-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a05128d315e2912791de6088c34136bfcdd0c7cbc1cf85fd6fd1bb321b7c849", size = 3416731, upload-time = "2023-10-22T22:03:01.043Z" }, - { url = "https://files.pythonhosted.org/packages/a2/23/80381a2d728d2a0c36e2eef202f5b77428990004d8fbdd3865558ff49fa5/uvloop-0.19.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cd81bdc2b8219cb4b2556eea39d2e36bfa375a2dd021404f90a62e44efaaf957", size = 4128572, upload-time = "2023-10-22T22:03:02.874Z" }, - { url = "https://files.pythonhosted.org/packages/6b/23/1ee41a15e1ad15182e2bd12cbfd37bcb6802f01d6bbcaddf6ca136cbb308/uvloop-0.19.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f17766fb6da94135526273080f3455a112f82570b2ee5daa64d682387fe0dcd", size = 4129235, upload-time = "2023-10-22T22:03:05.361Z" }, - { url = "https://files.pythonhosted.org/packages/41/2a/608ad69f27f51280098abee440c33e921d3ad203e2c86f7262e241e49c99/uvloop-0.19.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ce6b0af8f2729a02a5d1575feacb2a94fc7b2e983868b009d51c9a9d2149bef", size = 1357681, upload-time = "2023-10-22T22:03:07.158Z" }, - { url = "https://files.pythonhosted.org/packages/13/00/d0923d66d80c8717983493a4d7af747ce47f1c2147d82df057a846ba6bff/uvloop-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:31e672bb38b45abc4f26e273be83b72a0d28d074d5b370fc4dcf4c4eb15417d2", size = 746421, upload-time = "2023-10-22T22:03:09.4Z" }, - { url = "https://files.pythonhosted.org/packages/1f/c7/e494c367b0c6e6453f9bed5a78548f5b2ff49add36302cd915a91d347d88/uvloop-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:570fc0ed613883d8d30ee40397b79207eedd2624891692471808a95069a007c1", size = 3481000, upload-time = "2023-10-22T22:03:11.755Z" }, - { url = "https://files.pythonhosted.org/packages/86/cc/1829b3f740e4cb1baefff8240a1c6fc8db9e3caac7b93169aec7d4386069/uvloop-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5138821e40b0c3e6c9478643b4660bd44372ae1e16a322b8fc07478f92684e24", size = 3476361, upload-time = "2023-10-22T22:03:13.841Z" }, - { url = "https://files.pythonhosted.org/packages/7a/4c/ca87e8f5a30629ffa2038c20907c8ab455c5859ff10e810227b76e60d927/uvloop-0.19.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:91ab01c6cd00e39cde50173ba4ec68a1e578fee9279ba64f5221810a9e786533", size = 4169571, upload-time = "2023-10-22T22:03:15.618Z" }, - { url = "https://files.pythonhosted.org/packages/d2/a9/f947a00c47b1c87c937cac2423243a41ba08f0fb76d04eb0d1d170606e0a/uvloop-0.19.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:47bf3e9312f63684efe283f7342afb414eea4d3011542155c7e625cd799c3b12", size = 4170459, upload-time = "2023-10-22T22:03:17.988Z" }, - { url = "https://files.pythonhosted.org/packages/85/57/6736733bb0e86a4b5380d04082463b289c0baecaa205934ba81e8a1d5ea4/uvloop-0.19.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:da8435a3bd498419ee8c13c34b89b5005130a476bda1d6ca8cfdde3de35cd650", size = 1355376, upload-time = "2023-10-22T22:03:20.075Z" }, - { url = "https://files.pythonhosted.org/packages/eb/0c/51339463da912ed34b48d470538d98a91660749b2db56902f23db9b42fdd/uvloop-0.19.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:02506dc23a5d90e04d4f65c7791e65cf44bd91b37f24cfc3ef6cf2aff05dc7ec", size = 745031, upload-time = "2023-10-22T22:03:21.404Z" }, - { url = "https://files.pythonhosted.org/packages/e6/fc/f0daaf19f5b2116a2d26eb9f98c4a45084aea87bf03c33bcca7aa1ff36e5/uvloop-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2693049be9d36fef81741fddb3f441673ba12a34a704e7b4361efb75cf30befc", size = 4077630, upload-time = "2023-10-22T22:03:23.568Z" }, - { url = "https://files.pythonhosted.org/packages/fd/96/fdc318ffe82ae567592b213ec2fcd8ecedd927b5da068cf84d56b28c51a4/uvloop-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7010271303961c6f0fe37731004335401eb9075a12680738731e9c92ddd96ad6", size = 4159957, upload-time = "2023-10-22T22:03:25.278Z" }, - { url = "https://files.pythonhosted.org/packages/71/bc/092068ae7fc16dcf20f3e389126ba7800cee75ffba83f78bf1d167aee3cd/uvloop-0.19.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5daa304d2161d2918fa9a17d5635099a2f78ae5b5960e742b2fcfbb7aefaa593", size = 4014951, upload-time = "2023-10-22T22:03:27.055Z" }, - { url = "https://files.pythonhosted.org/packages/a6/f2/6ce1e73933eb038c89f929e26042e64b2cb8d4453410153eed14918ca9a8/uvloop-0.19.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7207272c9520203fea9b93843bb775d03e1cf88a80a936ce760f60bb5add92f3", size = 4100911, upload-time = "2023-10-22T22:03:29.39Z" }, + { url = "https://files.pythonhosted.org/packages/36/c2/27bf858a576b1fa35b5c2c2029c8cec424a8789e87545ed2f25466d1f21d/uvloop-0.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de4313d7f575474c8f5a12e163f6d89c0a878bc49219641d49e6f1444369a90e", size = 1443484 }, + { url = "https://files.pythonhosted.org/packages/4e/35/05b6064b93f4113412d1fd92bdcb6018607e78ae94d1712e63e533f9b2fa/uvloop-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5588bd21cf1fcf06bded085f37e43ce0e00424197e7c10e77afd4bbefffef428", size = 793850 }, + { url = "https://files.pythonhosted.org/packages/aa/56/b62ab4e10458ce96bb30c98d327c127f989d3bb4ef899e4c410c739f7ef6/uvloop-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b1fd71c3843327f3bbc3237bedcdb6504fd50368ab3e04d0410e52ec293f5b8", size = 3418601 }, + { url = "https://files.pythonhosted.org/packages/ab/ed/12729fba5e3b7e02ee70b3ea230b88e60a50375cf63300db22607694d2f0/uvloop-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a05128d315e2912791de6088c34136bfcdd0c7cbc1cf85fd6fd1bb321b7c849", size = 3416731 }, + { url = "https://files.pythonhosted.org/packages/a2/23/80381a2d728d2a0c36e2eef202f5b77428990004d8fbdd3865558ff49fa5/uvloop-0.19.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cd81bdc2b8219cb4b2556eea39d2e36bfa375a2dd021404f90a62e44efaaf957", size = 4128572 }, + { url = "https://files.pythonhosted.org/packages/6b/23/1ee41a15e1ad15182e2bd12cbfd37bcb6802f01d6bbcaddf6ca136cbb308/uvloop-0.19.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f17766fb6da94135526273080f3455a112f82570b2ee5daa64d682387fe0dcd", size = 4129235 }, + { url = "https://files.pythonhosted.org/packages/41/2a/608ad69f27f51280098abee440c33e921d3ad203e2c86f7262e241e49c99/uvloop-0.19.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ce6b0af8f2729a02a5d1575feacb2a94fc7b2e983868b009d51c9a9d2149bef", size = 1357681 }, + { url = "https://files.pythonhosted.org/packages/13/00/d0923d66d80c8717983493a4d7af747ce47f1c2147d82df057a846ba6bff/uvloop-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:31e672bb38b45abc4f26e273be83b72a0d28d074d5b370fc4dcf4c4eb15417d2", size = 746421 }, + { url = "https://files.pythonhosted.org/packages/1f/c7/e494c367b0c6e6453f9bed5a78548f5b2ff49add36302cd915a91d347d88/uvloop-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:570fc0ed613883d8d30ee40397b79207eedd2624891692471808a95069a007c1", size = 3481000 }, + { url = "https://files.pythonhosted.org/packages/86/cc/1829b3f740e4cb1baefff8240a1c6fc8db9e3caac7b93169aec7d4386069/uvloop-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5138821e40b0c3e6c9478643b4660bd44372ae1e16a322b8fc07478f92684e24", size = 3476361 }, + { url = "https://files.pythonhosted.org/packages/7a/4c/ca87e8f5a30629ffa2038c20907c8ab455c5859ff10e810227b76e60d927/uvloop-0.19.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:91ab01c6cd00e39cde50173ba4ec68a1e578fee9279ba64f5221810a9e786533", size = 4169571 }, + { url = "https://files.pythonhosted.org/packages/d2/a9/f947a00c47b1c87c937cac2423243a41ba08f0fb76d04eb0d1d170606e0a/uvloop-0.19.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:47bf3e9312f63684efe283f7342afb414eea4d3011542155c7e625cd799c3b12", size = 4170459 }, + { url = "https://files.pythonhosted.org/packages/85/57/6736733bb0e86a4b5380d04082463b289c0baecaa205934ba81e8a1d5ea4/uvloop-0.19.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:da8435a3bd498419ee8c13c34b89b5005130a476bda1d6ca8cfdde3de35cd650", size = 1355376 }, + { url = "https://files.pythonhosted.org/packages/eb/0c/51339463da912ed34b48d470538d98a91660749b2db56902f23db9b42fdd/uvloop-0.19.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:02506dc23a5d90e04d4f65c7791e65cf44bd91b37f24cfc3ef6cf2aff05dc7ec", size = 745031 }, + { url = "https://files.pythonhosted.org/packages/e6/fc/f0daaf19f5b2116a2d26eb9f98c4a45084aea87bf03c33bcca7aa1ff36e5/uvloop-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2693049be9d36fef81741fddb3f441673ba12a34a704e7b4361efb75cf30befc", size = 4077630 }, + { url = "https://files.pythonhosted.org/packages/fd/96/fdc318ffe82ae567592b213ec2fcd8ecedd927b5da068cf84d56b28c51a4/uvloop-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7010271303961c6f0fe37731004335401eb9075a12680738731e9c92ddd96ad6", size = 4159957 }, + { url = "https://files.pythonhosted.org/packages/71/bc/092068ae7fc16dcf20f3e389126ba7800cee75ffba83f78bf1d167aee3cd/uvloop-0.19.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5daa304d2161d2918fa9a17d5635099a2f78ae5b5960e742b2fcfbb7aefaa593", size = 4014951 }, + { url = "https://files.pythonhosted.org/packages/a6/f2/6ce1e73933eb038c89f929e26042e64b2cb8d4453410153eed14918ca9a8/uvloop-0.19.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7207272c9520203fea9b93843bb775d03e1cf88a80a936ce760f60bb5add92f3", size = 4100911 }, ] [[package]] @@ -2684,115 +3053,115 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/79/0ee412e1228aaf6f9568aa180b43cb482472de52560fbd7c283c786534af/watchfiles-0.21.0.tar.gz", hash = "sha256:c76c635fabf542bb78524905718c39f736a98e5ab25b23ec6d4abede1a85a6a3", size = 37098, upload-time = "2023-10-13T13:06:39.809Z" } +sdist = { url = "https://files.pythonhosted.org/packages/66/79/0ee412e1228aaf6f9568aa180b43cb482472de52560fbd7c283c786534af/watchfiles-0.21.0.tar.gz", hash = "sha256:c76c635fabf542bb78524905718c39f736a98e5ab25b23ec6d4abede1a85a6a3", size = 37098 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/85/ea2a035b7d86bf0a29ee1c32bc2df8ad4da77e6602806e679d9735ff28cb/watchfiles-0.21.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:27b4035013f1ea49c6c0b42d983133b136637a527e48c132d368eb19bf1ac6aa", size = 428182, upload-time = "2023-10-13T13:04:34.803Z" }, - { url = "https://files.pythonhosted.org/packages/b5/e5/240e5eb3ff0ee3da3b028ac5be2019c407bdd0f3fdb02bd75fdf3bd10aff/watchfiles-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c81818595eff6e92535ff32825f31c116f867f64ff8cdf6562cd1d6b2e1e8f3e", size = 418275, upload-time = "2023-10-13T13:04:36.632Z" }, - { url = "https://files.pythonhosted.org/packages/5b/79/ecd0dfb04443a1900cd3952d7ea6493bf655c2db9a0d3736a5d98a15da39/watchfiles-0.21.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6c107ea3cf2bd07199d66f156e3ea756d1b84dfd43b542b2d870b77868c98c03", size = 1379785, upload-time = "2023-10-13T13:04:38.641Z" }, - { url = "https://files.pythonhosted.org/packages/41/0e/3333b986b1889bb71f0e44b3fac0591824a679619b8b8ddd70ff8858edc4/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d9ac347653ebd95839a7c607608703b20bc07e577e870d824fa4801bc1cb124", size = 1349374, upload-time = "2023-10-13T13:04:41.711Z" }, - { url = "https://files.pythonhosted.org/packages/18/c4/ad5ad16cad900a29aaa792e0ed121ff70d76f74062b051661090d88c6dfd/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5eb86c6acb498208e7663ca22dbe68ca2cf42ab5bf1c776670a50919a56e64ab", size = 1348033, upload-time = "2023-10-13T13:04:43.324Z" }, - { url = "https://files.pythonhosted.org/packages/4e/d2/769254ff04ba88ceb179a6e892606ac4da17338eb010e85ca7a9c3339234/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f564bf68404144ea6b87a78a3f910cc8de216c6b12a4cf0b27718bf4ec38d303", size = 1464393, upload-time = "2023-10-13T13:04:44.818Z" }, - { url = "https://files.pythonhosted.org/packages/14/d0/662800e778ca20e7664dd5df57751aa79ef18b6abb92224b03c8c2e852a6/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d0f32ebfaa9c6011f8454994f86108c2eb9c79b8b7de00b36d558cadcedaa3d", size = 1542953, upload-time = "2023-10-13T13:04:46.714Z" }, - { url = "https://files.pythonhosted.org/packages/f7/4b/b90dcdc3bbaf3bb2db733e1beea2d01566b601c15fcf8e71dfcc8686c097/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d45d9b699ecbac6c7bd8e0a2609767491540403610962968d258fd6405c17c", size = 1346961, upload-time = "2023-10-13T13:04:48.072Z" }, - { url = "https://files.pythonhosted.org/packages/92/ff/75cc1b30c5abcad13a2a72e75625ec619c7a393028a111d7d24dba578d5e/watchfiles-0.21.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:aff06b2cac3ef4616e26ba17a9c250c1fe9dd8a5d907d0193f84c499b1b6e6a9", size = 1464393, upload-time = "2023-10-13T13:04:49.638Z" }, - { url = "https://files.pythonhosted.org/packages/9a/65/12cbeb363bf220482a559c48107edfd87f09248f55e1ac315a36c2098a0f/watchfiles-0.21.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d9792dff410f266051025ecfaa927078b94cc7478954b06796a9756ccc7e14a9", size = 1463409, upload-time = "2023-10-13T13:04:51.762Z" }, - { url = "https://files.pythonhosted.org/packages/f2/08/92e28867c66f0d9638bb131feca739057efc48dbcd391fd7f0a55507e470/watchfiles-0.21.0-cp310-none-win32.whl", hash = "sha256:214cee7f9e09150d4fb42e24919a1e74d8c9b8a9306ed1474ecaddcd5479c293", size = 268101, upload-time = "2023-10-13T13:04:53.78Z" }, - { url = "https://files.pythonhosted.org/packages/4b/ea/80527adf1ad51488a96fc201715730af5879f4dfeccb5e2069ff82d890d4/watchfiles-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:1ad7247d79f9f55bb25ab1778fd47f32d70cf36053941f07de0b7c4e96b5d235", size = 279675, upload-time = "2023-10-13T13:04:55.113Z" }, - { url = "https://files.pythonhosted.org/packages/57/b9/2667286003dd305b81d3a3aa824d3dfc63dacbf2a96faae09e72d953c430/watchfiles-0.21.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:668c265d90de8ae914f860d3eeb164534ba2e836811f91fecc7050416ee70aa7", size = 428210, upload-time = "2023-10-13T13:04:56.894Z" }, - { url = "https://files.pythonhosted.org/packages/a3/87/6793ac60d2e20c9c1883aec7431c2e7b501ee44a839f6da1b747c13baa23/watchfiles-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a23092a992e61c3a6a70f350a56db7197242f3490da9c87b500f389b2d01eef", size = 418196, upload-time = "2023-10-13T13:04:58.19Z" }, - { url = "https://files.pythonhosted.org/packages/5d/12/e1d1d220c5b99196eea38c9a878964f30a2b55ec9d72fd713191725b35e8/watchfiles-0.21.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e7941bbcfdded9c26b0bf720cb7e6fd803d95a55d2c14b4bd1f6a2772230c586", size = 1380287, upload-time = "2023-10-13T13:04:59.923Z" }, - { url = "https://files.pythonhosted.org/packages/0e/cf/126f0a8683f326d190c3539a769e45e747a80a5fcbf797de82e738c946ae/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11cd0c3100e2233e9c53106265da31d574355c288e15259c0d40a4405cbae317", size = 1349653, upload-time = "2023-10-13T13:05:01.622Z" }, - { url = "https://files.pythonhosted.org/packages/20/6e/6cffd795ec65dbc82f15d95b73d3042c1ddaffc4dd25f6c8240bfcf0640f/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78f30cbe8b2ce770160d3c08cff01b2ae9306fe66ce899b73f0409dc1846c1b", size = 1348844, upload-time = "2023-10-13T13:05:03.805Z" }, - { url = "https://files.pythonhosted.org/packages/d5/2a/f9633279d8937ad84c532997405dd106fa6100e8d2b83e364f1c87561f96/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6674b00b9756b0af620aa2a3346b01f8e2a3dc729d25617e1b89cf6af4a54eb1", size = 1464343, upload-time = "2023-10-13T13:05:05.248Z" }, - { url = "https://files.pythonhosted.org/packages/d7/49/9b2199bbf3c89e7c8dd795fced9dac29f201be8a28a5df0c8ff625737df6/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd7ac678b92b29ba630d8c842d8ad6c555abda1b9ef044d6cc092dacbfc9719d", size = 1542858, upload-time = "2023-10-13T13:05:06.791Z" }, - { url = "https://files.pythonhosted.org/packages/35/e0/e8a9c1fe30e98c5b3507ad381abc4d9ee2c3b9c0ae62ffe9c164a5838186/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c873345680c1b87f1e09e0eaf8cf6c891b9851d8b4d3645e7efe2ec20a20cc7", size = 1347464, upload-time = "2023-10-13T13:05:08.622Z" }, - { url = "https://files.pythonhosted.org/packages/ba/66/873739dd7defdfaee4b880114de9463fae18ba13ae2ddd784806b0ee33b6/watchfiles-0.21.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49f56e6ecc2503e7dbe233fa328b2be1a7797d31548e7a193237dcdf1ad0eee0", size = 1464343, upload-time = "2023-10-13T13:05:10.584Z" }, - { url = "https://files.pythonhosted.org/packages/bd/51/d7539aa258d8f0e5d7b870af8b9b8964b4f88a1e4517eeb8a2efb838e9b3/watchfiles-0.21.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:02d91cbac553a3ad141db016e3350b03184deaafeba09b9d6439826ee594b365", size = 1463338, upload-time = "2023-10-13T13:05:12.671Z" }, - { url = "https://files.pythonhosted.org/packages/ee/92/219c539a2a93b6870fa7b84eace946983126b20a7e15c6c034d8d0472682/watchfiles-0.21.0-cp311-none-win32.whl", hash = "sha256:ebe684d7d26239e23d102a2bad2a358dedf18e462e8808778703427d1f584400", size = 267658, upload-time = "2023-10-13T13:05:13.972Z" }, - { url = "https://files.pythonhosted.org/packages/f3/dc/2a8a447b783f5059c4bf7a6bad8fe59375a5a9ce872774763b25c21c2860/watchfiles-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:4566006aa44cb0d21b8ab53baf4b9c667a0ed23efe4aaad8c227bfba0bf15cbe", size = 280113, upload-time = "2023-10-13T13:05:15.289Z" }, - { url = "https://files.pythonhosted.org/packages/22/15/e4085181cf0210a6ec6eb29fee0c6088de867ee33d81555076a4a2726e8b/watchfiles-0.21.0-cp311-none-win_arm64.whl", hash = "sha256:c550a56bf209a3d987d5a975cdf2063b3389a5d16caf29db4bdddeae49f22078", size = 268688, upload-time = "2023-10-13T13:05:17.144Z" }, - { url = "https://files.pythonhosted.org/packages/a1/fd/2f009eb17809afd32a143b442856628585c9ce3a9c6d5c1841e44e35a16c/watchfiles-0.21.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:51ddac60b96a42c15d24fbdc7a4bfcd02b5a29c047b7f8bf63d3f6f5a860949a", size = 426902, upload-time = "2023-10-13T13:05:18.828Z" }, - { url = "https://files.pythonhosted.org/packages/e0/62/a2605f212a136e06f2d056ee7491ede9935ba0f1d5ceafd1f7da2a0c8625/watchfiles-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:511f0b034120cd1989932bf1e9081aa9fb00f1f949fbd2d9cab6264916ae89b1", size = 417300, upload-time = "2023-10-13T13:05:20.116Z" }, - { url = "https://files.pythonhosted.org/packages/69/0e/29f158fa22eb2cc1f188b5ec20fb5c0a64eb801e3901ad5b7ad546cbaed0/watchfiles-0.21.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cfb92d49dbb95ec7a07511bc9efb0faff8fe24ef3805662b8d6808ba8409a71a", size = 1378126, upload-time = "2023-10-13T13:05:21.508Z" }, - { url = "https://files.pythonhosted.org/packages/e8/f3/c67865cb5a174201c52d34e870cc7956b8408ee83ce9a02909d6a2a93a14/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f92944efc564867bbf841c823c8b71bb0be75e06b8ce45c084b46411475a915", size = 1348275, upload-time = "2023-10-13T13:05:22.995Z" }, - { url = "https://files.pythonhosted.org/packages/d7/eb/b6f1184d1c7b9670f5bd1e184e4c221ecf25fd817cf2fcac6adc387882b5/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:642d66b75eda909fd1112d35c53816d59789a4b38c141a96d62f50a3ef9b3360", size = 1347255, upload-time = "2023-10-13T13:05:24.618Z" }, - { url = "https://files.pythonhosted.org/packages/c8/27/e534e4b3fe739f4bf8bd5dc4c26cbc5d3baa427125d8ef78a6556acd6ff4/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d23bcd6c8eaa6324fe109d8cac01b41fe9a54b8c498af9ce464c1aeeb99903d6", size = 1462845, upload-time = "2023-10-13T13:05:26.531Z" }, - { url = "https://files.pythonhosted.org/packages/b0/ba/a0d1c1c55f75e7e47c8f79f2314f7ec670b5177596f6d27764aecc7048cd/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18d5b4da8cf3e41895b34e8c37d13c9ed294954907929aacd95153508d5d89d7", size = 1528957, upload-time = "2023-10-13T13:05:28.365Z" }, - { url = "https://files.pythonhosted.org/packages/1c/3a/4e38518c4dff58090c01fc8cc051fa08ac9ae00b361c855075809b0058ce/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b8d1eae0f65441963d805f766c7e9cd092f91e0c600c820c764a4ff71a0764c", size = 1345542, upload-time = "2023-10-13T13:05:29.862Z" }, - { url = "https://files.pythonhosted.org/packages/9f/b7/783097f8137a710d5cd9ccbfcd92e4b453d38dab05cfcb5dbd2c587752e5/watchfiles-0.21.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1fd9a5205139f3c6bb60d11f6072e0552f0a20b712c85f43d42342d162be1235", size = 1462238, upload-time = "2023-10-13T13:05:32.245Z" }, - { url = "https://files.pythonhosted.org/packages/6b/4c/b741eb38f2c408ae9c5a25235f6506b1dda43486ae0fdb4c462ef75bce11/watchfiles-0.21.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a1e3014a625bcf107fbf38eece0e47fa0190e52e45dc6eee5a8265ddc6dc5ea7", size = 1462406, upload-time = "2023-10-13T13:05:34.339Z" }, - { url = "https://files.pythonhosted.org/packages/77/e4/8d2b3c67364671b0e1c0ce383895a5415f45ecb3e8586982deff4a8e85c9/watchfiles-0.21.0-cp312-none-win32.whl", hash = "sha256:9d09869f2c5a6f2d9df50ce3064b3391d3ecb6dced708ad64467b9e4f2c9bef3", size = 266789, upload-time = "2023-10-13T13:05:35.606Z" }, - { url = "https://files.pythonhosted.org/packages/da/f2/6b1de38aeb21eb9dac1ae6a1ee4521566e79690117032036c737cfab52fa/watchfiles-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:18722b50783b5e30a18a8a5db3006bab146d2b705c92eb9a94f78c72beb94094", size = 280292, upload-time = "2023-10-13T13:05:37.357Z" }, - { url = "https://files.pythonhosted.org/packages/5a/a5/7aba9435beb863c2490bae3173a45f42044ac7a48155d3dd42ab49cfae45/watchfiles-0.21.0-cp312-none-win_arm64.whl", hash = "sha256:a3b9bec9579a15fb3ca2d9878deae789df72f2b0fdaf90ad49ee389cad5edab6", size = 268026, upload-time = "2023-10-13T13:05:38.591Z" }, - { url = "https://files.pythonhosted.org/packages/62/66/7463ceb43eabc6deaa795c7969ff4d4fd938de54e655035483dfd1e97c84/watchfiles-0.21.0-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab03a90b305d2588e8352168e8c5a1520b721d2d367f31e9332c4235b30b8994", size = 429092, upload-time = "2023-10-13T13:06:21.419Z" }, - { url = "https://files.pythonhosted.org/packages/fe/a3/42686af3a089f34aba35c39abac852869661938dae7025c1a0580dfe0fbf/watchfiles-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:927c589500f9f41e370b0125c12ac9e7d3a2fd166b89e9ee2828b3dda20bfe6f", size = 419188, upload-time = "2023-10-13T13:06:22.934Z" }, - { url = "https://files.pythonhosted.org/packages/37/17/4825999346f15d650f4c69093efa64fb040fbff4f706a20e8c4745f64070/watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bd467213195e76f838caf2c28cd65e58302d0254e636e7c0fca81efa4a2e62c", size = 1350366, upload-time = "2023-10-13T13:06:24.254Z" }, - { url = "https://files.pythonhosted.org/packages/70/76/8d124e14cf51af4d6bba926c7473f253c6efd1539ba62577f079a2d71537/watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02b73130687bc3f6bb79d8a170959042eb56eb3a42df3671c79b428cd73f17cc", size = 1346270, upload-time = "2023-10-13T13:06:25.742Z" }, + { url = "https://files.pythonhosted.org/packages/6e/85/ea2a035b7d86bf0a29ee1c32bc2df8ad4da77e6602806e679d9735ff28cb/watchfiles-0.21.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:27b4035013f1ea49c6c0b42d983133b136637a527e48c132d368eb19bf1ac6aa", size = 428182 }, + { url = "https://files.pythonhosted.org/packages/b5/e5/240e5eb3ff0ee3da3b028ac5be2019c407bdd0f3fdb02bd75fdf3bd10aff/watchfiles-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c81818595eff6e92535ff32825f31c116f867f64ff8cdf6562cd1d6b2e1e8f3e", size = 418275 }, + { url = "https://files.pythonhosted.org/packages/5b/79/ecd0dfb04443a1900cd3952d7ea6493bf655c2db9a0d3736a5d98a15da39/watchfiles-0.21.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6c107ea3cf2bd07199d66f156e3ea756d1b84dfd43b542b2d870b77868c98c03", size = 1379785 }, + { url = "https://files.pythonhosted.org/packages/41/0e/3333b986b1889bb71f0e44b3fac0591824a679619b8b8ddd70ff8858edc4/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d9ac347653ebd95839a7c607608703b20bc07e577e870d824fa4801bc1cb124", size = 1349374 }, + { url = "https://files.pythonhosted.org/packages/18/c4/ad5ad16cad900a29aaa792e0ed121ff70d76f74062b051661090d88c6dfd/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5eb86c6acb498208e7663ca22dbe68ca2cf42ab5bf1c776670a50919a56e64ab", size = 1348033 }, + { url = "https://files.pythonhosted.org/packages/4e/d2/769254ff04ba88ceb179a6e892606ac4da17338eb010e85ca7a9c3339234/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f564bf68404144ea6b87a78a3f910cc8de216c6b12a4cf0b27718bf4ec38d303", size = 1464393 }, + { url = "https://files.pythonhosted.org/packages/14/d0/662800e778ca20e7664dd5df57751aa79ef18b6abb92224b03c8c2e852a6/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d0f32ebfaa9c6011f8454994f86108c2eb9c79b8b7de00b36d558cadcedaa3d", size = 1542953 }, + { url = "https://files.pythonhosted.org/packages/f7/4b/b90dcdc3bbaf3bb2db733e1beea2d01566b601c15fcf8e71dfcc8686c097/watchfiles-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d45d9b699ecbac6c7bd8e0a2609767491540403610962968d258fd6405c17c", size = 1346961 }, + { url = "https://files.pythonhosted.org/packages/92/ff/75cc1b30c5abcad13a2a72e75625ec619c7a393028a111d7d24dba578d5e/watchfiles-0.21.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:aff06b2cac3ef4616e26ba17a9c250c1fe9dd8a5d907d0193f84c499b1b6e6a9", size = 1464393 }, + { url = "https://files.pythonhosted.org/packages/9a/65/12cbeb363bf220482a559c48107edfd87f09248f55e1ac315a36c2098a0f/watchfiles-0.21.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d9792dff410f266051025ecfaa927078b94cc7478954b06796a9756ccc7e14a9", size = 1463409 }, + { url = "https://files.pythonhosted.org/packages/f2/08/92e28867c66f0d9638bb131feca739057efc48dbcd391fd7f0a55507e470/watchfiles-0.21.0-cp310-none-win32.whl", hash = "sha256:214cee7f9e09150d4fb42e24919a1e74d8c9b8a9306ed1474ecaddcd5479c293", size = 268101 }, + { url = "https://files.pythonhosted.org/packages/4b/ea/80527adf1ad51488a96fc201715730af5879f4dfeccb5e2069ff82d890d4/watchfiles-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:1ad7247d79f9f55bb25ab1778fd47f32d70cf36053941f07de0b7c4e96b5d235", size = 279675 }, + { url = "https://files.pythonhosted.org/packages/57/b9/2667286003dd305b81d3a3aa824d3dfc63dacbf2a96faae09e72d953c430/watchfiles-0.21.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:668c265d90de8ae914f860d3eeb164534ba2e836811f91fecc7050416ee70aa7", size = 428210 }, + { url = "https://files.pythonhosted.org/packages/a3/87/6793ac60d2e20c9c1883aec7431c2e7b501ee44a839f6da1b747c13baa23/watchfiles-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a23092a992e61c3a6a70f350a56db7197242f3490da9c87b500f389b2d01eef", size = 418196 }, + { url = "https://files.pythonhosted.org/packages/5d/12/e1d1d220c5b99196eea38c9a878964f30a2b55ec9d72fd713191725b35e8/watchfiles-0.21.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e7941bbcfdded9c26b0bf720cb7e6fd803d95a55d2c14b4bd1f6a2772230c586", size = 1380287 }, + { url = "https://files.pythonhosted.org/packages/0e/cf/126f0a8683f326d190c3539a769e45e747a80a5fcbf797de82e738c946ae/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11cd0c3100e2233e9c53106265da31d574355c288e15259c0d40a4405cbae317", size = 1349653 }, + { url = "https://files.pythonhosted.org/packages/20/6e/6cffd795ec65dbc82f15d95b73d3042c1ddaffc4dd25f6c8240bfcf0640f/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78f30cbe8b2ce770160d3c08cff01b2ae9306fe66ce899b73f0409dc1846c1b", size = 1348844 }, + { url = "https://files.pythonhosted.org/packages/d5/2a/f9633279d8937ad84c532997405dd106fa6100e8d2b83e364f1c87561f96/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6674b00b9756b0af620aa2a3346b01f8e2a3dc729d25617e1b89cf6af4a54eb1", size = 1464343 }, + { url = "https://files.pythonhosted.org/packages/d7/49/9b2199bbf3c89e7c8dd795fced9dac29f201be8a28a5df0c8ff625737df6/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd7ac678b92b29ba630d8c842d8ad6c555abda1b9ef044d6cc092dacbfc9719d", size = 1542858 }, + { url = "https://files.pythonhosted.org/packages/35/e0/e8a9c1fe30e98c5b3507ad381abc4d9ee2c3b9c0ae62ffe9c164a5838186/watchfiles-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c873345680c1b87f1e09e0eaf8cf6c891b9851d8b4d3645e7efe2ec20a20cc7", size = 1347464 }, + { url = "https://files.pythonhosted.org/packages/ba/66/873739dd7defdfaee4b880114de9463fae18ba13ae2ddd784806b0ee33b6/watchfiles-0.21.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49f56e6ecc2503e7dbe233fa328b2be1a7797d31548e7a193237dcdf1ad0eee0", size = 1464343 }, + { url = "https://files.pythonhosted.org/packages/bd/51/d7539aa258d8f0e5d7b870af8b9b8964b4f88a1e4517eeb8a2efb838e9b3/watchfiles-0.21.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:02d91cbac553a3ad141db016e3350b03184deaafeba09b9d6439826ee594b365", size = 1463338 }, + { url = "https://files.pythonhosted.org/packages/ee/92/219c539a2a93b6870fa7b84eace946983126b20a7e15c6c034d8d0472682/watchfiles-0.21.0-cp311-none-win32.whl", hash = "sha256:ebe684d7d26239e23d102a2bad2a358dedf18e462e8808778703427d1f584400", size = 267658 }, + { url = "https://files.pythonhosted.org/packages/f3/dc/2a8a447b783f5059c4bf7a6bad8fe59375a5a9ce872774763b25c21c2860/watchfiles-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:4566006aa44cb0d21b8ab53baf4b9c667a0ed23efe4aaad8c227bfba0bf15cbe", size = 280113 }, + { url = "https://files.pythonhosted.org/packages/22/15/e4085181cf0210a6ec6eb29fee0c6088de867ee33d81555076a4a2726e8b/watchfiles-0.21.0-cp311-none-win_arm64.whl", hash = "sha256:c550a56bf209a3d987d5a975cdf2063b3389a5d16caf29db4bdddeae49f22078", size = 268688 }, + { url = "https://files.pythonhosted.org/packages/a1/fd/2f009eb17809afd32a143b442856628585c9ce3a9c6d5c1841e44e35a16c/watchfiles-0.21.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:51ddac60b96a42c15d24fbdc7a4bfcd02b5a29c047b7f8bf63d3f6f5a860949a", size = 426902 }, + { url = "https://files.pythonhosted.org/packages/e0/62/a2605f212a136e06f2d056ee7491ede9935ba0f1d5ceafd1f7da2a0c8625/watchfiles-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:511f0b034120cd1989932bf1e9081aa9fb00f1f949fbd2d9cab6264916ae89b1", size = 417300 }, + { url = "https://files.pythonhosted.org/packages/69/0e/29f158fa22eb2cc1f188b5ec20fb5c0a64eb801e3901ad5b7ad546cbaed0/watchfiles-0.21.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cfb92d49dbb95ec7a07511bc9efb0faff8fe24ef3805662b8d6808ba8409a71a", size = 1378126 }, + { url = "https://files.pythonhosted.org/packages/e8/f3/c67865cb5a174201c52d34e870cc7956b8408ee83ce9a02909d6a2a93a14/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f92944efc564867bbf841c823c8b71bb0be75e06b8ce45c084b46411475a915", size = 1348275 }, + { url = "https://files.pythonhosted.org/packages/d7/eb/b6f1184d1c7b9670f5bd1e184e4c221ecf25fd817cf2fcac6adc387882b5/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:642d66b75eda909fd1112d35c53816d59789a4b38c141a96d62f50a3ef9b3360", size = 1347255 }, + { url = "https://files.pythonhosted.org/packages/c8/27/e534e4b3fe739f4bf8bd5dc4c26cbc5d3baa427125d8ef78a6556acd6ff4/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d23bcd6c8eaa6324fe109d8cac01b41fe9a54b8c498af9ce464c1aeeb99903d6", size = 1462845 }, + { url = "https://files.pythonhosted.org/packages/b0/ba/a0d1c1c55f75e7e47c8f79f2314f7ec670b5177596f6d27764aecc7048cd/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18d5b4da8cf3e41895b34e8c37d13c9ed294954907929aacd95153508d5d89d7", size = 1528957 }, + { url = "https://files.pythonhosted.org/packages/1c/3a/4e38518c4dff58090c01fc8cc051fa08ac9ae00b361c855075809b0058ce/watchfiles-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b8d1eae0f65441963d805f766c7e9cd092f91e0c600c820c764a4ff71a0764c", size = 1345542 }, + { url = "https://files.pythonhosted.org/packages/9f/b7/783097f8137a710d5cd9ccbfcd92e4b453d38dab05cfcb5dbd2c587752e5/watchfiles-0.21.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1fd9a5205139f3c6bb60d11f6072e0552f0a20b712c85f43d42342d162be1235", size = 1462238 }, + { url = "https://files.pythonhosted.org/packages/6b/4c/b741eb38f2c408ae9c5a25235f6506b1dda43486ae0fdb4c462ef75bce11/watchfiles-0.21.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a1e3014a625bcf107fbf38eece0e47fa0190e52e45dc6eee5a8265ddc6dc5ea7", size = 1462406 }, + { url = "https://files.pythonhosted.org/packages/77/e4/8d2b3c67364671b0e1c0ce383895a5415f45ecb3e8586982deff4a8e85c9/watchfiles-0.21.0-cp312-none-win32.whl", hash = "sha256:9d09869f2c5a6f2d9df50ce3064b3391d3ecb6dced708ad64467b9e4f2c9bef3", size = 266789 }, + { url = "https://files.pythonhosted.org/packages/da/f2/6b1de38aeb21eb9dac1ae6a1ee4521566e79690117032036c737cfab52fa/watchfiles-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:18722b50783b5e30a18a8a5db3006bab146d2b705c92eb9a94f78c72beb94094", size = 280292 }, + { url = "https://files.pythonhosted.org/packages/5a/a5/7aba9435beb863c2490bae3173a45f42044ac7a48155d3dd42ab49cfae45/watchfiles-0.21.0-cp312-none-win_arm64.whl", hash = "sha256:a3b9bec9579a15fb3ca2d9878deae789df72f2b0fdaf90ad49ee389cad5edab6", size = 268026 }, + { url = "https://files.pythonhosted.org/packages/62/66/7463ceb43eabc6deaa795c7969ff4d4fd938de54e655035483dfd1e97c84/watchfiles-0.21.0-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab03a90b305d2588e8352168e8c5a1520b721d2d367f31e9332c4235b30b8994", size = 429092 }, + { url = "https://files.pythonhosted.org/packages/fe/a3/42686af3a089f34aba35c39abac852869661938dae7025c1a0580dfe0fbf/watchfiles-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:927c589500f9f41e370b0125c12ac9e7d3a2fd166b89e9ee2828b3dda20bfe6f", size = 419188 }, + { url = "https://files.pythonhosted.org/packages/37/17/4825999346f15d650f4c69093efa64fb040fbff4f706a20e8c4745f64070/watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bd467213195e76f838caf2c28cd65e58302d0254e636e7c0fca81efa4a2e62c", size = 1350366 }, + { url = "https://files.pythonhosted.org/packages/70/76/8d124e14cf51af4d6bba926c7473f253c6efd1539ba62577f079a2d71537/watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02b73130687bc3f6bb79d8a170959042eb56eb3a42df3671c79b428cd73f17cc", size = 1346270 }, ] [[package]] name = "wcwidth" version = "0.2.13" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301, upload-time = "2024-01-06T02:10:57.829Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166, upload-time = "2024-01-06T02:10:55.763Z" }, + { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 }, ] [[package]] name = "websocket-client" version = "1.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e6/30/fba0d96b4b5fbf5948ed3f4681f7da2f9f64512e1d303f94b4cc174c24a5/websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da", size = 54648, upload-time = "2024-04-23T22:16:16.976Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e6/30/fba0d96b4b5fbf5948ed3f4681f7da2f9f64512e1d303f94b4cc174c24a5/websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da", size = 54648 } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/84/44687a29792a70e111c5c477230a72c4b957d88d16141199bf9acb7537a3/websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526", size = 58826, upload-time = "2024-04-23T22:16:14.422Z" }, + { url = "https://files.pythonhosted.org/packages/5a/84/44687a29792a70e111c5c477230a72c4b957d88d16141199bf9acb7537a3/websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526", size = 58826 }, ] [[package]] name = "websockets" version = "12.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2e/62/7a7874b7285413c954a4cca3c11fd851f11b2fe5b4ae2d9bee4f6d9bdb10/websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b", size = 104994, upload-time = "2023-10-21T14:21:11.88Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2e/62/7a7874b7285413c954a4cca3c11fd851f11b2fe5b4ae2d9bee4f6d9bdb10/websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b", size = 104994 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/b9/360b86ded0920a93bff0db4e4b0aa31370b0208ca240b2e98d62aad8d082/websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374", size = 124025, upload-time = "2023-10-21T14:19:28.387Z" }, - { url = "https://files.pythonhosted.org/packages/bb/d3/1eca0d8fb6f0665c96f0dc7c0d0ec8aa1a425e8c003e0c18e1451f65d177/websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be", size = 121261, upload-time = "2023-10-21T14:19:30.203Z" }, - { url = "https://files.pythonhosted.org/packages/4e/e1/f6c3ecf7f1bfd9209e13949db027d7fdea2faf090c69b5f2d17d1d796d96/websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547", size = 121328, upload-time = "2023-10-21T14:19:31.765Z" }, - { url = "https://files.pythonhosted.org/packages/74/4d/f88eeceb23cb587c4aeca779e3f356cf54817af2368cb7f2bd41f93c8360/websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2", size = 130925, upload-time = "2023-10-21T14:19:33.36Z" }, - { url = "https://files.pythonhosted.org/packages/16/17/f63d9ee6ffd9afbeea021d5950d6e8db84cd4aead306c6c2ca523805699e/websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558", size = 129930, upload-time = "2023-10-21T14:19:35.109Z" }, - { url = "https://files.pythonhosted.org/packages/9a/12/c7a7504f5bf74d6ee0533f6fc7d30d8f4b79420ab179d1df2484b07602eb/websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480", size = 130245, upload-time = "2023-10-21T14:19:36.761Z" }, - { url = "https://files.pythonhosted.org/packages/e4/6a/3600c7771eb31116d2e77383d7345618b37bb93709d041e328c08e2a8eb3/websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c", size = 134966, upload-time = "2023-10-21T14:19:38.481Z" }, - { url = "https://files.pythonhosted.org/packages/22/26/df77c4b7538caebb78c9b97f43169ef742a4f445e032a5ea1aaef88f8f46/websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8", size = 134196, upload-time = "2023-10-21T14:19:40.264Z" }, - { url = "https://files.pythonhosted.org/packages/e5/18/18ce9a4a08203c8d0d3d561e3ea4f453daf32f099601fc831e60c8a9b0f2/websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603", size = 134822, upload-time = "2023-10-21T14:19:41.836Z" }, - { url = "https://files.pythonhosted.org/packages/45/51/1f823a341fc20a880e67ae62f6c38c4880a24a4b60fbe544a38f516f39a1/websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f", size = 124454, upload-time = "2023-10-21T14:19:43.639Z" }, - { url = "https://files.pythonhosted.org/packages/41/b0/5ec054cfcf23adfc88d39359b85e81d043af8a141e3ac8ce40f45a5ce5f4/websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf", size = 124974, upload-time = "2023-10-21T14:19:44.934Z" }, - { url = "https://files.pythonhosted.org/packages/02/73/9c1e168a2e7fdf26841dc98f5f5502e91dea47428da7690a08101f616169/websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4", size = 124047, upload-time = "2023-10-21T14:19:46.519Z" }, - { url = "https://files.pythonhosted.org/packages/e4/2d/9a683359ad2ed11b2303a7a94800db19c61d33fa3bde271df09e99936022/websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f", size = 121282, upload-time = "2023-10-21T14:19:47.739Z" }, - { url = "https://files.pythonhosted.org/packages/95/aa/75fa3b893142d6d98a48cb461169bd268141f2da8bfca97392d6462a02eb/websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3", size = 121325, upload-time = "2023-10-21T14:19:49.4Z" }, - { url = "https://files.pythonhosted.org/packages/6e/a4/51a25e591d645df71ee0dc3a2c880b28e5514c00ce752f98a40a87abcd1e/websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c", size = 131502, upload-time = "2023-10-21T14:19:50.683Z" }, - { url = "https://files.pythonhosted.org/packages/cd/ea/0ceeea4f5b87398fe2d9f5bcecfa00a1bcd542e2bfcac2f2e5dd612c4e9e/websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45", size = 130491, upload-time = "2023-10-21T14:19:51.835Z" }, - { url = "https://files.pythonhosted.org/packages/e3/05/f52a60b66d9faf07a4f7d71dc056bffafe36a7e98c4eb5b78f04fe6e4e85/websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04", size = 130872, upload-time = "2023-10-21T14:19:53.071Z" }, - { url = "https://files.pythonhosted.org/packages/ac/4e/c7361b2d7b964c40fea924d64881145164961fcd6c90b88b7e3ab2c4f431/websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447", size = 136318, upload-time = "2023-10-21T14:19:54.41Z" }, - { url = "https://files.pythonhosted.org/packages/0a/31/337bf35ae5faeaf364c9cddec66681cdf51dc4414ee7a20f92a18e57880f/websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca", size = 135594, upload-time = "2023-10-21T14:19:55.982Z" }, - { url = "https://files.pythonhosted.org/packages/95/aa/1ac767825c96f9d7e43c4c95683757d4ef28cf11fa47a69aca42428d3e3a/websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53", size = 136191, upload-time = "2023-10-21T14:19:57.349Z" }, - { url = "https://files.pythonhosted.org/packages/28/4b/344ec5cfeb6bc417da097f8253607c3aed11d9a305fb58346f506bf556d8/websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402", size = 124453, upload-time = "2023-10-21T14:19:59.11Z" }, - { url = "https://files.pythonhosted.org/packages/d1/40/6b169cd1957476374f51f4486a3e85003149e62a14e6b78a958c2222337a/websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b", size = 124971, upload-time = "2023-10-21T14:20:00.243Z" }, - { url = "https://files.pythonhosted.org/packages/a9/6d/23cc898647c8a614a0d9ca703695dd04322fb5135096a20c2684b7c852b6/websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df", size = 124061, upload-time = "2023-10-21T14:20:02.221Z" }, - { url = "https://files.pythonhosted.org/packages/39/34/364f30fdf1a375e4002a26ee3061138d1571dfda6421126127d379d13930/websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc", size = 121296, upload-time = "2023-10-21T14:20:03.591Z" }, - { url = "https://files.pythonhosted.org/packages/2e/00/96ae1c9dcb3bc316ef683f2febd8c97dde9f254dc36c3afc65c7645f734c/websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b", size = 121326, upload-time = "2023-10-21T14:20:04.956Z" }, - { url = "https://files.pythonhosted.org/packages/af/f1/bba1e64430685dd456c1a1fd6b0c791ae33104967b928aefeff261761e8d/websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb", size = 131807, upload-time = "2023-10-21T14:20:06.153Z" }, - { url = "https://files.pythonhosted.org/packages/62/3b/98ee269712f37d892b93852ce07b3e6d7653160ca4c0d4f8c8663f8021f8/websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92", size = 130751, upload-time = "2023-10-21T14:20:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/f1/00/d6f01ca2b191f8b0808e4132ccd2e7691f0453cbd7d0f72330eb97453c3a/websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed", size = 131176, upload-time = "2023-10-21T14:20:09.212Z" }, - { url = "https://files.pythonhosted.org/packages/af/9c/703ff3cd8109dcdee6152bae055d852ebaa7750117760ded697ab836cbcf/websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5", size = 136246, upload-time = "2023-10-21T14:20:10.423Z" }, - { url = "https://files.pythonhosted.org/packages/0b/a5/1a38fb85a456b9dc874ec984f3ff34f6550eafd17a3da28753cd3c1628e8/websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2", size = 135466, upload-time = "2023-10-21T14:20:11.826Z" }, - { url = "https://files.pythonhosted.org/packages/3c/98/1261f289dff7e65a38d59d2f591de6ed0a2580b729aebddec033c4d10881/websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113", size = 136083, upload-time = "2023-10-21T14:20:13.451Z" }, - { url = "https://files.pythonhosted.org/packages/a9/1c/f68769fba63ccb9c13fe0a25b616bd5aebeef1c7ddebc2ccc32462fb784d/websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d", size = 124460, upload-time = "2023-10-21T14:20:14.719Z" }, - { url = "https://files.pythonhosted.org/packages/20/52/8915f51f9aaef4e4361c89dd6cf69f72a0159f14e0d25026c81b6ad22525/websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f", size = 124985, upload-time = "2023-10-21T14:20:15.817Z" }, - { url = "https://files.pythonhosted.org/packages/43/8b/554a8a8bb6da9dd1ce04c44125e2192af7b7beebf6e3dbfa5d0e285cc20f/websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd", size = 121110, upload-time = "2023-10-21T14:20:48.335Z" }, - { url = "https://files.pythonhosted.org/packages/b0/8e/58b8812940d746ad74d395fb069497255cb5ef50748dfab1e8b386b1f339/websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870", size = 123216, upload-time = "2023-10-21T14:20:50.083Z" }, - { url = "https://files.pythonhosted.org/packages/81/ee/272cb67ace1786ce6d9f39d47b3c55b335e8b75dd1972a7967aad39178b6/websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077", size = 122821, upload-time = "2023-10-21T14:20:51.237Z" }, - { url = "https://files.pythonhosted.org/packages/a8/03/387fc902b397729df166763e336f4e5cec09fe7b9d60f442542c94a21be1/websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b", size = 122768, upload-time = "2023-10-21T14:20:52.59Z" }, - { url = "https://files.pythonhosted.org/packages/50/f0/5939fbc9bc1979d79a774ce5b7c4b33c0cefe99af22fb70f7462d0919640/websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30", size = 125009, upload-time = "2023-10-21T14:20:54.419Z" }, - { url = "https://files.pythonhosted.org/packages/79/4d/9cc401e7b07e80532ebc8c8e993f42541534da9e9249c59ee0139dcb0352/websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e", size = 118370, upload-time = "2023-10-21T14:21:10.075Z" }, + { url = "https://files.pythonhosted.org/packages/b1/b9/360b86ded0920a93bff0db4e4b0aa31370b0208ca240b2e98d62aad8d082/websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374", size = 124025 }, + { url = "https://files.pythonhosted.org/packages/bb/d3/1eca0d8fb6f0665c96f0dc7c0d0ec8aa1a425e8c003e0c18e1451f65d177/websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be", size = 121261 }, + { url = "https://files.pythonhosted.org/packages/4e/e1/f6c3ecf7f1bfd9209e13949db027d7fdea2faf090c69b5f2d17d1d796d96/websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547", size = 121328 }, + { url = "https://files.pythonhosted.org/packages/74/4d/f88eeceb23cb587c4aeca779e3f356cf54817af2368cb7f2bd41f93c8360/websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2", size = 130925 }, + { url = "https://files.pythonhosted.org/packages/16/17/f63d9ee6ffd9afbeea021d5950d6e8db84cd4aead306c6c2ca523805699e/websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558", size = 129930 }, + { url = "https://files.pythonhosted.org/packages/9a/12/c7a7504f5bf74d6ee0533f6fc7d30d8f4b79420ab179d1df2484b07602eb/websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480", size = 130245 }, + { url = "https://files.pythonhosted.org/packages/e4/6a/3600c7771eb31116d2e77383d7345618b37bb93709d041e328c08e2a8eb3/websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c", size = 134966 }, + { url = "https://files.pythonhosted.org/packages/22/26/df77c4b7538caebb78c9b97f43169ef742a4f445e032a5ea1aaef88f8f46/websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8", size = 134196 }, + { url = "https://files.pythonhosted.org/packages/e5/18/18ce9a4a08203c8d0d3d561e3ea4f453daf32f099601fc831e60c8a9b0f2/websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603", size = 134822 }, + { url = "https://files.pythonhosted.org/packages/45/51/1f823a341fc20a880e67ae62f6c38c4880a24a4b60fbe544a38f516f39a1/websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f", size = 124454 }, + { url = "https://files.pythonhosted.org/packages/41/b0/5ec054cfcf23adfc88d39359b85e81d043af8a141e3ac8ce40f45a5ce5f4/websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf", size = 124974 }, + { url = "https://files.pythonhosted.org/packages/02/73/9c1e168a2e7fdf26841dc98f5f5502e91dea47428da7690a08101f616169/websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4", size = 124047 }, + { url = "https://files.pythonhosted.org/packages/e4/2d/9a683359ad2ed11b2303a7a94800db19c61d33fa3bde271df09e99936022/websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f", size = 121282 }, + { url = "https://files.pythonhosted.org/packages/95/aa/75fa3b893142d6d98a48cb461169bd268141f2da8bfca97392d6462a02eb/websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3", size = 121325 }, + { url = "https://files.pythonhosted.org/packages/6e/a4/51a25e591d645df71ee0dc3a2c880b28e5514c00ce752f98a40a87abcd1e/websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c", size = 131502 }, + { url = "https://files.pythonhosted.org/packages/cd/ea/0ceeea4f5b87398fe2d9f5bcecfa00a1bcd542e2bfcac2f2e5dd612c4e9e/websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45", size = 130491 }, + { url = "https://files.pythonhosted.org/packages/e3/05/f52a60b66d9faf07a4f7d71dc056bffafe36a7e98c4eb5b78f04fe6e4e85/websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04", size = 130872 }, + { url = "https://files.pythonhosted.org/packages/ac/4e/c7361b2d7b964c40fea924d64881145164961fcd6c90b88b7e3ab2c4f431/websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447", size = 136318 }, + { url = "https://files.pythonhosted.org/packages/0a/31/337bf35ae5faeaf364c9cddec66681cdf51dc4414ee7a20f92a18e57880f/websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca", size = 135594 }, + { url = "https://files.pythonhosted.org/packages/95/aa/1ac767825c96f9d7e43c4c95683757d4ef28cf11fa47a69aca42428d3e3a/websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53", size = 136191 }, + { url = "https://files.pythonhosted.org/packages/28/4b/344ec5cfeb6bc417da097f8253607c3aed11d9a305fb58346f506bf556d8/websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402", size = 124453 }, + { url = "https://files.pythonhosted.org/packages/d1/40/6b169cd1957476374f51f4486a3e85003149e62a14e6b78a958c2222337a/websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b", size = 124971 }, + { url = "https://files.pythonhosted.org/packages/a9/6d/23cc898647c8a614a0d9ca703695dd04322fb5135096a20c2684b7c852b6/websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df", size = 124061 }, + { url = "https://files.pythonhosted.org/packages/39/34/364f30fdf1a375e4002a26ee3061138d1571dfda6421126127d379d13930/websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc", size = 121296 }, + { url = "https://files.pythonhosted.org/packages/2e/00/96ae1c9dcb3bc316ef683f2febd8c97dde9f254dc36c3afc65c7645f734c/websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b", size = 121326 }, + { url = "https://files.pythonhosted.org/packages/af/f1/bba1e64430685dd456c1a1fd6b0c791ae33104967b928aefeff261761e8d/websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb", size = 131807 }, + { url = "https://files.pythonhosted.org/packages/62/3b/98ee269712f37d892b93852ce07b3e6d7653160ca4c0d4f8c8663f8021f8/websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92", size = 130751 }, + { url = "https://files.pythonhosted.org/packages/f1/00/d6f01ca2b191f8b0808e4132ccd2e7691f0453cbd7d0f72330eb97453c3a/websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed", size = 131176 }, + { url = "https://files.pythonhosted.org/packages/af/9c/703ff3cd8109dcdee6152bae055d852ebaa7750117760ded697ab836cbcf/websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5", size = 136246 }, + { url = "https://files.pythonhosted.org/packages/0b/a5/1a38fb85a456b9dc874ec984f3ff34f6550eafd17a3da28753cd3c1628e8/websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2", size = 135466 }, + { url = "https://files.pythonhosted.org/packages/3c/98/1261f289dff7e65a38d59d2f591de6ed0a2580b729aebddec033c4d10881/websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113", size = 136083 }, + { url = "https://files.pythonhosted.org/packages/a9/1c/f68769fba63ccb9c13fe0a25b616bd5aebeef1c7ddebc2ccc32462fb784d/websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d", size = 124460 }, + { url = "https://files.pythonhosted.org/packages/20/52/8915f51f9aaef4e4361c89dd6cf69f72a0159f14e0d25026c81b6ad22525/websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f", size = 124985 }, + { url = "https://files.pythonhosted.org/packages/43/8b/554a8a8bb6da9dd1ce04c44125e2192af7b7beebf6e3dbfa5d0e285cc20f/websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd", size = 121110 }, + { url = "https://files.pythonhosted.org/packages/b0/8e/58b8812940d746ad74d395fb069497255cb5ef50748dfab1e8b386b1f339/websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870", size = 123216 }, + { url = "https://files.pythonhosted.org/packages/81/ee/272cb67ace1786ce6d9f39d47b3c55b335e8b75dd1972a7967aad39178b6/websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077", size = 122821 }, + { url = "https://files.pythonhosted.org/packages/a8/03/387fc902b397729df166763e336f4e5cec09fe7b9d60f442542c94a21be1/websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b", size = 122768 }, + { url = "https://files.pythonhosted.org/packages/50/f0/5939fbc9bc1979d79a774ce5b7c4b33c0cefe99af22fb70f7462d0919640/websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30", size = 125009 }, + { url = "https://files.pythonhosted.org/packages/79/4d/9cc401e7b07e80532ebc8c8e993f42541534da9e9249c59ee0139dcb0352/websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e", size = 118370 }, ] [[package]] @@ -2802,9 +3171,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/02/51/2e0fc149e7a810d300422ab543f87f2bcf64d985eb6f1228c4efd6e4f8d4/werkzeug-3.0.3.tar.gz", hash = "sha256:097e5bfda9f0aba8da6b8545146def481d06aa7d3266e7448e2cccf67dd8bd18", size = 803342, upload-time = "2024-05-05T23:10:31.999Z" } +sdist = { url = "https://files.pythonhosted.org/packages/02/51/2e0fc149e7a810d300422ab543f87f2bcf64d985eb6f1228c4efd6e4f8d4/werkzeug-3.0.3.tar.gz", hash = "sha256:097e5bfda9f0aba8da6b8545146def481d06aa7d3266e7448e2cccf67dd8bd18", size = 803342 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/6e/e792999e816d19d7fcbfa94c730936750036d65656a76a5a688b57a656c4/werkzeug-3.0.3-py3-none-any.whl", hash = "sha256:fc9645dc43e03e4d630d23143a04a7f947a9a3b5727cd535fdfe155a17cc48c8", size = 227274, upload-time = "2024-05-05T23:10:29.567Z" }, + { url = "https://files.pythonhosted.org/packages/9d/6e/e792999e816d19d7fcbfa94c730936750036d65656a76a5a688b57a656c4/werkzeug-3.0.3-py3-none-any.whl", hash = "sha256:fc9645dc43e03e4d630d23143a04a7f947a9a3b5727cd535fdfe155a17cc48c8", size = 227274 }, ] [[package]] @@ -2814,9 +3183,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c9/4a/44d3c295350d776427904d73c189e10aeae66d7f555bb2feee16d1e4ba5a/wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065", size = 53425, upload-time = "2022-08-23T19:58:21.447Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/4a/44d3c295350d776427904d73c189e10aeae66d7f555bb2feee16d1e4ba5a/wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065", size = 53425 } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/58/e860788190eba3bcce367f74d29c4675466ce8dddfba85f7827588416f01/wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736", size = 24226, upload-time = "2022-08-23T19:58:19.96Z" }, + { url = "https://files.pythonhosted.org/packages/78/58/e860788190eba3bcce367f74d29c4675466ce8dddfba85f7827588416f01/wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736", size = 24226 }, ] [[package]] @@ -2826,9 +3195,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "setuptools" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/46/c2/427f1867bb96555d1d34342f1dd97f8c420966ab564d58d18469a1db8736/zope.event-5.0.tar.gz", hash = "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd", size = 17350, upload-time = "2023-06-23T06:28:35.709Z" } +sdist = { url = "https://files.pythonhosted.org/packages/46/c2/427f1867bb96555d1d34342f1dd97f8c420966ab564d58d18469a1db8736/zope.event-5.0.tar.gz", hash = "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd", size = 17350 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/42/f8dbc2b9ad59e927940325a22d6d3931d630c3644dae7e2369ef5d9ba230/zope.event-5.0-py3-none-any.whl", hash = "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26", size = 6824, upload-time = "2023-06-23T06:28:32.652Z" }, + { url = "https://files.pythonhosted.org/packages/fe/42/f8dbc2b9ad59e927940325a22d6d3931d630c3644dae7e2369ef5d9ba230/zope.event-5.0-py3-none-any.whl", hash = "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26", size = 6824 }, ] [[package]] @@ -2838,24 +3207,24 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "setuptools" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/87/03/6b85c1df2dca1b9acca38b423d1e226d8ffdf30ebd78bcb398c511de8b54/zope.interface-6.1.tar.gz", hash = "sha256:2fdc7ccbd6eb6b7df5353012fbed6c3c5d04ceaca0038f75e601060e95345309", size = 293914, upload-time = "2023-10-05T11:24:38.943Z" } +sdist = { url = "https://files.pythonhosted.org/packages/87/03/6b85c1df2dca1b9acca38b423d1e226d8ffdf30ebd78bcb398c511de8b54/zope.interface-6.1.tar.gz", hash = "sha256:2fdc7ccbd6eb6b7df5353012fbed6c3c5d04ceaca0038f75e601060e95345309", size = 293914 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/ec/c1e7ce928dc10bfe02c6da7e964342d941aaf168f96f8084636167ea50d2/zope.interface-6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:43b576c34ef0c1f5a4981163b551a8781896f2a37f71b8655fd20b5af0386abb", size = 202417, upload-time = "2023-10-05T11:24:25.141Z" }, - { url = "https://files.pythonhosted.org/packages/f7/0b/12f269ad049fc40a7a3ab85445d7855b6bc6f1e774c5ca9dd6f5c32becb3/zope.interface-6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:67be3ca75012c6e9b109860820a8b6c9a84bfb036fbd1076246b98e56951ca92", size = 202528, upload-time = "2023-10-05T11:24:27.336Z" }, - { url = "https://files.pythonhosted.org/packages/7f/85/3a35144509eb4a5a2208b48ae8d116a969d67de62cc6513d85602144d9cd/zope.interface-6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b9bc671626281f6045ad61d93a60f52fd5e8209b1610972cf0ef1bbe6d808e3", size = 247532, upload-time = "2023-10-05T11:49:20.587Z" }, - { url = "https://files.pythonhosted.org/packages/50/d6/6176aaa1f6588378f5a5a4a9c6ad50a36824e902b2f844ca8de7f1b0c4a7/zope.interface-6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbe81def9cf3e46f16ce01d9bfd8bea595e06505e51b7baf45115c77352675fd", size = 241703, upload-time = "2023-10-05T11:25:33.542Z" }, - { url = "https://files.pythonhosted.org/packages/4f/20/94d4f221989b4bbdd09004b2afb329958e776b7015b7ea8bc915327e195a/zope.interface-6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dc998f6de015723196a904045e5a2217f3590b62ea31990672e31fbc5370b41", size = 247078, upload-time = "2023-10-05T11:25:48.235Z" }, - { url = "https://files.pythonhosted.org/packages/97/7e/b790b4ab9605010816a91df26a715f163e228d60eb36c947c3118fb65190/zope.interface-6.1-cp310-cp310-win_amd64.whl", hash = "sha256:239a4a08525c080ff833560171d23b249f7f4d17fcbf9316ef4159f44997616f", size = 204155, upload-time = "2023-10-05T11:37:56.715Z" }, - { url = "https://files.pythonhosted.org/packages/4a/0b/1d8817b8a3631384a26ff7faa4c1f3e6726f7e4950c3442721cfef2c95eb/zope.interface-6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9ffdaa5290422ac0f1688cb8adb1b94ca56cee3ad11f29f2ae301df8aecba7d1", size = 202441, upload-time = "2023-10-05T11:24:20.414Z" }, - { url = "https://files.pythonhosted.org/packages/3e/1f/43557bb2b6e8537002a5a26af9b899171e26ddfcdf17a00ff729b00c036b/zope.interface-6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34c15ca9248f2e095ef2e93af2d633358c5f048c49fbfddf5fdfc47d5e263736", size = 202530, upload-time = "2023-10-05T11:24:22.975Z" }, - { url = "https://files.pythonhosted.org/packages/37/a1/5d2b265f4b7371630cad5873d0873965e35ca3de993d11b9336c720f7259/zope.interface-6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b012d023b4fb59183909b45d7f97fb493ef7a46d2838a5e716e3155081894605", size = 249584, upload-time = "2023-10-05T11:49:22.978Z" }, - { url = "https://files.pythonhosted.org/packages/8b/6d/547bfa7465e5b296adba0aff5c7ace1150f2a9e429fbf6c33d6618275162/zope.interface-6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:97806e9ca3651588c1baaebb8d0c5ee3db95430b612db354c199b57378312ee8", size = 243737, upload-time = "2023-10-05T11:25:35.439Z" }, - { url = "https://files.pythonhosted.org/packages/db/5f/46946b588c43eb28efe0e46f4cf455b1ed8b2d1ea62a21b0001c6610662f/zope.interface-6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fddbab55a2473f1d3b8833ec6b7ac31e8211b0aa608df5ab09ce07f3727326de", size = 249104, upload-time = "2023-10-05T11:25:51.355Z" }, - { url = "https://files.pythonhosted.org/packages/6c/9c/9d3c0e7e5362ea59da3c42b3b2b9fc073db433a0fe3bc6cae0809ccec395/zope.interface-6.1-cp311-cp311-win_amd64.whl", hash = "sha256:a0da79117952a9a41253696ed3e8b560a425197d4e41634a23b1507efe3273f1", size = 204155, upload-time = "2023-10-05T11:39:39.139Z" }, - { url = "https://files.pythonhosted.org/packages/3c/91/68a0bbc97c2554f87d39572091954e94d043bcd83897cd6a779ca85cb5cc/zope.interface-6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8bb9c990ca9027b4214fa543fd4025818dc95f8b7abce79d61dc8a2112b561a", size = 202757, upload-time = "2023-10-05T11:25:05.865Z" }, - { url = "https://files.pythonhosted.org/packages/e1/84/850092a8ab7e87a3ea615daf3f822f7196c52592e3e92f264621b4cfe5a2/zope.interface-6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b51b64432eed4c0744241e9ce5c70dcfecac866dff720e746d0a9c82f371dfa7", size = 202654, upload-time = "2023-10-05T11:25:08.347Z" }, - { url = "https://files.pythonhosted.org/packages/57/23/508f7f79619ae4e025f5b264a9283efc3c805ed4c0ad75cb28c091179ced/zope.interface-6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa6fd016e9644406d0a61313e50348c706e911dca29736a3266fc9e28ec4ca6d", size = 254400, upload-time = "2023-10-05T11:49:25.326Z" }, - { url = "https://files.pythonhosted.org/packages/7c/0d/db0ccf0d12767015f23b302aebe98d5eca218aaadc70c2e3908b85fecd2a/zope.interface-6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c8cf55261e15590065039696607f6c9c1aeda700ceee40c70478552d323b3ff", size = 248853, upload-time = "2023-10-05T11:25:37.37Z" }, - { url = "https://files.pythonhosted.org/packages/fd/4f/8e80173ebcdefe0ff4164444c22b171cf8bd72533026befc2adf079f3ac8/zope.interface-6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e30506bcb03de8983f78884807e4fd95d8db6e65b69257eea05d13d519b83ac0", size = 255127, upload-time = "2023-10-05T11:25:53.819Z" }, - { url = "https://files.pythonhosted.org/packages/0f/d5/81f9789311d9773a02ed048af7452fc6cedce059748dba956c1dc040340a/zope.interface-6.1-cp312-cp312-win_amd64.whl", hash = "sha256:e33e86fd65f369f10608b08729c8f1c92ec7e0e485964670b4d2633a4812d36b", size = 204268, upload-time = "2023-10-05T11:41:22.778Z" }, + { url = "https://files.pythonhosted.org/packages/3c/ec/c1e7ce928dc10bfe02c6da7e964342d941aaf168f96f8084636167ea50d2/zope.interface-6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:43b576c34ef0c1f5a4981163b551a8781896f2a37f71b8655fd20b5af0386abb", size = 202417 }, + { url = "https://files.pythonhosted.org/packages/f7/0b/12f269ad049fc40a7a3ab85445d7855b6bc6f1e774c5ca9dd6f5c32becb3/zope.interface-6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:67be3ca75012c6e9b109860820a8b6c9a84bfb036fbd1076246b98e56951ca92", size = 202528 }, + { url = "https://files.pythonhosted.org/packages/7f/85/3a35144509eb4a5a2208b48ae8d116a969d67de62cc6513d85602144d9cd/zope.interface-6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b9bc671626281f6045ad61d93a60f52fd5e8209b1610972cf0ef1bbe6d808e3", size = 247532 }, + { url = "https://files.pythonhosted.org/packages/50/d6/6176aaa1f6588378f5a5a4a9c6ad50a36824e902b2f844ca8de7f1b0c4a7/zope.interface-6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbe81def9cf3e46f16ce01d9bfd8bea595e06505e51b7baf45115c77352675fd", size = 241703 }, + { url = "https://files.pythonhosted.org/packages/4f/20/94d4f221989b4bbdd09004b2afb329958e776b7015b7ea8bc915327e195a/zope.interface-6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dc998f6de015723196a904045e5a2217f3590b62ea31990672e31fbc5370b41", size = 247078 }, + { url = "https://files.pythonhosted.org/packages/97/7e/b790b4ab9605010816a91df26a715f163e228d60eb36c947c3118fb65190/zope.interface-6.1-cp310-cp310-win_amd64.whl", hash = "sha256:239a4a08525c080ff833560171d23b249f7f4d17fcbf9316ef4159f44997616f", size = 204155 }, + { url = "https://files.pythonhosted.org/packages/4a/0b/1d8817b8a3631384a26ff7faa4c1f3e6726f7e4950c3442721cfef2c95eb/zope.interface-6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9ffdaa5290422ac0f1688cb8adb1b94ca56cee3ad11f29f2ae301df8aecba7d1", size = 202441 }, + { url = "https://files.pythonhosted.org/packages/3e/1f/43557bb2b6e8537002a5a26af9b899171e26ddfcdf17a00ff729b00c036b/zope.interface-6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34c15ca9248f2e095ef2e93af2d633358c5f048c49fbfddf5fdfc47d5e263736", size = 202530 }, + { url = "https://files.pythonhosted.org/packages/37/a1/5d2b265f4b7371630cad5873d0873965e35ca3de993d11b9336c720f7259/zope.interface-6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b012d023b4fb59183909b45d7f97fb493ef7a46d2838a5e716e3155081894605", size = 249584 }, + { url = "https://files.pythonhosted.org/packages/8b/6d/547bfa7465e5b296adba0aff5c7ace1150f2a9e429fbf6c33d6618275162/zope.interface-6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:97806e9ca3651588c1baaebb8d0c5ee3db95430b612db354c199b57378312ee8", size = 243737 }, + { url = "https://files.pythonhosted.org/packages/db/5f/46946b588c43eb28efe0e46f4cf455b1ed8b2d1ea62a21b0001c6610662f/zope.interface-6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fddbab55a2473f1d3b8833ec6b7ac31e8211b0aa608df5ab09ce07f3727326de", size = 249104 }, + { url = "https://files.pythonhosted.org/packages/6c/9c/9d3c0e7e5362ea59da3c42b3b2b9fc073db433a0fe3bc6cae0809ccec395/zope.interface-6.1-cp311-cp311-win_amd64.whl", hash = "sha256:a0da79117952a9a41253696ed3e8b560a425197d4e41634a23b1507efe3273f1", size = 204155 }, + { url = "https://files.pythonhosted.org/packages/3c/91/68a0bbc97c2554f87d39572091954e94d043bcd83897cd6a779ca85cb5cc/zope.interface-6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8bb9c990ca9027b4214fa543fd4025818dc95f8b7abce79d61dc8a2112b561a", size = 202757 }, + { url = "https://files.pythonhosted.org/packages/e1/84/850092a8ab7e87a3ea615daf3f822f7196c52592e3e92f264621b4cfe5a2/zope.interface-6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b51b64432eed4c0744241e9ce5c70dcfecac866dff720e746d0a9c82f371dfa7", size = 202654 }, + { url = "https://files.pythonhosted.org/packages/57/23/508f7f79619ae4e025f5b264a9283efc3c805ed4c0ad75cb28c091179ced/zope.interface-6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa6fd016e9644406d0a61313e50348c706e911dca29736a3266fc9e28ec4ca6d", size = 254400 }, + { url = "https://files.pythonhosted.org/packages/7c/0d/db0ccf0d12767015f23b302aebe98d5eca218aaadc70c2e3908b85fecd2a/zope.interface-6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c8cf55261e15590065039696607f6c9c1aeda700ceee40c70478552d323b3ff", size = 248853 }, + { url = "https://files.pythonhosted.org/packages/fd/4f/8e80173ebcdefe0ff4164444c22b171cf8bd72533026befc2adf079f3ac8/zope.interface-6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e30506bcb03de8983f78884807e4fd95d8db6e65b69257eea05d13d519b83ac0", size = 255127 }, + { url = "https://files.pythonhosted.org/packages/0f/d5/81f9789311d9773a02ed048af7452fc6cedce059748dba956c1dc040340a/zope.interface-6.1-cp312-cp312-win_amd64.whl", hash = "sha256:e33e86fd65f369f10608b08729c8f1c92ec7e0e485964670b4d2633a4812d36b", size = 204268 }, ] From 244d097d01313e37cd915603f1fad324c71d65a6 Mon Sep 17 00:00:00 2001 From: waclaw66 Date: Tue, 12 Aug 2025 16:55:47 +0200 Subject: [PATCH 232/748] fix(mobile): enable person age pluralization (#20881) Enable person age pluralization --- i18n/en.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/i18n/en.json b/i18n/en.json index 2554bb5783..116dfaeb4a 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1457,9 +1457,9 @@ "permission_onboarding_permission_limited": "Permission limited. To let Immich backup and manage your entire gallery collection, grant photo and video permissions in Settings.", "permission_onboarding_request": "Immich requires permission to view your photos and videos.", "person": "Person", - "person_age_months": "{months} months old", - "person_age_year_months": "1 year, {months} months old", - "person_age_years": "{years} years old", + "person_age_months": "{months, plural, one {# month} other {# months}} old", + "person_age_year_months": "1 year, {months, plural, one {# month} other {# months}} old", + "person_age_years": "{years, plural, other {# years}} old", "person_birthdate": "Born on {date}", "person_hidden": "{name}{hidden, select, true { (hidden)} other {}}", "photo_shared_all_users": "Looks like you shared your photos with all users or you don't have any user to share with.", From 54960157c0268041b3792003328bbb175e4e1930 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 12 Aug 2025 11:08:31 -0500 Subject: [PATCH 233/748] chore: backup info card styling tweak (#20799) * chore: backup info card styling tweak * pr feedback --- .../onboarding-page/onboarding-backup.svelte | 87 ++++++++++--------- .../onboarding-page/onboarding-card.svelte | 2 +- web/src/routes/auth/onboarding/+page.svelte | 6 +- 3 files changed, 51 insertions(+), 44 deletions(-) diff --git a/web/src/lib/components/onboarding-page/onboarding-backup.svelte b/web/src/lib/components/onboarding-page/onboarding-backup.svelte index 1ae176e1ad..2a2877fa8d 100644 --- a/web/src/lib/components/onboarding-page/onboarding-backup.svelte +++ b/web/src/lib/components/onboarding-page/onboarding-backup.svelte @@ -1,16 +1,22 @@ diff --git a/web/src/lib/components/onboarding-page/onboarding-card.svelte b/web/src/lib/components/onboarding-page/onboarding-card.svelte index 4a373fc310..fd87464bd5 100644 --- a/web/src/lib/components/onboarding-page/onboarding-card.svelte +++ b/web/src/lib/components/onboarding-page/onboarding-card.svelte @@ -31,7 +31,7 @@
    {#if title || icon} diff --git a/web/src/routes/auth/onboarding/+page.svelte b/web/src/routes/auth/onboarding/+page.svelte index 679920c971..44f3792a5b 100644 --- a/web/src/routes/auth/onboarding/+page.svelte +++ b/web/src/routes/auth/onboarding/+page.svelte @@ -1,6 +1,7 @@ + + {#snippet buttons()} @@ -203,13 +260,61 @@ />
    - {#key duplicates[0].duplicateId} + {#key duplicates[duplicatesIndex].duplicateId} - handleResolve(duplicates[0].duplicateId, duplicateAssetIds, trashIds)} - onStack={(assets) => handleStack(duplicates[0].duplicateId, assets)} + handleResolve(duplicates[duplicatesIndex].duplicateId, duplicateAssetIds, trashIds)} + onStack={(assets) => handleStack(duplicates[duplicatesIndex].duplicateId, assets)} /> +
    +
    +
    + + +
    +
    + + +
    +
    +
    {/key} {:else}

    From e00556a34afe71bd163529e49aaa821bf43125e4 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Mon, 18 Aug 2025 19:15:03 -0400 Subject: [PATCH 253/748] feat: get metadata about the current api key (#21027) --- mobile/openapi/README.md | 1 + mobile/openapi/lib/api/api_keys_api.dart | 41 +++++++++++++++++++ open-api/immich-openapi-specs.json | 32 +++++++++++++++ open-api/typescript-sdk/src/fetch-client.ts | 8 ++++ .../controllers/api-key.controller.spec.ts | 13 ++++-- server/src/controllers/api-key.controller.ts | 8 +++- server/src/controllers/index.ts | 4 +- server/src/middleware/auth.guard.ts | 4 +- server/src/services/api-key.service.spec.ts | 37 ++++++++++++++++- server/src/services/api-key.service.ts | 15 ++++++- server/src/services/auth.service.spec.ts | 14 +++++++ server/src/services/auth.service.ts | 9 +++- 12 files changed, 174 insertions(+), 12 deletions(-) diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 0bee70d193..f0673e70b9 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -77,6 +77,7 @@ Class | Method | HTTP request | Description *APIKeysApi* | [**deleteApiKey**](doc//APIKeysApi.md#deleteapikey) | **DELETE** /api-keys/{id} | *APIKeysApi* | [**getApiKey**](doc//APIKeysApi.md#getapikey) | **GET** /api-keys/{id} | *APIKeysApi* | [**getApiKeys**](doc//APIKeysApi.md#getapikeys) | **GET** /api-keys | +*APIKeysApi* | [**getMyApiKey**](doc//APIKeysApi.md#getmyapikey) | **GET** /api-keys/me | *APIKeysApi* | [**updateApiKey**](doc//APIKeysApi.md#updateapikey) | **PUT** /api-keys/{id} | *ActivitiesApi* | [**createActivity**](doc//ActivitiesApi.md#createactivity) | **POST** /activities | *ActivitiesApi* | [**deleteActivity**](doc//ActivitiesApi.md#deleteactivity) | **DELETE** /activities/{id} | diff --git a/mobile/openapi/lib/api/api_keys_api.dart b/mobile/openapi/lib/api/api_keys_api.dart index e86c63bc6e..3ac829c30c 100644 --- a/mobile/openapi/lib/api/api_keys_api.dart +++ b/mobile/openapi/lib/api/api_keys_api.dart @@ -213,6 +213,47 @@ class APIKeysApi { return null; } + /// Performs an HTTP 'GET /api-keys/me' operation and returns the [Response]. + Future getMyApiKeyWithHttpInfo() async { + // ignore: prefer_const_declarations + final apiPath = r'/api-keys/me'; + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'GET', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + Future getMyApiKey() async { + final response = await getMyApiKeyWithHttpInfo(); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + // When a remote server returns no body with a status of 204, we shall not decode it. + // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" + // FormatException when trying to decode an empty string. + if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { + return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'APIKeyResponseDto',) as APIKeyResponseDto; + + } + return null; + } + /// This endpoint requires the `apiKey.update` permission. /// /// Note: This method returns the HTTP [Response]. diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 32b26b0cf0..197d414921 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -1488,6 +1488,38 @@ "description": "This endpoint requires the `apiKey.create` permission." } }, + "/api-keys/me": { + "get": { + "operationId": "getMyApiKey", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIKeyResponseDto" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "API Keys" + ] + } + }, "/api-keys/{id}": { "delete": { "operationId": "deleteApiKey", diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 7a0d12f99f..f0cdbef508 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -2027,6 +2027,14 @@ export function createApiKey({ apiKeyCreateDto }: { body: apiKeyCreateDto }))); } +export function getMyApiKey(opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: ApiKeyResponseDto; + }>("/api-keys/me", { + ...opts + })); +} /** * This endpoint requires the `apiKey.delete` permission. */ diff --git a/server/src/controllers/api-key.controller.spec.ts b/server/src/controllers/api-key.controller.spec.ts index 993ad012cc..c6dab09a3c 100644 --- a/server/src/controllers/api-key.controller.spec.ts +++ b/server/src/controllers/api-key.controller.spec.ts @@ -1,16 +1,16 @@ -import { APIKeyController } from 'src/controllers/api-key.controller'; +import { ApiKeyController } from 'src/controllers/api-key.controller'; import { Permission } from 'src/enum'; import { ApiKeyService } from 'src/services/api-key.service'; import request from 'supertest'; import { factory } from 'test/small.factory'; import { ControllerContext, controllerSetup, mockBaseService } from 'test/utils'; -describe(APIKeyController.name, () => { +describe(ApiKeyController.name, () => { let ctx: ControllerContext; const service = mockBaseService(ApiKeyService); beforeAll(async () => { - ctx = await controllerSetup(APIKeyController, [{ provide: ApiKeyService, useValue: service }]); + ctx = await controllerSetup(ApiKeyController, [{ provide: ApiKeyService, useValue: service }]); return () => ctx.close(); }); @@ -33,6 +33,13 @@ describe(APIKeyController.name, () => { }); }); + describe('GET /api-keys/me', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get(`/api-keys/me`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + describe('GET /api-keys/:id', () => { it('should be an authenticated route', async () => { await request(ctx.getHttpServer()).get(`/api-keys/${factory.uuid()}`); diff --git a/server/src/controllers/api-key.controller.ts b/server/src/controllers/api-key.controller.ts index dc9e85f33a..59b6908128 100644 --- a/server/src/controllers/api-key.controller.ts +++ b/server/src/controllers/api-key.controller.ts @@ -9,7 +9,7 @@ import { UUIDParamDto } from 'src/validation'; @ApiTags('API Keys') @Controller('api-keys') -export class APIKeyController { +export class ApiKeyController { constructor(private service: ApiKeyService) {} @Post() @@ -24,6 +24,12 @@ export class APIKeyController { return this.service.getAll(auth); } + @Get('me') + @Authenticated({ permission: false }) + async getMyApiKey(@Auth() auth: AuthDto): Promise { + return this.service.getMine(auth); + } + @Get(':id') @Authenticated({ permission: Permission.ApiKeyRead }) getApiKey(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { diff --git a/server/src/controllers/index.ts b/server/src/controllers/index.ts index 137abf103c..e3661ec794 100644 --- a/server/src/controllers/index.ts +++ b/server/src/controllers/index.ts @@ -1,6 +1,6 @@ import { ActivityController } from 'src/controllers/activity.controller'; import { AlbumController } from 'src/controllers/album.controller'; -import { APIKeyController } from 'src/controllers/api-key.controller'; +import { ApiKeyController } from 'src/controllers/api-key.controller'; import { AppController } from 'src/controllers/app.controller'; import { AssetMediaController } from 'src/controllers/asset-media.controller'; import { AssetController } from 'src/controllers/asset.controller'; @@ -34,7 +34,7 @@ import { UserController } from 'src/controllers/user.controller'; import { ViewController } from 'src/controllers/view.controller'; export const controllers = [ - APIKeyController, + ApiKeyController, ActivityController, AlbumController, AppController, diff --git a/server/src/middleware/auth.guard.ts b/server/src/middleware/auth.guard.ts index 80d7a37435..8af7bf7fb3 100644 --- a/server/src/middleware/auth.guard.ts +++ b/server/src/middleware/auth.guard.ts @@ -17,7 +17,7 @@ import { UAParser } from 'ua-parser-js'; type AdminRoute = { admin?: true }; type SharedLinkRoute = { sharedLink?: true }; -type AuthenticatedOptions = { permission?: Permission } & (AdminRoute | SharedLinkRoute); +type AuthenticatedOptions = { permission?: Permission | false } & (AdminRoute | SharedLinkRoute); export const Authenticated = (options: AuthenticatedOptions = {}): MethodDecorator => { const decorators: MethodDecorator[] = [ @@ -32,7 +32,7 @@ export const Authenticated = (options: AuthenticatedOptions = {}): MethodDecorat } if (options?.permission) { - decorators.push(ApiExtension(ApiCustomExtension.Permission, options.permission ?? Permission.All)); + decorators.push(ApiExtension(ApiCustomExtension.Permission, options.permission)); } if ((options as SharedLinkRoute)?.sharedLink) { diff --git a/server/src/services/api-key.service.spec.ts b/server/src/services/api-key.service.spec.ts index fffe7bb536..8d48b47f1e 100644 --- a/server/src/services/api-key.service.spec.ts +++ b/server/src/services/api-key.service.spec.ts @@ -1,4 +1,4 @@ -import { BadRequestException } from '@nestjs/common'; +import { BadRequestException, ForbiddenException } from '@nestjs/common'; import { Permission } from 'src/enum'; import { ApiKeyService } from 'src/services/api-key.service'; import { factory, newUuid } from 'test/small.factory'; @@ -134,6 +134,41 @@ describe(ApiKeyService.name, () => { }); }); + describe('getMine', () => { + it('should not work with a session token', async () => { + const session = factory.session(); + const auth = factory.auth({ session }); + + mocks.apiKey.getById.mockResolvedValue(void 0); + + await expect(sut.getMine(auth)).rejects.toBeInstanceOf(ForbiddenException); + + expect(mocks.apiKey.getById).not.toHaveBeenCalled(); + }); + + it('should throw an error if the key is not found', async () => { + const apiKey = factory.authApiKey(); + const auth = factory.auth({ apiKey }); + + mocks.apiKey.getById.mockResolvedValue(void 0); + + await expect(sut.getMine(auth)).rejects.toBeInstanceOf(BadRequestException); + + expect(mocks.apiKey.getById).toHaveBeenCalledWith(auth.user.id, apiKey.id); + }); + + it('should get a key by id', async () => { + const auth = factory.auth(); + const apiKey = factory.apiKey({ userId: auth.user.id }); + + mocks.apiKey.getById.mockResolvedValue(apiKey); + + await sut.getById(auth, apiKey.id); + + expect(mocks.apiKey.getById).toHaveBeenCalledWith(auth.user.id, apiKey.id); + }); + }); + describe('getById', () => { it('should throw an error if the key is not found', async () => { const auth = factory.auth(); diff --git a/server/src/services/api-key.service.ts b/server/src/services/api-key.service.ts index 82d4eabdfd..96671daab1 100644 --- a/server/src/services/api-key.service.ts +++ b/server/src/services/api-key.service.ts @@ -1,4 +1,4 @@ -import { BadRequestException, Injectable } from '@nestjs/common'; +import { BadRequestException, ForbiddenException, Injectable } from '@nestjs/common'; import { ApiKey } from 'src/database'; import { APIKeyCreateDto, APIKeyCreateResponseDto, APIKeyResponseDto, APIKeyUpdateDto } from 'src/dtos/api-key.dto'; import { AuthDto } from 'src/dtos/auth.dto'; @@ -46,6 +46,19 @@ export class ApiKeyService extends BaseService { await this.apiKeyRepository.delete(auth.user.id, id); } + async getMine(auth: AuthDto): Promise { + if (!auth.apiKey) { + throw new ForbiddenException('Not authenticated with an API Key'); + } + + const key = await this.apiKeyRepository.getById(auth.user.id, auth.apiKey.id); + if (!key) { + throw new BadRequestException('API Key not found'); + } + + return this.map(key); + } + async getById(auth: AuthDto, id: string): Promise { const key = await this.apiKeyRepository.getById(auth.user.id, id); if (!key) { diff --git a/server/src/services/auth.service.spec.ts b/server/src/services/auth.service.spec.ts index a76fc13009..f4ff0ee8c8 100644 --- a/server/src/services/auth.service.spec.ts +++ b/server/src/services/auth.service.spec.ts @@ -518,6 +518,20 @@ describe(AuthService.name, () => { await expect(result).rejects.toThrow('Missing required permission: all'); }); + it('should not require any permission when metadata is set to `false`', async () => { + const authUser = factory.authUser(); + const authApiKey = factory.authApiKey({ permissions: [Permission.ActivityRead] }); + + mocks.apiKey.getKey.mockResolvedValue({ ...authApiKey, user: authUser }); + + const result = sut.authenticate({ + headers: { 'x-api-key': 'auth_token' }, + queryParams: {}, + metadata: { adminRoute: false, sharedLinkRoute: false, uri: 'test', permission: false }, + }); + await expect(result).resolves.toEqual({ user: authUser, apiKey: expect.objectContaining(authApiKey) }); + }); + it('should return an auth dto', async () => { const authUser = factory.authUser(); const authApiKey = factory.authApiKey({ permissions: [Permission.All] }); diff --git a/server/src/services/auth.service.ts b/server/src/services/auth.service.ts index 1e65ba3272..69d872e8c9 100644 --- a/server/src/services/auth.service.ts +++ b/server/src/services/auth.service.ts @@ -48,7 +48,8 @@ export type ValidateRequest = { metadata: { sharedLinkRoute: boolean; adminRoute: boolean; - permission?: Permission; + /** `false` explicitly means no permission is required, which otherwise defaults to `all` */ + permission?: Permission | false; uri: string; }; }; @@ -187,7 +188,11 @@ export class AuthService extends BaseService { throw new ForbiddenException('Forbidden'); } - if (authDto.apiKey && !isGranted({ requested: [requestedPermission], current: authDto.apiKey.permissions })) { + if ( + authDto.apiKey && + requestedPermission !== false && + !isGranted({ requested: [requestedPermission], current: authDto.apiKey.permissions }) + ) { throw new ForbiddenException(`Missing required permission: ${requestedPermission}`); } From 9ff664ed36bbf29fa4d09b969b8f6531ece4e6c9 Mon Sep 17 00:00:00 2001 From: xCJPECKOVERx Date: Mon, 18 Aug 2025 20:42:47 -0400 Subject: [PATCH 254/748] feat(web): Add to Multiple Albums (#20072) * Multi add to album picker: - update modal for multi select - Update add-to-album and add-to-album-action to work with new array return from AlbumPickerModal - Add asset-utils.addAssetsToAlbums (incomplete) * initial addToAlbums endpoint * - fix endpoint - add test * - update return type - make open-api * - simplify return dto - handle notification * - fix returns - clean up * - update i18n - format & check * - checks * - correct successId count - fix assets_cannot_be_added language call * tests * foromat * refactor * - update successful add message to included total attempted * - fix web test - format i18n * - fix open-api * - fix imports to resolve checks * - PR suggestions * open-api * refactor addAssetsToAlbums * refactor it again * - fix error returns and tests * - swap icon for IconButton - don't nest the buttons * open-api * - Cleanup multi-select button to match Thumbnail * merge and openapi * - remove onclick from icon element * - fix double onClose call with keyboard shortcuts * - spelling and formatting - apply new api permission * - open-api * chore: styling * translation --------- Co-authored-by: Alex --- i18n/en.json | 6 + mobile/openapi/README.md | 4 + mobile/openapi/lib/api.dart | 3 + mobile/openapi/lib/api/albums_api.dart | 67 ++++ mobile/openapi/lib/api_client.dart | 6 + mobile/openapi/lib/api_helper.dart | 3 + .../lib/model/albums_add_assets_dto.dart | 111 ++++++ .../model/albums_add_assets_response_dto.dart | 132 +++++++ .../lib/model/bulk_id_error_reason.dart | 91 +++++ open-api/immich-openapi-specs.json | 119 +++++++ open-api/typescript-sdk/src/fetch-client.ts | 36 ++ .../src/controllers/album.controller.spec.ts | 7 + server/src/controllers/album.controller.ts | 8 + server/src/dtos/album.dto.ts | 19 + server/src/services/album.service.spec.ts | 332 ++++++++++++++++++ server/src/services/album.service.ts | 41 ++- .../actions/add-to-album-action.svelte | 19 +- .../asset-viewer/album-list-item.svelte | 160 +++++++-- .../photos-page/actions/add-to-album.svelte | 20 +- .../album-selection-utils.spec.ts | 22 +- .../album-selection/album-selection-utils.ts | 4 + .../new-album-list-item.svelte | 6 +- web/src/lib/modals/AlbumPickerModal.svelte | 71 +++- web/src/lib/utils/asset-utils.ts | 48 +++ 24 files changed, 1280 insertions(+), 55 deletions(-) create mode 100644 mobile/openapi/lib/model/albums_add_assets_dto.dart create mode 100644 mobile/openapi/lib/model/albums_add_assets_response_dto.dart create mode 100644 mobile/openapi/lib/model/bulk_id_error_reason.dart diff --git a/i18n/en.json b/i18n/en.json index 95fddca5d5..3988cfdca7 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -28,6 +28,9 @@ "add_to_album": "Add to album", "add_to_album_bottom_sheet_added": "Added to {album}", "add_to_album_bottom_sheet_already_exists": "Already in {album}", + "add_to_album_toggle": "Toggle selection for {album}", + "add_to_albums": "Add to albums", + "add_to_albums_count": "Add to albums ({count})", "add_to_shared_album": "Add to shared album", "add_url": "Add URL", "added_to_archive": "Added to archive", @@ -497,7 +500,9 @@ "assets": "Assets", "assets_added_count": "Added {count, plural, one {# asset} other {# assets}}", "assets_added_to_album_count": "Added {count, plural, one {# asset} other {# assets}} to the album", + "assets_added_to_albums_count": "Added {assetTotal} assets to {albumTotal} albums", "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} cannot be added to the album", + "assets_cannot_be_added_to_albums": "{count, plural, one {Asset} other {Assets}} cannot be added to any of the albums", "assets_count": "{count, plural, one {# asset} other {# assets}}", "assets_deleted_permanently": "{count} asset(s) deleted permanently", "assets_deleted_permanently_from_server": "{count} asset(s) deleted permanently from the Immich server", @@ -514,6 +519,7 @@ "assets_trashed_count": "Trashed {count, plural, one {# asset} other {# assets}}", "assets_trashed_from_server": "{count} asset(s) trashed from the Immich server", "assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Assets were}} already part of the album", + "assets_were_part_of_albums_count": "{count, plural, one {Asset was} other {Assets were}} already part of the albums", "authorized_devices": "Authorized Devices", "automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere", "automatic_endpoint_switching_title": "Automatic URL switching", diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index f0673e70b9..d07f13f7a3 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -84,6 +84,7 @@ Class | Method | HTTP request | Description *ActivitiesApi* | [**getActivities**](doc//ActivitiesApi.md#getactivities) | **GET** /activities | *ActivitiesApi* | [**getActivityStatistics**](doc//ActivitiesApi.md#getactivitystatistics) | **GET** /activities/statistics | *AlbumsApi* | [**addAssetsToAlbum**](doc//AlbumsApi.md#addassetstoalbum) | **PUT** /albums/{id}/assets | +*AlbumsApi* | [**addAssetsToAlbums**](doc//AlbumsApi.md#addassetstoalbums) | **PUT** /albums/assets | *AlbumsApi* | [**addUsersToAlbum**](doc//AlbumsApi.md#adduserstoalbum) | **PUT** /albums/{id}/users | *AlbumsApi* | [**createAlbum**](doc//AlbumsApi.md#createalbum) | **POST** /albums | *AlbumsApi* | [**deleteAlbum**](doc//AlbumsApi.md#deletealbum) | **DELETE** /albums/{id} | @@ -300,6 +301,8 @@ Class | Method | HTTP request | Description - [AlbumUserCreateDto](doc//AlbumUserCreateDto.md) - [AlbumUserResponseDto](doc//AlbumUserResponseDto.md) - [AlbumUserRole](doc//AlbumUserRole.md) + - [AlbumsAddAssetsDto](doc//AlbumsAddAssetsDto.md) + - [AlbumsAddAssetsResponseDto](doc//AlbumsAddAssetsResponseDto.md) - [AlbumsResponse](doc//AlbumsResponse.md) - [AlbumsUpdate](doc//AlbumsUpdate.md) - [AllJobStatusResponseDto](doc//AllJobStatusResponseDto.md) @@ -334,6 +337,7 @@ Class | Method | HTTP request | Description - [AudioCodec](doc//AudioCodec.md) - [AuthStatusResponseDto](doc//AuthStatusResponseDto.md) - [AvatarUpdate](doc//AvatarUpdate.md) + - [BulkIdErrorReason](doc//BulkIdErrorReason.md) - [BulkIdResponseDto](doc//BulkIdResponseDto.md) - [BulkIdsDto](doc//BulkIdsDto.md) - [CLIPConfig](doc//CLIPConfig.md) diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart index 8ecb9cd5f5..f5f353c968 100644 --- a/mobile/openapi/lib/api.dart +++ b/mobile/openapi/lib/api.dart @@ -79,6 +79,8 @@ part 'model/album_user_add_dto.dart'; part 'model/album_user_create_dto.dart'; part 'model/album_user_response_dto.dart'; part 'model/album_user_role.dart'; +part 'model/albums_add_assets_dto.dart'; +part 'model/albums_add_assets_response_dto.dart'; part 'model/albums_response.dart'; part 'model/albums_update.dart'; part 'model/all_job_status_response_dto.dart'; @@ -113,6 +115,7 @@ part 'model/asset_visibility.dart'; part 'model/audio_codec.dart'; part 'model/auth_status_response_dto.dart'; part 'model/avatar_update.dart'; +part 'model/bulk_id_error_reason.dart'; part 'model/bulk_id_response_dto.dart'; part 'model/bulk_ids_dto.dart'; part 'model/clip_config.dart'; diff --git a/mobile/openapi/lib/api/albums_api.dart b/mobile/openapi/lib/api/albums_api.dart index 10674b894f..a45083669c 100644 --- a/mobile/openapi/lib/api/albums_api.dart +++ b/mobile/openapi/lib/api/albums_api.dart @@ -91,6 +91,73 @@ class AlbumsApi { return null; } + /// This endpoint requires the `albumAsset.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// + /// Parameters: + /// + /// * [AlbumsAddAssetsDto] albumsAddAssetsDto (required): + /// + /// * [String] key: + /// + /// * [String] slug: + Future addAssetsToAlbumsWithHttpInfo(AlbumsAddAssetsDto albumsAddAssetsDto, { String? key, String? slug, }) async { + // ignore: prefer_const_declarations + final apiPath = r'/albums/assets'; + + // ignore: prefer_final_locals + Object? postBody = albumsAddAssetsDto; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + if (key != null) { + queryParams.addAll(_queryParams('', 'key', key)); + } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } + + const contentTypes = ['application/json']; + + + return apiClient.invokeAPI( + apiPath, + 'PUT', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// This endpoint requires the `albumAsset.create` permission. + /// + /// Parameters: + /// + /// * [AlbumsAddAssetsDto] albumsAddAssetsDto (required): + /// + /// * [String] key: + /// + /// * [String] slug: + Future addAssetsToAlbums(AlbumsAddAssetsDto albumsAddAssetsDto, { String? key, String? slug, }) async { + final response = await addAssetsToAlbumsWithHttpInfo(albumsAddAssetsDto, key: key, slug: slug, ); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + // When a remote server returns no body with a status of 204, we shall not decode it. + // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" + // FormatException when trying to decode an empty string. + if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { + return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'AlbumsAddAssetsResponseDto',) as AlbumsAddAssetsResponseDto; + + } + return null; + } + /// This endpoint requires the `albumUser.create` permission. /// /// Note: This method returns the HTTP [Response]. diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart index bd306cb216..3f31d4ed90 100644 --- a/mobile/openapi/lib/api_client.dart +++ b/mobile/openapi/lib/api_client.dart @@ -212,6 +212,10 @@ class ApiClient { return AlbumUserResponseDto.fromJson(value); case 'AlbumUserRole': return AlbumUserRoleTypeTransformer().decode(value); + case 'AlbumsAddAssetsDto': + return AlbumsAddAssetsDto.fromJson(value); + case 'AlbumsAddAssetsResponseDto': + return AlbumsAddAssetsResponseDto.fromJson(value); case 'AlbumsResponse': return AlbumsResponse.fromJson(value); case 'AlbumsUpdate': @@ -280,6 +284,8 @@ class ApiClient { return AuthStatusResponseDto.fromJson(value); case 'AvatarUpdate': return AvatarUpdate.fromJson(value); + case 'BulkIdErrorReason': + return BulkIdErrorReasonTypeTransformer().decode(value); case 'BulkIdResponseDto': return BulkIdResponseDto.fromJson(value); case 'BulkIdsDto': diff --git a/mobile/openapi/lib/api_helper.dart b/mobile/openapi/lib/api_helper.dart index 098d32f4f4..4adb62768b 100644 --- a/mobile/openapi/lib/api_helper.dart +++ b/mobile/openapi/lib/api_helper.dart @@ -79,6 +79,9 @@ String parameterToString(dynamic value) { if (value is AudioCodec) { return AudioCodecTypeTransformer().encode(value).toString(); } + if (value is BulkIdErrorReason) { + return BulkIdErrorReasonTypeTransformer().encode(value).toString(); + } if (value is CQMode) { return CQModeTypeTransformer().encode(value).toString(); } diff --git a/mobile/openapi/lib/model/albums_add_assets_dto.dart b/mobile/openapi/lib/model/albums_add_assets_dto.dart new file mode 100644 index 0000000000..bdbf68980c --- /dev/null +++ b/mobile/openapi/lib/model/albums_add_assets_dto.dart @@ -0,0 +1,111 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class AlbumsAddAssetsDto { + /// Returns a new [AlbumsAddAssetsDto] instance. + AlbumsAddAssetsDto({ + this.albumIds = const [], + this.assetIds = const [], + }); + + List albumIds; + + List assetIds; + + @override + bool operator ==(Object other) => identical(this, other) || other is AlbumsAddAssetsDto && + _deepEquality.equals(other.albumIds, albumIds) && + _deepEquality.equals(other.assetIds, assetIds); + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (albumIds.hashCode) + + (assetIds.hashCode); + + @override + String toString() => 'AlbumsAddAssetsDto[albumIds=$albumIds, assetIds=$assetIds]'; + + Map toJson() { + final json = {}; + json[r'albumIds'] = this.albumIds; + json[r'assetIds'] = this.assetIds; + return json; + } + + /// Returns a new [AlbumsAddAssetsDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static AlbumsAddAssetsDto? fromJson(dynamic value) { + upgradeDto(value, "AlbumsAddAssetsDto"); + if (value is Map) { + final json = value.cast(); + + return AlbumsAddAssetsDto( + albumIds: json[r'albumIds'] is Iterable + ? (json[r'albumIds'] as Iterable).cast().toList(growable: false) + : const [], + assetIds: json[r'assetIds'] is Iterable + ? (json[r'assetIds'] as Iterable).cast().toList(growable: false) + : const [], + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = AlbumsAddAssetsDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = AlbumsAddAssetsDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of AlbumsAddAssetsDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = AlbumsAddAssetsDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'albumIds', + 'assetIds', + }; +} + diff --git a/mobile/openapi/lib/model/albums_add_assets_response_dto.dart b/mobile/openapi/lib/model/albums_add_assets_response_dto.dart new file mode 100644 index 0000000000..168b3f2c45 --- /dev/null +++ b/mobile/openapi/lib/model/albums_add_assets_response_dto.dart @@ -0,0 +1,132 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class AlbumsAddAssetsResponseDto { + /// Returns a new [AlbumsAddAssetsResponseDto] instance. + AlbumsAddAssetsResponseDto({ + required this.albumSuccessCount, + required this.assetSuccessCount, + this.error, + required this.success, + }); + + int albumSuccessCount; + + int assetSuccessCount; + + /// + /// Please note: This property should have been non-nullable! Since the specification file + /// does not include a default value (using the "default:" property), however, the generated + /// source code must fall back to having a nullable type. + /// Consider adding a "default:" property in the specification file to hide this note. + /// + BulkIdErrorReason? error; + + bool success; + + @override + bool operator ==(Object other) => identical(this, other) || other is AlbumsAddAssetsResponseDto && + other.albumSuccessCount == albumSuccessCount && + other.assetSuccessCount == assetSuccessCount && + other.error == error && + other.success == success; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (albumSuccessCount.hashCode) + + (assetSuccessCount.hashCode) + + (error == null ? 0 : error!.hashCode) + + (success.hashCode); + + @override + String toString() => 'AlbumsAddAssetsResponseDto[albumSuccessCount=$albumSuccessCount, assetSuccessCount=$assetSuccessCount, error=$error, success=$success]'; + + Map toJson() { + final json = {}; + json[r'albumSuccessCount'] = this.albumSuccessCount; + json[r'assetSuccessCount'] = this.assetSuccessCount; + if (this.error != null) { + json[r'error'] = this.error; + } else { + // json[r'error'] = null; + } + json[r'success'] = this.success; + return json; + } + + /// Returns a new [AlbumsAddAssetsResponseDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static AlbumsAddAssetsResponseDto? fromJson(dynamic value) { + upgradeDto(value, "AlbumsAddAssetsResponseDto"); + if (value is Map) { + final json = value.cast(); + + return AlbumsAddAssetsResponseDto( + albumSuccessCount: mapValueOfType(json, r'albumSuccessCount')!, + assetSuccessCount: mapValueOfType(json, r'assetSuccessCount')!, + error: BulkIdErrorReason.fromJson(json[r'error']), + success: mapValueOfType(json, r'success')!, + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = AlbumsAddAssetsResponseDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = AlbumsAddAssetsResponseDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of AlbumsAddAssetsResponseDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = AlbumsAddAssetsResponseDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'albumSuccessCount', + 'assetSuccessCount', + 'success', + }; +} + diff --git a/mobile/openapi/lib/model/bulk_id_error_reason.dart b/mobile/openapi/lib/model/bulk_id_error_reason.dart new file mode 100644 index 0000000000..cdaf70217e --- /dev/null +++ b/mobile/openapi/lib/model/bulk_id_error_reason.dart @@ -0,0 +1,91 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + + +class BulkIdErrorReason { + /// Instantiate a new enum with the provided [value]. + const BulkIdErrorReason._(this.value); + + /// The underlying value of this enum member. + final String value; + + @override + String toString() => value; + + String toJson() => value; + + static const duplicate = BulkIdErrorReason._(r'duplicate'); + static const noPermission = BulkIdErrorReason._(r'no_permission'); + static const notFound = BulkIdErrorReason._(r'not_found'); + static const unknown = BulkIdErrorReason._(r'unknown'); + + /// List of all possible values in this [enum][BulkIdErrorReason]. + static const values = [ + duplicate, + noPermission, + notFound, + unknown, + ]; + + static BulkIdErrorReason? fromJson(dynamic value) => BulkIdErrorReasonTypeTransformer().decode(value); + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = BulkIdErrorReason.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } +} + +/// Transformation class that can [encode] an instance of [BulkIdErrorReason] to String, +/// and [decode] dynamic data back to [BulkIdErrorReason]. +class BulkIdErrorReasonTypeTransformer { + factory BulkIdErrorReasonTypeTransformer() => _instance ??= const BulkIdErrorReasonTypeTransformer._(); + + const BulkIdErrorReasonTypeTransformer._(); + + String encode(BulkIdErrorReason data) => data.value; + + /// Decodes a [dynamic value][data] to a BulkIdErrorReason. + /// + /// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully, + /// then null is returned. However, if [allowNull] is false and the [dynamic value][data] + /// cannot be decoded successfully, then an [UnimplementedError] is thrown. + /// + /// The [allowNull] is very handy when an API changes and a new enum value is added or removed, + /// and users are still using an old app with the old code. + BulkIdErrorReason? decode(dynamic data, {bool allowNull = true}) { + if (data != null) { + switch (data) { + case r'duplicate': return BulkIdErrorReason.duplicate; + case r'no_permission': return BulkIdErrorReason.noPermission; + case r'not_found': return BulkIdErrorReason.notFound; + case r'unknown': return BulkIdErrorReason.unknown; + default: + if (!allowNull) { + throw ArgumentError('Unknown enum value to decode: $data'); + } + } + } + return null; + } + + /// Singleton [BulkIdErrorReasonTypeTransformer] instance. + static BulkIdErrorReasonTypeTransformer? _instance; +} + diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 197d414921..96c42f981e 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -940,6 +940,67 @@ "description": "This endpoint requires the `album.create` permission." } }, + "/albums/assets": { + "put": { + "operationId": "addAssetsToAlbums", + "parameters": [ + { + "name": "key", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AlbumsAddAssetsDto" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AlbumsAddAssetsResponseDto" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Albums" + ], + "x-immich-permission": "albumAsset.create", + "description": "This endpoint requires the `albumAsset.create` permission." + } + }, "/albums/statistics": { "get": { "operationId": "getAlbumStatistics", @@ -9921,6 +9982,55 @@ ], "type": "string" }, + "AlbumsAddAssetsDto": { + "properties": { + "albumIds": { + "items": { + "format": "uuid", + "type": "string" + }, + "type": "array" + }, + "assetIds": { + "items": { + "format": "uuid", + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "albumIds", + "assetIds" + ], + "type": "object" + }, + "AlbumsAddAssetsResponseDto": { + "properties": { + "albumSuccessCount": { + "type": "integer" + }, + "assetSuccessCount": { + "type": "integer" + }, + "error": { + "allOf": [ + { + "$ref": "#/components/schemas/BulkIdErrorReason" + } + ] + }, + "success": { + "type": "boolean" + } + }, + "required": [ + "albumSuccessCount", + "assetSuccessCount", + "success" + ], + "type": "object" + }, "AlbumsResponse": { "properties": { "defaultAssetOrder": { @@ -10877,6 +10987,15 @@ }, "type": "object" }, + "BulkIdErrorReason": { + "enum": [ + "duplicate", + "no_permission", + "not_found", + "unknown" + ], + "type": "string" + }, "BulkIdResponseDto": { "properties": { "error": { diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index f0cdbef508..6dd0a5c622 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -384,6 +384,16 @@ export type CreateAlbumDto = { assetIds?: string[]; description?: string; }; +export type AlbumsAddAssetsDto = { + albumIds: string[]; + assetIds: string[]; +}; +export type AlbumsAddAssetsResponseDto = { + albumSuccessCount: number; + assetSuccessCount: number; + error?: BulkIdErrorReason; + success: boolean; +}; export type AlbumStatisticsResponseDto = { notShared: number; owned: number; @@ -1864,6 +1874,26 @@ export function createAlbum({ createAlbumDto }: { body: createAlbumDto }))); } +/** + * This endpoint requires the `albumAsset.create` permission. + */ +export function addAssetsToAlbums({ key, slug, albumsAddAssetsDto }: { + key?: string; + slug?: string; + albumsAddAssetsDto: AlbumsAddAssetsDto; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: AlbumsAddAssetsResponseDto; + }>(`/albums/assets${QS.query(QS.explode({ + key, + slug + }))}`, oazapfts.json({ + ...opts, + method: "PUT", + body: albumsAddAssetsDto + }))); +} /** * This endpoint requires the `album.statistics` permission. */ @@ -4553,6 +4583,12 @@ export enum AssetTypeEnum { Audio = "AUDIO", Other = "OTHER" } +export enum BulkIdErrorReason { + Duplicate = "duplicate", + NoPermission = "no_permission", + NotFound = "not_found", + Unknown = "unknown" +} export enum Error { Duplicate = "duplicate", NoPermission = "no_permission", diff --git a/server/src/controllers/album.controller.spec.ts b/server/src/controllers/album.controller.spec.ts index 9b8a19c129..d13227555b 100644 --- a/server/src/controllers/album.controller.spec.ts +++ b/server/src/controllers/album.controller.spec.ts @@ -65,6 +65,13 @@ describe(AlbumController.name, () => { }); }); + describe('PUT /albums/assets', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).put(`/albums/assets`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + describe('PATCH /albums/:id', () => { it('should be an authenticated route', async () => { await request(ctx.getHttpServer()).patch(`/albums/${factory.uuid()}`).send({ albumName: 'New album name' }); diff --git a/server/src/controllers/album.controller.ts b/server/src/controllers/album.controller.ts index a331fc04f1..47f8b5603a 100644 --- a/server/src/controllers/album.controller.ts +++ b/server/src/controllers/album.controller.ts @@ -4,6 +4,8 @@ import { AddUsersDto, AlbumInfoDto, AlbumResponseDto, + AlbumsAddAssetsDto, + AlbumsAddAssetsResponseDto, AlbumStatisticsResponseDto, CreateAlbumDto, GetAlbumsDto, @@ -77,6 +79,12 @@ export class AlbumController { return this.service.addAssets(auth, id, dto); } + @Put('assets') + @Authenticated({ permission: Permission.AlbumAssetCreate, sharedLink: true }) + addAssetsToAlbums(@Auth() auth: AuthDto, @Body() dto: AlbumsAddAssetsDto): Promise { + return this.service.addAssetsToAlbums(auth, dto); + } + @Delete(':id/assets') @Authenticated({ permission: Permission.AlbumAssetDelete }) removeAssetFromAlbum( diff --git a/server/src/dtos/album.dto.ts b/server/src/dtos/album.dto.ts index 3a88ba5be3..73630b63cb 100644 --- a/server/src/dtos/album.dto.ts +++ b/server/src/dtos/album.dto.ts @@ -3,6 +3,7 @@ import { Type } from 'class-transformer'; import { ArrayNotEmpty, IsArray, IsString, ValidateNested } from 'class-validator'; import _ from 'lodash'; import { AlbumUser, AuthSharedLink, User } from 'src/database'; +import { BulkIdErrorReason } from 'src/dtos/asset-ids.response.dto'; import { AssetResponseDto, MapAsset, mapAsset } from 'src/dtos/asset-response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { UserResponseDto, mapUser } from 'src/dtos/user.dto'; @@ -54,6 +55,24 @@ export class CreateAlbumDto { assetIds?: string[]; } +export class AlbumsAddAssetsDto { + @ValidateUUID({ each: true }) + albumIds!: string[]; + + @ValidateUUID({ each: true }) + assetIds!: string[]; +} + +export class AlbumsAddAssetsResponseDto { + success!: boolean; + @ApiProperty({ type: 'integer' }) + albumSuccessCount!: number; + @ApiProperty({ type: 'integer' }) + assetSuccessCount!: number; + @ValidateEnum({ enum: BulkIdErrorReason, name: 'BulkIdErrorReason', optional: true }) + error?: BulkIdErrorReason; +} + export class UpdateAlbumDto { @Optional() @IsString() diff --git a/server/src/services/album.service.spec.ts b/server/src/services/album.service.spec.ts index 6f07a31dd9..f3ba57d744 100644 --- a/server/src/services/album.service.spec.ts +++ b/server/src/services/album.service.spec.ts @@ -776,6 +776,338 @@ describe(AlbumService.name, () => { }); }); + describe('addAssetsToAlbums', () => { + it('should allow the owner to add assets', async () => { + mocks.access.album.checkOwnerAccess + .mockResolvedValueOnce(new Set(['album-123'])) + .mockResolvedValueOnce(new Set(['album-321'])); + mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1', 'asset-2', 'asset-3'])); + mocks.album.getById + .mockResolvedValueOnce(_.cloneDeep(albumStub.empty)) + .mockResolvedValueOnce(_.cloneDeep(albumStub.oneAsset)); + mocks.album.getAssetIds.mockResolvedValueOnce(new Set()).mockResolvedValueOnce(new Set()); + + await expect( + sut.addAssetsToAlbums(authStub.admin, { + albumIds: ['album-123', 'album-321'], + assetIds: ['asset-1', 'asset-2', 'asset-3'], + }), + ).resolves.toEqual({ success: true, albumSuccessCount: 2, assetSuccessCount: 3 }); + + expect(mocks.album.update).toHaveBeenCalledTimes(2); + expect(mocks.album.update).toHaveBeenNthCalledWith(1, 'album-123', { + id: 'album-123', + updatedAt: expect.any(Date), + albumThumbnailAssetId: 'asset-1', + }); + expect(mocks.album.update).toHaveBeenNthCalledWith(2, 'album-321', { + id: 'album-321', + updatedAt: expect.any(Date), + albumThumbnailAssetId: 'asset-1', + }); + expect(mocks.album.addAssetIds).toHaveBeenCalledWith('album-123', ['asset-1', 'asset-2', 'asset-3']); + expect(mocks.album.addAssetIds).toHaveBeenCalledWith('album-321', ['asset-1', 'asset-2', 'asset-3']); + }); + + it('should not set the thumbnail if the album has one already', async () => { + mocks.access.album.checkOwnerAccess + .mockResolvedValueOnce(new Set(['album-123'])) + .mockResolvedValueOnce(new Set(['album-321'])); + mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1', 'asset-2', 'asset-3'])); + mocks.album.getById + .mockResolvedValueOnce(_.cloneDeep({ ...albumStub.empty, albumThumbnailAssetId: 'asset-id' })) + .mockResolvedValueOnce(_.cloneDeep({ ...albumStub.oneAsset, albumThumbnailAssetId: 'asset-id' })); + mocks.album.getAssetIds.mockResolvedValueOnce(new Set()).mockResolvedValueOnce(new Set()); + + await expect( + sut.addAssetsToAlbums(authStub.admin, { + albumIds: ['album-123', 'album-321'], + assetIds: ['asset-1', 'asset-2', 'asset-3'], + }), + ).resolves.toEqual({ success: true, albumSuccessCount: 2, assetSuccessCount: 3 }); + + expect(mocks.album.update).toHaveBeenCalledTimes(2); + expect(mocks.album.update).toHaveBeenNthCalledWith(1, 'album-123', { + id: 'album-123', + updatedAt: expect.any(Date), + albumThumbnailAssetId: 'asset-id', + }); + expect(mocks.album.update).toHaveBeenNthCalledWith(2, 'album-321', { + id: 'album-321', + updatedAt: expect.any(Date), + albumThumbnailAssetId: 'asset-id', + }); + expect(mocks.album.addAssetIds).toHaveBeenCalledWith('album-123', ['asset-1', 'asset-2', 'asset-3']); + expect(mocks.album.addAssetIds).toHaveBeenCalledWith('album-321', ['asset-1', 'asset-2', 'asset-3']); + }); + + it('should allow a shared user to add assets', async () => { + mocks.access.album.checkSharedAlbumAccess + .mockResolvedValueOnce(new Set(['album-123'])) + .mockResolvedValueOnce(new Set(['album-321'])); + mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1', 'asset-2', 'asset-3'])); + mocks.album.getById + .mockResolvedValueOnce(_.cloneDeep(albumStub.sharedWithUser)) + .mockResolvedValueOnce(_.cloneDeep(albumStub.sharedWithMultiple)); + mocks.album.getAssetIds.mockResolvedValueOnce(new Set()).mockResolvedValueOnce(new Set()); + + await expect( + sut.addAssetsToAlbums(authStub.user1, { + albumIds: ['album-123', 'album-321'], + assetIds: ['asset-1', 'asset-2', 'asset-3'], + }), + ).resolves.toEqual({ success: true, albumSuccessCount: 2, assetSuccessCount: 3 }); + + expect(mocks.album.update).toHaveBeenCalledTimes(2); + expect(mocks.album.update).toHaveBeenNthCalledWith(1, 'album-123', { + id: 'album-123', + updatedAt: expect.any(Date), + albumThumbnailAssetId: 'asset-1', + }); + expect(mocks.album.update).toHaveBeenNthCalledWith(2, 'album-321', { + id: 'album-321', + updatedAt: expect.any(Date), + albumThumbnailAssetId: 'asset-1', + }); + expect(mocks.album.addAssetIds).toHaveBeenCalledWith('album-123', ['asset-1', 'asset-2', 'asset-3']); + expect(mocks.album.addAssetIds).toHaveBeenCalledWith('album-321', ['asset-1', 'asset-2', 'asset-3']); + expect(mocks.event.emit).toHaveBeenCalledWith('AlbumUpdate', { + id: 'album-123', + recipientId: 'admin_id', + }); + expect(mocks.event.emit).toHaveBeenCalledWith('AlbumUpdate', { + id: 'album-321', + recipientId: 'admin_id', + }); + }); + + it('should not allow a shared user with viewer access to add assets', async () => { + mocks.access.album.checkSharedAlbumAccess.mockResolvedValueOnce(new Set()).mockResolvedValueOnce(new Set()); + mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1', 'asset-2', 'asset-3'])); + mocks.album.getById + .mockResolvedValueOnce(_.cloneDeep(albumStub.sharedWithUser)) + .mockResolvedValueOnce(_.cloneDeep(albumStub.sharedWithAdmin)); + mocks.album.getAssetIds.mockResolvedValueOnce(new Set()).mockResolvedValueOnce(new Set()); + + await expect( + sut.addAssetsToAlbums(authStub.user2, { + albumIds: ['album-123', 'album-321'], + assetIds: ['asset-1', 'asset-2', 'asset-3'], + }), + ).resolves.toEqual({ + success: false, + albumSuccessCount: 0, + assetSuccessCount: 0, + error: BulkIdErrorReason.UNKNOWN, + }); + + expect(mocks.album.update).not.toHaveBeenCalled(); + }); + + it('should not allow a shared link user to add assets to multiple albums', async () => { + mocks.access.album.checkSharedLinkAccess + .mockResolvedValueOnce(new Set(['album-123'])) + .mockResolvedValueOnce(new Set()); + mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1', 'asset-2', 'asset-3'])); + mocks.album.getById + .mockResolvedValueOnce(_.cloneDeep(albumStub.sharedWithUser)) + .mockResolvedValueOnce(_.cloneDeep(albumStub.sharedWithMultiple)); + mocks.album.getAssetIds.mockResolvedValueOnce(new Set()).mockResolvedValueOnce(new Set()); + + await expect( + sut.addAssetsToAlbums(authStub.adminSharedLink, { + albumIds: ['album-123', 'album-321'], + assetIds: ['asset-1', 'asset-2', 'asset-3'], + }), + ).resolves.toEqual({ success: true, albumSuccessCount: 1, assetSuccessCount: 3 }); + + expect(mocks.album.update).toHaveBeenCalledTimes(1); + expect(mocks.album.update).toHaveBeenNthCalledWith(1, 'album-123', { + id: 'album-123', + updatedAt: expect.any(Date), + albumThumbnailAssetId: 'asset-1', + }); + expect(mocks.album.addAssetIds).toHaveBeenCalledTimes(1); + expect(mocks.album.addAssetIds).toHaveBeenCalledWith('album-123', ['asset-1', 'asset-2', 'asset-3']); + expect(mocks.event.emit).toHaveBeenCalledWith('AlbumUpdate', { + id: 'album-123', + recipientId: 'user-id', + }); + expect(mocks.access.album.checkSharedLinkAccess).toHaveBeenCalledWith( + authStub.adminSharedLink.sharedLink?.id, + new Set(['album-123']), + ); + }); + + it('should allow adding assets shared via partner sharing', async () => { + mocks.access.album.checkOwnerAccess + .mockResolvedValueOnce(new Set(['album-123'])) + .mockResolvedValueOnce(new Set(['album-321'])); + mocks.access.asset.checkPartnerAccess.mockResolvedValue(new Set(['asset-1', 'asset-2', 'asset-3'])); + mocks.album.getById + .mockResolvedValueOnce(_.cloneDeep(albumStub.empty)) + .mockResolvedValueOnce(_.cloneDeep(albumStub.oneAsset)); + mocks.album.getAssetIds.mockResolvedValueOnce(new Set()).mockResolvedValueOnce(new Set()); + + await expect( + sut.addAssetsToAlbums(authStub.admin, { + albumIds: ['album-123', 'album-321'], + assetIds: ['asset-1', 'asset-2', 'asset-3'], + }), + ).resolves.toEqual({ success: true, albumSuccessCount: 2, assetSuccessCount: 3 }); + + expect(mocks.album.update).toHaveBeenCalledTimes(2); + expect(mocks.album.update).toHaveBeenNthCalledWith(1, 'album-123', { + id: 'album-123', + updatedAt: expect.any(Date), + albumThumbnailAssetId: 'asset-1', + }); + expect(mocks.album.update).toHaveBeenNthCalledWith(2, 'album-321', { + id: 'album-321', + updatedAt: expect.any(Date), + albumThumbnailAssetId: 'asset-1', + }); + expect(mocks.album.addAssetIds).toHaveBeenCalledWith('album-123', ['asset-1', 'asset-2', 'asset-3']); + expect(mocks.album.addAssetIds).toHaveBeenCalledWith('album-321', ['asset-1', 'asset-2', 'asset-3']); + expect(mocks.access.asset.checkPartnerAccess).toHaveBeenCalledWith( + authStub.admin.user.id, + new Set(['asset-1', 'asset-2', 'asset-3']), + ); + }); + + it('should skip some duplicate assets', async () => { + mocks.access.album.checkOwnerAccess + .mockResolvedValueOnce(new Set(['album-123'])) + .mockResolvedValueOnce(new Set(['album-321'])); + mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1', 'asset-2', 'asset-3'])); + mocks.album.getById + .mockResolvedValueOnce(_.cloneDeep(albumStub.empty)) + .mockResolvedValueOnce(_.cloneDeep(albumStub.oneAsset)); + mocks.album.getAssetIds + .mockResolvedValueOnce(new Set(['asset-1', 'asset-2', 'asset-3'])) + .mockResolvedValueOnce(new Set()); + + await expect( + sut.addAssetsToAlbums(authStub.admin, { + albumIds: ['album-123', 'album-321'], + assetIds: ['asset-1', 'asset-2', 'asset-3'], + }), + ).resolves.toEqual({ success: true, albumSuccessCount: 1, assetSuccessCount: 3 }); + + expect(mocks.album.update).toHaveBeenCalledTimes(1); + expect(mocks.album.update).toHaveBeenNthCalledWith(1, 'album-321', { + id: 'album-321', + updatedAt: expect.any(Date), + albumThumbnailAssetId: 'asset-1', + }); + expect(mocks.album.addAssetIds).toHaveBeenCalledTimes(1); + expect(mocks.album.addAssetIds).toHaveBeenCalledWith('album-321', ['asset-1', 'asset-2', 'asset-3']); + }); + + it('should skip all duplicate assets', async () => { + mocks.access.album.checkOwnerAccess + .mockResolvedValueOnce(new Set(['album-123'])) + .mockResolvedValueOnce(new Set(['album-321'])); + mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1', 'asset-2'])); + mocks.album.getById + .mockResolvedValueOnce(_.cloneDeep(albumStub.empty)) + .mockResolvedValueOnce(_.cloneDeep(albumStub.oneAsset)); + mocks.album.getAssetIds.mockResolvedValue(new Set(['asset-1', 'asset-2'])); + + await expect( + sut.addAssetsToAlbums(authStub.admin, { + albumIds: ['album-123', 'album-321'], + assetIds: ['asset-1', 'asset-2'], + }), + ).resolves.toEqual({ + success: false, + albumSuccessCount: 0, + assetSuccessCount: 0, + error: BulkIdErrorReason.DUPLICATE, + }); + + expect(mocks.album.update).not.toHaveBeenCalled(); + expect(mocks.album.addAssetIds).not.toHaveBeenCalled(); + }); + + it('should skip assets not shared with user', async () => { + mocks.access.album.checkSharedAlbumAccess + .mockResolvedValueOnce(new Set(['album-123'])) + .mockResolvedValueOnce(new Set(['album-321'])); + mocks.album.getById + .mockResolvedValueOnce(_.cloneDeep(albumStub.sharedWithUser)) + .mockResolvedValueOnce(_.cloneDeep(albumStub.sharedWithMultiple)); + mocks.album.getAssetIds.mockResolvedValueOnce(new Set()).mockResolvedValueOnce(new Set()); + + await expect( + sut.addAssetsToAlbums(authStub.admin, { + albumIds: ['album-123', 'album-321'], + assetIds: ['asset-1', 'asset-2', 'asset-3'], + }), + ).resolves.toEqual({ + success: false, + albumSuccessCount: 0, + assetSuccessCount: 0, + error: BulkIdErrorReason.UNKNOWN, + }); + + expect(mocks.album.update).not.toHaveBeenCalled(); + expect(mocks.album.addAssetIds).not.toHaveBeenCalled(); + expect(mocks.access.asset.checkOwnerAccess).toHaveBeenCalledWith( + authStub.admin.user.id, + new Set(['asset-1', 'asset-2', 'asset-3']), + false, + ); + expect(mocks.access.asset.checkPartnerAccess).toHaveBeenCalledWith( + authStub.admin.user.id, + new Set(['asset-1', 'asset-2', 'asset-3']), + ); + }); + + it('should not allow unauthorized access to the albums', async () => { + mocks.album.getById + .mockResolvedValueOnce(_.cloneDeep(albumStub.sharedWithUser)) + .mockResolvedValueOnce(_.cloneDeep(albumStub.sharedWithMultiple)); + + await expect( + sut.addAssetsToAlbums(authStub.admin, { + albumIds: ['album-123', 'album-321'], + assetIds: ['asset-1', 'asset-2', 'asset-3'], + }), + ).resolves.toEqual({ + success: false, + albumSuccessCount: 0, + assetSuccessCount: 0, + error: BulkIdErrorReason.UNKNOWN, + }); + + expect(mocks.album.update).not.toHaveBeenCalled(); + expect(mocks.album.addAssetIds).not.toHaveBeenCalled(); + expect(mocks.access.album.checkOwnerAccess).toHaveBeenCalled(); + expect(mocks.access.album.checkSharedAlbumAccess).toHaveBeenCalled(); + }); + + it('should not allow unauthorized shared link access to the album', async () => { + mocks.album.getById + .mockResolvedValueOnce(_.cloneDeep(albumStub.empty)) + .mockResolvedValueOnce(_.cloneDeep(albumStub.oneAsset)); + + await expect( + sut.addAssetsToAlbums(authStub.adminSharedLink, { + albumIds: ['album-123', 'album-321'], + assetIds: ['asset-1', 'asset-2', 'asset-3'], + }), + ).resolves.toEqual({ + success: false, + albumSuccessCount: 0, + assetSuccessCount: 0, + error: BulkIdErrorReason.UNKNOWN, + }); + + expect(mocks.access.album.checkSharedLinkAccess).toHaveBeenCalled(); + }); + }); + describe('removeAssets', () => { it('should allow the owner to remove assets', async () => { mocks.access.album.checkOwnerAccess.mockResolvedValue(new Set(['album-123'])); diff --git a/server/src/services/album.service.ts b/server/src/services/album.service.ts index 90aefa6d72..32832f0dd3 100644 --- a/server/src/services/album.service.ts +++ b/server/src/services/album.service.ts @@ -3,6 +3,8 @@ import { AddUsersDto, AlbumInfoDto, AlbumResponseDto, + AlbumsAddAssetsDto, + AlbumsAddAssetsResponseDto, AlbumStatisticsResponseDto, CreateAlbumDto, GetAlbumsDto, @@ -13,7 +15,7 @@ import { UpdateAlbumDto, UpdateAlbumUserDto, } from 'src/dtos/album.dto'; -import { BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; +import { BulkIdErrorReason, BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { Permission } from 'src/enum'; import { AlbumAssetCount, AlbumInfoOptions } from 'src/repositories/album.repository'; @@ -186,6 +188,43 @@ export class AlbumService extends BaseService { return results; } + async addAssetsToAlbums(auth: AuthDto, dto: AlbumsAddAssetsDto): Promise { + const results: AlbumsAddAssetsResponseDto = { + success: false, + albumSuccessCount: 0, + assetSuccessCount: 0, + error: BulkIdErrorReason.DUPLICATE, + }; + const successfulAssetIds: Set = new Set(); + for (const albumId of dto.albumIds) { + try { + const albumResults = await this.addAssets(auth, albumId, { ids: dto.assetIds }); + + let success = false; + for (const res of albumResults) { + if (res.success) { + success = true; + results.success = true; + results.error = undefined; + successfulAssetIds.add(res.id); + } else if (results.error && res.error !== BulkIdErrorReason.DUPLICATE) { + results.error = BulkIdErrorReason.UNKNOWN; + } + } + if (success) { + results.albumSuccessCount++; + } + } catch { + if (results.error) { + results.error = BulkIdErrorReason.UNKNOWN; + } + } + } + results.assetSuccessCount = successfulAssetIds.size; + + return results; + } + async removeAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise { await this.requireAccess({ auth, permission: Permission.AlbumAssetDelete, ids: [id] }); diff --git a/web/src/lib/components/asset-viewer/actions/add-to-album-action.svelte b/web/src/lib/components/asset-viewer/actions/add-to-album-action.svelte index dca0b7918e..2c6ac54ef7 100644 --- a/web/src/lib/components/asset-viewer/actions/add-to-album-action.svelte +++ b/web/src/lib/components/asset-viewer/actions/add-to-album-action.svelte @@ -4,7 +4,7 @@ import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte'; import { AssetAction } from '$lib/constants'; import AlbumPickerModal from '$lib/modals/AlbumPickerModal.svelte'; - import { addAssetsToAlbum } from '$lib/utils/asset-utils'; + import { addAssetsToAlbum, addAssetsToAlbums } from '$lib/utils/asset-utils'; import { toTimelineAsset } from '$lib/utils/timeline-util'; import type { AssetResponseDto } from '@immich/sdk'; import { modalManager } from '@immich/ui'; @@ -20,14 +20,23 @@ let { asset, onAction, shared = false }: Props = $props(); const onClick = async () => { - const album = await modalManager.show(AlbumPickerModal, { shared }); + const albums = await modalManager.show(AlbumPickerModal, { shared }); - if (!album) { + if (!albums || albums.length === 0) { return; } - await addAssetsToAlbum(album.id, [asset.id]); - onAction({ type: AssetAction.ADD_TO_ALBUM, asset: toTimelineAsset(asset), album }); + if (albums.length === 1) { + const album = albums[0]; + await addAssetsToAlbum(album.id, [asset.id]); + onAction({ type: AssetAction.ADD_TO_ALBUM, asset: toTimelineAsset(asset), album }); + } else { + await addAssetsToAlbums( + albums.map((a) => a.id), + [asset.id], + ); + onAction({ type: AssetAction.ADD_TO_ALBUM, asset: toTimelineAsset(asset), album: albums[0] }); + } }; diff --git a/web/src/lib/components/asset-viewer/album-list-item.svelte b/web/src/lib/components/asset-viewer/album-list-item.svelte index 7751bd09d8..bf2e34b7c9 100644 --- a/web/src/lib/components/asset-viewer/album-list-item.svelte +++ b/web/src/lib/components/asset-viewer/album-list-item.svelte @@ -1,8 +1,11 @@ - + + {albumNameArray[0]}{albumNameArray[1]}{albumNameArray[2]} + + + + + + + {#if mouseOver || multiSelected} + + {/if} +

    diff --git a/web/src/lib/components/photos-page/actions/add-to-album.svelte b/web/src/lib/components/photos-page/actions/add-to-album.svelte index 5ec2e879c9..13a26cd137 100644 --- a/web/src/lib/components/photos-page/actions/add-to-album.svelte +++ b/web/src/lib/components/photos-page/actions/add-to-album.svelte @@ -2,7 +2,7 @@ import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte'; import AlbumPickerModal from '$lib/modals/AlbumPickerModal.svelte'; import type { OnAddToAlbum } from '$lib/utils/actions'; - import { addAssetsToAlbum } from '$lib/utils/asset-utils'; + import { addAssetsToAlbum, addAssetsToAlbums } from '$lib/utils/asset-utils'; import { modalManager } from '@immich/ui'; import { mdiImageAlbum, mdiShareVariantOutline } from '@mdi/js'; import { t } from 'svelte-i18n'; @@ -18,15 +18,23 @@ const { getAssets } = getAssetControlContext(); const onClick = async () => { - const album = await modalManager.show(AlbumPickerModal, { shared }); - - if (!album) { + const albums = await modalManager.show(AlbumPickerModal, { shared }); + if (!albums || albums.length === 0) { return; } const assetIds = [...getAssets()].map(({ id }) => id); - await addAssetsToAlbum(album.id, assetIds); - onAddToAlbum(assetIds, album.id); + if (albums.length === 1) { + const album = albums[0]; + await addAssetsToAlbum(album.id, assetIds); + onAddToAlbum(assetIds, album.id); + } else { + await addAssetsToAlbums( + albums.map(({ id }) => id), + assetIds, + ); + onAddToAlbum(assetIds, albums[0].id); + } }; diff --git a/web/src/lib/components/shared-components/album-selection/album-selection-utils.spec.ts b/web/src/lib/components/shared-components/album-selection/album-selection-utils.spec.ts index 242809d58f..a078e55762 100644 --- a/web/src/lib/components/shared-components/album-selection/album-selection-utils.spec.ts +++ b/web/src/lib/components/shared-components/album-selection/album-selection-utils.spec.ts @@ -24,19 +24,26 @@ const createAlbumRow = (album: AlbumResponseDto, selected: boolean) => ({ type: AlbumModalRowType.ALBUM_ITEM, album, selected, + multiSelected: false, }); describe('Album Modal', () => { it('non-shared with no albums configured yet shows message and new', () => { const converter = new AlbumModalRowConverter(false, AlbumSortBy.MostRecentPhoto, SortOrder.Desc); - const modalRows = converter.toModalRows('', [], [], -1); + const modalRows = converter.toModalRows('', [], [], -1, []); expect(modalRows).toStrictEqual([createNewAlbumRow(false), createMessageRow('no_albums_yet')]); }); it('non-shared with no matching albums shows message and new', () => { const converter = new AlbumModalRowConverter(false, AlbumSortBy.MostRecentPhoto, SortOrder.Desc); - const modalRows = converter.toModalRows('matches_nothing', [], [albumFactory.build({ albumName: 'Holidays' })], -1); + const modalRows = converter.toModalRows( + 'matches_nothing', + [], + [albumFactory.build({ albumName: 'Holidays' })], + -1, + [], + ); expect(modalRows).toStrictEqual([createNewAlbumRow(false), createMessageRow('no_albums_with_name_yet')]); }); @@ -44,7 +51,7 @@ describe('Album Modal', () => { it('non-shared displays single albums', () => { const converter = new AlbumModalRowConverter(false, AlbumSortBy.MostRecentPhoto, SortOrder.Desc); const holidayAlbum = albumFactory.build({ albumName: 'Holidays' }); - const modalRows = converter.toModalRows('', [], [holidayAlbum], -1); + const modalRows = converter.toModalRows('', [], [holidayAlbum], -1, []); expect(modalRows).toStrictEqual([ createNewAlbumRow(false), @@ -64,6 +71,7 @@ describe('Album Modal', () => { [holidayAlbum, constructionAlbum], [holidayAlbum, constructionAlbum, birthdayAlbum, christmasAlbum], -1, + [], ); expect(modalRows).toStrictEqual([ @@ -90,6 +98,7 @@ describe('Album Modal', () => { [holidayAlbum, constructionAlbum], [holidayAlbum, constructionAlbum, birthdayAlbum, christmasAlbum], -1, + [], ); expect(modalRows).toStrictEqual([ @@ -112,6 +121,7 @@ describe('Album Modal', () => { [holidayAlbum, constructionAlbum], [holidayAlbum, constructionAlbum, birthdayAlbum, christmasAlbum], -1, + [], ); expect(modalRows).toStrictEqual([ @@ -125,7 +135,7 @@ describe('Album Modal', () => { const converter = new AlbumModalRowConverter(false, AlbumSortBy.MostRecentPhoto, SortOrder.Desc); const holidayAlbum = albumFactory.build({ albumName: 'Holidays' }); const constructionAlbum = albumFactory.build({ albumName: 'Construction' }); - const modalRows = converter.toModalRows('', [holidayAlbum], [holidayAlbum, constructionAlbum], 0); + const modalRows = converter.toModalRows('', [holidayAlbum], [holidayAlbum, constructionAlbum], 0, []); expect(modalRows).toStrictEqual([ createNewAlbumRow(true), @@ -141,7 +151,7 @@ describe('Album Modal', () => { const converter = new AlbumModalRowConverter(false, AlbumSortBy.MostRecentPhoto, SortOrder.Desc); const holidayAlbum = albumFactory.build({ albumName: 'Holidays' }); const constructionAlbum = albumFactory.build({ albumName: 'Construction' }); - const modalRows = converter.toModalRows('', [holidayAlbum], [holidayAlbum, constructionAlbum], 1); + const modalRows = converter.toModalRows('', [holidayAlbum], [holidayAlbum, constructionAlbum], 1, []); expect(modalRows).toStrictEqual([ createNewAlbumRow(false), @@ -157,7 +167,7 @@ describe('Album Modal', () => { const converter = new AlbumModalRowConverter(false, AlbumSortBy.MostRecentPhoto, SortOrder.Desc); const holidayAlbum = albumFactory.build({ albumName: 'Holidays' }); const constructionAlbum = albumFactory.build({ albumName: 'Construction' }); - const modalRows = converter.toModalRows('', [holidayAlbum], [holidayAlbum, constructionAlbum], 3); + const modalRows = converter.toModalRows('', [holidayAlbum], [holidayAlbum, constructionAlbum], 3, []); expect(modalRows).toStrictEqual([ createNewAlbumRow(false), diff --git a/web/src/lib/components/shared-components/album-selection/album-selection-utils.ts b/web/src/lib/components/shared-components/album-selection/album-selection-utils.ts index 73f289eb1d..f016916f7f 100644 --- a/web/src/lib/components/shared-components/album-selection/album-selection-utils.ts +++ b/web/src/lib/components/shared-components/album-selection/album-selection-utils.ts @@ -16,6 +16,7 @@ export enum AlbumModalRowType { export type AlbumModalRow = { type: AlbumModalRowType; selected?: boolean; + multiSelected?: boolean; text?: string; album?: AlbumResponseDto; }; @@ -41,6 +42,7 @@ export class AlbumModalRowConverter { recentAlbums: AlbumResponseDto[], albums: AlbumResponseDto[], selectedRowIndex: number, + multiSelectedAlbumIds: string[], ): AlbumModalRow[] { // only show recent albums if no search was entered, or we're in the normal albums (non-shared) modal. const recentAlbumsToShow = !this.shared && search.length === 0 ? recentAlbums : []; @@ -64,6 +66,7 @@ export class AlbumModalRowConverter { rows.push({ type: AlbumModalRowType.ALBUM_ITEM, selected: selectedRowIndex === i + selectedOffsetDueToNewAlbumRow, + multiSelected: multiSelectedAlbumIds.includes(album.id), album, }); } @@ -81,6 +84,7 @@ export class AlbumModalRowConverter { rows.push({ type: AlbumModalRowType.ALBUM_ITEM, selected: selectedRowIndex === i + selectedOffsetDueToNewAndRecents, + multiSelected: multiSelectedAlbumIds.includes(album.id), album, }); } diff --git a/web/src/lib/components/shared-components/album-selection/new-album-list-item.svelte b/web/src/lib/components/shared-components/album-selection/new-album-list-item.svelte index d8be0e2a30..a1adc3ef4f 100644 --- a/web/src/lib/components/shared-components/album-selection/new-album-list-item.svelte +++ b/web/src/lib/components/shared-components/album-selection/new-album-list-item.svelte @@ -1,9 +1,9 @@ @@ -70,6 +74,8 @@ {/if} - + From 88072910da098885aa3fc786244cf388fa88bd9b Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 27 Aug 2025 14:31:23 -0400 Subject: [PATCH 342/748] feat: asset metadata (#20446) --- mobile/openapi/README.md | 10 + mobile/openapi/lib/api.dart | 6 + mobile/openapi/lib/api/assets_api.dart | 238 ++++++++++++- mobile/openapi/lib/api_client.dart | 12 + mobile/openapi/lib/api_helper.dart | 3 + .../openapi/lib/model/asset_metadata_key.dart | 82 +++++ .../model/asset_metadata_response_dto.dart | 115 +++++++ .../lib/model/asset_metadata_upsert_dto.dart | 99 ++++++ .../model/asset_metadata_upsert_item_dto.dart | 107 ++++++ .../model/sync_asset_metadata_delete_v1.dart | 107 ++++++ .../lib/model/sync_asset_metadata_v1.dart | 115 +++++++ .../openapi/lib/model/sync_entity_type.dart | 6 + .../openapi/lib/model/sync_request_type.dart | 3 + open-api/immich-openapi-specs.json | 314 +++++++++++++++++- open-api/typescript-sdk/src/fetch-client.ts | 74 +++++ .../src/controllers/asset.controller.spec.ts | 121 ++++++- server/src/controllers/asset.controller.ts | 35 ++ server/src/dtos/asset-media.dto.ts | 7 + server/src/dtos/asset.dto.ts | 53 ++- server/src/dtos/sync.dto.ts | 18 + server/src/enum.ts | 7 + server/src/queries/asset.repository.sql | 27 ++ server/src/queries/sync.repository.sql | 31 ++ server/src/repositories/asset.repository.ts | 42 ++- server/src/repositories/sync.repository.ts | 22 ++ server/src/repositories/user.repository.ts | 7 +- server/src/schema/functions.ts | 13 + server/src/schema/index.ts | 8 + .../1756318797207-AssetMetadataTables.ts | 58 ++++ .../tables/asset-metadata-audit.table.ts | 18 + .../src/schema/tables/asset-metadata.table.ts | 46 +++ server/src/services/asset-media.service.ts | 4 + server/src/services/asset.service.ts | 33 +- server/src/services/sync.service.ts | 29 ++ server/src/types.ts | 20 +- .../specs/sync/sync-asset-metadata.spec.ts | 126 +++++++ .../repositories/asset.repository.mock.ts | 4 + 37 files changed, 1999 insertions(+), 21 deletions(-) create mode 100644 mobile/openapi/lib/model/asset_metadata_key.dart create mode 100644 mobile/openapi/lib/model/asset_metadata_response_dto.dart create mode 100644 mobile/openapi/lib/model/asset_metadata_upsert_dto.dart create mode 100644 mobile/openapi/lib/model/asset_metadata_upsert_item_dto.dart create mode 100644 mobile/openapi/lib/model/sync_asset_metadata_delete_v1.dart create mode 100644 mobile/openapi/lib/model/sync_asset_metadata_v1.dart create mode 100644 server/src/schema/migrations/1756318797207-AssetMetadataTables.ts create mode 100644 server/src/schema/tables/asset-metadata-audit.table.ts create mode 100644 server/src/schema/tables/asset-metadata.table.ts create mode 100644 server/test/medium/specs/sync/sync-asset-metadata.spec.ts diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 04600250b1..27a0c6fcbe 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -97,16 +97,20 @@ Class | Method | HTTP request | Description *AlbumsApi* | [**updateAlbumUser**](doc//AlbumsApi.md#updatealbumuser) | **PUT** /albums/{id}/user/{userId} | *AssetsApi* | [**checkBulkUpload**](doc//AssetsApi.md#checkbulkupload) | **POST** /assets/bulk-upload-check | checkBulkUpload *AssetsApi* | [**checkExistingAssets**](doc//AssetsApi.md#checkexistingassets) | **POST** /assets/exist | checkExistingAssets +*AssetsApi* | [**deleteAssetMetadata**](doc//AssetsApi.md#deleteassetmetadata) | **DELETE** /assets/{id}/metadata/{key} | *AssetsApi* | [**deleteAssets**](doc//AssetsApi.md#deleteassets) | **DELETE** /assets | *AssetsApi* | [**downloadAsset**](doc//AssetsApi.md#downloadasset) | **GET** /assets/{id}/original | *AssetsApi* | [**getAllUserAssetsByDeviceId**](doc//AssetsApi.md#getalluserassetsbydeviceid) | **GET** /assets/device/{deviceId} | getAllUserAssetsByDeviceId *AssetsApi* | [**getAssetInfo**](doc//AssetsApi.md#getassetinfo) | **GET** /assets/{id} | +*AssetsApi* | [**getAssetMetadata**](doc//AssetsApi.md#getassetmetadata) | **GET** /assets/{id}/metadata | +*AssetsApi* | [**getAssetMetadataByKey**](doc//AssetsApi.md#getassetmetadatabykey) | **GET** /assets/{id}/metadata/{key} | *AssetsApi* | [**getAssetStatistics**](doc//AssetsApi.md#getassetstatistics) | **GET** /assets/statistics | *AssetsApi* | [**getRandom**](doc//AssetsApi.md#getrandom) | **GET** /assets/random | *AssetsApi* | [**playAssetVideo**](doc//AssetsApi.md#playassetvideo) | **GET** /assets/{id}/video/playback | *AssetsApi* | [**replaceAsset**](doc//AssetsApi.md#replaceasset) | **PUT** /assets/{id}/original | replaceAsset *AssetsApi* | [**runAssetJobs**](doc//AssetsApi.md#runassetjobs) | **POST** /assets/jobs | *AssetsApi* | [**updateAsset**](doc//AssetsApi.md#updateasset) | **PUT** /assets/{id} | +*AssetsApi* | [**updateAssetMetadata**](doc//AssetsApi.md#updateassetmetadata) | **PUT** /assets/{id}/metadata | *AssetsApi* | [**updateAssets**](doc//AssetsApi.md#updateassets) | **PUT** /assets | *AssetsApi* | [**uploadAsset**](doc//AssetsApi.md#uploadasset) | **POST** /assets | *AssetsApi* | [**viewAsset**](doc//AssetsApi.md#viewasset) | **GET** /assets/{id}/thumbnail | @@ -328,6 +332,10 @@ Class | Method | HTTP request | Description - [AssetMediaResponseDto](doc//AssetMediaResponseDto.md) - [AssetMediaSize](doc//AssetMediaSize.md) - [AssetMediaStatus](doc//AssetMediaStatus.md) + - [AssetMetadataKey](doc//AssetMetadataKey.md) + - [AssetMetadataResponseDto](doc//AssetMetadataResponseDto.md) + - [AssetMetadataUpsertDto](doc//AssetMetadataUpsertDto.md) + - [AssetMetadataUpsertItemDto](doc//AssetMetadataUpsertItemDto.md) - [AssetOrder](doc//AssetOrder.md) - [AssetResponseDto](doc//AssetResponseDto.md) - [AssetStackResponseDto](doc//AssetStackResponseDto.md) @@ -485,6 +493,8 @@ Class | Method | HTTP request | Description - [SyncAssetExifV1](doc//SyncAssetExifV1.md) - [SyncAssetFaceDeleteV1](doc//SyncAssetFaceDeleteV1.md) - [SyncAssetFaceV1](doc//SyncAssetFaceV1.md) + - [SyncAssetMetadataDeleteV1](doc//SyncAssetMetadataDeleteV1.md) + - [SyncAssetMetadataV1](doc//SyncAssetMetadataV1.md) - [SyncAssetV1](doc//SyncAssetV1.md) - [SyncAuthUserV1](doc//SyncAuthUserV1.md) - [SyncEntityType](doc//SyncEntityType.md) diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart index f5f353c968..a197f17fa7 100644 --- a/mobile/openapi/lib/api.dart +++ b/mobile/openapi/lib/api.dart @@ -106,6 +106,10 @@ part 'model/asset_jobs_dto.dart'; part 'model/asset_media_response_dto.dart'; part 'model/asset_media_size.dart'; part 'model/asset_media_status.dart'; +part 'model/asset_metadata_key.dart'; +part 'model/asset_metadata_response_dto.dart'; +part 'model/asset_metadata_upsert_dto.dart'; +part 'model/asset_metadata_upsert_item_dto.dart'; part 'model/asset_order.dart'; part 'model/asset_response_dto.dart'; part 'model/asset_stack_response_dto.dart'; @@ -263,6 +267,8 @@ part 'model/sync_asset_delete_v1.dart'; part 'model/sync_asset_exif_v1.dart'; part 'model/sync_asset_face_delete_v1.dart'; part 'model/sync_asset_face_v1.dart'; +part 'model/sync_asset_metadata_delete_v1.dart'; +part 'model/sync_asset_metadata_v1.dart'; part 'model/sync_asset_v1.dart'; part 'model/sync_auth_user_v1.dart'; part 'model/sync_entity_type.dart'; diff --git a/mobile/openapi/lib/api/assets_api.dart b/mobile/openapi/lib/api/assets_api.dart index c0de1a0801..0b53e09938 100644 --- a/mobile/openapi/lib/api/assets_api.dart +++ b/mobile/openapi/lib/api/assets_api.dart @@ -128,6 +128,56 @@ class AssetsApi { return null; } + /// This endpoint requires the `asset.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [AssetMetadataKey] key (required): + Future deleteAssetMetadataWithHttpInfo(String id, AssetMetadataKey key,) async { + // ignore: prefer_const_declarations + final apiPath = r'/assets/{id}/metadata/{key}' + .replaceAll('{id}', id) + .replaceAll('{key}', key.toString()); + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'DELETE', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// This endpoint requires the `asset.update` permission. + /// + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [AssetMetadataKey] key (required): + Future deleteAssetMetadata(String id, AssetMetadataKey key,) async { + final response = await deleteAssetMetadataWithHttpInfo(id, key,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + } + /// This endpoint requires the `asset.delete` permission. /// /// Note: This method returns the HTTP [Response]. @@ -368,6 +418,120 @@ class AssetsApi { return null; } + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// + /// Parameters: + /// + /// * [String] id (required): + Future getAssetMetadataWithHttpInfo(String id,) async { + // ignore: prefer_const_declarations + final apiPath = r'/assets/{id}/metadata' + .replaceAll('{id}', id); + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'GET', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// This endpoint requires the `asset.read` permission. + /// + /// Parameters: + /// + /// * [String] id (required): + Future?> getAssetMetadata(String id,) async { + final response = await getAssetMetadataWithHttpInfo(id,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + // When a remote server returns no body with a status of 204, we shall not decode it. + // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" + // FormatException when trying to decode an empty string. + if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { + final responseBody = await _decodeBodyBytes(response); + return (await apiClient.deserializeAsync(responseBody, 'List') as List) + .cast() + .toList(growable: false); + + } + return null; + } + + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [AssetMetadataKey] key (required): + Future getAssetMetadataByKeyWithHttpInfo(String id, AssetMetadataKey key,) async { + // ignore: prefer_const_declarations + final apiPath = r'/assets/{id}/metadata/{key}' + .replaceAll('{id}', id) + .replaceAll('{key}', key.toString()); + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'GET', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// This endpoint requires the `asset.read` permission. + /// + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [AssetMetadataKey] key (required): + Future getAssetMetadataByKey(String id, AssetMetadataKey key,) async { + final response = await getAssetMetadataByKeyWithHttpInfo(id, key,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + // When a remote server returns no body with a status of 204, we shall not decode it. + // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" + // FormatException when trying to decode an empty string. + if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { + return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'AssetMetadataResponseDto',) as AssetMetadataResponseDto; + + } + return null; + } + /// This endpoint requires the `asset.statistics` permission. /// /// Note: This method returns the HTTP [Response]. @@ -795,6 +959,66 @@ class AssetsApi { return null; } + /// This endpoint requires the `asset.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [AssetMetadataUpsertDto] assetMetadataUpsertDto (required): + Future updateAssetMetadataWithHttpInfo(String id, AssetMetadataUpsertDto assetMetadataUpsertDto,) async { + // ignore: prefer_const_declarations + final apiPath = r'/assets/{id}/metadata' + .replaceAll('{id}', id); + + // ignore: prefer_final_locals + Object? postBody = assetMetadataUpsertDto; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = ['application/json']; + + + return apiClient.invokeAPI( + apiPath, + 'PUT', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// This endpoint requires the `asset.update` permission. + /// + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [AssetMetadataUpsertDto] assetMetadataUpsertDto (required): + Future?> updateAssetMetadata(String id, AssetMetadataUpsertDto assetMetadataUpsertDto,) async { + final response = await updateAssetMetadataWithHttpInfo(id, assetMetadataUpsertDto,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + // When a remote server returns no body with a status of 204, we shall not decode it. + // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" + // FormatException when trying to decode an empty string. + if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { + final responseBody = await _decodeBodyBytes(response); + return (await apiClient.deserializeAsync(responseBody, 'List') as List) + .cast() + .toList(growable: false); + + } + return null; + } + /// This endpoint requires the `asset.update` permission. /// /// Note: This method returns the HTTP [Response]. @@ -855,6 +1079,8 @@ class AssetsApi { /// /// * [DateTime] fileModifiedAt (required): /// + /// * [List] metadata (required): + /// /// * [String] key: /// /// * [String] slug: @@ -873,7 +1099,7 @@ class AssetsApi { /// * [MultipartFile] sidecarData: /// /// * [AssetVisibility] visibility: - Future uploadAssetWithHttpInfo(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async { + Future uploadAssetWithHttpInfo(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, List metadata, { String? key, String? slug, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets'; @@ -936,6 +1162,10 @@ class AssetsApi { hasFields = true; mp.fields[r'livePhotoVideoId'] = parameterToString(livePhotoVideoId); } + if (metadata != null) { + hasFields = true; + mp.fields[r'metadata'] = parameterToString(metadata); + } if (sidecarData != null) { hasFields = true; mp.fields[r'sidecarData'] = sidecarData.field; @@ -974,6 +1204,8 @@ class AssetsApi { /// /// * [DateTime] fileModifiedAt (required): /// + /// * [List] metadata (required): + /// /// * [String] key: /// /// * [String] slug: @@ -992,8 +1224,8 @@ class AssetsApi { /// * [MultipartFile] sidecarData: /// /// * [AssetVisibility] visibility: - Future uploadAsset(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async { - final response = await uploadAssetWithHttpInfo(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, slug: slug, xImmichChecksum: xImmichChecksum, duration: duration, filename: filename, isFavorite: isFavorite, livePhotoVideoId: livePhotoVideoId, sidecarData: sidecarData, visibility: visibility, ); + Future uploadAsset(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, List metadata, { String? key, String? slug, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async { + final response = await uploadAssetWithHttpInfo(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, metadata, key: key, slug: slug, xImmichChecksum: xImmichChecksum, duration: duration, filename: filename, isFavorite: isFavorite, livePhotoVideoId: livePhotoVideoId, sidecarData: sidecarData, visibility: visibility, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart index 3f31d4ed90..3ea3b3c3e3 100644 --- a/mobile/openapi/lib/api_client.dart +++ b/mobile/openapi/lib/api_client.dart @@ -266,6 +266,14 @@ class ApiClient { return AssetMediaSizeTypeTransformer().decode(value); case 'AssetMediaStatus': return AssetMediaStatusTypeTransformer().decode(value); + case 'AssetMetadataKey': + return AssetMetadataKeyTypeTransformer().decode(value); + case 'AssetMetadataResponseDto': + return AssetMetadataResponseDto.fromJson(value); + case 'AssetMetadataUpsertDto': + return AssetMetadataUpsertDto.fromJson(value); + case 'AssetMetadataUpsertItemDto': + return AssetMetadataUpsertItemDto.fromJson(value); case 'AssetOrder': return AssetOrderTypeTransformer().decode(value); case 'AssetResponseDto': @@ -580,6 +588,10 @@ class ApiClient { return SyncAssetFaceDeleteV1.fromJson(value); case 'SyncAssetFaceV1': return SyncAssetFaceV1.fromJson(value); + case 'SyncAssetMetadataDeleteV1': + return SyncAssetMetadataDeleteV1.fromJson(value); + case 'SyncAssetMetadataV1': + return SyncAssetMetadataV1.fromJson(value); case 'SyncAssetV1': return SyncAssetV1.fromJson(value); case 'SyncAuthUserV1': diff --git a/mobile/openapi/lib/api_helper.dart b/mobile/openapi/lib/api_helper.dart index 4adb62768b..b34e9210c8 100644 --- a/mobile/openapi/lib/api_helper.dart +++ b/mobile/openapi/lib/api_helper.dart @@ -67,6 +67,9 @@ String parameterToString(dynamic value) { if (value is AssetMediaStatus) { return AssetMediaStatusTypeTransformer().encode(value).toString(); } + if (value is AssetMetadataKey) { + return AssetMetadataKeyTypeTransformer().encode(value).toString(); + } if (value is AssetOrder) { return AssetOrderTypeTransformer().encode(value).toString(); } diff --git a/mobile/openapi/lib/model/asset_metadata_key.dart b/mobile/openapi/lib/model/asset_metadata_key.dart new file mode 100644 index 0000000000..70186cd41c --- /dev/null +++ b/mobile/openapi/lib/model/asset_metadata_key.dart @@ -0,0 +1,82 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + + +class AssetMetadataKey { + /// Instantiate a new enum with the provided [value]. + const AssetMetadataKey._(this.value); + + /// The underlying value of this enum member. + final String value; + + @override + String toString() => value; + + String toJson() => value; + + static const mobileApp = AssetMetadataKey._(r'mobile-app'); + + /// List of all possible values in this [enum][AssetMetadataKey]. + static const values = [ + mobileApp, + ]; + + static AssetMetadataKey? fromJson(dynamic value) => AssetMetadataKeyTypeTransformer().decode(value); + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = AssetMetadataKey.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } +} + +/// Transformation class that can [encode] an instance of [AssetMetadataKey] to String, +/// and [decode] dynamic data back to [AssetMetadataKey]. +class AssetMetadataKeyTypeTransformer { + factory AssetMetadataKeyTypeTransformer() => _instance ??= const AssetMetadataKeyTypeTransformer._(); + + const AssetMetadataKeyTypeTransformer._(); + + String encode(AssetMetadataKey data) => data.value; + + /// Decodes a [dynamic value][data] to a AssetMetadataKey. + /// + /// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully, + /// then null is returned. However, if [allowNull] is false and the [dynamic value][data] + /// cannot be decoded successfully, then an [UnimplementedError] is thrown. + /// + /// The [allowNull] is very handy when an API changes and a new enum value is added or removed, + /// and users are still using an old app with the old code. + AssetMetadataKey? decode(dynamic data, {bool allowNull = true}) { + if (data != null) { + switch (data) { + case r'mobile-app': return AssetMetadataKey.mobileApp; + default: + if (!allowNull) { + throw ArgumentError('Unknown enum value to decode: $data'); + } + } + } + return null; + } + + /// Singleton [AssetMetadataKeyTypeTransformer] instance. + static AssetMetadataKeyTypeTransformer? _instance; +} + diff --git a/mobile/openapi/lib/model/asset_metadata_response_dto.dart b/mobile/openapi/lib/model/asset_metadata_response_dto.dart new file mode 100644 index 0000000000..af5769b9bb --- /dev/null +++ b/mobile/openapi/lib/model/asset_metadata_response_dto.dart @@ -0,0 +1,115 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class AssetMetadataResponseDto { + /// Returns a new [AssetMetadataResponseDto] instance. + AssetMetadataResponseDto({ + required this.key, + required this.updatedAt, + required this.value, + }); + + AssetMetadataKey key; + + DateTime updatedAt; + + Object value; + + @override + bool operator ==(Object other) => identical(this, other) || other is AssetMetadataResponseDto && + other.key == key && + other.updatedAt == updatedAt && + other.value == value; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (key.hashCode) + + (updatedAt.hashCode) + + (value.hashCode); + + @override + String toString() => 'AssetMetadataResponseDto[key=$key, updatedAt=$updatedAt, value=$value]'; + + Map toJson() { + final json = {}; + json[r'key'] = this.key; + json[r'updatedAt'] = this.updatedAt.toUtc().toIso8601String(); + json[r'value'] = this.value; + return json; + } + + /// Returns a new [AssetMetadataResponseDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static AssetMetadataResponseDto? fromJson(dynamic value) { + upgradeDto(value, "AssetMetadataResponseDto"); + if (value is Map) { + final json = value.cast(); + + return AssetMetadataResponseDto( + key: AssetMetadataKey.fromJson(json[r'key'])!, + updatedAt: mapDateTime(json, r'updatedAt', r'')!, + value: mapValueOfType(json, r'value')!, + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = AssetMetadataResponseDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = AssetMetadataResponseDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of AssetMetadataResponseDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = AssetMetadataResponseDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'key', + 'updatedAt', + 'value', + }; +} + diff --git a/mobile/openapi/lib/model/asset_metadata_upsert_dto.dart b/mobile/openapi/lib/model/asset_metadata_upsert_dto.dart new file mode 100644 index 0000000000..45d044feb0 --- /dev/null +++ b/mobile/openapi/lib/model/asset_metadata_upsert_dto.dart @@ -0,0 +1,99 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class AssetMetadataUpsertDto { + /// Returns a new [AssetMetadataUpsertDto] instance. + AssetMetadataUpsertDto({ + this.items = const [], + }); + + List items; + + @override + bool operator ==(Object other) => identical(this, other) || other is AssetMetadataUpsertDto && + _deepEquality.equals(other.items, items); + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (items.hashCode); + + @override + String toString() => 'AssetMetadataUpsertDto[items=$items]'; + + Map toJson() { + final json = {}; + json[r'items'] = this.items; + return json; + } + + /// Returns a new [AssetMetadataUpsertDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static AssetMetadataUpsertDto? fromJson(dynamic value) { + upgradeDto(value, "AssetMetadataUpsertDto"); + if (value is Map) { + final json = value.cast(); + + return AssetMetadataUpsertDto( + items: AssetMetadataUpsertItemDto.listFromJson(json[r'items']), + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = AssetMetadataUpsertDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = AssetMetadataUpsertDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of AssetMetadataUpsertDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = AssetMetadataUpsertDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'items', + }; +} + diff --git a/mobile/openapi/lib/model/asset_metadata_upsert_item_dto.dart b/mobile/openapi/lib/model/asset_metadata_upsert_item_dto.dart new file mode 100644 index 0000000000..4b7e6579a1 --- /dev/null +++ b/mobile/openapi/lib/model/asset_metadata_upsert_item_dto.dart @@ -0,0 +1,107 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class AssetMetadataUpsertItemDto { + /// Returns a new [AssetMetadataUpsertItemDto] instance. + AssetMetadataUpsertItemDto({ + required this.key, + required this.value, + }); + + AssetMetadataKey key; + + Object value; + + @override + bool operator ==(Object other) => identical(this, other) || other is AssetMetadataUpsertItemDto && + other.key == key && + other.value == value; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (key.hashCode) + + (value.hashCode); + + @override + String toString() => 'AssetMetadataUpsertItemDto[key=$key, value=$value]'; + + Map toJson() { + final json = {}; + json[r'key'] = this.key; + json[r'value'] = this.value; + return json; + } + + /// Returns a new [AssetMetadataUpsertItemDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static AssetMetadataUpsertItemDto? fromJson(dynamic value) { + upgradeDto(value, "AssetMetadataUpsertItemDto"); + if (value is Map) { + final json = value.cast(); + + return AssetMetadataUpsertItemDto( + key: AssetMetadataKey.fromJson(json[r'key'])!, + value: mapValueOfType(json, r'value')!, + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = AssetMetadataUpsertItemDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = AssetMetadataUpsertItemDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of AssetMetadataUpsertItemDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = AssetMetadataUpsertItemDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'key', + 'value', + }; +} + diff --git a/mobile/openapi/lib/model/sync_asset_metadata_delete_v1.dart b/mobile/openapi/lib/model/sync_asset_metadata_delete_v1.dart new file mode 100644 index 0000000000..c9a7ef4670 --- /dev/null +++ b/mobile/openapi/lib/model/sync_asset_metadata_delete_v1.dart @@ -0,0 +1,107 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class SyncAssetMetadataDeleteV1 { + /// Returns a new [SyncAssetMetadataDeleteV1] instance. + SyncAssetMetadataDeleteV1({ + required this.assetId, + required this.key, + }); + + String assetId; + + AssetMetadataKey key; + + @override + bool operator ==(Object other) => identical(this, other) || other is SyncAssetMetadataDeleteV1 && + other.assetId == assetId && + other.key == key; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (assetId.hashCode) + + (key.hashCode); + + @override + String toString() => 'SyncAssetMetadataDeleteV1[assetId=$assetId, key=$key]'; + + Map toJson() { + final json = {}; + json[r'assetId'] = this.assetId; + json[r'key'] = this.key; + return json; + } + + /// Returns a new [SyncAssetMetadataDeleteV1] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static SyncAssetMetadataDeleteV1? fromJson(dynamic value) { + upgradeDto(value, "SyncAssetMetadataDeleteV1"); + if (value is Map) { + final json = value.cast(); + + return SyncAssetMetadataDeleteV1( + assetId: mapValueOfType(json, r'assetId')!, + key: AssetMetadataKey.fromJson(json[r'key'])!, + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = SyncAssetMetadataDeleteV1.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = SyncAssetMetadataDeleteV1.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of SyncAssetMetadataDeleteV1-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = SyncAssetMetadataDeleteV1.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'assetId', + 'key', + }; +} + diff --git a/mobile/openapi/lib/model/sync_asset_metadata_v1.dart b/mobile/openapi/lib/model/sync_asset_metadata_v1.dart new file mode 100644 index 0000000000..720fcef947 --- /dev/null +++ b/mobile/openapi/lib/model/sync_asset_metadata_v1.dart @@ -0,0 +1,115 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class SyncAssetMetadataV1 { + /// Returns a new [SyncAssetMetadataV1] instance. + SyncAssetMetadataV1({ + required this.assetId, + required this.key, + required this.value, + }); + + String assetId; + + AssetMetadataKey key; + + Object value; + + @override + bool operator ==(Object other) => identical(this, other) || other is SyncAssetMetadataV1 && + other.assetId == assetId && + other.key == key && + other.value == value; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (assetId.hashCode) + + (key.hashCode) + + (value.hashCode); + + @override + String toString() => 'SyncAssetMetadataV1[assetId=$assetId, key=$key, value=$value]'; + + Map toJson() { + final json = {}; + json[r'assetId'] = this.assetId; + json[r'key'] = this.key; + json[r'value'] = this.value; + return json; + } + + /// Returns a new [SyncAssetMetadataV1] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static SyncAssetMetadataV1? fromJson(dynamic value) { + upgradeDto(value, "SyncAssetMetadataV1"); + if (value is Map) { + final json = value.cast(); + + return SyncAssetMetadataV1( + assetId: mapValueOfType(json, r'assetId')!, + key: AssetMetadataKey.fromJson(json[r'key'])!, + value: mapValueOfType(json, r'value')!, + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = SyncAssetMetadataV1.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = SyncAssetMetadataV1.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of SyncAssetMetadataV1-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = SyncAssetMetadataV1.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'assetId', + 'key', + 'value', + }; +} + diff --git a/mobile/openapi/lib/model/sync_entity_type.dart b/mobile/openapi/lib/model/sync_entity_type.dart index f259fdc9d9..1a86b870e1 100644 --- a/mobile/openapi/lib/model/sync_entity_type.dart +++ b/mobile/openapi/lib/model/sync_entity_type.dart @@ -29,6 +29,8 @@ class SyncEntityType { static const assetV1 = SyncEntityType._(r'AssetV1'); static const assetDeleteV1 = SyncEntityType._(r'AssetDeleteV1'); static const assetExifV1 = SyncEntityType._(r'AssetExifV1'); + static const assetMetadataV1 = SyncEntityType._(r'AssetMetadataV1'); + static const assetMetadataDeleteV1 = SyncEntityType._(r'AssetMetadataDeleteV1'); static const partnerV1 = SyncEntityType._(r'PartnerV1'); static const partnerDeleteV1 = SyncEntityType._(r'PartnerDeleteV1'); static const partnerAssetV1 = SyncEntityType._(r'PartnerAssetV1'); @@ -76,6 +78,8 @@ class SyncEntityType { assetV1, assetDeleteV1, assetExifV1, + assetMetadataV1, + assetMetadataDeleteV1, partnerV1, partnerDeleteV1, partnerAssetV1, @@ -158,6 +162,8 @@ class SyncEntityTypeTypeTransformer { case r'AssetV1': return SyncEntityType.assetV1; case r'AssetDeleteV1': return SyncEntityType.assetDeleteV1; case r'AssetExifV1': return SyncEntityType.assetExifV1; + case r'AssetMetadataV1': return SyncEntityType.assetMetadataV1; + case r'AssetMetadataDeleteV1': return SyncEntityType.assetMetadataDeleteV1; case r'PartnerV1': return SyncEntityType.partnerV1; case r'PartnerDeleteV1': return SyncEntityType.partnerDeleteV1; case r'PartnerAssetV1': return SyncEntityType.partnerAssetV1; diff --git a/mobile/openapi/lib/model/sync_request_type.dart b/mobile/openapi/lib/model/sync_request_type.dart index 8a1857366e..c3dc1c4d61 100644 --- a/mobile/openapi/lib/model/sync_request_type.dart +++ b/mobile/openapi/lib/model/sync_request_type.dart @@ -30,6 +30,7 @@ class SyncRequestType { static const albumAssetExifsV1 = SyncRequestType._(r'AlbumAssetExifsV1'); static const assetsV1 = SyncRequestType._(r'AssetsV1'); static const assetExifsV1 = SyncRequestType._(r'AssetExifsV1'); + static const assetMetadataV1 = SyncRequestType._(r'AssetMetadataV1'); static const authUsersV1 = SyncRequestType._(r'AuthUsersV1'); static const memoriesV1 = SyncRequestType._(r'MemoriesV1'); static const memoryToAssetsV1 = SyncRequestType._(r'MemoryToAssetsV1'); @@ -52,6 +53,7 @@ class SyncRequestType { albumAssetExifsV1, assetsV1, assetExifsV1, + assetMetadataV1, authUsersV1, memoriesV1, memoryToAssetsV1, @@ -109,6 +111,7 @@ class SyncRequestTypeTypeTransformer { case r'AlbumAssetExifsV1': return SyncRequestType.albumAssetExifsV1; case r'AssetsV1': return SyncRequestType.assetsV1; case r'AssetExifsV1': return SyncRequestType.assetExifsV1; + case r'AssetMetadataV1': return SyncRequestType.assetMetadataV1; case r'AuthUsersV1': return SyncRequestType.authUsersV1; case r'MemoriesV1': return SyncRequestType.memoriesV1; case r'MemoryToAssetsV1': return SyncRequestType.memoryToAssetsV1; diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index eb9b6ac5a9..44b4e0da4f 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -2245,6 +2245,203 @@ "description": "This endpoint requires the `asset.update` permission." } }, + "/assets/{id}/metadata": { + "get": { + "operationId": "getAssetMetadata", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/AssetMetadataResponseDto" + }, + "type": "array" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Assets" + ], + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." + }, + "put": { + "operationId": "updateAssetMetadata", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AssetMetadataUpsertDto" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/AssetMetadataResponseDto" + }, + "type": "array" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Assets" + ], + "x-immich-permission": "asset.update", + "description": "This endpoint requires the `asset.update` permission." + } + }, + "/assets/{id}/metadata/{key}": { + "delete": { + "operationId": "deleteAssetMetadata", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "type": "string" + } + }, + { + "name": "key", + "required": true, + "in": "path", + "schema": { + "$ref": "#/components/schemas/AssetMetadataKey" + } + } + ], + "responses": { + "204": { + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Assets" + ], + "x-immich-permission": "asset.update", + "description": "This endpoint requires the `asset.update` permission." + }, + "get": { + "operationId": "getAssetMetadataByKey", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "type": "string" + } + }, + { + "name": "key", + "required": true, + "in": "path", + "schema": { + "$ref": "#/components/schemas/AssetMetadataKey" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AssetMetadataResponseDto" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Assets" + ], + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." + } + }, "/assets/{id}/original": { "get": { "operationId": "downloadAsset", @@ -10615,6 +10812,12 @@ "format": "uuid", "type": "string" }, + "metadata": { + "items": { + "$ref": "#/components/schemas/AssetMetadataUpsertItemDto" + }, + "type": "array" + }, "sidecarData": { "format": "binary", "type": "string" @@ -10632,7 +10835,8 @@ "deviceAssetId", "deviceId", "fileCreatedAt", - "fileModifiedAt" + "fileModifiedAt", + "metadata" ], "type": "object" }, @@ -10707,6 +10911,69 @@ ], "type": "string" }, + "AssetMetadataKey": { + "enum": [ + "mobile-app" + ], + "type": "string" + }, + "AssetMetadataResponseDto": { + "properties": { + "key": { + "allOf": [ + { + "$ref": "#/components/schemas/AssetMetadataKey" + } + ] + }, + "updatedAt": { + "format": "date-time", + "type": "string" + }, + "value": { + "type": "object" + } + }, + "required": [ + "key", + "updatedAt", + "value" + ], + "type": "object" + }, + "AssetMetadataUpsertDto": { + "properties": { + "items": { + "items": { + "$ref": "#/components/schemas/AssetMetadataUpsertItemDto" + }, + "type": "array" + } + }, + "required": [ + "items" + ], + "type": "object" + }, + "AssetMetadataUpsertItemDto": { + "properties": { + "key": { + "allOf": [ + { + "$ref": "#/components/schemas/AssetMetadataKey" + } + ] + }, + "value": { + "type": "object" + } + }, + "required": [ + "key", + "value" + ], + "type": "object" + }, "AssetOrder": { "enum": [ "asc", @@ -14944,6 +15211,48 @@ ], "type": "object" }, + "SyncAssetMetadataDeleteV1": { + "properties": { + "assetId": { + "type": "string" + }, + "key": { + "allOf": [ + { + "$ref": "#/components/schemas/AssetMetadataKey" + } + ] + } + }, + "required": [ + "assetId", + "key" + ], + "type": "object" + }, + "SyncAssetMetadataV1": { + "properties": { + "assetId": { + "type": "string" + }, + "key": { + "allOf": [ + { + "$ref": "#/components/schemas/AssetMetadataKey" + } + ] + }, + "value": { + "type": "object" + } + }, + "required": [ + "assetId", + "key", + "value" + ], + "type": "object" + }, "SyncAssetV1": { "properties": { "checksum": { @@ -15114,6 +15423,8 @@ "AssetV1", "AssetDeleteV1", "AssetExifV1", + "AssetMetadataV1", + "AssetMetadataDeleteV1", "PartnerV1", "PartnerDeleteV1", "PartnerAssetV1", @@ -15373,6 +15684,7 @@ "AlbumAssetExifsV1", "AssetsV1", "AssetExifsV1", + "AssetMetadataV1", "AuthUsersV1", "MemoriesV1", "MemoryToAssetsV1", diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 08fa714823..3213b5e240 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -447,6 +447,10 @@ export type AssetBulkDeleteDto = { force?: boolean; ids: string[]; }; +export type AssetMetadataUpsertItemDto = { + key: AssetMetadataKey; + value: object; +}; export type AssetMediaCreateDto = { assetData: Blob; deviceAssetId: string; @@ -457,6 +461,7 @@ export type AssetMediaCreateDto = { filename?: string; isFavorite?: boolean; livePhotoVideoId?: string; + metadata: AssetMetadataUpsertItemDto[]; sidecarData?: Blob; visibility?: AssetVisibility; }; @@ -516,6 +521,14 @@ export type UpdateAssetDto = { rating?: number; visibility?: AssetVisibility; }; +export type AssetMetadataResponseDto = { + key: AssetMetadataKey; + updatedAt: string; + value: object; +}; +export type AssetMetadataUpsertDto = { + items: AssetMetadataUpsertItemDto[]; +}; export type AssetMediaReplaceDto = { assetData: Blob; deviceAssetId: string; @@ -2273,6 +2286,61 @@ export function updateAsset({ id, updateAssetDto }: { body: updateAssetDto }))); } +/** + * This endpoint requires the `asset.read` permission. + */ +export function getAssetMetadata({ id }: { + id: string; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: AssetMetadataResponseDto[]; + }>(`/assets/${encodeURIComponent(id)}/metadata`, { + ...opts + })); +} +/** + * This endpoint requires the `asset.update` permission. + */ +export function updateAssetMetadata({ id, assetMetadataUpsertDto }: { + id: string; + assetMetadataUpsertDto: AssetMetadataUpsertDto; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: AssetMetadataResponseDto[]; + }>(`/assets/${encodeURIComponent(id)}/metadata`, oazapfts.json({ + ...opts, + method: "PUT", + body: assetMetadataUpsertDto + }))); +} +/** + * This endpoint requires the `asset.update` permission. + */ +export function deleteAssetMetadata({ id, key }: { + id: string; + key: AssetMetadataKey; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchText(`/assets/${encodeURIComponent(id)}/metadata/${encodeURIComponent(key)}`, { + ...opts, + method: "DELETE" + })); +} +/** + * This endpoint requires the `asset.read` permission. + */ +export function getAssetMetadataByKey({ id, key }: { + id: string; + key: AssetMetadataKey; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: AssetMetadataResponseDto; + }>(`/assets/${encodeURIComponent(id)}/metadata/${encodeURIComponent(key)}`, { + ...opts + })); +} /** * This endpoint requires the `asset.download` permission. */ @@ -4725,6 +4793,9 @@ export enum Permission { AdminUserDelete = "adminUser.delete", AdminAuthUnlinkAll = "adminAuth.unlinkAll" } +export enum AssetMetadataKey { + MobileApp = "mobile-app" +} export enum AssetMediaStatus { Created = "created", Replaced = "replaced", @@ -4811,6 +4882,8 @@ export enum SyncEntityType { AssetV1 = "AssetV1", AssetDeleteV1 = "AssetDeleteV1", AssetExifV1 = "AssetExifV1", + AssetMetadataV1 = "AssetMetadataV1", + AssetMetadataDeleteV1 = "AssetMetadataDeleteV1", PartnerV1 = "PartnerV1", PartnerDeleteV1 = "PartnerDeleteV1", PartnerAssetV1 = "PartnerAssetV1", @@ -4858,6 +4931,7 @@ export enum SyncRequestType { AlbumAssetExifsV1 = "AlbumAssetExifsV1", AssetsV1 = "AssetsV1", AssetExifsV1 = "AssetExifsV1", + AssetMetadataV1 = "AssetMetadataV1", AuthUsersV1 = "AuthUsersV1", MemoriesV1 = "MemoriesV1", MemoryToAssetsV1 = "MemoryToAssetsV1", diff --git a/server/src/controllers/asset.controller.spec.ts b/server/src/controllers/asset.controller.spec.ts index 66d2d7c206..7a7a37fe2e 100644 --- a/server/src/controllers/asset.controller.spec.ts +++ b/server/src/controllers/asset.controller.spec.ts @@ -1,4 +1,5 @@ import { AssetController } from 'src/controllers/asset.controller'; +import { AssetMetadataKey } from 'src/enum'; import { AssetService } from 'src/services/asset.service'; import request from 'supertest'; import { factory } from 'test/small.factory'; @@ -6,14 +7,16 @@ import { ControllerContext, controllerSetup, mockBaseService } from 'test/utils' describe(AssetController.name, () => { let ctx: ControllerContext; + const service = mockBaseService(AssetService); beforeAll(async () => { - ctx = await controllerSetup(AssetController, [{ provide: AssetService, useValue: mockBaseService(AssetService) }]); + ctx = await controllerSetup(AssetController, [{ provide: AssetService, useValue: service }]); return () => ctx.close(); }); beforeEach(() => { ctx.reset(); + service.resetAllMocks(); }); describe('PUT /assets', () => { @@ -115,4 +118,120 @@ describe(AssetController.name, () => { ); }); }); + + describe('GET /assets/:id/metadata', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get(`/assets/${factory.uuid()}/metadata`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('PUT /assets/:id/metadata', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).put(`/assets/${factory.uuid()}/metadata`).send({ items: [] }); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require a valid id', async () => { + const { status, body } = await request(ctx.getHttpServer()).put(`/assets/123/metadata`).send({ items: [] }); + expect(status).toBe(400); + expect(body).toEqual(factory.responses.badRequest(expect.arrayContaining(['id must be a UUID']))); + }); + + it('should require items to be an array', async () => { + const { status, body } = await request(ctx.getHttpServer()).put(`/assets/${factory.uuid()}/metadata`).send({}); + expect(status).toBe(400); + expect(body).toEqual(factory.responses.badRequest(['items must be an array'])); + }); + + it('should require each item to have a valid key', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .put(`/assets/${factory.uuid()}/metadata`) + .send({ items: [{ key: 'someKey' }] }); + expect(status).toBe(400); + expect(body).toEqual( + factory.responses.badRequest( + expect.arrayContaining([expect.stringContaining('items.0.key must be one of the following values')]), + ), + ); + }); + + it('should require each item to have a value', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .put(`/assets/${factory.uuid()}/metadata`) + .send({ items: [{ key: 'mobile-app', value: null }] }); + expect(status).toBe(400); + expect(body).toEqual( + factory.responses.badRequest(expect.arrayContaining([expect.stringContaining('value must be an object')])), + ); + }); + + describe(AssetMetadataKey.MobileApp, () => { + it('should accept valid data and pass to service correctly', async () => { + const assetId = factory.uuid(); + const { status } = await request(ctx.getHttpServer()) + .put(`/assets/${assetId}/metadata`) + .send({ items: [{ key: 'mobile-app', value: { iCloudId: '123' } }] }); + expect(service.upsertMetadata).toHaveBeenCalledWith(undefined, assetId, { + items: [{ key: 'mobile-app', value: { iCloudId: '123' } }], + }); + expect(status).toBe(200); + }); + + it('should work without iCloudId', async () => { + const assetId = factory.uuid(); + const { status } = await request(ctx.getHttpServer()) + .put(`/assets/${assetId}/metadata`) + .send({ items: [{ key: 'mobile-app', value: {} }] }); + expect(service.upsertMetadata).toHaveBeenCalledWith(undefined, assetId, { + items: [{ key: 'mobile-app', value: {} }], + }); + expect(status).toBe(200); + }); + }); + }); + + describe('GET /assets/:id/metadata/:key', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get(`/assets/${factory.uuid()}/metadata/mobile-app`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require a valid id', async () => { + const { status, body } = await request(ctx.getHttpServer()).get(`/assets/123/metadata/mobile-app`); + expect(status).toBe(400); + expect(body).toEqual(factory.responses.badRequest(expect.arrayContaining(['id must be a UUID']))); + }); + + it('should require a valid key', async () => { + const { status, body } = await request(ctx.getHttpServer()).get(`/assets/${factory.uuid()}/metadata/invalid`); + expect(status).toBe(400); + expect(body).toEqual( + factory.responses.badRequest( + expect.arrayContaining([expect.stringContaining('key must be one of the following value')]), + ), + ); + }); + }); + + describe('DELETE /assets/:id/metadata/:key', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).delete(`/assets/${factory.uuid()}/metadata/mobile-app`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require a valid id', async () => { + const { status, body } = await request(ctx.getHttpServer()).delete(`/assets/123/metadata/mobile-app`); + expect(status).toBe(400); + expect(body).toEqual(factory.responses.badRequest(['id must be a UUID'])); + }); + + it('should require a valid key', async () => { + const { status, body } = await request(ctx.getHttpServer()).delete(`/assets/${factory.uuid()}/metadata/invalid`); + expect(status).toBe(400); + expect(body).toEqual( + factory.responses.badRequest([expect.stringContaining('key must be one of the following values')]), + ); + }); + }); }); diff --git a/server/src/controllers/asset.controller.ts b/server/src/controllers/asset.controller.ts index edb5aab602..1f320f6595 100644 --- a/server/src/controllers/asset.controller.ts +++ b/server/src/controllers/asset.controller.ts @@ -6,6 +6,9 @@ import { AssetBulkDeleteDto, AssetBulkUpdateDto, AssetJobsDto, + AssetMetadataResponseDto, + AssetMetadataRouteParams, + AssetMetadataUpsertDto, AssetStatsDto, AssetStatsResponseDto, DeviceIdDto, @@ -85,4 +88,36 @@ export class AssetController { ): Promise { return this.service.update(auth, id, dto); } + + @Get(':id/metadata') + @Authenticated({ permission: Permission.AssetRead }) + getAssetMetadata(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { + return this.service.getMetadata(auth, id); + } + + @Put(':id/metadata') + @Authenticated({ permission: Permission.AssetUpdate }) + updateAssetMetadata( + @Auth() auth: AuthDto, + @Param() { id }: UUIDParamDto, + @Body() dto: AssetMetadataUpsertDto, + ): Promise { + return this.service.upsertMetadata(auth, id, dto); + } + + @Get(':id/metadata/:key') + @Authenticated({ permission: Permission.AssetRead }) + getAssetMetadataByKey( + @Auth() auth: AuthDto, + @Param() { id, key }: AssetMetadataRouteParams, + ): Promise { + return this.service.getMetadataByKey(auth, id, key); + } + + @Delete(':id/metadata/:key') + @Authenticated({ permission: Permission.AssetUpdate }) + @HttpCode(HttpStatus.NO_CONTENT) + deleteAssetMetadata(@Auth() auth: AuthDto, @Param() { id, key }: AssetMetadataRouteParams): Promise { + return this.service.deleteMetadataByKey(auth, id, key); + } } diff --git a/server/src/dtos/asset-media.dto.ts b/server/src/dtos/asset-media.dto.ts index ea86e087d8..25395000cd 100644 --- a/server/src/dtos/asset-media.dto.ts +++ b/server/src/dtos/asset-media.dto.ts @@ -1,6 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { ArrayNotEmpty, IsArray, IsNotEmpty, IsString, ValidateNested } from 'class-validator'; +import { AssetMetadataUpsertItemDto } from 'src/dtos/asset.dto'; import { AssetVisibility } from 'src/enum'; import { Optional, ValidateBoolean, ValidateDate, ValidateEnum, ValidateUUID } from 'src/validation'; @@ -64,6 +65,12 @@ export class AssetMediaCreateDto extends AssetMediaBase { @ValidateUUID({ optional: true }) livePhotoVideoId?: string; + @Optional() + @IsArray() + @ValidateNested({ each: true }) + @Type(() => AssetMetadataUpsertItemDto) + metadata!: AssetMetadataUpsertItemDto[]; + @ApiProperty({ type: 'string', format: 'binary', required: false }) [UploadFieldName.SIDECAR_DATA]?: any; } diff --git a/server/src/dtos/asset.dto.ts b/server/src/dtos/asset.dto.ts index 31e5679e76..6a89b7e2cf 100644 --- a/server/src/dtos/asset.dto.ts +++ b/server/src/dtos/asset.dto.ts @@ -1,21 +1,25 @@ import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { + IsArray, IsDateString, IsInt, IsLatitude, IsLongitude, IsNotEmpty, + IsObject, IsPositive, IsString, IsTimeZone, Max, Min, ValidateIf, + ValidateNested, } from 'class-validator'; import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; -import { AssetType, AssetVisibility } from 'src/enum'; +import { AssetMetadataKey, AssetType, AssetVisibility } from 'src/enum'; import { AssetStats } from 'src/repositories/asset.repository'; +import { AssetMetadata, AssetMetadataItem } from 'src/types'; import { IsNotSiblingOf, Optional, ValidateBoolean, ValidateEnum, ValidateUUID } from 'src/validation'; export class DeviceIdDto { @@ -135,6 +139,53 @@ export class AssetStatsResponseDto { total!: number; } +export class AssetMetadataRouteParams { + @ValidateUUID() + id!: string; + + @ValidateEnum({ enum: AssetMetadataKey, name: 'AssetMetadataKey' }) + key!: AssetMetadataKey; +} + +export class AssetMetadataUpsertDto { + @IsArray() + @ValidateNested({ each: true }) + @Type(() => AssetMetadataUpsertItemDto) + items!: AssetMetadataUpsertItemDto[]; +} + +export class AssetMetadataUpsertItemDto implements AssetMetadataItem { + @ValidateEnum({ enum: AssetMetadataKey, name: 'AssetMetadataKey' }) + key!: AssetMetadataKey; + + @IsObject() + @ValidateNested() + @Type((options) => { + switch (options?.object.key) { + case AssetMetadataKey.MobileApp: { + return AssetMetadataMobileAppDto; + } + default: { + return Object; + } + } + }) + value!: AssetMetadata[AssetMetadataKey]; +} + +export class AssetMetadataMobileAppDto { + @IsString() + @Optional() + iCloudId?: string; +} + +export class AssetMetadataResponseDto { + @ValidateEnum({ enum: AssetMetadataKey, name: 'AssetMetadataKey' }) + key!: AssetMetadataKey; + value!: object; + updatedAt!: Date; +} + export const mapStats = (stats: AssetStats): AssetStatsResponseDto => { return { images: stats[AssetType.Image], diff --git a/server/src/dtos/sync.dto.ts b/server/src/dtos/sync.dto.ts index 9ac85755ab..0fae619e0f 100644 --- a/server/src/dtos/sync.dto.ts +++ b/server/src/dtos/sync.dto.ts @@ -4,6 +4,7 @@ import { ArrayMaxSize, IsInt, IsPositive, IsString } from 'class-validator'; import { AssetResponseDto } from 'src/dtos/asset-response.dto'; import { AlbumUserRole, + AssetMetadataKey, AssetOrder, AssetType, AssetVisibility, @@ -162,6 +163,21 @@ export class SyncAssetExifV1 { fps!: number | null; } +@ExtraModel() +export class SyncAssetMetadataV1 { + assetId!: string; + @ValidateEnum({ enum: AssetMetadataKey, name: 'AssetMetadataKey' }) + key!: AssetMetadataKey; + value!: object; +} + +@ExtraModel() +export class SyncAssetMetadataDeleteV1 { + assetId!: string; + @ValidateEnum({ enum: AssetMetadataKey, name: 'AssetMetadataKey' }) + key!: AssetMetadataKey; +} + @ExtraModel() export class SyncAlbumDeleteV1 { albumId!: string; @@ -328,6 +344,8 @@ export type SyncItem = { [SyncEntityType.PartnerDeleteV1]: SyncPartnerDeleteV1; [SyncEntityType.AssetV1]: SyncAssetV1; [SyncEntityType.AssetDeleteV1]: SyncAssetDeleteV1; + [SyncEntityType.AssetMetadataV1]: SyncAssetMetadataV1; + [SyncEntityType.AssetMetadataDeleteV1]: SyncAssetMetadataDeleteV1; [SyncEntityType.AssetExifV1]: SyncAssetExifV1; [SyncEntityType.PartnerAssetV1]: SyncAssetV1; [SyncEntityType.PartnerAssetBackfillV1]: SyncAssetV1; diff --git a/server/src/enum.ts b/server/src/enum.ts index 02ef222883..bf72b24a14 100644 --- a/server/src/enum.ts +++ b/server/src/enum.ts @@ -276,6 +276,10 @@ export enum UserMetadataKey { Onboarding = 'onboarding', } +export enum AssetMetadataKey { + MobileApp = 'mobile-app', +} + export enum UserAvatarColor { Primary = 'primary', Pink = 'pink', @@ -627,6 +631,7 @@ export enum SyncRequestType { AlbumAssetExifsV1 = 'AlbumAssetExifsV1', AssetsV1 = 'AssetsV1', AssetExifsV1 = 'AssetExifsV1', + AssetMetadataV1 = 'AssetMetadataV1', AuthUsersV1 = 'AuthUsersV1', MemoriesV1 = 'MemoriesV1', MemoryToAssetsV1 = 'MemoryToAssetsV1', @@ -650,6 +655,8 @@ export enum SyncEntityType { AssetV1 = 'AssetV1', AssetDeleteV1 = 'AssetDeleteV1', AssetExifV1 = 'AssetExifV1', + AssetMetadataV1 = 'AssetMetadataV1', + AssetMetadataDeleteV1 = 'AssetMetadataDeleteV1', PartnerV1 = 'PartnerV1', PartnerDeleteV1 = 'PartnerDeleteV1', diff --git a/server/src/queries/asset.repository.sql b/server/src/queries/asset.repository.sql index e2bc80eabe..1283ff0a66 100644 --- a/server/src/queries/asset.repository.sql +++ b/server/src/queries/asset.repository.sql @@ -19,6 +19,33 @@ returning "dateTimeOriginal", "timeZone" +-- AssetRepository.getMetadata +select + "key", + "value", + "updatedAt" +from + "asset_metadata" +where + "assetId" = $1 + +-- AssetRepository.getMetadataByKey +select + "key", + "value", + "updatedAt" +from + "asset_metadata" +where + "assetId" = $1 + and "key" = $2 + +-- AssetRepository.deleteMetadataByKey +delete from "asset_metadata" +where + "assetId" = $1 + and "key" = $2 + -- AssetRepository.getByDayOfYear with "res" as ( diff --git a/server/src/queries/sync.repository.sql b/server/src/queries/sync.repository.sql index 80021368a0..3e70baa5d4 100644 --- a/server/src/queries/sync.repository.sql +++ b/server/src/queries/sync.repository.sql @@ -539,6 +539,37 @@ where order by "asset_face"."updateId" asc +-- SyncRepository.assetMetadata.getDeletes +select + "asset_metadata_audit"."id", + "assetId", + "key" +from + "asset_metadata_audit" as "asset_metadata_audit" + left join "asset" on "asset"."id" = "asset_metadata_audit"."assetId" +where + "asset_metadata_audit"."id" < $1 + and "asset_metadata_audit"."id" > $2 + and "asset"."ownerId" = $3 +order by + "asset_metadata_audit"."id" asc + +-- SyncRepository.assetMetadata.getUpserts +select + "assetId", + "key", + "value", + "asset_metadata"."updateId" +from + "asset_metadata" as "asset_metadata" + inner join "asset" on "asset"."id" = "asset_metadata"."assetId" +where + "asset_metadata"."updateId" < $1 + and "asset_metadata"."updateId" > $2 + and "asset"."ownerId" = $3 +order by + "asset_metadata"."updateId" asc + -- SyncRepository.authUser.getUpserts select "id", diff --git a/server/src/repositories/asset.repository.ts b/server/src/repositories/asset.repository.ts index 6752d7bf62..ae595e35ae 100644 --- a/server/src/repositories/asset.repository.ts +++ b/server/src/repositories/asset.repository.ts @@ -1,15 +1,16 @@ import { Injectable } from '@nestjs/common'; -import { Insertable, Kysely, NotNull, Selectable, UpdateResult, Updateable, sql } from 'kysely'; +import { Insertable, Kysely, NotNull, Selectable, sql, Updateable, UpdateResult } from 'kysely'; import { isEmpty, isUndefined, omitBy } from 'lodash'; import { InjectKysely } from 'nestjs-kysely'; import { Stack } from 'src/database'; import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators'; -import { AssetFileType, AssetOrder, AssetStatus, AssetType, AssetVisibility } from 'src/enum'; +import { AssetFileType, AssetMetadataKey, AssetOrder, AssetStatus, AssetType, AssetVisibility } from 'src/enum'; import { DB } from 'src/schema'; import { AssetExifTable } from 'src/schema/tables/asset-exif.table'; import { AssetFileTable } from 'src/schema/tables/asset-file.table'; import { AssetJobStatusTable } from 'src/schema/tables/asset-job-status.table'; import { AssetTable } from 'src/schema/tables/asset.table'; +import { AssetMetadataItem } from 'src/types'; import { anyUuid, asUuid, @@ -210,6 +211,43 @@ export class AssetRepository { .execute(); } + @GenerateSql({ params: [DummyValue.UUID] }) + getMetadata(assetId: string) { + return this.db + .selectFrom('asset_metadata') + .select(['key', 'value', 'updatedAt']) + .where('assetId', '=', assetId) + .execute(); + } + + upsertMetadata(id: string, items: AssetMetadataItem[]) { + return this.db + .insertInto('asset_metadata') + .values(items.map((item) => ({ assetId: id, ...item }))) + .onConflict((oc) => + oc + .columns(['assetId', 'key']) + .doUpdateSet((eb) => ({ key: eb.ref('excluded.key'), value: eb.ref('excluded.value') })), + ) + .returning(['key', 'value', 'updatedAt']) + .execute(); + } + + @GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING] }) + getMetadataByKey(assetId: string, key: AssetMetadataKey) { + return this.db + .selectFrom('asset_metadata') + .select(['key', 'value', 'updatedAt']) + .where('assetId', '=', assetId) + .where('key', '=', key) + .executeTakeFirst(); + } + + @GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING] }) + async deleteMetadataByKey(id: string, key: AssetMetadataKey) { + await this.db.deleteFrom('asset_metadata').where('assetId', '=', id).where('key', '=', key).execute(); + } + create(asset: Insertable) { return this.db.insertInto('asset').values(asset).returningAll().executeTakeFirstOrThrow(); } diff --git a/server/src/repositories/sync.repository.ts b/server/src/repositories/sync.repository.ts index 13e933fd2f..398d49bd5d 100644 --- a/server/src/repositories/sync.repository.ts +++ b/server/src/repositories/sync.repository.ts @@ -54,6 +54,7 @@ export class SyncRepository { asset: AssetSync; assetExif: AssetExifSync; assetFace: AssetFaceSync; + assetMetadata: AssetMetadataSync; authUser: AuthUserSync; memory: MemorySync; memoryToAsset: MemoryToAssetSync; @@ -75,6 +76,7 @@ export class SyncRepository { this.asset = new AssetSync(this.db); this.assetExif = new AssetExifSync(this.db); this.assetFace = new AssetFaceSync(this.db); + this.assetMetadata = new AssetMetadataSync(this.db); this.authUser = new AuthUserSync(this.db); this.memory = new MemorySync(this.db); this.memoryToAsset = new MemoryToAssetSync(this.db); @@ -685,3 +687,23 @@ class UserMetadataSync extends BaseSync { .stream(); } } + +class AssetMetadataSync extends BaseSync { + @GenerateSql({ params: [dummyQueryOptions, DummyValue.UUID], stream: true }) + getDeletes(options: SyncQueryOptions, userId: string) { + return this.auditQuery('asset_metadata_audit', options) + .select(['asset_metadata_audit.id', 'assetId', 'key']) + .leftJoin('asset', 'asset.id', 'asset_metadata_audit.assetId') + .where('asset.ownerId', '=', userId) + .stream(); + } + + @GenerateSql({ params: [dummyQueryOptions, DummyValue.UUID], stream: true }) + getUpserts(options: SyncQueryOptions, userId: string) { + return this.upsertQuery('asset_metadata', options) + .select(['assetId', 'key', 'value', 'asset_metadata.updateId']) + .innerJoin('asset', 'asset.id', 'asset_metadata.assetId') + .where('asset.ownerId', '=', userId) + .stream(); + } +} diff --git a/server/src/repositories/user.repository.ts b/server/src/repositories/user.repository.ts index a63a4cc553..44f4a2bb9c 100644 --- a/server/src/repositories/user.repository.ts +++ b/server/src/repositories/user.repository.ts @@ -7,13 +7,10 @@ import { columns } from 'src/database'; import { DummyValue, GenerateSql } from 'src/decorators'; import { AssetType, AssetVisibility, UserStatus } from 'src/enum'; import { DB } from 'src/schema'; -import { UserMetadataTable } from 'src/schema/tables/user-metadata.table'; import { UserTable } from 'src/schema/tables/user.table'; import { UserMetadata, UserMetadataItem } from 'src/types'; import { asUuid } from 'src/utils/database'; -type Upsert = Insertable; - export interface UserListFilter { id?: string; withDeleted?: boolean; @@ -211,12 +208,12 @@ export class UserRepository { async upsertMetadata(id: string, { key, value }: { key: T; value: UserMetadata[T] }) { await this.db .insertInto('user_metadata') - .values({ userId: id, key, value } as Upsert) + .values({ userId: id, key, value }) .onConflict((oc) => oc.columns(['userId', 'key']).doUpdateSet({ key, value, - } as Upsert), + }), ) .execute(); } diff --git a/server/src/schema/functions.ts b/server/src/schema/functions.ts index 786e7a1ffa..e255742b5d 100644 --- a/server/src/schema/functions.ts +++ b/server/src/schema/functions.ts @@ -230,6 +230,19 @@ export const user_metadata_audit = registerFunction({ END`, }); +export const asset_metadata_audit = registerFunction({ + name: 'asset_metadata_audit', + returnType: 'TRIGGER', + language: 'PLPGSQL', + body: ` + BEGIN + INSERT INTO asset_metadata_audit ("assetId", "key") + SELECT "assetId", "key" + FROM OLD; + RETURN NULL; + END`, +}); + export const asset_face_audit = registerFunction({ name: 'asset_face_audit', returnType: 'TRIGGER', diff --git a/server/src/schema/index.ts b/server/src/schema/index.ts index 8982437b34..48f454d455 100644 --- a/server/src/schema/index.ts +++ b/server/src/schema/index.ts @@ -5,6 +5,7 @@ import { album_user_delete_audit, asset_delete_audit, asset_face_audit, + asset_metadata_audit, f_concat_ws, f_unaccent, immich_uuid_v7, @@ -32,6 +33,8 @@ import { AssetFaceAuditTable } from 'src/schema/tables/asset-face-audit.table'; import { AssetFaceTable } from 'src/schema/tables/asset-face.table'; import { AssetFileTable } from 'src/schema/tables/asset-file.table'; import { AssetJobStatusTable } from 'src/schema/tables/asset-job-status.table'; +import { AssetMetadataAuditTable } from 'src/schema/tables/asset-metadata-audit.table'; +import { AssetMetadataTable } from 'src/schema/tables/asset-metadata.table'; import { AssetTable } from 'src/schema/tables/asset.table'; import { AuditTable } from 'src/schema/tables/audit.table'; import { FaceSearchTable } from 'src/schema/tables/face-search.table'; @@ -81,6 +84,8 @@ export class ImmichDatabase { AssetAuditTable, AssetFaceTable, AssetFaceAuditTable, + AssetMetadataTable, + AssetMetadataAuditTable, AssetJobStatusTable, AssetTable, AssetFileTable, @@ -135,6 +140,7 @@ export class ImmichDatabase { stack_delete_audit, person_delete_audit, user_metadata_audit, + asset_metadata_audit, asset_face_audit, ]; @@ -164,6 +170,8 @@ export interface DB { asset_face: AssetFaceTable; asset_face_audit: AssetFaceAuditTable; asset_file: AssetFileTable; + asset_metadata: AssetMetadataTable; + asset_metadata_audit: AssetMetadataAuditTable; asset_job_status: AssetJobStatusTable; asset_audit: AssetAuditTable; diff --git a/server/src/schema/migrations/1756318797207-AssetMetadataTables.ts b/server/src/schema/migrations/1756318797207-AssetMetadataTables.ts new file mode 100644 index 0000000000..ba0bad9d9a --- /dev/null +++ b/server/src/schema/migrations/1756318797207-AssetMetadataTables.ts @@ -0,0 +1,58 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await sql`CREATE OR REPLACE FUNCTION asset_metadata_audit() + RETURNS TRIGGER + LANGUAGE PLPGSQL + AS $$ + BEGIN + INSERT INTO asset_metadata_audit ("assetId", "key") + SELECT "assetId", "key" + FROM OLD; + RETURN NULL; + END + $$;`.execute(db); + await sql`CREATE TABLE "asset_metadata_audit" ( + "id" uuid NOT NULL DEFAULT immich_uuid_v7(), + "assetId" uuid NOT NULL, + "key" character varying NOT NULL, + "deletedAt" timestamp with time zone NOT NULL DEFAULT clock_timestamp(), + CONSTRAINT "asset_metadata_audit_pkey" PRIMARY KEY ("id") +);`.execute(db); + await sql`CREATE INDEX "asset_metadata_audit_assetId_idx" ON "asset_metadata_audit" ("assetId");`.execute(db); + await sql`CREATE INDEX "asset_metadata_audit_key_idx" ON "asset_metadata_audit" ("key");`.execute(db); + await sql`CREATE INDEX "asset_metadata_audit_deletedAt_idx" ON "asset_metadata_audit" ("deletedAt");`.execute(db); + await sql`CREATE TABLE "asset_metadata" ( + "assetId" uuid NOT NULL, + "key" character varying NOT NULL, + "value" jsonb NOT NULL, + "updateId" uuid NOT NULL DEFAULT immich_uuid_v7(), + "updatedAt" timestamp with time zone NOT NULL DEFAULT now(), + CONSTRAINT "asset_metadata_assetId_fkey" FOREIGN KEY ("assetId") REFERENCES "asset" ("id") ON UPDATE CASCADE ON DELETE CASCADE, + CONSTRAINT "asset_metadata_pkey" PRIMARY KEY ("assetId", "key") +);`.execute(db); + await sql`CREATE INDEX "asset_metadata_updateId_idx" ON "asset_metadata" ("updateId");`.execute(db); + await sql`CREATE INDEX "asset_metadata_updatedAt_idx" ON "asset_metadata" ("updatedAt");`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "asset_metadata_audit" + AFTER DELETE ON "asset_metadata" + REFERENCING OLD TABLE AS "old" + FOR EACH STATEMENT + WHEN (pg_trigger_depth() = 0) + EXECUTE FUNCTION asset_metadata_audit();`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "asset_metadata_updated_at" + BEFORE UPDATE ON "asset_metadata" + FOR EACH ROW + EXECUTE FUNCTION updated_at();`.execute(db); + await sql`INSERT INTO "migration_overrides" ("name", "value") VALUES ('function_asset_metadata_audit', '{"type":"function","name":"asset_metadata_audit","sql":"CREATE OR REPLACE FUNCTION asset_metadata_audit()\\n RETURNS TRIGGER\\n LANGUAGE PLPGSQL\\n AS $$\\n BEGIN\\n INSERT INTO asset_metadata_audit (\\"assetId\\", \\"key\\")\\n SELECT \\"assetId\\", \\"key\\"\\n FROM OLD;\\n RETURN NULL;\\n END\\n $$;"}'::jsonb);`.execute(db); + await sql`INSERT INTO "migration_overrides" ("name", "value") VALUES ('trigger_asset_metadata_audit', '{"type":"trigger","name":"asset_metadata_audit","sql":"CREATE OR REPLACE TRIGGER \\"asset_metadata_audit\\"\\n AFTER DELETE ON \\"asset_metadata\\"\\n REFERENCING OLD TABLE AS \\"old\\"\\n FOR EACH STATEMENT\\n WHEN (pg_trigger_depth() = 0)\\n EXECUTE FUNCTION asset_metadata_audit();"}'::jsonb);`.execute(db); + await sql`INSERT INTO "migration_overrides" ("name", "value") VALUES ('trigger_asset_metadata_updated_at', '{"type":"trigger","name":"asset_metadata_updated_at","sql":"CREATE OR REPLACE TRIGGER \\"asset_metadata_updated_at\\"\\n BEFORE UPDATE ON \\"asset_metadata\\"\\n FOR EACH ROW\\n EXECUTE FUNCTION updated_at();"}'::jsonb);`.execute(db); +} + +export async function down(db: Kysely): Promise { + await sql`DROP TABLE "asset_metadata_audit";`.execute(db); + await sql`DROP TABLE "asset_metadata";`.execute(db); + await sql`DROP FUNCTION asset_metadata_audit;`.execute(db); + await sql`DELETE FROM "migration_overrides" WHERE "name" = 'function_asset_metadata_audit';`.execute(db); + await sql`DELETE FROM "migration_overrides" WHERE "name" = 'trigger_asset_metadata_audit';`.execute(db); + await sql`DELETE FROM "migration_overrides" WHERE "name" = 'trigger_asset_metadata_updated_at';`.execute(db); +} diff --git a/server/src/schema/tables/asset-metadata-audit.table.ts b/server/src/schema/tables/asset-metadata-audit.table.ts new file mode 100644 index 0000000000..3b94ce6d1a --- /dev/null +++ b/server/src/schema/tables/asset-metadata-audit.table.ts @@ -0,0 +1,18 @@ +import { PrimaryGeneratedUuidV7Column } from 'src/decorators'; +import { AssetMetadataKey } from 'src/enum'; +import { Column, CreateDateColumn, Generated, Table, Timestamp } from 'src/sql-tools'; + +@Table('asset_metadata_audit') +export class AssetMetadataAuditTable { + @PrimaryGeneratedUuidV7Column() + id!: Generated; + + @Column({ type: 'uuid', index: true }) + assetId!: string; + + @Column({ index: true }) + key!: AssetMetadataKey; + + @CreateDateColumn({ default: () => 'clock_timestamp()', index: true }) + deletedAt!: Generated; +} diff --git a/server/src/schema/tables/asset-metadata.table.ts b/server/src/schema/tables/asset-metadata.table.ts new file mode 100644 index 0000000000..486101408d --- /dev/null +++ b/server/src/schema/tables/asset-metadata.table.ts @@ -0,0 +1,46 @@ +import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators'; +import { AssetMetadataKey } from 'src/enum'; +import { asset_metadata_audit } from 'src/schema/functions'; +import { AssetTable } from 'src/schema/tables/asset.table'; +import { + AfterDeleteTrigger, + Column, + ForeignKeyColumn, + Generated, + PrimaryColumn, + Table, + Timestamp, + UpdateDateColumn, +} from 'src/sql-tools'; +import { AssetMetadata, AssetMetadataItem } from 'src/types'; + +@UpdatedAtTrigger('asset_metadata_updated_at') +@Table('asset_metadata') +@AfterDeleteTrigger({ + scope: 'statement', + function: asset_metadata_audit, + referencingOldTableAs: 'old', + when: 'pg_trigger_depth() = 0', +}) +export class AssetMetadataTable implements AssetMetadataItem { + @ForeignKeyColumn(() => AssetTable, { + onUpdate: 'CASCADE', + onDelete: 'CASCADE', + primary: true, + // [assetId, key] is the PK constraint + index: false, + }) + assetId!: string; + + @PrimaryColumn({ type: 'character varying' }) + key!: T; + + @Column({ type: 'jsonb' }) + value!: AssetMetadata[T]; + + @UpdateIdColumn({ index: true }) + updateId!: Generated; + + @UpdateDateColumn({ index: true }) + updatedAt!: Generated; +} diff --git a/server/src/services/asset-media.service.ts b/server/src/services/asset-media.service.ts index 517a1f665f..69e1dfd3a0 100644 --- a/server/src/services/asset-media.service.ts +++ b/server/src/services/asset-media.service.ts @@ -423,6 +423,10 @@ export class AssetMediaService extends BaseService { sidecarPath: sidecarFile?.originalPath, }); + if (dto.metadata) { + await this.assetRepository.upsertMetadata(asset.id, dto.metadata); + } + if (sidecarFile) { await this.storageRepository.utimes(sidecarFile.originalPath, new Date(), new Date(dto.fileModifiedAt)); } diff --git a/server/src/services/asset.service.ts b/server/src/services/asset.service.ts index 9a2c580707..725e3cff15 100644 --- a/server/src/services/asset.service.ts +++ b/server/src/services/asset.service.ts @@ -9,12 +9,14 @@ import { AssetBulkUpdateDto, AssetJobName, AssetJobsDto, + AssetMetadataResponseDto, + AssetMetadataUpsertDto, AssetStatsDto, UpdateAssetDto, mapStats, } from 'src/dtos/asset.dto'; import { AuthDto } from 'src/dtos/auth.dto'; -import { AssetStatus, AssetVisibility, JobName, JobStatus, Permission, QueueName } from 'src/enum'; +import { AssetMetadataKey, AssetStatus, AssetVisibility, JobName, JobStatus, Permission, QueueName } from 'src/enum'; import { BaseService } from 'src/services/base.service'; import { ISidecarWriteJob, JobItem, JobOf } from 'src/types'; import { requireElevatedPermission } from 'src/utils/access'; @@ -93,7 +95,7 @@ export class AssetService extends BaseService { } } - await this.updateMetadata({ id, description, dateTimeOriginal, latitude, longitude, rating }); + await this.updateExif({ id, description, dateTimeOriginal, latitude, longitude, rating }); const asset = await this.assetRepository.update({ id, ...rest }); @@ -273,6 +275,31 @@ export class AssetService extends BaseService { }); } + async getMetadata(auth: AuthDto, id: string): Promise { + await this.requireAccess({ auth, permission: Permission.AssetRead, ids: [id] }); + return this.assetRepository.getMetadata(id); + } + + async upsertMetadata(auth: AuthDto, id: string, dto: AssetMetadataUpsertDto): Promise { + await this.requireAccess({ auth, permission: Permission.AssetUpdate, ids: [id] }); + return this.assetRepository.upsertMetadata(id, dto.items); + } + + async getMetadataByKey(auth: AuthDto, id: string, key: AssetMetadataKey): Promise { + await this.requireAccess({ auth, permission: Permission.AssetRead, ids: [id] }); + + const item = await this.assetRepository.getMetadataByKey(id, key); + if (!item) { + throw new BadRequestException(`Metadata with key "${key}" not found for asset with id "${id}"`); + } + return item; + } + + async deleteMetadataByKey(auth: AuthDto, id: string, key: AssetMetadataKey): Promise { + await this.requireAccess({ auth, permission: Permission.AssetUpdate, ids: [id] }); + return this.assetRepository.deleteMetadataByKey(id, key); + } + async run(auth: AuthDto, dto: AssetJobsDto) { await this.requireAccess({ auth, permission: Permission.AssetUpdate, ids: dto.assetIds }); @@ -313,7 +340,7 @@ export class AssetService extends BaseService { return asset; } - private async updateMetadata(dto: ISidecarWriteJob) { + private async updateExif(dto: ISidecarWriteJob) { const { id, description, dateTimeOriginal, latitude, longitude, rating } = dto; const writes = _.omitBy({ description, dateTimeOriginal, latitude, longitude, rating }, _.isUndefined); if (Object.keys(writes).length > 0) { diff --git a/server/src/services/sync.service.ts b/server/src/services/sync.service.ts index 6b8512eacb..677c799fb8 100644 --- a/server/src/services/sync.service.ts +++ b/server/src/services/sync.service.ts @@ -74,6 +74,7 @@ export const SYNC_TYPES_ORDER = [ SyncRequestType.PeopleV1, SyncRequestType.AssetFacesV1, SyncRequestType.UserMetadataV1, + SyncRequestType.AssetMetadataV1, ]; const throwSessionRequired = () => { @@ -156,6 +157,7 @@ export class SyncService extends BaseService { [SyncRequestType.AssetsV1]: () => this.syncAssetsV1(options, response, checkpointMap), [SyncRequestType.AssetExifsV1]: () => this.syncAssetExifsV1(options, response, checkpointMap), [SyncRequestType.PartnerAssetsV1]: () => this.syncPartnerAssetsV1(options, response, checkpointMap, session.id), + [SyncRequestType.AssetMetadataV1]: () => this.syncAssetMetadataV1(options, response, checkpointMap, auth), [SyncRequestType.PartnerAssetExifsV1]: () => this.syncPartnerAssetExifsV1(options, response, checkpointMap, session.id), [SyncRequestType.AlbumsV1]: () => this.syncAlbumsV1(options, response, checkpointMap), @@ -759,6 +761,33 @@ export class SyncService extends BaseService { } } + private async syncAssetMetadataV1( + options: SyncQueryOptions, + response: Writable, + checkpointMap: CheckpointMap, + auth: AuthDto, + ) { + const deleteType = SyncEntityType.AssetMetadataDeleteV1; + const deletes = this.syncRepository.assetMetadata.getDeletes( + { ...options, ack: checkpointMap[deleteType] }, + auth.user.id, + ); + + for await (const { id, ...data } of deletes) { + send(response, { type: deleteType, ids: [id], data }); + } + + const upsertType = SyncEntityType.AssetMetadataV1; + const upserts = this.syncRepository.assetMetadata.getUpserts( + { ...options, ack: checkpointMap[upsertType] }, + auth.user.id, + ); + + for await (const { updateId, ...data } of upserts) { + send(response, { type: upsertType, ids: [updateId], data }); + } + } + private async upsertBackfillCheckpoint(item: { type: SyncEntityType; sessionId: string; createId: string }) { const { type, sessionId, createId } = item; await this.syncCheckpointRepository.upsertAll([ diff --git a/server/src/types.ts b/server/src/types.ts index 9cd1aa996b..b77dd4df6e 100644 --- a/server/src/types.ts +++ b/server/src/types.ts @@ -1,6 +1,7 @@ import { SystemConfig } from 'src/config'; import { VECTOR_EXTENSIONS } from 'src/constants'; import { + AssetMetadataKey, AssetOrder, AssetType, DatabaseSslMode, @@ -465,11 +466,6 @@ export interface SystemMetadata extends Record = { - key: T; - value: UserMetadata[T]; -}; - export interface UserPreferences { albums: { defaultAssetOrder: AssetOrder; @@ -514,8 +510,22 @@ export interface UserPreferences { }; } +export type UserMetadataItem = { + key: T; + value: UserMetadata[T]; +}; + export interface UserMetadata extends Record> { [UserMetadataKey.Preferences]: DeepPartial; [UserMetadataKey.License]: { licenseKey: string; activationKey: string; activatedAt: string }; [UserMetadataKey.Onboarding]: { isOnboarded: boolean }; } + +export type AssetMetadataItem = { + key: T; + value: AssetMetadata[T]; +}; + +export interface AssetMetadata extends Record> { + [AssetMetadataKey.MobileApp]: { iCloudId: string }; +} diff --git a/server/test/medium/specs/sync/sync-asset-metadata.spec.ts b/server/test/medium/specs/sync/sync-asset-metadata.spec.ts new file mode 100644 index 0000000000..84353883a2 --- /dev/null +++ b/server/test/medium/specs/sync/sync-asset-metadata.spec.ts @@ -0,0 +1,126 @@ +import { Kysely } from 'kysely'; +import { AssetMetadataKey, SyncEntityType, SyncRequestType } from 'src/enum'; +import { AssetRepository } from 'src/repositories/asset.repository'; +import { DB } from 'src/schema'; +import { SyncTestContext } from 'test/medium.factory'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = async (db?: Kysely) => { + const ctx = new SyncTestContext(db || defaultDatabase); + const { auth, user, session } = await ctx.newSyncAuthUser(); + return { auth, user, session, ctx }; +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe(SyncEntityType.AssetMetadataV1, () => { + it('should detect and sync new asset metadata', async () => { + const { auth, user, ctx } = await setup(); + + const assetRepo = ctx.get(AssetRepository); + const { asset } = await ctx.newAsset({ ownerId: user.id }); + await assetRepo.upsertMetadata(asset.id, [{ key: AssetMetadataKey.MobileApp, value: { iCloudId: 'abc123' } }]); + + const response = await ctx.syncStream(auth, [SyncRequestType.AssetMetadataV1]); + expect(response).toHaveLength(1); + expect(response).toEqual([ + { + ack: expect.any(String), + data: { + key: AssetMetadataKey.MobileApp, + assetId: asset.id, + value: { iCloudId: 'abc123' }, + }, + type: 'AssetMetadataV1', + }, + ]); + + await ctx.syncAckAll(auth, response); + await expect(ctx.syncStream(auth, [SyncRequestType.AssetMetadataV1])).resolves.toEqual([]); + }); + + it('should update asset metadata', async () => { + const { auth, user, ctx } = await setup(); + + const assetRepo = ctx.get(AssetRepository); + const { asset } = await ctx.newAsset({ ownerId: user.id }); + await assetRepo.upsertMetadata(asset.id, [{ key: AssetMetadataKey.MobileApp, value: { iCloudId: 'abc123' } }]); + + const response = await ctx.syncStream(auth, [SyncRequestType.AssetMetadataV1]); + expect(response).toHaveLength(1); + expect(response).toEqual([ + { + ack: expect.any(String), + data: { + key: AssetMetadataKey.MobileApp, + assetId: asset.id, + value: { iCloudId: 'abc123' }, + }, + type: 'AssetMetadataV1', + }, + ]); + + await ctx.syncAckAll(auth, response); + + await assetRepo.upsertMetadata(asset.id, [{ key: AssetMetadataKey.MobileApp, value: { iCloudId: 'abc456' } }]); + + const updatedResponse = await ctx.syncStream(auth, [SyncRequestType.AssetMetadataV1]); + expect(updatedResponse).toEqual([ + { + ack: expect.any(String), + data: { + key: AssetMetadataKey.MobileApp, + assetId: asset.id, + value: { iCloudId: 'abc456' }, + }, + type: 'AssetMetadataV1', + }, + ]); + + await ctx.syncAckAll(auth, updatedResponse); + await expect(ctx.syncStream(auth, [SyncRequestType.AssetMetadataV1])).resolves.toEqual([]); + }); +}); + +describe(SyncEntityType.AssetMetadataDeleteV1, () => { + it('should delete and sync asset metadata', async () => { + const { auth, user, ctx } = await setup(); + + const assetRepo = ctx.get(AssetRepository); + const { asset } = await ctx.newAsset({ ownerId: user.id }); + await assetRepo.upsertMetadata(asset.id, [{ key: AssetMetadataKey.MobileApp, value: { iCloudId: 'abc123' } }]); + + const response = await ctx.syncStream(auth, [SyncRequestType.AssetMetadataV1]); + expect(response).toHaveLength(1); + expect(response).toEqual([ + { + ack: expect.any(String), + data: { + key: AssetMetadataKey.MobileApp, + assetId: asset.id, + value: { iCloudId: 'abc123' }, + }, + type: 'AssetMetadataV1', + }, + ]); + + await ctx.syncAckAll(auth, response); + + await assetRepo.deleteMetadataByKey(asset.id, AssetMetadataKey.MobileApp); + + await expect(ctx.syncStream(auth, [SyncRequestType.AssetMetadataV1])).resolves.toEqual([ + { + ack: expect.any(String), + data: { + assetId: asset.id, + key: AssetMetadataKey.MobileApp, + }, + type: 'AssetMetadataDeleteV1', + }, + ]); + }); +}); diff --git a/server/test/repositories/asset.repository.mock.ts b/server/test/repositories/asset.repository.mock.ts index 79e3d506f3..e735b37564 100644 --- a/server/test/repositories/asset.repository.mock.ts +++ b/server/test/repositories/asset.repository.mock.ts @@ -41,5 +41,9 @@ export const newAssetRepositoryMock = (): Mocked Date: Wed, 27 Aug 2025 15:10:55 -0400 Subject: [PATCH 343/748] fix: motion video extraction race condition (#21285) fix: motion video extraction race ccondition --- server/src/services/asset-media.service.ts | 4 +- server/src/services/metadata.service.ts | 82 +++++++++++++--------- server/src/utils/database.ts | 6 +- 3 files changed, 56 insertions(+), 36 deletions(-) diff --git a/server/src/services/asset-media.service.ts b/server/src/services/asset-media.service.ts index 69e1dfd3a0..54bbedea9c 100644 --- a/server/src/services/asset-media.service.ts +++ b/server/src/services/asset-media.service.ts @@ -27,7 +27,7 @@ import { BaseService } from 'src/services/base.service'; import { UploadFile } from 'src/types'; import { requireUploadAccess } from 'src/utils/access'; import { asRequest, getAssetFiles, onBeforeLink } from 'src/utils/asset.util'; -import { ASSET_CHECKSUM_CONSTRAINT } from 'src/utils/database'; +import { isAssetChecksumConstraint } from 'src/utils/database'; import { getFilenameExtension, getFileNameWithoutExtension, ImmichFileResponse } from 'src/utils/file'; import { mimeTypes } from 'src/utils/mime-types'; import { fromChecksum } from 'src/utils/request'; @@ -318,7 +318,7 @@ export class AssetMediaService extends BaseService { }); // handle duplicates with a success response - if (error.constraint_name === ASSET_CHECKSUM_CONSTRAINT) { + if (isAssetChecksumConstraint(error)) { const duplicateId = await this.assetRepository.getUploadAssetIdByChecksum(auth.user.id, file.checksum); if (!duplicateId) { this.logger.error(`Error locating duplicate for checksum constraint`); diff --git a/server/src/services/metadata.service.ts b/server/src/services/metadata.service.ts index c675b7200d..ac2b927510 100644 --- a/server/src/services/metadata.service.ts +++ b/server/src/services/metadata.service.ts @@ -29,6 +29,7 @@ import { AssetFaceTable } from 'src/schema/tables/asset-face.table'; import { PersonTable } from 'src/schema/tables/person.table'; import { BaseService } from 'src/services/base.service'; import { JobItem, JobOf } from 'src/types'; +import { isAssetChecksumConstraint } from 'src/utils/database'; import { isFaceImportEnabled } from 'src/utils/misc'; import { upsertTags } from 'src/utils/tag'; @@ -545,47 +546,62 @@ export class MetadataService extends BaseService { }); } const checksum = this.cryptoRepository.hashSha1(video); + const checksumQuery = { ownerId: asset.ownerId, libraryId: asset.libraryId ?? undefined, checksum }; - let motionAsset = await this.assetRepository.getByChecksum({ - ownerId: asset.ownerId, - libraryId: asset.libraryId ?? undefined, - checksum, - }); - if (motionAsset) { + let motionAsset = await this.assetRepository.getByChecksum(checksumQuery); + let isNewMotionAsset = false; + + if (!motionAsset) { + try { + const motionAssetId = this.cryptoRepository.randomUUID(); + motionAsset = await this.assetRepository.create({ + id: motionAssetId, + libraryId: asset.libraryId, + type: AssetType.Video, + fileCreatedAt: dates.dateTimeOriginal, + fileModifiedAt: stats.mtime, + localDateTime: dates.localDateTime, + checksum, + ownerId: asset.ownerId, + originalPath: StorageCore.getAndroidMotionPath(asset, motionAssetId), + originalFileName: `${path.parse(asset.originalFileName).name}.mp4`, + visibility: AssetVisibility.Hidden, + deviceAssetId: 'NONE', + deviceId: 'NONE', + }); + + isNewMotionAsset = true; + + if (!asset.isExternal) { + await this.userRepository.updateUsage(asset.ownerId, video.byteLength); + } + } catch (error) { + if (!isAssetChecksumConstraint(error)) { + throw error; + } + + motionAsset = await this.assetRepository.getByChecksum(checksumQuery); + if (!motionAsset) { + this.logger.warn(`Unable to find existing motion video asset for ${asset.id}: ${asset.originalPath}`); + return; + } + } + } + + if (!isNewMotionAsset) { this.logger.debugFn(() => { const base64Checksum = checksum.toString('base64'); return `Motion asset with checksum ${base64Checksum} already exists for asset ${asset.id}: ${asset.originalPath}`; }); + } - // Hide the motion photo video asset if it's not already hidden to prepare for linking - if (motionAsset.visibility === AssetVisibility.Timeline) { - await this.assetRepository.update({ - id: motionAsset.id, - visibility: AssetVisibility.Hidden, - }); - this.logger.log(`Hid unlinked motion photo video asset (${motionAsset.id})`); - } - } else { - const motionAssetId = this.cryptoRepository.randomUUID(); - motionAsset = await this.assetRepository.create({ - id: motionAssetId, - libraryId: asset.libraryId, - type: AssetType.Video, - fileCreatedAt: dates.dateTimeOriginal, - fileModifiedAt: stats.mtime, - localDateTime: dates.localDateTime, - checksum, - ownerId: asset.ownerId, - originalPath: StorageCore.getAndroidMotionPath(asset, motionAssetId), - originalFileName: `${path.parse(asset.originalFileName).name}.mp4`, + // Hide the motion photo video asset if it's not already hidden to prepare for linking + if (motionAsset.visibility === AssetVisibility.Timeline) { + await this.assetRepository.update({ + id: motionAsset.id, visibility: AssetVisibility.Hidden, - deviceAssetId: 'NONE', - deviceId: 'NONE', }); - - if (!asset.isExternal) { - await this.userRepository.updateUsage(asset.ownerId, video.byteLength); - } + this.logger.log(`Hid unlinked motion photo video asset (${motionAsset.id})`); } if (asset.livePhotoVideoId !== motionAsset.id) { diff --git a/server/src/utils/database.ts b/server/src/utils/database.ts index 1ef9b8e926..d9fe6b7897 100644 --- a/server/src/utils/database.ts +++ b/server/src/utils/database.ts @@ -14,7 +14,7 @@ import { import { PostgresJSDialect } from 'kysely-postgres-js'; import { jsonArrayFrom, jsonObjectFrom } from 'kysely/helpers/postgres'; import { parse } from 'pg-connection-string'; -import postgres, { Notice } from 'postgres'; +import postgres, { Notice, PostgresError } from 'postgres'; import { columns, Exif, Person } from 'src/database'; import { AssetFileType, AssetVisibility, DatabaseExtension, DatabaseSslMode } from 'src/enum'; import { AssetSearchBuilderOptions } from 'src/repositories/search.repository'; @@ -153,6 +153,10 @@ export function toJson { + return (error as PostgresError)?.constraint_name === 'UQ_assets_owner_checksum'; +}; + export function withDefaultVisibility(qb: SelectQueryBuilder) { return qb.where('asset.visibility', 'in', [sql.lit(AssetVisibility.Archive), sql.lit(AssetVisibility.Timeline)]); } From ae104ad7cc1256a51dd039bc006cfe20eebdeea8 Mon Sep 17 00:00:00 2001 From: prajwal <93521144+Prajwalg19@users.noreply.github.com> Date: Thu, 28 Aug 2025 01:21:43 +0530 Subject: [PATCH 344/748] fix(web): add primary text color to file upload toast (#21340) * fix:add primary text color to file upload toast * fix:make progress bar visible in dark mode * fix:make it text-primary --------- Co-authored-by: prajwal --- .../components/shared-components/upload-asset-preview.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/lib/components/shared-components/upload-asset-preview.svelte b/web/src/lib/components/shared-components/upload-asset-preview.svelte index 428dafebe3..985400a033 100644 --- a/web/src/lib/components/shared-components/upload-asset-preview.svelte +++ b/web/src/lib/components/shared-components/upload-asset-preview.svelte @@ -91,9 +91,9 @@ {#if uploadAsset.state === UploadState.STARTED} -
    +
    -

    +

    {#if uploadAsset.message} {uploadAsset.message} {:else} From dc6ac3aaecacf8cbc2fd19bd37ebf65cc5ad76d7 Mon Sep 17 00:00:00 2001 From: Mert <101130780+mertalev@users.noreply.github.com> Date: Wed, 27 Aug 2025 17:40:45 -0400 Subject: [PATCH 345/748] fix(mobile): thumbnail requests not being cancelled (#21331) * fix requests not being cancelled * handle thumbhash --- .../widgets/images/image_provider.dart | 3 +-- .../widgets/images/local_image_provider.dart | 5 ++-- .../widgets/images/remote_image_provider.dart | 26 +++++++------------ .../widgets/images/thumb_hash_provider.dart | 5 ++-- .../widgets/images/thumbnail.widget.dart | 11 ++++++++ 5 files changed, 27 insertions(+), 23 deletions(-) diff --git a/mobile/lib/presentation/widgets/images/image_provider.dart b/mobile/lib/presentation/widgets/images/image_provider.dart index d0428e5013..dd87d2f228 100644 --- a/mobile/lib/presentation/widgets/images/image_provider.dart +++ b/mobile/lib/presentation/widgets/images/image_provider.dart @@ -50,12 +50,11 @@ mixin CancellableImageProviderMixin on CancellableImageProvide Stream loadRequest(ImageRequest request, ImageDecoderCallback decode) async* { if (isCancelled) { + this.request = null; evict(); return; } - this.request = request; - try { final image = await request.load(decode); if (image == null || isCancelled) { diff --git a/mobile/lib/presentation/widgets/images/local_image_provider.dart b/mobile/lib/presentation/widgets/images/local_image_provider.dart index 8bdbe3c16a..223d095432 100644 --- a/mobile/lib/presentation/widgets/images/local_image_provider.dart +++ b/mobile/lib/presentation/widgets/images/local_image_provider.dart @@ -35,7 +35,8 @@ class LocalThumbProvider extends CancellableImageProvider } Stream _codec(LocalThumbProvider key, ImageDecoderCallback decode) { - return loadRequest(LocalImageRequest(localId: key.id, size: key.size, assetType: key.assetType), decode); + final request = this.request = LocalImageRequest(localId: key.id, size: key.size, assetType: key.assetType); + return loadRequest(request, decode); } @override @@ -87,7 +88,7 @@ class LocalFullImageProvider extends CancellableImageProvider } Stream _codec(RemoteThumbProvider key, ImageDecoderCallback decode) { - final request = RemoteImageRequest( + final request = this.request = RemoteImageRequest( uri: getThumbnailUrlForRemoteId(key.assetId), headers: ApiService.getRequestHeaders(), cacheManager: cacheManager, @@ -92,16 +92,12 @@ class RemoteFullImageProvider extends CancellableImageProvider @override ImageStreamCompleter loadImage(ThumbHashProvider key, ImageDecoderCallback decode) { - return OneFramePlaceholderImageStreamCompleter(_loadCodec(key, decode))..addOnLastListenerRemovedCallback(cancel); + return OneFramePlaceholderImageStreamCompleter(_loadCodec(key, decode), onDispose: cancel); } Stream _loadCodec(ThumbHashProvider key, ImageDecoderCallback decode) { - return loadRequest(ThumbhashImageRequest(thumbhash: key.thumbHash), decode); + final request = this.request = ThumbhashImageRequest(thumbhash: key.thumbHash); + return loadRequest(request, decode); } @override diff --git a/mobile/lib/presentation/widgets/images/thumbnail.widget.dart b/mobile/lib/presentation/widgets/images/thumbnail.widget.dart index 9cf77cc29e..3ecd5cd491 100644 --- a/mobile/lib/presentation/widgets/images/thumbnail.widget.dart +++ b/mobile/lib/presentation/widgets/images/thumbnail.widget.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/images/image_provider.dart'; import 'package:immich_mobile/presentation/widgets/images/local_image_provider.dart'; import 'package:immich_mobile/presentation/widgets/images/remote_image_provider.dart'; import 'package:immich_mobile/presentation/widgets/images/thumb_hash_provider.dart'; @@ -235,6 +236,16 @@ class _ThumbnailState extends State with SingleTickerProviderStateMix @override void dispose() { + final imageProvider = widget.imageProvider; + if (imageProvider is CancellableImageProvider) { + imageProvider.cancel(); + } + + final thumbhashProvider = widget.thumbhashProvider; + if (thumbhashProvider is CancellableImageProvider) { + thumbhashProvider.cancel(); + } + _fadeController.removeStatusListener(_onAnimationStatusChanged); _fadeController.dispose(); _stopListeningToStream(); From a5841a8bf44bb6764ddd9179273986d55bbdeeea Mon Sep 17 00:00:00 2001 From: Mert <101130780+mertalev@users.noreply.github.com> Date: Wed, 27 Aug 2025 22:16:41 -0400 Subject: [PATCH 346/748] fix(mobile): memory lane rebuild (#21350) * avoid unnecessary timeline rebuild * add key * handle disabled memories * avoid rebuild if no memories --- .../repositories/memory.repository.dart | 5 ++++- .../pages/dev/main_timeline.page.dart | 21 ++++--------------- .../widgets/memory/memory_lane.widget.dart | 15 +++++++++---- .../infrastructure/memory.provider.dart | 11 +++++----- 4 files changed, 24 insertions(+), 28 deletions(-) diff --git a/mobile/lib/infrastructure/repositories/memory.repository.dart b/mobile/lib/infrastructure/repositories/memory.repository.dart index 2a52faf2dd..b5bed18ad5 100644 --- a/mobile/lib/infrastructure/repositories/memory.repository.dart +++ b/mobile/lib/infrastructure/repositories/memory.repository.dart @@ -30,6 +30,9 @@ class DriftMemoryRepository extends DriftDatabaseRepository { ..orderBy([OrderingTerm.desc(_db.memoryEntity.memoryAt), OrderingTerm.asc(_db.remoteAssetEntity.createdAt)]); final rows = await query.get(); + if (rows.isEmpty) { + return const []; + } final Map memoriesMap = {}; @@ -46,7 +49,7 @@ class DriftMemoryRepository extends DriftDatabaseRepository { } } - return memoriesMap.values.toList(); + return memoriesMap.values.toList(growable: false); } Future get(String memoryId) async { diff --git a/mobile/lib/presentation/pages/dev/main_timeline.page.dart b/mobile/lib/presentation/pages/dev/main_timeline.page.dart index 8ef3ef9757..60a296a22c 100644 --- a/mobile/lib/presentation/pages/dev/main_timeline.page.dart +++ b/mobile/lib/presentation/pages/dev/main_timeline.page.dart @@ -4,7 +4,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/presentation/widgets/memory/memory_lane.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; import 'package:immich_mobile/providers/infrastructure/memory.provider.dart'; -import 'package:immich_mobile/providers/user.provider.dart'; @RoutePage() class MainTimelinePage extends ConsumerWidget { @@ -12,22 +11,10 @@ class MainTimelinePage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final memoryLaneProvider = ref.watch(driftMemoryFutureProvider); - final memoriesEnabled = ref.watch(currentUserProvider.select((user) => user?.memoryEnabled ?? true)); - - return memoryLaneProvider.maybeWhen( - data: (memories) { - return memories.isEmpty || !memoriesEnabled - ? const Timeline() - : Timeline( - topSliverWidget: SliverToBoxAdapter( - key: Key('memory-lane-${memories.first.assets.first.id}'), - child: DriftMemoryLane(memories: memories), - ), - topSliverWidgetHeight: 200, - ); - }, - orElse: () => const Timeline(), + final hasMemories = ref.watch(driftMemoryFutureProvider.select((state) => state.value?.isNotEmpty ?? false)); + return Timeline( + topSliverWidget: const SliverToBoxAdapter(child: DriftMemoryLane()), + topSliverWidgetHeight: hasMemories ? 200 : 0, ); } } diff --git a/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart b/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart index ec49bbec96..b2c61c7488 100644 --- a/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart +++ b/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart @@ -7,15 +7,20 @@ import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart' import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart'; import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/memory.provider.dart'; import 'package:immich_mobile/routing/router.dart'; class DriftMemoryLane extends ConsumerWidget { - final List memories; - - const DriftMemoryLane({super.key, required this.memories}); + const DriftMemoryLane({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { + final memoryLaneProvider = ref.watch(driftMemoryFutureProvider); + final memories = memoryLaneProvider.value ?? const []; + if (memories.isEmpty) { + return const SizedBox.shrink(); + } + return ConstrainedBox( constraints: const BoxConstraints(maxHeight: 200), child: CarouselView( @@ -38,7 +43,9 @@ class DriftMemoryLane extends ConsumerWidget { context.pushRoute(DriftMemoryRoute(memories: memories, memoryIndex: index)); }, - children: memories.map((memory) => DriftMemoryCard(memory: memory)).toList(), + children: memories + .map((memory) => DriftMemoryCard(key: Key(memory.id), memory: memory)) + .toList(growable: false), ), ); } diff --git a/mobile/lib/providers/infrastructure/memory.provider.dart b/mobile/lib/providers/infrastructure/memory.provider.dart index e5809a12b4..0965f4349b 100644 --- a/mobile/lib/providers/infrastructure/memory.provider.dart +++ b/mobile/lib/providers/infrastructure/memory.provider.dart @@ -14,13 +14,12 @@ final driftMemoryServiceProvider = Provider( (ref) => DriftMemoryService(ref.watch(driftMemoryRepositoryProvider)), ); -final driftMemoryFutureProvider = FutureProvider.autoDispose>((ref) async { - final user = ref.watch(currentUserProvider); - if (user == null) { - return []; +final driftMemoryFutureProvider = FutureProvider.autoDispose>((ref) { + final (userId, enabled) = ref.watch(currentUserProvider.select((user) => (user?.id, user?.memoryEnabled ?? true))); + if (userId == null || !enabled) { + return const []; } final service = ref.watch(driftMemoryServiceProvider); - - return service.getMemoryLane(user.id); + return service.getMemoryLane(userId); }); From f65dabd43acfe893b059987a0882fcc96ad43824 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 27 Aug 2025 21:17:56 -0500 Subject: [PATCH 347/748] chore: post release tasks (#21228) --- mobile/ios/Runner.xcodeproj/project.pbxproj | 18 +++++++++--------- mobile/ios/Runner/Info.plist | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/mobile/ios/Runner.xcodeproj/project.pbxproj b/mobile/ios/Runner.xcodeproj/project.pbxproj index 563c4cda33..827c9be881 100644 --- a/mobile/ios/Runner.xcodeproj/project.pbxproj +++ b/mobile/ios/Runner.xcodeproj/project.pbxproj @@ -669,7 +669,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 217; + CURRENT_PROJECT_VERSION = 218; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; @@ -813,7 +813,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 217; + CURRENT_PROJECT_VERSION = 218; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; @@ -843,7 +843,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 217; + CURRENT_PROJECT_VERSION = 218; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; @@ -877,7 +877,7 @@ CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 217; + CURRENT_PROJECT_VERSION = 218; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -920,7 +920,7 @@ CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 217; + CURRENT_PROJECT_VERSION = 218; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -960,7 +960,7 @@ CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 217; + CURRENT_PROJECT_VERSION = 218; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -999,7 +999,7 @@ CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 217; + CURRENT_PROJECT_VERSION = 218; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -1043,7 +1043,7 @@ CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 217; + CURRENT_PROJECT_VERSION = 218; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -1084,7 +1084,7 @@ CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 217; + CURRENT_PROJECT_VERSION = 218; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; diff --git a/mobile/ios/Runner/Info.plist b/mobile/ios/Runner/Info.plist index 8c72f125f4..5db281ea86 100644 --- a/mobile/ios/Runner/Info.plist +++ b/mobile/ios/Runner/Info.plist @@ -78,7 +78,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.139.3 + 1.139.4 CFBundleSignature ???? CFBundleURLTypes @@ -105,7 +105,7 @@ CFBundleVersion - 217 + 218 FLTEnableImpeller ITSAppUsesNonExemptEncryption From e2169f531613732c4baca94a6fa7ed885c7d9ddd Mon Sep 17 00:00:00 2001 From: Yaros Date: Thu, 28 Aug 2025 04:42:38 +0200 Subject: [PATCH 348/748] fix(mobile): fast animations when "disable animations" enabled (#21309) * fix(mobile): disable animations speed android * use animationBehavior instead of workaround --- .../widgets/common/mesmerizing_sliver_app_bar.dart | 12 ++++++++++-- mobile/lib/widgets/common/person_sliver_app_bar.dart | 12 ++++++++++-- .../widgets/common/remote_album_sliver_app_bar.dart | 12 ++++++++++-- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/mobile/lib/widgets/common/mesmerizing_sliver_app_bar.dart b/mobile/lib/widgets/common/mesmerizing_sliver_app_bar.dart index 359b400456..73dbbfc85b 100644 --- a/mobile/lib/widgets/common/mesmerizing_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/mesmerizing_sliver_app_bar.dart @@ -272,9 +272,17 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic void initState() { super.initState(); - _zoomController = AnimationController(duration: const Duration(seconds: 12), vsync: this); + _zoomController = AnimationController( + duration: const Duration(seconds: 12), + vsync: this, + animationBehavior: AnimationBehavior.preserve, + ); - _crossFadeController = AnimationController(duration: const Duration(milliseconds: 1200), vsync: this); + _crossFadeController = AnimationController( + duration: const Duration(milliseconds: 1200), + vsync: this, + animationBehavior: AnimationBehavior.preserve, + ); _zoomAnimation = Tween( begin: 1.0, diff --git a/mobile/lib/widgets/common/person_sliver_app_bar.dart b/mobile/lib/widgets/common/person_sliver_app_bar.dart index 1cc117139d..0f9555a101 100644 --- a/mobile/lib/widgets/common/person_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/person_sliver_app_bar.dart @@ -378,9 +378,17 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic void initState() { super.initState(); - _zoomController = AnimationController(duration: const Duration(seconds: 12), vsync: this); + _zoomController = AnimationController( + duration: const Duration(seconds: 12), + vsync: this, + animationBehavior: AnimationBehavior.preserve, + ); - _crossFadeController = AnimationController(duration: const Duration(milliseconds: 1200), vsync: this); + _crossFadeController = AnimationController( + duration: const Duration(milliseconds: 1200), + vsync: this, + animationBehavior: AnimationBehavior.preserve, + ); _zoomAnimation = Tween( begin: 1.0, diff --git a/mobile/lib/widgets/common/remote_album_sliver_app_bar.dart b/mobile/lib/widgets/common/remote_album_sliver_app_bar.dart index 54497a10de..f9768d575e 100644 --- a/mobile/lib/widgets/common/remote_album_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/remote_album_sliver_app_bar.dart @@ -378,9 +378,17 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic void initState() { super.initState(); - _zoomController = AnimationController(duration: const Duration(seconds: 12), vsync: this); + _zoomController = AnimationController( + duration: const Duration(seconds: 12), + vsync: this, + animationBehavior: AnimationBehavior.preserve, + ); - _crossFadeController = AnimationController(duration: const Duration(milliseconds: 1200), vsync: this); + _crossFadeController = AnimationController( + duration: const Duration(milliseconds: 1200), + vsync: this, + animationBehavior: AnimationBehavior.preserve, + ); _zoomAnimation = Tween( begin: 1.0, From a3808c26ce15ba80023b79e0335a3a38518c235c Mon Sep 17 00:00:00 2001 From: Yaros Date: Thu, 28 Aug 2025 04:43:39 +0200 Subject: [PATCH 349/748] fix(web): middle click not working on videos (#21304) Co-authored-by: Alex --- web/src/lib/components/assets/thumbnail/thumbnail.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/lib/components/assets/thumbnail/thumbnail.svelte b/web/src/lib/components/assets/thumbnail/thumbnail.svelte index e4b590b8ea..9af9287c76 100644 --- a/web/src/lib/components/assets/thumbnail/thumbnail.svelte +++ b/web/src/lib/components/assets/thumbnail/thumbnail.svelte @@ -321,7 +321,7 @@ onComplete={(errored) => ((loaded = true), (thumbError = errored))} /> {#if asset.isVideo} -

    +
    {:else if asset.isImage && asset.livePhotoVideoId} -
    +
    Date: Wed, 27 Aug 2025 21:44:19 -0500 Subject: [PATCH 350/748] chore(deps): pin busybox docker tag to ab33eac (#21280) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- docker/docker-compose.dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index 439140e3f5..2c003270e4 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -185,7 +185,7 @@ services: init: container_name: init - image: busybox + image: busybox@sha256:ab33eacc8251e3807b85bb6dba570e4698c3998eca6f0fc2ccb60575a563ea74 env_file: - .env user: 0:0 From 227789225ab9aa4675272c6eb81c878dc4385058 Mon Sep 17 00:00:00 2001 From: Mert <101130780+mertalev@users.noreply.github.com> Date: Wed, 27 Aug 2025 22:52:51 -0400 Subject: [PATCH 351/748] fix(mobile): allow gestures in asset viewer before image is loaded (#21354) * allow gestures while loading * disable zoom --- .../photo_view/src/photo_view_wrappers.dart | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart b/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart index 65037dde96..a2ad04e6b5 100644 --- a/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart +++ b/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart @@ -172,12 +172,36 @@ class _ImageWrapperState extends State { @override Widget build(BuildContext context) { - if (_loading) { - return _buildLoading(context); - } - - if (_lastException != null) { - return _buildError(context); + if (_loading || _lastException != null) { + return CustomChildWrapper( + childSize: null, + backgroundDecoration: widget.backgroundDecoration, + heroAttributes: widget.heroAttributes, + scaleStateChangedCallback: widget.scaleStateChangedCallback, + enableRotation: widget.enableRotation, + controller: widget.controller, + scaleStateController: widget.scaleStateController, + maxScale: widget.maxScale, + minScale: widget.minScale, + initialScale: widget.initialScale, + basePosition: widget.basePosition, + scaleStateCycle: widget.scaleStateCycle, + onTapUp: widget.onTapUp, + onTapDown: widget.onTapDown, + onDragStart: widget.onDragStart, + onDragEnd: widget.onDragEnd, + onDragUpdate: widget.onDragUpdate, + onScaleEnd: widget.onScaleEnd, + onLongPressStart: widget.onLongPressStart, + outerSize: widget.outerSize, + gestureDetectorBehavior: widget.gestureDetectorBehavior, + tightMode: widget.tightMode, + filterQuality: widget.filterQuality, + disableGestures: widget.disableGestures, + disableScaleGestures: true, + enablePanAlways: widget.enablePanAlways, + child: _loading ? _buildLoading(context) : _buildError(context), + ); } final scaleBoundaries = ScaleBoundaries( From e78144ea316e8b09ef998c0463cb2b9faef3b14e Mon Sep 17 00:00:00 2001 From: Snowknight26 Date: Wed, 27 Aug 2025 22:00:50 -0500 Subject: [PATCH 352/748] fix(web): Translate confirmation modal header and action buttons (#21330) fix(web): Translate confirmation modal --- web/src/routes/+layout.svelte | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/src/routes/+layout.svelte b/web/src/routes/+layout.svelte index 71958d9d9f..d2311a4204 100644 --- a/web/src/routes/+layout.svelte +++ b/web/src/routes/+layout.svelte @@ -36,6 +36,8 @@ close: $t('close'), show_password: $t('show_password'), hide_password: $t('hide_password'), + confirm: $t('confirm'), + cancel: $t('cancel'), }); }); From 0df88fc22bca588dc743ac57ec69fc784c7f699c Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Thu, 28 Aug 2025 19:41:54 +0530 Subject: [PATCH 353/748] feat: beta background sync (#21243) * feat: ios background sync # Conflicts: # mobile/ios/Runner/Info.plist * feat: Android sync * add local sync worker and rename stuff * group upload notifications * uncomment onresume beta handling * rename methods --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Co-authored-by: Alex --- mobile/analysis_options.yaml | 1 + .../kotlin/app/alextran/immich/ImmichApp.kt | 24 +- .../app/alextran/immich/MainActivity.kt | 35 ++- .../immich/background/BackgroundWorker.g.kt | 238 ++++++++++++++ .../immich/background/BackgroundWorker.kt | 162 ++++++++++ .../background/BackgroundWorkerApiImpl.kt | 92 ++++++ .../immich/background/MediaObserver.kt | 34 ++ mobile/ios/Runner.xcodeproj/project.pbxproj | 32 +- mobile/ios/Runner/AppDelegate.swift | 17 +- .../Background/BackgroundWorker.g.swift | 245 +++++++++++++++ .../Runner/Background/BackgroundWorker.swift | 202 ++++++++++++ .../Background/BackgroundWorkerApiImpl.swift | 155 +++++++++ mobile/ios/Runner/Info.plist | 15 +- .../services/background_worker.service.dart | 232 ++++++++++++++ mobile/lib/domain/services/hash.service.dart | 20 ++ mobile/lib/domain/services/log.service.dart | 5 + mobile/lib/domain/utils/background_sync.dart | 22 ++ mobile/lib/main.dart | 47 +-- .../lib/pages/backup/drift_backup.page.dart | 3 + .../pages/common/change_experience.page.dart | 4 + .../lib/platform/background_worker_api.g.dart | 296 ++++++++++++++++++ .../lib/providers/backup/backup.provider.dart | 4 + .../lib/repositories/upload.repository.dart | 8 +- mobile/lib/services/upload.service.dart | 32 +- mobile/lib/utils/bootstrap.dart | 33 ++ mobile/lib/utils/isolate.dart | 6 +- mobile/makefile | 2 + mobile/pigeon/background_worker_api.dart | 48 +++ 28 files changed, 1933 insertions(+), 81 deletions(-) create mode 100644 mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.g.kt create mode 100644 mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.kt create mode 100644 mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerApiImpl.kt create mode 100644 mobile/android/app/src/main/kotlin/app/alextran/immich/background/MediaObserver.kt create mode 100644 mobile/ios/Runner/Background/BackgroundWorker.g.swift create mode 100644 mobile/ios/Runner/Background/BackgroundWorker.swift create mode 100644 mobile/ios/Runner/Background/BackgroundWorkerApiImpl.swift create mode 100644 mobile/lib/domain/services/background_worker.service.dart create mode 100644 mobile/lib/platform/background_worker_api.g.dart create mode 100644 mobile/pigeon/background_worker_api.dart diff --git a/mobile/analysis_options.yaml b/mobile/analysis_options.yaml index 1b0b7170d2..bef051bff2 100644 --- a/mobile/analysis_options.yaml +++ b/mobile/analysis_options.yaml @@ -81,6 +81,7 @@ custom_lint: # acceptable exceptions for the time being (until Isar is fully replaced) - lib/providers/app_life_cycle.provider.dart - integration_test/test_utils/general_helper.dart + - lib/domain/services/background_worker.service.dart - lib/main.dart - lib/pages/album/album_asset_selection.page.dart - lib/routing/router.dart diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/ImmichApp.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/ImmichApp.kt index ff806870f9..4237643233 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/ImmichApp.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/ImmichApp.kt @@ -5,15 +5,15 @@ import androidx.work.Configuration import androidx.work.WorkManager class ImmichApp : Application() { - override fun onCreate() { - super.onCreate() - val config = Configuration.Builder().build() - WorkManager.initialize(this, config) - // always start BackupWorker after WorkManager init; this fixes the following bug: - // After the process is killed (by user or system), the first trigger (taking a new picture) is lost. - // Thus, the BackupWorker is not started. If the system kills the process after each initialization - // (because of low memory etc.), the backup is never performed. - // As a workaround, we also run a backup check when initializing the application - ContentObserverWorker.startBackupWorker(context = this, delayMilliseconds = 0) - } -} \ No newline at end of file + override fun onCreate() { + super.onCreate() + val config = Configuration.Builder().build() + WorkManager.initialize(this, config) + // always start BackupWorker after WorkManager init; this fixes the following bug: + // After the process is killed (by user or system), the first trigger (taking a new picture) is lost. + // Thus, the BackupWorker is not started. If the system kills the process after each initialization + // (because of low memory etc.), the backup is never performed. + // As a workaround, we also run a backup check when initializing the application + ContentObserverWorker.startBackupWorker(context = this, delayMilliseconds = 0) + } +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/MainActivity.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/MainActivity.kt index b1a50695a3..a87feddd1a 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/MainActivity.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/MainActivity.kt @@ -1,8 +1,10 @@ package app.alextran.immich +import android.content.Context import android.os.Build import android.os.ext.SdkExtensions -import androidx.annotation.NonNull +import app.alextran.immich.background.BackgroundWorkerApiImpl +import app.alextran.immich.background.BackgroundWorkerFgHostApi import app.alextran.immich.images.ThumbnailApi import app.alextran.immich.images.ThumbnailsImpl import app.alextran.immich.sync.NativeSyncApi @@ -12,19 +14,26 @@ import io.flutter.embedding.android.FlutterFragmentActivity import io.flutter.embedding.engine.FlutterEngine class MainActivity : FlutterFragmentActivity() { - override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { + override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) - flutterEngine.plugins.add(BackgroundServicePlugin()) - flutterEngine.plugins.add(HttpSSLOptionsPlugin()) - // No need to set up method channel here as it's now handled in the plugin + registerPlugins(this, flutterEngine) + } - val nativeSyncApiImpl = - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R || SdkExtensions.getExtensionVersion(Build.VERSION_CODES.R) < 1) { - NativeSyncApiImpl26(this) - } else { - NativeSyncApiImpl30(this) - } - NativeSyncApi.setUp(flutterEngine.dartExecutor.binaryMessenger, nativeSyncApiImpl) - ThumbnailApi.setUp(flutterEngine.dartExecutor.binaryMessenger, ThumbnailsImpl(this)) + companion object { + fun registerPlugins(ctx: Context, flutterEngine: FlutterEngine) { + flutterEngine.plugins.add(BackgroundServicePlugin()) + flutterEngine.plugins.add(HttpSSLOptionsPlugin()) + + val messenger = flutterEngine.dartExecutor.binaryMessenger + val nativeSyncApiImpl = + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R || SdkExtensions.getExtensionVersion(Build.VERSION_CODES.R) < 1) { + NativeSyncApiImpl26(ctx) + } else { + NativeSyncApiImpl30(ctx) + } + NativeSyncApi.setUp(messenger, nativeSyncApiImpl) + ThumbnailApi.setUp(messenger, ThumbnailsImpl(ctx)) + BackgroundWorkerFgHostApi.setUp(messenger, BackgroundWorkerApiImpl(ctx)) + } } } diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.g.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.g.kt new file mode 100644 index 0000000000..39a2345a9b --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.g.kt @@ -0,0 +1,238 @@ +// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// See also: https://pub.dev/packages/pigeon +@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") + +package app.alextran.immich.background + +import android.util.Log +import io.flutter.plugin.common.BasicMessageChannel +import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.EventChannel +import io.flutter.plugin.common.MessageCodec +import io.flutter.plugin.common.StandardMethodCodec +import io.flutter.plugin.common.StandardMessageCodec +import java.io.ByteArrayOutputStream +import java.nio.ByteBuffer +private object BackgroundWorkerPigeonUtils { + + fun createConnectionError(channelName: String): FlutterError { + return FlutterError("channel-error", "Unable to establish connection on channel: '$channelName'.", "") } + + fun wrapResult(result: Any?): List { + return listOf(result) + } + + fun wrapError(exception: Throwable): List { + return if (exception is FlutterError) { + listOf( + exception.code, + exception.message, + exception.details + ) + } else { + listOf( + exception.javaClass.simpleName, + exception.toString(), + "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception) + ) + } + } +} + +/** + * Error class for passing custom error details to Flutter via a thrown PlatformException. + * @property code The error code. + * @property message The error message. + * @property details The error details. Must be a datatype supported by the api codec. + */ +class FlutterError ( + val code: String, + override val message: String? = null, + val details: Any? = null +) : Throwable() +private open class BackgroundWorkerPigeonCodec : StandardMessageCodec() { + override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { + return super.readValueOfType(type, buffer) + } + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + super.writeValue(stream, value) + } +} + +/** Generated interface from Pigeon that represents a handler of messages from Flutter. */ +interface BackgroundWorkerFgHostApi { + fun enableSyncWorker() + fun enableUploadWorker(callbackHandle: Long) + fun disableUploadWorker() + + companion object { + /** The codec used by BackgroundWorkerFgHostApi. */ + val codec: MessageCodec by lazy { + BackgroundWorkerPigeonCodec() + } + /** Sets up an instance of `BackgroundWorkerFgHostApi` to handle messages through the `binaryMessenger`. */ + @JvmOverloads + fun setUp(binaryMessenger: BinaryMessenger, api: BackgroundWorkerFgHostApi?, messageChannelSuffix: String = "") { + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.enableSyncWorker$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.enableSyncWorker() + listOf(null) + } catch (exception: Throwable) { + BackgroundWorkerPigeonUtils.wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.enableUploadWorker$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val callbackHandleArg = args[0] as Long + val wrapped: List = try { + api.enableUploadWorker(callbackHandleArg) + listOf(null) + } catch (exception: Throwable) { + BackgroundWorkerPigeonUtils.wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.disableUploadWorker$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.disableUploadWorker() + listOf(null) + } catch (exception: Throwable) { + BackgroundWorkerPigeonUtils.wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + } + } +} +/** Generated interface from Pigeon that represents a handler of messages from Flutter. */ +interface BackgroundWorkerBgHostApi { + fun onInitialized() + + companion object { + /** The codec used by BackgroundWorkerBgHostApi. */ + val codec: MessageCodec by lazy { + BackgroundWorkerPigeonCodec() + } + /** Sets up an instance of `BackgroundWorkerBgHostApi` to handle messages through the `binaryMessenger`. */ + @JvmOverloads + fun setUp(binaryMessenger: BinaryMessenger, api: BackgroundWorkerBgHostApi?, messageChannelSuffix: String = "") { + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerBgHostApi.onInitialized$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.onInitialized() + listOf(null) + } catch (exception: Throwable) { + BackgroundWorkerPigeonUtils.wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + } + } +} +/** Generated class from Pigeon that represents Flutter messages that can be called from Kotlin. */ +class BackgroundWorkerFlutterApi(private val binaryMessenger: BinaryMessenger, private val messageChannelSuffix: String = "") { + companion object { + /** The codec used by BackgroundWorkerFlutterApi. */ + val codec: MessageCodec by lazy { + BackgroundWorkerPigeonCodec() + } + } + fun onLocalSync(maxSecondsArg: Long?, callback: (Result) -> Unit) +{ + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onLocalSync$separatedMessageChannelSuffix" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(maxSecondsArg)) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + callback(Result.success(Unit)) + } + } else { + callback(Result.failure(BackgroundWorkerPigeonUtils.createConnectionError(channelName))) + } + } + } + fun onIosUpload(isRefreshArg: Boolean, maxSecondsArg: Long?, callback: (Result) -> Unit) +{ + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onIosUpload$separatedMessageChannelSuffix" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(isRefreshArg, maxSecondsArg)) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + callback(Result.success(Unit)) + } + } else { + callback(Result.failure(BackgroundWorkerPigeonUtils.createConnectionError(channelName))) + } + } + } + fun onAndroidUpload(callback: (Result) -> Unit) +{ + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onAndroidUpload$separatedMessageChannelSuffix" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(null) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + callback(Result.success(Unit)) + } + } else { + callback(Result.failure(BackgroundWorkerPigeonUtils.createConnectionError(channelName))) + } + } + } + fun cancel(callback: (Result) -> Unit) +{ + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.cancel$separatedMessageChannelSuffix" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(null) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + callback(Result.success(Unit)) + } + } else { + callback(Result.failure(BackgroundWorkerPigeonUtils.createConnectionError(channelName))) + } + } + } +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.kt new file mode 100644 index 0000000000..0ce601b363 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.kt @@ -0,0 +1,162 @@ +package app.alextran.immich.background + +import android.content.Context +import android.os.Handler +import android.os.Looper +import android.util.Log +import androidx.work.ListenableWorker +import androidx.work.WorkerParameters +import app.alextran.immich.MainActivity +import com.google.common.util.concurrent.ListenableFuture +import com.google.common.util.concurrent.SettableFuture +import io.flutter.FlutterInjector +import io.flutter.embedding.engine.FlutterEngine +import io.flutter.embedding.engine.dart.DartExecutor.DartCallback +import io.flutter.embedding.engine.loader.FlutterLoader +import io.flutter.view.FlutterCallbackInformation + +private const val TAG = "BackgroundWorker" + +enum class BackgroundTaskType { + LOCAL_SYNC, + UPLOAD, +} + +class BackgroundWorker(context: Context, params: WorkerParameters) : + ListenableWorker(context, params), BackgroundWorkerBgHostApi { + private val ctx: Context = context.applicationContext + + /// The Flutter loader that loads the native Flutter library and resources. + /// This must be initialized before starting the Flutter engine. + private var loader: FlutterLoader = FlutterInjector.instance().flutterLoader() + + /// The Flutter engine created specifically for background execution. + /// This is a separate instance from the main Flutter engine that handles the UI. + /// It operates in its own isolate and doesn't share memory with the main engine. + /// Must be properly started, registered, and torn down during background execution. + private var engine: FlutterEngine? = null + + // Used to call methods on the flutter side + private var flutterApi: BackgroundWorkerFlutterApi? = null + + /// Result returned when the background task completes. This is used to signal + /// to the WorkManager that the task has finished, either successfully or with failure. + private val completionHandler: SettableFuture = SettableFuture.create() + + /// Flag to track whether the background task has completed to prevent duplicate completions + private var isComplete = false + + init { + if (!loader.initialized()) { + loader.startInitialization(ctx) + } + } + + override fun startWork(): ListenableFuture { + Log.i(TAG, "Starting background upload worker") + + loader.ensureInitializationCompleteAsync(ctx, null, Handler(Looper.getMainLooper())) { + engine = FlutterEngine(ctx) + + // Retrieve the callback handle stored by the main Flutter app + // This handle points to the Flutter function that should be executed in the background + val callbackHandle = + ctx.getSharedPreferences(BackgroundWorkerApiImpl.SHARED_PREF_NAME, Context.MODE_PRIVATE) + .getLong(BackgroundWorkerApiImpl.SHARED_PREF_CALLBACK_HANDLE, 0L) + + if (callbackHandle == 0L) { + // Without a valid callback handle, we cannot start the Flutter background execution + complete(Result.failure()) + return@ensureInitializationCompleteAsync + } + + // Start the Flutter engine with the specified callback as the entry point + val callback = FlutterCallbackInformation.lookupCallbackInformation(callbackHandle) + if (callback == null) { + complete(Result.failure()) + return@ensureInitializationCompleteAsync + } + + // Register custom plugins + MainActivity.registerPlugins(ctx, engine!!) + flutterApi = + BackgroundWorkerFlutterApi(binaryMessenger = engine!!.dartExecutor.binaryMessenger) + BackgroundWorkerBgHostApi.setUp( + binaryMessenger = engine!!.dartExecutor.binaryMessenger, + api = this + ) + + engine!!.dartExecutor.executeDartCallback( + DartCallback(ctx.assets, loader.findAppBundlePath(), callback) + ) + } + + return completionHandler + } + + /** + * Called by the Flutter side when it has finished initialization and is ready to receive commands. + * Routes the appropriate task type (refresh or processing) to the corresponding Flutter method. + * This method acts as a bridge between the native Android background task system and Flutter. + */ + override fun onInitialized() { + val taskTypeIndex = inputData.getInt(BackgroundWorkerApiImpl.WORKER_DATA_TASK_TYPE, 0) + val taskType = BackgroundTaskType.entries[taskTypeIndex] + + when (taskType) { + BackgroundTaskType.LOCAL_SYNC -> flutterApi?.onLocalSync(null) { handleHostResult(it) } + BackgroundTaskType.UPLOAD -> flutterApi?.onAndroidUpload { handleHostResult(it) } + } + } + + /** + * Called when the system has to stop this worker because constraints are + * no longer met or the system needs resources for more important tasks + * This is also called when the worker has been explicitly cancelled or replaced + */ + override fun onStopped() { + Log.d(TAG, "About to stop BackupWorker") + + if (isComplete) { + return + } + + Handler(Looper.getMainLooper()).postAtFrontOfQueue { + if (flutterApi != null) { + flutterApi?.cancel { + complete(Result.failure()) + } + } + } + + Handler(Looper.getMainLooper()).postDelayed({ + complete(Result.failure()) + }, 5000) + } + + private fun handleHostResult(result: kotlin.Result) { + if (isComplete) { + return + } + + result.fold( + onSuccess = { _ -> complete(Result.success()) }, + onFailure = { _ -> onStopped() } + ) + } + + /** + * Cleans up resources by destroying the Flutter engine context and invokes the completion handler. + * This method ensures that the background task is marked as complete, releases the Flutter engine, + * and notifies the caller of the task's success or failure. This is the final step in the + * background task lifecycle and should only be called once per task instance. + * + * - Parameter success: Indicates whether the background task completed successfully + */ + private fun complete(success: Result) { + isComplete = true + engine?.destroy() + flutterApi = null + completionHandler.set(success) + } +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerApiImpl.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerApiImpl.kt new file mode 100644 index 0000000000..7a3226f961 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerApiImpl.kt @@ -0,0 +1,92 @@ +package app.alextran.immich.background + +import android.content.Context +import android.provider.MediaStore +import android.util.Log +import androidx.core.content.edit +import androidx.work.BackoffPolicy +import androidx.work.Constraints +import androidx.work.Data +import androidx.work.ExistingWorkPolicy +import androidx.work.OneTimeWorkRequest +import androidx.work.WorkManager +import java.util.concurrent.TimeUnit + +private const val TAG = "BackgroundUploadImpl" + +class BackgroundWorkerApiImpl(context: Context) : BackgroundWorkerFgHostApi { + private val ctx: Context = context.applicationContext + override fun enableSyncWorker() { + enqueueMediaObserver(ctx) + Log.i(TAG, "Scheduled media observer") + } + + override fun enableUploadWorker(callbackHandle: Long) { + updateUploadEnabled(ctx, true) + updateCallbackHandle(ctx, callbackHandle) + Log.i(TAG, "Scheduled background upload tasks") + } + + override fun disableUploadWorker() { + updateUploadEnabled(ctx, false) + WorkManager.getInstance(ctx).cancelUniqueWork(BACKGROUND_WORKER_NAME) + Log.i(TAG, "Cancelled background upload tasks") + } + + companion object { + private const val BACKGROUND_WORKER_NAME = "immich/BackgroundWorkerV1" + private const val OBSERVER_WORKER_NAME = "immich/MediaObserverV1" + + const val WORKER_DATA_TASK_TYPE = "taskType" + + const val SHARED_PREF_NAME = "Immich::Background" + const val SHARED_PREF_BACKUP_ENABLED = "Background::backup::enabled" + const val SHARED_PREF_CALLBACK_HANDLE = "Background::backup::callbackHandle" + + private fun updateUploadEnabled(context: Context, enabled: Boolean) { + context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE).edit { + putBoolean(SHARED_PREF_BACKUP_ENABLED, enabled) + } + } + + private fun updateCallbackHandle(context: Context, callbackHandle: Long) { + context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE).edit { + putLong(SHARED_PREF_CALLBACK_HANDLE, callbackHandle) + } + } + + fun enqueueMediaObserver(ctx: Context) { + val constraints = Constraints.Builder() + .addContentUriTrigger(MediaStore.Images.Media.INTERNAL_CONTENT_URI, true) + .addContentUriTrigger(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true) + .addContentUriTrigger(MediaStore.Video.Media.INTERNAL_CONTENT_URI, true) + .addContentUriTrigger(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true) + .setTriggerContentUpdateDelay(5, TimeUnit.SECONDS) + .setTriggerContentMaxDelay(1, TimeUnit.MINUTES) + .build() + + val work = OneTimeWorkRequest.Builder(MediaObserver::class.java) + .setConstraints(constraints) + .build() + WorkManager.getInstance(ctx) + .enqueueUniqueWork(OBSERVER_WORKER_NAME, ExistingWorkPolicy.REPLACE, work) + + Log.i(TAG, "Enqueued media observer worker with name: $OBSERVER_WORKER_NAME") + } + + fun enqueueBackgroundWorker(ctx: Context, taskType: BackgroundTaskType) { + val constraints = Constraints.Builder().setRequiresBatteryNotLow(true).build() + + val data = Data.Builder() + data.putInt(WORKER_DATA_TASK_TYPE, taskType.ordinal) + val work = OneTimeWorkRequest.Builder(BackgroundWorker::class.java) + .setConstraints(constraints) + .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 1, TimeUnit.MINUTES) + .setInputData(data.build()).build() + WorkManager.getInstance(ctx) + .enqueueUniqueWork(BACKGROUND_WORKER_NAME, ExistingWorkPolicy.REPLACE, work) + + Log.i(TAG, "Enqueued background worker with name: $BACKGROUND_WORKER_NAME") + } + } +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/MediaObserver.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/MediaObserver.kt new file mode 100644 index 0000000000..0ec6eeb3a5 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/MediaObserver.kt @@ -0,0 +1,34 @@ +package app.alextran.immich.background + +import android.content.Context +import android.util.Log +import androidx.work.Worker +import androidx.work.WorkerParameters + +class MediaObserver(context: Context, params: WorkerParameters) : Worker(context, params) { + private val ctx: Context = context.applicationContext + + override fun doWork(): Result { + Log.i("MediaObserver", "Content change detected, starting background worker") + + // Enqueue backup worker only if there are new media changes + if (triggeredContentUris.isNotEmpty()) { + val type = + if (isBackupEnabled(ctx)) BackgroundTaskType.UPLOAD else BackgroundTaskType.LOCAL_SYNC + BackgroundWorkerApiImpl.enqueueBackgroundWorker(ctx, type) + } + + // Re-enqueue itself to listen for future changes + BackgroundWorkerApiImpl.enqueueMediaObserver(ctx) + return Result.success() + } + + private fun isBackupEnabled(context: Context): Boolean { + val prefs = + context.getSharedPreferences( + BackgroundWorkerApiImpl.SHARED_PREF_NAME, + Context.MODE_PRIVATE + ) + return prefs.getBoolean(BackgroundWorkerApiImpl.SHARED_PREF_BACKUP_ENABLED, false) + } +} diff --git a/mobile/ios/Runner.xcodeproj/project.pbxproj b/mobile/ios/Runner.xcodeproj/project.pbxproj index 827c9be881..087297ab71 100644 --- a/mobile/ios/Runner.xcodeproj/project.pbxproj +++ b/mobile/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 77; objects = { /* Begin PBXBuildFile section */ @@ -16,6 +16,9 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + B21E34AA2E5AFD2B0031FDB9 /* BackgroundWorkerApiImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = B21E34A92E5AFD210031FDB9 /* BackgroundWorkerApiImpl.swift */; }; + B21E34AC2E5B09190031FDB9 /* BackgroundWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = B21E34AB2E5B09100031FDB9 /* BackgroundWorker.swift */; }; + B2BE315F2E5E5229006EEF88 /* BackgroundWorker.g.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2BE315E2E5E5229006EEF88 /* BackgroundWorker.g.swift */; }; D218389C4A4C4693F141F7D1 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 886774DBDDE6B35BF2B4F2CD /* Pods_Runner.framework */; }; F02538E92DFBCBDD008C3FA3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; F0B57D3A2DF764BD00DC5BCC /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0B57D392DF764BD00DC5BCC /* WidgetKit.framework */; }; @@ -92,6 +95,9 @@ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; B1FBA9EE014DE20271B0FE77 /* Pods-ShareExtension.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.profile.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.profile.xcconfig"; sourceTree = ""; }; + B21E34A92E5AFD210031FDB9 /* BackgroundWorkerApiImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundWorkerApiImpl.swift; sourceTree = ""; }; + B21E34AB2E5B09100031FDB9 /* BackgroundWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundWorker.swift; sourceTree = ""; }; + B2BE315E2E5E5229006EEF88 /* BackgroundWorker.g.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundWorker.g.swift; sourceTree = ""; }; E0E99CDC17B3EB7FA8BA2332 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; F0B57D382DF764BD00DC5BCC /* WidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = WidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; F0B57D392DF764BD00DC5BCC /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; @@ -123,8 +129,6 @@ /* Begin PBXFileSystemSynchronizedRootGroup section */ B2CF7F8C2DDE4EBB00744BF6 /* Sync */ = { isa = PBXFileSystemSynchronizedRootGroup; - exceptions = ( - ); path = Sync; sourceTree = ""; }; @@ -237,6 +241,7 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( + B21E34A62E5AF9760031FDB9 /* Background */, B2CF7F8C2DDE4EBB00744BF6 /* Sync */, FA9973382CF6DF4B000EF859 /* Runner.entitlements */, 65DD438629917FAD0047FFA8 /* BackgroundSync */, @@ -254,6 +259,16 @@ path = Runner; sourceTree = ""; }; + B21E34A62E5AF9760031FDB9 /* Background */ = { + isa = PBXGroup; + children = ( + B2BE315E2E5E5229006EEF88 /* BackgroundWorker.g.swift */, + B21E34AB2E5B09100031FDB9 /* BackgroundWorker.swift */, + B21E34A92E5AFD210031FDB9 /* BackgroundWorkerApiImpl.swift */, + ); + path = Background; + sourceTree = ""; + }; FAC6F8B62D287F120078CB2F /* ShareExtension */ = { isa = PBXGroup; children = ( @@ -490,10 +505,14 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", ); + inputPaths = ( + ); name = "[CP] Copy Pods Resources"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", ); + outputPaths = ( + ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; @@ -522,10 +541,14 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); + inputPaths = ( + ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); + outputPaths = ( + ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; @@ -540,10 +563,13 @@ files = ( 65F32F31299BD2F800CE9261 /* BackgroundServicePlugin.swift in Sources */, 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + B21E34AC2E5B09190031FDB9 /* BackgroundWorker.swift in Sources */, FEAFA8732E4D42F4001E47FE /* Thumbhash.swift in Sources */, FED3B1962E253E9B0030FD97 /* ThumbnailsImpl.swift in Sources */, + B21E34AA2E5AFD2B0031FDB9 /* BackgroundWorkerApiImpl.swift in Sources */, FED3B1972E253E9B0030FD97 /* Thumbnails.g.swift in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + B2BE315F2E5E5229006EEF88 /* BackgroundWorker.g.swift in Sources */, 65F32F33299D349D00CE9261 /* BackgroundSyncWorker.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/mobile/ios/Runner/AppDelegate.swift b/mobile/ios/Runner/AppDelegate.swift index dedda5bd12..04422eb2b4 100644 --- a/mobile/ios/Runner/AppDelegate.swift +++ b/mobile/ios/Runner/AppDelegate.swift @@ -19,13 +19,12 @@ import UIKit } GeneratedPluginRegistrant.register(with: self) - BackgroundServicePlugin.registerBackgroundProcessing() - - BackgroundServicePlugin.register(with: self.registrar(forPlugin: "BackgroundServicePlugin")!) - let controller: FlutterViewController = window?.rootViewController as! FlutterViewController - NativeSyncApiSetup.setUp(binaryMessenger: controller.binaryMessenger, api: NativeSyncApiImpl()) - ThumbnailApiSetup.setUp(binaryMessenger: controller.binaryMessenger, api: ThumbnailApiImpl()) + AppDelegate.registerPlugins(binaryMessenger: controller.binaryMessenger) + BackgroundServicePlugin.register(with: self.registrar(forPlugin: "BackgroundServicePlugin")!) + + BackgroundServicePlugin.registerBackgroundProcessing() + BackgroundWorkerApiImpl.registerBackgroundProcessing() BackgroundServicePlugin.setPluginRegistrantCallback { registry in if !registry.hasPlugin("org.cocoapods.path-provider-foundation") { @@ -51,4 +50,10 @@ import UIKit return super.application(application, didFinishLaunchingWithOptions: launchOptions) } + + public static func registerPlugins(binaryMessenger: FlutterBinaryMessenger) { + NativeSyncApiSetup.setUp(binaryMessenger: binaryMessenger, api: NativeSyncApiImpl()) + ThumbnailApiSetup.setUp(binaryMessenger: binaryMessenger, api: ThumbnailApiImpl()) + BackgroundWorkerFgHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: BackgroundWorkerApiImpl()) + } } diff --git a/mobile/ios/Runner/Background/BackgroundWorker.g.swift b/mobile/ios/Runner/Background/BackgroundWorker.g.swift new file mode 100644 index 0000000000..e9513db8da --- /dev/null +++ b/mobile/ios/Runner/Background/BackgroundWorker.g.swift @@ -0,0 +1,245 @@ +// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +import Foundation + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#else + #error("Unsupported platform.") +#endif + +private func wrapResult(_ result: Any?) -> [Any?] { + return [result] +} + +private func wrapError(_ error: Any) -> [Any?] { + if let pigeonError = error as? PigeonError { + return [ + pigeonError.code, + pigeonError.message, + pigeonError.details, + ] + } + if let flutterError = error as? FlutterError { + return [ + flutterError.code, + flutterError.message, + flutterError.details, + ] + } + return [ + "\(error)", + "\(type(of: error))", + "Stacktrace: \(Thread.callStackSymbols)", + ] +} + +private func createConnectionError(withChannelName channelName: String) -> PigeonError { + return PigeonError(code: "channel-error", message: "Unable to establish connection on channel: '\(channelName)'.", details: "") +} + +private func isNullish(_ value: Any?) -> Bool { + return value is NSNull || value == nil +} + +private func nilOrValue(_ value: Any?) -> T? { + if value is NSNull { return nil } + return value as! T? +} + + +private class BackgroundWorkerPigeonCodecReader: FlutterStandardReader { +} + +private class BackgroundWorkerPigeonCodecWriter: FlutterStandardWriter { +} + +private class BackgroundWorkerPigeonCodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + return BackgroundWorkerPigeonCodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + return BackgroundWorkerPigeonCodecWriter(data: data) + } +} + +class BackgroundWorkerPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { + static let shared = BackgroundWorkerPigeonCodec(readerWriter: BackgroundWorkerPigeonCodecReaderWriter()) +} + +/// Generated protocol from Pigeon that represents a handler of messages from Flutter. +protocol BackgroundWorkerFgHostApi { + func enableSyncWorker() throws + func enableUploadWorker(callbackHandle: Int64) throws + func disableUploadWorker() throws +} + +/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. +class BackgroundWorkerFgHostApiSetup { + static var codec: FlutterStandardMessageCodec { BackgroundWorkerPigeonCodec.shared } + /// Sets up an instance of `BackgroundWorkerFgHostApi` to handle messages through the `binaryMessenger`. + static func setUp(binaryMessenger: FlutterBinaryMessenger, api: BackgroundWorkerFgHostApi?, messageChannelSuffix: String = "") { + let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + let enableSyncWorkerChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.enableSyncWorker\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + enableSyncWorkerChannel.setMessageHandler { _, reply in + do { + try api.enableSyncWorker() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + enableSyncWorkerChannel.setMessageHandler(nil) + } + let enableUploadWorkerChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.enableUploadWorker\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + enableUploadWorkerChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let callbackHandleArg = args[0] as! Int64 + do { + try api.enableUploadWorker(callbackHandle: callbackHandleArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + enableUploadWorkerChannel.setMessageHandler(nil) + } + let disableUploadWorkerChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.disableUploadWorker\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + disableUploadWorkerChannel.setMessageHandler { _, reply in + do { + try api.disableUploadWorker() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + disableUploadWorkerChannel.setMessageHandler(nil) + } + } +} +/// Generated protocol from Pigeon that represents a handler of messages from Flutter. +protocol BackgroundWorkerBgHostApi { + func onInitialized() throws +} + +/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. +class BackgroundWorkerBgHostApiSetup { + static var codec: FlutterStandardMessageCodec { BackgroundWorkerPigeonCodec.shared } + /// Sets up an instance of `BackgroundWorkerBgHostApi` to handle messages through the `binaryMessenger`. + static func setUp(binaryMessenger: FlutterBinaryMessenger, api: BackgroundWorkerBgHostApi?, messageChannelSuffix: String = "") { + let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + let onInitializedChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.BackgroundWorkerBgHostApi.onInitialized\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + onInitializedChannel.setMessageHandler { _, reply in + do { + try api.onInitialized() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + onInitializedChannel.setMessageHandler(nil) + } + } +} +/// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift. +protocol BackgroundWorkerFlutterApiProtocol { + func onLocalSync(maxSeconds maxSecondsArg: Int64?, completion: @escaping (Result) -> Void) + func onIosUpload(isRefresh isRefreshArg: Bool, maxSeconds maxSecondsArg: Int64?, completion: @escaping (Result) -> Void) + func onAndroidUpload(completion: @escaping (Result) -> Void) + func cancel(completion: @escaping (Result) -> Void) +} +class BackgroundWorkerFlutterApi: BackgroundWorkerFlutterApiProtocol { + private let binaryMessenger: FlutterBinaryMessenger + private let messageChannelSuffix: String + init(binaryMessenger: FlutterBinaryMessenger, messageChannelSuffix: String = "") { + self.binaryMessenger = binaryMessenger + self.messageChannelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + } + var codec: BackgroundWorkerPigeonCodec { + return BackgroundWorkerPigeonCodec.shared + } + func onLocalSync(maxSeconds maxSecondsArg: Int64?, completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onLocalSync\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([maxSecondsArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(())) + } + } + } + func onIosUpload(isRefresh isRefreshArg: Bool, maxSeconds maxSecondsArg: Int64?, completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onIosUpload\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([isRefreshArg, maxSecondsArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(())) + } + } + } + func onAndroidUpload(completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onAndroidUpload\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage(nil) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(())) + } + } + } + func cancel(completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.cancel\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage(nil) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(())) + } + } + } +} diff --git a/mobile/ios/Runner/Background/BackgroundWorker.swift b/mobile/ios/Runner/Background/BackgroundWorker.swift new file mode 100644 index 0000000000..db849d942b --- /dev/null +++ b/mobile/ios/Runner/Background/BackgroundWorker.swift @@ -0,0 +1,202 @@ +import BackgroundTasks +import Flutter + +enum BackgroundTaskType { case localSync, refreshUpload, processingUpload } + +/* + * DEBUG: Testing Background Tasks in Xcode + * + * To test background task functionality during development: + * 1. Pause the application in Xcode debugger + * 2. In the debugger console, enter one of the following commands: + + ## For local sync (short-running sync): + + e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"app.alextran.immich.background.localSync"] + + ## For background refresh (short-running sync): + + e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"app.alextran.immich.background.refreshUpload"] + + ## For background processing (long-running upload): + + e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"app.alextran.immich.background.processingUpload"] + + * To simulate task expiration (useful for testing expiration handlers): + + e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@"app.alextran.immich.background.localSync"] + + e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@"app.alextran.immich.background.refreshUpload"] + + e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@"app.alextran.immich.background.processingUpload"] + + * 3. Resume the application to see the background code execute + * + * NOTE: This must be tested on a physical device, not in the simulator. + * In testing, only the background processing task can be reliably simulated. + * These commands submit the respective task to BGTaskScheduler for immediate processing. + * Use the expiration commands to test how the app handles iOS terminating background tasks. + */ + + +/// The background worker which creates a new Flutter VM, communicates with it +/// to run the backup job, and then finishes execution and calls back to its callback handler. +/// This class manages a separate Flutter engine instance for background execution, +/// independent of the main UI Flutter engine. +class BackgroundWorker: BackgroundWorkerBgHostApi { + private let taskType: BackgroundTaskType + /// The maximum number of seconds to run the task before timing out + private let maxSeconds: Int? + /// Callback function to invoke when the background task completes + private let completionHandler: (_ success: Bool) -> Void + + /// The Flutter engine created specifically for background execution. + /// This is a separate instance from the main Flutter engine that handles the UI. + /// It operates in its own isolate and doesn't share memory with the main engine. + /// Must be properly started, registered, and torn down during background execution. + private let engine = FlutterEngine(name: "BackgroundImmich") + + /// Used to call methods on the flutter side + private var flutterApi: BackgroundWorkerFlutterApi? + + /// Flag to track whether the background task has completed to prevent duplicate completions + private var isComplete = false + + /** + * Initializes a new background worker with the specified task type and execution constraints. + * Creates a new Flutter engine instance for background execution and sets up the necessary + * communication channels between native iOS and Flutter code. + * + * - Parameters: + * - taskType: The type of background task to execute (upload or sync task) + * - maxSeconds: Optional maximum execution time in seconds before the task is cancelled + * - completionHandler: Callback function invoked when the task completes, with success status + */ + init(taskType: BackgroundTaskType, maxSeconds: Int?, completionHandler: @escaping (_ success: Bool) -> Void) { + self.taskType = taskType + self.maxSeconds = maxSeconds + self.completionHandler = completionHandler + // Should be initialized only after the engine starts running + self.flutterApi = nil + } + + /** + * Starts the background Flutter engine and begins execution of the background task. + * Retrieves the callback handle from UserDefaults, looks up the Flutter callback, + * starts the engine, and sets up a timeout timer if specified. + */ + func run() { + // Retrieve the callback handle stored by the main Flutter app + // This handle points to the Flutter function that should be executed in the background + let callbackHandle = Int64(UserDefaults.standard.string( + forKey: BackgroundWorkerApiImpl.backgroundUploadCallbackHandleKey) ?? "0") ?? 0 + + if callbackHandle == 0 { + // Without a valid callback handle, we cannot start the Flutter background execution + complete(success: false) + return + } + + // Use the callback handle to retrieve the actual Flutter callback information + guard let callback = FlutterCallbackCache.lookupCallbackInformation(callbackHandle) else { + // The callback handle is invalid or the callback was not found + complete(success: false) + return + } + + // Start the Flutter engine with the specified callback as the entry point + let isRunning = engine.run( + withEntrypoint: callback.callbackName, + libraryURI: callback.callbackLibraryPath + ) + + // Verify that the Flutter engine started successfully + if !isRunning { + complete(success: false) + return + } + + // Register plugins in the new engine + GeneratedPluginRegistrant.register(with: engine) + // Register custom plugins + AppDelegate.registerPlugins(binaryMessenger: engine.binaryMessenger) + flutterApi = BackgroundWorkerFlutterApi(binaryMessenger: engine.binaryMessenger) + BackgroundWorkerBgHostApiSetup.setUp(binaryMessenger: engine.binaryMessenger, api: self) + + // Set up a timeout timer if maxSeconds was specified to prevent runaway background tasks + if maxSeconds != nil { + // Schedule a timer to cancel the task after the specified timeout period + Timer.scheduledTimer(withTimeInterval: TimeInterval(maxSeconds!), repeats: false) { _ in + self.cancel() + } + } + } + + /** + * Called by the Flutter side when it has finished initialization and is ready to receive commands. + * Routes the appropriate task type (refresh or processing) to the corresponding Flutter method. + * This method acts as a bridge between the native iOS background task system and Flutter. + */ + func onInitialized() throws { + switch self.taskType { + case .refreshUpload, .processingUpload: + flutterApi?.onIosUpload(isRefresh: self.taskType == .refreshUpload, + maxSeconds: maxSeconds.map { Int64($0) }, completion: { result in + self.handleHostResult(result: result) + }) + case .localSync: + flutterApi?.onLocalSync(maxSeconds: maxSeconds.map { Int64($0) }, completion: { result in + self.handleHostResult(result: result) + }) + } + } + + /** + * Cancels the currently running background task, either due to timeout or external request. + * Sends a cancel signal to the Flutter side and sets up a fallback timer to ensure + * the completion handler is eventually called even if Flutter doesn't respond. + */ + func cancel() { + if isComplete { + return + } + + isComplete = true + flutterApi?.cancel { result in + self.complete(success: false) + } + + // Fallback safety mechanism: ensure completion is called within 2 seconds + // This prevents the background task from hanging indefinitely if Flutter doesn't respond + Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { _ in + self.complete(success: false) + } + } + + /** + * Handles the result from Flutter API calls and determines the success/failure status. + * Converts Flutter's Result type to a simple boolean success indicator for task completion. + * + * - Parameter result: The result returned from a Flutter API call + */ + private func handleHostResult(result: Result) { + switch result { + case .success(): self.complete(success: true) + case .failure(_): self.cancel() + } + } + + /** + * Cleans up resources by destroying the Flutter engine context and invokes the completion handler. + * This method ensures that the background task is marked as complete, releases the Flutter engine, + * and notifies the caller of the task's success or failure. This is the final step in the + * background task lifecycle and should only be called once per task instance. + * + * - Parameter success: Indicates whether the background task completed successfully + */ + private func complete(success: Bool) { + isComplete = true + engine.destroyContext() + completionHandler(success) + } +} diff --git a/mobile/ios/Runner/Background/BackgroundWorkerApiImpl.swift b/mobile/ios/Runner/Background/BackgroundWorkerApiImpl.swift new file mode 100644 index 0000000000..f36085de0b --- /dev/null +++ b/mobile/ios/Runner/Background/BackgroundWorkerApiImpl.swift @@ -0,0 +1,155 @@ +import BackgroundTasks + +class BackgroundWorkerApiImpl: BackgroundWorkerFgHostApi { + func enableSyncWorker() throws { + BackgroundWorkerApiImpl.scheduleLocalSync() + print("BackgroundUploadImpl:enableSyncWorker Local Sync worker scheduled") + } + + func enableUploadWorker(callbackHandle: Int64) throws { + BackgroundWorkerApiImpl.updateUploadEnabled(true) + // Store the callback handle for later use when starting background Flutter isolates + BackgroundWorkerApiImpl.updateUploadCallbackHandle(callbackHandle) + + BackgroundWorkerApiImpl.scheduleRefreshUpload() + BackgroundWorkerApiImpl.scheduleProcessingUpload() + print("BackgroundUploadImpl:enableUploadWorker Scheduled background upload tasks") + } + + func disableUploadWorker() throws { + BackgroundWorkerApiImpl.updateUploadEnabled(false) + BackgroundWorkerApiImpl.cancelUploadTasks() + print("BackgroundUploadImpl:disableUploadWorker Disabled background upload tasks") + } + + public static let backgroundUploadEnabledKey = "immich:background:backup:enabled" + public static let backgroundUploadCallbackHandleKey = "immich:background:backup:callbackHandle" + + private static let localSyncTaskID = "app.alextran.immich.background.localSync" + private static let refreshUploadTaskID = "app.alextran.immich.background.refreshUpload" + private static let processingUploadTaskID = "app.alextran.immich.background.processingUpload" + + private static func updateUploadEnabled(_ isEnabled: Bool) { + return UserDefaults.standard.set(isEnabled, forKey: BackgroundWorkerApiImpl.backgroundUploadEnabledKey) + } + + private static func updateUploadCallbackHandle(_ callbackHandle: Int64) { + return UserDefaults.standard.set(String(callbackHandle), forKey: BackgroundWorkerApiImpl.backgroundUploadCallbackHandleKey) + } + + private static func cancelUploadTasks() { + BackgroundWorkerApiImpl.updateUploadEnabled(false) + BGTaskScheduler.shared.cancel(taskRequestWithIdentifier: refreshUploadTaskID); + BGTaskScheduler.shared.cancel(taskRequestWithIdentifier: processingUploadTaskID); + } + + public static func registerBackgroundProcessing() { + BGTaskScheduler.shared.register( + forTaskWithIdentifier: processingUploadTaskID, using: nil) { task in + if task is BGProcessingTask { + handleBackgroundProcessing(task: task as! BGProcessingTask) + } + } + + BGTaskScheduler.shared.register( + forTaskWithIdentifier: refreshUploadTaskID, using: nil) { task in + if task is BGAppRefreshTask { + handleBackgroundRefresh(task: task as! BGAppRefreshTask, taskType: .refreshUpload) + } + } + + BGTaskScheduler.shared.register( + forTaskWithIdentifier: localSyncTaskID, using: nil) { task in + if task is BGAppRefreshTask { + handleBackgroundRefresh(task: task as! BGAppRefreshTask, taskType: .localSync) + } + } + } + + private static func scheduleLocalSync() { + let backgroundRefresh = BGAppRefreshTaskRequest(identifier: localSyncTaskID) + backgroundRefresh.earliestBeginDate = Date(timeIntervalSinceNow: 5 * 60) // 5 mins + + do { + try BGTaskScheduler.shared.submit(backgroundRefresh) + } catch { + print("Could not schedule the local sync task \(error.localizedDescription)") + } + } + + private static func scheduleRefreshUpload() { + let backgroundRefresh = BGAppRefreshTaskRequest(identifier: refreshUploadTaskID) + backgroundRefresh.earliestBeginDate = Date(timeIntervalSinceNow: 5 * 60) // 5 mins + + do { + try BGTaskScheduler.shared.submit(backgroundRefresh) + } catch { + print("Could not schedule the refresh upload task \(error.localizedDescription)") + } + } + + private static func scheduleProcessingUpload() { + let backgroundProcessing = BGProcessingTaskRequest(identifier: processingUploadTaskID) + + backgroundProcessing.requiresNetworkConnectivity = true + backgroundProcessing.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60) // 15 mins + + do { + try BGTaskScheduler.shared.submit(backgroundProcessing) + } catch { + print("Could not schedule the processing upload task \(error.localizedDescription)") + } + } + + private static func handleBackgroundRefresh(task: BGAppRefreshTask, taskType: BackgroundTaskType) { + scheduleRefreshUpload() + // Restrict the refresh task to run only for a maximum of 20 seconds + runBackgroundWorker(task: task, taskType: taskType, maxSeconds: 20) + } + + private static func handleBackgroundProcessing(task: BGProcessingTask) { + scheduleProcessingUpload() + // There are no restrictions for processing tasks. Although, the OS could signal expiration at any time + runBackgroundWorker(task: task, taskType: .processingUpload, maxSeconds: nil) + } + + /** + * Executes the background worker within the context of a background task. + * This method creates a BackgroundWorker, sets up task expiration handling, + * and manages the synchronization between the background task and the Flutter engine. + * + * - Parameters: + * - task: The iOS background task that provides the execution context + * - taskType: The type of background operation to perform (refresh or processing) + * - maxSeconds: Optional timeout for the operation in seconds + */ + private static func runBackgroundWorker(task: BGTask, taskType: BackgroundTaskType, maxSeconds: Int?) { + let semaphore = DispatchSemaphore(value: 0) + var isSuccess = true + + let backgroundWorker = BackgroundWorker(taskType: taskType, maxSeconds: maxSeconds) { success in + isSuccess = success + semaphore.signal() + } + + task.expirationHandler = { + DispatchQueue.main.async { + backgroundWorker.cancel() + } + isSuccess = false + + // Schedule a timer to signal the semaphore after 2 seconds + Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { _ in + semaphore.signal() + } + } + + DispatchQueue.main.async { + backgroundWorker.run() + } + + semaphore.wait() + task.setTaskCompleted(success: isSuccess) + print("Background task completed with success: \(isSuccess)") + } +} diff --git a/mobile/ios/Runner/Info.plist b/mobile/ios/Runner/Info.plist index 5db281ea86..1a3658ed16 100644 --- a/mobile/ios/Runner/Info.plist +++ b/mobile/ios/Runner/Info.plist @@ -6,6 +6,9 @@ $(CUSTOM_GROUP_ID) BGTaskSchedulerPermittedIdentifiers + app.alextran.immich.background.localSync + app.alextran.immich.background.refreshUpload + app.alextran.immich.background.processingUpload app.alextran.immich.backgroundFetch app.alextran.immich.backgroundProcessing @@ -78,7 +81,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.139.4 + 1.139.3 CFBundleSignature ???? CFBundleURLTypes @@ -105,7 +108,7 @@ CFBundleVersion - 218 + 217 FLTEnableImpeller ITSAppUsesNonExemptEncryption @@ -134,6 +137,9 @@ We need to access the camera to let you take beautiful video using this app NSFaceIDUsageDescription We need to use FaceID to allow access to your locked folder + NSLocalNetworkUsageDescription + We need local network permission to connect to the local server using IP address and + allow the casting feature to work NSLocationAlwaysAndWhenInUseUsageDescription We require this permission to access the local WiFi name for background upload mechanism NSLocationUsageDescription @@ -180,8 +186,5 @@ io.flutter.embedded_views_preview - NSLocalNetworkUsageDescription - We need local network permission to connect to the local server using IP address and - allow the casting feature to work - + \ No newline at end of file diff --git a/mobile/lib/domain/services/background_worker.service.dart b/mobile/lib/domain/services/background_worker.service.dart new file mode 100644 index 0000000000..33c58cf743 --- /dev/null +++ b/mobile/lib/domain/services/background_worker.service.dart @@ -0,0 +1,232 @@ +import 'dart:async'; +import 'dart:ui'; + +import 'package:background_downloader/background_downloader.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/constants.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart'; +import 'package:immich_mobile/platform/background_worker_api.g.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/providers/background_sync.provider.dart'; +import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; +import 'package:immich_mobile/providers/db.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/services/auth.service.dart'; +import 'package:immich_mobile/services/localization.service.dart'; +import 'package:immich_mobile/services/upload.service.dart'; +import 'package:immich_mobile/utils/bootstrap.dart'; +import 'package:immich_mobile/utils/http_ssl_options.dart'; +import 'package:isar/isar.dart'; +import 'package:logging/logging.dart'; + +class BackgroundWorkerFgService { + final BackgroundWorkerFgHostApi _foregroundHostApi; + + const BackgroundWorkerFgService(this._foregroundHostApi); + + // TODO: Move this call to native side once old timeline is removed + Future enableSyncService() => _foregroundHostApi.enableSyncWorker(); + + Future enableUploadService() => _foregroundHostApi.enableUploadWorker( + PluginUtilities.getCallbackHandle(_backgroundSyncNativeEntrypoint)!.toRawHandle(), + ); + + Future disableUploadService() => _foregroundHostApi.disableUploadWorker(); +} + +class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi { + late final ProviderContainer _ref; + final Isar _isar; + final Drift _drift; + final DriftLogger _driftLogger; + final BackgroundWorkerBgHostApi _backgroundHostApi; + final Logger _logger = Logger('BackgroundUploadBgService'); + + bool _isCleanedUp = false; + + BackgroundWorkerBgService({required Isar isar, required Drift drift, required DriftLogger driftLogger}) + : _isar = isar, + _drift = drift, + _driftLogger = driftLogger, + _backgroundHostApi = BackgroundWorkerBgHostApi() { + _ref = ProviderContainer( + overrides: [ + dbProvider.overrideWithValue(isar), + isarProvider.overrideWithValue(isar), + driftProvider.overrideWith(driftOverride(drift)), + ], + ); + BackgroundWorkerFlutterApi.setUp(this); + } + + bool get _isBackupEnabled => _ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup); + + Future init() async { + await loadTranslations(); + HttpSSLOptions.apply(applyNative: false); + await _ref.read(authServiceProvider).setOpenApiServiceEndpoint(); + + // Initialize the file downloader + await FileDownloader().configure( + globalConfig: [ + // maxConcurrent: 6, maxConcurrentByHost(server):6, maxConcurrentByGroup: 3 + (Config.holdingQueue, (6, 6, 3)), + // On Android, if files are larger than 256MB, run in foreground service + (Config.runInForegroundIfFileLargerThan, 256), + ], + ); + await FileDownloader().trackTasksInGroup(kDownloadGroupLivePhoto, markDownloadedComplete: false); + await FileDownloader().trackTasks(); + configureFileDownloaderNotifications(); + + // Notify the host that the background upload service has been initialized and is ready to use + await _backgroundHostApi.onInitialized(); + } + + @override + Future onLocalSync(int? maxSeconds) async { + _logger.info('Local background syncing started'); + final sw = Stopwatch()..start(); + + final timeout = maxSeconds != null ? Duration(seconds: maxSeconds) : null; + await _syncAssets(hashTimeout: timeout, syncRemote: false); + + sw.stop(); + _logger.info("Local sync completed in ${sw.elapsed.inSeconds}s"); + } + + /* We do the following on Android upload + * - Sync local assets + * - Hash local assets 3 / 6 minutes + * - Sync remote assets + * - Check and requeue upload tasks + */ + @override + Future onAndroidUpload() async { + _logger.info('Android background processing started'); + final sw = Stopwatch()..start(); + + await _syncAssets(hashTimeout: Duration(minutes: _isBackupEnabled ? 3 : 6)); + await _handleBackup(processBulk: false); + + await _cleanup(); + + sw.stop(); + _logger.info("Android background processing completed in ${sw.elapsed.inSeconds}s"); + } + + /* We do the following on background upload + * - Sync local assets + * - Hash local assets + * - Sync remote assets + * - Check and requeue upload tasks + * + * The native side will not send the maxSeconds value for processing tasks + */ + @override + Future onIosUpload(bool isRefresh, int? maxSeconds) async { + _logger.info('iOS background upload started with maxSeconds: ${maxSeconds}s'); + final sw = Stopwatch()..start(); + + final timeout = isRefresh ? const Duration(seconds: 5) : Duration(minutes: _isBackupEnabled ? 3 : 6); + await _syncAssets(hashTimeout: timeout); + + final backupFuture = _handleBackup(); + if (maxSeconds != null) { + await backupFuture.timeout(Duration(seconds: maxSeconds - 1), onTimeout: () {}); + } else { + await backupFuture; + } + + await _cleanup(); + + sw.stop(); + _logger.info("iOS background upload completed in ${sw.elapsed.inSeconds}s"); + } + + @override + Future cancel() async { + _logger.warning("Background upload cancelled"); + await _cleanup(); + } + + Future _cleanup() async { + if (_isCleanedUp) { + return; + } + + _isCleanedUp = true; + await _ref.read(backgroundSyncProvider).cancel(); + await _ref.read(backgroundSyncProvider).cancelLocal(); + await _isar.close(); + await _drift.close(); + await _driftLogger.close(); + _ref.dispose(); + } + + Future _handleBackup({bool processBulk = true}) async { + if (!_isBackupEnabled) { + return; + } + + final currentUser = _ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + if (processBulk) { + return _ref.read(driftBackupProvider.notifier).handleBackupResume(currentUser.id); + } + + final activeTask = await _ref.read(uploadServiceProvider).getActiveTasks(currentUser.id); + if (activeTask.isNotEmpty) { + await _ref.read(uploadServiceProvider).resumeBackup(); + } else { + await _ref.read(uploadServiceProvider).startBackupSerial(currentUser.id); + } + } + + Future _syncAssets({Duration? hashTimeout, bool syncRemote = true}) async { + final futures = >[]; + + final localSyncFuture = _ref.read(backgroundSyncProvider).syncLocal().then((_) async { + if (_isCleanedUp) { + return; + } + + var hashFuture = _ref.read(backgroundSyncProvider).hashAssets(); + if (hashTimeout != null) { + hashFuture = hashFuture.timeout( + hashTimeout, + onTimeout: () { + // Consume cancellation errors as we want to continue processing + }, + ); + } + + return hashFuture; + }); + + futures.add(localSyncFuture); + if (syncRemote) { + final remoteSyncFuture = _ref.read(backgroundSyncProvider).syncRemote(); + futures.add(remoteSyncFuture); + } + + await Future.wait(futures); + } +} + +@pragma('vm:entry-point') +Future _backgroundSyncNativeEntrypoint() async { + WidgetsFlutterBinding.ensureInitialized(); + DartPluginRegistrant.ensureInitialized(); + + final (isar, drift, logDB) = await Bootstrap.initDB(); + await Bootstrap.initDomain(isar, drift, logDB, shouldBufferLogs: false); + await BackgroundWorkerBgService(isar: isar, drift: drift, driftLogger: logDB).init(); +} diff --git a/mobile/lib/domain/services/hash.service.dart b/mobile/lib/domain/services/hash.service.dart index a8eea2c25e..90720fdc76 100644 --- a/mobile/lib/domain/services/hash.service.dart +++ b/mobile/lib/domain/services/hash.service.dart @@ -15,6 +15,7 @@ class HashService { final DriftLocalAssetRepository _localAssetRepository; final StorageRepository _storageRepository; final NativeSyncApi _nativeSyncApi; + final bool Function()? _cancelChecker; final _log = Logger('HashService'); HashService({ @@ -22,13 +23,17 @@ class HashService { required DriftLocalAssetRepository localAssetRepository, required StorageRepository storageRepository, required NativeSyncApi nativeSyncApi, + bool Function()? cancelChecker, this.batchSizeLimit = kBatchHashSizeLimit, this.batchFileLimit = kBatchHashFileLimit, }) : _localAlbumRepository = localAlbumRepository, _localAssetRepository = localAssetRepository, _storageRepository = storageRepository, + _cancelChecker = cancelChecker, _nativeSyncApi = nativeSyncApi; + bool get isCancelled => _cancelChecker?.call() ?? false; + Future hashAssets() async { final Stopwatch stopwatch = Stopwatch()..start(); // Sorted by backupSelection followed by isCloud @@ -37,6 +42,11 @@ class HashService { ); for (final album in localAlbums) { + if (isCancelled) { + _log.warning("Hashing cancelled. Stopped processing albums."); + break; + } + final assetsToHash = await _localAlbumRepository.getAssetsToHash(album.id); if (assetsToHash.isNotEmpty) { await _hashAssets(assetsToHash); @@ -55,6 +65,11 @@ class HashService { final toHash = <_AssetToPath>[]; for (final asset in assetsToHash) { + if (isCancelled) { + _log.warning("Hashing cancelled. Stopped processing assets."); + return; + } + final file = await _storageRepository.getFileForAsset(asset.id); if (file == null) { continue; @@ -89,6 +104,11 @@ class HashService { ); for (int i = 0; i < hashes.length; i++) { + if (isCancelled) { + _log.warning("Hashing cancelled. Stopped processing batch."); + return; + } + final hash = hashes[i]; final asset = toHash[i].asset; if (hash?.length == 20) { diff --git a/mobile/lib/domain/services/log.service.dart b/mobile/lib/domain/services/log.service.dart index 1053d5e54f..d21cb7ab09 100644 --- a/mobile/lib/domain/services/log.service.dart +++ b/mobile/lib/domain/services/log.service.dart @@ -123,6 +123,11 @@ class LogService { _flushTimer = null; final buffer = [..._msgBuffer]; _msgBuffer.clear(); + + if (buffer.isEmpty) { + return; + } + await _logRepository.insertAll(buffer); } } diff --git a/mobile/lib/domain/utils/background_sync.dart b/mobile/lib/domain/utils/background_sync.dart index cbf4030788..d8042c707c 100644 --- a/mobile/lib/domain/utils/background_sync.dart +++ b/mobile/lib/domain/utils/background_sync.dart @@ -59,6 +59,28 @@ class BackgroundSyncManager { } } + Future cancelLocal() async { + final futures = []; + + if (_hashTask != null) { + futures.add(_hashTask!.future); + } + _hashTask?.cancel(); + _hashTask = null; + + if (_deviceAlbumSyncTask != null) { + futures.add(_deviceAlbumSyncTask!.future); + } + _deviceAlbumSyncTask?.cancel(); + _deviceAlbumSyncTask = null; + + try { + await Future.wait(futures); + } on CanceledError { + // Ignore cancellation errors + } + } + // No need to cancel the task, as it can also be run when the user logs out Future syncLocal({bool full = false}) { if (_deviceAlbumSyncTask != null) { diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 0cab21748c..21093df24d 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -12,10 +12,13 @@ import 'package:flutter_displaymode/flutter_displaymode.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/constants/locales.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/generated/codegen_loader.g.dart'; import 'package:immich_mobile/providers/app_life_cycle.provider.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/share_intent_upload.provider.dart'; +import 'package:immich_mobile/providers/backup/backup.provider.dart'; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/providers/locale_provider.dart'; @@ -23,6 +26,7 @@ import 'package:immich_mobile/providers/routes.provider.dart'; import 'package:immich_mobile/providers/theme.provider.dart'; import 'package:immich_mobile/routing/app_navigation_observer.dart'; import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/services/background.service.dart'; import 'package:immich_mobile/services/deep_link.service.dart'; import 'package:immich_mobile/services/local_notification.service.dart'; @@ -165,36 +169,6 @@ class ImmichAppState extends ConsumerState with WidgetsBindingObserve await ref.read(localNotificationService).setup(); } - void _configureFileDownloaderNotifications() { - FileDownloader().configureNotificationForGroup( - kDownloadGroupImage, - running: TaskNotification('downloading_media'.tr(), '${'file_name'.tr()}: {filename}'), - complete: TaskNotification('download_finished'.tr(), '${'file_name'.tr()}: {filename}'), - progressBar: true, - ); - - FileDownloader().configureNotificationForGroup( - kDownloadGroupVideo, - running: TaskNotification('downloading_media'.tr(), '${'file_name'.tr()}: {filename}'), - complete: TaskNotification('download_finished'.tr(), '${'file_name'.tr()}: {filename}'), - progressBar: true, - ); - - FileDownloader().configureNotificationForGroup( - kManualUploadGroup, - running: TaskNotification('uploading_media'.tr(), '${'file_name'.tr()}: {displayName}'), - complete: TaskNotification('upload_finished'.tr(), '${'file_name'.tr()}: {displayName}'), - progressBar: true, - ); - - FileDownloader().configureNotificationForGroup( - kBackupGroup, - running: TaskNotification('uploading_media'.tr(), '${'file_name'.tr()}: {displayName}'), - complete: TaskNotification('upload_finished'.tr(), '${'file_name'.tr()}: {displayName}'), - progressBar: true, - ); - } - Future _deepLinkBuilder(PlatformDeepLink deepLink) async { final deepLinkHandler = ref.read(deepLinkServiceProvider); final currentRouteName = ref.read(currentRouteNameProvider.notifier).state; @@ -221,7 +195,7 @@ class ImmichAppState extends ConsumerState with WidgetsBindingObserve super.didChangeDependencies(); Intl.defaultLocale = context.locale.toLanguageTag(); WidgetsBinding.instance.addPostFrameCallback((_) { - _configureFileDownloaderNotifications(); + configureFileDownloaderNotifications(); }); } @@ -231,7 +205,16 @@ class ImmichAppState extends ConsumerState with WidgetsBindingObserve initApp().then((_) => debugPrint("App Init Completed")); WidgetsBinding.instance.addPostFrameCallback((_) { // needs to be delayed so that EasyLocalization is working - ref.read(backgroundServiceProvider).resumeServiceIfEnabled(); + if (Store.isBetaTimelineEnabled) { + ref.read(driftBackgroundUploadFgService).enableSyncService(); + if (ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup)) { + ref.read(backgroundServiceProvider).disableService(); + ref.read(driftBackgroundUploadFgService).enableUploadService(); + } + } else { + ref.read(backgroundServiceProvider).resumeServiceIfEnabled(); + ref.read(driftBackgroundUploadFgService).disableUploadService(); + } }); ref.read(shareIntentUploadProvider.notifier).init(); diff --git a/mobile/lib/pages/backup/drift_backup.page.dart b/mobile/lib/pages/backup/drift_backup.page.dart index b125c35908..5140c62a0d 100644 --- a/mobile/lib/pages/backup/drift_backup.page.dart +++ b/mobile/lib/pages/backup/drift_backup.page.dart @@ -8,6 +8,7 @@ import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/backup/backup_toggle_button.widget.dart'; import 'package:immich_mobile/providers/background_sync.provider.dart'; +import 'package:immich_mobile/providers/backup/backup.provider.dart'; import 'package:immich_mobile/providers/backup/backup_album.provider.dart'; import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; @@ -42,10 +43,12 @@ class _DriftBackupPageState extends ConsumerState { await ref.read(backgroundSyncProvider).syncRemote(); await ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id); + await ref.read(driftBackgroundUploadFgService).enableUploadService(); await ref.read(driftBackupProvider.notifier).startBackup(currentUser.id); } Future stopBackup() async { + await ref.read(driftBackgroundUploadFgService).disableUploadService(); await ref.read(driftBackupProvider.notifier).cancel(); } diff --git a/mobile/lib/pages/common/change_experience.page.dart b/mobile/lib/pages/common/change_experience.page.dart index 3e9747ce32..9064f32066 100644 --- a/mobile/lib/pages/common/change_experience.page.dart +++ b/mobile/lib/pages/common/change_experience.page.dart @@ -14,6 +14,7 @@ import 'package:immich_mobile/providers/backup/manual_upload.provider.dart'; import 'package:immich_mobile/providers/gallery_permission.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/providers/websocket.provider.dart'; +import 'package:immich_mobile/services/background.service.dart'; import 'package:immich_mobile/utils/migration.dart'; import 'package:logging/logging.dart'; import 'package:permission_handler/permission_handler.dart'; @@ -68,12 +69,15 @@ class _ChangeExperiencePageState extends ConsumerState { await migrateDeviceAssetToSqlite(ref.read(isarProvider), ref.read(driftProvider)); await migrateBackupAlbumsToSqlite(ref.read(isarProvider), ref.read(driftProvider)); await migrateStoreToSqlite(ref.read(isarProvider), ref.read(driftProvider)); + await ref.read(backgroundServiceProvider).disableService(); } } else { await ref.read(backgroundSyncProvider).cancel(); ref.read(websocketProvider.notifier).stopListeningToBetaEvents(); ref.read(websocketProvider.notifier).startListeningToOldEvents(); await migrateStoreToIsar(ref.read(isarProvider), ref.read(driftProvider)); + await ref.read(backgroundServiceProvider).resumeServiceIfEnabled(); + await ref.read(driftBackgroundUploadFgService).disableUploadService(); } await IsarStoreRepository(ref.read(isarProvider)).upsert(StoreKey.betaTimeline, widget.switchingToBeta); diff --git a/mobile/lib/platform/background_worker_api.g.dart b/mobile/lib/platform/background_worker_api.g.dart new file mode 100644 index 0000000000..646eb63b76 --- /dev/null +++ b/mobile/lib/platform/background_worker_api.g.dart @@ -0,0 +1,296 @@ +// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + +PlatformException _createConnectionError(String channelName) { + return PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); +} + +List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { + if (empty) { + return []; + } + if (error == null) { + return [result]; + } + return [error.code, error.message, error.details]; +} + +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + default: + return super.readValueOfType(type, buffer); + } + } +} + +class BackgroundWorkerFgHostApi { + /// Constructor for [BackgroundWorkerFgHostApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + BackgroundWorkerFgHostApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + final BinaryMessenger? pigeonVar_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + final String pigeonVar_messageChannelSuffix; + + Future enableSyncWorker() async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.enableSyncWorker$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + Future enableUploadWorker(int callbackHandle) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.enableUploadWorker$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([callbackHandle]); + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + Future disableUploadWorker() async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.disableUploadWorker$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } +} + +class BackgroundWorkerBgHostApi { + /// Constructor for [BackgroundWorkerBgHostApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + BackgroundWorkerBgHostApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + final BinaryMessenger? pigeonVar_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + final String pigeonVar_messageChannelSuffix; + + Future onInitialized() async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.BackgroundWorkerBgHostApi.onInitialized$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } +} + +abstract class BackgroundWorkerFlutterApi { + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + Future onLocalSync(int? maxSeconds); + + Future onIosUpload(bool isRefresh, int? maxSeconds); + + Future onAndroidUpload(); + + Future cancel(); + + static void setUp( + BackgroundWorkerFlutterApi? api, { + BinaryMessenger? binaryMessenger, + String messageChannelSuffix = '', + }) { + messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + { + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onLocalSync$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger, + ); + if (api == null) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + assert( + message != null, + 'Argument for dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onLocalSync was null.', + ); + final List args = (message as List?)!; + final int? arg_maxSeconds = (args[0] as int?); + try { + await api.onLocalSync(arg_maxSeconds); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); + } + }); + } + } + { + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onIosUpload$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger, + ); + if (api == null) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + assert( + message != null, + 'Argument for dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onIosUpload was null.', + ); + final List args = (message as List?)!; + final bool? arg_isRefresh = (args[0] as bool?); + assert( + arg_isRefresh != null, + 'Argument for dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onIosUpload was null, expected non-null bool.', + ); + final int? arg_maxSeconds = (args[1] as int?); + try { + await api.onIosUpload(arg_isRefresh!, arg_maxSeconds); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); + } + }); + } + } + { + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onAndroidUpload$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger, + ); + if (api == null) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + try { + await api.onAndroidUpload(); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); + } + }); + } + } + { + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.cancel$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger, + ); + if (api == null) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + try { + await api.cancel(); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); + } + }); + } + } + } +} diff --git a/mobile/lib/providers/backup/backup.provider.dart b/mobile/lib/providers/backup/backup.provider.dart index 76cb383465..6035e53e5d 100644 --- a/mobile/lib/providers/backup/backup.provider.dart +++ b/mobile/lib/providers/backup/backup.provider.dart @@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/domain/services/background_worker.service.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/backup_album.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; @@ -17,6 +18,7 @@ import 'package:immich_mobile/models/backup/current_upload_asset.model.dart'; import 'package:immich_mobile/models/backup/error_upload_asset.model.dart'; import 'package:immich_mobile/models/backup/success_upload_asset.model.dart'; import 'package:immich_mobile/models/server_info/server_disk_info.model.dart'; +import 'package:immich_mobile/platform/background_worker_api.g.dart'; import 'package:immich_mobile/providers/app_life_cycle.provider.dart'; import 'package:immich_mobile/providers/auth.provider.dart'; import 'package:immich_mobile/providers/backup/error_backup_list.provider.dart'; @@ -34,6 +36,8 @@ import 'package:logging/logging.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:photo_manager/photo_manager.dart' show PMProgressHandler; +final driftBackgroundUploadFgService = Provider((ref) => BackgroundWorkerFgService(BackgroundWorkerFgHostApi())); + final backupProvider = StateNotifierProvider((ref) { return BackupNotifier( ref.watch(backupServiceProvider), diff --git a/mobile/lib/repositories/upload.repository.dart b/mobile/lib/repositories/upload.repository.dart index 220dbf81c3..c8b06ae102 100644 --- a/mobile/lib/repositories/upload.repository.dart +++ b/mobile/lib/repositories/upload.repository.dart @@ -27,8 +27,12 @@ class UploadRepository { ); } - void enqueueBackgroundAll(List tasks) { - FileDownloader().enqueueAll(tasks); + Future enqueueBackground(UploadTask task) { + return FileDownloader().enqueue(task); + } + + Future enqueueBackgroundAll(List tasks) { + return FileDownloader().enqueueAll(tasks); } Future deleteDatabaseRecords(String group) { diff --git a/mobile/lib/services/upload.service.dart b/mobile/lib/services/upload.service.dart index 9e5193c8cb..635604b096 100644 --- a/mobile/lib/services/upload.service.dart +++ b/mobile/lib/services/upload.service.dart @@ -78,8 +78,8 @@ class UploadService { _taskProgressController.close(); } - void enqueueTasks(List tasks) { - _uploadRepository.enqueueBackgroundAll(tasks); + Future enqueueTasks(List tasks) { + return _uploadRepository.enqueueBackgroundAll(tasks); } Future> getActiveTasks(String group) { @@ -113,7 +113,7 @@ class UploadService { } if (tasks.isNotEmpty) { - enqueueTasks(tasks); + await enqueueTasks(tasks); } } @@ -149,13 +149,37 @@ class UploadService { if (tasks.isNotEmpty && !shouldAbortQueuingTasks) { count += tasks.length; - enqueueTasks(tasks); + await enqueueTasks(tasks); onEnqueueTasks(EnqueueStatus(enqueueCount: count, totalCount: candidates.length)); } } } + // Enqueue All does not work from the background on Android yet. This method is a temporary workaround + // that enqueues tasks one by one. + Future startBackupSerial(String userId) async { + await _storageRepository.clearCache(); + + shouldAbortQueuingTasks = false; + + final candidates = await _backupRepository.getCandidates(userId); + if (candidates.isEmpty) { + return; + } + + for (final asset in candidates) { + if (shouldAbortQueuingTasks) { + break; + } + + final task = await _getUploadTask(asset); + if (task != null) { + await _uploadRepository.enqueueBackground(task); + } + } + } + /// Cancel all ongoing uploads and reset the upload queue /// /// Return the number of left over tasks in the queue diff --git a/mobile/lib/utils/bootstrap.dart b/mobile/lib/utils/bootstrap.dart index 480d918b4e..e7abc66040 100644 --- a/mobile/lib/utils/bootstrap.dart +++ b/mobile/lib/utils/bootstrap.dart @@ -1,6 +1,8 @@ import 'dart:io'; +import 'package:background_downloader/background_downloader.dart'; import 'package:flutter/foundation.dart'; +import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/services/log.service.dart'; import 'package:immich_mobile/domain/services/store.service.dart'; @@ -11,6 +13,7 @@ import 'package:immich_mobile/entities/backup_album.entity.dart'; import 'package:immich_mobile/entities/duplicated_asset.entity.dart'; import 'package:immich_mobile/entities/etag.entity.dart'; import 'package:immich_mobile/entities/ios_device_asset.entity.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/infrastructure/entities/device_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; @@ -22,6 +25,36 @@ import 'package:immich_mobile/infrastructure/repositories/store.repository.dart' import 'package:isar/isar.dart'; import 'package:path_provider/path_provider.dart'; +void configureFileDownloaderNotifications() { + FileDownloader().configureNotificationForGroup( + kDownloadGroupImage, + running: TaskNotification('downloading_media'.t(), '${'file_name'.t()}: {filename}'), + complete: TaskNotification('download_finished'.t(), '${'file_name'.t()}: {filename}'), + progressBar: true, + ); + + FileDownloader().configureNotificationForGroup( + kDownloadGroupVideo, + running: TaskNotification('downloading_media'.t(), '${'file_name'.t()}: {filename}'), + complete: TaskNotification('download_finished'.t(), '${'file_name'.t()}: {filename}'), + progressBar: true, + ); + + FileDownloader().configureNotificationForGroup( + kManualUploadGroup, + running: TaskNotification('uploading_media'.t(), 'backup_background_service_in_progress_notification'.t()), + complete: TaskNotification('upload_finished'.t(), 'backup_background_service_in_progress_notification'.t()), + groupNotificationId: kManualUploadGroup, + ); + + FileDownloader().configureNotificationForGroup( + kBackupGroup, + running: TaskNotification('uploading_media'.t(), 'backup_background_service_in_progress_notification'.t()), + complete: TaskNotification('upload_finished'.t(), 'backup_background_service_in_progress_notification'.t()), + groupNotificationId: kBackupGroup, + ); +} + abstract final class Bootstrap { static Future<(Isar isar, Drift drift, DriftLogger logDb)> initDB() async { final drift = Drift(); diff --git a/mobile/lib/utils/isolate.dart b/mobile/lib/utils/isolate.dart index 58e7ad7f25..cca1498e0f 100644 --- a/mobile/lib/utils/isolate.dart +++ b/mobile/lib/utils/isolate.dart @@ -57,7 +57,7 @@ Cancelable runInIsolateGentle({ log.severe("Error in runInIsolateGentle ${debugLabel == null ? '' : ' for $debugLabel'}", error, stack); } finally { try { - await LogService.I.flush(); + await LogService.I.dispose(); await logDb.close(); await ref.read(driftProvider).close(); @@ -72,8 +72,8 @@ Cancelable runInIsolateGentle({ } ref.dispose(); - } catch (error) { - debugPrint("Error closing resources in isolate: $error"); + } catch (error, stack) { + debugPrint("Error closing resources in isolate: $error, $stack"); } finally { ref.dispose(); // Delay to ensure all resources are released diff --git a/mobile/makefile b/mobile/makefile index 5a31481f45..1a20e769ef 100644 --- a/mobile/makefile +++ b/mobile/makefile @@ -8,8 +8,10 @@ build: pigeon: dart run pigeon --input pigeon/native_sync_api.dart dart run pigeon --input pigeon/thumbnail_api.dart + dart run pigeon --input pigeon/background_worker_api.dart dart format lib/platform/native_sync_api.g.dart dart format lib/platform/thumbnail_api.g.dart + dart format lib/platform/background_worker_api.g.dart watch: dart run build_runner watch --delete-conflicting-outputs diff --git a/mobile/pigeon/background_worker_api.dart b/mobile/pigeon/background_worker_api.dart new file mode 100644 index 0000000000..eb1b7a2c5e --- /dev/null +++ b/mobile/pigeon/background_worker_api.dart @@ -0,0 +1,48 @@ +import 'package:pigeon/pigeon.dart'; + +@ConfigurePigeon( + PigeonOptions( + dartOut: 'lib/platform/background_worker_api.g.dart', + swiftOut: 'ios/Runner/Background/BackgroundWorker.g.swift', + swiftOptions: SwiftOptions(includeErrorClass: false), + kotlinOut: 'android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.g.kt', + kotlinOptions: KotlinOptions(package: 'app.alextran.immich.background'), + dartOptions: DartOptions(), + dartPackageName: 'immich_mobile', + ), +) +@HostApi() +abstract class BackgroundWorkerFgHostApi { + void enableSyncWorker(); + + // Enables the background upload service with the given callback handle + void enableUploadWorker(int callbackHandle); + + // Disables the background upload service + void disableUploadWorker(); +} + +@HostApi() +abstract class BackgroundWorkerBgHostApi { + // Called from the background flutter engine when it has bootstrapped and established the + // required platform channels to notify the native side to start the background upload + void onInitialized(); +} + +@FlutterApi() +abstract class BackgroundWorkerFlutterApi { + // Android & iOS: Called when the local sync is triggered + @async + void onLocalSync(int? maxSeconds); + + // iOS Only: Called when the iOS background upload is triggered + @async + void onIosUpload(bool isRefresh, int? maxSeconds); + + // Android Only: Called when the Android background upload is triggered + @async + void onAndroidUpload(); + + @async + void cancel(); +} From 80fa5ec19884c5ca37c3df56c96aa769d16672cf Mon Sep 17 00:00:00 2001 From: xCJPECKOVERx Date: Thu, 28 Aug 2025 12:47:53 -0400 Subject: [PATCH 354/748] fix(web): Slideshow fade occurs when not in slideshow (#21326) - ensure slideshow transition only shows when both enabled and in a slideshow --- web/src/lib/components/asset-viewer/asset-viewer.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/lib/components/asset-viewer/asset-viewer.svelte b/web/src/lib/components/asset-viewer/asset-viewer.svelte index 452510f508..0737031635 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer.svelte @@ -491,7 +491,7 @@ onPreviousAsset={() => navigateAsset('previous')} onNextAsset={() => navigateAsset('next')} {sharedLink} - haveFadeTransition={$slideshowState === SlideshowState.None || $slideshowTransition} + haveFadeTransition={$slideshowState !== SlideshowState.None && $slideshowTransition} /> {/if} {:else} From 662d44536e85e1b18cbca6c181967637b27c201e Mon Sep 17 00:00:00 2001 From: Johann Date: Thu, 28 Aug 2025 18:54:11 +0200 Subject: [PATCH 355/748] feat(web): add geolocation utility (#20758) * feat(geolocation): add geolocation utility * feat(web): geolocation utility - fix code review - 1 * feat(web): geolocation utility - fix code review - 2 * chore: cleanup * chore: feedback * feat(web): add animation and text animation on locations change and action text on thumbnail * styling, messages and filtering * selected color * format i18n * fix lint --------- Co-authored-by: Jason Rasmussen Co-authored-by: Alex --- i18n/en.json | 13 + web/src/lib/assets/empty-5.svg | 1 + .../asset-viewer/detail-panel-location.svelte | 12 +- .../assets/thumbnail/thumbnail.svelte | 2 +- .../actions/change-location-action.svelte | 15 +- .../shared-components/change-location.svelte | 42 +-- .../shared-components/date-picker.svelte | 113 ++++++ .../geolocation/geolocation.svelte | 104 ++++++ .../utilities-page/utilities-menu.svelte | 32 +- web/src/lib/constants.ts | 1 + .../GeolocationUpdateConfirmModal.svelte | 33 ++ web/src/lib/utils/date-time.spec.ts | 23 +- web/src/lib/utils/date-time.ts | 30 ++ web/src/lib/utils/navigation.ts | 13 + web/src/lib/utils/string-utils.ts | 10 + .../(user)/utilities/geolocation/+page.svelte | 321 ++++++++++++++++++ .../(user)/utilities/geolocation/+page.ts | 17 + 17 files changed, 733 insertions(+), 49 deletions(-) create mode 100644 web/src/lib/assets/empty-5.svg create mode 100644 web/src/lib/components/shared-components/date-picker.svelte create mode 100644 web/src/lib/components/utilities-page/geolocation/geolocation.svelte create mode 100644 web/src/lib/modals/GeolocationUpdateConfirmModal.svelte create mode 100644 web/src/routes/(user)/utilities/geolocation/+page.svelte create mode 100644 web/src/routes/(user)/utilities/geolocation/+page.ts diff --git a/i18n/en.json b/i18n/en.json index ccd0c9d7fe..5d215e2c36 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -461,6 +461,7 @@ "app_bar_signout_dialog_title": "Sign out", "app_settings": "App Settings", "appears_in": "Appears in", + "apply_count": "Apply ({count, number})", "archive": "Archive", "archive_action_prompt": "{count} added to Archive", "archive_or_unarchive_photo": "Archive or unarchive photo", @@ -1073,12 +1074,18 @@ "gcast_enabled": "Google Cast", "gcast_enabled_description": "This feature loads external resources from Google in order to work.", "general": "General", + "geolocation_instruction_all_have_location": "All assets for this date already have location data. Try showing all assets or select a different date", + "geolocation_instruction_location": "Click on an asset with GPS coordinates to use its location, or select a location directly from the map", + "geolocation_instruction_no_date": "Select a date to manage location data for photos and videos from that day", + "geolocation_instruction_no_photos": "No photos or videos found for this date. Select a different date to show them", "get_help": "Get Help", "get_wifiname_error": "Could not get Wi-Fi name. Make sure you have granted the necessary permissions and are connected to a Wi-Fi network", "getting_started": "Getting Started", "go_back": "Go back", "go_to_folder": "Go to folder", "go_to_search": "Go to search", + "gps": "GPS", + "gps_missing": "No GPS", "grant_permission": "Grant permission", "group_albums_by": "Group albums by...", "group_country": "Group by country", @@ -1262,6 +1269,7 @@ "main_branch_warning": "You're using a development version; we strongly recommend using a release version!", "main_menu": "Main menu", "make": "Make", + "manage_geolocation": "Manage location", "manage_shared_links": "Manage shared links", "manage_sharing_with_partners": "Manage sharing with partners", "manage_the_app_settings": "Manage the app settings", @@ -1722,6 +1730,7 @@ "select_user_for_sharing_page_err_album": "Failed to create album", "selected": "Selected", "selected_count": "{count, plural, other {# selected}}", + "selected_gps_coordinates": "selected gps coordinates", "send_message": "Send message", "send_welcome_email": "Send welcome email", "server_endpoint": "Server Endpoint", @@ -1832,8 +1841,10 @@ "shift_to_permanent_delete": "press ⇧ to permanently delete asset", "show_album_options": "Show album options", "show_albums": "Show albums", + "show_all_assets": "Show all assets", "show_all_people": "Show all people", "show_and_hide_people": "Show & hide people", + "show_assets_without_location": "Show assets without location", "show_file_location": "Show file location", "show_gallery": "Show gallery", "show_hidden_people": "Show hidden people", @@ -1993,6 +2004,7 @@ "unstacked_assets_count": "Un-stacked {count, plural, one {# asset} other {# assets}}", "untagged": "Untagged", "up_next": "Up next", + "update_location_action_prompt": "Update the location of {count} selected assets with:", "updated_at": "Updated", "updated_password": "Updated password", "upload": "Upload", @@ -2017,6 +2029,7 @@ "use_biometric": "Use biometric", "use_current_connection": "use current connection", "use_custom_date_range": "Use custom date range instead", + "use_this_location": "Click to use location", "user": "User", "user_has_been_deleted": "This user has been deleted.", "user_id": "User ID", diff --git a/web/src/lib/assets/empty-5.svg b/web/src/lib/assets/empty-5.svg new file mode 100644 index 0000000000..e9e24d0499 --- /dev/null +++ b/web/src/lib/assets/empty-5.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/src/lib/components/asset-viewer/detail-panel-location.svelte b/web/src/lib/components/asset-viewer/detail-panel-location.svelte index 42cbefadf1..783eba9c5f 100644 --- a/web/src/lib/components/asset-viewer/detail-panel-location.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel-location.svelte @@ -16,18 +16,22 @@ let isShowChangeLocation = $state(false); - async function handleConfirmChangeLocation(gps: { lng: number; lat: number }) { + const onClose = async (point?: { lng: number; lat: number }) => { isShowChangeLocation = false; + if (!point) { + return; + } + try { asset = await updateAsset({ id: asset.id, - updateAssetDto: { latitude: gps.lat, longitude: gps.lng }, + updateAssetDto: { latitude: point.lat, longitude: point.lng }, }); } catch (error) { handleError(error, $t('errors.unable_to_change_location')); } - } + }; {#if asset.exifInfo?.country} @@ -85,6 +89,6 @@ {#if isShowChangeLocation} - (isShowChangeLocation = false)} /> + {/if} diff --git a/web/src/lib/components/assets/thumbnail/thumbnail.svelte b/web/src/lib/components/assets/thumbnail/thumbnail.svelte index 9af9287c76..e01f2dc4f6 100644 --- a/web/src/lib/components/assets/thumbnail/thumbnail.svelte +++ b/web/src/lib/components/assets/thumbnail/thumbnail.svelte @@ -197,7 +197,7 @@
    @@ -39,5 +44,5 @@ /> {/if} {#if isShowChangeLocation} - (isShowChangeLocation = false)} /> + {/if} diff --git a/web/src/lib/components/shared-components/change-location.svelte b/web/src/lib/components/shared-components/change-location.svelte index 0829adaf4e..831fae02c2 100644 --- a/web/src/lib/components/shared-components/change-location.svelte +++ b/web/src/lib/components/shared-components/change-location.svelte @@ -21,11 +21,11 @@ interface Props { asset?: AssetResponseDto | undefined; - onCancel: () => void; - onConfirm: (point: Point) => void; + point?: Point; + onClose: (point?: Point) => void; } - let { asset = undefined, onCancel, onConfirm }: Props = $props(); + let { asset = undefined, point: initialPoint, onClose }: Props = $props(); let places: PlacesResponseDto[] = $state([]); let suggestedPlaces: PlacesResponseDto[] = $state([]); @@ -38,14 +38,20 @@ let previousLocation = get(lastChosenLocation); - let assetLat = $derived(asset?.exifInfo?.latitude ?? undefined); - let assetLng = $derived(asset?.exifInfo?.longitude ?? undefined); + let assetLat = $derived(initialPoint?.lat ?? asset?.exifInfo?.latitude ?? undefined); + let assetLng = $derived(initialPoint?.lng ?? asset?.exifInfo?.longitude ?? undefined); let mapLat = $derived(assetLat ?? previousLocation?.lat ?? undefined); let mapLng = $derived(assetLng ?? previousLocation?.lng ?? undefined); let zoom = $derived(mapLat !== undefined && mapLng !== undefined ? 12.5 : 1); + $effect(() => { + if (mapElement && initialPoint) { + mapElement.addClipMapMarker(initialPoint.lng, initialPoint.lat); + } + }); + $effect(() => { if (places) { suggestedPlaces = places.slice(0, 5); @@ -55,14 +61,14 @@ } }); - let point: Point | null = $state(null); + let point: Point | null = $state(initialPoint ?? null); - const handleConfirm = () => { - if (point) { + const handleConfirm = (confirmed?: boolean) => { + if (point && confirmed) { lastChosenLocation.set(point); - onConfirm(point); + onClose(point); } else { - onCancel(); + onClose(); } }; @@ -109,6 +115,11 @@ point = { lng: longitude, lat: latitude }; mapElement?.addClipMapMarker(longitude, latitude); }; + + const onUpdate = (lat: number, lng: number) => { + point = { lat, lng }; + mapElement?.addClipMapMarker(lng, lat); + }; (confirmed ? handleConfirm() : onCancel())} + onClose={handleConfirm} > {#snippet promptSnippet()}
    @@ -197,14 +208,7 @@
    - { - point = { lat, lng }; - mapElement?.addClipMapMarker(lng, lat); - }} - /> +
    {/snippet} diff --git a/web/src/lib/components/shared-components/date-picker.svelte b/web/src/lib/components/shared-components/date-picker.svelte new file mode 100644 index 0000000000..67b1ee73a9 --- /dev/null +++ b/web/src/lib/components/shared-components/date-picker.svelte @@ -0,0 +1,113 @@ + + +
    +
    +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + +
    +
    +
    diff --git a/web/src/lib/components/utilities-page/geolocation/geolocation.svelte b/web/src/lib/components/utilities-page/geolocation/geolocation.svelte new file mode 100644 index 0000000000..0efde6df7e --- /dev/null +++ b/web/src/lib/components/utilities-page/geolocation/geolocation.svelte @@ -0,0 +1,104 @@ + + +
    +
    + { + if (asset.exifInfo?.latitude && asset.exifInfo?.longitude) { + onLocation({ latitude: asset.exifInfo?.latitude, longitude: asset.exifInfo?.longitude }); + } else { + onSelectAsset(asset); + } + }} + onSelect={() => onSelectAsset(asset)} + onMouseEvent={() => onMouseEvent(asset)} + selected={assetInteraction.hasSelectedAsset(asset.id)} + selectionCandidate={assetInteraction.hasSelectionCandidate(asset.id)} + thumbnailSize={boxWidth} + readonly={hasGps} + /> + + {#if hasGps} +
    + {$t('gps')} +
    + {:else} +
    + {$t('gps_missing')} +
    + {/if} +
    + +
    +

    + {new Date(asset.localDateTime).toLocaleDateString(undefined, { + year: 'numeric', + month: 'short', + day: 'numeric', + })} +

    +

    + {new Date(asset.localDateTime).toLocaleTimeString(undefined, { + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + timeZone: 'UTC', + })} +

    + {#if hasGps} +

    + {asset.exifInfo?.country} +

    +

    + {asset.exifInfo?.city} +

    + {/if} +
    +
    diff --git a/web/src/lib/components/utilities-page/utilities-menu.svelte b/web/src/lib/components/utilities-page/utilities-menu.svelte index 5484ce4ea0..97e205fcd1 100644 --- a/web/src/lib/components/utilities-page/utilities-menu.svelte +++ b/web/src/lib/components/utilities-page/utilities-menu.svelte @@ -1,29 +1,23 @@

    {$t('organize_your_library').toUpperCase()}

    - - - - {$t('review_duplicates')} - - - - - {$t('review_large_files')} - + {#each links as link (link.href)} + + + {link.label} + + {/each}
    diff --git a/web/src/lib/constants.ts b/web/src/lib/constants.ts index f2de6d5deb..a4cdb656b4 100644 --- a/web/src/lib/constants.ts +++ b/web/src/lib/constants.ts @@ -52,6 +52,7 @@ export enum AppRoute { UTILITIES = '/utilities', DUPLICATES = '/utilities/duplicates', LARGE_FILES = '/utilities/large-files', + GEOLOCATION = '/utilities/geolocation', FOLDERS = '/folders', TAGS = '/tags', diff --git a/web/src/lib/modals/GeolocationUpdateConfirmModal.svelte b/web/src/lib/modals/GeolocationUpdateConfirmModal.svelte new file mode 100644 index 0000000000..7bad707447 --- /dev/null +++ b/web/src/lib/modals/GeolocationUpdateConfirmModal.svelte @@ -0,0 +1,33 @@ + + + + +

    + {$t('update_location_action_prompt', { + values: { + count: assetCount, + }, + })} +

    + +

    - {$t('latitude')}: {location.latitude}

    +

    - {$t('longitude')}: {location.longitude}

    +
    + + + + + + +
    diff --git a/web/src/lib/utils/date-time.spec.ts b/web/src/lib/utils/date-time.spec.ts index d96bef45d6..bca57863a9 100644 --- a/web/src/lib/utils/date-time.spec.ts +++ b/web/src/lib/utils/date-time.spec.ts @@ -1,5 +1,5 @@ import { writable } from 'svelte/store'; -import { getAlbumDateRange, timeToSeconds } from './date-time'; +import { buildDateRangeFromYearMonthAndDay, getAlbumDateRange, timeToSeconds } from './date-time'; describe('converting time to seconds', () => { it('parses hh:mm:ss correctly', () => { @@ -75,3 +75,24 @@ describe('getAlbumDate', () => { expect(getAlbumDateRange({ startDate: '2021-01-01T00:00:00+05:00' })).toEqual('Jan 1, 2021'); }); }); + +describe('buildDateRangeFromYearMonthAndDay', () => { + it('should build correct date range for a specific day', () => { + const result = buildDateRangeFromYearMonthAndDay(2023, 1, 8); + + expect(result.from).toContain('2023-01-08T00:00:00'); + expect(result.to).toContain('2023-01-09T00:00:00'); + }); + + it('should build correct date range for a month', () => { + const result = buildDateRangeFromYearMonthAndDay(2023, 2); + expect(result.from).toContain('2023-02-01T00:00:00'); + expect(result.to).toContain('2023-03-01T00:00:00'); + }); + + it('should build correct date range for a year', () => { + const result = buildDateRangeFromYearMonthAndDay(2023); + expect(result.from).toContain('2023-01-01T00:00:00'); + expect(result.to).toContain('2024-01-01T00:00:00'); + }); +}); diff --git a/web/src/lib/utils/date-time.ts b/web/src/lib/utils/date-time.ts index 8a50df9cfe..bf87d041cc 100644 --- a/web/src/lib/utils/date-time.ts +++ b/web/src/lib/utils/date-time.ts @@ -85,3 +85,33 @@ export const getAlbumDateRange = (album: { startDate?: string; endDate?: string */ export const asLocalTimeISO = (date: DateTime) => (date.setZone('utc', { keepLocalTime: true }) as DateTime).toISO(); + +/** + * Creates a date range for filtering assets based on year, month, and day parameters + */ +export const buildDateRangeFromYearMonthAndDay = (year: number, month?: number, day?: number) => { + const baseDate = DateTime.fromObject({ + year, + month: month || 1, + day: day || 1, + }); + + let from: DateTime; + let to: DateTime; + + if (day) { + from = baseDate.startOf('day'); + to = baseDate.plus({ days: 1 }).startOf('day'); + } else if (month) { + from = baseDate.startOf('month'); + to = baseDate.plus({ months: 1 }).startOf('month'); + } else { + from = baseDate.startOf('year'); + to = baseDate.plus({ years: 1 }).startOf('year'); + } + + return { + from: from.toISO() || undefined, + to: to.toISO() || undefined, + }; +}; diff --git a/web/src/lib/utils/navigation.ts b/web/src/lib/utils/navigation.ts index 642c8165df..c3fe051f12 100644 --- a/web/src/lib/utils/navigation.ts +++ b/web/src/lib/utils/navigation.ts @@ -145,3 +145,16 @@ export const clearQueryParam = async (queryParam: string, url: URL) => { await goto(url, { keepFocus: true }); } }; + +export const getQueryValue = (queryKey: string) => { + const url = globalThis.location.href; + const urlObject = new URL(url); + return urlObject.searchParams.get(queryKey); +}; + +export const setQueryValue = async (queryKey: string, queryValue: string) => { + const url = globalThis.location.href; + const urlObject = new URL(url); + urlObject.searchParams.set(queryKey, queryValue); + await goto(urlObject, { keepFocus: true }); +}; diff --git a/web/src/lib/utils/string-utils.ts b/web/src/lib/utils/string-utils.ts index 0170c34737..3795af40c7 100644 --- a/web/src/lib/utils/string-utils.ts +++ b/web/src/lib/utils/string-utils.ts @@ -5,3 +5,13 @@ export const removeAccents = (str: string) => { export const normalizeSearchString = (str: string) => { return removeAccents(str.toLocaleLowerCase()); }; + +export const buildDateString = (year: number, month?: number, day?: number) => { + return [ + year.toString(), + month && !Number.isNaN(month) ? month.toString() : undefined, + day && !Number.isNaN(day) ? day.toString() : undefined, + ] + .filter((date) => date !== undefined) + .join('-'); +}; diff --git a/web/src/routes/(user)/utilities/geolocation/+page.svelte b/web/src/routes/(user)/utilities/geolocation/+page.svelte new file mode 100644 index 0000000000..c251146b45 --- /dev/null +++ b/web/src/routes/(user)/utilities/geolocation/+page.svelte @@ -0,0 +1,321 @@ + + + + + + {#snippet buttons()} +
    + {#if filteredAssets.length > 0} + + {/if} +
    +

    {$t('selected_gps_coordinates')}

    + {location.latitude.toFixed(3)}, {location.longitude.toFixed(3)} +
    + + + +
    + {/snippet} + +
    +
    + +
    + +
    + + +
    +
    + + {#if isLoading} +
    + +
    + {/if} + + {#if filteredAssets && filteredAssets.length > 0} +
    + {#each filteredAssets as asset (asset.id)} + handleSelectAssets(asset)} + onMouseEvent={(asset) => assetMouseEventHandler(asset)} + onLocation={(selected) => { + location = selected; + locationUpdated = true; + setTimeout(() => { + locationUpdated = false; + }, 1000); + }} + /> + {/each} +
    + {:else} +
    + {#if partialDate == null} + + {:else if showOnlyAssetsWithoutLocation && filteredAssets.length === 0 && assets.length > 0} + + {:else} + + {/if} +
    + {/if} +
    diff --git a/web/src/routes/(user)/utilities/geolocation/+page.ts b/web/src/routes/(user)/utilities/geolocation/+page.ts new file mode 100644 index 0000000000..f5c227a7ef --- /dev/null +++ b/web/src/routes/(user)/utilities/geolocation/+page.ts @@ -0,0 +1,17 @@ +import { authenticate } from '$lib/utils/auth'; +import { getFormatter } from '$lib/utils/i18n'; +import { getQueryValue } from '$lib/utils/navigation'; +import type { PageLoad } from './$types'; + +export const load = (async ({ url }) => { + await authenticate(url); + const partialDate = getQueryValue('date'); + const $t = await getFormatter(); + + return { + partialDate, + meta: { + title: $t('manage_geolocation'), + }, + }; +}) satisfies PageLoad; From 8853079c5476eace3b359d6f6f0dff6b38957930 Mon Sep 17 00:00:00 2001 From: Sudheer Reddy Puthana Date: Thu, 28 Aug 2025 13:30:15 -0400 Subject: [PATCH 356/748] feat(mobile): add read only mode (#19368) * feat(mobile): Add Kid (Readonly) Mode toggle This commit introduces a "Kid (Readonly) Mode" feature. - Adds a `KidModeProvider` to manage the state of Kid Mode. - Implements a `KidModeCheckbox` widget in the app bar dialog to toggle Kid Mode. - When Kid Mode is enabled, - Disables selecting the multigrid & the bottom bar - Removes the top bar from view Signed-off-by: Sudheer Puthana Reverts the changes to devtools_options.yaml file Signed-off-by: Sudheer Puthana refactor: replace Kid Mode with Readonly Mode This commit replaces the "Kid Mode" feature with a more generic "Readonly Mode". - Renamed `KidModeProvider` to `ReadonlyModeProvider`. - Readonly Mode state is now persisted in app settings. - Added a new app setting `allowUserAvatarOverride` to toggle read-only mode. - Updated translations. - Added a message in the app bar dialog indicating when read-only mode is active. Signed-off-by: Sudheer Puthana Address comments - - Removes the `allowUserAvatarOverride` setting. - Hides the bottom gallery bar when read-only mode is enabled. - Adds an icon on the main app bar when read-only mode is enabled with a snackbar. Signed-off-by: Sudheer Puthana Update to snackbar - When toggling readonly mode from either the settings or the app bar, a snackbar notification will now appear. - The readonly mode message in the profile drawer has been restyled. - The upload button in the app bar is now hidden when readonly mode is enabled. Signed-off-by: Sudheer Puthana Removes clearing of snackbar Signed-off-by: Sudheer Puthana Address Comments - Consolidated snackbar messages for enabling/disabling readonly mode. - Ensured the "Select All" icon in asset group titles is hidden in readonly mode. Signed-off-by: Sudheer Puthana Adds in the missing translation keys for readonly_mode Signed-off-by: Sudheer Puthana Fix translation Signed-off-by: Sudheer Puthana Fix check failure for BorderRadius Signed-off-by: Sudheer Puthana Changes: - Adjusted AppBar background color in readonly mode. - Removes cross-out pencil icon button in favor of above. - Hides the "Edit" icon next to date/time, disable description and onTap for people and location when readonly mode is enabled. Signed-off-by: Sudheer Puthana Address comments from Alex - Moved readonly mode check to `GalleryAppBar` to hide the entire `TopControlAppBar` when readonly mode is enabled. - Changed `toggleReadonlyMode` in `ImmichAppBar` to directly toggle the state. Signed-off-by: Sudheer Puthana migrate readonly mode to new beta timeline remove readonly mode from legacy UI only show readonly functionality when on beta timeline simplify selection icon update generated provider chore: more formatting * fix: bad merge * chore: use Notifier for readonlyModeProvider * fix: drag select now honors readonly mode * fix: disable asset bottom sheet in readonly * fix: disable editing user icon when in readonly * chore: remove generated file * fix: disable tabs instead entire tab bar This solves the issues with the scrubber * chore: remove unneeded import * chore: lint * remove unused condition in bottomsheet --------- Co-authored-by: Brandon Wees Co-authored-by: Alex --- i18n/en.json | 5 ++ mobile/lib/domain/models/store.model.dart | 3 ++ .../pages/common/change_experience.page.dart | 2 + mobile/lib/pages/common/tab_shell.page.dart | 5 ++ .../asset_viewer/asset_viewer.page.dart | 3 +- .../asset_viewer/bottom_bar.widget.dart | 4 +- .../asset_viewer/top_app_bar.widget.dart | 4 +- .../widgets/timeline/fixed/segment.model.dart | 4 +- .../widgets/timeline/header.widget.dart | 27 ++++++----- .../widgets/timeline/timeline.widget.dart | 6 ++- .../readonly_mode.provider.dart | 36 ++++++++++++++ mobile/lib/services/app_settings.service.dart | 3 +- .../common/app_bar_dialog/app_bar_dialog.dart | 22 +++++++++ .../app_bar_dialog/app_bar_profile_info.dart | 47 ++++++++++++++----- .../widgets/common/immich_sliver_app_bar.dart | 25 ++++++++-- .../widgets/settings/advanced_settings.dart | 24 ++++++++++ 16 files changed, 187 insertions(+), 33 deletions(-) create mode 100644 mobile/lib/providers/infrastructure/readonly_mode.provider.dart diff --git a/i18n/en.json b/i18n/en.json index 5d215e2c36..c83aac618e 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -396,6 +396,8 @@ "advanced_settings_prefer_remote_title": "Prefer remote images", "advanced_settings_proxy_headers_subtitle": "Define proxy headers Immich should send with each network request", "advanced_settings_proxy_headers_title": "Proxy Headers", + "advanced_settings_readonly_mode_subtitle": "Enables the read-only mode where the photos can be only viewed, things like selecting multiple images, sharing, casting, delete are all disabled. Enable/Disable read-only via user avatar from the main screen", + "advanced_settings_readonly_mode_title": "Read-only Mode", "advanced_settings_self_signed_ssl_subtitle": "Skips SSL certificate verification for the server endpoint. Required for self-signed certificates.", "advanced_settings_self_signed_ssl_title": "Allow self-signed SSL certificates", "advanced_settings_sync_remote_deletions_subtitle": "Automatically delete or restore an asset on this device when that action is taken on the web", @@ -1516,6 +1518,7 @@ "profile_drawer_client_out_of_date_minor": "Mobile App is out of date. Please update to the latest minor version.", "profile_drawer_client_server_up_to_date": "Client and Server are up-to-date", "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "Read-only mode enabled. Double-tap the user avatar icon to exit.", "profile_drawer_server_out_of_date_major": "Server is out of date. Please update to the latest major version.", "profile_drawer_server_out_of_date_minor": "Server is out of date. Please update to the latest minor version.", "profile_image_of_user": "Profile image of {user}", @@ -1561,6 +1564,8 @@ "rating_description": "Display the EXIF rating in the info panel", "reaction_options": "Reaction options", "read_changelog": "Read Changelog", + "readonly_mode_disabled": "Read-only mode disabled", + "readonly_mode_enabled": "Read-only mode enabled", "reassign": "Reassign", "reassigned_assets_to_existing_person": "Re-assigned {count, plural, one {# asset} other {# assets}} to {name, select, null {an existing person} other {{name}}}", "reassigned_assets_to_new_person": "Re-assigned {count, plural, one {# asset} other {# assets}} to a new person", diff --git a/mobile/lib/domain/models/store.model.dart b/mobile/lib/domain/models/store.model.dart index e4e316b814..6dcd81774a 100644 --- a/mobile/lib/domain/models/store.model.dart +++ b/mobile/lib/domain/models/store.model.dart @@ -67,6 +67,9 @@ enum StoreKey { loadOriginalVideo._(136), manageLocalMediaAndroid._(137), + // Read-only Mode settings + readonlyModeEnabled._(138), + // Experimental stuff photoManagerCustomFilter._(1000), betaPromptShown._(1001), diff --git a/mobile/lib/pages/common/change_experience.page.dart b/mobile/lib/pages/common/change_experience.page.dart index 9064f32066..9bb2895907 100644 --- a/mobile/lib/pages/common/change_experience.page.dart +++ b/mobile/lib/pages/common/change_experience.page.dart @@ -13,6 +13,7 @@ import 'package:immich_mobile/providers/backup/backup.provider.dart'; import 'package:immich_mobile/providers/backup/manual_upload.provider.dart'; import 'package:immich_mobile/providers/gallery_permission.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; import 'package:immich_mobile/providers/websocket.provider.dart'; import 'package:immich_mobile/services/background.service.dart'; import 'package:immich_mobile/utils/migration.dart'; @@ -75,6 +76,7 @@ class _ChangeExperiencePageState extends ConsumerState { await ref.read(backgroundSyncProvider).cancel(); ref.read(websocketProvider.notifier).stopListeningToBetaEvents(); ref.read(websocketProvider.notifier).startListeningToOldEvents(); + ref.read(readonlyModeProvider.notifier).setReadonlyMode(false); await migrateStoreToIsar(ref.read(isarProvider), ref.read(driftProvider)); await ref.read(backgroundServiceProvider).resumeServiceIfEnabled(); await ref.read(driftBackgroundUploadFgService).disableUploadService(); diff --git a/mobile/lib/pages/common/tab_shell.page.dart b/mobile/lib/pages/common/tab_shell.page.dart index 983164831a..41b01ad3a3 100644 --- a/mobile/lib/pages/common/tab_shell.page.dart +++ b/mobile/lib/pages/common/tab_shell.page.dart @@ -11,6 +11,7 @@ import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; import 'package:immich_mobile/providers/search/search_input_focus.provider.dart'; import 'package:immich_mobile/providers/tab.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; @@ -54,6 +55,7 @@ class _TabShellPageState extends ConsumerState { @override Widget build(BuildContext context) { final isScreenLandscape = context.orientation == Orientation.landscape; + final isReadonlyModeEnabled = ref.watch(readonlyModeProvider); final navigationDestinations = [ NavigationDestination( @@ -65,16 +67,19 @@ class _TabShellPageState extends ConsumerState { label: 'search'.tr(), icon: const Icon(Icons.search_rounded), selectedIcon: Icon(Icons.search, color: context.primaryColor), + enabled: !isReadonlyModeEnabled, ), NavigationDestination( label: 'albums'.tr(), icon: const Icon(Icons.photo_album_outlined), selectedIcon: Icon(Icons.photo_album_rounded, color: context.primaryColor), + enabled: !isReadonlyModeEnabled, ), NavigationDestination( label: 'library'.tr(), icon: const Icon(Icons.space_dashboard_outlined), selectedIcon: Icon(Icons.space_dashboard_rounded, color: context.primaryColor), + enabled: !isReadonlyModeEnabled, ), ]; diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart index 6c78cfac3e..5e906b820f 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart @@ -24,6 +24,7 @@ import 'package:immich_mobile/providers/asset_viewer/video_player_controls_provi import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart'; import 'package:immich_mobile/providers/cast.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart'; import 'package:immich_mobile/widgets/photo_view/photo_view.dart'; @@ -308,7 +309,7 @@ class _AssetViewerState extends ConsumerState { bottomSheetController.jumpTo((centre + distanceToOrigin) / ctx.height); } - if (distanceToOrigin > openThreshold && !showingBottomSheet) { + if (distanceToOrigin > openThreshold && !showingBottomSheet && !ref.read(readonlyModeProvider)) { _openBottomSheet(ctx); } } diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart index 732afee7f9..e581e32df0 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart @@ -12,6 +12,7 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/unarchive_acti import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; import 'package:immich_mobile/providers/routes.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/widgets/asset_viewer/video_controls.dart'; @@ -26,6 +27,7 @@ class ViewerBottomBar extends ConsumerWidget { return const SizedBox.shrink(); } + final isReadonlyModeEnabled = ref.watch(readonlyModeProvider); final user = ref.watch(currentUserProvider); final isOwner = asset is RemoteAsset && asset.ownerId == user?.id; final isSheetOpen = ref.watch(assetViewerProvider.select((s) => s.showingBottomSheet)); @@ -60,7 +62,7 @@ class ViewerBottomBar extends ConsumerWidget { duration: Durations.short2, child: AnimatedSwitcher( duration: Durations.short4, - child: isSheetOpen + child: isSheetOpen || isReadonlyModeEnabled ? const SizedBox.shrink() : Theme( data: context.themeData.copyWith( diff --git a/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart index 411e279460..570df1afbb 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart @@ -14,6 +14,7 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/unfavorite_act import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/cast.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; import 'package:immich_mobile/providers/routes.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; @@ -34,6 +35,7 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget { final user = ref.watch(currentUserProvider); final isOwner = asset is RemoteAsset && asset.ownerId == user?.id; final isInLockedView = ref.watch(inLockedViewProvider); + final isReadonlyModeEnabled = ref.watch(readonlyModeProvider); final previousRouteName = ref.watch(previousRouteNameProvider); final showViewInTimelineButton = @@ -94,7 +96,7 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget { iconTheme: const IconThemeData(size: 22, color: Colors.white), actionsIconTheme: const IconThemeData(size: 22, color: Colors.white), shape: const Border(), - actions: isShowingSheet + actions: isShowingSheet || isReadonlyModeEnabled ? null : isInLockedView ? lockedViewActions diff --git a/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart b/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart index 05f96d49de..5eda738e76 100644 --- a/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart +++ b/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart @@ -15,6 +15,7 @@ import 'package:immich_mobile/presentation/widgets/timeline/timeline_drag_region import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart'; import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/routing/router.dart'; @@ -190,11 +191,12 @@ class _AssetTileWidget extends ConsumerWidget { final lockSelection = _getLockSelectionStatus(ref); final showStorageIndicator = ref.watch(timelineArgsProvider.select((args) => args.showStorageIndicator)); + final isReadonlyModeEnabled = ref.watch(readonlyModeProvider); return RepaintBoundary( child: GestureDetector( onTap: () => lockSelection ? null : _handleOnTap(context, ref, assetIndex, asset, heroOffset), - onLongPress: () => lockSelection ? null : _handleOnLongPress(ref, asset), + onLongPress: () => lockSelection || isReadonlyModeEnabled ? null : _handleOnLongPress(ref, asset), child: ThumbnailTile( asset, lockSelection: lockSelection, diff --git a/mobile/lib/presentation/widgets/timeline/header.widget.dart b/mobile/lib/presentation/widgets/timeline/header.widget.dart index 8e383a1477..3eff305251 100644 --- a/mobile/lib/presentation/widgets/timeline/header.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/header.widget.dart @@ -7,9 +7,10 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; -class TimelineHeader extends StatelessWidget { +class TimelineHeader extends HookConsumerWidget { final Bucket bucket; final HeaderType header; final double height; @@ -36,13 +37,12 @@ class TimelineHeader extends StatelessWidget { } @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { if (bucket is! TimeBucket || header == HeaderType.none) { return const SizedBox.shrink(); } final date = (bucket as TimeBucket).date; - final isMonthHeader = header == HeaderType.month || header == HeaderType.monthAndDay; final isDayHeader = header == HeaderType.day || header == HeaderType.monthAndDay; @@ -98,16 +98,19 @@ class _BulkSelectIconButton extends ConsumerWidget { bucketAssets = []; } + final isReadonlyModeEnabled = ref.watch(readonlyModeProvider); final isAllSelected = ref.watch(bucketSelectionProvider(bucketAssets)); - return IconButton( - onPressed: () { - ref.read(multiSelectProvider.notifier).toggleBucketSelection(assetOffset, bucket.assetCount); - ref.read(hapticFeedbackProvider.notifier).heavyImpact(); - }, - icon: isAllSelected - ? Icon(Icons.check_circle_rounded, size: 26, color: context.primaryColor) - : Icon(Icons.check_circle_outline_rounded, size: 26, color: context.colorScheme.onSurfaceSecondary), - ); + return isReadonlyModeEnabled + ? const SizedBox.shrink() + : IconButton( + onPressed: () { + ref.read(multiSelectProvider.notifier).toggleBucketSelection(assetOffset, bucket.assetCount); + ref.read(hapticFeedbackProvider.notifier).heavyImpact(); + }, + icon: isAllSelected + ? Icon(Icons.check_circle_rounded, size: 26, color: context.primaryColor) + : Icon(Icons.check_circle_outline_rounded, size: 26, color: context.colorScheme.onSurfaceSecondary), + ); } } diff --git a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart index c859ae0e80..125f8505a1 100644 --- a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart @@ -19,6 +19,7 @@ import 'package:immich_mobile/presentation/widgets/timeline/scrubber.widget.dart import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline_drag_region.dart'; +import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; @@ -256,6 +257,7 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { final maxHeight = ref.watch(timelineArgsProvider.select((args) => args.maxHeight)); final isSelectionMode = ref.watch(multiSelectProvider.select((s) => s.forceEnable)); final isMultiSelectEnabled = ref.watch(multiSelectProvider.select((s) => s.isEnabled)); + final isReadonlyModeEnabled = ref.watch(readonlyModeProvider); return PopScope( canPop: !isMultiSelectEnabled, @@ -342,9 +344,9 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { ), }, child: TimelineDragRegion( - onStart: _setDragStartIndex, + onStart: !isReadonlyModeEnabled ? _setDragStartIndex : null, onAssetEnter: _handleDragAssetEnter, - onEnd: _stopDrag, + onEnd: !isReadonlyModeEnabled ? _stopDrag : null, onScroll: _dragScroll, onScrollStart: () { // Minimize the bottom sheet when drag selection starts diff --git a/mobile/lib/providers/infrastructure/readonly_mode.provider.dart b/mobile/lib/providers/infrastructure/readonly_mode.provider.dart new file mode 100644 index 0000000000..9e96c3cfc4 --- /dev/null +++ b/mobile/lib/providers/infrastructure/readonly_mode.provider.dart @@ -0,0 +1,36 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; + +class ReadOnlyModeNotifier extends Notifier { + late AppSettingsService _appSettingService; + + @override + bool build() { + _appSettingService = ref.read(appSettingsServiceProvider); + final readonlyMode = _appSettingService.getSetting(AppSettingsEnum.readonlyModeEnabled); + return readonlyMode; + } + + void setMode(bool value) { + _appSettingService.setSetting(AppSettingsEnum.readonlyModeEnabled, value); + state = value; + + if (value) { + ref.read(appRouterProvider).navigate(const MainTimelineRoute()); + } + } + + void setReadonlyMode(bool isEnabled) { + state = isEnabled; + setMode(state); + } + + void toggleReadonlyMode() { + state = !state; + setMode(state); + } +} + +final readonlyModeProvider = NotifierProvider(() => ReadOnlyModeNotifier()); diff --git a/mobile/lib/services/app_settings.service.dart b/mobile/lib/services/app_settings.service.dart index 8a4b0c6719..d98b14408f 100644 --- a/mobile/lib/services/app_settings.service.dart +++ b/mobile/lib/services/app_settings.service.dart @@ -49,7 +49,8 @@ enum AppSettingsEnum { betaTimeline(StoreKey.betaTimeline, null, false), enableBackup(StoreKey.enableBackup, null, false), useCellularForUploadVideos(StoreKey.useWifiForUploadVideos, null, false), - useCellularForUploadPhotos(StoreKey.useWifiForUploadPhotos, null, false); + useCellularForUploadPhotos(StoreKey.useWifiForUploadPhotos, null, false), + readonlyModeEnabled(StoreKey.readonlyModeEnabled, "readonlyModeEnabled", false); const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue); diff --git a/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart b/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart index ccfc374fef..b204058859 100644 --- a/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart +++ b/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart @@ -12,6 +12,7 @@ import 'package:immich_mobile/providers/backup/manual_upload.provider.dart'; import 'package:immich_mobile/providers/locale_provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/providers/websocket.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/utils/bytes_units.dart'; import 'package:immich_mobile/widgets/common/app_bar_dialog/app_bar_profile_info.dart'; @@ -33,6 +34,7 @@ class ImmichAppBarDialog extends HookConsumerWidget { final horizontalPadding = isHorizontal ? 100.0 : 20.0; final user = ref.watch(currentUserProvider); final isLoggingOut = useState(false); + final isReadonlyModeEnabled = ref.watch(readonlyModeProvider); useEffect(() { ref.read(backupProvider.notifier).updateDiskInfo(); @@ -214,6 +216,25 @@ class ImmichAppBarDialog extends HookConsumerWidget { ); } + buildReadonlyMessage() { + return Padding( + padding: const EdgeInsets.only(left: 10.0, right: 10.0), + child: ListTile( + dense: true, + visualDensity: VisualDensity.standard, + contentPadding: const EdgeInsets.only(left: 20, right: 20), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), + minLeadingWidth: 20, + tileColor: theme.primaryColor.withAlpha(80), + title: Text( + "profile_drawer_readonly_mode", + style: theme.textTheme.labelLarge?.copyWith(color: theme.textTheme.labelLarge?.color?.withAlpha(250)), + textAlign: TextAlign.center, + ).tr(), + ), + ); + } + return Dismissible( behavior: HitTestBehavior.translucent, direction: DismissDirection.down, @@ -238,6 +259,7 @@ class ImmichAppBarDialog extends HookConsumerWidget { const AppBarProfileInfoBox(), buildStorageInformation(), const AppBarServerInfo(), + if (isReadonlyModeEnabled) buildReadonlyMessage(), buildAppLogButton(), buildSettingButton(), buildSignOutButton(), diff --git a/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart b/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart index b1f5b192dd..a9c7a467c2 100644 --- a/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart +++ b/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart @@ -1,9 +1,12 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/providers/auth.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; import 'package:immich_mobile/providers/backup/backup.provider.dart'; import 'package:immich_mobile/providers/upload_profile_image.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; @@ -17,6 +20,7 @@ class AppBarProfileInfoBox extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final authState = ref.watch(authProvider); final uploadProfileImageStatus = ref.watch(uploadProfileImageProvider).status; + final isReadonlyModeEnabled = ref.watch(readonlyModeProvider); final user = ref.watch(currentUserProvider); buildUserProfileImage() { @@ -55,6 +59,25 @@ class AppBarProfileInfoBox extends HookConsumerWidget { } } + void toggleReadonlyMode() { + // read only mode is only supported int he beta experience + // TODO: remove this check when the beta UI goes stable + if (!Store.isBetaTimelineEnabled) return; + + final isReadonlyModeEnabled = ref.watch(readonlyModeProvider); + ref.read(readonlyModeProvider.notifier).toggleReadonlyMode(); + + context.scaffoldMessenger.showSnackBar( + SnackBar( + duration: const Duration(seconds: 2), + content: Text( + (isReadonlyModeEnabled ? "readonly_mode_disabled" : "readonly_mode_enabled").tr(), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), + ), + ), + ); + } + return Padding( padding: const EdgeInsets.symmetric(horizontal: 10.0), child: Container( @@ -67,23 +90,25 @@ class AppBarProfileInfoBox extends HookConsumerWidget { minLeadingWidth: 50, leading: GestureDetector( onTap: pickUserProfileImage, + onDoubleTap: toggleReadonlyMode, child: Stack( clipBehavior: Clip.none, children: [ buildUserProfileImage(), - Positioned( - bottom: -5, - right: -8, - child: Material( - color: context.colorScheme.surfaceContainerHighest, - elevation: 3, - shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(50.0))), - child: Padding( - padding: const EdgeInsets.all(5.0), - child: Icon(Icons.camera_alt_outlined, color: context.primaryColor, size: 14), + if (!isReadonlyModeEnabled) + Positioned( + bottom: -5, + right: -8, + child: Material( + color: context.colorScheme.surfaceContainerHighest, + elevation: 3, + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(50.0))), + child: Padding( + padding: const EdgeInsets.all(5.0), + child: Icon(Icons.camera_alt_outlined, color: context.primaryColor, size: 14), + ), ), ), - ), ], ), ), diff --git a/mobile/lib/widgets/common/immich_sliver_app_bar.dart b/mobile/lib/widgets/common/immich_sliver_app_bar.dart index 06a97d1ce5..78fa607666 100644 --- a/mobile/lib/widgets/common/immich_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/immich_sliver_app_bar.dart @@ -10,6 +10,7 @@ import 'package:immich_mobile/models/server_info/server_info.model.dart'; import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; import 'package:immich_mobile/providers/cast.provider.dart'; import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/sync_status.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; @@ -42,6 +43,7 @@ class ImmichSliverAppBar extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final isCasting = ref.watch(castProvider.select((c) => c.isCasting)); + final isReadonlyModeEnabled = ref.watch(readonlyModeProvider); final isMultiSelectEnabled = ref.watch(multiSelectProvider.select((s) => s.isEnabled)); return SliverAnimatedOpacity( @@ -57,7 +59,7 @@ class ImmichSliverAppBar extends ConsumerWidget { centerTitle: false, title: title ?? const _ImmichLogoWithText(), actions: [ - if (isCasting) + if (isCasting && !isReadonlyModeEnabled) Padding( padding: const EdgeInsets.only(right: 12), child: IconButton( @@ -70,12 +72,13 @@ class ImmichSliverAppBar extends ConsumerWidget { const _SyncStatusIndicator(), if (actions != null) ...actions!.map((action) => Padding(padding: const EdgeInsets.only(right: 16), child: action)), - if (kDebugMode || kProfileMode) + if ((kDebugMode || kProfileMode) && !isReadonlyModeEnabled) IconButton( icon: const Icon(Icons.science_rounded), onPressed: () => context.pushRoute(const FeatInDevRoute()), ), - if (showUploadButton) const Padding(padding: EdgeInsets.only(right: 20), child: _BackupIndicator()), + if (showUploadButton && !isReadonlyModeEnabled) + const Padding(padding: EdgeInsets.only(right: 20), child: _BackupIndicator()), const Padding(padding: EdgeInsets.only(right: 20), child: _ProfileIndicator()), ], ), @@ -137,8 +140,24 @@ class _ProfileIndicator extends ConsumerWidget { final user = ref.watch(currentUserProvider); const widgetSize = 30.0; + void toggleReadonlyMode() { + final isReadonlyModeEnabled = ref.watch(readonlyModeProvider); + ref.read(readonlyModeProvider.notifier).toggleReadonlyMode(); + + context.scaffoldMessenger.showSnackBar( + SnackBar( + duration: const Duration(seconds: 2), + content: Text( + (isReadonlyModeEnabled ? "readonly_mode_disabled" : "readonly_mode_enabled").tr(), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), + ), + ), + ); + } + return InkWell( onTap: () => showDialog(context: context, useRootNavigator: false, builder: (ctx) => const ImmichAppBarDialog()), + onDoubleTap: () => toggleReadonlyMode(), borderRadius: const BorderRadius.all(Radius.circular(12)), child: Badge( label: Container( diff --git a/mobile/lib/widgets/settings/advanced_settings.dart b/mobile/lib/widgets/settings/advanced_settings.dart index 3f196b840b..cd2fa93b85 100644 --- a/mobile/lib/widgets/settings/advanced_settings.dart +++ b/mobile/lib/widgets/settings/advanced_settings.dart @@ -6,7 +6,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart' hide Store; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/services/log.service.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; import 'package:immich_mobile/repositories/local_files_manager.repository.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; @@ -31,6 +34,7 @@ class AdvancedSettings extends HookConsumerWidget { final preferRemote = useAppSettingsState(AppSettingsEnum.preferRemoteImage); final allowSelfSignedSSLCert = useAppSettingsState(AppSettingsEnum.allowSelfSignedSSLCert); final useAlternatePMFilter = useAppSettingsState(AppSettingsEnum.photoManagerCustomFilter); + final readonlyModeEnabled = useAppSettingsState(AppSettingsEnum.readonlyModeEnabled); final logLevel = Level.LEVELS[levelId.value].name; @@ -102,6 +106,26 @@ class AdvancedSettings extends HookConsumerWidget { title: "advanced_settings_enable_alternate_media_filter_title".tr(), subtitle: "advanced_settings_enable_alternate_media_filter_subtitle".tr(), ), + // TODO: Remove this check when beta timeline goes stable + if (Store.isBetaTimelineEnabled) + SettingsSwitchListTile( + valueNotifier: readonlyModeEnabled, + title: "advanced_settings_readonly_mode_title".tr(), + subtitle: "advanced_settings_readonly_mode_subtitle".tr(), + onChanged: (value) { + readonlyModeEnabled.value = value; + ref.read(readonlyModeProvider.notifier).setReadonlyMode(value); + context.scaffoldMessenger.showSnackBar( + SnackBar( + duration: const Duration(seconds: 2), + content: Text( + (value ? "readonly_mode_enabled" : "readonly_mode_disabled").tr(), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), + ), + ), + ); + }, + ), ]; return SettingsSubPageScaffold(settings: advancedSettings); From b6223af5cab601e8ad9e4b332e580becdc83faa3 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 28 Aug 2025 18:50:45 +0000 Subject: [PATCH 357/748] chore: version v1.140.0 --- cli/package.json | 2 +- docs/static/archived-versions.json | 4 ++++ e2e/package.json | 2 +- mobile/android/fastlane/Fastfile | 4 ++-- mobile/ios/fastlane/Fastfile | 2 +- mobile/openapi/README.md | 2 +- mobile/pubspec.yaml | 2 +- open-api/immich-openapi-specs.json | 2 +- open-api/typescript-sdk/package.json | 2 +- open-api/typescript-sdk/src/fetch-client.ts | 2 +- server/package.json | 2 +- web/package.json | 2 +- 12 files changed, 16 insertions(+), 12 deletions(-) diff --git a/cli/package.json b/cli/package.json index 8962ad4645..b11cebccea 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@immich/cli", - "version": "2.2.84", + "version": "2.2.85", "description": "Command Line Interface (CLI) for Immich", "type": "module", "exports": "./dist/index.js", diff --git a/docs/static/archived-versions.json b/docs/static/archived-versions.json index b884b358f0..8988cda910 100644 --- a/docs/static/archived-versions.json +++ b/docs/static/archived-versions.json @@ -1,4 +1,8 @@ [ + { + "label": "v1.140.0", + "url": "https://v1.140.0.archive.immich.app" + }, { "label": "v1.139.4", "url": "https://v1.139.4.archive.immich.app" diff --git a/e2e/package.json b/e2e/package.json index beddd8e49d..559ddf00ee 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -1,6 +1,6 @@ { "name": "immich-e2e", - "version": "1.139.4", + "version": "1.140.0", "description": "", "main": "index.js", "type": "module", diff --git a/mobile/android/fastlane/Fastfile b/mobile/android/fastlane/Fastfile index 1be349f808..dee51026d5 100644 --- a/mobile/android/fastlane/Fastfile +++ b/mobile/android/fastlane/Fastfile @@ -35,8 +35,8 @@ platform :android do task: 'bundle', build_type: 'Release', properties: { - "android.injected.version.code" => 3009, - "android.injected.version.name" => "1.139.4", + "android.injected.version.code" => 3010, + "android.injected.version.name" => "1.140.0", } ) upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab') diff --git a/mobile/ios/fastlane/Fastfile b/mobile/ios/fastlane/Fastfile index 60a5f24eef..85b3c451d7 100644 --- a/mobile/ios/fastlane/Fastfile +++ b/mobile/ios/fastlane/Fastfile @@ -22,7 +22,7 @@ platform :ios do path: "./Runner.xcodeproj", ) increment_version_number( - version_number: "1.139.4" + version_number: "1.140.0" ) increment_build_number( build_number: latest_testflight_build_number + 1, diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 27a0c6fcbe..6e9b23ed8f 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -3,7 +3,7 @@ Immich API This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: -- API version: 1.139.4 +- API version: 1.140.0 - Generator version: 7.8.0 - Build package: org.openapitools.codegen.languages.DartClientCodegen diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 7d40c80a26..b3f659af24 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -2,7 +2,7 @@ name: immich_mobile description: Immich - selfhosted backup media file on mobile phone publish_to: 'none' -version: 1.139.4+3009 +version: 1.140.0+3010 environment: sdk: '>=3.8.0 <4.0.0' diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 44b4e0da4f..2692aa7593 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -9789,7 +9789,7 @@ "info": { "title": "Immich", "description": "Immich API", - "version": "1.139.4", + "version": "1.140.0", "contact": {} }, "tags": [], diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index 5b0b693c85..e67bcdaac6 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@immich/sdk", - "version": "1.139.4", + "version": "1.140.0", "description": "Auto-generated TypeScript SDK for the Immich API", "type": "module", "main": "./build/index.js", diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 3213b5e240..c80e8d6a4b 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -1,6 +1,6 @@ /** * Immich - * 1.139.4 + * 1.140.0 * DO NOT MODIFY - This file has been generated using oazapfts. * See https://www.npmjs.com/package/oazapfts */ diff --git a/server/package.json b/server/package.json index 5ac0a8f043..217fc80dfe 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "immich", - "version": "1.139.4", + "version": "1.140.0", "description": "", "author": "", "private": true, diff --git a/web/package.json b/web/package.json index dffc246da2..f60f55bdd1 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "immich-web", - "version": "1.139.4", + "version": "1.140.0", "license": "GNU Affero General Public License version 3", "type": "module", "scripts": { From 460e1d4715a9910ce451c01f5c8acc857a2beb5f Mon Sep 17 00:00:00 2001 From: Sergey Katsubo Date: Fri, 29 Aug 2025 03:22:40 +0300 Subject: [PATCH 358/748] fix(server): folder sort order (#21383) --- server/src/queries/view.repository.sql | 2 ++ server/src/repositories/view-repository.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/server/src/queries/view.repository.sql b/server/src/queries/view.repository.sql index 81f5ca20b8..31da10123f 100644 --- a/server/src/queries/view.repository.sql +++ b/server/src/queries/view.repository.sql @@ -12,6 +12,8 @@ where and "fileCreatedAt" is not null and "fileModifiedAt" is not null and "localDateTime" is not null +order by + "directoryPath" asc -- ViewRepository.getAssetsByOriginalPath select diff --git a/server/src/repositories/view-repository.ts b/server/src/repositories/view-repository.ts index 93c1280191..ceab79f6eb 100644 --- a/server/src/repositories/view-repository.ts +++ b/server/src/repositories/view-repository.ts @@ -20,6 +20,7 @@ export class ViewRepository { .where('fileCreatedAt', 'is not', null) .where('fileModifiedAt', 'is not', null) .where('localDateTime', 'is not', null) + .orderBy('directoryPath', 'asc') .execute(); return results.map((row) => row.directoryPath.replaceAll(/\/$/g, '')); From 94872414817bc3bdd4c728668603cf1d67f7aab3 Mon Sep 17 00:00:00 2001 From: Mert <101130780+mertalev@users.noreply.github.com> Date: Thu, 28 Aug 2025 20:23:40 -0400 Subject: [PATCH 359/748] fix(server): refresh faces query (#21380) --- server/src/queries/asset.job.repository.sql | 3 +-- server/src/repositories/asset-job.repository.ts | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/server/src/queries/asset.job.repository.sql b/server/src/queries/asset.job.repository.sql index df8163be3e..ef1b5fe79e 100644 --- a/server/src/queries/asset.job.repository.sql +++ b/server/src/queries/asset.job.repository.sql @@ -468,9 +468,8 @@ where "asset"."visibility" != $1 and "asset"."deletedAt" is null and "job_status"."previewAt" is not null - and "job_status"."facesRecognizedAt" is null order by - "asset"."createdAt" desc + "asset"."fileCreatedAt" desc -- AssetJobRepository.streamForMigrationJob select diff --git a/server/src/repositories/asset-job.repository.ts b/server/src/repositories/asset-job.repository.ts index 0500bb867f..f7715b027c 100644 --- a/server/src/repositories/asset-job.repository.ts +++ b/server/src/repositories/asset-job.repository.ts @@ -334,9 +334,9 @@ export class AssetJobRepository { @GenerateSql({ params: [], stream: true }) streamForDetectFacesJob(force?: boolean) { return this.assetsWithPreviews() - .$if(!force, (qb) => qb.where('job_status.facesRecognizedAt', 'is', null)) + .$if(force === false, (qb) => qb.where('job_status.facesRecognizedAt', 'is', null)) .select(['asset.id']) - .orderBy('asset.createdAt', 'desc') + .orderBy('asset.fileCreatedAt', 'desc') .stream(); } From 147accd9579b6d7800a62cdbe5824a1ded1da85b Mon Sep 17 00:00:00 2001 From: Min Idzelis Date: Thu, 28 Aug 2025 22:07:29 -0400 Subject: [PATCH 360/748] fix: fix docker perms for dev (#21359) --- .../server/container-compose-overrides.yml | 2 +- .github/workflows/test.yml | 3 +- Makefile | 38 ++++++++++++++----- docker/docker-compose.dev.yml | 2 +- misc/release/pump-version.sh | 2 +- 5 files changed, 33 insertions(+), 14 deletions(-) diff --git a/.devcontainer/server/container-compose-overrides.yml b/.devcontainer/server/container-compose-overrides.yml index 539caa0dd1..abf34ad68c 100644 --- a/.devcontainer/server/container-compose-overrides.yml +++ b/.devcontainer/server/container-compose-overrides.yml @@ -26,7 +26,7 @@ services: env_file: !reset [] init: env_file: !reset [] - command: sh -c 'for path in /data /data/upload /usr/src/app/.pnpm-store /usr/src/app/server/node_modules /usr/src/app/server/dist /usr/src/app/.github/node_modules /usr/src/app/cli/node_modules /usr/src/app/docs/node_modules /usr/src/app/e2e/node_modules /usr/src/app/open-api/typescript-sdk/node_modules /usr/src/app/web/.svelte-kit /usr/src/app/web/coverage /usr/src/app/node_modules /usr/src/app/web/node_modules; do [ -e "$$path" ] && chown -R ${UID:-1000}:${GID:-1000} "$$path" || true; done' + command: sh -c 'find /data -maxdepth 1 ! -path "/data/postgres" -type d -exec chown ${UID:-1000}:${GID:-1000} {} + 2>/dev/null || true; for path in /usr/src/app/.pnpm-store /usr/src/app/server/node_modules /usr/src/app/server/dist /usr/src/app/.github/node_modules /usr/src/app/cli/node_modules /usr/src/app/docs/node_modules /usr/src/app/e2e/node_modules /usr/src/app/open-api/typescript-sdk/node_modules /usr/src/app/web/.svelte-kit /usr/src/app/web/coverage /usr/src/app/node_modules /usr/src/app/web/node_modules; do [ -e "$$path" ] && chown -R ${UID:-1000}:${GID:-1000} "$$path" || true; done' immich-machine-learning: env_file: !reset [] database: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 39b4b12b1a..e3d2c9b0dc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -569,7 +569,8 @@ jobs: - name: Build the app run: pnpm --filter immich build - name: Run API generation - run: make open-api + run: ./bin/generate-open-api.sh + working-directory: open-api - name: Find file changes uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4 id: verify-changed-files diff --git a/Makefile b/Makefile index 31a00ee6be..13da918683 100644 --- a/Makefile +++ b/Makefile @@ -60,20 +60,37 @@ VOLUME_DIRS = \ ./e2e/node_modules \ ./docs/node_modules \ ./server/node_modules \ - ./server/dist \ ./open-api/typescript-sdk/node_modules \ ./.github/node_modules \ ./node_modules \ ./cli/node_modules -# create empty directories and chown to current user +# Include .env file if it exists +-include docker/.env + +# Helper function to chown, on error suggest remediation and exit +define safe_chown + if chown $(2) $(or $(UID),1000):$(or $(GID),1000) "$(1)" 2>/dev/null; then \ + true; \ + else \ + echo "Permission denied when changing owner of volumes and upload location. Try running 'sudo make prepare-volumes' first."; \ + exit 1; \ + fi; +endef +# create empty directories and chown prepare-volumes: - @for dir in $(VOLUME_DIRS); do \ - mkdir -p $$dir; \ - done - @if [ -n "$(VOLUME_DIRS)" ]; then \ - chown -R $$(id -u):$$(id -g) $(VOLUME_DIRS); \ - fi + @$(foreach dir,$(VOLUME_DIRS),mkdir -p $(dir);) + @$(foreach dir,$(VOLUME_DIRS),$(call safe_chown,$(dir),-R)) +ifneq ($(UPLOAD_LOCATION),) +ifeq ($(filter /%,$(UPLOAD_LOCATION)),) + @mkdir -p "docker/$(UPLOAD_LOCATION)" + @$(call safe_chown,docker/$(UPLOAD_LOCATION),) +else + @mkdir -p "$(UPLOAD_LOCATION)" + @$(call safe_chown,$(UPLOAD_LOCATION),) +endif +endif + MODULES = e2e server web cli sdk docs .github @@ -150,8 +167,9 @@ clean: find . -name ".svelte-kit" -type d -prune -exec rm -rf '{}' + find . -name "coverage" -type d -prune -exec rm -rf '{}' + find . -name ".pnpm-store" -type d -prune -exec rm -rf '{}' + - command -v docker >/dev/null 2>&1 && docker compose -f ./docker/docker-compose.dev.yml rm -v -f || true - command -v docker >/dev/null 2>&1 && docker compose -f ./e2e/docker-compose.yml rm -v -f || true + command -v docker >/dev/null 2>&1 && docker compose -f ./docker/docker-compose.dev.yml down -v --remove-orphans || true + command -v docker >/dev/null 2>&1 && docker compose -f ./e2e/docker-compose.yml down -v --remove-orphans || true + setup-server-dev: install-server setup-web-dev: install-sdk build-sdk install-web diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index 2c003270e4..372352d12a 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -189,7 +189,7 @@ services: env_file: - .env user: 0:0 - command: sh -c 'for path in /usr/src/app/.pnpm-store /usr/src/app/server/node_modules /usr/src/app/server/dist /usr/src/app/.github/node_modules /usr/src/app/cli/node_modules /usr/src/app/docs/node_modules /usr/src/app/e2e/node_modules /usr/src/app/open-api/typescript-sdk/node_modules /usr/src/app/web/.svelte-kit /usr/src/app/web/coverage /usr/src/app/node_modules /usr/src/app/web/node_modules; do [ -e "$$path" ] && chown -R ${UID:-1000}:${GID:-1000} "$$path" || true; done' + command: sh -c 'find /data -maxdepth 1 -type d -exec chown ${UID:-1000}:${GID:-1000} {} + 2>/dev/null || true; for path in /usr/src/app/.pnpm-store /usr/src/app/server/node_modules /usr/src/app/server/dist /usr/src/app/.github/node_modules /usr/src/app/cli/node_modules /usr/src/app/docs/node_modules /usr/src/app/e2e/node_modules /usr/src/app/open-api/typescript-sdk/node_modules /usr/src/app/web/.svelte-kit /usr/src/app/web/coverage /usr/src/app/node_modules /usr/src/app/web/node_modules; do [ -e "$$path" ] && chown -R ${UID:-1000}:${GID:-1000} "$$path" || true; done' volumes: - pnpm-store:/usr/src/app/.pnpm-store - server-node_modules:/usr/src/app/server/node_modules diff --git a/misc/release/pump-version.sh b/misc/release/pump-version.sh index 789805255b..35ce9a1f33 100755 --- a/misc/release/pump-version.sh +++ b/misc/release/pump-version.sh @@ -65,7 +65,7 @@ if [ "$CURRENT_SERVER" != "$NEXT_SERVER" ]; then pnpm install --frozen-lockfile --prefix server pnpm --prefix server run build - make open-api + ( cd ./open-api && bash ./bin/generate-open-api.sh ) jq --arg version "$NEXT_SERVER" '.version = $version' open-api/typescript-sdk/package.json > open-api/typescript-sdk/package.json.tmp && mv open-api/typescript-sdk/package.json.tmp open-api/typescript-sdk/package.json From f5954f4c9b24665920c8b97ff2f812abb7a378e9 Mon Sep 17 00:00:00 2001 From: Sergey Katsubo Date: Fri, 29 Aug 2025 18:24:21 +0300 Subject: [PATCH 361/748] chore(docs): Avoid /data in external library examples (#21357) * Avoid /data for external libraries * Remove mention of microservice containers * Update docs/docs/features/libraries.md Co-authored-by: Matthew Momjian <50788000+mmomjian@users.noreply.github.com> --------- Co-authored-by: Matthew Momjian <50788000+mmomjian@users.noreply.github.com> --- docs/docs/features/libraries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/features/libraries.md b/docs/docs/features/libraries.md index f274ca3c70..e68bcdc272 100644 --- a/docs/docs/features/libraries.md +++ b/docs/docs/features/libraries.md @@ -33,7 +33,7 @@ Sometimes, an external library will not scan correctly. This can happen if Immic - Are the permissions set correctly? - Make sure you are using forward slashes (`/`) and not backward slashes. -To validate that Immich can reach your external library, start a shell inside the container. Run `docker exec -it immich_server bash` to a bash shell. If your import path is `/data/import/photos`, check it with `ls /data/import/photos`. Do the same check for the same in any microservices containers. +To validate that Immich can reach your external library, start a shell inside the container. Run `docker exec -it immich_server bash` to a bash shell. If your import path is `/mnt/photos`, check it with `ls /mnt/photos`. If you are using a dedicated microservices container, make sure to add the same mount point and check for availability within the microservices container as well. ### Exclusion Patterns From f75c9dfe374c0a34ead94c12f08db75553032969 Mon Sep 17 00:00:00 2001 From: Aaron Liu Date: Fri, 29 Aug 2025 16:54:42 -0400 Subject: [PATCH 362/748] fix(devcontainer): logging typo (#21415) --- .devcontainer/server/container-start-backend.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/server/container-start-backend.sh b/.devcontainer/server/container-start-backend.sh index d0176a7d66..35fa60f89b 100755 --- a/.devcontainer/server/container-start-backend.sh +++ b/.devcontainer/server/container-start-backend.sh @@ -11,7 +11,7 @@ run_cmd pnpm --filter immich install log "Starting Nest API Server" log "" cd "${IMMICH_WORKSPACE}/server" || ( - log "Immich workspace not found"jj + log "Immich workspace not found" exit 1 ) From 303307e1ac9949e1a4c56bb1cf27e4969d46135b Mon Sep 17 00:00:00 2001 From: Mert <101130780+mertalev@users.noreply.github.com> Date: Fri, 29 Aug 2025 20:33:58 -0400 Subject: [PATCH 363/748] fix(mobile): memory lane query (#21422) --- mobile/lib/infrastructure/repositories/memory.repository.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/lib/infrastructure/repositories/memory.repository.dart b/mobile/lib/infrastructure/repositories/memory.repository.dart index b5bed18ad5..0dcf7200cc 100644 --- a/mobile/lib/infrastructure/repositories/memory.repository.dart +++ b/mobile/lib/infrastructure/repositories/memory.repository.dart @@ -15,8 +15,8 @@ class DriftMemoryRepository extends DriftDatabaseRepository { final query = _db.select(_db.memoryEntity).join([ - leftOuterJoin(_db.memoryAssetEntity, _db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id)), - leftOuterJoin( + innerJoin(_db.memoryAssetEntity, _db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id)), + innerJoin( _db.remoteAssetEntity, _db.remoteAssetEntity.id.equalsExp(_db.memoryAssetEntity.assetId) & _db.remoteAssetEntity.deletedAt.isNull() & From b3372064e0620e215a4ad98f6fa045419fa654bb Mon Sep 17 00:00:00 2001 From: Brandon Wees Date: Sat, 30 Aug 2025 13:33:11 -0500 Subject: [PATCH 364/748] fix: default zoom level when location is not set (#21428) --- web/src/lib/components/shared-components/change-location.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/lib/components/shared-components/change-location.svelte b/web/src/lib/components/shared-components/change-location.svelte index 831fae02c2..2dfab6b631 100644 --- a/web/src/lib/components/shared-components/change-location.svelte +++ b/web/src/lib/components/shared-components/change-location.svelte @@ -44,7 +44,7 @@ let mapLat = $derived(assetLat ?? previousLocation?.lat ?? undefined); let mapLng = $derived(assetLng ?? previousLocation?.lng ?? undefined); - let zoom = $derived(mapLat !== undefined && mapLng !== undefined ? 12.5 : 1); + let zoom = $derived(mapLat && mapLng ? 12.5 : 1); $effect(() => { if (mapElement && initialPoint) { From 225af973c1d651788c80bed7afcf0e13e885aee5 Mon Sep 17 00:00:00 2001 From: Snowknight26 Date: Sat, 30 Aug 2025 13:39:25 -0500 Subject: [PATCH 365/748] fix(web): Prevent changing asset location triggering keyboard shortcuts (#21451) fix(web): Prevent changing asset location triggering asset keyboard shortcuts --- .../__test__/number-range-input.spec.ts | 28 ++++++++++++++++++- .../coordinates-input.svelte | 8 ++++-- .../number-range-input.svelte | 5 +++- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/web/src/lib/components/shared-components/__test__/number-range-input.spec.ts b/web/src/lib/components/shared-components/__test__/number-range-input.spec.ts index be09d2a35c..dc325bd52c 100644 --- a/web/src/lib/components/shared-components/__test__/number-range-input.spec.ts +++ b/web/src/lib/components/shared-components/__test__/number-range-input.spec.ts @@ -1,18 +1,24 @@ import NumberRangeInput from '$lib/components/shared-components/number-range-input.svelte'; import { render, type RenderResult } from '@testing-library/svelte'; import userEvent from '@testing-library/user-event'; +import type { Mock } from 'vitest'; describe('NumberRangeInput component', () => { const user = userEvent.setup(); let sut: RenderResult; let input: HTMLInputElement; + let onInput: Mock; + let onKeyDown: Mock; beforeEach(() => { + onInput = vi.fn(); + onKeyDown = vi.fn(); sut = render(NumberRangeInput, { id: '', min: -90, max: 90, - onInput: () => {}, + onInput, + onKeyDown, }); input = sut.getByRole('spinbutton') as HTMLInputElement; }); @@ -21,35 +27,55 @@ describe('NumberRangeInput component', () => { expect(input.value).toBe(''); await sut.rerender({ value: 10 }); expect(input.value).toBe('10'); + expect(onInput).not.toHaveBeenCalled(); + expect(onKeyDown).not.toHaveBeenCalled(); }); it('restricts minimum value', async () => { await user.type(input, '-91'); expect(input.value).toBe('-90'); + expect(onInput).toHaveBeenCalled(); + expect(onKeyDown).toHaveBeenCalled(); }); it('restricts maximum value', async () => { await user.type(input, '09990'); expect(input.value).toBe('90'); + expect(onInput).toHaveBeenCalled(); + expect(onKeyDown).toHaveBeenCalled(); }); it('allows entering negative numbers', async () => { await user.type(input, '-10'); expect(input.value).toBe('-10'); + expect(onInput).toHaveBeenCalled(); + expect(onKeyDown).toHaveBeenCalled(); }); it('allows entering zero', async () => { await user.type(input, '0'); expect(input.value).toBe('0'); + expect(onInput).toHaveBeenCalled(); + expect(onKeyDown).toHaveBeenCalled(); }); it('allows entering decimal numbers', async () => { await user.type(input, '-0.09001'); expect(input.value).toBe('-0.09001'); + expect(onInput).toHaveBeenCalled(); + expect(onKeyDown).toHaveBeenCalled(); }); it('ignores text input', async () => { await user.type(input, 'test'); expect(input.value).toBe(''); + expect(onInput).toHaveBeenCalled(); + expect(onKeyDown).toHaveBeenCalled(); + }); + + it('test', async () => { + await user.type(input, 'd'); + expect(onInput).not.toHaveBeenCalled(); + expect(onKeyDown).toHaveBeenCalled(); }); }); diff --git a/web/src/lib/components/shared-components/coordinates-input.svelte b/web/src/lib/components/shared-components/coordinates-input.svelte index 9e71197dd5..9b35d1a485 100644 --- a/web/src/lib/components/shared-components/coordinates-input.svelte +++ b/web/src/lib/components/shared-components/coordinates-input.svelte @@ -20,6 +20,10 @@ } }; + const onKeyDown = (event: KeyboardEvent) => { + event.stopPropagation(); + }; + const onPaste = (event: ClipboardEvent) => { const pastedText = event.clipboardData?.getData('text/plain'); if (!pastedText) { @@ -42,10 +46,10 @@
    - +
    - +
    diff --git a/web/src/lib/components/shared-components/number-range-input.svelte b/web/src/lib/components/shared-components/number-range-input.svelte index 95a9a12a98..13b3d18cc0 100644 --- a/web/src/lib/components/shared-components/number-range-input.svelte +++ b/web/src/lib/components/shared-components/number-range-input.svelte @@ -1,6 +1,6 @@ - -
    -
    -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - -
    -
    -
    diff --git a/web/src/lib/components/utilities-page/geolocation/geolocation.svelte b/web/src/lib/components/utilities-page/geolocation/geolocation.svelte deleted file mode 100644 index 0efde6df7e..0000000000 --- a/web/src/lib/components/utilities-page/geolocation/geolocation.svelte +++ /dev/null @@ -1,104 +0,0 @@ - - -
    -
    - { - if (asset.exifInfo?.latitude && asset.exifInfo?.longitude) { - onLocation({ latitude: asset.exifInfo?.latitude, longitude: asset.exifInfo?.longitude }); - } else { - onSelectAsset(asset); - } - }} - onSelect={() => onSelectAsset(asset)} - onMouseEvent={() => onMouseEvent(asset)} - selected={assetInteraction.hasSelectedAsset(asset.id)} - selectionCandidate={assetInteraction.hasSelectionCandidate(asset.id)} - thumbnailSize={boxWidth} - readonly={hasGps} - /> - - {#if hasGps} -
    - {$t('gps')} -
    - {:else} -
    - {$t('gps_missing')} -
    - {/if} -
    - -
    -

    - {new Date(asset.localDateTime).toLocaleDateString(undefined, { - year: 'numeric', - month: 'short', - day: 'numeric', - })} -

    -

    - {new Date(asset.localDateTime).toLocaleTimeString(undefined, { - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - timeZone: 'UTC', - })} -

    - {#if hasGps} -

    - {asset.exifInfo?.country} -

    -

    - {asset.exifInfo?.city} -

    - {/if} -
    -
    diff --git a/web/src/lib/managers/timeline-manager/month-group.svelte.ts b/web/src/lib/managers/timeline-manager/month-group.svelte.ts index 03d138f680..e406972900 100644 --- a/web/src/lib/managers/timeline-manager/month-group.svelte.ts +++ b/web/src/lib/managers/timeline-manager/month-group.svelte.ts @@ -187,6 +187,11 @@ export class MonthGroup { thumbhash: bucketAssets.thumbhash[i], people: null, // People are not included in the bucket assets }; + + if (bucketAssets.latitude?.[i] && bucketAssets.longitude?.[i]) { + timelineAsset.latitude = bucketAssets.latitude?.[i]; + timelineAsset.longitude = bucketAssets.longitude?.[i]; + } this.addTimelineAsset(timelineAsset, addContext); } diff --git a/web/src/lib/managers/timeline-manager/types.ts b/web/src/lib/managers/timeline-manager/types.ts index 18ee0426f3..fea62084b2 100644 --- a/web/src/lib/managers/timeline-manager/types.ts +++ b/web/src/lib/managers/timeline-manager/types.ts @@ -31,6 +31,8 @@ export type TimelineAsset = { city: string | null; country: string | null; people: string[] | null; + latitude?: number | null; + longitude?: number | null; }; export type AssetOperation = (asset: TimelineAsset) => { remove: boolean }; diff --git a/web/src/lib/utils/date-time.spec.ts b/web/src/lib/utils/date-time.spec.ts index bca57863a9..d96bef45d6 100644 --- a/web/src/lib/utils/date-time.spec.ts +++ b/web/src/lib/utils/date-time.spec.ts @@ -1,5 +1,5 @@ import { writable } from 'svelte/store'; -import { buildDateRangeFromYearMonthAndDay, getAlbumDateRange, timeToSeconds } from './date-time'; +import { getAlbumDateRange, timeToSeconds } from './date-time'; describe('converting time to seconds', () => { it('parses hh:mm:ss correctly', () => { @@ -75,24 +75,3 @@ describe('getAlbumDate', () => { expect(getAlbumDateRange({ startDate: '2021-01-01T00:00:00+05:00' })).toEqual('Jan 1, 2021'); }); }); - -describe('buildDateRangeFromYearMonthAndDay', () => { - it('should build correct date range for a specific day', () => { - const result = buildDateRangeFromYearMonthAndDay(2023, 1, 8); - - expect(result.from).toContain('2023-01-08T00:00:00'); - expect(result.to).toContain('2023-01-09T00:00:00'); - }); - - it('should build correct date range for a month', () => { - const result = buildDateRangeFromYearMonthAndDay(2023, 2); - expect(result.from).toContain('2023-02-01T00:00:00'); - expect(result.to).toContain('2023-03-01T00:00:00'); - }); - - it('should build correct date range for a year', () => { - const result = buildDateRangeFromYearMonthAndDay(2023); - expect(result.from).toContain('2023-01-01T00:00:00'); - expect(result.to).toContain('2024-01-01T00:00:00'); - }); -}); diff --git a/web/src/lib/utils/date-time.ts b/web/src/lib/utils/date-time.ts index bf87d041cc..8a50df9cfe 100644 --- a/web/src/lib/utils/date-time.ts +++ b/web/src/lib/utils/date-time.ts @@ -85,33 +85,3 @@ export const getAlbumDateRange = (album: { startDate?: string; endDate?: string */ export const asLocalTimeISO = (date: DateTime) => (date.setZone('utc', { keepLocalTime: true }) as DateTime).toISO(); - -/** - * Creates a date range for filtering assets based on year, month, and day parameters - */ -export const buildDateRangeFromYearMonthAndDay = (year: number, month?: number, day?: number) => { - const baseDate = DateTime.fromObject({ - year, - month: month || 1, - day: day || 1, - }); - - let from: DateTime; - let to: DateTime; - - if (day) { - from = baseDate.startOf('day'); - to = baseDate.plus({ days: 1 }).startOf('day'); - } else if (month) { - from = baseDate.startOf('month'); - to = baseDate.plus({ months: 1 }).startOf('month'); - } else { - from = baseDate.startOf('year'); - to = baseDate.plus({ years: 1 }).startOf('year'); - } - - return { - from: from.toISO() || undefined, - to: to.toISO() || undefined, - }; -}; diff --git a/web/src/lib/utils/timeline-util.ts b/web/src/lib/utils/timeline-util.ts index a1147b708f..6a0f12c20e 100644 --- a/web/src/lib/utils/timeline-util.ts +++ b/web/src/lib/utils/timeline-util.ts @@ -190,6 +190,8 @@ export const toTimelineAsset = (unknownAsset: AssetResponseDto | TimelineAsset): city: city || null, country: country || null, people, + latitude: assetResponse.exifInfo?.latitude || null, + longitude: assetResponse.exifInfo?.longitude || null, }; }; diff --git a/web/src/routes/(user)/utilities/geolocation/+page.svelte b/web/src/routes/(user)/utilities/geolocation/+page.svelte index 48e94d750f..ab0a59f3ef 100644 --- a/web/src/routes/(user)/utilities/geolocation/+page.svelte +++ b/web/src/routes/(user)/utilities/geolocation/+page.svelte @@ -1,26 +1,21 @@ @@ -224,9 +139,7 @@ {#snippet buttons()}
    - {#if filteredAssets.length > 0} - - {/if} +
    -
    - {#if isLoading}
    {/if} - {#if filteredAssets && filteredAssets.length > 0} -
    - {#each filteredAssets as asset (asset.id)} - handleSelectAssets(asset)} - onMouseEvent={(asset) => assetMouseEventHandler(asset)} - onLocation={(selected) => { - location = selected; - locationUpdated = true; - setTimeout(() => { - locationUpdated = false; - }, 1000); - }} - /> - {/each} -
    - {:else} -
    - {#if partialDate == null} - - {:else if showOnlyAssetsWithoutLocation && filteredAssets.length === 0 && assets.length > 0} - + + {#snippet customLayout(asset: TimelineAsset)} + {#if hasGps(asset)} +
    + {asset.city || $t('gps')} +
    {:else} - +
    + {$t('gps_missing')} +
    {/if} -
    - {/if} + {/snippet} + {#snippet empty()} + {}} /> + {/snippet} +
    diff --git a/web/src/routes/(user)/utilities/geolocation/+page.ts b/web/src/routes/(user)/utilities/geolocation/+page.ts index f5c227a7ef..1ada22a237 100644 --- a/web/src/routes/(user)/utilities/geolocation/+page.ts +++ b/web/src/routes/(user)/utilities/geolocation/+page.ts @@ -1,15 +1,12 @@ import { authenticate } from '$lib/utils/auth'; import { getFormatter } from '$lib/utils/i18n'; -import { getQueryValue } from '$lib/utils/navigation'; import type { PageLoad } from './$types'; export const load = (async ({ url }) => { await authenticate(url); - const partialDate = getQueryValue('date'); const $t = await getFormatter(); return { - partialDate, meta: { title: $t('manage_geolocation'), }, diff --git a/web/src/routes/(user)/utilities/geolocation/photos/[photoId]/+page.ts b/web/src/routes/(user)/utilities/geolocation/photos/[photoId]/+page.ts new file mode 100644 index 0000000000..17fd84097f --- /dev/null +++ b/web/src/routes/(user)/utilities/geolocation/photos/[photoId]/+page.ts @@ -0,0 +1,8 @@ +import { AppRoute } from '$lib/constants'; +import { redirect } from '@sveltejs/kit'; +import type { PageLoad } from './$types'; + +export const load = (({ params }) => { + const photoId = params.photoId; + return redirect(302, `${AppRoute.PHOTOS}/${photoId}`); +}) satisfies PageLoad; From 2801b0953d143d430605f263fe5f623a214683af Mon Sep 17 00:00:00 2001 From: per-review Date: Wed, 10 Sep 2025 03:39:15 +0200 Subject: [PATCH 439/748] docs: be explicit about which container exposes metrics (#20424) * Be explicit about which container exposes metrics * Update docs/docs/features/monitoring.md Co-authored-by: bo0tzz --------- Co-authored-by: Jason Rasmussen Co-authored-by: bo0tzz --- docs/docs/features/monitoring.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/features/monitoring.md b/docs/docs/features/monitoring.md index 64377ec073..c80f66902b 100644 --- a/docs/docs/features/monitoring.md +++ b/docs/docs/features/monitoring.md @@ -66,7 +66,7 @@ The provided file is just a starting point. There are a ton of ways to configure After bringing down the containers with `docker compose down` and back up with `docker compose up -d`, a Prometheus instance will now collect metrics from the immich server and microservices containers. Note that we didn't need to expose any new ports for these containers - the communication is handled in the internal Docker network. :::note -To see exactly what metrics are made available, you can additionally add `8081:8081` to the server container's ports and `8082:8082` to the microservices container's ports. +To see exactly what metrics are made available, you can additionally add `8081:8081` (API metrics) and `8082:8082` (microservices metrics) to the immich_server container's ports. Visiting the `/metrics` endpoint for these services will show the same raw data that Prometheus collects. To configure these ports see [`IMMICH_API_METRICS_PORT` & `IMMICH_MICROSERVICES_METRICS_PORT`](/docs/install/environment-variables/#general). ::: From b97d73d7a7f7cd37b12ffcaf0acfd6192959ce71 Mon Sep 17 00:00:00 2001 From: bo0tzz Date: Wed, 10 Sep 2025 03:46:59 +0200 Subject: [PATCH 440/748] fix: handle missing checkbox in close-dupes (#21689) --- .github/workflows/close-duplicates.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/close-duplicates.yml b/.github/workflows/close-duplicates.yml index 3237a84765..d9ac98aa2d 100644 --- a/.github/workflows/close-duplicates.yml +++ b/.github/workflows/close-duplicates.yml @@ -37,20 +37,19 @@ jobs: container: image: yshavit/mdq:0.9.0@sha256:4399483ca857fb1a7ed28a596f754c7373e358647de31ce14b79a27c91e1e35e outputs: - json: ${{ steps.get_checkbox.outputs.json }} + checked: ${{ steps.get_checkbox.outputs.checked }} steps: - id: get_checkbox env: BODY: ${{ needs.get_body.outputs.body }} - # TODO: We should detect if the checkbox is missing entirely and also close_and_comment in that case. run: | - JSON=$(echo "$BODY" | base64 -d | /mdq --output json '# I have searched | - [?] Yes') - echo "json=$JSON" >> $GITHUB_OUTPUT + CHECKED=$(echo "$BODY" | base64 -d | /mdq --output json '# I have searched | - [?] Yes' | jq '.items[0].list[0].checked // false') + echo "checked=$CHECKED" >> $GITHUB_OUTPUT close_and_comment: runs-on: ubuntu-latest needs: [get_checkbox_json, should_run] - if: ${{ needs.should_run.outputs.should_run == 'true' && !fromJSON(needs.get_checkbox_json.outputs.json).items[0].list[0].checked }} + if: ${{ needs.should_run.outputs.should_run == 'true' && needs.get_checkbox_json.outputs.checked != 'true' }} permissions: issues: write discussions: write From 4c918254b954f4a9dd02c0fdac6d65aaa037ae1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Ko=C5=88a=C5=99=C3=ADk?= Date: Wed, 10 Sep 2025 04:02:23 +0200 Subject: [PATCH 441/748] fix: use relative path in start.sh (#20434) Dehardcode path to Immich in start.sh --- server/bin/start.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/bin/start.sh b/server/bin/start.sh index 10f897dd8e..15390ae158 100755 --- a/server/bin/start.sh +++ b/server/bin/start.sh @@ -8,7 +8,7 @@ else echo "skipping libmimalloc - path not found $lib_path" fi export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/lib/jellyfin-ffmpeg/lib" -SERVER_HOME=/usr/src/app/server +SERVER_HOME="$(readlink -f "$(dirname "$0")/..")" read_file_and_export() { fname="${!1}" From e95096d14f826c5615f0f0583c2510085984f711 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Tue, 9 Sep 2025 22:58:46 -0400 Subject: [PATCH 442/748] fix: prefer Creation Date over Create Date (#21756) --- e2e/src/api/specs/asset.e2e-spec.ts | 6 +++--- server/src/services/metadata.service.spec.ts | 11 +++++++++++ server/src/services/metadata.service.ts | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/e2e/src/api/specs/asset.e2e-spec.ts b/e2e/src/api/specs/asset.e2e-spec.ts index 9c8b893075..5c30ff5cbe 100644 --- a/e2e/src/api/specs/asset.e2e-spec.ts +++ b/e2e/src/api/specs/asset.e2e-spec.ts @@ -1466,10 +1466,10 @@ describe('/asset', () => { expectedDate: '2023-04-04T04:00:00.000Z', }, { - name: 'CreateDate when DateTimeOriginal missing', + name: 'CreationDate when DateTimeOriginal missing', exifData: { - CreateDate: '2023:05:05 05:00:00', // TESTABLE - CreationDate: '2023:07:07 07:00:00', // TESTABLE + CreationDate: '2023:05:05 05:00:00', // TESTABLE + CreateDate: '2023:07:07 07:00:00', // TESTABLE GPSDateTime: '2023:10:10 10:00:00', // TESTABLE }, expectedDate: '2023-05-05T05:00:00.000Z', diff --git a/server/src/services/metadata.service.spec.ts b/server/src/services/metadata.service.spec.ts index 413b20a954..0adb390f6a 100644 --- a/server/src/services/metadata.service.spec.ts +++ b/server/src/services/metadata.service.spec.ts @@ -1660,5 +1660,16 @@ describe(MetadataService.name, () => { expect(result?.tag).toBe('GPSDateTime'); expect(result?.dateTime?.toDate()?.toISOString()).toBe('2023-10-10T10:00:00.000Z'); }); + + it('should prefer CreationDate over CreateDate', () => { + const tags = { + CreationDate: '2025:05:24 18:26:20+02:00', + CreateDate: '2025:08:27 08:45:40', + }; + + const result = firstDateTime(tags); + expect(result?.tag).toBe('CreationDate'); + expect(result?.dateTime?.toDate()?.toISOString()).toBe('2025-05-24T16:26:20.000Z'); + }); }); }); diff --git a/server/src/services/metadata.service.ts b/server/src/services/metadata.service.ts index 94ccd41ff5..7d3de76550 100644 --- a/server/src/services/metadata.service.ts +++ b/server/src/services/metadata.service.ts @@ -39,9 +39,9 @@ const EXIF_DATE_TAGS: Array = [ 'SubSecCreateDate', 'SubSecMediaCreateDate', 'DateTimeOriginal', + 'CreationDate', 'CreateDate', 'MediaCreateDate', - 'CreationDate', 'DateTimeCreated', 'GPSDateTime', 'DateTimeUTC', From 00c88b26368131c801359e13b44ecf51bde07d2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C4=81rti=C5=86=C5=A1=20Bru=C5=86enieks?= Date: Wed, 10 Sep 2025 15:07:58 +0300 Subject: [PATCH 443/748] docs: add community immich drop uploader project (#21775) --- docs/src/components/community-projects.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/src/components/community-projects.tsx b/docs/src/components/community-projects.tsx index 46e28b3b76..930cff66c1 100644 --- a/docs/src/components/community-projects.tsx +++ b/docs/src/components/community-projects.tsx @@ -105,6 +105,11 @@ const projects: CommunityProjectProps[] = [ description: 'Speed up your machine learning by load balancing your requests to multiple computers', url: 'https://github.com/apetersson/immich_ml_balancer', }, + { + title: 'Immich Drop Uploader', + description: 'A tiny, zero-login web app for collecting photos/videos from anyone into your Immich server.', + url: 'https://github.com/Nasogaa/immich-drop', + }, ]; function CommunityProject({ title, description, url }: CommunityProjectProps): JSX.Element { From e52cc259d568f2d4c2667b7f5299de4d6f1dfb71 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 10 Sep 2025 08:15:03 -0400 Subject: [PATCH 444/748] fix(web): cancel uploads on logout (#21760) --- web/src/lib/managers/upload-manager.svelte.ts | 7 ++++++- web/src/lib/utils/file-uploader.ts | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/web/src/lib/managers/upload-manager.svelte.ts b/web/src/lib/managers/upload-manager.svelte.ts index 0ff2b0c214..61c6d73b53 100644 --- a/web/src/lib/managers/upload-manager.svelte.ts +++ b/web/src/lib/managers/upload-manager.svelte.ts @@ -1,11 +1,16 @@ import { eventManager } from '$lib/managers/event-manager.svelte'; +import { uploadAssetsStore } from '$lib/stores/upload'; import { getSupportedMediaTypes, type ServerMediaTypesResponseDto } from '@immich/sdk'; class UploadManager { mediaTypes = $state({ image: [], sidecar: [], video: [] }); constructor() { - eventManager.on('app.init', () => void this.#loadExtensions()); + eventManager.on('app.init', () => void this.#loadExtensions()).on('auth.logout', () => void this.reset()); + } + + reset() { + uploadAssetsStore.reset(); } async #loadExtensions() { diff --git a/web/src/lib/utils/file-uploader.ts b/web/src/lib/utils/file-uploader.ts index 5f519f9d8e..c572ec1760 100644 --- a/web/src/lib/utils/file-uploader.ts +++ b/web/src/lib/utils/file-uploader.ts @@ -2,6 +2,7 @@ import { authManager } from '$lib/managers/auth-manager.svelte'; import { uploadManager } from '$lib/managers/upload-manager.svelte'; import { UploadState } from '$lib/models/upload-asset'; import { uploadAssetsStore } from '$lib/stores/upload'; +import { user } from '$lib/stores/user.store'; import { uploadRequest } from '$lib/utils'; import { addAssetsToAlbum } from '$lib/utils/asset-utils'; import { ExecutorQueue } from '$lib/utils/executor-queue'; @@ -231,6 +232,11 @@ async function fileUploader({ return responseData.id; } catch (error) { + // ignore errors if the user logs out during uploads + if (!get(user)) { + return; + } + const errorMessage = handleError(error, $t('errors.unable_to_upload_file')); uploadAssetsStore.track('error'); uploadAssetsStore.updateItem(deviceAssetId, { state: UploadState.ERROR, error: errorMessage }); From 8e5d52abbb1565f862fdc119e58ef1128d434e71 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 10 Sep 2025 08:15:57 -0400 Subject: [PATCH 445/748] fix(web): transparent background color (#21747) --- web/src/lib/components/assets/thumbnail/thumbnail.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/lib/components/assets/thumbnail/thumbnail.svelte b/web/src/lib/components/assets/thumbnail/thumbnail.svelte index e01f2dc4f6..dd7f30b981 100644 --- a/web/src/lib/components/assets/thumbnail/thumbnail.svelte +++ b/web/src/lib/components/assets/thumbnail/thumbnail.svelte @@ -197,7 +197,7 @@
    Date: Wed, 10 Sep 2025 08:17:21 -0400 Subject: [PATCH 446/748] chore: remove typeorm dependency (#21754) --- pnpm-lock.yaml | 139 -------------------------------------------- server/package.json | 1 - 2 files changed, 140 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 70d339f300..cc4bedad91 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -520,9 +520,6 @@ importers: thumbhash: specifier: ^0.1.1 version: 0.1.1 - typeorm: - specifier: ^0.3.17 - version: 0.3.25(ioredis@5.7.0)(pg@8.16.3)(reflect-metadata@0.2.2) ua-parser-js: specifier: ^2.0.0 version: 2.0.4(encoding@0.1.13) @@ -3685,9 +3682,6 @@ packages: peerDependencies: socket.io-adapter: ^2.5.4 - '@sqltools/formatter@1.2.5': - resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==} - '@standard-schema/spec@1.0.0': resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} @@ -4715,10 +4709,6 @@ packages: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} - ansis@3.17.0: - resolution: {integrity: sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==} - engines: {node: '>=14'} - ansis@4.1.0: resolution: {integrity: sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w==} engines: {node: '>=14'} @@ -4730,10 +4720,6 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} - app-root-path@3.1.0: - resolution: {integrity: sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==} - engines: {node: '>= 6.0.0'} - append-field@1.0.0: resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} @@ -5681,9 +5667,6 @@ packages: resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} engines: {node: '>=18'} - dayjs@1.11.13: - resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} - debounce@1.2.1: resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==} @@ -5735,14 +5718,6 @@ packages: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} - dedent@1.6.0: - resolution: {integrity: sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==} - peerDependencies: - babel-plugin-macros: ^3.1.0 - peerDependenciesMeta: - babel-plugin-macros: - optional: true - deep-eql@5.0.2: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} @@ -5958,10 +5933,6 @@ packages: resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} engines: {node: '>=10'} - dotenv@16.6.1: - resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} - engines: {node: '>=12'} - dotenv@17.2.1: resolution: {integrity: sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==} engines: {node: '>=12'} @@ -9933,10 +9904,6 @@ packages: setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - sha.js@2.4.11: - resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} - hasBin: true - shallow-clone@3.0.1: resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} engines: {node: '>=8'} @@ -10132,10 +10099,6 @@ packages: resolution: {integrity: sha512-bZydXEXhaNDQBr8xYHC3a8thwcaMuTBp0CkKGjwGYDsIB26tnlWeWPwJtSQ0TEwiJcz9iJJON5mFPkx7XroHcg==} hasBin: true - sql-highlight@6.1.0: - resolution: {integrity: sha512-ed7OK4e9ywpE7pgRMkMQmZDPKSVdm0oX5IEtZiKnFucSF0zu6c80GZBe38UqHuVhTWJ9xsKgSMjCG2bml86KvA==} - engines: {node: '>=14'} - srcset@4.0.0: resolution: {integrity: sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==} engines: {node: '>=12'} @@ -10706,65 +10669,6 @@ packages: typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - typeorm@0.3.25: - resolution: {integrity: sha512-fTKDFzWXKwAaBdEMU4k661seZewbNYET4r1J/z3Jwf+eAvlzMVpTLKAVcAzg75WwQk7GDmtsmkZ5MfkmXCiFWg==} - engines: {node: '>=16.13.0'} - hasBin: true - peerDependencies: - '@google-cloud/spanner': ^5.18.0 || ^6.0.0 || ^7.0.0 - '@sap/hana-client': ^2.12.25 - better-sqlite3: ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 - hdb-pool: ^0.1.6 - ioredis: ^5.0.4 - mongodb: ^5.8.0 || ^6.0.0 - mssql: ^9.1.1 || ^10.0.1 || ^11.0.1 - mysql2: ^2.2.5 || ^3.0.1 - oracledb: ^6.3.0 - pg: ^8.5.1 - pg-native: ^3.0.0 - pg-query-stream: ^4.0.0 - redis: ^3.1.1 || ^4.0.0 - reflect-metadata: ^0.1.14 || ^0.2.0 - sql.js: ^1.4.0 - sqlite3: ^5.0.3 - ts-node: ^10.7.0 - typeorm-aurora-data-api-driver: ^2.0.0 || ^3.0.0 - peerDependenciesMeta: - '@google-cloud/spanner': - optional: true - '@sap/hana-client': - optional: true - better-sqlite3: - optional: true - hdb-pool: - optional: true - ioredis: - optional: true - mongodb: - optional: true - mssql: - optional: true - mysql2: - optional: true - oracledb: - optional: true - pg: - optional: true - pg-native: - optional: true - pg-query-stream: - optional: true - redis: - optional: true - sql.js: - optional: true - sqlite3: - optional: true - ts-node: - optional: true - typeorm-aurora-data-api-driver: - optional: true - typescript-eslint@8.39.1: resolution: {integrity: sha512-GDUv6/NDYngUlNvwaHM1RamYftxf782IyEDbdj3SeaIHHv8fNQVRC++fITT7kUJV/5rIA/tkoRSSskt6osEfqg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -14995,8 +14899,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@sqltools/formatter@1.2.5': {} - '@standard-schema/spec@1.0.0': {} '@sveltejs/acorn-typescript@1.0.5(acorn@8.15.0)': @@ -16249,8 +16151,6 @@ snapshots: ansi-styles@6.2.1: {} - ansis@3.17.0: {} - ansis@4.1.0: {} any-promise@1.3.0: {} @@ -16260,8 +16160,6 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 - app-root-path@3.1.0: {} - append-field@1.0.0: {} aproba@2.0.0: {} @@ -17291,8 +17189,6 @@ snapshots: whatwg-url: 14.2.0 optional: true - dayjs@1.11.13: {} - debounce@1.2.1: {} debounce@2.2.0: {} @@ -17326,8 +17222,6 @@ snapshots: dependencies: mimic-response: 3.1.0 - dedent@1.6.0: {} - deep-eql@5.0.2: {} deep-equal@1.0.1: {} @@ -17650,8 +17544,6 @@ snapshots: dependencies: is-obj: 2.0.0 - dotenv@16.6.1: {} - dotenv@17.2.1: {} dunder-proto@1.0.1: @@ -22581,11 +22473,6 @@ snapshots: setprototypeof@1.2.0: {} - sha.js@2.4.11: - dependencies: - inherits: 2.0.4 - safe-buffer: 5.2.1 - shallow-clone@3.0.1: dependencies: kind-of: 6.0.3 @@ -22869,8 +22756,6 @@ snapshots: argparse: 2.0.1 nearley: 2.20.1 - sql-highlight@6.1.0: {} - srcset@4.0.0: {} ssh-remote-port-forward@1.0.4: @@ -23532,30 +23417,6 @@ snapshots: typedarray@0.0.6: {} - typeorm@0.3.25(ioredis@5.7.0)(pg@8.16.3)(reflect-metadata@0.2.2): - dependencies: - '@sqltools/formatter': 1.2.5 - ansis: 3.17.0 - app-root-path: 3.1.0 - buffer: 6.0.3 - dayjs: 1.11.13 - debug: 4.4.1 - dedent: 1.6.0 - dotenv: 16.6.1 - glob: 10.4.5 - reflect-metadata: 0.2.2 - sha.js: 2.4.11 - sql-highlight: 6.1.0 - tslib: 2.8.1 - uuid: 11.1.0 - yargs: 17.7.2 - optionalDependencies: - ioredis: 5.7.0 - pg: 8.16.3 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - typescript-eslint@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2): dependencies: '@typescript-eslint/eslint-plugin': 8.39.1(@typescript-eslint/parser@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) diff --git a/server/package.json b/server/package.json index f8268420f8..c2a0c6c8cd 100644 --- a/server/package.json +++ b/server/package.json @@ -106,7 +106,6 @@ "socket.io": "^4.8.1", "tailwindcss-preset-email": "^1.4.0", "thumbhash": "^0.1.1", - "typeorm": "^0.3.17", "ua-parser-js": "^2.0.0", "uuid": "^11.1.0", "validator": "^13.12.0" From cc08ebdf80fbd77983c6d2ceef0158a322fafdc9 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 10 Sep 2025 08:17:41 -0400 Subject: [PATCH 447/748] fix(web): website frozen after modal closes (#21752) --- pnpm-lock.yaml | 63 +++++++++++++++++++++++++++++++----------------- web/package.json | 2 +- 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cc4bedad91..6de71f29c4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -684,8 +684,8 @@ importers: specifier: file:../open-api/typescript-sdk version: link:../open-api/typescript-sdk '@immich/ui': - specifier: ^0.24.0 - version: 0.24.1(@internationalized/date@3.8.2)(svelte@5.35.5) + specifier: ^0.27.1 + version: 0.27.1(@internationalized/date@3.8.2)(svelte@5.35.5) '@mapbox/mapbox-gl-rtl-text': specifier: 0.2.3 version: 0.2.3(mapbox-gl@1.13.3) @@ -1565,6 +1565,10 @@ packages: resolution: {integrity: sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==} engines: {node: '>=6.9.0'} + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + engines: {node: '>=6.9.0'} + '@babel/template@7.27.2': resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} @@ -2405,8 +2409,8 @@ packages: '@floating-ui/core@1.7.3': resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} - '@floating-ui/dom@1.7.3': - resolution: {integrity: sha512-uZA413QEpNuhtb3/iIKoYMSK07keHPYeXF02Zhd6e213j+d1NamLix/mCLxBUDW/Gx52sPH2m+chlUsyaBs/Ag==} + '@floating-ui/dom@1.7.4': + resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} '@floating-ui/utils@0.2.10': resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} @@ -2589,8 +2593,8 @@ packages: cpu: [x64] os: [win32] - '@immich/ui@0.24.1': - resolution: {integrity: sha512-phJ9BHV0+OnKsxXD+5+Te5Amnb1N4ExYpRGSJPYFqutd5WXeN7kZGKZXd3CfcQ1e31SXRy4DsHSGdM1pY7AUgA==} + '@immich/ui@0.27.1': + resolution: {integrity: sha512-d/LqCpFZwaZ6Vp2wz+DkhMirMle2zL/y4SHyKLmA0QI6pwz+yZaym6DlYkx3ZPKlN10/ugeHi58fXdlMxJiuKA==} peerDependencies: svelte: ^5.0.0 @@ -4921,8 +4925,8 @@ packages: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} - bits-ui@2.9.4: - resolution: {integrity: sha512-Cqn685P6DDuEyBZT/CWMyS5+8JAnYbctvoEVPcmiut+HUpG3SozVgjoDaUib5VG4ZYUKEi1FPwHxiXo9c6J0PA==} + bits-ui@2.9.6: + resolution: {integrity: sha512-OzHktsQRsIz/hIMk5VwHo96Wpp/KY68q/ebUPUzTbvuFBrALB/X+QvO4KLgdczj5dfb3xHs9zpWq8yMH8ZbZlA==} engines: {node: '>=20'} peerDependencies: '@internationalized/date': ^3.8.1 @@ -8363,6 +8367,9 @@ packages: nwsapi@2.2.21: resolution: {integrity: sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA==} + nwsapi@2.2.22: + resolution: {integrity: sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==} + nypm@0.6.0: resolution: {integrity: sha512-mn8wBFV9G9+UFHIrq+pZ2r2zL4aPau/by3kJb3cM7+5tQHMt6HGQB8FDIeKFYp8o0D2pnH6nVsO88N4AmUxIWg==} engines: {node: ^14.16.0 || >=16.10.0} @@ -9986,6 +9993,10 @@ packages: simple-get@3.1.1: resolution: {integrity: sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==} + simple-icons@15.14.0: + resolution: {integrity: sha512-eTBZiiwDFN8RPkcmHKoUz1+sckeqNQXv5ujQcgQddDzp3xuDIFWeZh/i0oEv1StOPsf9NPMC0gTBxUzhPqHzag==} + engines: {node: '>=0.12.18'} + simple-swizzle@0.2.2: resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} @@ -10372,8 +10383,8 @@ packages: tailwind-merge@3.3.1: resolution: {integrity: sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==} - tailwind-variants@2.1.0: - resolution: {integrity: sha512-82m0eRex0z6A3GpvfoTCpHr+wWJmbecfVZfP3mqLoDxeya5tN4mYJQZwa5Aw1hRZTedwpu1D2JizYenoEdyD8w==} + tailwind-variants@3.1.1: + resolution: {integrity: sha512-ftLXe3krnqkMHsuBTEmaVUXYovXtPyTK7ckEfDRXS8PBZx0bAUas+A0jYxuKA5b8qg++wvQ3d2MQ7l/xeZxbZQ==} engines: {node: '>=16.x', pnpm: '>=7.x'} peerDependencies: tailwind-merge: '>=3.0.0' @@ -12228,6 +12239,8 @@ snapshots: '@babel/runtime@7.28.3': {} + '@babel/runtime@7.28.4': {} + '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 @@ -13535,7 +13548,7 @@ snapshots: dependencies: '@floating-ui/utils': 0.2.10 - '@floating-ui/dom@1.7.3': + '@floating-ui/dom@1.7.4': dependencies: '@floating-ui/core': 1.7.3 '@floating-ui/utils': 0.2.10 @@ -13691,13 +13704,14 @@ snapshots: '@img/sharp-win32-x64@0.34.3': optional: true - '@immich/ui@0.24.1(@internationalized/date@3.8.2)(svelte@5.35.5)': + '@immich/ui@0.27.1(@internationalized/date@3.8.2)(svelte@5.35.5)': dependencies: '@mdi/js': 7.4.47 - bits-ui: 2.9.4(@internationalized/date@3.8.2)(svelte@5.35.5) + bits-ui: 2.9.6(@internationalized/date@3.8.2)(svelte@5.35.5) + simple-icons: 15.14.0 svelte: 5.35.5 tailwind-merge: 3.3.1 - tailwind-variants: 2.1.0(tailwind-merge@3.3.1)(tailwindcss@4.1.12) + tailwind-variants: 3.1.1(tailwind-merge@3.3.1)(tailwindcss@4.1.12) tailwindcss: 4.1.12 transitivePeerDependencies: - '@internationalized/date' @@ -14874,7 +14888,7 @@ snapshots: '@slorber/react-helmet-async@1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 invariant: 2.2.4 prop-types: 15.8.1 react: 18.3.1 @@ -16354,10 +16368,10 @@ snapshots: binary-extensions@2.3.0: {} - bits-ui@2.9.4(@internationalized/date@3.8.2)(svelte@5.35.5): + bits-ui@2.9.6(@internationalized/date@3.8.2)(svelte@5.35.5): dependencies: '@floating-ui/core': 1.7.3 - '@floating-ui/dom': 1.7.3 + '@floating-ui/dom': 1.7.4 '@internationalized/date': 3.8.2 esm-env: 1.2.2 runed: 0.29.2(svelte@5.35.5) @@ -19338,7 +19352,7 @@ snapshots: http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.21 + nwsapi: 2.2.22 parse5: 7.3.0 rrweb-cssom: 0.8.0 saxes: 6.0.0 @@ -19368,7 +19382,7 @@ snapshots: http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.21 + nwsapi: 2.2.22 parse5: 7.3.0 rrweb-cssom: 0.8.0 saxes: 6.0.0 @@ -20666,6 +20680,9 @@ snapshots: nwsapi@2.2.21: optional: true + nwsapi@2.2.22: + optional: true + nypm@0.6.0: dependencies: citty: 0.1.6 @@ -21873,7 +21890,7 @@ snapshots: react-redux@7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@types/react-redux': 7.1.34 hoist-non-react-statics: 3.3.2 loose-envify: 1.4.0 @@ -22012,7 +22029,7 @@ snapshots: redux@4.2.1: dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 reflect-metadata@0.2.2: {} @@ -22605,6 +22622,8 @@ snapshots: simple-concat: 1.0.1 optional: true + simple-icons@15.14.0: {} + simple-swizzle@0.2.2: dependencies: is-arrayish: 0.3.2 @@ -23078,7 +23097,7 @@ snapshots: tailwind-merge@3.3.1: {} - tailwind-variants@2.1.0(tailwind-merge@3.3.1)(tailwindcss@4.1.12): + tailwind-variants@3.1.1(tailwind-merge@3.3.1)(tailwindcss@4.1.12): dependencies: tailwindcss: 4.1.12 optionalDependencies: diff --git a/web/package.json b/web/package.json index d8f2a0c3e7..86574fde4f 100644 --- a/web/package.json +++ b/web/package.json @@ -28,7 +28,7 @@ "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", "@immich/sdk": "file:../open-api/typescript-sdk", - "@immich/ui": "^0.24.0", + "@immich/ui": "^0.27.1", "@mapbox/mapbox-gl-rtl-text": "0.2.3", "@mdi/js": "^7.4.47", "@photo-sphere-viewer/core": "^5.11.5", From bee0ae430aff93393659a14e19538bababaa1af6 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 10 Sep 2025 08:17:58 -0400 Subject: [PATCH 448/748] fix(web): map popup accessibility (#21759) --- web/src/app.css | 10 ++++++++++ .../lib/components/asset-viewer/detail-panel.svelte | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/web/src/app.css b/web/src/app.css index db6c43652b..f66743f736 100644 --- a/web/src/app.css +++ b/web/src/app.css @@ -169,3 +169,13 @@ filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.8)); } } + +.maplibregl-popup { + .maplibregl-popup-tip { + @apply border-t-subtle! translate-y-[-1px]; + } + + .maplibregl-popup-content { + @apply bg-subtle rounded-lg; + } +} diff --git a/web/src/lib/components/asset-viewer/detail-panel.svelte b/web/src/lib/components/asset-viewer/detail-panel.svelte index d3f2c842bb..3bcac83914 100644 --- a/web/src/lib/components/asset-viewer/detail-panel.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel.svelte @@ -525,7 +525,7 @@ {$t('open_in_openstreetmap')} From 0090b9db4a50a9cbafe61433c580b02d63b71037 Mon Sep 17 00:00:00 2001 From: Sergey Katsubo Date: Wed, 10 Sep 2025 15:22:55 +0300 Subject: [PATCH 449/748] fix(docs): fix the SQL query for finding assets with missing thumbnails (#21770) Fix the SQL query for missing thumbnails in docs --- docs/docs/guides/database-queries.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/docs/guides/database-queries.md b/docs/docs/guides/database-queries.md index 267e7bf2ad..1a5c2ed193 100644 --- a/docs/docs/guides/database-queries.md +++ b/docs/docs/guides/database-queries.md @@ -147,7 +147,10 @@ SELECT "key", "value" FROM "system_metadata" WHERE "key" = 'system-config'; ### File properties ```sql title="Without thumbnails" -SELECT * FROM "asset" WHERE "asset"."previewPath" IS NULL OR "asset"."thumbnailPath" IS NULL; +SELECT * FROM "asset" +WHERE (NOT EXISTS (SELECT 1 FROM "asset_file" WHERE "asset"."id" = "asset_file"."assetId" AND "asset_file"."type" = 'thumbnail') + OR NOT EXISTS (SELECT 1 FROM "asset_file" WHERE "asset"."id" = "asset_file"."assetId" AND "asset_file"."type" = 'preview')) +AND "asset"."visibility" = 'timeline'; ``` ```sql title="Failed file movements" From 9d3ca3ad3fff302a3cb75b06969f5ec9506448b6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 10 Sep 2025 08:23:33 -0400 Subject: [PATCH 450/748] chore(deps): update dependency vite to v7.1.5 [security] (#21748) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- pnpm-lock.yaml | 345 +++++++++++++++++++++++++------------------------ 1 file changed, 177 insertions(+), 168 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6de71f29c4..2e87eff590 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -109,10 +109,10 @@ importers: version: 8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) vite: specifier: ^7.0.0 - version: 7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + version: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vite-tsconfig-paths: specifier: ^5.0.0 - version: 5.1.4(typescript@5.9.2)(vite@7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 5.1.4(typescript@5.9.2)(vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) vitest: specifier: ^3.0.0 version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.1)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) @@ -667,10 +667,10 @@ importers: version: 8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) unplugin-swc: specifier: ^1.4.5 - version: 1.5.5(@swc/core@1.13.3(@swc/helpers@0.5.17))(rollup@4.46.3) + version: 1.5.5(@swc/core@1.13.3(@swc/helpers@0.5.17))(rollup@4.50.1) vite-tsconfig-paths: specifier: ^5.0.0 - version: 5.1.4(typescript@5.9.2)(vite@7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 5.1.4(typescript@5.9.2)(vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) vitest: specifier: ^3.0.0 version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.1)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2(encoding@0.1.13)))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) @@ -794,25 +794,25 @@ importers: version: 3.1.2 '@sveltejs/adapter-static': specifier: ^3.0.8 - version: 3.0.9(@sveltejs/kit@2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))) + version: 3.0.9(@sveltejs/kit@2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))) '@sveltejs/enhanced-img': specifier: ^0.8.0 - version: 0.8.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(rollup@4.46.3)(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 0.8.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(rollup@4.50.1)(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@sveltejs/kit': specifier: ^2.27.1 - version: 2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@sveltejs/vite-plugin-svelte': specifier: 6.1.2 - version: 6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@tailwindcss/vite': specifier: ^4.1.7 - version: 4.1.12(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 4.1.12(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@testing-library/jest-dom': specifier: ^6.4.2 version: 6.7.0 '@testing-library/svelte': specifier: ^5.2.8 - version: 5.2.8(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 5.2.8(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@testing-library/user-event': specifier: ^14.5.2 version: 14.6.1(@testing-library/dom@10.4.0) @@ -878,7 +878,7 @@ importers: version: 3.4.0(prettier@3.6.2)(svelte@5.35.5) rollup-plugin-visualizer: specifier: ^6.0.0 - version: 6.0.3(rollup@4.46.3) + version: 6.0.3(rollup@4.50.1) svelte: specifier: 5.35.5 version: 5.35.5 @@ -899,7 +899,7 @@ importers: version: 8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) vite: specifier: ^7.1.2 - version: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + version: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vitest: specifier: ^3.0.0 version: 3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) @@ -1561,8 +1561,8 @@ packages: resolution: {integrity: sha512-vDVrlmRAY8z9Ul/HxT+8ceAru95LQgkSKiXkSYZvqtbkPSfhZJgpRp45Cldbh1GJ1kxzQkI70AqyrTI58KpaWQ==} engines: {node: '>=6.9.0'} - '@babel/runtime@7.28.3': - resolution: {integrity: sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==} + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} engines: {node: '>=6.9.0'} '@babel/runtime@7.28.4': @@ -3542,103 +3542,108 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.46.3': - resolution: {integrity: sha512-UmTdvXnLlqQNOCJnyksjPs1G4GqXNGW1LrzCe8+8QoaLhhDeTXYBgJ3k6x61WIhlHX2U+VzEJ55TtIjR/HTySA==} + '@rollup/rollup-android-arm-eabi@4.50.1': + resolution: {integrity: sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.46.3': - resolution: {integrity: sha512-8NoxqLpXm7VyeI0ocidh335D6OKT0UJ6fHdnIxf3+6oOerZZc+O7r+UhvROji6OspyPm+rrIdb1gTXtVIqn+Sg==} + '@rollup/rollup-android-arm64@4.50.1': + resolution: {integrity: sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.46.3': - resolution: {integrity: sha512-csnNavqZVs1+7/hUKtgjMECsNG2cdB8F7XBHP6FfQjqhjF8rzMzb3SLyy/1BG7YSfQ+bG75Ph7DyedbUqwq1rA==} + '@rollup/rollup-darwin-arm64@4.50.1': + resolution: {integrity: sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.46.3': - resolution: {integrity: sha512-r2MXNjbuYabSIX5yQqnT8SGSQ26XQc8fmp6UhlYJd95PZJkQD1u82fWP7HqvGUf33IsOC6qsiV+vcuD4SDP6iw==} + '@rollup/rollup-darwin-x64@4.50.1': + resolution: {integrity: sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.46.3': - resolution: {integrity: sha512-uluObTmgPJDuJh9xqxyr7MV61Imq+0IvVsAlWyvxAaBSNzCcmZlhfYcRhCdMaCsy46ccZa7vtDDripgs9Jkqsw==} + '@rollup/rollup-freebsd-arm64@4.50.1': + resolution: {integrity: sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.46.3': - resolution: {integrity: sha512-AVJXEq9RVHQnejdbFvh1eWEoobohUYN3nqJIPI4mNTMpsyYN01VvcAClxflyk2HIxvLpRcRggpX1m9hkXkpC/A==} + '@rollup/rollup-freebsd-x64@4.50.1': + resolution: {integrity: sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.46.3': - resolution: {integrity: sha512-byyflM+huiwHlKi7VHLAYTKr67X199+V+mt1iRgJenAI594vcmGGddWlu6eHujmcdl6TqSNnvqaXJqZdnEWRGA==} + '@rollup/rollup-linux-arm-gnueabihf@4.50.1': + resolution: {integrity: sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.46.3': - resolution: {integrity: sha512-aLm3NMIjr4Y9LklrH5cu7yybBqoVCdr4Nvnm8WB7PKCn34fMCGypVNpGK0JQWdPAzR/FnoEoFtlRqZbBBLhVoQ==} + '@rollup/rollup-linux-arm-musleabihf@4.50.1': + resolution: {integrity: sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.46.3': - resolution: {integrity: sha512-VtilE6eznJRDIoFOzaagQodUksTEfLIsvXymS+UdJiSXrPW7Ai+WG4uapAc3F7Hgs791TwdGh4xyOzbuzIZrnw==} + '@rollup/rollup-linux-arm64-gnu@4.50.1': + resolution: {integrity: sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.46.3': - resolution: {integrity: sha512-dG3JuS6+cRAL0GQ925Vppafi0qwZnkHdPeuZIxIPXqkCLP02l7ka+OCyBoDEv8S+nKHxfjvjW4OZ7hTdHkx8/w==} + '@rollup/rollup-linux-arm64-musl@4.50.1': + resolution: {integrity: sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.46.3': - resolution: {integrity: sha512-iU8DxnxEKJptf8Vcx4XvAUdpkZfaz0KWfRrnIRrOndL0SvzEte+MTM7nDH4A2Now4FvTZ01yFAgj6TX/mZl8hQ==} + '@rollup/rollup-linux-loongarch64-gnu@4.50.1': + resolution: {integrity: sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.46.3': - resolution: {integrity: sha512-VrQZp9tkk0yozJoQvQcqlWiqaPnLM6uY1qPYXvukKePb0fqaiQtOdMJSxNFUZFsGw5oA5vvVokjHrx8a9Qsz2A==} + '@rollup/rollup-linux-ppc64-gnu@4.50.1': + resolution: {integrity: sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.46.3': - resolution: {integrity: sha512-uf2eucWSUb+M7b0poZ/08LsbcRgaDYL8NCGjUeFMwCWFwOuFcZ8D9ayPl25P3pl+D2FH45EbHdfyUesQ2Lt9wA==} + '@rollup/rollup-linux-riscv64-gnu@4.50.1': + resolution: {integrity: sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.46.3': - resolution: {integrity: sha512-7tnUcDvN8DHm/9ra+/nF7lLzYHDeODKKKrh6JmZejbh1FnCNZS8zMkZY5J4sEipy2OW1d1Ncc4gNHUd0DLqkSg==} + '@rollup/rollup-linux-riscv64-musl@4.50.1': + resolution: {integrity: sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.46.3': - resolution: {integrity: sha512-MUpAOallJim8CsJK+4Lc9tQzlfPbHxWDrGXZm2z6biaadNpvh3a5ewcdat478W+tXDoUiHwErX/dOql7ETcLqg==} + '@rollup/rollup-linux-s390x-gnu@4.50.1': + resolution: {integrity: sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.46.3': - resolution: {integrity: sha512-F42IgZI4JicE2vM2PWCe0N5mR5vR0gIdORPqhGQ32/u1S1v3kLtbZ0C/mi9FFk7C5T0PgdeyWEPajPjaUpyoKg==} + '@rollup/rollup-linux-x64-gnu@4.50.1': + resolution: {integrity: sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.46.3': - resolution: {integrity: sha512-oLc+JrwwvbimJUInzx56Q3ujL3Kkhxehg7O1gWAYzm8hImCd5ld1F2Gry5YDjR21MNb5WCKhC9hXgU7rRlyegQ==} + '@rollup/rollup-linux-x64-musl@4.50.1': + resolution: {integrity: sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.46.3': - resolution: {integrity: sha512-lOrQ+BVRstruD1fkWg9yjmumhowR0oLAAzavB7yFSaGltY8klttmZtCLvOXCmGE9mLIn8IBV/IFrQOWz5xbFPg==} + '@rollup/rollup-openharmony-arm64@4.50.1': + resolution: {integrity: sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.50.1': + resolution: {integrity: sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.46.3': - resolution: {integrity: sha512-vvrVKPRS4GduGR7VMH8EylCBqsDcw6U+/0nPDuIjXQRbHJc6xOBj+frx8ksfZAh6+Fptw5wHrN7etlMmQnPQVg==} + '@rollup/rollup-win32-ia32-msvc@4.50.1': + resolution: {integrity: sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.46.3': - resolution: {integrity: sha512-fi3cPxCnu3ZeM3EwKZPgXbWoGzm2XHgB/WShKI81uj8wG0+laobmqy5wbgEwzstlbLu4MyO8C19FyhhWseYKNQ==} + '@rollup/rollup-win32-x64-msvc@4.50.1': + resolution: {integrity: sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA==} cpu: [x64] os: [win32] @@ -8364,8 +8369,8 @@ packages: peerDependencies: webpack: ^4.0.0 || ^5.0.0 - nwsapi@2.2.21: - resolution: {integrity: sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA==} + nwsapi@2.2.22: + resolution: {integrity: sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==} nwsapi@2.2.22: resolution: {integrity: sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==} @@ -9753,8 +9758,8 @@ packages: rollup: optional: true - rollup@4.46.3: - resolution: {integrity: sha512-RZn2XTjXb8t5g13f5YclGoilU/kwT696DIkY3sywjdZidNSi3+vseaQov7D7BZXVJCPv3pDWUN69C78GGbXsKw==} + rollup@4.50.1: + resolution: {integrity: sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -10519,8 +10524,8 @@ packages: tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - tinyglobby@0.2.14: - resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} tinypool@1.1.1: @@ -10957,8 +10962,8 @@ packages: vite: optional: true - vite@7.1.2: - resolution: {integrity: sha512-J0SQBPlQiEXAF7tajiH+rUooJPo0l8KQgyg4/aMunNtrOa7bwuZJsJbDWzeljqQpgftxuq5yNJxQ91O9ts29UQ==} + vite@7.1.5: + resolution: {integrity: sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -12237,7 +12242,7 @@ snapshots: dependencies: core-js-pure: 3.43.0 - '@babel/runtime@7.28.3': {} + '@babel/runtime@7.28.4': {} '@babel/runtime@7.28.4': {} @@ -12561,7 +12566,7 @@ snapshots: '@babel/preset-env': 7.27.2(@babel/core@7.27.7) '@babel/preset-react': 7.27.1(@babel/core@7.27.7) '@babel/preset-typescript': 7.27.1(@babel/core@7.27.7) - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@babel/runtime-corejs3': 7.27.6 '@babel/traverse': 7.28.3 '@docusaurus/logger': 3.8.1 @@ -14797,72 +14802,75 @@ snapshots: react: 18.3.1 react-redux: 7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@rollup/pluginutils@5.2.0(rollup@4.46.3)': + '@rollup/pluginutils@5.2.0(rollup@4.50.1)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.3 optionalDependencies: - rollup: 4.46.3 + rollup: 4.50.1 - '@rollup/rollup-android-arm-eabi@4.46.3': + '@rollup/rollup-android-arm-eabi@4.50.1': optional: true - '@rollup/rollup-android-arm64@4.46.3': + '@rollup/rollup-android-arm64@4.50.1': optional: true - '@rollup/rollup-darwin-arm64@4.46.3': + '@rollup/rollup-darwin-arm64@4.50.1': optional: true - '@rollup/rollup-darwin-x64@4.46.3': + '@rollup/rollup-darwin-x64@4.50.1': optional: true - '@rollup/rollup-freebsd-arm64@4.46.3': + '@rollup/rollup-freebsd-arm64@4.50.1': optional: true - '@rollup/rollup-freebsd-x64@4.46.3': + '@rollup/rollup-freebsd-x64@4.50.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.46.3': + '@rollup/rollup-linux-arm-gnueabihf@4.50.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.46.3': + '@rollup/rollup-linux-arm-musleabihf@4.50.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.46.3': + '@rollup/rollup-linux-arm64-gnu@4.50.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.46.3': + '@rollup/rollup-linux-arm64-musl@4.50.1': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.46.3': + '@rollup/rollup-linux-loongarch64-gnu@4.50.1': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.46.3': + '@rollup/rollup-linux-ppc64-gnu@4.50.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.46.3': + '@rollup/rollup-linux-riscv64-gnu@4.50.1': optional: true - '@rollup/rollup-linux-riscv64-musl@4.46.3': + '@rollup/rollup-linux-riscv64-musl@4.50.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.46.3': + '@rollup/rollup-linux-s390x-gnu@4.50.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.46.3': + '@rollup/rollup-linux-x64-gnu@4.50.1': optional: true - '@rollup/rollup-linux-x64-musl@4.46.3': + '@rollup/rollup-linux-x64-musl@4.50.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.46.3': + '@rollup/rollup-openharmony-arm64@4.50.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.46.3': + '@rollup/rollup-win32-arm64-msvc@4.50.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.46.3': + '@rollup/rollup-win32-ia32-msvc@4.50.1': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.50.1': optional: true '@scarf/scarf@1.4.0': {} @@ -14919,29 +14927,29 @@ snapshots: dependencies: acorn: 8.15.0 - '@sveltejs/adapter-static@3.0.9(@sveltejs/kit@2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))': + '@sveltejs/adapter-static@3.0.9(@sveltejs/kit@2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))': dependencies: - '@sveltejs/kit': 2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@sveltejs/kit': 2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) - '@sveltejs/enhanced-img@0.8.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(rollup@4.46.3)(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@sveltejs/enhanced-img@0.8.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(rollup@4.50.1)(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: - '@sveltejs/vite-plugin-svelte': 6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@sveltejs/vite-plugin-svelte': 6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) magic-string: 0.30.17 sharp: 0.34.3 svelte: 5.35.5 svelte-parse-markup: 0.1.5(svelte@5.35.5) - vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) - vite-imagetools: 8.0.0(rollup@4.46.3) + vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite-imagetools: 8.0.0(rollup@4.50.1) zimmerframe: 1.1.2 transitivePeerDependencies: - rollup - supports-color - '@sveltejs/kit@2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@sveltejs/kit@2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@standard-schema/spec': 1.0.0 '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0) - '@sveltejs/vite-plugin-svelte': 6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@sveltejs/vite-plugin-svelte': 6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@types/cookie': 0.6.0 acorn: 8.15.0 cookie: 0.6.0 @@ -14954,27 +14962,27 @@ snapshots: set-cookie-parser: 2.7.1 sirv: 3.0.1 svelte: 5.35.5 - vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) - '@sveltejs/vite-plugin-svelte-inspector@5.0.0(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@sveltejs/vite-plugin-svelte-inspector@5.0.0(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: - '@sveltejs/vite-plugin-svelte': 6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@sveltejs/vite-plugin-svelte': 6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) debug: 4.4.1 svelte: 5.35.5 - vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 5.0.0(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@sveltejs/vite-plugin-svelte-inspector': 5.0.0(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) debug: 4.4.1 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.17 svelte: 5.35.5 - vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) - vitefu: 1.1.1(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vitefu: 1.1.1(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) transitivePeerDependencies: - supports-color @@ -15196,17 +15204,17 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.1.12 '@tailwindcss/oxide-win32-x64-msvc': 4.1.12 - '@tailwindcss/vite@4.1.12(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@tailwindcss/vite@4.1.12(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@tailwindcss/node': 4.1.12 '@tailwindcss/oxide': 4.1.12 tailwindcss: 4.1.12 - vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) '@testing-library/dom@10.4.0': dependencies: '@babel/code-frame': 7.27.1 - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 '@types/aria-query': 5.0.4 aria-query: 5.3.0 chalk: 4.1.2 @@ -15223,12 +15231,12 @@ snapshots: picocolors: 1.1.1 redent: 3.0.0 - '@testing-library/svelte@5.2.8(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@testing-library/svelte@5.2.8(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@testing-library/dom': 10.4.0 svelte: 5.35.5 optionalDependencies: - vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.0)': @@ -15883,21 +15891,21 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(vite@7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@vitest/mocker@3.2.4(vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) - '@vitest/mocker@3.2.4(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@vitest/mocker@3.2.4(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) '@vitest/pretty-format@3.2.4': dependencies: @@ -18796,7 +18804,7 @@ snapshots: history@4.10.1: dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 loose-envify: 1.4.0 resolve-pathname: 3.0.0 tiny-invariant: 1.3.3 @@ -19323,7 +19331,7 @@ snapshots: http-proxy-agent: 5.0.0 https-proxy-agent: 5.0.1 is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.21 + nwsapi: 2.2.22 parse5: 7.3.0 saxes: 6.0.0 symbol-tree: 3.2.4 @@ -20619,7 +20627,7 @@ snapshots: proc-log: 5.0.0 semver: 7.7.2 tar: 7.4.3 - tinyglobby: 0.2.14 + tinyglobby: 0.2.15 which: 5.0.0 transitivePeerDependencies: - supports-color @@ -20677,7 +20685,7 @@ snapshots: schema-utils: 3.3.0 webpack: 5.100.2 - nwsapi@2.2.21: + nwsapi@2.2.22: optional: true nwsapi@2.2.22: @@ -21878,7 +21886,7 @@ snapshots: react-loadable-ssr-addon-v5-slorber@1.0.1(@docusaurus/react-loadable@6.0.0(react@18.3.1))(webpack@5.100.2): dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 react-loadable: '@docusaurus/react-loadable@6.0.0(react@18.3.1)' webpack: 5.100.2 @@ -21902,13 +21910,13 @@ snapshots: react-router-config@5.1.1(react-router@5.3.4(react@18.3.1))(react@18.3.1): dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 react: 18.3.1 react-router: 5.3.4(react@18.3.1) react-router-dom@5.3.4(react@18.3.1): dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 history: 4.10.1 loose-envify: 1.4.0 prop-types: 15.8.1 @@ -21919,7 +21927,7 @@ snapshots: react-router@5.3.4(react@18.3.1): dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.28.4 history: 4.10.1 hoist-non-react-statics: 3.3.2 loose-envify: 1.4.0 @@ -22240,39 +22248,40 @@ snapshots: robust-predicates@3.0.2: {} - rollup-plugin-visualizer@6.0.3(rollup@4.46.3): + rollup-plugin-visualizer@6.0.3(rollup@4.50.1): dependencies: open: 8.4.2 picomatch: 4.0.3 source-map: 0.7.4 yargs: 17.7.2 optionalDependencies: - rollup: 4.46.3 + rollup: 4.50.1 - rollup@4.46.3: + rollup@4.50.1: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.46.3 - '@rollup/rollup-android-arm64': 4.46.3 - '@rollup/rollup-darwin-arm64': 4.46.3 - '@rollup/rollup-darwin-x64': 4.46.3 - '@rollup/rollup-freebsd-arm64': 4.46.3 - '@rollup/rollup-freebsd-x64': 4.46.3 - '@rollup/rollup-linux-arm-gnueabihf': 4.46.3 - '@rollup/rollup-linux-arm-musleabihf': 4.46.3 - '@rollup/rollup-linux-arm64-gnu': 4.46.3 - '@rollup/rollup-linux-arm64-musl': 4.46.3 - '@rollup/rollup-linux-loongarch64-gnu': 4.46.3 - '@rollup/rollup-linux-ppc64-gnu': 4.46.3 - '@rollup/rollup-linux-riscv64-gnu': 4.46.3 - '@rollup/rollup-linux-riscv64-musl': 4.46.3 - '@rollup/rollup-linux-s390x-gnu': 4.46.3 - '@rollup/rollup-linux-x64-gnu': 4.46.3 - '@rollup/rollup-linux-x64-musl': 4.46.3 - '@rollup/rollup-win32-arm64-msvc': 4.46.3 - '@rollup/rollup-win32-ia32-msvc': 4.46.3 - '@rollup/rollup-win32-x64-msvc': 4.46.3 + '@rollup/rollup-android-arm-eabi': 4.50.1 + '@rollup/rollup-android-arm64': 4.50.1 + '@rollup/rollup-darwin-arm64': 4.50.1 + '@rollup/rollup-darwin-x64': 4.50.1 + '@rollup/rollup-freebsd-arm64': 4.50.1 + '@rollup/rollup-freebsd-x64': 4.50.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.50.1 + '@rollup/rollup-linux-arm-musleabihf': 4.50.1 + '@rollup/rollup-linux-arm64-gnu': 4.50.1 + '@rollup/rollup-linux-arm64-musl': 4.50.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.50.1 + '@rollup/rollup-linux-ppc64-gnu': 4.50.1 + '@rollup/rollup-linux-riscv64-gnu': 4.50.1 + '@rollup/rollup-linux-riscv64-musl': 4.50.1 + '@rollup/rollup-linux-s390x-gnu': 4.50.1 + '@rollup/rollup-linux-x64-gnu': 4.50.1 + '@rollup/rollup-linux-x64-musl': 4.50.1 + '@rollup/rollup-openharmony-arm64': 4.50.1 + '@rollup/rollup-win32-arm64-msvc': 4.50.1 + '@rollup/rollup-win32-ia32-msvc': 4.50.1 + '@rollup/rollup-win32-x64-msvc': 4.50.1 fsevents: 2.3.3 router@2.2.0: @@ -23293,7 +23302,7 @@ snapshots: tinyexec@0.3.2: {} - tinyglobby@0.2.14: + tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 @@ -23583,9 +23592,9 @@ snapshots: unpipe@1.0.0: {} - unplugin-swc@1.5.5(@swc/core@1.13.3(@swc/helpers@0.5.17))(rollup@4.46.3): + unplugin-swc@1.5.5(@swc/core@1.13.3(@swc/helpers@0.5.17))(rollup@4.50.1): dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.46.3) + '@rollup/pluginutils': 5.2.0(rollup@4.50.1) '@swc/core': 1.13.3(@swc/helpers@0.5.17) load-tsconfig: 0.2.5 unplugin: 2.3.5 @@ -23725,9 +23734,9 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.2 - vite-imagetools@8.0.0(rollup@4.46.3): + vite-imagetools@8.0.0(rollup@4.50.1): dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.46.3) + '@rollup/pluginutils': 5.2.0(rollup@4.50.1) imagetools-core: 8.0.0 sharp: 0.34.3 transitivePeerDependencies: @@ -23740,7 +23749,7 @@ snapshots: debug: 4.4.1 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) transitivePeerDependencies: - '@types/node' - jiti @@ -23761,7 +23770,7 @@ snapshots: debug: 4.4.1 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) transitivePeerDependencies: - '@types/node' - jiti @@ -23776,25 +23785,25 @@ snapshots: - tsx - yaml - vite-tsconfig-paths@5.1.4(typescript@5.9.2)(vite@7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): + vite-tsconfig-paths@5.1.4(typescript@5.9.2)(vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): dependencies: debug: 4.4.1 globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.9.2) optionalDependencies: - vite: 7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) transitivePeerDependencies: - supports-color - typescript - vite@7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): + vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): dependencies: esbuild: 0.25.9 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.46.3 - tinyglobby: 0.2.14 + rollup: 4.50.1 + tinyglobby: 0.2.15 optionalDependencies: '@types/node': 22.18.1 fsevents: 2.3.3 @@ -23803,14 +23812,14 @@ snapshots: terser: 5.43.1 yaml: 2.8.1 - vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): + vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): dependencies: esbuild: 0.25.9 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.46.3 - tinyglobby: 0.2.14 + rollup: 4.50.1 + tinyglobby: 0.2.15 optionalDependencies: '@types/node': 24.3.0 fsevents: 2.3.3 @@ -23819,9 +23828,9 @@ snapshots: terser: 5.43.1 yaml: 2.8.1 - vitefu@1.1.1(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): + vitefu@1.1.1(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): optionalDependencies: - vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vitest-fetch-mock@0.4.5(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.1)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): dependencies: @@ -23831,7 +23840,7 @@ snapshots: dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@vitest/mocker': 3.2.4(vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -23846,10 +23855,10 @@ snapshots: std-env: 3.9.0 tinybench: 2.9.0 tinyexec: 0.3.2 - tinyglobby: 0.2.14 + tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vite-node: 3.2.4(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: @@ -23875,7 +23884,7 @@ snapshots: dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@vitest/mocker': 3.2.4(vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -23890,10 +23899,10 @@ snapshots: std-env: 3.9.0 tinybench: 2.9.0 tinyexec: 0.3.2 - tinyglobby: 0.2.14 + tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vite-node: 3.2.4(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: @@ -23919,7 +23928,7 @@ snapshots: dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@vitest/mocker': 3.2.4(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -23934,10 +23943,10 @@ snapshots: std-env: 3.9.0 tinybench: 2.9.0 tinyexec: 0.3.2 - tinyglobby: 0.2.14 + tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vite-node: 3.2.4(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: From 8bf45eb71890edf77114a6e4d0f7aef7088ab25a Mon Sep 17 00:00:00 2001 From: Oleksandr Povar <1074182+zvirja@users.noreply.github.com> Date: Wed, 10 Sep 2025 14:28:01 +0200 Subject: [PATCH 451/748] chore: update README (#21718) Enhance README UA translation --- readme_i18n/README_uk_UA.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/readme_i18n/README_uk_UA.md b/readme_i18n/README_uk_UA.md index 5a33fa210d..33687bbc50 100644 --- a/readme_i18n/README_uk_UA.md +++ b/readme_i18n/README_uk_UA.md @@ -42,11 +42,11 @@ - ⚠️ Цей проєкт перебуває **в дуже активній** розробці. - ⚠️ Очікуйте безліч помилок і глобальних змін. -- ⚠️ **Не використовуйте цей додаток як єдине сховище своїх фото та відео.** +- ⚠️ **Не використовуйте цей застосунок як єдине сховище своїх фото та відео.** - ⚠️ Завжди дотримуйтесь [плану резервного копіювання 3-2-1](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) для ваших дорогоцінних фотографій та відео! > [!NOTE] -> Основну документацію, зокрема посібники з встановлення, можна знайти за адресою https://immich.app/. +> Основну документацію, зокрема посібники зі встановлення, можна знайти за адресою https://immich.app/. ## Посилання @@ -61,7 +61,7 @@ ## Демо -Доступ до демо-версії [тут](https://demo.immich.app). Для мобільного додатку ви можете використовувати `https://demo.immich.app` в якості `Server Endpoint URL`. +Доступ до демо-версії [тут](https://demo.immich.app). Для мобільного застосунку ви можете використовувати `https://demo.immich.app` в якості `Server Endpoint URL`. ### Облікові дані для входу @@ -74,7 +74,7 @@ | Функції | Додаток | Веб | | :------------------------------------------------------- | ------- | --- | | Завантаження та перегляд відео й фото | Так | Так | -| Автоматичне резервне копіювання при відкритті додатка | Так | Н/Д | +| Автоматичне резервне копіювання при відкритті застосунку | Так | Н/Д | | Запобігання дублюванню файлів | Так | Так | | Вибір альбомів для резервного копіювання | Так | Н/Д | | Завантаження фото та відео на локальний пристрій | Так | Так | @@ -112,7 +112,7 @@ Статус перекладів -## Активність репозитарію +## Активність репозиторію ![Діяльність](https://repobeats.axiom.co/api/embed/9e86d9dc3ddd137161f2f6d2e758d7863b1789cb.svg "Зображення аналітики Repobeats") From b21084b851ee9309744b6f64edad28fd39fb9f9c Mon Sep 17 00:00:00 2001 From: Sergey Katsubo Date: Wed, 10 Sep 2025 15:35:55 +0300 Subject: [PATCH 452/748] fix(server): correct immich-cli symlink in Immich docker image (#21318) Fix immich-cli symlink in Immich docker image --- server/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/Dockerfile b/server/Dockerfile index a554e19406..9fa401dbdd 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -125,7 +125,7 @@ ENV NODE_ENV=production \ COPY --from=server-prod /output/server-pruned ./server COPY --from=web-prod /usr/src/app/web/build /build/www COPY --from=cli-prod /output/cli-pruned ./cli -RUN ln -s ./cli/bin/immich server/bin/immich +RUN ln -s ../../cli/bin/immich server/bin/immich COPY LICENSE /licenses/LICENSE.txt COPY LICENSE /LICENSE From 41c1d4d44b0216a3f15903717c9ed9e22bc63c8a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 10 Sep 2025 08:36:27 -0400 Subject: [PATCH 453/748] chore(deps): update ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0 docker digest to 8d292bd (#21287) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- docker/docker-compose.dev.yml | 2 +- docker/docker-compose.prod.yml | 2 +- docker/docker-compose.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index 372352d12a..b0be9b5266 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -149,7 +149,7 @@ services: database: container_name: immich_postgres - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:32324a2f41df5de9efe1af166b7008c3f55646f8d0e00d9550c16c9822366b4a + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:8d292bdb796aa58bbbaa47fe971c8516f6f57d6a47e7172e62754feb6ed4e7b0 env_file: - .env environment: diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml index 7c658de336..382d18d1ed 100644 --- a/docker/docker-compose.prod.yml +++ b/docker/docker-compose.prod.yml @@ -63,7 +63,7 @@ services: database: container_name: immich_postgres - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:32324a2f41df5de9efe1af166b7008c3f55646f8d0e00d9550c16c9822366b4a + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:8d292bdb796aa58bbbaa47fe971c8516f6f57d6a47e7172e62754feb6ed4e7b0 env_file: - .env environment: diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 052ae8b334..2b733f67f7 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -56,7 +56,7 @@ services: database: container_name: immich_postgres - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:32324a2f41df5de9efe1af166b7008c3f55646f8d0e00d9550c16c9822366b4a + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:8d292bdb796aa58bbbaa47fe971c8516f6f57d6a47e7172e62754feb6ed4e7b0 environment: POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_USER: ${DB_USERNAME} From ee98e690974ba7a1de65195b950a8501594fee3b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 10 Sep 2025 08:36:44 -0400 Subject: [PATCH 454/748] chore(deps): update docker.io/valkey/valkey:8-bookworm docker digest to fea8b3e (#21281) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- docker/docker-compose.dev.yml | 2 +- docker/docker-compose.prod.yml | 2 +- docker/docker-compose.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index b0be9b5266..5c1a21c7ce 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -143,7 +143,7 @@ services: redis: container_name: immich_redis - image: docker.io/valkey/valkey:8-bookworm@sha256:a137a2b60aca1a75130022d6bb96af423fefae4eb55faf395732db3544803280 + image: docker.io/valkey/valkey:8-bookworm@sha256:fea8b3e67b15729d4bb70589eb03367bab9ad1ee89c876f54327fc7c6e618571 healthcheck: test: redis-cli ping || exit 1 diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml index 382d18d1ed..f7d1f564cf 100644 --- a/docker/docker-compose.prod.yml +++ b/docker/docker-compose.prod.yml @@ -56,7 +56,7 @@ services: redis: container_name: immich_redis - image: docker.io/valkey/valkey:8-bookworm@sha256:a137a2b60aca1a75130022d6bb96af423fefae4eb55faf395732db3544803280 + image: docker.io/valkey/valkey:8-bookworm@sha256:fea8b3e67b15729d4bb70589eb03367bab9ad1ee89c876f54327fc7c6e618571 healthcheck: test: redis-cli ping || exit 1 restart: always diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 2b733f67f7..c401d4cfc7 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -49,7 +49,7 @@ services: redis: container_name: immich_redis - image: docker.io/valkey/valkey:8-bookworm@sha256:a137a2b60aca1a75130022d6bb96af423fefae4eb55faf395732db3544803280 + image: docker.io/valkey/valkey:8-bookworm@sha256:fea8b3e67b15729d4bb70589eb03367bab9ad1ee89c876f54327fc7c6e618571 healthcheck: test: redis-cli ping || exit 1 restart: always From 39eee6a634cd8eaaf4ca3a7b2d8c80a7789401f2 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 10 Sep 2025 09:11:42 -0400 Subject: [PATCH 455/748] fix: welcome email password (#21732) --- server/src/repositories/event.repository.ts | 2 +- .../src/services/notification.service.spec.ts | 2 +- server/src/services/notification.service.ts | 70 ++----------------- server/src/services/user-admin.service.ts | 2 +- server/src/types.ts | 2 +- 5 files changed, 8 insertions(+), 70 deletions(-) diff --git a/server/src/repositories/event.repository.ts b/server/src/repositories/event.repository.ts index c1b26d5dde..ec4c8a8f52 100644 --- a/server/src/repositories/event.repository.ts +++ b/server/src/repositories/event.repository.ts @@ -81,7 +81,7 @@ type EventMap = { StackDeleteAll: [{ stackIds: string[]; userId: string }]; // user events - UserSignup: [{ notify: boolean; id: string; tempPassword?: string }]; + UserSignup: [{ notify: boolean; id: string; password?: string }]; // websocket events WebsocketConnect: [{ userId: string }]; diff --git a/server/src/services/notification.service.spec.ts b/server/src/services/notification.service.spec.ts index eef1c4f8b2..11c385b1e2 100644 --- a/server/src/services/notification.service.spec.ts +++ b/server/src/services/notification.service.spec.ts @@ -147,7 +147,7 @@ describe(NotificationService.name, () => { await sut.onUserSignup({ id: '', notify: true }); expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.NotifyUserSignup, - data: { id: '', tempPassword: undefined }, + data: { id: '', password: undefined }, }); }); }); diff --git a/server/src/services/notification.service.ts b/server/src/services/notification.service.ts index 1a257309b2..91a043d405 100644 --- a/server/src/services/notification.service.ts +++ b/server/src/services/notification.service.ts @@ -191,9 +191,9 @@ export class NotificationService extends BaseService { } @OnEvent({ name: 'UserSignup' }) - async onUserSignup({ notify, id, tempPassword }: ArgOf<'UserSignup'>) { + async onUserSignup({ notify, id, password: password }: ArgOf<'UserSignup'>) { if (notify) { - await this.jobRepository.queue({ name: JobName.NotifyUserSignup, data: { id, tempPassword } }); + await this.jobRepository.queue({ name: JobName.NotifyUserSignup, data: { id, password } }); } } @@ -251,70 +251,8 @@ export class NotificationService extends BaseService { return { messageId }; } - async getTemplate(name: EmailTemplate, customTemplate: string) { - const { server, templates } = await this.getConfig({ withCache: false }); - - let templateResponse = ''; - - switch (name) { - case EmailTemplate.WELCOME: { - const { html: _welcomeHtml } = await this.emailRepository.renderEmail({ - template: EmailTemplate.WELCOME, - data: { - baseUrl: getExternalDomain(server), - displayName: 'John Doe', - username: 'john@doe.com', - password: 'thisIsAPassword123', - }, - customTemplate: customTemplate || templates.email.welcomeTemplate, - }); - - templateResponse = _welcomeHtml; - break; - } - case EmailTemplate.ALBUM_UPDATE: { - const { html: _updateAlbumHtml } = await this.emailRepository.renderEmail({ - template: EmailTemplate.ALBUM_UPDATE, - data: { - baseUrl: getExternalDomain(server), - albumId: '1', - albumName: 'Favorite Photos', - recipientName: 'Jane Doe', - cid: undefined, - }, - customTemplate: customTemplate || templates.email.albumInviteTemplate, - }); - templateResponse = _updateAlbumHtml; - break; - } - - case EmailTemplate.ALBUM_INVITE: { - const { html } = await this.emailRepository.renderEmail({ - template: EmailTemplate.ALBUM_INVITE, - data: { - baseUrl: getExternalDomain(server), - albumId: '1', - albumName: "John Doe's Favorites", - senderName: 'John Doe', - recipientName: 'Jane Doe', - cid: undefined, - }, - customTemplate: customTemplate || templates.email.albumInviteTemplate, - }); - templateResponse = html; - break; - } - default: { - templateResponse = ''; - break; - } - } - - return { name, html: templateResponse }; - } - @OnJob({ name: JobName.NotifyUserSignup, queue: QueueName.Notification }) - async handleUserSignup({ id, tempPassword }: JobOf) { + async handleUserSignup({ id, password }: JobOf) { const user = await this.userRepository.get(id, { withDeleted: false }); if (!user) { return JobStatus.Skipped; @@ -327,7 +265,7 @@ export class NotificationService extends BaseService { baseUrl: getExternalDomain(server), displayName: user.name, username: user.email, - password: tempPassword, + password, }, customTemplate: templates.email.welcomeTemplate, }); diff --git a/server/src/services/user-admin.service.ts b/server/src/services/user-admin.service.ts index 3ae9d429eb..ce70419ff6 100644 --- a/server/src/services/user-admin.service.ts +++ b/server/src/services/user-admin.service.ts @@ -38,7 +38,7 @@ export class UserAdminService extends BaseService { await this.eventRepository.emit('UserSignup', { notify: !!notify, id: user.id, - tempPassword: user.shouldChangePassword ? userDto.password : undefined, + password: userDto.password, }); return mapUserAdmin(user); diff --git a/server/src/types.ts b/server/src/types.ts index 9e54de80bb..ce0baa1672 100644 --- a/server/src/types.ts +++ b/server/src/types.ts @@ -249,7 +249,7 @@ export interface IEmailJob { } export interface INotifySignupJob extends IEntityJob { - tempPassword?: string; + password?: string; } export interface INotifyAlbumInviteJob extends IEntityJob { From 67a8cab286e889ef982d82183d9aee1db800a758 Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Wed, 10 Sep 2025 19:08:53 +0530 Subject: [PATCH 456/748] feat: resurrect advanced info (#21633) * feat: resurrect advanced info * display null values as well * add exif details --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- i18n/en.json | 1 + mobile/lib/domain/services/asset.service.dart | 13 + .../repositories/backup.repository.dart | 19 - .../repositories/local_asset.repository.dart | 27 ++ .../repositories/remote_asset.repository.dart | 6 + .../pages/drift_asset_troubleshoot.page.dart | 345 ++++++++++++++++++ .../advanced_info_action_button.widget.dart | 30 ++ .../asset_viewer/bottom_sheet.widget.dart | 14 +- .../archive_bottom_sheet.widget.dart | 2 +- .../general_bottom_sheet.widget.dart | 7 + .../backup/drift_backup.provider.dart | 4 +- .../infrastructure/action.provider.dart | 12 + mobile/lib/routing/router.dart | 2 + mobile/lib/routing/router.gr.dart | 37 ++ mobile/lib/utils/action_button.utils.dart | 9 +- .../test/utils/action_button_utils_test.dart | 71 ++++ 16 files changed, 571 insertions(+), 28 deletions(-) create mode 100644 mobile/lib/presentation/pages/drift_asset_troubleshoot.page.dart create mode 100644 mobile/lib/presentation/widgets/action_buttons/advanced_info_action_button.widget.dart diff --git a/i18n/en.json b/i18n/en.json index afedf0081b..e9f965d677 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1978,6 +1978,7 @@ "trash_page_select_assets_btn": "Select assets", "trash_page_title": "Trash ({count})", "trashed_items_will_be_permanently_deleted_after": "Trashed items will be permanently deleted after {days, plural, one {# day} other {# days}}.", + "troubleshoot": "Troubleshoot", "type": "Type", "unable_to_change_pin_code": "Unable to change PIN code", "unable_to_setup_pin_code": "Unable to setup PIN code", diff --git a/mobile/lib/domain/services/asset.service.dart b/mobile/lib/domain/services/asset.service.dart index df34a41e54..875dc80702 100644 --- a/mobile/lib/domain/services/asset.service.dart +++ b/mobile/lib/domain/services/asset.service.dart @@ -1,3 +1,4 @@ +import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/exif.model.dart'; import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; @@ -27,6 +28,14 @@ class AssetService { return asset is LocalAsset ? _localAssetRepository.watch(id) : _remoteAssetRepository.watch(id); } + Future> getLocalAssetsByChecksum(String checksum) { + return _localAssetRepository.getByChecksum(checksum); + } + + Future getRemoteAssetByChecksum(String checksum) { + return _remoteAssetRepository.getByChecksum(checksum); + } + Future getRemoteAsset(String id) { return _remoteAssetRepository.get(id); } @@ -89,4 +98,8 @@ class AssetService { Future getLocalHashedCount() { return _localAssetRepository.getHashedCount(); } + + Future> getSourceAlbums(String localAssetId, {BackupSelection? backupSelection}) { + return _localAssetRepository.getSourceAlbums(localAssetId, backupSelection: backupSelection); + } } diff --git a/mobile/lib/infrastructure/repositories/backup.repository.dart b/mobile/lib/infrastructure/repositories/backup.repository.dart index 057c7a7bf6..1e9f69147c 100644 --- a/mobile/lib/infrastructure/repositories/backup.repository.dart +++ b/mobile/lib/infrastructure/repositories/backup.repository.dart @@ -4,7 +4,6 @@ import 'package:drift/drift.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; -import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; @@ -138,22 +137,4 @@ class DriftBackupRepository extends DriftDatabaseRepository { return query.map((localAsset) => localAsset.toDto()).get(); } - - FutureOr> getSourceAlbums(String localAssetId) { - final query = _db.localAlbumEntity.select() - ..where( - (lae) => - existsQuery( - _db.localAlbumAssetEntity.selectOnly() - ..addColumns([_db.localAlbumAssetEntity.albumId]) - ..where( - _db.localAlbumAssetEntity.albumId.equalsExp(lae.id) & - _db.localAlbumAssetEntity.assetId.equals(localAssetId), - ), - ) & - lae.backupSelection.equalsValue(BackupSelection.selected), - ) - ..orderBy([(lae) => OrderingTerm.asc(lae.name)]); - return query.map((localAlbum) => localAlbum.toDto()).get(); - } } diff --git a/mobile/lib/infrastructure/repositories/local_asset.repository.dart b/mobile/lib/infrastructure/repositories/local_asset.repository.dart index 5865447064..05c8e06678 100644 --- a/mobile/lib/infrastructure/repositories/local_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/local_asset.repository.dart @@ -1,6 +1,8 @@ import 'package:collection/collection.dart'; import 'package:drift/drift.dart'; +import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; @@ -26,6 +28,12 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository { Future get(String id) => _assetSelectable(id).getSingleOrNull(); + Future> getByChecksum(String checksum) { + final query = _db.localAssetEntity.select()..where((lae) => lae.checksum.equals(checksum)); + + return query.map((row) => row.toDto()).get(); + } + Stream watch(String id) => _assetSelectable(id).watchSingleOrNull(); Future updateHashes(Iterable hashes) { @@ -69,4 +77,23 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository { Future getHashedCount() { return _db.managers.localAssetEntity.filter((e) => e.checksum.isNull().not()).count(); } + + Future> getSourceAlbums(String localAssetId, {BackupSelection? backupSelection}) { + final query = _db.localAlbumEntity.select() + ..where( + (lae) => existsQuery( + _db.localAlbumAssetEntity.selectOnly() + ..addColumns([_db.localAlbumAssetEntity.albumId]) + ..where( + _db.localAlbumAssetEntity.albumId.equalsExp(lae.id) & + _db.localAlbumAssetEntity.assetId.equals(localAssetId), + ), + ), + ) + ..orderBy([(lae) => OrderingTerm.asc(lae.name)]); + if (backupSelection != null) { + query.where((lae) => lae.backupSelection.equalsValue(backupSelection)); + } + return query.map((localAlbum) => localAlbum.toDto()).get(); + } } diff --git a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart index 3ed7dddfe8..01aa10c7ad 100644 --- a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart @@ -55,6 +55,12 @@ class RemoteAssetRepository extends DriftDatabaseRepository { return _assetSelectable(id).getSingleOrNull(); } + Future getByChecksum(String checksum) { + final query = _db.remoteAssetEntity.select()..where((row) => row.checksum.equals(checksum)); + + return query.map((row) => row.toDto()).getSingleOrNull(); + } + Future> getStackChildren(RemoteAsset asset) { if (asset.stackId == null) { return Future.value([]); diff --git a/mobile/lib/presentation/pages/drift_asset_troubleshoot.page.dart b/mobile/lib/presentation/pages/drift_asset_troubleshoot.page.dart new file mode 100644 index 0000000000..1cd6bee67d --- /dev/null +++ b/mobile/lib/presentation/pages/drift_asset_troubleshoot.page.dart @@ -0,0 +1,345 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/exif.model.dart'; +import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; + +@RoutePage() +class AssetTroubleshootPage extends ConsumerWidget { + final BaseAsset asset; + + const AssetTroubleshootPage({super.key, required this.asset}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Scaffold( + appBar: AppBar(title: const Text("Asset Troubleshoot")), + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: _AssetDetailsView(asset: asset), + ), + ), + ); + } +} + +class _AssetDetailsView extends ConsumerWidget { + final BaseAsset asset; + + const _AssetDetailsView({required this.asset}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _AssetPropertiesSection(asset: asset), + const SizedBox(height: 16), + Text('Matching Assets', style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold)), + if (asset.checksum != null) ...[ + _LocalAssetsSection(asset: asset), + const SizedBox(height: 16), + _RemoteAssetSection(asset: asset), + ] else ...[ + const _PropertySectionCard( + title: 'Local Assets', + properties: [_PropertyItem(label: 'Status', value: 'No checksum available - cannot fetch local assets')], + ), + const SizedBox(height: 16), + const _PropertySectionCard( + title: 'Remote Assets', + properties: [_PropertyItem(label: 'Status', value: 'No checksum available - cannot fetch remote asset')], + ), + ], + ], + ); + } +} + +class _AssetPropertiesSection extends ConsumerStatefulWidget { + final BaseAsset asset; + + const _AssetPropertiesSection({required this.asset}); + + @override + ConsumerState createState() => _AssetPropertiesSectionState(); +} + +class _AssetPropertiesSectionState extends ConsumerState<_AssetPropertiesSection> { + List<_PropertyItem> properties = []; + + @override + void initState() { + super.initState(); + _buildAssetProperties(widget.asset).whenComplete(() { + if (mounted) { + setState(() {}); + } + }); + } + + @override + Widget build(BuildContext context) { + final title = _getAssetTypeTitle(widget.asset); + + return _PropertySectionCard(title: title, properties: properties); + } + + Future _buildAssetProperties(BaseAsset asset) async { + _addCommonProperties(); + + if (asset is LocalAsset) { + await _addLocalAssetProperties(asset); + } else if (asset is RemoteAsset) { + await _addRemoteAssetProperties(asset); + } + } + + void _addCommonProperties() { + final asset = widget.asset; + properties.addAll([ + _PropertyItem(label: 'Name', value: asset.name), + _PropertyItem(label: 'Checksum', value: asset.checksum), + _PropertyItem(label: 'Type', value: asset.type.toString()), + _PropertyItem(label: 'Created At', value: asset.createdAt.toString()), + _PropertyItem(label: 'Updated At', value: asset.updatedAt.toString()), + _PropertyItem(label: 'Width', value: asset.width?.toString()), + _PropertyItem(label: 'Height', value: asset.height?.toString()), + _PropertyItem( + label: 'Duration', + value: asset.durationInSeconds != null ? '${asset.durationInSeconds} seconds' : null, + ), + _PropertyItem(label: 'Is Favorite', value: asset.isFavorite.toString()), + _PropertyItem(label: 'Live Photo Video ID', value: asset.livePhotoVideoId), + ]); + } + + Future _addLocalAssetProperties(LocalAsset asset) async { + properties.insertAll(0, [ + _PropertyItem(label: 'Local ID', value: asset.id), + _PropertyItem(label: 'Remote ID', value: asset.remoteId), + ]); + + properties.insert(4, _PropertyItem(label: 'Orientation', value: asset.orientation.toString())); + final albums = await ref.read(assetServiceProvider).getSourceAlbums(asset.id); + properties.add(_PropertyItem(label: 'Album', value: albums.map((a) => a.name).join(', '))); + } + + Future _addRemoteAssetProperties(RemoteAsset asset) async { + properties.insertAll(0, [ + _PropertyItem(label: 'Remote ID', value: asset.id), + _PropertyItem(label: 'Local ID', value: asset.localId), + _PropertyItem(label: 'Owner ID', value: asset.ownerId), + ]); + + final additionalProps = <_PropertyItem>[ + _PropertyItem(label: 'Thumb Hash', value: asset.thumbHash), + _PropertyItem(label: 'Visibility', value: asset.visibility.toString()), + _PropertyItem(label: 'Stack ID', value: asset.stackId), + ]; + + properties.insertAll(4, additionalProps); + + final exif = await ref.read(assetServiceProvider).getExif(asset); + if (exif != null) { + _addExifProperties(exif); + } else { + properties.add(const _PropertyItem(label: 'EXIF', value: null)); + } + } + + void _addExifProperties(ExifInfo exif) { + properties.addAll([ + _PropertyItem( + label: 'File Size', + value: exif.fileSize != null ? '${(exif.fileSize! / 1024 / 1024).toStringAsFixed(2)} MB' : null, + ), + _PropertyItem(label: 'Description', value: exif.description), + _PropertyItem(label: 'EXIF Width', value: exif.width?.toString()), + _PropertyItem(label: 'EXIF Height', value: exif.height?.toString()), + _PropertyItem(label: 'Date Taken', value: exif.dateTimeOriginal?.toString()), + _PropertyItem(label: 'Time Zone', value: exif.timeZone), + _PropertyItem(label: 'Camera Make', value: exif.make), + _PropertyItem(label: 'Camera Model', value: exif.model), + _PropertyItem(label: 'Lens', value: exif.lens), + _PropertyItem(label: 'F-Number', value: exif.f != null ? 'f/${exif.fNumber}' : null), + _PropertyItem(label: 'Focal Length', value: exif.mm != null ? '${exif.focalLength}mm' : null), + _PropertyItem(label: 'ISO', value: exif.iso?.toString()), + _PropertyItem(label: 'Exposure Time', value: exif.exposureTime.isNotEmpty ? exif.exposureTime : null), + _PropertyItem( + label: 'GPS Coordinates', + value: exif.hasCoordinates ? '${exif.latitude}, ${exif.longitude}' : null, + ), + _PropertyItem( + label: 'Location', + value: [exif.city, exif.state, exif.country].where((e) => e != null && e.isNotEmpty).join(', '), + ), + ]); + } + + String _getAssetTypeTitle(BaseAsset asset) { + if (asset is LocalAsset) return 'Local Asset'; + if (asset is RemoteAsset) return 'Remote Asset'; + return 'Base Asset'; + } +} + +class _LocalAssetsSection extends ConsumerWidget { + final BaseAsset asset; + + const _LocalAssetsSection({required this.asset}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final assetService = ref.watch(assetServiceProvider); + + return FutureBuilder>( + future: assetService.getLocalAssetsByChecksum(asset.checksum!), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const _PropertySectionCard( + title: 'Local Assets', + properties: [_PropertyItem(label: 'Status', value: 'Loading...')], + ); + } + + if (snapshot.hasError) { + return _PropertySectionCard( + title: 'Local Assets', + properties: [_PropertyItem(label: 'Error', value: snapshot.error.toString())], + ); + } + + final localAssets = snapshot.data?.cast() ?? []; + if (asset is LocalAsset) { + localAssets.removeWhere((a) => a.id == (asset as LocalAsset).id); + + if (localAssets.isEmpty) { + return const SizedBox.shrink(); + } + } + + if (localAssets.isEmpty) { + return const _PropertySectionCard( + title: 'Local Assets', + properties: [_PropertyItem(label: 'Status', value: 'No local assets found with this checksum')], + ); + } + + return Column( + children: [ + if (localAssets.length > 1) + _PropertySectionCard( + title: 'Local Assets Summary', + properties: [_PropertyItem(label: 'Total Count', value: localAssets.length.toString())], + ), + ...localAssets.map((localAsset) { + return Padding( + padding: const EdgeInsets.only(top: 16), + child: _AssetPropertiesSection(asset: localAsset), + ); + }), + ], + ); + }, + ); + } +} + +class _RemoteAssetSection extends ConsumerWidget { + final BaseAsset asset; + + const _RemoteAssetSection({required this.asset}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final assetService = ref.watch(assetServiceProvider); + + if (asset is RemoteAsset) { + return const SizedBox.shrink(); + } + + return FutureBuilder( + future: assetService.getRemoteAssetByChecksum(asset.checksum!), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const _PropertySectionCard( + title: 'Remote Assets', + properties: [_PropertyItem(label: 'Status', value: 'Loading...')], + ); + } + + if (snapshot.hasError) { + return _PropertySectionCard( + title: 'Remote Assets', + properties: [_PropertyItem(label: 'Error', value: snapshot.error.toString())], + ); + } + + final remoteAsset = snapshot.data; + + if (remoteAsset == null) { + return const _PropertySectionCard( + title: 'Remote Assets', + properties: [_PropertyItem(label: 'Status', value: 'No remote asset found with this checksum')], + ); + } + + return _AssetPropertiesSection(asset: remoteAsset); + }, + ); + } +} + +class _PropertySectionCard extends StatelessWidget { + final String title; + final List<_PropertyItem> properties; + + const _PropertySectionCard({required this.title, required this.properties}); + + @override + Widget build(BuildContext context) { + return Card( + margin: const EdgeInsets.symmetric(vertical: 8), + child: Padding( + padding: const EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(title, style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold)), + const SizedBox(height: 8), + ...properties, + ], + ), + ), + ); + } +} + +class _PropertyItem extends StatelessWidget { + final String label; + final String? value; + + const _PropertyItem({required this.label, this.value}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 120, + child: Text('$label:', style: const TextStyle(fontWeight: FontWeight.w500)), + ), + Expanded( + child: Text(value ?? 'N/A', style: TextStyle(color: Theme.of(context).colorScheme.secondary)), + ), + ], + ), + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/advanced_info_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/advanced_info_action_button.widget.dart new file mode 100644 index 0000000000..170f827fdb --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/advanced_info_action_button.widget.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; + +class AdvancedInfoActionButton extends ConsumerWidget { + final ActionSource source; + + const AdvancedInfoActionButton({super.key, required this.source}); + + void _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + ref.read(actionProvider.notifier).troubleshoot(source, context); + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + return BaseActionButton( + maxWidth: 115.0, + iconData: Icons.help_outline_rounded, + label: "troubleshoot".t(context: context), + onPressed: () => _onTap(context, ref), + ); + } +} diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart index ae55fb671b..7431290ad8 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart @@ -5,6 +5,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/exif.model.dart'; +import 'package:immich_mobile/domain/models/setting.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet/sheet_location_details.widget.dart'; @@ -14,6 +15,7 @@ import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; import 'package:immich_mobile/providers/routes.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; @@ -41,6 +43,7 @@ class AssetDetailBottomSheet extends ConsumerWidget { final isInLockedView = ref.watch(inLockedViewProvider); final currentAlbum = ref.watch(currentRemoteAlbumProvider); final isArchived = asset is RemoteAsset && asset.visibility == AssetVisibility.archive; + final advancedTroubleshooting = ref.watch(settingsProvider.notifier).get(Setting.advancedTroubleshooting); final buttonContext = ActionButtonContext( asset: asset, @@ -49,6 +52,7 @@ class AssetDetailBottomSheet extends ConsumerWidget { isTrashEnabled: isTrashEnable, isInLockedView: isInLockedView, currentAlbum: currentAlbum, + advancedTroubleshooting: advancedTroubleshooting, source: ActionSource.viewer, ); @@ -122,6 +126,10 @@ class _AssetDetailBottomSheet extends ConsumerWidget { return [fNumber, exposureTime, focalLength, iso].where((spec) => spec != null && spec.isNotEmpty).join(_kSeparator); } + Future _editDateTime(BuildContext context, WidgetRef ref) async { + await ref.read(actionProvider.notifier).editDateTime(ActionSource.viewer, context); + } + @override Widget build(BuildContext context, WidgetRef ref) { final asset = ref.watch(currentAssetNotifier); @@ -132,10 +140,6 @@ class _AssetDetailBottomSheet extends ConsumerWidget { final exifInfo = ref.watch(currentAssetExifProvider).valueOrNull; final cameraTitle = _getCameraInfoTitle(exifInfo); - Future editDateTime() async { - await ref.read(actionProvider.notifier).editDateTime(ActionSource.viewer, context); - } - return SliverList.list( children: [ // Asset Date and Time @@ -143,7 +147,7 @@ class _AssetDetailBottomSheet extends ConsumerWidget { title: _getDateTime(context, asset), titleStyle: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600), trailing: asset.hasRemote ? const Icon(Icons.edit, size: 18) : null, - onTap: asset.hasRemote ? () async => await editDateTime() : null, + onTap: asset.hasRemote ? () async => await _editDateTime(context, ref) : null, ), if (exifInfo != null) _SheetAssetDescription(exif: exifInfo), const SheetPeopleDetails(), diff --git a/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart index 45c602935d..0ac0bab81d 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/edit_location_action_button.widget.dart'; diff --git a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart index f1f092d2e2..a9496423f6 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart @@ -4,6 +4,8 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/album/album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/setting.model.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/advanced_info_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; @@ -21,6 +23,7 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_ import 'package:immich_mobile/presentation/widgets/album/album_selector.widget.dart'; import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; @@ -51,6 +54,7 @@ class _GeneralBottomSheetState extends ConsumerState { Widget build(BuildContext context) { final multiselect = ref.watch(multiSelectProvider); final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); + final advancedTroubleshooting = ref.watch(settingsProvider.notifier).get(Setting.advancedTroubleshooting); Future addAssetsToAlbum(RemoteAlbum album) async { final selectedAssets = multiselect.selectedAssets; @@ -88,6 +92,9 @@ class _GeneralBottomSheetState extends ConsumerState { maxChildSize: 0.85, shouldCloseOnMinExtent: false, actions: [ + if (multiselect.selectedAssets.length == 1 && advancedTroubleshooting) ...[ + const AdvancedInfoActionButton(source: ActionSource.timeline), + ], const ShareActionButton(source: ActionSource.timeline), if (multiselect.hasRemote) ...[ const ShareLinkActionButton(source: ActionSource.timeline), diff --git a/mobile/lib/providers/backup/drift_backup.provider.dart b/mobile/lib/providers/backup/drift_backup.provider.dart index 418410de0c..21bee38004 100644 --- a/mobile/lib/providers/backup/drift_backup.provider.dart +++ b/mobile/lib/providers/backup/drift_backup.provider.dart @@ -6,11 +6,11 @@ import 'package:background_downloader/background_downloader.dart'; import 'package:collection/collection.dart'; import 'package:flutter/widgets.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; - import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/infrastructure/repositories/backup.repository.dart'; +import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/services/upload.service.dart'; import 'package:logging/logging.dart'; @@ -380,5 +380,5 @@ final driftCandidateBackupAlbumInfoProvider = FutureProvider.autoDispose.family< ref, assetId, ) { - return ref.read(backupRepositoryProvider).getSourceAlbums(assetId); + return ref.read(localAssetRepository).getSourceAlbums(assetId, backupSelection: BackupSelection.selected); }); diff --git a/mobile/lib/providers/infrastructure/action.provider.dart b/mobile/lib/providers/infrastructure/action.provider.dart index 65b4327b7a..03e2dfc6d5 100644 --- a/mobile/lib/providers/infrastructure/action.provider.dart +++ b/mobile/lib/providers/infrastructure/action.provider.dart @@ -1,3 +1,4 @@ +import 'package:auto_route/auto_route.dart'; import 'package:background_downloader/background_downloader.dart'; import 'package:flutter/material.dart'; import 'package:immich_mobile/constants/enums.dart'; @@ -6,6 +7,7 @@ import 'package:immich_mobile/models/download/livephotos_medatada.model.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/action.service.dart'; import 'package:immich_mobile/services/download.service.dart'; import 'package:immich_mobile/services/timeline.service.dart'; @@ -115,6 +117,16 @@ class ActionNotifier extends Notifier { }; } + Future troubleshoot(ActionSource source, BuildContext context) async { + final assets = _getAssets(source); + if (assets.length > 1) { + return ActionResult(count: assets.length, success: false, error: 'Cannot troubleshoot multiple assets'); + } + context.pushRoute(AssetTroubleshootRoute(asset: assets.first)); + + return ActionResult(count: assets.length, success: true); + } + Future shareLink(ActionSource source, BuildContext context) async { final ids = _getRemoteIdsForSource(source); try { diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index b289cc3225..14af0b2600 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -86,6 +86,7 @@ import 'package:immich_mobile/presentation/pages/drift_album.page.dart'; import 'package:immich_mobile/presentation/pages/drift_album_options.page.dart'; import 'package:immich_mobile/presentation/pages/drift_archive.page.dart'; import 'package:immich_mobile/presentation/pages/drift_asset_selection_timeline.page.dart'; +import 'package:immich_mobile/presentation/pages/drift_asset_troubleshoot.page.dart'; import 'package:immich_mobile/presentation/pages/drift_create_album.page.dart'; import 'package:immich_mobile/presentation/pages/drift_favorite.page.dart'; import 'package:immich_mobile/presentation/pages/drift_library.page.dart'; @@ -343,6 +344,7 @@ class AppRouter extends RootStackRouter { AutoRoute(page: DriftFilterImageRoute.page), AutoRoute(page: DriftActivitiesRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: DriftBackupAssetDetailRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: AssetTroubleshootRoute.page, guards: [_authGuard, _duplicateGuard]), // required to handle all deeplinks in deep_link.service.dart // auto_route_library#1722 RedirectRoute(path: '*', redirectTo: '/'), diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index 84f2685ab5..4d50a1bba5 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -403,6 +403,43 @@ class ArchiveRoute extends PageRouteInfo { ); } +/// generated route for +/// [AssetTroubleshootPage] +class AssetTroubleshootRoute extends PageRouteInfo { + AssetTroubleshootRoute({ + Key? key, + required BaseAsset asset, + List? children, + }) : super( + AssetTroubleshootRoute.name, + args: AssetTroubleshootRouteArgs(key: key, asset: asset), + initialChildren: children, + ); + + static const String name = 'AssetTroubleshootRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + final args = data.argsAs(); + return AssetTroubleshootPage(key: args.key, asset: args.asset); + }, + ); +} + +class AssetTroubleshootRouteArgs { + const AssetTroubleshootRouteArgs({this.key, required this.asset}); + + final Key? key; + + final BaseAsset asset; + + @override + String toString() { + return 'AssetTroubleshootRouteArgs{key: $key, asset: $asset}'; + } +} + /// generated route for /// [AssetViewerPage] class AssetViewerRoute extends PageRouteInfo { diff --git a/mobile/lib/utils/action_button.utils.dart b/mobile/lib/utils/action_button.utils.dart index 10facea9a2..4dfc0398bd 100644 --- a/mobile/lib/utils/action_button.utils.dart +++ b/mobile/lib/utils/action_button.utils.dart @@ -1,6 +1,8 @@ import 'package:flutter/widgets.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/album/album.model.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/advanced_info_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; @@ -15,7 +17,6 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/share_link_act import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/unarchive_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; -import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; class ActionButtonContext { final BaseAsset asset; @@ -24,6 +25,7 @@ class ActionButtonContext { final bool isTrashEnabled; final bool isInLockedView; final RemoteAlbum? currentAlbum; + final bool advancedTroubleshooting; final ActionSource source; const ActionButtonContext({ @@ -33,11 +35,13 @@ class ActionButtonContext { required this.isTrashEnabled, required this.isInLockedView, required this.currentAlbum, + required this.advancedTroubleshooting, required this.source, }); } enum ActionButtonType { + advancedInfo, share, shareLink, archive, @@ -55,6 +59,7 @@ enum ActionButtonType { bool shouldShow(ActionButtonContext context) { return switch (this) { + ActionButtonType.advancedInfo => context.advancedTroubleshooting, ActionButtonType.share => true, ActionButtonType.shareLink => !context.isInLockedView && // @@ -115,6 +120,7 @@ enum ActionButtonType { Widget buildButton(ActionButtonContext context) { return switch (this) { + ActionButtonType.advancedInfo => AdvancedInfoActionButton(source: context.source), ActionButtonType.share => ShareActionButton(source: context.source), ActionButtonType.shareLink => ShareLinkActionButton(source: context.source), ActionButtonType.archive => ArchiveActionButton(source: context.source), @@ -138,6 +144,7 @@ enum ActionButtonType { class ActionButtonBuilder { static const List _actionTypes = [ + ActionButtonType.advancedInfo, ActionButtonType.share, ActionButtonType.shareLink, ActionButtonType.likeActivity, diff --git a/mobile/test/utils/action_button_utils_test.dart b/mobile/test/utils/action_button_utils_test.dart index 3cb77c0b33..497246e2a1 100644 --- a/mobile/test/utils/action_button_utils_test.dart +++ b/mobile/test/utils/action_button_utils_test.dart @@ -81,6 +81,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -110,6 +111,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -124,6 +126,7 @@ void main() { isTrashEnabled: true, isInLockedView: true, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -141,6 +144,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -156,6 +160,7 @@ void main() { isTrashEnabled: true, isInLockedView: true, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -171,6 +176,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -188,6 +194,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -203,6 +210,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -218,6 +226,7 @@ void main() { isTrashEnabled: true, isInLockedView: true, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -233,6 +242,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -248,6 +258,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -265,6 +276,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -280,6 +292,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -295,6 +308,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -312,6 +326,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -327,6 +342,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -342,6 +358,7 @@ void main() { isTrashEnabled: true, isInLockedView: true, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -359,6 +376,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -374,6 +392,7 @@ void main() { isTrashEnabled: false, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -391,6 +410,7 @@ void main() { isTrashEnabled: false, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -406,6 +426,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -423,6 +444,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -440,6 +462,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -457,6 +480,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -472,6 +496,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -489,6 +514,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -506,6 +532,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: album, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -520,6 +547,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -537,6 +565,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: album, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -552,6 +581,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: album, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -567,6 +597,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: album, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -581,12 +612,45 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); expect(ActionButtonType.likeActivity.shouldShow(context), isFalse); }); }); + + group('advancedTroubleshooting button', () { + test('should show when in advanced troubleshooting mode', () { + final context = ActionButtonContext( + asset: mergedAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: true, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.advancedInfo.shouldShow(context), isTrue); + }); + + test('should not show when not in advanced troubleshooting mode', () { + final context = ActionButtonContext( + asset: mergedAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.advancedInfo.shouldShow(context), isFalse); + }); + }); }); group('ActionButtonType.buildButton', () { @@ -602,6 +666,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); }); @@ -617,6 +682,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: album, + advancedTroubleshooting: false, source: ActionSource.timeline, ); final widget = buttonType.buildButton(contextWithAlbum); @@ -639,6 +705,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -658,6 +725,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: album, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -675,6 +743,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -693,6 +762,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); @@ -705,6 +775,7 @@ void main() { isTrashEnabled: true, isInLockedView: false, currentAlbum: null, + advancedTroubleshooting: false, source: ActionSource.timeline, ); From e18e4c59627e22366abdb0ce4974713b677ece9b Mon Sep 17 00:00:00 2001 From: Noel S Date: Wed, 10 Sep 2025 06:39:36 -0700 Subject: [PATCH 457/748] fix(mobile): Change read-only mode activation method to remove double click lag (#21743) * Change activation method to long press * Update text to new method --- i18n/en.json | 2 +- .../widgets/common/app_bar_dialog/app_bar_profile_info.dart | 4 ++-- mobile/lib/widgets/common/immich_sliver_app_bar.dart | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/i18n/en.json b/i18n/en.json index e9f965d677..4d940ffadc 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1517,7 +1517,7 @@ "profile_drawer_client_out_of_date_minor": "Mobile App is out of date. Please update to the latest minor version.", "profile_drawer_client_server_up_to_date": "Client and Server are up-to-date", "profile_drawer_github": "GitHub", - "profile_drawer_readonly_mode": "Read-only mode enabled. Double-tap the user avatar icon to exit.", + "profile_drawer_readonly_mode": "Read-only mode enabled. Long-press the user avatar icon to exit.", "profile_drawer_server_out_of_date_major": "Server is out of date. Please update to the latest major version.", "profile_drawer_server_out_of_date_minor": "Server is out of date. Please update to the latest minor version.", "profile_image_of_user": "Profile image of {user}", diff --git a/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart b/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart index a9c7a467c2..00366ca580 100644 --- a/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart +++ b/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart @@ -90,11 +90,11 @@ class AppBarProfileInfoBox extends HookConsumerWidget { minLeadingWidth: 50, leading: GestureDetector( onTap: pickUserProfileImage, - onDoubleTap: toggleReadonlyMode, + onLongPress: toggleReadonlyMode, child: Stack( clipBehavior: Clip.none, children: [ - buildUserProfileImage(), + AbsorbPointer(child: buildUserProfileImage()), if (!isReadonlyModeEnabled) Positioned( bottom: -5, diff --git a/mobile/lib/widgets/common/immich_sliver_app_bar.dart b/mobile/lib/widgets/common/immich_sliver_app_bar.dart index ee111851ad..378a31f33e 100644 --- a/mobile/lib/widgets/common/immich_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/immich_sliver_app_bar.dart @@ -157,7 +157,7 @@ class _ProfileIndicator extends ConsumerWidget { return InkWell( onTap: () => showDialog(context: context, useRootNavigator: false, builder: (ctx) => const ImmichAppBarDialog()), - onDoubleTap: () => toggleReadonlyMode(), + onLongPress: () => toggleReadonlyMode(), borderRadius: const BorderRadius.all(Radius.circular(12)), child: Badge( label: Container( @@ -173,7 +173,7 @@ class _ProfileIndicator extends ConsumerWidget { ? const Icon(Icons.face_outlined, size: widgetSize) : Semantics( label: "logged_in_as".tr(namedArgs: {"user": user.name}), - child: UserCircleAvatar(radius: 17, size: 31, user: user), + child: AbsorbPointer(child: UserCircleAvatar(radius: 17, size: 31, user: user)), ), ), ); From 027dab14872b67f4f7a46f789edef769c935aa22 Mon Sep 17 00:00:00 2001 From: Yaros Date: Wed, 10 Sep 2025 15:47:38 +0200 Subject: [PATCH 458/748] fix(web): memory viewer arrow navigation (#19400) * fix(web): memory viewer jumps down on arrow keys * fix pnpm lockfile --------- Co-authored-by: Jason Rasmussen --- pnpm-lock.yaml | 383 +++++++++--------- .../memory-page/memory-viewer.svelte | 1 + .../gallery-viewer/gallery-viewer.svelte | 10 +- 3 files changed, 190 insertions(+), 204 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2e87eff590..0fb05f0bb8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -109,10 +109,10 @@ importers: version: 8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) vite: specifier: ^7.0.0 - version: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + version: 7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vite-tsconfig-paths: specifier: ^5.0.0 - version: 5.1.4(typescript@5.9.2)(vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 5.1.4(typescript@5.9.2)(vite@7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) vitest: specifier: ^3.0.0 version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.1)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) @@ -667,10 +667,10 @@ importers: version: 8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) unplugin-swc: specifier: ^1.4.5 - version: 1.5.5(@swc/core@1.13.3(@swc/helpers@0.5.17))(rollup@4.50.1) + version: 1.5.5(@swc/core@1.13.3(@swc/helpers@0.5.17))(rollup@4.46.3) vite-tsconfig-paths: specifier: ^5.0.0 - version: 5.1.4(typescript@5.9.2)(vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 5.1.4(typescript@5.9.2)(vite@7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) vitest: specifier: ^3.0.0 version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.1)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2(encoding@0.1.13)))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) @@ -794,25 +794,25 @@ importers: version: 3.1.2 '@sveltejs/adapter-static': specifier: ^3.0.8 - version: 3.0.9(@sveltejs/kit@2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))) + version: 3.0.9(@sveltejs/kit@2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))) '@sveltejs/enhanced-img': specifier: ^0.8.0 - version: 0.8.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(rollup@4.50.1)(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 0.8.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(rollup@4.46.3)(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@sveltejs/kit': specifier: ^2.27.1 - version: 2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@sveltejs/vite-plugin-svelte': specifier: 6.1.2 - version: 6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@tailwindcss/vite': specifier: ^4.1.7 - version: 4.1.12(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 4.1.12(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@testing-library/jest-dom': specifier: ^6.4.2 version: 6.7.0 '@testing-library/svelte': specifier: ^5.2.8 - version: 5.2.8(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 5.2.8(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@testing-library/user-event': specifier: ^14.5.2 version: 14.6.1(@testing-library/dom@10.4.0) @@ -878,7 +878,7 @@ importers: version: 3.4.0(prettier@3.6.2)(svelte@5.35.5) rollup-plugin-visualizer: specifier: ^6.0.0 - version: 6.0.3(rollup@4.50.1) + version: 6.0.3(rollup@4.46.3) svelte: specifier: 5.35.5 version: 5.35.5 @@ -899,7 +899,7 @@ importers: version: 8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) vite: specifier: ^7.1.2 - version: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + version: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vitest: specifier: ^3.0.0 version: 3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) @@ -1561,12 +1561,8 @@ packages: resolution: {integrity: sha512-vDVrlmRAY8z9Ul/HxT+8ceAru95LQgkSKiXkSYZvqtbkPSfhZJgpRp45Cldbh1GJ1kxzQkI70AqyrTI58KpaWQ==} engines: {node: '>=6.9.0'} - '@babel/runtime@7.28.4': - resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} - engines: {node: '>=6.9.0'} - - '@babel/runtime@7.28.4': - resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + '@babel/runtime@7.28.3': + resolution: {integrity: sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==} engines: {node: '>=6.9.0'} '@babel/template@7.27.2': @@ -2409,8 +2405,8 @@ packages: '@floating-ui/core@1.7.3': resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} - '@floating-ui/dom@1.7.4': - resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} + '@floating-ui/dom@1.7.3': + resolution: {integrity: sha512-uZA413QEpNuhtb3/iIKoYMSK07keHPYeXF02Zhd6e213j+d1NamLix/mCLxBUDW/Gx52sPH2m+chlUsyaBs/Ag==} '@floating-ui/utils@0.2.10': resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} @@ -3542,108 +3538,103 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.50.1': - resolution: {integrity: sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag==} + '@rollup/rollup-android-arm-eabi@4.46.3': + resolution: {integrity: sha512-UmTdvXnLlqQNOCJnyksjPs1G4GqXNGW1LrzCe8+8QoaLhhDeTXYBgJ3k6x61WIhlHX2U+VzEJ55TtIjR/HTySA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.50.1': - resolution: {integrity: sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw==} + '@rollup/rollup-android-arm64@4.46.3': + resolution: {integrity: sha512-8NoxqLpXm7VyeI0ocidh335D6OKT0UJ6fHdnIxf3+6oOerZZc+O7r+UhvROji6OspyPm+rrIdb1gTXtVIqn+Sg==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.50.1': - resolution: {integrity: sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw==} + '@rollup/rollup-darwin-arm64@4.46.3': + resolution: {integrity: sha512-csnNavqZVs1+7/hUKtgjMECsNG2cdB8F7XBHP6FfQjqhjF8rzMzb3SLyy/1BG7YSfQ+bG75Ph7DyedbUqwq1rA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.50.1': - resolution: {integrity: sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw==} + '@rollup/rollup-darwin-x64@4.46.3': + resolution: {integrity: sha512-r2MXNjbuYabSIX5yQqnT8SGSQ26XQc8fmp6UhlYJd95PZJkQD1u82fWP7HqvGUf33IsOC6qsiV+vcuD4SDP6iw==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.50.1': - resolution: {integrity: sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA==} + '@rollup/rollup-freebsd-arm64@4.46.3': + resolution: {integrity: sha512-uluObTmgPJDuJh9xqxyr7MV61Imq+0IvVsAlWyvxAaBSNzCcmZlhfYcRhCdMaCsy46ccZa7vtDDripgs9Jkqsw==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.50.1': - resolution: {integrity: sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ==} + '@rollup/rollup-freebsd-x64@4.46.3': + resolution: {integrity: sha512-AVJXEq9RVHQnejdbFvh1eWEoobohUYN3nqJIPI4mNTMpsyYN01VvcAClxflyk2HIxvLpRcRggpX1m9hkXkpC/A==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.50.1': - resolution: {integrity: sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg==} + '@rollup/rollup-linux-arm-gnueabihf@4.46.3': + resolution: {integrity: sha512-byyflM+huiwHlKi7VHLAYTKr67X199+V+mt1iRgJenAI594vcmGGddWlu6eHujmcdl6TqSNnvqaXJqZdnEWRGA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.50.1': - resolution: {integrity: sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw==} + '@rollup/rollup-linux-arm-musleabihf@4.46.3': + resolution: {integrity: sha512-aLm3NMIjr4Y9LklrH5cu7yybBqoVCdr4Nvnm8WB7PKCn34fMCGypVNpGK0JQWdPAzR/FnoEoFtlRqZbBBLhVoQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.50.1': - resolution: {integrity: sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw==} + '@rollup/rollup-linux-arm64-gnu@4.46.3': + resolution: {integrity: sha512-VtilE6eznJRDIoFOzaagQodUksTEfLIsvXymS+UdJiSXrPW7Ai+WG4uapAc3F7Hgs791TwdGh4xyOzbuzIZrnw==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.50.1': - resolution: {integrity: sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w==} + '@rollup/rollup-linux-arm64-musl@4.46.3': + resolution: {integrity: sha512-dG3JuS6+cRAL0GQ925Vppafi0qwZnkHdPeuZIxIPXqkCLP02l7ka+OCyBoDEv8S+nKHxfjvjW4OZ7hTdHkx8/w==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.50.1': - resolution: {integrity: sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q==} + '@rollup/rollup-linux-loongarch64-gnu@4.46.3': + resolution: {integrity: sha512-iU8DxnxEKJptf8Vcx4XvAUdpkZfaz0KWfRrnIRrOndL0SvzEte+MTM7nDH4A2Now4FvTZ01yFAgj6TX/mZl8hQ==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.50.1': - resolution: {integrity: sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q==} + '@rollup/rollup-linux-ppc64-gnu@4.46.3': + resolution: {integrity: sha512-VrQZp9tkk0yozJoQvQcqlWiqaPnLM6uY1qPYXvukKePb0fqaiQtOdMJSxNFUZFsGw5oA5vvVokjHrx8a9Qsz2A==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.50.1': - resolution: {integrity: sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ==} + '@rollup/rollup-linux-riscv64-gnu@4.46.3': + resolution: {integrity: sha512-uf2eucWSUb+M7b0poZ/08LsbcRgaDYL8NCGjUeFMwCWFwOuFcZ8D9ayPl25P3pl+D2FH45EbHdfyUesQ2Lt9wA==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.50.1': - resolution: {integrity: sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg==} + '@rollup/rollup-linux-riscv64-musl@4.46.3': + resolution: {integrity: sha512-7tnUcDvN8DHm/9ra+/nF7lLzYHDeODKKKrh6JmZejbh1FnCNZS8zMkZY5J4sEipy2OW1d1Ncc4gNHUd0DLqkSg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.50.1': - resolution: {integrity: sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg==} + '@rollup/rollup-linux-s390x-gnu@4.46.3': + resolution: {integrity: sha512-MUpAOallJim8CsJK+4Lc9tQzlfPbHxWDrGXZm2z6biaadNpvh3a5ewcdat478W+tXDoUiHwErX/dOql7ETcLqg==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.50.1': - resolution: {integrity: sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA==} + '@rollup/rollup-linux-x64-gnu@4.46.3': + resolution: {integrity: sha512-F42IgZI4JicE2vM2PWCe0N5mR5vR0gIdORPqhGQ32/u1S1v3kLtbZ0C/mi9FFk7C5T0PgdeyWEPajPjaUpyoKg==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.50.1': - resolution: {integrity: sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg==} + '@rollup/rollup-linux-x64-musl@4.46.3': + resolution: {integrity: sha512-oLc+JrwwvbimJUInzx56Q3ujL3Kkhxehg7O1gWAYzm8hImCd5ld1F2Gry5YDjR21MNb5WCKhC9hXgU7rRlyegQ==} cpu: [x64] os: [linux] - '@rollup/rollup-openharmony-arm64@4.50.1': - resolution: {integrity: sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA==} - cpu: [arm64] - os: [openharmony] - - '@rollup/rollup-win32-arm64-msvc@4.50.1': - resolution: {integrity: sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ==} + '@rollup/rollup-win32-arm64-msvc@4.46.3': + resolution: {integrity: sha512-lOrQ+BVRstruD1fkWg9yjmumhowR0oLAAzavB7yFSaGltY8klttmZtCLvOXCmGE9mLIn8IBV/IFrQOWz5xbFPg==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.50.1': - resolution: {integrity: sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A==} + '@rollup/rollup-win32-ia32-msvc@4.46.3': + resolution: {integrity: sha512-vvrVKPRS4GduGR7VMH8EylCBqsDcw6U+/0nPDuIjXQRbHJc6xOBj+frx8ksfZAh6+Fptw5wHrN7etlMmQnPQVg==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.50.1': - resolution: {integrity: sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA==} + '@rollup/rollup-win32-x64-msvc@4.46.3': + resolution: {integrity: sha512-fi3cPxCnu3ZeM3EwKZPgXbWoGzm2XHgB/WShKI81uj8wG0+laobmqy5wbgEwzstlbLu4MyO8C19FyhhWseYKNQ==} cpu: [x64] os: [win32] @@ -4930,8 +4921,8 @@ packages: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} - bits-ui@2.9.6: - resolution: {integrity: sha512-OzHktsQRsIz/hIMk5VwHo96Wpp/KY68q/ebUPUzTbvuFBrALB/X+QvO4KLgdczj5dfb3xHs9zpWq8yMH8ZbZlA==} + bits-ui@2.9.4: + resolution: {integrity: sha512-Cqn685P6DDuEyBZT/CWMyS5+8JAnYbctvoEVPcmiut+HUpG3SozVgjoDaUib5VG4ZYUKEi1FPwHxiXo9c6J0PA==} engines: {node: '>=20'} peerDependencies: '@internationalized/date': ^3.8.1 @@ -8369,11 +8360,8 @@ packages: peerDependencies: webpack: ^4.0.0 || ^5.0.0 - nwsapi@2.2.22: - resolution: {integrity: sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==} - - nwsapi@2.2.22: - resolution: {integrity: sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==} + nwsapi@2.2.21: + resolution: {integrity: sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA==} nypm@0.6.0: resolution: {integrity: sha512-mn8wBFV9G9+UFHIrq+pZ2r2zL4aPau/by3kJb3cM7+5tQHMt6HGQB8FDIeKFYp8o0D2pnH6nVsO88N4AmUxIWg==} @@ -9758,8 +9746,8 @@ packages: rollup: optional: true - rollup@4.50.1: - resolution: {integrity: sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==} + rollup@4.46.3: + resolution: {integrity: sha512-RZn2XTjXb8t5g13f5YclGoilU/kwT696DIkY3sywjdZidNSi3+vseaQov7D7BZXVJCPv3pDWUN69C78GGbXsKw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -10524,8 +10512,8 @@ packages: tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - tinyglobby@0.2.15: - resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + tinyglobby@0.2.14: + resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} engines: {node: '>=12.0.0'} tinypool@1.1.1: @@ -10962,8 +10950,8 @@ packages: vite: optional: true - vite@7.1.5: - resolution: {integrity: sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==} + vite@7.1.2: + resolution: {integrity: sha512-J0SQBPlQiEXAF7tajiH+rUooJPo0l8KQgyg4/aMunNtrOa7bwuZJsJbDWzeljqQpgftxuq5yNJxQ91O9ts29UQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -12242,9 +12230,7 @@ snapshots: dependencies: core-js-pure: 3.43.0 - '@babel/runtime@7.28.4': {} - - '@babel/runtime@7.28.4': {} + '@babel/runtime@7.28.3': {} '@babel/template@7.27.2': dependencies: @@ -12566,7 +12552,7 @@ snapshots: '@babel/preset-env': 7.27.2(@babel/core@7.27.7) '@babel/preset-react': 7.27.1(@babel/core@7.27.7) '@babel/preset-typescript': 7.27.1(@babel/core@7.27.7) - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.3 '@babel/runtime-corejs3': 7.27.6 '@babel/traverse': 7.28.3 '@docusaurus/logger': 3.8.1 @@ -13553,7 +13539,7 @@ snapshots: dependencies: '@floating-ui/utils': 0.2.10 - '@floating-ui/dom@1.7.4': + '@floating-ui/dom@1.7.3': dependencies: '@floating-ui/core': 1.7.3 '@floating-ui/utils': 0.2.10 @@ -13712,7 +13698,7 @@ snapshots: '@immich/ui@0.27.1(@internationalized/date@3.8.2)(svelte@5.35.5)': dependencies: '@mdi/js': 7.4.47 - bits-ui: 2.9.6(@internationalized/date@3.8.2)(svelte@5.35.5) + bits-ui: 2.9.4(@internationalized/date@3.8.2)(svelte@5.35.5) simple-icons: 15.14.0 svelte: 5.35.5 tailwind-merge: 3.3.1 @@ -14802,75 +14788,72 @@ snapshots: react: 18.3.1 react-redux: 7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@rollup/pluginutils@5.2.0(rollup@4.50.1)': + '@rollup/pluginutils@5.2.0(rollup@4.46.3)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.3 optionalDependencies: - rollup: 4.50.1 + rollup: 4.46.3 - '@rollup/rollup-android-arm-eabi@4.50.1': + '@rollup/rollup-android-arm-eabi@4.46.3': optional: true - '@rollup/rollup-android-arm64@4.50.1': + '@rollup/rollup-android-arm64@4.46.3': optional: true - '@rollup/rollup-darwin-arm64@4.50.1': + '@rollup/rollup-darwin-arm64@4.46.3': optional: true - '@rollup/rollup-darwin-x64@4.50.1': + '@rollup/rollup-darwin-x64@4.46.3': optional: true - '@rollup/rollup-freebsd-arm64@4.50.1': + '@rollup/rollup-freebsd-arm64@4.46.3': optional: true - '@rollup/rollup-freebsd-x64@4.50.1': + '@rollup/rollup-freebsd-x64@4.46.3': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.50.1': + '@rollup/rollup-linux-arm-gnueabihf@4.46.3': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.50.1': + '@rollup/rollup-linux-arm-musleabihf@4.46.3': optional: true - '@rollup/rollup-linux-arm64-gnu@4.50.1': + '@rollup/rollup-linux-arm64-gnu@4.46.3': optional: true - '@rollup/rollup-linux-arm64-musl@4.50.1': + '@rollup/rollup-linux-arm64-musl@4.46.3': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.50.1': + '@rollup/rollup-linux-loongarch64-gnu@4.46.3': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.50.1': + '@rollup/rollup-linux-ppc64-gnu@4.46.3': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.50.1': + '@rollup/rollup-linux-riscv64-gnu@4.46.3': optional: true - '@rollup/rollup-linux-riscv64-musl@4.50.1': + '@rollup/rollup-linux-riscv64-musl@4.46.3': optional: true - '@rollup/rollup-linux-s390x-gnu@4.50.1': + '@rollup/rollup-linux-s390x-gnu@4.46.3': optional: true - '@rollup/rollup-linux-x64-gnu@4.50.1': + '@rollup/rollup-linux-x64-gnu@4.46.3': optional: true - '@rollup/rollup-linux-x64-musl@4.50.1': + '@rollup/rollup-linux-x64-musl@4.46.3': optional: true - '@rollup/rollup-openharmony-arm64@4.50.1': + '@rollup/rollup-win32-arm64-msvc@4.46.3': optional: true - '@rollup/rollup-win32-arm64-msvc@4.50.1': + '@rollup/rollup-win32-ia32-msvc@4.46.3': optional: true - '@rollup/rollup-win32-ia32-msvc@4.50.1': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.50.1': + '@rollup/rollup-win32-x64-msvc@4.46.3': optional: true '@scarf/scarf@1.4.0': {} @@ -14896,7 +14879,7 @@ snapshots: '@slorber/react-helmet-async@1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.3 invariant: 2.2.4 prop-types: 15.8.1 react: 18.3.1 @@ -14927,29 +14910,29 @@ snapshots: dependencies: acorn: 8.15.0 - '@sveltejs/adapter-static@3.0.9(@sveltejs/kit@2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))': + '@sveltejs/adapter-static@3.0.9(@sveltejs/kit@2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))': dependencies: - '@sveltejs/kit': 2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@sveltejs/kit': 2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) - '@sveltejs/enhanced-img@0.8.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(rollup@4.50.1)(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@sveltejs/enhanced-img@0.8.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(rollup@4.46.3)(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: - '@sveltejs/vite-plugin-svelte': 6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@sveltejs/vite-plugin-svelte': 6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) magic-string: 0.30.17 sharp: 0.34.3 svelte: 5.35.5 svelte-parse-markup: 0.1.5(svelte@5.35.5) - vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) - vite-imagetools: 8.0.0(rollup@4.50.1) + vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite-imagetools: 8.0.0(rollup@4.46.3) zimmerframe: 1.1.2 transitivePeerDependencies: - rollup - supports-color - '@sveltejs/kit@2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@sveltejs/kit@2.27.1(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@standard-schema/spec': 1.0.0 '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0) - '@sveltejs/vite-plugin-svelte': 6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@sveltejs/vite-plugin-svelte': 6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@types/cookie': 0.6.0 acorn: 8.15.0 cookie: 0.6.0 @@ -14962,27 +14945,27 @@ snapshots: set-cookie-parser: 2.7.1 sirv: 3.0.1 svelte: 5.35.5 - vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) - '@sveltejs/vite-plugin-svelte-inspector@5.0.0(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@sveltejs/vite-plugin-svelte-inspector@5.0.0(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: - '@sveltejs/vite-plugin-svelte': 6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@sveltejs/vite-plugin-svelte': 6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) debug: 4.4.1 svelte: 5.35.5 - vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 5.0.0(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@sveltejs/vite-plugin-svelte-inspector': 5.0.0(@sveltejs/vite-plugin-svelte@6.1.2(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) debug: 4.4.1 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.17 svelte: 5.35.5 - vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) - vitefu: 1.1.1(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vitefu: 1.1.1(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) transitivePeerDependencies: - supports-color @@ -15204,17 +15187,17 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.1.12 '@tailwindcss/oxide-win32-x64-msvc': 4.1.12 - '@tailwindcss/vite@4.1.12(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@tailwindcss/vite@4.1.12(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@tailwindcss/node': 4.1.12 '@tailwindcss/oxide': 4.1.12 tailwindcss: 4.1.12 - vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) '@testing-library/dom@10.4.0': dependencies: '@babel/code-frame': 7.27.1 - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.3 '@types/aria-query': 5.0.4 aria-query: 5.3.0 chalk: 4.1.2 @@ -15231,12 +15214,12 @@ snapshots: picocolors: 1.1.1 redent: 3.0.0 - '@testing-library/svelte@5.2.8(svelte@5.35.5)(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@testing-library/svelte@5.2.8(svelte@5.35.5)(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@testing-library/dom': 10.4.0 svelte: 5.35.5 optionalDependencies: - vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.0)': @@ -15891,21 +15874,21 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@vitest/mocker@3.2.4(vite@7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) - '@vitest/mocker@3.2.4(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@vitest/mocker@3.2.4(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) '@vitest/pretty-format@3.2.4': dependencies: @@ -16376,10 +16359,10 @@ snapshots: binary-extensions@2.3.0: {} - bits-ui@2.9.6(@internationalized/date@3.8.2)(svelte@5.35.5): + bits-ui@2.9.4(@internationalized/date@3.8.2)(svelte@5.35.5): dependencies: '@floating-ui/core': 1.7.3 - '@floating-ui/dom': 1.7.4 + '@floating-ui/dom': 1.7.3 '@internationalized/date': 3.8.2 esm-env: 1.2.2 runed: 0.29.2(svelte@5.35.5) @@ -18804,7 +18787,7 @@ snapshots: history@4.10.1: dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.3 loose-envify: 1.4.0 resolve-pathname: 3.0.0 tiny-invariant: 1.3.3 @@ -19331,7 +19314,7 @@ snapshots: http-proxy-agent: 5.0.0 https-proxy-agent: 5.0.1 is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.22 + nwsapi: 2.2.21 parse5: 7.3.0 saxes: 6.0.0 symbol-tree: 3.2.4 @@ -19360,7 +19343,7 @@ snapshots: http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.22 + nwsapi: 2.2.21 parse5: 7.3.0 rrweb-cssom: 0.8.0 saxes: 6.0.0 @@ -19390,7 +19373,7 @@ snapshots: http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.22 + nwsapi: 2.2.21 parse5: 7.3.0 rrweb-cssom: 0.8.0 saxes: 6.0.0 @@ -20627,7 +20610,7 @@ snapshots: proc-log: 5.0.0 semver: 7.7.2 tar: 7.4.3 - tinyglobby: 0.2.15 + tinyglobby: 0.2.14 which: 5.0.0 transitivePeerDependencies: - supports-color @@ -20685,10 +20668,7 @@ snapshots: schema-utils: 3.3.0 webpack: 5.100.2 - nwsapi@2.2.22: - optional: true - - nwsapi@2.2.22: + nwsapi@2.2.21: optional: true nypm@0.6.0: @@ -21886,7 +21866,7 @@ snapshots: react-loadable-ssr-addon-v5-slorber@1.0.1(@docusaurus/react-loadable@6.0.0(react@18.3.1))(webpack@5.100.2): dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.3 react-loadable: '@docusaurus/react-loadable@6.0.0(react@18.3.1)' webpack: 5.100.2 @@ -21898,7 +21878,7 @@ snapshots: react-redux@7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.3 '@types/react-redux': 7.1.34 hoist-non-react-statics: 3.3.2 loose-envify: 1.4.0 @@ -21910,13 +21890,13 @@ snapshots: react-router-config@5.1.1(react-router@5.3.4(react@18.3.1))(react@18.3.1): dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.3 react: 18.3.1 react-router: 5.3.4(react@18.3.1) react-router-dom@5.3.4(react@18.3.1): dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.3 history: 4.10.1 loose-envify: 1.4.0 prop-types: 15.8.1 @@ -21927,7 +21907,7 @@ snapshots: react-router@5.3.4(react@18.3.1): dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.3 history: 4.10.1 hoist-non-react-statics: 3.3.2 loose-envify: 1.4.0 @@ -22037,7 +22017,7 @@ snapshots: redux@4.2.1: dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.3 reflect-metadata@0.2.2: {} @@ -22248,40 +22228,39 @@ snapshots: robust-predicates@3.0.2: {} - rollup-plugin-visualizer@6.0.3(rollup@4.50.1): + rollup-plugin-visualizer@6.0.3(rollup@4.46.3): dependencies: open: 8.4.2 picomatch: 4.0.3 source-map: 0.7.4 yargs: 17.7.2 optionalDependencies: - rollup: 4.50.1 + rollup: 4.46.3 - rollup@4.50.1: + rollup@4.46.3: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.50.1 - '@rollup/rollup-android-arm64': 4.50.1 - '@rollup/rollup-darwin-arm64': 4.50.1 - '@rollup/rollup-darwin-x64': 4.50.1 - '@rollup/rollup-freebsd-arm64': 4.50.1 - '@rollup/rollup-freebsd-x64': 4.50.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.50.1 - '@rollup/rollup-linux-arm-musleabihf': 4.50.1 - '@rollup/rollup-linux-arm64-gnu': 4.50.1 - '@rollup/rollup-linux-arm64-musl': 4.50.1 - '@rollup/rollup-linux-loongarch64-gnu': 4.50.1 - '@rollup/rollup-linux-ppc64-gnu': 4.50.1 - '@rollup/rollup-linux-riscv64-gnu': 4.50.1 - '@rollup/rollup-linux-riscv64-musl': 4.50.1 - '@rollup/rollup-linux-s390x-gnu': 4.50.1 - '@rollup/rollup-linux-x64-gnu': 4.50.1 - '@rollup/rollup-linux-x64-musl': 4.50.1 - '@rollup/rollup-openharmony-arm64': 4.50.1 - '@rollup/rollup-win32-arm64-msvc': 4.50.1 - '@rollup/rollup-win32-ia32-msvc': 4.50.1 - '@rollup/rollup-win32-x64-msvc': 4.50.1 + '@rollup/rollup-android-arm-eabi': 4.46.3 + '@rollup/rollup-android-arm64': 4.46.3 + '@rollup/rollup-darwin-arm64': 4.46.3 + '@rollup/rollup-darwin-x64': 4.46.3 + '@rollup/rollup-freebsd-arm64': 4.46.3 + '@rollup/rollup-freebsd-x64': 4.46.3 + '@rollup/rollup-linux-arm-gnueabihf': 4.46.3 + '@rollup/rollup-linux-arm-musleabihf': 4.46.3 + '@rollup/rollup-linux-arm64-gnu': 4.46.3 + '@rollup/rollup-linux-arm64-musl': 4.46.3 + '@rollup/rollup-linux-loongarch64-gnu': 4.46.3 + '@rollup/rollup-linux-ppc64-gnu': 4.46.3 + '@rollup/rollup-linux-riscv64-gnu': 4.46.3 + '@rollup/rollup-linux-riscv64-musl': 4.46.3 + '@rollup/rollup-linux-s390x-gnu': 4.46.3 + '@rollup/rollup-linux-x64-gnu': 4.46.3 + '@rollup/rollup-linux-x64-musl': 4.46.3 + '@rollup/rollup-win32-arm64-msvc': 4.46.3 + '@rollup/rollup-win32-ia32-msvc': 4.46.3 + '@rollup/rollup-win32-x64-msvc': 4.46.3 fsevents: 2.3.3 router@2.2.0: @@ -23302,7 +23281,7 @@ snapshots: tinyexec@0.3.2: {} - tinyglobby@0.2.15: + tinyglobby@0.2.14: dependencies: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 @@ -23592,9 +23571,9 @@ snapshots: unpipe@1.0.0: {} - unplugin-swc@1.5.5(@swc/core@1.13.3(@swc/helpers@0.5.17))(rollup@4.50.1): + unplugin-swc@1.5.5(@swc/core@1.13.3(@swc/helpers@0.5.17))(rollup@4.46.3): dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.50.1) + '@rollup/pluginutils': 5.2.0(rollup@4.46.3) '@swc/core': 1.13.3(@swc/helpers@0.5.17) load-tsconfig: 0.2.5 unplugin: 2.3.5 @@ -23734,9 +23713,9 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.2 - vite-imagetools@8.0.0(rollup@4.50.1): + vite-imagetools@8.0.0(rollup@4.46.3): dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.50.1) + '@rollup/pluginutils': 5.2.0(rollup@4.46.3) imagetools-core: 8.0.0 sharp: 0.34.3 transitivePeerDependencies: @@ -23749,7 +23728,7 @@ snapshots: debug: 4.4.1 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) transitivePeerDependencies: - '@types/node' - jiti @@ -23770,7 +23749,7 @@ snapshots: debug: 4.4.1 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) transitivePeerDependencies: - '@types/node' - jiti @@ -23785,25 +23764,25 @@ snapshots: - tsx - yaml - vite-tsconfig-paths@5.1.4(typescript@5.9.2)(vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): + vite-tsconfig-paths@5.1.4(typescript@5.9.2)(vite@7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): dependencies: debug: 4.4.1 globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.9.2) optionalDependencies: - vite: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) transitivePeerDependencies: - supports-color - typescript - vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): + vite@7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): dependencies: esbuild: 0.25.9 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.50.1 - tinyglobby: 0.2.15 + rollup: 4.46.3 + tinyglobby: 0.2.14 optionalDependencies: '@types/node': 22.18.1 fsevents: 2.3.3 @@ -23812,14 +23791,14 @@ snapshots: terser: 5.43.1 yaml: 2.8.1 - vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): + vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): dependencies: esbuild: 0.25.9 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.50.1 - tinyglobby: 0.2.15 + rollup: 4.46.3 + tinyglobby: 0.2.14 optionalDependencies: '@types/node': 24.3.0 fsevents: 2.3.3 @@ -23828,9 +23807,9 @@ snapshots: terser: 5.43.1 yaml: 2.8.1 - vitefu@1.1.1(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): + vitefu@1.1.1(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): optionalDependencies: - vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vitest-fetch-mock@0.4.5(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.1)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): dependencies: @@ -23840,7 +23819,7 @@ snapshots: dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@vitest/mocker': 3.2.4(vite@7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -23855,10 +23834,10 @@ snapshots: std-env: 3.9.0 tinybench: 2.9.0 tinyexec: 0.3.2 - tinyglobby: 0.2.15 + tinyglobby: 0.2.14 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vite-node: 3.2.4(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: @@ -23884,7 +23863,7 @@ snapshots: dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@vitest/mocker': 3.2.4(vite@7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -23899,10 +23878,10 @@ snapshots: std-env: 3.9.0 tinybench: 2.9.0 tinyexec: 0.3.2 - tinyglobby: 0.2.15 + tinyglobby: 0.2.14 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vite-node: 3.2.4(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: @@ -23928,7 +23907,7 @@ snapshots: dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@vitest/mocker': 3.2.4(vite@7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -23943,10 +23922,10 @@ snapshots: std-env: 3.9.0 tinybench: 2.9.0 tinyexec: 0.3.2 - tinyglobby: 0.2.15 + tinyglobby: 0.2.14 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.2(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vite-node: 3.2.4(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: diff --git a/web/src/lib/components/memory-page/memory-viewer.svelte b/web/src/lib/components/memory-page/memory-viewer.svelte index b7e0dae17c..9d43b40141 100644 --- a/web/src/lib/components/memory-page/memory-viewer.svelte +++ b/web/src/lib/components/memory-page/memory-viewer.svelte @@ -662,6 +662,7 @@ viewport={galleryViewport} {assetInteraction} slidingWindowOffset={viewerHeight} + arrowNavigation={false} />
    diff --git a/web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte b/web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte index d369068a2c..e9282ae5d8 100644 --- a/web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte +++ b/web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte @@ -42,6 +42,7 @@ onReload?: (() => void) | undefined; pageHeaderOffset?: number; slidingWindowOffset?: number; + arrowNavigation?: boolean; } let { @@ -60,6 +61,7 @@ onReload = undefined, slidingWindowOffset = 0, pageHeaderOffset = 0, + arrowNavigation = true, }: Props = $props(); let { isViewing: isViewerOpen, asset: viewingAsset, setAssetId } = assetViewingStore; @@ -306,8 +308,12 @@ { shortcut: { key: '?', shift: true }, onShortcut: handleOpenShortcutModal }, { shortcut: { key: '/' }, onShortcut: () => goto(AppRoute.EXPLORE) }, { shortcut: { key: 'A', ctrl: true }, onShortcut: () => selectAllAssets() }, - { shortcut: { key: 'ArrowRight' }, preventDefault: false, onShortcut: focusNextAsset }, - { shortcut: { key: 'ArrowLeft' }, preventDefault: false, onShortcut: focusPreviousAsset }, + ...(arrowNavigation + ? [ + { shortcut: { key: 'ArrowRight' }, preventDefault: false, onShortcut: focusNextAsset }, + { shortcut: { key: 'ArrowLeft' }, preventDefault: false, onShortcut: focusPreviousAsset }, + ] + : []), ]; if (assetInteraction.selectionActive) { From 9a5a3c0a1d793deaf0cb357c27f0f67514c7a49e Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 10 Sep 2025 11:50:31 -0500 Subject: [PATCH 459/748] chore: refactor life cycle events (#21781) --- .../providers/app_life_cycle.provider.dart | 70 +++++++------------ 1 file changed, 24 insertions(+), 46 deletions(-) diff --git a/mobile/lib/providers/app_life_cycle.provider.dart b/mobile/lib/providers/app_life_cycle.provider.dart index 3da653444c..18b7c3464a 100644 --- a/mobile/lib/providers/app_life_cycle.provider.dart +++ b/mobile/lib/providers/app_life_cycle.provider.dart @@ -125,6 +125,18 @@ class AppLifeCycleNotifier extends StateNotifier { } } + Future _safeRun(Future action, String debugName) async { + if (!_shouldContinueOperation()) { + return; + } + + try { + await action; + } catch (e, stackTrace) { + _log.warning("Error during $debugName operation", e, stackTrace); + } + } + Future _handleBetaTimelineResume() async { _ref.read(backupProvider.notifier).cancelBackup(); final lockManager = _ref.read(isolateLockManagerProvider(kIsolateLockManagerPort)); @@ -150,57 +162,23 @@ class AppLifeCycleNotifier extends StateNotifier { final backgroundManager = _ref.read(backgroundSyncProvider); final isAlbumLinkedSyncEnable = _ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.syncAlbums); + final isEnableBackup = _ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup); try { // Run operations sequentially with state checks and error handling for each - if (_shouldContinueOperation()) { - try { - await backgroundManager.syncLocal(); - } catch (e, stackTrace) { - _log.warning("Failed syncLocal: $e", e, stackTrace); + _safeRun(backgroundManager.syncLocal(), "syncLocal"); + _safeRun(backgroundManager.hashAssets(), "hashAssets"); + _safeRun(backgroundManager.syncRemote(), "syncRemote").then((_) { + if (isAlbumLinkedSyncEnable) { + _safeRun(backgroundManager.syncLinkedAlbum(), "syncLinkedAlbum"); } - } - - // Check if app is still active before hashing - if (_shouldContinueOperation()) { - try { - await backgroundManager.hashAssets(); - } catch (e, stackTrace) { - _log.warning("Failed hashAssets: $e", e, stackTrace); - } - } - - // Check if app is still active before remote sync - if (_shouldContinueOperation()) { - try { - await backgroundManager.syncRemote(); - } catch (e, stackTrace) { - _log.warning("Failed syncRemote: $e", e, stackTrace); - } - - if (isAlbumLinkedSyncEnable && _shouldContinueOperation()) { - try { - await backgroundManager.syncLinkedAlbum(); - } catch (e, stackTrace) { - _log.warning("Failed syncLinkedAlbum: $e", e, stackTrace); - } - } - } + }); // Handle backup resume only if still active - if (_shouldContinueOperation()) { - final isEnableBackup = _ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup); - - if (isEnableBackup) { - final currentUser = _ref.read(currentUserProvider); - if (currentUser != null) { - try { - await _ref.read(driftBackupProvider.notifier).handleBackupResume(currentUser.id); - _log.fine("Completed backup resume"); - } catch (e, stackTrace) { - _log.warning("Failed backup resume: $e", e, stackTrace); - } - } + if (isEnableBackup) { + final currentUser = _ref.read(currentUserProvider); + if (currentUser != null) { + _safeRun(_ref.read(driftBackupProvider.notifier).handleBackupResume(currentUser.id), "handleBackupResume"); } } } catch (e, stackTrace) { @@ -209,7 +187,7 @@ class AppLifeCycleNotifier extends StateNotifier { // Ensure lock is released even if operations fail try { lockManager.releaseLock(); - _log.fine("Lock released after background sync operations"); + _log.info("Lock released after background sync operations"); } catch (lockError) { _log.warning("Failed to release lock after error: $lockError"); } From 2c7b980eed8dfb9778272f68eb61a2ca40b517bc Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 10 Sep 2025 12:11:46 -0500 Subject: [PATCH 460/748] chore: make beta timeline the default (#21751) * chore: make beta timeline the default * fix: logic * awaiting * refactor --- i18n/en.json | 4 +- mobile/lib/domain/models/store.model.dart | 3 +- mobile/lib/domain/services/store.service.dart | 2 +- mobile/lib/pages/common/settings.page.dart | 12 +- .../lib/pages/common/splash_screen.page.dart | 9 + ...ttings.page.dart => sync_status.page.dart} | 10 +- mobile/lib/routing/router.dart | 4 +- mobile/lib/routing/router.gr.dart | 32 +- mobile/lib/services/app_settings.service.dart | 2 +- mobile/lib/utils/bootstrap.dart | 2 +- mobile/lib/utils/migration.dart | 72 +++- .../beta_sync_settings.dart | 376 ------------------ .../sync_status_and_actions.dart | 353 ++++++++++++++++ 13 files changed, 468 insertions(+), 413 deletions(-) rename mobile/lib/pages/settings/{beta_sync_settings.page.dart => sync_status.page.dart} (71%) delete mode 100644 mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart create mode 100644 mobile/lib/widgets/settings/beta_sync_settings/sync_status_and_actions.dart diff --git a/i18n/en.json b/i18n/en.json index 4d940ffadc..82c5c147aa 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -597,8 +597,6 @@ "backup_setting_subtitle": "Manage background and foreground upload settings", "backup_settings_subtitle": "Manage upload settings", "backward": "Backward", - "beta_sync": "Beta Sync Status", - "beta_sync_subtitle": "Manage the new sync system", "biometric_auth_enabled": "Biometric authentication enabled", "biometric_locked_out": "You are locked out of biometric authentication", "biometric_no_options": "No biometric options available", @@ -1919,6 +1917,8 @@ "sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums", "sync_local": "Sync Local", "sync_remote": "Sync Remote", + "sync_status": "Sync Status", + "sync_status_subtitle": "View and manage the sync system", "sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich", "tag": "Tag", "tag_assets": "Tag assets", diff --git a/mobile/lib/domain/models/store.model.dart b/mobile/lib/domain/models/store.model.dart index 6dcd81774a..17ead45f01 100644 --- a/mobile/lib/domain/models/store.model.dart +++ b/mobile/lib/domain/models/store.model.dart @@ -76,7 +76,8 @@ enum StoreKey { betaTimeline._(1002), enableBackup._(1003), useWifiForUploadVideos._(1004), - useWifiForUploadPhotos._(1005); + useWifiForUploadPhotos._(1005), + needBetaMigration._(1006); const StoreKey._(this.id); final int id; diff --git a/mobile/lib/domain/services/store.service.dart b/mobile/lib/domain/services/store.service.dart index 3347134ae6..762d5db3b9 100644 --- a/mobile/lib/domain/services/store.service.dart +++ b/mobile/lib/domain/services/store.service.dart @@ -90,7 +90,7 @@ class StoreService { _cache.clear(); } - bool get isBetaTimelineEnabled => tryGet(StoreKey.betaTimeline) ?? false; + bool get isBetaTimelineEnabled => tryGet(StoreKey.betaTimeline) ?? true; } class StoreKeyNotFoundException implements Exception { diff --git a/mobile/lib/pages/common/settings.page.dart b/mobile/lib/pages/common/settings.page.dart index 7bc8cd2b3a..014136ddb4 100644 --- a/mobile/lib/pages/common/settings.page.dart +++ b/mobile/lib/pages/common/settings.page.dart @@ -11,7 +11,7 @@ import 'package:immich_mobile/widgets/settings/asset_list_settings/asset_list_se import 'package:immich_mobile/widgets/settings/asset_viewer_settings/asset_viewer_settings.dart'; import 'package:immich_mobile/widgets/settings/backup_settings/backup_settings.dart'; import 'package:immich_mobile/widgets/settings/backup_settings/drift_backup_settings.dart'; -import 'package:immich_mobile/widgets/settings/beta_sync_settings/beta_sync_settings.dart'; +import 'package:immich_mobile/widgets/settings/beta_sync_settings/sync_status_and_actions.dart'; import 'package:immich_mobile/widgets/settings/beta_timeline_list_tile.dart'; import 'package:immich_mobile/widgets/settings/language_settings.dart'; import 'package:immich_mobile/widgets/settings/networking_settings/networking_settings.dart'; @@ -20,7 +20,7 @@ import 'package:immich_mobile/widgets/settings/preference_settings/preference_se import 'package:immich_mobile/widgets/settings/settings_card.dart'; enum SettingSection { - beta('beta_sync', Icons.sync_outlined, "beta_sync_subtitle"), + beta('sync_status', Icons.sync_outlined, "sync_status_subtitle"), advanced('advanced', Icons.build_outlined, "advanced_settings_tile_subtitle"), assetViewer('asset_viewer_settings_title', Icons.image_outlined, "asset_viewer_settings_subtitle"), backup('backup', Icons.cloud_upload_outlined, "backup_settings_subtitle"), @@ -76,9 +76,9 @@ class _MobileLayout extends StatelessWidget { if (Store.isBetaTimelineEnabled) SettingsCard( icon: Icons.sync_outlined, - title: 'beta_sync'.tr(), - subtitle: 'beta_sync_subtitle'.tr(), - settingRoute: const BetaSyncSettingsRoute(), + title: 'sync_status'.tr(), + subtitle: 'sync_status_subtitle'.tr(), + settingRoute: const SyncStatusRoute(), ), ] : [ @@ -143,7 +143,7 @@ class _BetaLandscapeToggle extends HookWidget { mainAxisAlignment: MainAxisAlignment.start, children: [ const SizedBox(height: 100, child: BetaTimelineListTile()), - if (Store.isBetaTimelineEnabled) const Expanded(child: BetaSyncSettings()), + if (Store.isBetaTimelineEnabled) const Expanded(child: SyncStatusAndActions()), ], ); } diff --git a/mobile/lib/pages/common/splash_screen.page.dart b/mobile/lib/pages/common/splash_screen.page.dart index 64db7daee6..f41cf317bf 100644 --- a/mobile/lib/pages/common/splash_screen.page.dart +++ b/mobile/lib/pages/common/splash_screen.page.dart @@ -80,7 +80,16 @@ class SplashScreenPageState extends ConsumerState { return; } + // clean install - change the default of the flag + // current install not using beta timeline if (context.router.current.name == SplashScreenRoute.name) { + final needBetaMigration = Store.get(StoreKey.needBetaMigration, false); + if (needBetaMigration) { + await Store.put(StoreKey.needBetaMigration, false); + context.router.replaceAll([ChangeExperienceRoute(switchingToBeta: true)]); + return; + } + context.replaceRoute(Store.isBetaTimelineEnabled ? const TabShellRoute() : const TabControllerRoute()); } diff --git a/mobile/lib/pages/settings/beta_sync_settings.page.dart b/mobile/lib/pages/settings/sync_status.page.dart similarity index 71% rename from mobile/lib/pages/settings/beta_sync_settings.page.dart rename to mobile/lib/pages/settings/sync_status.page.dart index 992557b7c6..d54ba89e5d 100644 --- a/mobile/lib/pages/settings/beta_sync_settings.page.dart +++ b/mobile/lib/pages/settings/sync_status.page.dart @@ -1,25 +1,25 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; -import 'package:immich_mobile/widgets/settings/beta_sync_settings/beta_sync_settings.dart'; +import 'package:immich_mobile/widgets/settings/beta_sync_settings/sync_status_and_actions.dart'; @RoutePage() -class BetaSyncSettingsPage extends StatelessWidget { - const BetaSyncSettingsPage({super.key}); +class SyncStatusPage extends StatelessWidget { + const SyncStatusPage({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( elevation: 0, - title: const Text("beta_sync").t(context: context), + title: const Text("sync_status").t(context: context), leading: IconButton( onPressed: () => context.maybePop(true), splashRadius: 24, icon: const Icon(Icons.arrow_back_ios_rounded), ), ), - body: const BetaSyncSettings(), + body: const SyncStatusAndActions(), ); } } diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index 14af0b2600..cdf384fcf8 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -76,7 +76,7 @@ import 'package:immich_mobile/pages/search/map/map_location_picker.page.dart'; import 'package:immich_mobile/pages/search/person_result.page.dart'; import 'package:immich_mobile/pages/search/recently_taken.page.dart'; import 'package:immich_mobile/pages/search/search.page.dart'; -import 'package:immich_mobile/pages/settings/beta_sync_settings.page.dart'; +import 'package:immich_mobile/pages/settings/sync_status.page.dart'; import 'package:immich_mobile/pages/share_intent/share_intent.page.dart'; import 'package:immich_mobile/presentation/pages/dev/feat_in_development.page.dart'; import 'package:immich_mobile/presentation/pages/dev/main_timeline.page.dart'; @@ -333,7 +333,7 @@ class AppRouter extends RootStackRouter { AutoRoute(page: ChangeExperienceRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: DriftPartnerRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: DriftUploadDetailRoute.page, guards: [_authGuard, _duplicateGuard]), - AutoRoute(page: BetaSyncSettingsRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: SyncStatusRoute.page, guards: [_duplicateGuard]), AutoRoute(page: DriftPeopleCollectionRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: DriftPersonRoute.page, guards: [_authGuard]), AutoRoute(page: DriftBackupOptionsRoute.page, guards: [_authGuard, _duplicateGuard]), diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index 4d50a1bba5..981828acf1 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -546,22 +546,6 @@ class BackupOptionsRoute extends PageRouteInfo { ); } -/// generated route for -/// [BetaSyncSettingsPage] -class BetaSyncSettingsRoute extends PageRouteInfo { - const BetaSyncSettingsRoute({List? children}) - : super(BetaSyncSettingsRoute.name, initialChildren: children); - - static const String name = 'BetaSyncSettingsRoute'; - - static PageInfo page = PageInfo( - name, - builder: (data) { - return const BetaSyncSettingsPage(); - }, - ); -} - /// generated route for /// [ChangeExperiencePage] class ChangeExperienceRoute extends PageRouteInfo { @@ -2666,6 +2650,22 @@ class SplashScreenRoute extends PageRouteInfo { ); } +/// generated route for +/// [SyncStatusPage] +class SyncStatusRoute extends PageRouteInfo { + const SyncStatusRoute({List? children}) + : super(SyncStatusRoute.name, initialChildren: children); + + static const String name = 'SyncStatusRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const SyncStatusPage(); + }, + ); +} + /// generated route for /// [TabControllerPage] class TabControllerRoute extends PageRouteInfo { diff --git a/mobile/lib/services/app_settings.service.dart b/mobile/lib/services/app_settings.service.dart index d98b14408f..d53cd85b95 100644 --- a/mobile/lib/services/app_settings.service.dart +++ b/mobile/lib/services/app_settings.service.dart @@ -46,7 +46,7 @@ enum AppSettingsEnum { syncAlbums(StoreKey.syncAlbums, null, false), autoEndpointSwitching(StoreKey.autoEndpointSwitching, null, false), photoManagerCustomFilter(StoreKey.photoManagerCustomFilter, null, true), - betaTimeline(StoreKey.betaTimeline, null, false), + betaTimeline(StoreKey.betaTimeline, null, true), enableBackup(StoreKey.enableBackup, null, false), useCellularForUploadVideos(StoreKey.useWifiForUploadVideos, null, false), useCellularForUploadPhotos(StoreKey.useWifiForUploadPhotos, null, false), diff --git a/mobile/lib/utils/bootstrap.dart b/mobile/lib/utils/bootstrap.dart index e7abc66040..c7d7cb8192 100644 --- a/mobile/lib/utils/bootstrap.dart +++ b/mobile/lib/utils/bootstrap.dart @@ -90,7 +90,7 @@ abstract final class Bootstrap { } static Future initDomain(Isar db, Drift drift, DriftLogger logDb, {bool shouldBufferLogs = true}) async { - final isBeta = await IsarStoreRepository(db).tryGet(StoreKey.betaTimeline) ?? false; + final isBeta = await IsarStoreRepository(db).tryGet(StoreKey.betaTimeline) ?? true; final IStoreRepository storeRepo = isBeta ? DriftStoreRepository(drift) : IsarStoreRepository(db); await StoreService.init(storeRepository: storeRepo); diff --git a/mobile/lib/utils/migration.dart b/mobile/lib/utils/migration.dart index 0a786fed0b..c21f2979d9 100644 --- a/mobile/lib/utils/migration.dart +++ b/mobile/lib/utils/migration.dart @@ -33,12 +33,11 @@ import 'package:logging/logging.dart'; // ignore: import_rule_photo_manager import 'package:photo_manager/photo_manager.dart'; -const int targetVersion = 14; +const int targetVersion = 15; Future migrateDatabaseIfNeeded(Isar db, Drift drift) async { final hasVersion = Store.tryGet(StoreKey.version) != null; final int version = Store.get(StoreKey.version, targetVersion); - if (version < 9) { await Store.put(StoreKey.version, targetVersion); final value = await db.storeValues.get(StoreKey.currentUser.id); @@ -68,6 +67,22 @@ Future migrateDatabaseIfNeeded(Isar db, Drift drift) async { await Store.populateCache(); } + // Handle migration only for this version + // TODO: remove when old timeline is removed + if (version == 15) { + final isBeta = Store.tryGet(StoreKey.betaTimeline); + final isNewInstallation = await _isNewInstallation(db, drift); + + // For new installations, no migration needed + // For existing installations, only migrate if beta timeline is not enabled (null or false) + if (isNewInstallation || isBeta == true) { + await Store.put(StoreKey.needBetaMigration, false); + } else { + await resetDriftDatabase(drift); + await Store.put(StoreKey.needBetaMigration, true); + } + } + if (targetVersion >= 12) { await Store.put(StoreKey.version, targetVersion); return; @@ -80,6 +95,35 @@ Future migrateDatabaseIfNeeded(Isar db, Drift drift) async { } } +Future _isNewInstallation(Isar db, Drift drift) async { + try { + final isarUserCount = await db.users.count(); + if (isarUserCount > 0) { + return false; + } + + final isarAssetCount = await db.assets.count(); + if (isarAssetCount > 0) { + return false; + } + + final driftStoreCount = await drift.storeEntity.select().get().then((list) => list.length); + if (driftStoreCount > 0) { + return false; + } + + final driftAssetCount = await drift.localAssetEntity.select().get().then((list) => list.length); + if (driftAssetCount > 0) { + return false; + } + + return true; + } catch (error) { + debugPrint("[MIGRATION] Error checking if new installation: $error"); + return false; + } +} + Future _migrateTo(Isar db, int version) async { await Store.delete(StoreKey.assetETag); await db.writeTxn(() async { @@ -284,3 +328,27 @@ Future> runNewSync(WidgetRef ref, {bool full = false}) { }), ]); } + +Future resetDriftDatabase(Drift drift) async { + // https://github.com/simolus3/drift/commit/bd80a46264b6dd833ef4fd87fffc03f5a832ab41#diff-3f879e03b4a35779344ef16170b9353608dd9c42385f5402ec6035aac4dd8a04R76-R94 + final database = drift.attachedDatabase; + await database.exclusively(() async { + // https://stackoverflow.com/a/65743498/25690041 + await database.customStatement('PRAGMA writable_schema = 1;'); + await database.customStatement('DELETE FROM sqlite_master;'); + await database.customStatement('VACUUM;'); + await database.customStatement('PRAGMA writable_schema = 0;'); + await database.customStatement('PRAGMA integrity_check'); + + await database.customStatement('PRAGMA user_version = 0'); + await database.beforeOpen( + // ignore: invalid_use_of_internal_member + database.resolvedEngine.executor, + OpeningDetails(null, database.schemaVersion), + ); + await database.customStatement('PRAGMA user_version = ${database.schemaVersion}'); + + // Refresh all stream queries + database.notifyUpdates({for (final table in database.allTables) TableUpdate.onTable(table)}); + }); +} diff --git a/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart b/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart deleted file mode 100644 index e5c65a9c67..0000000000 --- a/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart +++ /dev/null @@ -1,376 +0,0 @@ -import 'dart:io'; - -import 'package:drift/drift.dart' as drift_db; -import 'package:flutter/material.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/extensions/translate_extensions.dart'; -import 'package:immich_mobile/providers/background_sync.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/memory.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/storage.provider.dart'; -import 'package:immich_mobile/providers/sync_status.provider.dart'; -import 'package:immich_mobile/widgets/settings/beta_sync_settings/entity_count_tile.dart'; -import 'package:path/path.dart' as path; -import 'package:path_provider/path_provider.dart'; -import 'package:share_plus/share_plus.dart'; - -class BetaSyncSettings extends HookConsumerWidget { - const BetaSyncSettings({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final assetService = ref.watch(assetServiceProvider); - final localAlbumService = ref.watch(localAlbumServiceProvider); - final remoteAlbumService = ref.watch(remoteAlbumServiceProvider); - final memoryService = ref.watch(driftMemoryServiceProvider); - - Future> loadCounts() async { - final assetCounts = assetService.getAssetCounts(); - final localAlbumCounts = localAlbumService.getCount(); - final remoteAlbumCounts = remoteAlbumService.getCount(); - final memoryCount = memoryService.getCount(); - final getLocalHashedCount = assetService.getLocalHashedCount(); - - return await Future.wait([assetCounts, localAlbumCounts, remoteAlbumCounts, memoryCount, getLocalHashedCount]); - } - - Future resetDatabase() async { - // https://github.com/simolus3/drift/commit/bd80a46264b6dd833ef4fd87fffc03f5a832ab41#diff-3f879e03b4a35779344ef16170b9353608dd9c42385f5402ec6035aac4dd8a04R76-R94 - final drift = ref.read(driftProvider); - final database = drift.attachedDatabase; - await database.exclusively(() async { - // https://stackoverflow.com/a/65743498/25690041 - await database.customStatement('PRAGMA writable_schema = 1;'); - await database.customStatement('DELETE FROM sqlite_master;'); - await database.customStatement('VACUUM;'); - await database.customStatement('PRAGMA writable_schema = 0;'); - await database.customStatement('PRAGMA integrity_check'); - - await database.customStatement('PRAGMA user_version = 0'); - await database.beforeOpen( - // ignore: invalid_use_of_internal_member - database.resolvedEngine.executor, - drift_db.OpeningDetails(null, database.schemaVersion), - ); - await database.customStatement('PRAGMA user_version = ${database.schemaVersion}'); - - // Refresh all stream queries - database.notifyUpdates({for (final table in database.allTables) drift_db.TableUpdate.onTable(table)}); - }); - } - - Future exportDatabase() async { - try { - // WAL Checkpoint to ensure all changes are written to the database - await ref.read(driftProvider).customStatement("pragma wal_checkpoint(truncate)"); - final documentsDir = await getApplicationDocumentsDirectory(); - final dbFile = File(path.join(documentsDir.path, 'immich.sqlite')); - - if (!await dbFile.exists()) { - if (context.mounted) { - context.scaffoldMessenger.showSnackBar( - SnackBar(content: Text("Database file not found".t(context: context))), - ); - } - return; - } - - final timestamp = DateTime.now().millisecondsSinceEpoch; - final exportFile = File(path.join(documentsDir.path, 'immich_export_$timestamp.sqlite')); - - await dbFile.copy(exportFile.path); - - await Share.shareXFiles([XFile(exportFile.path)], text: 'Immich Database Export'); - - Future.delayed(const Duration(seconds: 30), () async { - if (await exportFile.exists()) { - await exportFile.delete(); - } - }); - - if (context.mounted) { - context.scaffoldMessenger.showSnackBar( - SnackBar(content: Text("Database exported successfully".t(context: context))), - ); - } - } catch (e) { - if (context.mounted) { - context.scaffoldMessenger.showSnackBar( - SnackBar(content: Text("Failed to export database: $e".t(context: context))), - ); - } - } - } - - Future clearFileCache() async { - await ref.read(storageRepositoryProvider).clearCache(); - } - - Future resetSqliteDb(BuildContext context, Future Function() resetDatabase) { - return showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: Text("reset_sqlite".t(context: context)), - content: Text("reset_sqlite_confirmation".t(context: context)), - actions: [ - TextButton( - onPressed: () => context.pop(), - child: Text("cancel".t(context: context)), - ), - TextButton( - onPressed: () async { - await resetDatabase(); - context.pop(); - context.scaffoldMessenger.showSnackBar( - SnackBar(content: Text("reset_sqlite_success".t(context: context))), - ); - }, - child: Text( - "confirm".t(context: context), - style: TextStyle(color: context.colorScheme.error), - ), - ), - ], - ); - }, - ); - } - - return FutureBuilder>( - future: loadCounts(), - builder: (context, snapshot) { - if (snapshot.connectionState != ConnectionState.done) { - return const CircularProgressIndicator(); - } - - if (snapshot.hasError) { - return ListView( - children: [ - Padding( - padding: const EdgeInsets.all(16.0), - child: Center( - child: Text( - "Error occur, reset the local database by tapping the button below", - style: context.textTheme.bodyLarge, - ), - ), - ), - - ListTile( - title: Text( - "reset_sqlite".t(context: context), - style: TextStyle(color: context.colorScheme.error, fontWeight: FontWeight.w500), - ), - leading: Icon(Icons.settings_backup_restore_rounded, color: context.colorScheme.error), - onTap: () async { - await resetSqliteDb(context, resetDatabase); - }, - ), - ], - ); - } - - final assetCounts = snapshot.data![0]! as (int, int); - final localAssetCount = assetCounts.$1; - final remoteAssetCount = assetCounts.$2; - - final localAlbumCount = snapshot.data![1]! as int; - final remoteAlbumCount = snapshot.data![2]! as int; - final memoryCount = snapshot.data![3]! as int; - final localHashedCount = snapshot.data![4]! as int; - - return Padding( - padding: const EdgeInsets.only(top: 16, bottom: 32), - child: ListView( - children: [ - _SectionHeaderText(text: "assets".t(context: context)), - Padding( - padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), - child: Flex( - direction: Axis.horizontal, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - spacing: 8.0, - children: [ - Expanded( - child: EntitiyCountTile( - label: "local".t(context: context), - count: localAssetCount, - icon: Icons.smartphone, - ), - ), - Expanded( - child: EntitiyCountTile( - label: "remote".t(context: context), - count: remoteAssetCount, - icon: Icons.cloud, - ), - ), - ], - ), - ), - _SectionHeaderText(text: "albums".t(context: context)), - Padding( - padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), - child: Flex( - direction: Axis.horizontal, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - spacing: 8.0, - children: [ - Expanded( - child: EntitiyCountTile( - label: "local".t(context: context), - count: localAlbumCount, - icon: Icons.smartphone, - ), - ), - Expanded( - child: EntitiyCountTile( - label: "remote".t(context: context), - count: remoteAlbumCount, - icon: Icons.cloud, - ), - ), - ], - ), - ), - _SectionHeaderText(text: "other".t(context: context)), - Padding( - padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), - child: Flex( - direction: Axis.horizontal, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - spacing: 8.0, - children: [ - Expanded( - child: EntitiyCountTile( - label: "memories".t(context: context), - count: memoryCount, - icon: Icons.calendar_today, - ), - ), - Expanded( - child: EntitiyCountTile( - label: "hashed_assets".t(context: context), - count: localHashedCount, - icon: Icons.tag, - ), - ), - ], - ), - ), - const Divider(height: 1, indent: 16, endIndent: 16), - const SizedBox(height: 24), - _SectionHeaderText(text: "jobs".t(context: context)), - ListTile( - title: Text( - "sync_local".t(context: context), - style: const TextStyle(fontWeight: FontWeight.w500), - ), - subtitle: Text("tap_to_run_job".t(context: context)), - leading: const Icon(Icons.sync), - trailing: _SyncStatusIcon(status: ref.watch(syncStatusProvider).localSyncStatus), - onTap: () { - ref.read(backgroundSyncProvider).syncLocal(full: true); - }, - ), - ListTile( - title: Text( - "sync_remote".t(context: context), - style: const TextStyle(fontWeight: FontWeight.w500), - ), - subtitle: Text("tap_to_run_job".t(context: context)), - leading: const Icon(Icons.cloud_sync), - trailing: _SyncStatusIcon(status: ref.watch(syncStatusProvider).remoteSyncStatus), - onTap: () { - ref.read(backgroundSyncProvider).syncRemote(); - }, - ), - ListTile( - title: Text( - "hash_asset".t(context: context), - style: const TextStyle(fontWeight: FontWeight.w500), - ), - leading: const Icon(Icons.tag), - subtitle: Text("tap_to_run_job".t(context: context)), - trailing: _SyncStatusIcon(status: ref.watch(syncStatusProvider).hashJobStatus), - onTap: () { - ref.read(backgroundSyncProvider).hashAssets(); - }, - ), - const Divider(height: 1, indent: 16, endIndent: 16), - const SizedBox(height: 24), - _SectionHeaderText(text: "actions".t(context: context)), - ListTile( - title: Text( - "clear_file_cache".t(context: context), - style: const TextStyle(fontWeight: FontWeight.w500), - ), - leading: const Icon(Icons.playlist_remove_rounded), - onTap: clearFileCache, - ), - ListTile( - title: Text( - "export_database".t(context: context), - style: const TextStyle(fontWeight: FontWeight.w500), - ), - subtitle: Text("export_database_description".t(context: context)), - leading: const Icon(Icons.download), - onTap: exportDatabase, - ), - ListTile( - title: Text( - "reset_sqlite".t(context: context), - style: TextStyle(color: context.colorScheme.error, fontWeight: FontWeight.w500), - ), - leading: Icon(Icons.settings_backup_restore_rounded, color: context.colorScheme.error), - onTap: () async { - await resetSqliteDb(context, resetDatabase); - }, - ), - ], - ), - ); - }, - ); - } -} - -class _SyncStatusIcon extends StatelessWidget { - final SyncStatus status; - - const _SyncStatusIcon({required this.status}); - - @override - Widget build(BuildContext context) { - return switch (status) { - SyncStatus.idle => const Icon(Icons.pause_circle_outline_rounded), - SyncStatus.syncing => const SizedBox(height: 24, width: 24, child: CircularProgressIndicator(strokeWidth: 2)), - SyncStatus.success => const Icon(Icons.check_circle_outline, color: Colors.green), - SyncStatus.error => Icon(Icons.error_outline, color: context.colorScheme.error), - }; - } -} - -class _SectionHeaderText extends StatelessWidget { - final String text; - - const _SectionHeaderText({required this.text}); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(left: 16.0), - child: Text( - text.toUpperCase(), - style: context.textTheme.labelLarge?.copyWith( - fontWeight: FontWeight.w500, - color: context.colorScheme.onSurface.withAlpha(200), - ), - ), - ); - } -} diff --git a/mobile/lib/widgets/settings/beta_sync_settings/sync_status_and_actions.dart b/mobile/lib/widgets/settings/beta_sync_settings/sync_status_and_actions.dart new file mode 100644 index 0000000000..e32df03cab --- /dev/null +++ b/mobile/lib/widgets/settings/beta_sync_settings/sync_status_and_actions.dart @@ -0,0 +1,353 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/background_sync.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/memory.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/storage.provider.dart'; +import 'package:immich_mobile/providers/sync_status.provider.dart'; +import 'package:immich_mobile/utils/migration.dart'; +import 'package:immich_mobile/widgets/settings/beta_sync_settings/entity_count_tile.dart'; +import 'package:path/path.dart' as path; +import 'package:path_provider/path_provider.dart'; +import 'package:share_plus/share_plus.dart'; + +class SyncStatusAndActions extends HookConsumerWidget { + const SyncStatusAndActions({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + Future exportDatabase() async { + try { + // WAL Checkpoint to ensure all changes are written to the database + await ref.read(driftProvider).customStatement("pragma wal_checkpoint(truncate)"); + final documentsDir = await getApplicationDocumentsDirectory(); + final dbFile = File(path.join(documentsDir.path, 'immich.sqlite')); + + if (!await dbFile.exists()) { + if (context.mounted) { + context.scaffoldMessenger.showSnackBar( + SnackBar(content: Text("Database file not found".t(context: context))), + ); + } + return; + } + + final timestamp = DateTime.now().millisecondsSinceEpoch; + final exportFile = File(path.join(documentsDir.path, 'immich_export_$timestamp.sqlite')); + + await dbFile.copy(exportFile.path); + + await Share.shareXFiles([XFile(exportFile.path)], text: 'Immich Database Export'); + + Future.delayed(const Duration(seconds: 30), () async { + if (await exportFile.exists()) { + await exportFile.delete(); + } + }); + + if (context.mounted) { + context.scaffoldMessenger.showSnackBar( + SnackBar(content: Text("Database exported successfully".t(context: context))), + ); + } + } catch (e) { + if (context.mounted) { + context.scaffoldMessenger.showSnackBar( + SnackBar(content: Text("Failed to export database: $e".t(context: context))), + ); + } + } + } + + Future clearFileCache() async { + await ref.read(storageRepositoryProvider).clearCache(); + } + + Future resetSqliteDb(BuildContext context) { + return showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text("reset_sqlite".t(context: context)), + content: Text("reset_sqlite_confirmation".t(context: context)), + actions: [ + TextButton( + onPressed: () => context.pop(), + child: Text("cancel".t(context: context)), + ), + TextButton( + onPressed: () async { + await resetDriftDatabase(ref.read(driftProvider)); + context.pop(); + context.scaffoldMessenger.showSnackBar( + SnackBar(content: Text("reset_sqlite_success".t(context: context))), + ); + }, + child: Text( + "confirm".t(context: context), + style: TextStyle(color: context.colorScheme.error), + ), + ), + ], + ); + }, + ); + } + + return Padding( + padding: const EdgeInsets.only(top: 16, bottom: 32), + child: ListView( + children: [ + _SectionHeaderText(text: "assets".t(context: context)), + const _SyncStatsCounts(), + const Divider(height: 1, indent: 16, endIndent: 16), + const SizedBox(height: 24), + _SectionHeaderText(text: "jobs".t(context: context)), + ListTile( + title: Text( + "sync_local".t(context: context), + style: const TextStyle(fontWeight: FontWeight.w500), + ), + subtitle: Text("tap_to_run_job".t(context: context)), + leading: const Icon(Icons.sync), + trailing: _SyncStatusIcon(status: ref.watch(syncStatusProvider).localSyncStatus), + onTap: () { + ref.read(backgroundSyncProvider).syncLocal(full: true); + }, + ), + ListTile( + title: Text( + "sync_remote".t(context: context), + style: const TextStyle(fontWeight: FontWeight.w500), + ), + subtitle: Text("tap_to_run_job".t(context: context)), + leading: const Icon(Icons.cloud_sync), + trailing: _SyncStatusIcon(status: ref.watch(syncStatusProvider).remoteSyncStatus), + onTap: () { + ref.read(backgroundSyncProvider).syncRemote(); + }, + ), + ListTile( + title: Text( + "hash_asset".t(context: context), + style: const TextStyle(fontWeight: FontWeight.w500), + ), + leading: const Icon(Icons.tag), + subtitle: Text("tap_to_run_job".t(context: context)), + trailing: _SyncStatusIcon(status: ref.watch(syncStatusProvider).hashJobStatus), + onTap: () { + ref.read(backgroundSyncProvider).hashAssets(); + }, + ), + const Divider(height: 1, indent: 16, endIndent: 16), + const SizedBox(height: 24), + _SectionHeaderText(text: "actions".t(context: context)), + ListTile( + title: Text( + "clear_file_cache".t(context: context), + style: const TextStyle(fontWeight: FontWeight.w500), + ), + leading: const Icon(Icons.playlist_remove_rounded), + onTap: clearFileCache, + ), + ListTile( + title: Text( + "export_database".t(context: context), + style: const TextStyle(fontWeight: FontWeight.w500), + ), + subtitle: Text("export_database_description".t(context: context)), + leading: const Icon(Icons.download), + onTap: exportDatabase, + ), + ListTile( + title: Text( + "reset_sqlite".t(context: context), + style: TextStyle(color: context.colorScheme.error, fontWeight: FontWeight.w500), + ), + leading: Icon(Icons.settings_backup_restore_rounded, color: context.colorScheme.error), + onTap: () async { + await resetSqliteDb(context); + }, + ), + ], + ), + ); + } +} + +class _SyncStatusIcon extends StatelessWidget { + final SyncStatus status; + + const _SyncStatusIcon({required this.status}); + + @override + Widget build(BuildContext context) { + return switch (status) { + SyncStatus.idle => const Icon(Icons.pause_circle_outline_rounded), + SyncStatus.syncing => const SizedBox(height: 24, width: 24, child: CircularProgressIndicator(strokeWidth: 2)), + SyncStatus.success => const Icon(Icons.check_circle_outline, color: Colors.green), + SyncStatus.error => Icon(Icons.error_outline, color: context.colorScheme.error), + }; + } +} + +class _SectionHeaderText extends StatelessWidget { + final String text; + + const _SectionHeaderText({required this.text}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16.0), + child: Text( + text.toUpperCase(), + style: context.textTheme.labelLarge?.copyWith( + fontWeight: FontWeight.w500, + color: context.colorScheme.onSurface.withAlpha(200), + ), + ), + ); + } +} + +class _SyncStatsCounts extends ConsumerWidget { + const _SyncStatsCounts(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final assetService = ref.watch(assetServiceProvider); + final localAlbumService = ref.watch(localAlbumServiceProvider); + final remoteAlbumService = ref.watch(remoteAlbumServiceProvider); + final memoryService = ref.watch(driftMemoryServiceProvider); + + Future> loadCounts() async { + final assetCounts = assetService.getAssetCounts(); + final localAlbumCounts = localAlbumService.getCount(); + final remoteAlbumCounts = remoteAlbumService.getCount(); + final memoryCount = memoryService.getCount(); + final getLocalHashedCount = assetService.getLocalHashedCount(); + + return await Future.wait([assetCounts, localAlbumCounts, remoteAlbumCounts, memoryCount, getLocalHashedCount]); + } + + return FutureBuilder( + future: loadCounts(), + builder: (context, snapshot) { + if (snapshot.connectionState != ConnectionState.done) { + return const Center(child: SizedBox(height: 48, width: 48, child: CircularProgressIndicator())); + } + + if (snapshot.hasError) { + return ListView( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Center( + child: Text( + "Error occur, reset the local database by tapping the button below", + style: context.textTheme.bodyLarge, + ), + ), + ), + ], + ); + } + + final assetCounts = snapshot.data![0]! as (int, int); + final localAssetCount = assetCounts.$1; + final remoteAssetCount = assetCounts.$2; + + final localAlbumCount = snapshot.data![1]! as int; + final remoteAlbumCount = snapshot.data![2]! as int; + final memoryCount = snapshot.data![3]! as int; + final localHashedCount = snapshot.data![4]! as int; + + return Column( + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), + child: Flex( + direction: Axis.horizontal, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + spacing: 8.0, + children: [ + Expanded( + child: EntitiyCountTile( + label: "local".t(context: context), + count: localAssetCount, + icon: Icons.smartphone, + ), + ), + Expanded( + child: EntitiyCountTile( + label: "remote".t(context: context), + count: remoteAssetCount, + icon: Icons.cloud, + ), + ), + ], + ), + ), + _SectionHeaderText(text: "albums".t(context: context)), + Padding( + padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), + child: Flex( + direction: Axis.horizontal, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + spacing: 8.0, + children: [ + Expanded( + child: EntitiyCountTile( + label: "local".t(context: context), + count: localAlbumCount, + icon: Icons.smartphone, + ), + ), + Expanded( + child: EntitiyCountTile( + label: "remote".t(context: context), + count: remoteAlbumCount, + icon: Icons.cloud, + ), + ), + ], + ), + ), + _SectionHeaderText(text: "other".t(context: context)), + Padding( + padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), + child: Flex( + direction: Axis.horizontal, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + spacing: 8.0, + children: [ + Expanded( + child: EntitiyCountTile( + label: "memories".t(context: context), + count: memoryCount, + icon: Icons.calendar_today, + ), + ), + Expanded( + child: EntitiyCountTile( + label: "hashed_assets".t(context: context), + count: localHashedCount, + icon: Icons.tag, + ), + ), + ], + ), + ), + ], + ); + }, + ); + } +} From e239b8d2faa7b0ac2290d044fbed7719f7dd0c68 Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Wed, 10 Sep 2025 22:45:42 +0530 Subject: [PATCH 461/748] fix: android crash on app pause (#21768) * revert service locks * rename backgroundWorkerFgServiceProvider * refactor: parallel background worker init (#21769) * refactor: parallel background worker init * fix: hashing not running from the background engine (#21773) * init and dispose workmanager from background engine * log message contend --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Co-authored-by: Alex --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Co-authored-by: Alex --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Co-authored-by: Alex --- .../services/background_worker.service.dart | 67 +++-- .../domain/utils/isolate_lock_manager.dart | 235 ------------------ mobile/lib/main.dart | 6 +- .../pages/common/change_experience.page.dart | 3 +- .../lib/pages/common/splash_screen.page.dart | 21 +- .../providers/app_life_cycle.provider.dart | 42 ---- .../providers/background_sync.provider.dart | 5 - .../lib/providers/backup/backup.provider.dart | 4 - .../infrastructure/platform.provider.dart | 4 + 9 files changed, 44 insertions(+), 343 deletions(-) delete mode 100644 mobile/lib/domain/utils/isolate_lock_manager.dart diff --git a/mobile/lib/domain/services/background_worker.service.dart b/mobile/lib/domain/services/background_worker.service.dart index b3d97e0938..29c15bd915 100644 --- a/mobile/lib/domain/services/background_worker.service.dart +++ b/mobile/lib/domain/services/background_worker.service.dart @@ -5,7 +5,6 @@ import 'package:background_downloader/background_downloader.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/constants.dart'; -import 'package:immich_mobile/domain/utils/isolate_lock_manager.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart'; import 'package:immich_mobile/platform/background_worker_api.g.dart'; @@ -24,6 +23,7 @@ import 'package:immich_mobile/utils/bootstrap.dart'; import 'package:immich_mobile/utils/http_ssl_options.dart'; import 'package:isar/isar.dart'; import 'package:logging/logging.dart'; +import 'package:worker_manager/worker_manager.dart'; class BackgroundWorkerFgService { final BackgroundWorkerFgHostApi _foregroundHostApi; @@ -42,8 +42,7 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi { final Drift _drift; final DriftLogger _driftLogger; final BackgroundWorkerBgHostApi _backgroundHostApi; - final Logger _logger = Logger('BackgroundUploadBgService'); - late final IsolateLockManager _lockManager; + final Logger _logger = Logger('BackgroundWorkerBgService'); bool _isCleanedUp = false; @@ -59,7 +58,6 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi { driftProvider.overrideWith(driftOverride(drift)), ], ); - _lockManager = IsolateLockManager(onCloseRequest: _cleanup); BackgroundWorkerFlutterApi.setUp(this); } @@ -67,41 +65,30 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi { Future init() async { try { - await loadTranslations(); HttpSSLOptions.apply(applyNative: false); - await _ref.read(authServiceProvider).setOpenApiServiceEndpoint(); - // Initialize the file downloader - await FileDownloader().configure( - globalConfig: [ - // maxConcurrent: 6, maxConcurrentByHost(server):6, maxConcurrentByGroup: 3 - (Config.holdingQueue, (6, 6, 3)), - // On Android, if files are larger than 256MB, run in foreground service - (Config.runInForegroundIfFileLargerThan, 256), - ], - ); - await FileDownloader().trackTasksInGroup(kDownloadGroupLivePhoto, markDownloadedComplete: false); - await FileDownloader().trackTasks(); + await Future.wait([ + loadTranslations(), + workerManager.init(dynamicSpawning: true), + _ref.read(authServiceProvider).setOpenApiServiceEndpoint(), + // Initialize the file downloader + FileDownloader().configure( + globalConfig: [ + // maxConcurrent: 6, maxConcurrentByHost(server):6, maxConcurrentByGroup: 3 + (Config.holdingQueue, (6, 6, 3)), + // On Android, if files are larger than 256MB, run in foreground service + (Config.runInForegroundIfFileLargerThan, 256), + ], + ), + FileDownloader().trackTasksInGroup(kDownloadGroupLivePhoto, markDownloadedComplete: false), + FileDownloader().trackTasks(), + _ref.read(fileMediaRepositoryProvider).enableBackgroundAccess(), + ]); + configureFileDownloaderNotifications(); - await _ref.read(fileMediaRepositoryProvider).enableBackgroundAccess(); - // Notify the host that the background upload service has been initialized and is ready to use - debugPrint("Acquiring background worker lock"); - if (await _lockManager.acquireLock().timeout( - const Duration(seconds: 5), - onTimeout: () { - _lockManager.cancel(); - return false; - }, - )) { - _logger.info("Acquired background worker lock"); - await _backgroundHostApi.onInitialized(); - return; - } - - _logger.warning("Failed to acquire background worker lock"); - await _cleanup(); - await _backgroundHostApi.close(); + // Notify the host that the background worker service has been initialized and is ready to use + _backgroundHostApi.onInitialized(); } catch (error, stack) { _logger.severe("Failed to initialize background worker", error, stack); _backgroundHostApi.close(); @@ -170,6 +157,7 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi { _isCleanedUp = true; _logger.info("Cleaning up background worker"); final cleanupFutures = [ + workerManager.dispose(), _drift.close(), _driftLogger.close(), _ref.read(backgroundSyncProvider).cancel(), @@ -180,8 +168,6 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi { cleanupFutures.add(_isar.close()); } _ref.dispose(); - _lockManager.releaseLock(); - await Future.wait(cleanupFutures); _logger.info("Background worker resources cleaned up"); } catch (error, stack) { @@ -191,22 +177,29 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi { Future _handleBackup({bool processBulk = true}) async { if (!_isBackupEnabled) { + _logger.info("[_handleBackup 1] Backup is disabled. Skipping backup routine"); return; } + _logger.info("[_handleBackup 2] Enqueuing assets for backup from the background service"); + final currentUser = _ref.read(currentUserProvider); if (currentUser == null) { + _logger.warning("[_handleBackup 3] No current user found. Skipping backup from background"); return; } if (processBulk) { + _logger.info("[_handleBackup 4] Resume backup from background"); return _ref.read(driftBackupProvider.notifier).handleBackupResume(currentUser.id); } final activeTask = await _ref.read(uploadServiceProvider).getActiveTasks(currentUser.id); if (activeTask.isNotEmpty) { + _logger.info("[_handleBackup 5] Resuming backup for active tasks from background"); await _ref.read(uploadServiceProvider).resumeBackup(); } else { + _logger.info("[_handleBackup 6] Starting serial backup for new tasks from background"); await _ref.read(uploadServiceProvider).startBackupSerial(currentUser.id); } } diff --git a/mobile/lib/domain/utils/isolate_lock_manager.dart b/mobile/lib/domain/utils/isolate_lock_manager.dart deleted file mode 100644 index 37de649204..0000000000 --- a/mobile/lib/domain/utils/isolate_lock_manager.dart +++ /dev/null @@ -1,235 +0,0 @@ -import 'dart:isolate'; -import 'dart:ui'; - -import 'package:flutter/foundation.dart'; -import 'package:logging/logging.dart'; - -const String kIsolateLockManagerPort = "immich://isolate_mutex"; - -enum _LockStatus { active, released } - -class _IsolateRequest { - const _IsolateRequest(); -} - -class _HeartbeatRequest extends _IsolateRequest { - // Port for the receiver to send replies back - final SendPort sendPort; - - const _HeartbeatRequest(this.sendPort); - - Map toJson() { - return {'type': 'heartbeat', 'sendPort': sendPort}; - } -} - -class _CloseRequest extends _IsolateRequest { - const _CloseRequest(); - - Map toJson() { - return {'type': 'close'}; - } -} - -class _IsolateResponse { - const _IsolateResponse(); -} - -class _HeartbeatResponse extends _IsolateResponse { - final _LockStatus status; - - const _HeartbeatResponse(this.status); - - Map toJson() { - return {'type': 'heartbeat', 'status': status.index}; - } -} - -typedef OnCloseLockHolderRequest = void Function(); - -class IsolateLockManager { - final String _portName; - bool _hasLock = false; - ReceivePort? _receivePort; - final OnCloseLockHolderRequest? _onCloseRequest; - final Set _waitingIsolates = {}; - // Token object - a new one is created for each acquisition attempt - Object? _currentAcquisitionToken; - - IsolateLockManager({String? portName, OnCloseLockHolderRequest? onCloseRequest}) - : _portName = portName ?? kIsolateLockManagerPort, - _onCloseRequest = onCloseRequest; - - Future acquireLock() async { - if (_hasLock) { - Logger('BackgroundWorkerLockManager').warning("WARNING: [acquireLock] called more than once"); - return true; - } - - // Create a new token - this invalidates any previous attempt - final token = _currentAcquisitionToken = Object(); - - final ReceivePort rp = _receivePort = ReceivePort(_portName); - final SendPort sp = rp.sendPort; - - while (!IsolateNameServer.registerPortWithName(sp, _portName)) { - // This attempt was superseded by a newer one in the same isolate - if (_currentAcquisitionToken != token) { - return false; - } - - await _lockReleasedByHolder(token); - } - - _hasLock = true; - rp.listen(_onRequest); - return true; - } - - Future _lockReleasedByHolder(Object token) async { - SendPort? holder = IsolateNameServer.lookupPortByName(_portName); - debugPrint("Found lock holder: $holder"); - if (holder == null) { - // No holder, try and acquire lock - return; - } - - final ReceivePort tempRp = ReceivePort(); - final SendPort tempSp = tempRp.sendPort; - final bs = tempRp.asBroadcastStream(); - - try { - while (true) { - // Send a heartbeat request with the send port to receive reply from the holder - - debugPrint("Sending heartbeat request to lock holder"); - holder.send(_HeartbeatRequest(tempSp).toJson()); - dynamic answer = await bs.first.timeout(const Duration(seconds: 3), onTimeout: () => null); - - debugPrint("Received heartbeat response from lock holder: $answer"); - // This attempt was superseded by a newer one in the same isolate - if (_currentAcquisitionToken != token) { - break; - } - - if (answer == null) { - // Holder failed, most likely killed without calling releaseLock - // Check if a different waiting isolate took the lock - if (holder == IsolateNameServer.lookupPortByName(_portName)) { - // No, remove the stale lock - IsolateNameServer.removePortNameMapping(_portName); - } - break; - } - - // Unknown message type received for heartbeat request. Try again - _IsolateResponse? response = _parseResponse(answer); - if (response == null || response is! _HeartbeatResponse) { - break; - } - - if (response.status == _LockStatus.released) { - // Holder has released the lock - break; - } - - // If the _LockStatus is active, we check again if the task completed - // by sending a released messaged again, if not, send a new heartbeat again - - // Check if the holder completed its task after the heartbeat - answer = await bs.first.timeout( - const Duration(seconds: 3), - onTimeout: () => const _HeartbeatResponse(_LockStatus.active).toJson(), - ); - - response = _parseResponse(answer); - if (response is _HeartbeatResponse && response.status == _LockStatus.released) { - break; - } - } - } catch (e) { - // Timeout or error - } finally { - tempRp.close(); - } - return; - } - - _IsolateRequest? _parseRequest(dynamic msg) { - if (msg is! Map) { - return null; - } - - return switch (msg['type']) { - 'heartbeat' => _HeartbeatRequest(msg['sendPort']), - 'close' => const _CloseRequest(), - _ => null, - }; - } - - _IsolateResponse? _parseResponse(dynamic msg) { - if (msg is! Map) { - return null; - } - - return switch (msg['type']) { - 'heartbeat' => _HeartbeatResponse(_LockStatus.values[msg['status']]), - _ => null, - }; - } - - // Executed in the isolate with the lock - void _onRequest(dynamic msg) { - final request = _parseRequest(msg); - if (request == null) { - return; - } - - if (request is _HeartbeatRequest) { - // Add the send port to the list of waiting isolates - _waitingIsolates.add(request.sendPort); - request.sendPort.send(const _HeartbeatResponse(_LockStatus.active).toJson()); - return; - } - - if (request is _CloseRequest) { - _onCloseRequest?.call(); - return; - } - } - - void releaseLock() { - if (_hasLock) { - IsolateNameServer.removePortNameMapping(_portName); - - // Notify waiting isolates - for (final port in _waitingIsolates) { - port.send(const _HeartbeatResponse(_LockStatus.released).toJson()); - } - _waitingIsolates.clear(); - - _hasLock = false; - } - - _receivePort?.close(); - _receivePort = null; - } - - void cancel() { - if (_hasLock) { - return; - } - - debugPrint("Cancelling ongoing acquire lock attempts"); - // Create a new token to invalidate ongoing acquire lock attempts - _currentAcquisitionToken = Object(); - } - - void requestHolderToClose() { - if (_hasLock) { - return; - } - - IsolateNameServer.lookupPortByName(_portName)?.send(const _CloseRequest().toJson()); - } -} diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 9066c5bfc7..4f74c30e3b 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -17,9 +17,9 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/generated/codegen_loader.g.dart'; import 'package:immich_mobile/providers/app_life_cycle.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/share_intent_upload.provider.dart'; -import 'package:immich_mobile/providers/backup/backup.provider.dart'; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/platform.provider.dart'; import 'package:immich_mobile/providers/locale_provider.dart'; import 'package:immich_mobile/providers/routes.provider.dart'; import 'package:immich_mobile/providers/theme.provider.dart'; @@ -205,9 +205,9 @@ class ImmichAppState extends ConsumerState with WidgetsBindingObserve // needs to be delayed so that EasyLocalization is working if (Store.isBetaTimelineEnabled) { ref.read(backgroundServiceProvider).disableService(); - ref.read(driftBackgroundUploadFgService).enable(); + ref.read(backgroundWorkerFgServiceProvider).enable(); } else { - ref.read(driftBackgroundUploadFgService).disable(); + ref.read(backgroundWorkerFgServiceProvider).disable(); ref.read(backgroundServiceProvider).resumeServiceIfEnabled(); } }); diff --git a/mobile/lib/pages/common/change_experience.page.dart b/mobile/lib/pages/common/change_experience.page.dart index ffdba1fb71..8779eecd7f 100644 --- a/mobile/lib/pages/common/change_experience.page.dart +++ b/mobile/lib/pages/common/change_experience.page.dart @@ -13,6 +13,7 @@ import 'package:immich_mobile/providers/backup/backup.provider.dart'; import 'package:immich_mobile/providers/backup/manual_upload.provider.dart'; import 'package:immich_mobile/providers/gallery_permission.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/platform.provider.dart'; import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; import 'package:immich_mobile/providers/websocket.provider.dart'; import 'package:immich_mobile/services/background.service.dart'; @@ -79,7 +80,7 @@ class _ChangeExperiencePageState extends ConsumerState { ref.read(readonlyModeProvider.notifier).setReadonlyMode(false); await migrateStoreToIsar(ref.read(isarProvider), ref.read(driftProvider)); await ref.read(backgroundServiceProvider).resumeServiceIfEnabled(); - await ref.read(driftBackgroundUploadFgService).disable(); + await ref.read(backgroundWorkerFgServiceProvider).disable(); } await IsarStoreRepository(ref.read(isarProvider)).upsert(StoreKey.betaTimeline, widget.switchingToBeta); diff --git a/mobile/lib/pages/common/splash_screen.page.dart b/mobile/lib/pages/common/splash_screen.page.dart index f41cf317bf..c64d6fe80f 100644 --- a/mobile/lib/pages/common/splash_screen.page.dart +++ b/mobile/lib/pages/common/splash_screen.page.dart @@ -2,10 +2,8 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; -import 'package:immich_mobile/domain/utils/isolate_lock_manager.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/providers/auth.provider.dart'; -import 'package:immich_mobile/providers/background_sync.provider.dart'; import 'package:immich_mobile/providers/backup/backup.provider.dart'; import 'package:immich_mobile/providers/gallery_permission.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; @@ -23,23 +21,14 @@ class SplashScreenPage extends StatefulHookConsumerWidget { class SplashScreenPageState extends ConsumerState { final log = Logger("SplashScreenPage"); - @override void initState() { super.initState(); - final lockManager = ref.read(isolateLockManagerProvider(kIsolateLockManagerPort)); - - lockManager.requestHolderToClose(); - lockManager - .acquireLock() - .timeout(const Duration(seconds: 5)) - .whenComplete( - () => ref - .read(authProvider.notifier) - .setOpenApiServiceEndpoint() - .then(logConnectionInfo) - .whenComplete(() => resumeSession()), - ); + ref + .read(authProvider.notifier) + .setOpenApiServiceEndpoint() + .then(logConnectionInfo) + .whenComplete(() => resumeSession()); } void logConnectionInfo(String? endpoint) { diff --git a/mobile/lib/providers/app_life_cycle.provider.dart b/mobile/lib/providers/app_life_cycle.provider.dart index 18b7c3464a..cfe10a472d 100644 --- a/mobile/lib/providers/app_life_cycle.provider.dart +++ b/mobile/lib/providers/app_life_cycle.provider.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/services/log.service.dart'; -import 'package:immich_mobile/domain/utils/isolate_lock_manager.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/models/backup/backup_state.model.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; @@ -139,27 +138,10 @@ class AppLifeCycleNotifier extends StateNotifier { Future _handleBetaTimelineResume() async { _ref.read(backupProvider.notifier).cancelBackup(); - final lockManager = _ref.read(isolateLockManagerProvider(kIsolateLockManagerPort)); // Give isolates time to complete any ongoing database transactions await Future.delayed(const Duration(milliseconds: 500)); - lockManager.requestHolderToClose(); - - // Add timeout to prevent deadlock on lock acquisition - try { - await lockManager.acquireLock().timeout( - const Duration(seconds: 10), - onTimeout: () { - _log.warning("Lock acquisition timed out, proceeding without lock"); - throw TimeoutException("Lock acquisition timed out", const Duration(seconds: 10)); - }, - ); - } catch (e) { - _log.warning("Failed to acquire lock: $e"); - return; - } - final backgroundManager = _ref.read(backgroundSyncProvider); final isAlbumLinkedSyncEnable = _ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.syncAlbums); final isEnableBackup = _ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup); @@ -186,7 +168,6 @@ class AppLifeCycleNotifier extends StateNotifier { } finally { // Ensure lock is released even if operations fail try { - lockManager.releaseLock(); _log.info("Lock released after background sync operations"); } catch (lockError) { _log.warning("Failed to release lock after error: $lockError"); @@ -241,28 +222,6 @@ class AppLifeCycleNotifier extends StateNotifier { if (_ref.read(backupProvider.notifier).backupProgress != BackUpProgressEnum.manualInProgress) { _ref.read(backupProvider.notifier).cancelBackup(); } - } else { - final backgroundManager = _ref.read(backgroundSyncProvider); - - // Cancel operations with extended timeout to allow database transactions to complete - try { - await Future.wait([ - backgroundManager.cancel().timeout(const Duration(seconds: 10)), - backgroundManager.cancelLocal().timeout(const Duration(seconds: 10)), - ]).timeout(const Duration(seconds: 15)); - - // Give additional time for isolates to clean up database connections - await Future.delayed(const Duration(milliseconds: 1000)); - } catch (e) { - _log.warning("Timeout during background cancellation: $e"); - } - - // Always release the lock, even if cancellation failed - try { - _ref.read(isolateLockManagerProvider(kIsolateLockManagerPort)).releaseLock(); - } catch (e) { - _log.warning("Failed to release lock on pause: $e"); - } } _ref.read(websocketProvider.notifier).disconnect(); @@ -290,7 +249,6 @@ class AppLifeCycleNotifier extends StateNotifier { } catch (_) {} if (Store.isBetaTimelineEnabled) { - _ref.read(isolateLockManagerProvider(kIsolateLockManagerPort)).releaseLock(); return; } diff --git a/mobile/lib/providers/background_sync.provider.dart b/mobile/lib/providers/background_sync.provider.dart index 1981c45fb1..e6e83b64df 100644 --- a/mobile/lib/providers/background_sync.provider.dart +++ b/mobile/lib/providers/background_sync.provider.dart @@ -1,6 +1,5 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/utils/background_sync.dart'; -import 'package:immich_mobile/domain/utils/isolate_lock_manager.dart'; import 'package:immich_mobile/providers/sync_status.provider.dart'; final backgroundSyncProvider = Provider((ref) { @@ -19,7 +18,3 @@ final backgroundSyncProvider = Provider((ref) { ref.onDispose(manager.cancel); return manager; }); - -final isolateLockManagerProvider = Provider.family((ref, name) { - return IsolateLockManager(portName: name); -}); diff --git a/mobile/lib/providers/backup/backup.provider.dart b/mobile/lib/providers/backup/backup.provider.dart index 6035e53e5d..76cb383465 100644 --- a/mobile/lib/providers/backup/backup.provider.dart +++ b/mobile/lib/providers/backup/backup.provider.dart @@ -6,7 +6,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; -import 'package:immich_mobile/domain/services/background_worker.service.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/backup_album.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; @@ -18,7 +17,6 @@ import 'package:immich_mobile/models/backup/current_upload_asset.model.dart'; import 'package:immich_mobile/models/backup/error_upload_asset.model.dart'; import 'package:immich_mobile/models/backup/success_upload_asset.model.dart'; import 'package:immich_mobile/models/server_info/server_disk_info.model.dart'; -import 'package:immich_mobile/platform/background_worker_api.g.dart'; import 'package:immich_mobile/providers/app_life_cycle.provider.dart'; import 'package:immich_mobile/providers/auth.provider.dart'; import 'package:immich_mobile/providers/backup/error_backup_list.provider.dart'; @@ -36,8 +34,6 @@ import 'package:logging/logging.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:photo_manager/photo_manager.dart' show PMProgressHandler; -final driftBackgroundUploadFgService = Provider((ref) => BackgroundWorkerFgService(BackgroundWorkerFgHostApi())); - final backupProvider = StateNotifierProvider((ref) { return BackupNotifier( ref.watch(backupServiceProvider), diff --git a/mobile/lib/providers/infrastructure/platform.provider.dart b/mobile/lib/providers/infrastructure/platform.provider.dart index 6469624c09..05901a4fec 100644 --- a/mobile/lib/providers/infrastructure/platform.provider.dart +++ b/mobile/lib/providers/infrastructure/platform.provider.dart @@ -1,7 +1,11 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/services/background_worker.service.dart'; +import 'package:immich_mobile/platform/background_worker_api.g.dart'; import 'package:immich_mobile/platform/native_sync_api.g.dart'; import 'package:immich_mobile/platform/thumbnail_api.g.dart'; +final backgroundWorkerFgServiceProvider = Provider((_) => BackgroundWorkerFgService(BackgroundWorkerFgHostApi())); + final nativeSyncApiProvider = Provider((_) => NativeSyncApi()); final thumbnailApi = ThumbnailApi(); From 417d3bbf5071ab3dffd13c8fbaf4820ea4a6e732 Mon Sep 17 00:00:00 2001 From: bo0tzz Date: Wed, 10 Sep 2025 20:44:23 +0200 Subject: [PATCH 462/748] fix: remove invalid read syntax (#21684) --- .github/workflows/merge-translations.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/merge-translations.yml b/.github/workflows/merge-translations.yml index c4167efa8a..1dafa50b75 100644 --- a/.github/workflows/merge-translations.yml +++ b/.github/workflows/merge-translations.yml @@ -23,16 +23,16 @@ jobs: run: | set -euo pipefail - gh pr list --repo $GITHUB_REPOSITORY --author weblate --json number,mergeable | read PR + PR=$(gh pr list --repo $GITHUB_REPOSITORY --author weblate --json number,mergeable) echo "$PR" - echo "$PR" | jq ' + PR_NUMBER=$(echo "$PR" | jq ' if length == 1 then .[0].number else error("Expected exactly 1 entry, got \(length)") end - ' 2>&1 | read PR_NUMBER || exit 1 + ' 2>&1) || exit 1 echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_OUTPUT echo "Selected PR $PR_NUMBER" @@ -64,8 +64,8 @@ jobs: GH_TOKEN: ${{ steps.generate_token.outputs.token }} PR_NUMBER: ${{ steps.find_pr.outputs.PR_NUMBER }} run: | - gh api -X POST "repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/reviews" --field event='APPROVE' --field body='Automatically merging translations PR' \ - | jq '.id' | read REVIEW_ID + REVIEW_ID=$(gh api -X POST "repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/reviews" --field event='APPROVE' --field body='Automatically merging translations PR' \ + | jq '.id') echo "REVIEW_ID=$REVIEW_ID" >> $GITHUB_OUTPUT gh pr merge "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --auto --squash From 170306af1a97348b304c8e57d48e6a840e6eca56 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 10 Sep 2025 14:51:11 -0400 Subject: [PATCH 463/748] fix(deps): update machine-learning (#21704) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- machine-learning/Dockerfile | 6 +-- machine-learning/uv.lock | 93 +++++++++++++++++++------------------ 2 files changed, 50 insertions(+), 49 deletions(-) diff --git a/machine-learning/Dockerfile b/machine-learning/Dockerfile index d1d7f1037e..dd8d8ad7e8 100644 --- a/machine-learning/Dockerfile +++ b/machine-learning/Dockerfile @@ -1,6 +1,6 @@ ARG DEVICE=cpu -FROM python:3.11-bookworm@sha256:c642d5dfaf9115a12086785f23008558ae2e13bcd0c4794536340bcb777a4381 AS builder-cpu +FROM python:3.11-bookworm@sha256:fc1f2e357c307c4044133952b203e66a47e7726821a664f603a180a0c5823844 AS builder-cpu FROM builder-cpu AS builder-openvino @@ -68,11 +68,11 @@ RUN if [ "$DEVICE" = "rocm" ]; then \ uv pip install /opt/onnxruntime_rocm-*.whl; \ fi -FROM python:3.11-slim-bookworm@sha256:838ff46ae6c481e85e369706fa3dea5166953824124735639f3c9f52af85f319 AS prod-cpu +FROM python:3.11-slim-bookworm@sha256:873f91540d53b36327ed4fb018c9669107a4e2a676719720edb4209c4b15d029 AS prod-cpu ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2 -FROM python:3.11-slim-bookworm@sha256:838ff46ae6c481e85e369706fa3dea5166953824124735639f3c9f52af85f319 AS prod-openvino +FROM python:3.11-slim-bookworm@sha256:873f91540d53b36327ed4fb018c9669107a4e2a676719720edb4209c4b15d029 AS prod-openvino RUN apt-get update && \ apt-get install --no-install-recommends -yqq ocl-icd-libopencl1 wget && \ diff --git a/machine-learning/uv.lock b/machine-learning/uv.lock index fa54a13e8c..393dabe319 100644 --- a/machine-learning/uv.lock +++ b/machine-learning/uv.lock @@ -1341,7 +1341,7 @@ wheels = [ [[package]] name = "locust" -version = "2.39.1" +version = "2.40.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "configargparse" }, @@ -1353,6 +1353,7 @@ dependencies = [ { name = "locust-cloud" }, { name = "msgpack" }, { name = "psutil" }, + { name = "pytest" }, { name = "python-engineio" }, { name = "python-socketio", extra = ["client"] }, { name = "pywin32", marker = "sys_platform == 'win32'" }, @@ -1360,12 +1361,12 @@ dependencies = [ { name = "requests" }, { name = "setuptools" }, { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.12'" }, { name = "werkzeug" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/95/c8/10aa5445c404eed389b56877e6714c1787190cc09dd70059ce3765979ec5/locust-2.39.1.tar.gz", hash = "sha256:6bdd19e27edf9a1c84391d6cf6e9a737dfb832be7dfbf39053191ae31b9cc498", size = 1409902, upload-time = "2025-08-29T17:41:01.544Z" } +sdist = { url = "https://files.pythonhosted.org/packages/27/e0/a99401e233ad1b9ad26265ad8f45f2466abb6ef954e7747e8484864eb6df/locust-2.40.2.tar.gz", hash = "sha256:9ffdf900d1ad949d4c5809e2a4e526bba582175f025f24da2755f43f4b5cb23e", size = 1411854, upload-time = "2025-09-08T12:55:28.664Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/b3/b2f4b2ca88b1e72eba7be2b2982533b887f8b709d222db78eb9602aa5121/locust-2.39.1-py3-none-any.whl", hash = "sha256:fd5148f2f1a4ed34aee968abc4393674e69d1b5e1b54db50a397f6eb09ce0b04", size = 1428155, upload-time = "2025-08-29T17:41:00.245Z" }, + { url = "https://files.pythonhosted.org/packages/f5/e7/85ddb125d91b3a2bfa2a52eeae2d4c7da062239aaa475d6aebddb5688f41/locust-2.40.2-py3-none-any.whl", hash = "sha256:c8f0060d2bd8479034e9e61e6473669c4c8216930d99ee61ec0e627340b89d3e", size = 1430483, upload-time = "2025-09-08T12:55:25.659Z" }, ] [[package]] @@ -2204,7 +2205,7 @@ wheels = [ [[package]] name = "pytest" -version = "8.4.1" +version = "8.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -2215,9 +2216,9 @@ dependencies = [ { name = "pygments" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, + { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, ] [[package]] @@ -2249,14 +2250,14 @@ wheels = [ [[package]] name = "pytest-mock" -version = "3.14.1" +version = "3.15.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/71/28/67172c96ba684058a4d24ffe144d64783d2a270d0af0d9e792737bddc75c/pytest_mock-3.14.1.tar.gz", hash = "sha256:159e9edac4c451ce77a5cdb9fc5d1100708d2dd4ba3c3df572f14097351af80e", size = 33241, upload-time = "2025-05-26T13:58:45.167Z" } +sdist = { url = "https://files.pythonhosted.org/packages/61/99/3323ee5c16b3637b4d941c362182d3e749c11e400bea31018c42219f3a98/pytest_mock-3.15.0.tar.gz", hash = "sha256:ab896bd190316b9d5d87b277569dfcdf718b2d049a2ccff5f7aca279c002a1cf", size = 33838, upload-time = "2025-09-04T20:57:48.679Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/05/77b60e520511c53d1c1ca75f1930c7dd8e971d0c4379b7f4b3f9644685ba/pytest_mock-3.14.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0", size = 9923, upload-time = "2025-05-26T13:58:43.487Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b3/7fefc43fb706380144bcd293cc6e446e6f637ddfa8b83f48d1734156b529/pytest_mock-3.15.0-py3-none-any.whl", hash = "sha256:ef2219485fb1bd256b00e7ad7466ce26729b30eadfc7cbcdb4fa9a92ca68db6f", size = 10050, upload-time = "2025-09-04T20:57:47.274Z" }, ] [[package]] @@ -2532,28 +2533,28 @@ wheels = [ [[package]] name = "ruff" -version = "0.12.11" +version = "0.13.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/de/55/16ab6a7d88d93001e1ae4c34cbdcfb376652d761799459ff27c1dc20f6fa/ruff-0.12.11.tar.gz", hash = "sha256:c6b09ae8426a65bbee5425b9d0b82796dbb07cb1af045743c79bfb163001165d", size = 5347103, upload-time = "2025-08-28T13:59:08.87Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6e/1a/1f4b722862840295bcaba8c9e5261572347509548faaa99b2d57ee7bfe6a/ruff-0.13.0.tar.gz", hash = "sha256:5b4b1ee7eb35afae128ab94459b13b2baaed282b1fb0f472a73c82c996c8ae60", size = 5372863, upload-time = "2025-09-10T16:25:37.917Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/a2/3b3573e474de39a7a475f3fbaf36a25600bfeb238e1a90392799163b64a0/ruff-0.12.11-py3-none-linux_armv6l.whl", hash = "sha256:93fce71e1cac3a8bf9200e63a38ac5c078f3b6baebffb74ba5274fb2ab276065", size = 11979885, upload-time = "2025-08-28T13:58:26.654Z" }, - { url = "https://files.pythonhosted.org/packages/76/e4/235ad6d1785a2012d3ded2350fd9bc5c5af8c6f56820e696b0118dfe7d24/ruff-0.12.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b8e33ac7b28c772440afa80cebb972ffd823621ded90404f29e5ab6d1e2d4b93", size = 12742364, upload-time = "2025-08-28T13:58:30.256Z" }, - { url = "https://files.pythonhosted.org/packages/2c/0d/15b72c5fe6b1e402a543aa9d8960e0a7e19dfb079f5b0b424db48b7febab/ruff-0.12.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d69fb9d4937aa19adb2e9f058bc4fbfe986c2040acb1a4a9747734834eaa0bfd", size = 11920111, upload-time = "2025-08-28T13:58:33.677Z" }, - { url = "https://files.pythonhosted.org/packages/3e/c0/f66339d7893798ad3e17fa5a1e587d6fd9806f7c1c062b63f8b09dda6702/ruff-0.12.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:411954eca8464595077a93e580e2918d0a01a19317af0a72132283e28ae21bee", size = 12160060, upload-time = "2025-08-28T13:58:35.74Z" }, - { url = "https://files.pythonhosted.org/packages/03/69/9870368326db26f20c946205fb2d0008988aea552dbaec35fbacbb46efaa/ruff-0.12.11-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6a2c0a2e1a450f387bf2c6237c727dd22191ae8c00e448e0672d624b2bbd7fb0", size = 11799848, upload-time = "2025-08-28T13:58:38.051Z" }, - { url = "https://files.pythonhosted.org/packages/25/8c/dd2c7f990e9b3a8a55eee09d4e675027d31727ce33cdb29eab32d025bdc9/ruff-0.12.11-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ca4c3a7f937725fd2413c0e884b5248a19369ab9bdd850b5781348ba283f644", size = 13536288, upload-time = "2025-08-28T13:58:40.046Z" }, - { url = "https://files.pythonhosted.org/packages/7a/30/d5496fa09aba59b5e01ea76775a4c8897b13055884f56f1c35a4194c2297/ruff-0.12.11-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4d1df0098124006f6a66ecf3581a7f7e754c4df7644b2e6704cd7ca80ff95211", size = 14490633, upload-time = "2025-08-28T13:58:42.285Z" }, - { url = "https://files.pythonhosted.org/packages/9b/2f/81f998180ad53445d403c386549d6946d0748e536d58fce5b5e173511183/ruff-0.12.11-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a8dd5f230efc99a24ace3b77e3555d3fbc0343aeed3fc84c8d89e75ab2ff793", size = 13888430, upload-time = "2025-08-28T13:58:44.641Z" }, - { url = "https://files.pythonhosted.org/packages/87/71/23a0d1d5892a377478c61dbbcffe82a3476b050f38b5162171942a029ef3/ruff-0.12.11-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4dc75533039d0ed04cd33fb8ca9ac9620b99672fe7ff1533b6402206901c34ee", size = 12913133, upload-time = "2025-08-28T13:58:47.039Z" }, - { url = "https://files.pythonhosted.org/packages/80/22/3c6cef96627f89b344c933781ed38329bfb87737aa438f15da95907cbfd5/ruff-0.12.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4fc58f9266d62c6eccc75261a665f26b4ef64840887fc6cbc552ce5b29f96cc8", size = 13169082, upload-time = "2025-08-28T13:58:49.157Z" }, - { url = "https://files.pythonhosted.org/packages/05/b5/68b3ff96160d8b49e8dd10785ff3186be18fd650d356036a3770386e6c7f/ruff-0.12.11-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:5a0113bd6eafd545146440225fe60b4e9489f59eb5f5f107acd715ba5f0b3d2f", size = 13139490, upload-time = "2025-08-28T13:58:51.593Z" }, - { url = "https://files.pythonhosted.org/packages/59/b9/050a3278ecd558f74f7ee016fbdf10591d50119df8d5f5da45a22c6afafc/ruff-0.12.11-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0d737b4059d66295c3ea5720e6efc152623bb83fde5444209b69cd33a53e2000", size = 11958928, upload-time = "2025-08-28T13:58:53.943Z" }, - { url = "https://files.pythonhosted.org/packages/f9/bc/93be37347db854806904a43b0493af8d6873472dfb4b4b8cbb27786eb651/ruff-0.12.11-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:916fc5defee32dbc1fc1650b576a8fed68f5e8256e2180d4d9855aea43d6aab2", size = 11764513, upload-time = "2025-08-28T13:58:55.976Z" }, - { url = "https://files.pythonhosted.org/packages/7a/a1/1471751e2015a81fd8e166cd311456c11df74c7e8769d4aabfbc7584c7ac/ruff-0.12.11-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c984f07d7adb42d3ded5be894fb4007f30f82c87559438b4879fe7aa08c62b39", size = 12745154, upload-time = "2025-08-28T13:58:58.16Z" }, - { url = "https://files.pythonhosted.org/packages/68/ab/2542b14890d0f4872dd81b7b2a6aed3ac1786fae1ce9b17e11e6df9e31e3/ruff-0.12.11-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e07fbb89f2e9249f219d88331c833860489b49cdf4b032b8e4432e9b13e8a4b9", size = 13227653, upload-time = "2025-08-28T13:59:00.276Z" }, - { url = "https://files.pythonhosted.org/packages/22/16/2fbfc61047dbfd009c58a28369a693a1484ad15441723be1cd7fe69bb679/ruff-0.12.11-py3-none-win32.whl", hash = "sha256:c792e8f597c9c756e9bcd4d87cf407a00b60af77078c96f7b6366ea2ce9ba9d3", size = 11944270, upload-time = "2025-08-28T13:59:02.347Z" }, - { url = "https://files.pythonhosted.org/packages/08/a5/34276984705bfe069cd383101c45077ee029c3fe3b28225bf67aa35f0647/ruff-0.12.11-py3-none-win_amd64.whl", hash = "sha256:a3283325960307915b6deb3576b96919ee89432ebd9c48771ca12ee8afe4a0fd", size = 13046600, upload-time = "2025-08-28T13:59:04.751Z" }, - { url = "https://files.pythonhosted.org/packages/84/a8/001d4a7c2b37623a3fd7463208267fb906df40ff31db496157549cfd6e72/ruff-0.12.11-py3-none-win_arm64.whl", hash = "sha256:bae4d6e6a2676f8fb0f98b74594a048bae1b944aab17e9f5d504062303c6dbea", size = 12135290, upload-time = "2025-08-28T13:59:06.933Z" }, + { url = "https://files.pythonhosted.org/packages/ac/fe/6f87b419dbe166fd30a991390221f14c5b68946f389ea07913e1719741e0/ruff-0.13.0-py3-none-linux_armv6l.whl", hash = "sha256:137f3d65d58ee828ae136a12d1dc33d992773d8f7644bc6b82714570f31b2004", size = 12187826, upload-time = "2025-09-10T16:24:39.5Z" }, + { url = "https://files.pythonhosted.org/packages/e4/25/c92296b1fc36d2499e12b74a3fdb230f77af7bdf048fad7b0a62e94ed56a/ruff-0.13.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:21ae48151b66e71fd111b7d79f9ad358814ed58c339631450c66a4be33cc28b9", size = 12933428, upload-time = "2025-09-10T16:24:43.866Z" }, + { url = "https://files.pythonhosted.org/packages/44/cf/40bc7221a949470307d9c35b4ef5810c294e6cfa3caafb57d882731a9f42/ruff-0.13.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:64de45f4ca5441209e41742d527944635a05a6e7c05798904f39c85bafa819e3", size = 12095543, upload-time = "2025-09-10T16:24:46.638Z" }, + { url = "https://files.pythonhosted.org/packages/f1/03/8b5ff2a211efb68c63a1d03d157e924997ada87d01bebffbd13a0f3fcdeb/ruff-0.13.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b2c653ae9b9d46e0ef62fc6fbf5b979bda20a0b1d2b22f8f7eb0cde9f4963b8", size = 12312489, upload-time = "2025-09-10T16:24:49.556Z" }, + { url = "https://files.pythonhosted.org/packages/37/fc/2336ef6d5e9c8d8ea8305c5f91e767d795cd4fc171a6d97ef38a5302dadc/ruff-0.13.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4cec632534332062bc9eb5884a267b689085a1afea9801bf94e3ba7498a2d207", size = 11991631, upload-time = "2025-09-10T16:24:53.439Z" }, + { url = "https://files.pythonhosted.org/packages/39/7f/f6d574d100fca83d32637d7f5541bea2f5e473c40020bbc7fc4a4d5b7294/ruff-0.13.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dcd628101d9f7d122e120ac7c17e0a0f468b19bc925501dbe03c1cb7f5415b24", size = 13720602, upload-time = "2025-09-10T16:24:56.392Z" }, + { url = "https://files.pythonhosted.org/packages/fd/c8/a8a5b81d8729b5d1f663348d11e2a9d65a7a9bd3c399763b1a51c72be1ce/ruff-0.13.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:afe37db8e1466acb173bb2a39ca92df00570e0fd7c94c72d87b51b21bb63efea", size = 14697751, upload-time = "2025-09-10T16:24:59.89Z" }, + { url = "https://files.pythonhosted.org/packages/57/f5/183ec292272ce7ec5e882aea74937f7288e88ecb500198b832c24debc6d3/ruff-0.13.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f96a8d90bb258d7d3358b372905fe7333aaacf6c39e2408b9f8ba181f4b6ef2", size = 14095317, upload-time = "2025-09-10T16:25:03.025Z" }, + { url = "https://files.pythonhosted.org/packages/9f/8d/7f9771c971724701af7926c14dab31754e7b303d127b0d3f01116faef456/ruff-0.13.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b5e3d883e4f924c5298e3f2ee0f3085819c14f68d1e5b6715597681433f153", size = 13144418, upload-time = "2025-09-10T16:25:06.272Z" }, + { url = "https://files.pythonhosted.org/packages/a8/a6/7985ad1778e60922d4bef546688cd8a25822c58873e9ff30189cfe5dc4ab/ruff-0.13.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03447f3d18479df3d24917a92d768a89f873a7181a064858ea90a804a7538991", size = 13370843, upload-time = "2025-09-10T16:25:09.965Z" }, + { url = "https://files.pythonhosted.org/packages/64/1c/bafdd5a7a05a50cc51d9f5711da704942d8dd62df3d8c70c311e98ce9f8a/ruff-0.13.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:fbc6b1934eb1c0033da427c805e27d164bb713f8e273a024a7e86176d7f462cf", size = 13321891, upload-time = "2025-09-10T16:25:12.969Z" }, + { url = "https://files.pythonhosted.org/packages/bc/3e/7817f989cb9725ef7e8d2cee74186bf90555279e119de50c750c4b7a72fe/ruff-0.13.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a8ab6a3e03665d39d4a25ee199d207a488724f022db0e1fe4002968abdb8001b", size = 12119119, upload-time = "2025-09-10T16:25:16.621Z" }, + { url = "https://files.pythonhosted.org/packages/58/07/9df080742e8d1080e60c426dce6e96a8faf9a371e2ce22eef662e3839c95/ruff-0.13.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d2a5c62f8ccc6dd2fe259917482de7275cecc86141ee10432727c4816235bc41", size = 11961594, upload-time = "2025-09-10T16:25:19.49Z" }, + { url = "https://files.pythonhosted.org/packages/6a/f4/ae1185349197d26a2316840cb4d6c3fba61d4ac36ed728bf0228b222d71f/ruff-0.13.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b7b85ca27aeeb1ab421bc787009831cffe6048faae08ad80867edab9f2760945", size = 12933377, upload-time = "2025-09-10T16:25:22.371Z" }, + { url = "https://files.pythonhosted.org/packages/b6/39/e776c10a3b349fc8209a905bfb327831d7516f6058339a613a8d2aaecacd/ruff-0.13.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:79ea0c44a3032af768cabfd9616e44c24303af49d633b43e3a5096e009ebe823", size = 13418555, upload-time = "2025-09-10T16:25:25.681Z" }, + { url = "https://files.pythonhosted.org/packages/46/09/dca8df3d48e8b3f4202bf20b1658898e74b6442ac835bfe2c1816d926697/ruff-0.13.0-py3-none-win32.whl", hash = "sha256:4e473e8f0e6a04e4113f2e1de12a5039579892329ecc49958424e5568ef4f768", size = 12141613, upload-time = "2025-09-10T16:25:28.664Z" }, + { url = "https://files.pythonhosted.org/packages/61/21/0647eb71ed99b888ad50e44d8ec65d7148babc0e242d531a499a0bbcda5f/ruff-0.13.0-py3-none-win_amd64.whl", hash = "sha256:48e5c25c7a3713eea9ce755995767f4dcd1b0b9599b638b12946e892123d1efb", size = 13258250, upload-time = "2025-09-10T16:25:31.773Z" }, + { url = "https://files.pythonhosted.org/packages/e1/a3/03216a6a86c706df54422612981fb0f9041dbb452c3401501d4a22b942c9/ruff-0.13.0-py3-none-win_arm64.whl", hash = "sha256:ab80525317b1e1d38614addec8ac954f1b3e662de9d59114ecbf771d00cf613e", size = 12312357, upload-time = "2025-09-10T16:25:35.595Z" }, ] [[package]] @@ -2876,27 +2877,27 @@ wheels = [ [[package]] name = "tokenizers" -version = "0.21.4" +version = "0.22.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c2/2f/402986d0823f8d7ca139d969af2917fefaa9b947d1fb32f6168c509f2492/tokenizers-0.21.4.tar.gz", hash = "sha256:fa23f85fbc9a02ec5c6978da172cdcbac23498c3ca9f3645c5c68740ac007880", size = 351253, upload-time = "2025-07-28T15:48:54.325Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/b4/c1ce3699e81977da2ace8b16d2badfd42b060e7d33d75c4ccdbf9dc920fa/tokenizers-0.22.0.tar.gz", hash = "sha256:2e33b98525be8453f355927f3cab312c36cd3e44f4d7e9e97da2fa94d0a49dcb", size = 362771, upload-time = "2025-08-29T10:25:33.914Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/98/c6/fdb6f72bf6454f52eb4a2510be7fb0f614e541a2554d6210e370d85efff4/tokenizers-0.21.4-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:2ccc10a7c3bcefe0f242867dc914fc1226ee44321eb618cfe3019b5df3400133", size = 2863987, upload-time = "2025-07-28T15:48:44.877Z" }, - { url = "https://files.pythonhosted.org/packages/8d/a6/28975479e35ddc751dc1ddc97b9b69bf7fcf074db31548aab37f8116674c/tokenizers-0.21.4-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:5e2f601a8e0cd5be5cc7506b20a79112370b9b3e9cb5f13f68ab11acd6ca7d60", size = 2732457, upload-time = "2025-07-28T15:48:43.265Z" }, - { url = "https://files.pythonhosted.org/packages/aa/8f/24f39d7b5c726b7b0be95dca04f344df278a3fe3a4deb15a975d194cbb32/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b376f5a1aee67b4d29032ee85511bbd1b99007ec735f7f35c8a2eb104eade5", size = 3012624, upload-time = "2025-07-28T13:22:43.895Z" }, - { url = "https://files.pythonhosted.org/packages/58/47/26358925717687a58cb74d7a508de96649544fad5778f0cd9827398dc499/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2107ad649e2cda4488d41dfd031469e9da3fcbfd6183e74e4958fa729ffbf9c6", size = 2939681, upload-time = "2025-07-28T13:22:47.499Z" }, - { url = "https://files.pythonhosted.org/packages/99/6f/cc300fea5db2ab5ddc2c8aea5757a27b89c84469899710c3aeddc1d39801/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c73012da95afafdf235ba80047699df4384fdc481527448a078ffd00e45a7d9", size = 3247445, upload-time = "2025-07-28T15:48:39.711Z" }, - { url = "https://files.pythonhosted.org/packages/be/bf/98cb4b9c3c4afd8be89cfa6423704337dc20b73eb4180397a6e0d456c334/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f23186c40395fc390d27f519679a58023f368a0aad234af145e0f39ad1212732", size = 3428014, upload-time = "2025-07-28T13:22:49.569Z" }, - { url = "https://files.pythonhosted.org/packages/75/c7/96c1cc780e6ca7f01a57c13235dd05b7bc1c0f3588512ebe9d1331b5f5ae/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cc88bb34e23a54cc42713d6d98af5f1bf79c07653d24fe984d2d695ba2c922a2", size = 3193197, upload-time = "2025-07-28T13:22:51.471Z" }, - { url = "https://files.pythonhosted.org/packages/f2/90/273b6c7ec78af547694eddeea9e05de771278bd20476525ab930cecaf7d8/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51b7eabb104f46c1c50b486520555715457ae833d5aee9ff6ae853d1130506ff", size = 3115426, upload-time = "2025-07-28T15:48:41.439Z" }, - { url = "https://files.pythonhosted.org/packages/91/43/c640d5a07e95f1cf9d2c92501f20a25f179ac53a4f71e1489a3dcfcc67ee/tokenizers-0.21.4-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:714b05b2e1af1288bd1bc56ce496c4cebb64a20d158ee802887757791191e6e2", size = 9089127, upload-time = "2025-07-28T15:48:46.472Z" }, - { url = "https://files.pythonhosted.org/packages/44/a1/dd23edd6271d4dca788e5200a807b49ec3e6987815cd9d0a07ad9c96c7c2/tokenizers-0.21.4-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:1340ff877ceedfa937544b7d79f5b7becf33a4cfb58f89b3b49927004ef66f78", size = 9055243, upload-time = "2025-07-28T15:48:48.539Z" }, - { url = "https://files.pythonhosted.org/packages/21/2b/b410d6e9021c4b7ddb57248304dc817c4d4970b73b6ee343674914701197/tokenizers-0.21.4-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:3c1f4317576e465ac9ef0d165b247825a2a4078bcd01cba6b54b867bdf9fdd8b", size = 9298237, upload-time = "2025-07-28T15:48:50.443Z" }, - { url = "https://files.pythonhosted.org/packages/b7/0a/42348c995c67e2e6e5c89ffb9cfd68507cbaeb84ff39c49ee6e0a6dd0fd2/tokenizers-0.21.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:c212aa4e45ec0bb5274b16b6f31dd3f1c41944025c2358faaa5782c754e84c24", size = 9461980, upload-time = "2025-07-28T15:48:52.325Z" }, - { url = "https://files.pythonhosted.org/packages/3d/d3/dacccd834404cd71b5c334882f3ba40331ad2120e69ded32cf5fda9a7436/tokenizers-0.21.4-cp39-abi3-win32.whl", hash = "sha256:6c42a930bc5f4c47f4ea775c91de47d27910881902b0f20e4990ebe045a415d0", size = 2329871, upload-time = "2025-07-28T15:48:56.841Z" }, - { url = "https://files.pythonhosted.org/packages/41/f2/fd673d979185f5dcbac4be7d09461cbb99751554ffb6718d0013af8604cb/tokenizers-0.21.4-cp39-abi3-win_amd64.whl", hash = "sha256:475d807a5c3eb72c59ad9b5fcdb254f6e17f53dfcbb9903233b0dfa9c943b597", size = 2507568, upload-time = "2025-07-28T15:48:55.456Z" }, + { url = "https://files.pythonhosted.org/packages/6d/b1/18c13648edabbe66baa85fe266a478a7931ddc0cd1ba618802eb7b8d9865/tokenizers-0.22.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:eaa9620122a3fb99b943f864af95ed14c8dfc0f47afa3b404ac8c16b3f2bb484", size = 3081954, upload-time = "2025-08-29T10:25:24.993Z" }, + { url = "https://files.pythonhosted.org/packages/c2/02/c3c454b641bd7c4f79e4464accfae9e7dfc913a777d2e561e168ae060362/tokenizers-0.22.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:71784b9ab5bf0ff3075bceeb198149d2c5e068549c0d18fe32d06ba0deb63f79", size = 2945644, upload-time = "2025-08-29T10:25:23.405Z" }, + { url = "https://files.pythonhosted.org/packages/55/02/d10185ba2fd8c2d111e124c9d92de398aee0264b35ce433f79fb8472f5d0/tokenizers-0.22.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec5b71f668a8076802b0241a42387d48289f25435b86b769ae1837cad4172a17", size = 3254764, upload-time = "2025-08-29T10:25:12.445Z" }, + { url = "https://files.pythonhosted.org/packages/13/89/17514bd7ef4bf5bfff58e2b131cec0f8d5cea2b1c8ffe1050a2c8de88dbb/tokenizers-0.22.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ea8562fa7498850d02a16178105b58803ea825b50dc9094d60549a7ed63654bb", size = 3161654, upload-time = "2025-08-29T10:25:15.493Z" }, + { url = "https://files.pythonhosted.org/packages/5a/d8/bac9f3a7ef6dcceec206e3857c3b61bb16c6b702ed7ae49585f5bd85c0ef/tokenizers-0.22.0-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4136e1558a9ef2e2f1de1555dcd573e1cbc4a320c1a06c4107a3d46dc8ac6e4b", size = 3511484, upload-time = "2025-08-29T10:25:20.477Z" }, + { url = "https://files.pythonhosted.org/packages/aa/27/9c9800eb6763683010a4851db4d1802d8cab9cec114c17056eccb4d4a6e0/tokenizers-0.22.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cdf5954de3962a5fd9781dc12048d24a1a6f1f5df038c6e95db328cd22964206", size = 3712829, upload-time = "2025-08-29T10:25:17.154Z" }, + { url = "https://files.pythonhosted.org/packages/10/e3/b1726dbc1f03f757260fa21752e1921445b5bc350389a8314dd3338836db/tokenizers-0.22.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8337ca75d0731fc4860e6204cc24bb36a67d9736142aa06ed320943b50b1e7ed", size = 3408934, upload-time = "2025-08-29T10:25:18.76Z" }, + { url = "https://files.pythonhosted.org/packages/d4/61/aeab3402c26874b74bb67a7f2c4b569dde29b51032c5384db592e7b216f4/tokenizers-0.22.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a89264e26f63c449d8cded9061adea7b5de53ba2346fc7e87311f7e4117c1cc8", size = 3345585, upload-time = "2025-08-29T10:25:22.08Z" }, + { url = "https://files.pythonhosted.org/packages/bc/d3/498b4a8a8764cce0900af1add0f176ff24f475d4413d55b760b8cdf00893/tokenizers-0.22.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:790bad50a1b59d4c21592f9c3cf5e5cf9c3c7ce7e1a23a739f13e01fb1be377a", size = 9322986, upload-time = "2025-08-29T10:25:26.607Z" }, + { url = "https://files.pythonhosted.org/packages/a2/62/92378eb1c2c565837ca3cb5f9569860d132ab9d195d7950c1ea2681dffd0/tokenizers-0.22.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:76cf6757c73a10ef10bf06fa937c0ec7393d90432f543f49adc8cab3fb6f26cb", size = 9276630, upload-time = "2025-08-29T10:25:28.349Z" }, + { url = "https://files.pythonhosted.org/packages/eb/f0/342d80457aa1cda7654327460f69db0d69405af1e4c453f4dc6ca7c4a76e/tokenizers-0.22.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:1626cb186e143720c62c6c6b5371e62bbc10af60481388c0da89bc903f37ea0c", size = 9547175, upload-time = "2025-08-29T10:25:29.989Z" }, + { url = "https://files.pythonhosted.org/packages/14/84/8aa9b4adfc4fbd09381e20a5bc6aa27040c9c09caa89988c01544e008d18/tokenizers-0.22.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:da589a61cbfea18ae267723d6b029b84598dc8ca78db9951d8f5beff72d8507c", size = 9692735, upload-time = "2025-08-29T10:25:32.089Z" }, + { url = "https://files.pythonhosted.org/packages/bf/24/83ee2b1dc76bfe05c3142e7d0ccdfe69f0ad2f1ebf6c726cea7f0874c0d0/tokenizers-0.22.0-cp39-abi3-win32.whl", hash = "sha256:dbf9d6851bddae3e046fedfb166f47743c1c7bd11c640f0691dd35ef0bcad3be", size = 2471915, upload-time = "2025-08-29T10:25:36.411Z" }, + { url = "https://files.pythonhosted.org/packages/d1/9b/0e0bf82214ee20231845b127aa4a8015936ad5a46779f30865d10e404167/tokenizers-0.22.0-cp39-abi3-win_amd64.whl", hash = "sha256:c78174859eeaee96021f248a56c801e36bfb6bd5b067f2e95aa82445ca324f00", size = 2680494, upload-time = "2025-08-29T10:25:35.14Z" }, ] [[package]] From 9af44fbd697ae20f6e0c0c3dfb8e33fad1d25bb7 Mon Sep 17 00:00:00 2001 From: Mert <101130780+mertalev@users.noreply.github.com> Date: Wed, 10 Sep 2025 14:53:18 -0400 Subject: [PATCH 464/748] chore(deps): update base image to trixie (#21786) bump base image --- server/Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/Dockerfile b/server/Dockerfile index 9fa401dbdd..6bdf57d4dc 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -1,5 +1,5 @@ # dev build -FROM ghcr.io/immich-app/base-server-dev:202509021104@sha256:47d38c94775332000a93fbbeca1c796687b2d2919e3c75b6e26ab8a65d1864f3 AS dev +FROM ghcr.io/immich-app/base-server-dev:202509091104@sha256:4f9275330f1e49e7ce9840758ea91839052fe6ed40972d5bb97a9af857fa956a AS dev ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \ CI=1 \ @@ -77,7 +77,7 @@ RUN apt-get update \ RUN dart --disable-analytics # production-builder-base image -FROM ghcr.io/immich-app/base-server-dev:202509021104@sha256:47d38c94775332000a93fbbeca1c796687b2d2919e3c75b6e26ab8a65d1864f3 AS prod-builder-base +FROM ghcr.io/immich-app/base-server-dev:202509091104@sha256:4f9275330f1e49e7ce9840758ea91839052fe6ed40972d5bb97a9af857fa956a AS prod-builder-base ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \ CI=1 \ COREPACK_HOME=/tmp @@ -115,7 +115,7 @@ RUN pnpm --filter @immich/sdk --filter @immich/cli --frozen-lockfile install && pnpm --filter @immich/cli --prod --no-optional deploy /output/cli-pruned # prod base image -FROM ghcr.io/immich-app/base-server-prod:202509021104@sha256:84f3727cff75c623f79236cdd9a2b72c84f7665057f474851016f702c67157af +FROM ghcr.io/immich-app/base-server-prod:202509091104@sha256:d1ccbac24c84f2f8277cf85281edfca62d85d7daed6a62b8efd3a81bcd3c5e0e WORKDIR /usr/src/app ENV NODE_ENV=production \ From f7d9215464c54e18bd17a85c8b3f362c92121feb Mon Sep 17 00:00:00 2001 From: bo0tzz Date: Wed, 10 Sep 2025 20:58:22 +0200 Subject: [PATCH 465/748] fix: bad scripting in merge-translations (#21787) --- .github/workflows/merge-translations.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/merge-translations.yml b/.github/workflows/merge-translations.yml index 1dafa50b75..52b17271ec 100644 --- a/.github/workflows/merge-translations.yml +++ b/.github/workflows/merge-translations.yml @@ -37,7 +37,10 @@ jobs: echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_OUTPUT echo "Selected PR $PR_NUMBER" - echo "$PR" | jq -e '.[0].mergeable == "MERGEABLE"' || { echo "PR is not mergeable" ; exit 1 } + if ! echo "$PR" | jq -e '.[0].mergeable == "MERGEABLE"'; then + echo "PR is not mergeable" + exit 1 + fi - name: Generate a token id: generate_token From 7e377d3e424fb5b2efab0c3e84f01b091cae14f2 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 10 Sep 2025 15:35:41 -0400 Subject: [PATCH 466/748] chore: deprecate (#21791) --- mobile/openapi/README.md | 3 +- mobile/openapi/lib/api/assets_api.dart | 8 +- mobile/openapi/lib/api/deprecated_api.dart | 134 ++++++++++++++++++ open-api/immich-openapi-specs.json | 11 +- open-api/typescript-sdk/src/fetch-client.ts | 2 +- .../src/controllers/asset-media.controller.ts | 5 +- server/src/decorators.ts | 15 +- 7 files changed, 163 insertions(+), 15 deletions(-) diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 6cd26aefc4..339ae6ff5d 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -107,7 +107,7 @@ Class | Method | HTTP request | Description *AssetsApi* | [**getAssetStatistics**](doc//AssetsApi.md#getassetstatistics) | **GET** /assets/statistics | *AssetsApi* | [**getRandom**](doc//AssetsApi.md#getrandom) | **GET** /assets/random | *AssetsApi* | [**playAssetVideo**](doc//AssetsApi.md#playassetvideo) | **GET** /assets/{id}/video/playback | -*AssetsApi* | [**replaceAsset**](doc//AssetsApi.md#replaceasset) | **PUT** /assets/{id}/original | replaceAsset +*AssetsApi* | [**replaceAsset**](doc//AssetsApi.md#replaceasset) | **PUT** /assets/{id}/original | Replace the asset with new file, without changing its id *AssetsApi* | [**runAssetJobs**](doc//AssetsApi.md#runassetjobs) | **POST** /assets/jobs | *AssetsApi* | [**updateAsset**](doc//AssetsApi.md#updateasset) | **PUT** /assets/{id} | *AssetsApi* | [**updateAssetMetadata**](doc//AssetsApi.md#updateassetmetadata) | **PUT** /assets/{id}/metadata | @@ -128,6 +128,7 @@ Class | Method | HTTP request | Description *AuthenticationApi* | [**validateAccessToken**](doc//AuthenticationApi.md#validateaccesstoken) | **POST** /auth/validateToken | *DeprecatedApi* | [**createPartnerDeprecated**](doc//DeprecatedApi.md#createpartnerdeprecated) | **POST** /partners/{id} | *DeprecatedApi* | [**getRandom**](doc//DeprecatedApi.md#getrandom) | **GET** /assets/random | +*DeprecatedApi* | [**replaceAsset**](doc//DeprecatedApi.md#replaceasset) | **PUT** /assets/{id}/original | Replace the asset with new file, without changing its id *DownloadApi* | [**downloadArchive**](doc//DownloadApi.md#downloadarchive) | **POST** /download/archive | *DownloadApi* | [**getDownloadInfo**](doc//DownloadApi.md#getdownloadinfo) | **POST** /download/info | *DuplicatesApi* | [**deleteDuplicate**](doc//DuplicatesApi.md#deleteduplicate) | **DELETE** /duplicates/{id} | diff --git a/mobile/openapi/lib/api/assets_api.dart b/mobile/openapi/lib/api/assets_api.dart index e16ac2f535..063f9ea43b 100644 --- a/mobile/openapi/lib/api/assets_api.dart +++ b/mobile/openapi/lib/api/assets_api.dart @@ -729,9 +729,9 @@ class AssetsApi { return null; } - /// replaceAsset + /// Replace the asset with new file, without changing its id /// - /// Replace the asset with new file, without changing its id. This endpoint requires the `asset.replace` permission. + /// This property was deprecated in v1.142.0. Replace the asset with new file, without changing its id. This endpoint requires the `asset.replace` permission. /// /// Note: This method returns the HTTP [Response]. /// @@ -823,9 +823,9 @@ class AssetsApi { ); } - /// replaceAsset + /// Replace the asset with new file, without changing its id /// - /// Replace the asset with new file, without changing its id. This endpoint requires the `asset.replace` permission. + /// This property was deprecated in v1.142.0. Replace the asset with new file, without changing its id. This endpoint requires the `asset.replace` permission. /// /// Parameters: /// diff --git a/mobile/openapi/lib/api/deprecated_api.dart b/mobile/openapi/lib/api/deprecated_api.dart index cdcd27750d..9246998ca2 100644 --- a/mobile/openapi/lib/api/deprecated_api.dart +++ b/mobile/openapi/lib/api/deprecated_api.dart @@ -127,4 +127,138 @@ class DeprecatedApi { } return null; } + + /// Replace the asset with new file, without changing its id + /// + /// This property was deprecated in v1.142.0. Replace the asset with new file, without changing its id. This endpoint requires the `asset.replace` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [MultipartFile] assetData (required): + /// + /// * [String] deviceAssetId (required): + /// + /// * [String] deviceId (required): + /// + /// * [DateTime] fileCreatedAt (required): + /// + /// * [DateTime] fileModifiedAt (required): + /// + /// * [String] key: + /// + /// * [String] slug: + /// + /// * [String] duration: + /// + /// * [String] filename: + Future replaceAssetWithHttpInfo(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? duration, String? filename, }) async { + // ignore: prefer_const_declarations + final apiPath = r'/assets/{id}/original' + .replaceAll('{id}', id); + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + if (key != null) { + queryParams.addAll(_queryParams('', 'key', key)); + } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } + + const contentTypes = ['multipart/form-data']; + + bool hasFields = false; + final mp = MultipartRequest('PUT', Uri.parse(apiPath)); + if (assetData != null) { + hasFields = true; + mp.fields[r'assetData'] = assetData.field; + mp.files.add(assetData); + } + if (deviceAssetId != null) { + hasFields = true; + mp.fields[r'deviceAssetId'] = parameterToString(deviceAssetId); + } + if (deviceId != null) { + hasFields = true; + mp.fields[r'deviceId'] = parameterToString(deviceId); + } + if (duration != null) { + hasFields = true; + mp.fields[r'duration'] = parameterToString(duration); + } + if (fileCreatedAt != null) { + hasFields = true; + mp.fields[r'fileCreatedAt'] = parameterToString(fileCreatedAt); + } + if (fileModifiedAt != null) { + hasFields = true; + mp.fields[r'fileModifiedAt'] = parameterToString(fileModifiedAt); + } + if (filename != null) { + hasFields = true; + mp.fields[r'filename'] = parameterToString(filename); + } + if (hasFields) { + postBody = mp; + } + + return apiClient.invokeAPI( + apiPath, + 'PUT', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Replace the asset with new file, without changing its id + /// + /// This property was deprecated in v1.142.0. Replace the asset with new file, without changing its id. This endpoint requires the `asset.replace` permission. + /// + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [MultipartFile] assetData (required): + /// + /// * [String] deviceAssetId (required): + /// + /// * [String] deviceId (required): + /// + /// * [DateTime] fileCreatedAt (required): + /// + /// * [DateTime] fileModifiedAt (required): + /// + /// * [String] key: + /// + /// * [String] slug: + /// + /// * [String] duration: + /// + /// * [String] filename: + Future replaceAsset(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? duration, String? filename, }) async { + final response = await replaceAssetWithHttpInfo(id, assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, slug: slug, duration: duration, filename: filename, ); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + // When a remote server returns no body with a status of 204, we shall not decode it. + // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" + // FormatException when trying to decode an empty string. + if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { + return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'AssetMediaResponseDto',) as AssetMediaResponseDto; + + } + return null; + } } diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 5a847fc830..7caf215042 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -2504,7 +2504,8 @@ "description": "This endpoint requires the `asset.download` permission." }, "put": { - "description": "Replace the asset with new file, without changing its id. This endpoint requires the `asset.replace` permission.", + "deprecated": true, + "description": "This property was deprecated in v1.142.0. Replace the asset with new file, without changing its id. This endpoint requires the `asset.replace` permission.", "operationId": "replaceAsset", "parameters": [ { @@ -2566,12 +2567,14 @@ "api_key": [] } ], - "summary": "replaceAsset", + "summary": "Replace the asset with new file, without changing its id", "tags": [ - "Assets" + "Assets", + "Deprecated" ], "x-immich-lifecycle": { - "addedAt": "v1.106.0" + "addedAt": "v1.106.0", + "deprecatedAt": "v1.142.0" }, "x-immich-permission": "asset.replace" } diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 18f70f9ab8..bc38a69079 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -2368,7 +2368,7 @@ export function downloadAsset({ id, key, slug }: { })); } /** - * replaceAsset + * Replace the asset with new file, without changing its id */ export function replaceAsset({ id, key, slug, assetMediaReplaceDto }: { id: string; diff --git a/server/src/controllers/asset-media.controller.ts b/server/src/controllers/asset-media.controller.ts index 171cfe7047..688e513b64 100644 --- a/server/src/controllers/asset-media.controller.ts +++ b/server/src/controllers/asset-media.controller.ts @@ -96,8 +96,9 @@ export class AssetMediaController { @Put(':id/original') @UseInterceptors(FileUploadInterceptor) @ApiConsumes('multipart/form-data') - @EndpointLifecycle({ addedAt: 'v1.106.0' }) - @ApiOperation({ + @EndpointLifecycle({ + addedAt: 'v1.106.0', + deprecatedAt: 'v1.142.0', summary: 'replaceAsset', description: 'Replace the asset with new file, without changing its id', }) diff --git a/server/src/decorators.ts b/server/src/decorators.ts index b88f2d2d7e..2f1e76d097 100644 --- a/server/src/decorators.ts +++ b/server/src/decorators.ts @@ -1,5 +1,5 @@ import { SetMetadata, applyDecorators } from '@nestjs/common'; -import { ApiExtension, ApiOperation, ApiProperty, ApiTags } from '@nestjs/swagger'; +import { ApiExtension, ApiOperation, ApiOperationOptions, ApiProperty, ApiTags } from '@nestjs/swagger'; import _ from 'lodash'; import { ADDED_IN_PREFIX, DEPRECATED_IN_PREFIX, LIFECYCLE_EXTENSION } from 'src/constants'; import { ImmichWorker, JobName, MetadataKey, QueueName } from 'src/enum'; @@ -159,12 +159,21 @@ type LifecycleMetadata = { deprecatedAt?: LifecycleRelease; }; -export const EndpointLifecycle = ({ addedAt, deprecatedAt }: LifecycleMetadata) => { +export const EndpointLifecycle = ({ + addedAt, + deprecatedAt, + description, + ...options +}: LifecycleMetadata & ApiOperationOptions) => { const decorators: MethodDecorator[] = [ApiExtension(LIFECYCLE_EXTENSION, { addedAt, deprecatedAt })]; if (deprecatedAt) { decorators.push( ApiTags('Deprecated'), - ApiOperation({ deprecated: true, description: DEPRECATED_IN_PREFIX + deprecatedAt }), + ApiOperation({ + deprecated: true, + description: DEPRECATED_IN_PREFIX + deprecatedAt + (description ? `. ${description}` : ''), + ...options, + }), ); } From 761ac074c983a327efe3028f0dc683f37876626b Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 10 Sep 2025 16:08:15 -0400 Subject: [PATCH 467/748] fix(web): asset refresh (#21788) --- .../asset-viewer/detail-panel-tags.svelte | 1 - .../asset-viewer/detail-panel.svelte | 83 ++++++------------- .../duplicates-compare-control.svelte | 44 +++++----- web/src/lib/utils/asset-utils.ts | 10 +++ 4 files changed, 56 insertions(+), 82 deletions(-) diff --git a/web/src/lib/components/asset-viewer/detail-panel-tags.svelte b/web/src/lib/components/asset-viewer/detail-panel-tags.svelte index 4dd05f520a..c971933f88 100644 --- a/web/src/lib/components/asset-viewer/detail-panel-tags.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel-tags.svelte @@ -21,7 +21,6 @@ const handleAddTag = async () => { const success = await modalManager.show(AssetTagModal, { assetIds: [asset.id] }); - if (success) { asset = await getAssetInfo({ id: asset.id }); } diff --git a/web/src/lib/components/asset-viewer/detail-panel.svelte b/web/src/lib/components/asset-viewer/detail-panel.svelte index 3bcac83914..c115558923 100644 --- a/web/src/lib/components/asset-viewer/detail-panel.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel.svelte @@ -16,21 +16,14 @@ import { locale } from '$lib/stores/preferences.store'; import { featureFlags } from '$lib/stores/server-config.store'; import { preferences, user } from '$lib/stores/user.store'; - import { getAssetThumbnailUrl, getPeopleThumbnailUrl, handlePromiseError } from '$lib/utils'; - import { delay, isFlipped } from '$lib/utils/asset-utils'; + import { getAssetThumbnailUrl, getPeopleThumbnailUrl } from '$lib/utils'; + import { delay, getDimensions } from '$lib/utils/asset-utils'; import { getByteUnitString } from '$lib/utils/byte-units'; import { handleError } from '$lib/utils/handle-error'; import { getMetadataSearchQuery } from '$lib/utils/metadata-search'; import { fromISODateTime, fromISODateTimeUTC } from '$lib/utils/timeline-util'; import { getParentPath } from '$lib/utils/tree-utils'; - import { - AssetMediaSize, - getAssetInfo, - updateAsset, - type AlbumResponseDto, - type AssetResponseDto, - type ExifResponseDto, - } from '@immich/sdk'; + import { AssetMediaSize, getAssetInfo, updateAsset, type AlbumResponseDto, type AssetResponseDto } from '@immich/sdk'; import { IconButton } from '@immich/ui'; import { mdiCalendar, @@ -61,17 +54,28 @@ let { asset, albums = [], currentAlbum = null, onClose }: Props = $props(); - const getDimensions = (exifInfo: ExifResponseDto) => { - const { exifImageWidth: width, exifImageHeight: height } = exifInfo; - if (isFlipped(exifInfo.orientation)) { - return { width: height, height: width }; - } - - return { width, height }; - }; - let showAssetPath = $state(false); let showEditFaces = $state(false); + let isOwner = $derived($user?.id === asset.ownerId); + let people = $derived(asset.people || []); + let unassignedFaces = $derived(asset.unassignedFaces || []); + let showingHiddenPeople = $state(false); + let timeZone = $derived(asset.exifInfo?.timeZone); + let dateTime = $derived( + timeZone && asset.exifInfo?.dateTimeOriginal + ? fromISODateTime(asset.exifInfo.dateTimeOriginal, timeZone) + : fromISODateTimeUTC(asset.localDateTime), + ); + let latlng = $derived( + (() => { + const lat = asset.exifInfo?.latitude; + const lng = asset.exifInfo?.longitude; + + if (lat && lng) { + return { lat: Number(lat.toFixed(7)), lng: Number(lng.toFixed(7)) }; + } + })(), + ); let previousId: string | undefined = $state(); $effect(() => { @@ -84,42 +88,6 @@ } }); - let isOwner = $derived($user?.id === asset.ownerId); - - const handleNewAsset = async (newAsset: AssetResponseDto) => { - // TODO: check if reloading asset data is necessary - if (newAsset.id && !authManager.isSharedLink) { - const data = await getAssetInfo({ id: asset.id }); - people = data?.people || []; - unassignedFaces = data?.unassignedFaces || []; - } - }; - - $effect(() => { - handlePromiseError(handleNewAsset(asset)); - }); - - let latlng = $derived( - (() => { - const lat = asset.exifInfo?.latitude; - const lng = asset.exifInfo?.longitude; - - if (lat && lng) { - return { lat: Number(lat.toFixed(7)), lng: Number(lng.toFixed(7)) }; - } - })(), - ); - - let people = $state(asset.people || []); - let unassignedFaces = $state(asset.unassignedFaces || []); - let showingHiddenPeople = $state(false); - let timeZone = $derived(asset.exifInfo?.timeZone); - let dateTime = $derived( - timeZone && asset.exifInfo?.dateTimeOriginal - ? fromISODateTime(asset.exifInfo.dateTimeOriginal, timeZone) - : fromISODateTimeUTC(asset.localDateTime), - ); - const getMegapixel = (width: number, height: number): number | undefined => { const megapixel = Math.round((height * width) / 1_000_000); @@ -131,10 +99,7 @@ }; const handleRefreshPeople = async () => { - await getAssetInfo({ id: asset.id }).then((data) => { - people = data?.people || []; - unassignedFaces = data?.unassignedFaces || []; - }); + asset = await getAssetInfo({ id: asset.id }); showEditFaces = false; }; diff --git a/web/src/lib/components/utilities-page/duplicates/duplicates-compare-control.svelte b/web/src/lib/components/utilities-page/duplicates/duplicates-compare-control.svelte index ccc0249043..70e0aab076 100644 --- a/web/src/lib/components/utilities-page/duplicates/duplicates-compare-control.svelte +++ b/web/src/lib/components/utilities-page/duplicates/duplicates-compare-control.svelte @@ -2,11 +2,12 @@ import { shortcuts } from '$lib/actions/shortcut'; import Portal from '$lib/components/shared-components/portal/portal.svelte'; import DuplicateAsset from '$lib/components/utilities-page/duplicates/duplicate-asset.svelte'; + import { authManager } from '$lib/managers/auth-manager.svelte'; import { assetViewingStore } from '$lib/stores/asset-viewing.store'; import { handlePromiseError } from '$lib/utils'; import { suggestDuplicate } from '$lib/utils/duplicate-utils'; import { navigate } from '$lib/utils/navigation'; - import { type AssetResponseDto } from '@immich/sdk'; + import { getAssetInfo, type AssetResponseDto } from '@immich/sdk'; import { Button } from '@immich/ui'; import { mdiCheck, mdiImageMultipleOutline, mdiTrashCanOutline } from '@mdi/js'; import { onDestroy, onMount } from 'svelte'; @@ -42,32 +43,32 @@ assetViewingStore.showAssetViewer(false); }); - const onNext = () => { + const onNext = async () => { const index = getAssetIndex($viewingAsset.id) + 1; if (index >= assets.length) { - return Promise.resolve(false); + return false; } - setAsset(assets[index]); - return Promise.resolve(true); + await onViewAsset(assets[index]); + return true; }; - const onPrevious = () => { + const onPrevious = async () => { const index = getAssetIndex($viewingAsset.id) - 1; if (index < 0) { - return Promise.resolve(false); + return false; } - setAsset(assets[index]); - return Promise.resolve(true); + await onViewAsset(assets[index]); + return true; }; - const onRandom = () => { + const onRandom = async () => { if (assets.length <= 0) { - return Promise.resolve(undefined); + return; } const index = Math.floor(Math.random() * assets.length); const asset = assets[index]; - setAsset(asset); - return Promise.resolve(asset); + await onViewAsset(asset); + return { id: asset.id }; }; const onSelectAsset = (asset: AssetResponseDto) => { @@ -86,6 +87,12 @@ selectedAssetIds = new SvelteSet(assets.map((asset) => asset.id)); }; + const onViewAsset = async ({ id }: AssetResponseDto) => { + const asset = await getAssetInfo({ ...authManager.params, id }); + setAsset(asset); + await navigate({ targetRoute: 'current', assetId: asset.id }); + }; + const handleResolve = () => { const trashIds = assets.map((asset) => asset.id).filter((id) => !selectedAssetIds.has(id)); const duplicateAssetIds = assets.map((asset) => asset.id); @@ -102,9 +109,7 @@ { shortcut: { key: 'a' }, onShortcut: onSelectAll }, { shortcut: { key: 's' }, - onShortcut: () => { - setAsset(assets[0]); - }, + onShortcut: () => onViewAsset(assets[0]), }, { shortcut: { key: 'd' }, onShortcut: onSelectNone }, { shortcut: { key: 'c', shift: true }, onShortcut: handleResolve }, @@ -166,12 +171,7 @@
    {#each assets as asset (asset.id)} - setAsset(asset)} - /> + {/each}
    diff --git a/web/src/lib/utils/asset-utils.ts b/web/src/lib/utils/asset-utils.ts index 822820911e..1e6295242a 100644 --- a/web/src/lib/utils/asset-utils.ts +++ b/web/src/lib/utils/asset-utils.ts @@ -34,6 +34,7 @@ import { type AssetResponseDto, type AssetTypeEnum, type DownloadInfoDto, + type ExifResponseDto, type StackResponseDto, type UserPreferencesResponseDto, type UserResponseDto, @@ -328,6 +329,15 @@ export function isFlipped(orientation?: string | null) { return value && (isRotated270CW(value) || isRotated90CW(value)); } +export const getDimensions = (exifInfo: ExifResponseDto) => { + const { exifImageWidth: width, exifImageHeight: height } = exifInfo; + if (isFlipped(exifInfo.orientation)) { + return { width: height, height: width }; + } + + return { width, height }; +}; + export function getFileSize(asset: AssetResponseDto, maxPrecision = 4): string { const size = asset.exifInfo?.fileSizeInByte || 0; return size > 0 ? getByteUnitString(size, undefined, maxPrecision) : 'Invalid Data'; From 8529f92ebc4f5deed02a9f20b9bb58be0898fe2e Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 10 Sep 2025 16:19:43 -0400 Subject: [PATCH 468/748] fix(web): map in album shared link (#21793) --- web/src/lib/components/album-page/album-map.svelte | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/src/lib/components/album-page/album-map.svelte b/web/src/lib/components/album-page/album-map.svelte index 7d7060ac0a..c161bac552 100644 --- a/web/src/lib/components/album-page/album-map.svelte +++ b/web/src/lib/components/album-page/album-map.svelte @@ -1,4 +1,5 @@ From 4f4a50ac11a0777fcde73394a6e0e29170e59fa0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 23:12:15 -0400 Subject: [PATCH 527/748] chore(deps): update dependency @types/node to ^22.18.1 (#22042) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- cli/package.json | 2 +- e2e/package.json | 2 +- open-api/typescript-sdk/package.json | 2 +- pnpm-lock.yaml | 8 ++++---- server/package.json | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cli/package.json b/cli/package.json index 7e11d1a15a..5b9b2d810c 100644 --- a/cli/package.json +++ b/cli/package.json @@ -20,7 +20,7 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.18.0", + "@types/node": "^22.18.1", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", diff --git a/e2e/package.json b/e2e/package.json index 9356538d7c..737f488a50 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -25,7 +25,7 @@ "@playwright/test": "^1.44.1", "@socket.io/component-emitter": "^3.1.2", "@types/luxon": "^3.4.2", - "@types/node": "^22.18.0", + "@types/node": "^22.18.1", "@types/oidc-provider": "^9.0.0", "@types/pg": "^8.15.1", "@types/pngjs": "^6.0.4", diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index 3a52fd6360..8f0d44dcbb 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -19,7 +19,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.18.0", + "@types/node": "^22.18.1", "typescript": "^5.3.3" }, "repository": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0fb05f0bb8..d903068ea3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -63,7 +63,7 @@ importers: specifier: ^4.13.1 version: 4.13.4 '@types/node': - specifier: ^22.18.0 + specifier: ^22.18.1 version: 22.18.1 '@vitest/coverage-v8': specifier: ^3.0.0 @@ -214,7 +214,7 @@ importers: specifier: ^3.4.2 version: 3.7.1 '@types/node': - specifier: ^22.18.0 + specifier: ^22.18.1 version: 22.18.1 '@types/oidc-provider': specifier: ^9.0.0 @@ -296,7 +296,7 @@ importers: version: 1.0.4 devDependencies: '@types/node': - specifier: ^22.18.0 + specifier: ^22.18.1 version: 22.18.1 typescript: specifier: ^5.3.3 @@ -585,7 +585,7 @@ importers: specifier: ^2.0.0 version: 2.0.0 '@types/node': - specifier: ^22.18.0 + specifier: ^22.18.1 version: 22.18.1 '@types/nodemailer': specifier: ^6.4.14 diff --git a/server/package.json b/server/package.json index 0a713689da..0f1734dc01 100644 --- a/server/package.json +++ b/server/package.json @@ -129,7 +129,7 @@ "@types/luxon": "^3.6.2", "@types/mock-fs": "^4.13.1", "@types/multer": "^2.0.0", - "@types/node": "^22.18.0", + "@types/node": "^22.18.1", "@types/nodemailer": "^6.4.14", "@types/picomatch": "^4.0.0", "@types/pngjs": "^6.0.5", From 5fb0afb0d0958ca39946cde87e7f58d052e0a6cc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 23:13:21 -0400 Subject: [PATCH 528/748] chore(deps): update ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0 docker digest to c44be5f (#22038) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- docker/docker-compose.dev.yml | 2 +- docker/docker-compose.prod.yml | 2 +- docker/docker-compose.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index 1bc1908d4e..7864a1edd1 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -149,7 +149,7 @@ services: database: container_name: immich_postgres - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:8d292bdb796aa58bbbaa47fe971c8516f6f57d6a47e7172e62754feb6ed4e7b0 + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:c44be5f2871c59362966d71eab4268170eb6f5653c0e6170184e72b38ffdf107 env_file: - .env environment: diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml index f7d1f564cf..c3fb9c7736 100644 --- a/docker/docker-compose.prod.yml +++ b/docker/docker-compose.prod.yml @@ -63,7 +63,7 @@ services: database: container_name: immich_postgres - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:8d292bdb796aa58bbbaa47fe971c8516f6f57d6a47e7172e62754feb6ed4e7b0 + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:c44be5f2871c59362966d71eab4268170eb6f5653c0e6170184e72b38ffdf107 env_file: - .env environment: diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index c401d4cfc7..3316c17839 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -56,7 +56,7 @@ services: database: container_name: immich_postgres - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:8d292bdb796aa58bbbaa47fe971c8516f6f57d6a47e7172e62754feb6ed4e7b0 + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:c44be5f2871c59362966d71eab4268170eb6f5653c0e6170184e72b38ffdf107 environment: POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_USER: ${DB_USERNAME} From 0f79e0c38eb377d421f230abc6a156f11ea88328 Mon Sep 17 00:00:00 2001 From: Stewart Rand Date: Tue, 16 Sep 2025 00:28:42 -0300 Subject: [PATCH 529/748] fix: Use CSS for uppercase text (#22011) --- .../admin-page/jobs/job-tile.svelte | 18 ++++++++--------- .../admin-page/jobs/jobs-panel.svelte | 6 +++--- .../server-stats/server-stats-panel.svelte | 16 +++++++-------- .../admin-page/server-stats/stats-card.svelte | 2 +- .../settings/auth/auth-settings.svelte | 20 +++++++++---------- .../storage-template-settings.svelte | 2 +- .../supported-datetime-panel.svelte | 16 +++++++-------- .../supported-variables-panel.svelte | 8 ++++---- .../asset-viewer/detail-panel-tags.svelte | 2 +- .../asset-viewer/detail-panel.svelte | 10 +++++----- .../asset-viewer/download-panel.svelte | 2 +- .../editor/crop-tool/crop-tool.svelte | 4 ++-- .../memory-page/memory-viewer.svelte | 4 ++-- .../onboarding-page/onboarding-card.svelte | 4 ++-- .../onboarding-page/onboarding-theme.svelte | 4 ++-- .../search-bar/search-camera-section.svelte | 2 +- .../search-bar/search-date-section.svelte | 4 ++-- .../search-bar/search-display-section.svelte | 2 +- .../search-bar/search-history-box.svelte | 2 +- .../search-bar/search-location-section.svelte | 2 +- .../search-bar/search-media-section.svelte | 2 +- .../search-bar/search-people-section.svelte | 2 +- .../search-bar/search-ratings-section.svelte | 16 ++++++++------- .../search-bar/search-tags-section.svelte | 20 ++++++++++--------- .../settings/setting-input-field.svelte | 2 +- .../side-bar/user-sidebar.svelte | 2 +- .../shared-components/user-avatar.svelte | 4 ++-- .../sharedlinks-page/shared-link-card.svelte | 2 +- .../user-settings-page/device-list.svelte | 12 +++++------ .../partner-settings.svelte | 8 ++++---- .../utilities-page/utilities-menu.svelte | 2 +- web/src/lib/modals/AlbumOptionsModal.svelte | 4 ++-- web/src/lib/modals/TagCreateModal.svelte | 2 +- web/src/lib/modals/TagEditModal.svelte | 6 +----- .../[[assetId=id]]/+page.svelte | 2 +- .../[[assetId=id]]/+page.svelte | 2 +- .../[[assetId=id]]/+page.svelte | 6 +++--- .../[[assetId=id]]/+page.svelte | 2 +- web/src/routes/admin/users/[id]/+page.svelte | 11 +++------- web/src/routes/auth/login/+page.svelte | 4 ++-- 40 files changed, 118 insertions(+), 123 deletions(-) diff --git a/web/src/lib/components/admin-page/jobs/job-tile.svelte b/web/src/lib/components/admin-page/jobs/job-tile.svelte index d2e0ca3ac4..dcd76fa238 100644 --- a/web/src/lib/components/admin-page/jobs/job-tile.svelte +++ b/web/src/lib/components/admin-page/jobs/job-tile.svelte @@ -67,7 +67,7 @@
    {#if jobCounts.failed > 0} @@ -137,7 +137,7 @@ onClick={() => onCommand({ command: JobCommand.Start, force: false })} > - {$t('disabled').toUpperCase()} + {$t('disabled')} {/if} @@ -145,7 +145,7 @@ {#if waitingCount > 0} onCommand({ command: JobCommand.Empty, force: false })}> - {$t('clear').toUpperCase()} + {$t('clear')} {/if} {#if queueStatus.isPaused} @@ -153,12 +153,12 @@ onCommand({ command: JobCommand.Resume, force: false })}> - {$t('resume').toUpperCase()} + {$t('resume')} {:else} onCommand({ command: JobCommand.Pause, force: false })}> - {$t('pause').toUpperCase()} + {$t('pause')} {/if} {/if} @@ -167,25 +167,25 @@ {#if allText} onCommand({ command: JobCommand.Start, force: true })}> - {allText} + {allText} {/if} {#if refreshText} onCommand({ command: JobCommand.Start, force: undefined })}> - {refreshText} + {refreshText} {/if} onCommand({ command: JobCommand.Start, force: false })}> - {missingText} + {missingText} {/if} {#if !disabled && !multipleButtons && isIdle} onCommand({ command: JobCommand.Start, force: false })}> - {missingText} + {missingText} {/if}
    diff --git a/web/src/lib/components/admin-page/jobs/jobs-panel.svelte b/web/src/lib/components/admin-page/jobs/jobs-panel.svelte index 463bcb3d20..93e015e251 100644 --- a/web/src/lib/components/admin-page/jobs/jobs-panel.svelte +++ b/web/src/lib/components/admin-page/jobs/jobs-panel.svelte @@ -177,9 +177,9 @@ {disabled} {subtitle} {description} - allText={allText?.toUpperCase()} - refreshText={refreshText?.toUpperCase()} - missingText={missingText.toUpperCase()} + {allText} + {refreshText} + {missingText} {jobCounts} {queueStatus} onCommand={(command) => (handleCommandOverride || handleCommand)(jobName, command)} diff --git a/web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte b/web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte index b9ceec7445..25c5a0dc56 100644 --- a/web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte +++ b/web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte @@ -36,19 +36,19 @@
    -

    {$t('total_usage').toUpperCase()}

    +

    {$t('total_usage')}

    -

    {$t('photos').toUpperCase()}

    +

    {$t('photos')}

    @@ -60,7 +60,7 @@
    -

    {$t('videos').toUpperCase()}

    +

    {$t('videos')}

    @@ -72,7 +72,7 @@
    -

    {$t('storage').toUpperCase()}

    +

    {$t('storage')}

    @@ -87,7 +87,7 @@
    -

    {$t('user_usage_detail').toUpperCase()}

    +

    {$t('user_usage_detail')}

    - {title} + {title}
    diff --git a/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte b/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte index ef371910c5..07a0e3952c 100644 --- a/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte +++ b/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte @@ -183,7 +183,7 @@ {$t('template')}
    -

    {$t('preview').toUpperCase()}

    +

    {$t('preview')}

    diff --git a/web/src/lib/components/admin-page/settings/storage-template/supported-datetime-panel.svelte b/web/src/lib/components/admin-page/settings/storage-template/supported-datetime-panel.svelte index 8d8d6f0fa7..af21cdc3c9 100644 --- a/web/src/lib/components/admin-page/settings/storage-template/supported-datetime-panel.svelte +++ b/web/src/lib/components/admin-page/settings/storage-template/supported-datetime-panel.svelte @@ -16,7 +16,7 @@

    -

    {$t('date_and_time').toUpperCase()}

    +

    {$t('date_and_time')}

    @@ -27,7 +27,7 @@
    -

    {$t('year').toUpperCase()}

    +

    {$t('year')}

      {#each options.yearOptions as yearFormat, index (index)}
    • {'{{'}{yearFormat}{'}}'} - {getLuxonExample(yearFormat)}
    • @@ -36,7 +36,7 @@
    -

    {$t('month').toUpperCase()}

    +

    {$t('month')}

      {#each options.monthOptions as monthFormat, index (index)}
    • {'{{'}{monthFormat}{'}}'} - {getLuxonExample(monthFormat)}
    • @@ -45,7 +45,7 @@
    -

    {$t('week').toUpperCase()}

    +

    {$t('week')}

      {#each options.weekOptions as weekFormat, index (index)}
    • {'{{'}{weekFormat}{'}}'} - {getLuxonExample(weekFormat)}
    • @@ -54,7 +54,7 @@
    -

    {$t('day').toUpperCase()}

    +

    {$t('day')}

      {#each options.dayOptions as dayFormat, index (index)}
    • {'{{'}{dayFormat}{'}}'} - {getLuxonExample(dayFormat)}
    • @@ -63,7 +63,7 @@
    -

    {$t('hour').toUpperCase()}

    +

    {$t('hour')}

      {#each options.hourOptions as dayFormat, index (index)}
    • {'{{'}{dayFormat}{'}}'} - {getLuxonExample(dayFormat)}
    • @@ -72,7 +72,7 @@
    -

    {$t('minute').toUpperCase()}

    +

    {$t('minute')}

      {#each options.minuteOptions as dayFormat, index (index)}
    • {'{{'}{dayFormat}{'}}'} - {getLuxonExample(dayFormat)}
    • @@ -81,7 +81,7 @@
    -

    {$t('second').toUpperCase()}

    +

    {$t('second')}

    {#each sortOptionsMetadata as option, index (index)} diff --git a/web/src/lib/components/asset-viewer/detail-panel-location.svelte b/web/src/lib/components/asset-viewer/detail-panel-location.svelte index 57fe04b0ef..1a3779f528 100644 --- a/web/src/lib/components/asset-viewer/detail-panel-location.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel-location.svelte @@ -40,8 +40,7 @@ class="flex w-full text-start justify-between place-items-start gap-4 py-4" onclick={() => (isOwner ? (isShowChangeLocation = true) : null)} title={isOwner ? $t('edit_location') : ''} - class:hover:dark:text-immich-dark-primary={isOwner} - class:hover:text-immich-primary={isOwner} + class:hover:text-primary={isOwner} >
    @@ -72,7 +71,7 @@ {:else if !asset.exifInfo?.city && isOwner}
    @@ -116,7 +116,7 @@ {#if user.quotaSizeInBytes !== null} / {getByteUnitString(user.quotaSizeInBytes, $locale, 0)} {/if} - + {#if user.quotaSizeInBytes !== null && user.quotaSizeInBytes >= 0} ({(user.quotaSizeInBytes === 0 ? 1 : user.usage / user.quotaSizeInBytes).toLocaleString($locale, { style: 'percent', diff --git a/web/src/lib/components/shared-components/map/map.svelte b/web/src/lib/components/shared-components/map/map.svelte index 555181ab3b..bf2c4357e0 100644 --- a/web/src/lib/components/shared-components/map/map.svelte +++ b/web/src/lib/components/shared-components/map/map.svelte @@ -367,11 +367,7 @@ > {#snippet children({ feature }: { feature: Feature })} {#if useLocationPin} - + {:else}
    -

    +

    {$user.name}

    {$user.email}

    @@ -107,7 +107,7 @@ - {$t('reset_to_default')} - {/if}
    diff --git a/web/src/lib/components/shared-components/settings/setting-checkboxes.svelte b/web/src/lib/components/shared-components/settings/setting-checkboxes.svelte index efec23460c..940bab36fc 100644 --- a/web/src/lib/components/shared-components/settings/setting-checkboxes.svelte +++ b/web/src/lib/components/shared-components/settings/setting-checkboxes.svelte @@ -31,7 +31,7 @@
    -
    {$t('user')}
    diff --git a/web/src/lib/components/user-settings-page/user-purchase-settings.svelte b/web/src/lib/components/user-settings-page/user-purchase-settings.svelte index 9a61c6086b..5aeed92d41 100644 --- a/web/src/lib/components/user-settings-page/user-purchase-settings.svelte +++ b/web/src/lib/components/user-settings-page/user-purchase-settings.svelte @@ -122,10 +122,10 @@
    - +
    -

    +

    {$t('purchase_server_title')}

    @@ -154,10 +154,10 @@
    - +
    -

    +

    {$t('purchase_individual_title')}

    {#if $user.license?.activatedAt} diff --git a/web/src/lib/components/user-settings-page/user-usage-statistic.svelte b/web/src/lib/components/user-settings-page/user-usage-statistic.svelte index 933d54ceea..e2848f09fb 100644 --- a/web/src/lib/components/user-settings-page/user-usage-statistic.svelte +++ b/web/src/lib/components/user-settings-page/user-usage-statistic.svelte @@ -71,7 +71,7 @@
    {$t('name')}
    @@ -94,9 +94,7 @@
    {$t('view_name')}
    - + diff --git a/web/src/lib/components/utilities-page/utilities-menu.svelte b/web/src/lib/components/utilities-page/utilities-menu.svelte index d9e22aaa9e..fc747dc6af 100644 --- a/web/src/lib/components/utilities-page/utilities-menu.svelte +++ b/web/src/lib/components/utilities-page/utilities-menu.svelte @@ -16,7 +16,7 @@ {#each links as link (link.href)} - + {link.label} {/each} diff --git a/web/src/lib/elements/Dropdown.svelte b/web/src/lib/elements/Dropdown.svelte index 8bc956c779..6cf4b005b2 100644 --- a/web/src/lib/elements/Dropdown.svelte +++ b/web/src/lib/elements/Dropdown.svelte @@ -121,10 +121,10 @@ onclick={() => !renderedOption.disabled && handleSelectOption(option)} > {#if isEqual(selectedOption, option)} -
    +
    -

    +

    {renderedOption.title}

    {:else} diff --git a/web/src/lib/elements/StarRating.svelte b/web/src/lib/elements/StarRating.svelte index 37634c3311..f345dc86b7 100644 --- a/web/src/lib/elements/StarRating.svelte +++ b/web/src/lib/elements/StarRating.svelte @@ -57,7 +57,7 @@
    setHoverRating(0)} use:focusOutside={{ onFocusOut: reset }} use:shortcuts={[ @@ -114,7 +114,7 @@ ratingSelection = 0; handleSelect(ratingSelection); }} - class="cursor-pointer text-xs text-immich-primary dark:text-immich-dark-primary" + class="cursor-pointer text-xs text-primary" > {$t('rating_clear')} diff --git a/web/src/lib/modals/AlbumUsersModal.svelte b/web/src/lib/modals/AlbumUsersModal.svelte index 32c8cd28cf..ac33ed0be9 100644 --- a/web/src/lib/modals/AlbumUsersModal.svelte +++ b/web/src/lib/modals/AlbumUsersModal.svelte @@ -140,7 +140,7 @@ {/if} diff --git a/web/src/lib/modals/ApiKeySecretModal.svelte b/web/src/lib/modals/ApiKeySecretModal.svelte index b50aed5f79..e3c8d13fb8 100644 --- a/web/src/lib/modals/ApiKeySecretModal.svelte +++ b/web/src/lib/modals/ApiKeySecretModal.svelte @@ -14,7 +14,7 @@ -
    +

    {$t('api_key_description')}

    diff --git a/web/src/lib/modals/HelpAndFeedbackModal.svelte b/web/src/lib/modals/HelpAndFeedbackModal.svelte index 0e4f33dfa6..f2b60738a4 100644 --- a/web/src/lib/modals/HelpAndFeedbackModal.svelte +++ b/web/src/lib/modals/HelpAndFeedbackModal.svelte @@ -20,10 +20,7 @@
    -

    +

    {$t('documentation')}

    @@ -32,10 +29,7 @@
    -

    +

    {$t('source')}

    @@ -44,10 +38,7 @@
    -

    +

    {$t('discord')}

    @@ -56,10 +47,7 @@
    -

    +

    {$t('bugs_and_feature_requests')}

    @@ -75,10 +63,7 @@
    -

    +

    {$t('documentation')}

    @@ -89,10 +74,7 @@
    -

    +

    {$t('source')}

    @@ -103,10 +85,7 @@
    -

    +

    {$t('support')}

    @@ -117,10 +96,7 @@
    -

    +

    {$t('bugs_and_feature_requests')}

    diff --git a/web/src/lib/modals/PersonEditBirthDateModal.svelte b/web/src/lib/modals/PersonEditBirthDateModal.svelte index 1b4d7591e2..520a606971 100644 --- a/web/src/lib/modals/PersonEditBirthDateModal.svelte +++ b/web/src/lib/modals/PersonEditBirthDateModal.svelte @@ -37,7 +37,7 @@ -
    +

    {$t('birthdate_set_description')}

    diff --git a/web/src/lib/modals/ServerAboutModal.svelte b/web/src/lib/modals/ServerAboutModal.svelte index 963e2bff64..99967e7588 100644 --- a/web/src/lib/modals/ServerAboutModal.svelte +++ b/web/src/lib/modals/ServerAboutModal.svelte @@ -17,11 +17,9 @@ -
    +
    - +
    - +

    {info.exiftool}

    - +

    {info.nodejs}

    - +

    {info.libvips}

    10 ? 'col-span-2' : ''}> - +

    {info.imagemagick}

    10 ? 'col-span-2' : ''}> - +

    {info.ffmpeg}

    @@ -82,9 +70,7 @@ {#if info.repository && info.repositoryUrl}
    - +
    - +
    - +
    - +
    - +
    {$t('owned')} {$t('shared')}
    diff --git a/web/src/routes/admin/users/+page.svelte b/web/src/routes/admin/users/+page.svelte index f34b393291..9416d92db8 100644 --- a/web/src/routes/admin/users/+page.svelte +++ b/web/src/routes/admin/users/+page.svelte @@ -87,7 +87,7 @@
    {$t('name')}
    Date: Wed, 17 Sep 2025 12:14:16 -0400 Subject: [PATCH 581/748] fix(mobile): load original image (#22142) load original image --- .../alextran/immich/images/ThumbnailsImpl.kt | 8 ++++---- mobile/ios/Runner/Images/ThumbnailsImpl.swift | 2 +- .../widgets/images/local_image_provider.dart | 17 ++++++++++++++++- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/images/ThumbnailsImpl.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/images/ThumbnailsImpl.kt index 1b1716f55c..1ccd742d67 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/images/ThumbnailsImpl.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/images/ThumbnailsImpl.kt @@ -8,7 +8,6 @@ import android.net.Uri import android.os.Build import android.os.CancellationSignal import android.os.OperationCanceledException -import android.provider.MediaStore import android.provider.MediaStore.Images import android.provider.MediaStore.Video import android.util.Size @@ -19,7 +18,6 @@ import com.bumptech.glide.Glide import com.bumptech.glide.Priority import com.bumptech.glide.load.DecodeFormat import java.util.Base64 -import java.util.HashMap import java.util.concurrent.CancellationException import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.Future @@ -202,8 +200,10 @@ class ThumbnailsImpl(context: Context) : ThumbnailApi { val source = ImageDecoder.createSource(resolver, uri) signal.throwIfCanceled() ImageDecoder.decodeBitmap(source) { decoder, info, _ -> - val sampleSize = max(1, min(info.size.width / targetWidth, info.size.height / targetHeight)) - decoder.setTargetSampleSize(sampleSize) + if (targetWidth > 0 && targetHeight > 0) { + val sample = max(1, min(info.size.width / targetWidth, info.size.height / targetHeight)) + decoder.setTargetSampleSize(sample) + } decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE decoder.setTargetColorSpace(ColorSpace.get(ColorSpace.Named.SRGB)) } diff --git a/mobile/ios/Runner/Images/ThumbnailsImpl.swift b/mobile/ios/Runner/Images/ThumbnailsImpl.swift index d1ea2cc0e0..452ca62377 100644 --- a/mobile/ios/Runner/Images/ThumbnailsImpl.swift +++ b/mobile/ios/Runner/Images/ThumbnailsImpl.swift @@ -105,7 +105,7 @@ class ThumbnailApiImpl: ThumbnailApi { var image: UIImage? Self.imageManager.requestImage( for: asset, - targetSize: CGSize(width: Double(width), height: Double(height)), + targetSize: width > 0 && height > 0 ? CGSize(width: Double(width), height: Double(height)) : PHImageManagerMaximumSize, contentMode: .aspectFill, options: Self.requestOptions, resultHandler: { (_image, info) -> Void in diff --git a/mobile/lib/presentation/widgets/images/local_image_provider.dart b/mobile/lib/presentation/widgets/images/local_image_provider.dart index 223d095432..f90961ea5a 100644 --- a/mobile/lib/presentation/widgets/images/local_image_provider.dart +++ b/mobile/lib/presentation/widgets/images/local_image_provider.dart @@ -4,6 +4,8 @@ import 'dart:ui'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/loaders/image_request.dart'; import 'package:immich_mobile/presentation/widgets/images/image_provider.dart'; import 'package:immich_mobile/presentation/widgets/images/one_frame_multi_image_stream_completer.dart'; @@ -88,13 +90,26 @@ class LocalFullImageProvider extends CancellableImageProvider Date: Wed, 17 Sep 2025 21:48:54 +0530 Subject: [PATCH 582/748] fix: show delete on device when asset has a local match (#22143) * fix: show delete on device when asset has a local match * change test description --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- mobile/lib/utils/action_button.utils.dart | 2 +- mobile/test/utils/action_button_utils_test.dart | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/mobile/lib/utils/action_button.utils.dart b/mobile/lib/utils/action_button.utils.dart index 4dfc0398bd..090aeeeaa7 100644 --- a/mobile/lib/utils/action_button.utils.dart +++ b/mobile/lib/utils/action_button.utils.dart @@ -102,7 +102,7 @@ enum ActionButtonType { context.asset.hasRemote, ActionButtonType.deleteLocal => !context.isInLockedView && // - context.asset.storage == AssetState.local, + context.asset.hasLocal, ActionButtonType.upload => !context.isInLockedView && // context.asset.storage == AssetState.local, diff --git a/mobile/test/utils/action_button_utils_test.dart b/mobile/test/utils/action_button_utils_test.dart index 497246e2a1..f8c51173d7 100644 --- a/mobile/test/utils/action_button_utils_test.dart +++ b/mobile/test/utils/action_button_utils_test.dart @@ -502,6 +502,21 @@ void main() { expect(ActionButtonType.deleteLocal.shouldShow(context), isFalse); }); + + test('should show when asset is merged', () { + final context = ActionButtonContext( + asset: mergedAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.deleteLocal.shouldShow(context), isTrue); + }); }); group('upload button', () { From 98ea3847e52bc50bd4004394bcce2a99a07c71b4 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 17 Sep 2025 12:23:23 -0400 Subject: [PATCH 583/748] refactor: server-about-modal (#22138) * refactor: server-about-modal * fix: bits-ui scroll lock cleanup --- web/src/lib/components/ServerAboutItem.svelte | 24 +++ .../shared-components/change-date.spec.ts | 9 +- web/src/lib/modals/ServerAboutModal.svelte | 159 +++++------------- 3 files changed, 74 insertions(+), 118 deletions(-) create mode 100644 web/src/lib/components/ServerAboutItem.svelte diff --git a/web/src/lib/components/ServerAboutItem.svelte b/web/src/lib/components/ServerAboutItem.svelte new file mode 100644 index 0000000000..9e169a9839 --- /dev/null +++ b/web/src/lib/components/ServerAboutItem.svelte @@ -0,0 +1,24 @@ + + +
    + + + {#if versionHref} + {version} + {:else} + {version} + {/if} + +
    diff --git a/web/src/lib/components/shared-components/change-date.spec.ts b/web/src/lib/components/shared-components/change-date.spec.ts index 43035051f3..63926a44a6 100644 --- a/web/src/lib/components/shared-components/change-date.spec.ts +++ b/web/src/lib/components/shared-components/change-date.spec.ts @@ -1,6 +1,6 @@ import { getIntersectionObserverMock } from '$lib/__mocks__/intersection-observer.mock'; import { getVisualViewportMock } from '$lib/__mocks__/visual-viewport.mock'; -import { fireEvent, render, screen } from '@testing-library/svelte'; +import { fireEvent, render, screen, waitFor } from '@testing-library/svelte'; import userEvent from '@testing-library/user-event'; import { DateTime } from 'luxon'; import ChangeDate from './change-date.svelte'; @@ -30,6 +30,13 @@ describe('ChangeDate component', () => { vi.resetAllMocks(); }); + afterAll(async () => { + await waitFor(() => { + // check that bits-ui body scroll-lock class is gone + expect(document.body.style.pointerEvents).not.toBe('none'); + }); + }); + test('should render correct values', () => { render(ChangeDate, { initialDate, initialTimeZone, onCancel, onConfirm }); expect(getDateInput().value).toBe('2024-01-01T00:00'); diff --git a/web/src/lib/modals/ServerAboutModal.svelte b/web/src/lib/modals/ServerAboutModal.svelte index 99967e7588..92bbac3d67 100644 --- a/web/src/lib/modals/ServerAboutModal.svelte +++ b/web/src/lib/modals/ServerAboutModal.svelte @@ -1,8 +1,8 @@ - -
    - + +
    +